pax_global_header00006660000000000000000000000064141174252030014511gustar00rootroot0000000000000052 comment=1d7485af15ae1e00e94957930a39bc3a2d510ec3 onionbalance-0.2.2/000077500000000000000000000000001411742520300141425ustar00rootroot00000000000000onionbalance-0.2.2/.gitattributes000066400000000000000000000000461411742520300170350ustar00rootroot00000000000000onionbalance/_version.py export-subst onionbalance-0.2.2/.travis.yml000066400000000000000000000007551411742520300162620ustar00rootroot00000000000000language: python sudo: required dist: bionic python: 3.7 env: - TEST=unit install: - pip install -r requirements.txt - pip install -r test-requirements.txt script: # Tests are run with linters - if [[ $TEST == functional* ]]; then source ./test/scripts/run-functional-tests.sh; elif [[ $TEST == 'unit' ]]; then source ./test/scripts/run-unit-tests.sh; fi - sphinx-build -W -b html -d ./docs/_build ./docs ./docs/_build/html after_success: - coveralls onionbalance-0.2.2/CHANGES.rst000066400000000000000000000107521411742520300157510ustar00rootroot000000000000000.2.2 ----- - Add an OBv3 hacking guide. - Remove tox and simplify build procedure. - A single OnionBalance can now support multiple onion services. 0.2.1 ----- - v2 codebase now uses Cryptodome instead of the deprecated PyCrypto library. - v3 codebase is now more flexible when it comes to requiring a live consensus. This should increase the reachability of Onionbalance in scenarios where the network is having trouble establishing a new consensus. - v3 support for connecting to the control port through a Unix socket. Patch by Peter Tripp. - Introduce status socket support for v3 onions. Patch by vporton. - Sending a SIGHUP signal now reloads the v3 config. Patch by Peter Chung. 0.2.0 ----- - Allow migration from Tor to Onionbalance by reading tor private keys directly using the 'key' directive in the YAML config file. Also update `onionbalance-config` to support that. - Improve `onionbalance-config` for v3 onions. Simplify the output directory (and change docs to reflect so) and the wizard suggestions. 0.1.9 ----- - Initial support for v3 onions! 0.1.8 ----- - Fix a bug which could cause descriptor fetching to crash and stall if an old instance descriptor was retrieved from a HSDir. #64 - Minors fixes to documentation and addition of a tutorial. 0.1.7 ----- - Add functionality to reconnect to the Tor control port while Onionbalance is running. Thank you to Ceysun Sucu for the patch. #45 - Fix bug where instance descriptors were not updated correctly when an instance address was listed under multiple master service. #49 - Improve performance by only requesting each unique instance descriptor once per round, rather once for each time it was listed in the config file. #51 - Fix bug where an exception was raised when the status socket location did not exist. - Improve the installation documentation for Debian and Fedora/EPEL installations. 0.1.6 ----- - Remove unicode tags from the yaml files generated by onionbalance-config. - Fix bug resulting in invalid instance onion addresses when attempting to remove the ".onion" TLD. #44 0.1.5 ----- - Log error when Onionbalance does not have permission to read a private key. #34 - Fix bug loading descriptors when an address with .onion extension is listed in the configuration file. #37 - Add support for connecting to the Tor control port over a unix domain socket. #3 0.1.4 ----- - Use setproctitle to set a cleaner process title - Replace the python-schedule dependency with a custom scheduler. - Add a Unix domain socket which outputs the status of the Onionbalance service when a client connects. By default this socket is created at `/var/run/onionbalance/control`. Thank you to Federico Ceratto for the original socket implementation. - Add support for handling the `SIGINT` and `SIGTERM` signals. Thank you to Federico Ceratto for this feature. - Upgrade tests to use the stable Tor 0.2.7.x release. - Fix bug when validating the modulus length of a provided RSA private key. - Upload distinct service descriptors to each hidden service directory by default. The distinct descriptors allows up to 60 introduction points or backend instances to be reachable by external clients. Thank you to Ceysun Sucu for describing this technique in his Masters thesis. - Add `INITIAL_DELAY` option to wait longer before initial descriptor publication. This is useful when there are many backend instance descriptors which need to be downloaded. - Add configuration option to allow connecting to a Tor control port on a different host. - Remove external image assets when documentation is generated locally instead of on ReadTheDocs. 0.1.3 ----- - Streamline the integration tests by using Tor and Chutney from the upstream repositories. - Fix bug when HSFETCH is called with a HSDir argument (3d225fd). - Remove the 'schedule' package from the source code and re-add it as a dependency. This Python package is now packaged for Debian. - Extensively restructure the documentation to make it more comprehensible. - Add --version argument to the command line - Add configuration options to output log entries to a log file. 0.1.2 ----- - Remove dependency on the schedule package to prepare for packaging Onionbalance in Debian. The schedule code is now included directly in onionbalance/schedule.py. - Fix the executable path in the help messages for onionbalance and onionbalance-config. 0.1.1 ----- - Patch to resolve issue when saving generated torrc files from onionbalance-config in Python 2. 0.1.0 ----- - Initial release onionbalance-0.2.2/COPYING000066400000000000000000001045141411742520300152020ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . onionbalance-0.2.2/MANIFEST.in000066400000000000000000000003071411742520300157000ustar00rootroot00000000000000include README.rst include COPYING include requirements.txt recursive-include docs *.rst recursive-include onionbalance/config_generator/data * include versioneer.py include onionbalance/_version.py onionbalance-0.2.2/README.rst000066400000000000000000000031241411742520300156310ustar00rootroot00000000000000.. image:: obv3_logo.jpg Onionbalance ============ Introduction ------------ Onionbalance allows Tor onion service requests to be distributed across multiple backend Tor instances. Onionbalance provides load-balancing while also making onion services more resilient and reliable by eliminating single points-of-failure. |build-status| |docs| Getting Started --------------- Installation and usage documentation is available at https://onionbalance.readthedocs.org. Contact ------- This software is under active development and likely contains bugs. Please open bug reports on Github if you discover any issues with the software or documentation. :: pub rsa4096 2012-02-22 [SC] 13C81580203AE18BB7C0424E09CC7F5315F271D9 uid [ultimate] George Kadianakis uid [ultimate] George Kadianakis sub rsa4096 2012-02-22 [E] The Onionbalance software was originally authored and maintained by Donncha Ó Cearbhaill. Thanks for all the code!!! .. |build-status| image:: https://img.shields.io/travis/asn-d6/onionbalance.svg?style=flat :alt: build status :scale: 100% :target: https://travis-ci.org/asn-d6/onionbalance .. |coverage| image:: https://coveralls.io/repos/github/asn-d6/onionbalance/badge.svg?branch=master :alt: Code coverage :target: https://coveralls.io/github/asn-d6/onionbalance?branch=master .. |docs| image:: https://readthedocs.org/projects/onionbalance-v3/badge/?version=latest :alt: Documentation Status :scale: 100% :target: https://onionbalance.readthedocs.org/en/latest/ onionbalance-0.2.2/docs/000077500000000000000000000000001411742520300150725ustar00rootroot00000000000000onionbalance-0.2.2/docs/Makefile000066400000000000000000000164111411742520300165350ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " applehelp to make an Apple Help Book" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/onionbalance.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/onionbalance.qhc" applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @echo @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." @echo "N.B. You won't be able to view it unless you put it in" \ "~/Library/Documentation/Help or install it in your application" \ "bundle." devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/onionbalance" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/onionbalance" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." coverage: $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage @echo "Testing of coverage in the sources finished, look at the " \ "results in $(BUILDDIR)/coverage/python.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." onionbalance-0.2.2/docs/alpha-testing-v3.txt000066400000000000000000000001751411742520300207240ustar00rootroot00000000000000Please see https://onionbalance-v3.readthedocs.io/en/latest/v3/tutorial-v3.html for an up-to-date guide for v3 onionbalance. onionbalance-0.2.2/docs/changelog.rst000066400000000000000000000001031411742520300175450ustar00rootroot00000000000000.. _changelog: Change Log ========== .. include:: ../CHANGES.rst onionbalance-0.2.2/docs/conf.py000066400000000000000000000142111411742520300163700ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # onionbalance documentation build configuration file, created by # sphinx-quickstart on Wed Jun 10 13:54:42 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os import datetime import sphinx.environment from docutils.utils import get_source_line # Documentation configuration __version__ = '0.2.2' __author__ = "George Kadianakis, Donncha O'Cearbhaill" __contact__ = "asn@torproject.org" # Ignore the 'dev' version suffix. if __version__.endswith('dev'): __version__ = __version__[:-4] # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('..')) on_rtd = os.environ.get('READTHEDOCS', None) == 'True' # -- General configuration ------------------------------------------------ # Don't give warning for external images def _warn_node(self, msg, node): if not msg.startswith('nonlocal image URI found:'): self._warnfunc(msg, '%s:%s' % get_source_line(node)) sphinx.environment.BuildEnvironment.warn_node = _warn_node # If your documentation needs a minimal Sphinx version, state it here. needs_sphinx = '1.1' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'alabaster', 'sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.viewcode', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The encoding of source files. source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Onionbalance' # Remove copyright notice for man page copyright = '' author = __author__ # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = __version__ # The full version, including alpha/beta/rc tags. release = __version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = 'en' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build', 'modules.rst'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = { "description": "Load balancing and redundancy for Tor onion services.", 'github_user': 'asn-d6', 'github_repo': 'onionbalance', 'github_button': False, 'travis_button': False, } # Enable external resources on the RTD hosted documentation only if on_rtd: html_theme_options['github_button'] = True html_theme_options['travis_button'] = True # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. html_short_title = "Onionbalance Docs" # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] # Custom sidebar templates, maps document names to template names. html_sidebars = { '**': [ 'about.html', 'navigation.html', 'relations.html', ] } # If false, no module index is generated. html_domain_indices = False # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. html_show_sphinx = False # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. html_show_copyright = False # Output file base name for HTML help builder. htmlhelp_basename = 'onionbalancedoc' # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('running-onionbalance', 'onionbalance', 'a Tor onion service load balancer', ['%s <%s>' % (__author__, __contact__)], 1), ('onionbalance-config', 'onionbalance-config', 'tool for generating onionbalance config files and keys', ['%s <%s>' % (__author__, __contact__)], 1), ] # If true, show URL addresses after external links. #man_show_urls = False onionbalance-0.2.2/docs/contributors.rst000066400000000000000000000025321411742520300203630ustar00rootroot00000000000000.. _contributors: Contributors ============ Thank you to the following contributors and others for their invaluble help and advice in developing Onionbalance. Contributions of any kind (code, documentation, testing) are very welcome. * `Donncha Ó Cearbhaill `_ - Original author and maintainer of Onionbalance!!! * `Federico Ceratto `_ - Tireless assistance with Debian packaging and Onionbalance improvements. - Replaced and reimplemented the job scheduler. - Implemented support for Unix signals and added a status socket to retrieve information about the running service. * `Michael Scherer `_ - Improving the Debian installation documentation. * `s7r `_ - Assisted in testing and load testing Onionbalance from an early stage. - Many useful suggestions for performance and usability improvements. * `Ceysun Sucu `_ - Added code to reconnect to the Tor control port while Onionbalance is running. * `Alec Muffett `_ - Extensively tested Onionbalance, found many bugs and made many suggestions to improve the software. * `duritong `_ - Packaged Onionbalance for Fedora, CentOS, and Redhat 7 (EPEL repository). onionbalance-0.2.2/docs/index.rst000066400000000000000000000025411411742520300167350ustar00rootroot00000000000000.. onionbalance documentation master file, created by sphinx-quickstart on Wed Jun 10 13:54:42 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. .. image:: ./../obv3_logo.jpg Overview ======== Onionbalance is the best way to load balance onion services across multiple backend Tor instances. This way the load of introduction and rendezvous requests get distributed across multiple hosts. Onionbalance provides load-balancing while also making onion services more resilient and reliable by eliminating single points-of-failure. - Latest release: |version| (:ref:`changelog`) - Repository: https://gitlab.torproject.org/tpo/core/onionbalance - GitHub mirror: https://github.com/asn-d6/onionbalance/ - Issue tracker: https://github.com/asn-d6/onionbalance/issues - PyPI: https://pypi.org/project/Onionbalance/ - IRC: #tor-dev @ OFTC Quickstart ============ Onionbalance supports both v2 and v3 onions but because of the different setup procedure, we have separate guides for them. See the :ref:`tutorial_v3` page for setting up a v3 onionbalance, or the :ref:`tutorial_v2` page for setting up a v2 onionbalance. Table Of Contents ==================== .. toctree:: :maxdepth: 1 :titlesonly: v3/tutorial-v3 v2/tutorial-v2 use-cases contributors changelog v3/hacking.rst onionbalance-0.2.2/docs/make.bat000066400000000000000000000161301411742520300165000ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled echo. coverage to run coverage check of the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) REM Check if sphinx-build is available and fallback to Python version if any %SPHINXBUILD% 2> nul if errorlevel 9009 goto sphinx_python goto sphinx_ok :sphinx_python set SPHINXBUILD=python -m sphinx.__init__ %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) :sphinx_ok if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\onionbalance.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\onionbalance.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "coverage" ( %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage if errorlevel 1 exit /b 1 echo. echo.Testing of coverage in the sources finished, look at the ^ results in %BUILDDIR%/coverage/python.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end onionbalance-0.2.2/docs/onionbalance-config.rst000066400000000000000000000027101411742520300215170ustar00rootroot00000000000000.. _onionbalance_config: onionbalance-config Tool ======================== Description ----------- The ``onionbalance-config`` tool is the fastest way to generate the necessary keys and config files to get your onion service up and running. .. code-block:: console $ onionbalance-config When called without any arguments, the config generator will run in an interactive mode and prompt for user input. The ``master`` directory should be stored on the management server while the other ``instance`` directories should be transferred to the respective backend servers. Files ----- master/config.yaml This is the configuration file that is used my the Onionbalance management server. master/.key The private key which will become the public address and identity for your onion service. It is essential that you keep this key secure. master/torrc-server A sample Tor configuration file which can be used with the Tor instance running on the management server (v2-only). srv/torrc-instance A sample Tor config file which contains the Tor ``HiddenService*`` options needed for your backend Tor instance (v2-only). srv//private_key Directory containing the private key for your backend onion service instance. This key is less critical as it can be rotated if lost or compromised (v2-only). See Also -------- Full documentation for the **Onionbalance** software is available at https://onionbalance.readthedocs.org/ onionbalance-0.2.2/docs/use-cases.rst000066400000000000000000000035511411742520300175200ustar00rootroot00000000000000Onionbalance Use Cases ========================== There a many ways to use Onionbalance to increase the scalability, reliability and security of your onion service. The following are some examples of what is possible. Current Deployments ------------------- **SKS Keyserver Pool** Kristian Fiskerstrand has set up a onion service `keyserver pool `_ which connects users to one of the available onion service key servers. Other Examples -------------- - A popular onion service with an overloaded web server or Tor process A service such as Facebook which gets a large number of users would like to distribute client requests across multiple servers as the load is too much for a single Tor instance to handle. They would also like to balance between instances when the 'encrypted services' proposal is implemented [2555]. - Redundancy and automatic failover A political activist would like to keep their web service accessible and secure in the event that the secret police seize some of their servers. Clients should ideally automatically fail-over to another online instances with minimal service disruption. - Secure Onion Service Key storage An onion service operator would like to compartmentalize their permanent onion key in a secure location separate to their Tor process and other services. With this proposal permanent keys could be stored on an independent, isolated system. Research -------- `Ceysun Sucu `_ has analysed Onionbalance and other approaches to onion service scaling in his masters thesis `Tor\: Onion Service Scaling `_. The thesis provides a good overview of current approaches. It is a recommended read for those interested in higher performance onion services. onionbalance-0.2.2/docs/v2/000077500000000000000000000000001411742520300154215ustar00rootroot00000000000000onionbalance-0.2.2/docs/v2/design.rst000066400000000000000000000205521411742520300174300ustar00rootroot00000000000000Design Document =============== This tool is designed to allow requests to Tor onion service to be directed to multiple back-end Tor instances, thereby increasing availability and reliability. The design involves collating the set of introduction points created by one or more independent Tor onion service instances into a single 'master' descriptor. Overview -------- This tool is designed to allow requests to Tor onion service to be directed to multiple back-end Tor instances, thereby increasing availability and reliability. The design involves collating the set of introduction points created by one or more independent Tor onion service instances into a single 'master' onion service descriptor. The master descriptor is signed by the onion service permanent key and published to the HSDir system as normal. Clients who wish to access the onion service would then retrieve the *master* service descriptor and try to connect to introduction points from the descriptor in a random order. If a client successfully establishes an introduction circuit, they can begin communicating with one of the onion services instances with the normal onion service protocol defined in rend-spec.txt Instance A load-balancing node running an individual onion service. Introduction Point A Tor relay chosen by an onion service instance as a medium-term *meeting-place* for initial client connections. Master Descriptor An onion service descriptor published with the desired onion address containing introduction points for each instance. Management Server Server running Onionbalance which collates introduction points and publishes a master descriptor. Metadata Channel A direct connection from an instance to a management server which can be used for instance descriptor upload and transfer of other data. Retrieving Introduction Point Data ---------------------------------- The core functionality of the Onionbalance service is the collation of introduction point data from multiple onion service instances by the management server. In its basic mode of operation, the introduction point information is transferred from the onion service instances to the management server via the HSDir system. Each instance runs an onion service with an instance specific permanent key. The instance publishes a descriptor to the DHT at regularly intervals or when its introduction point set changes. On initial startup the management server will load the previously published master descriptor from the DHT if it exists. The master descriptor is used to prepopulate the introduction point set. The management server regularly polls the HSDir system for a descriptor for each of its instances. Currently polling occurs every 10 minutes. This polling period can be tuned for onion services with shorter or longer lasting introduction points. When the management server receives a new descriptor from the HSDir system, it should before a number of checks to ensure that it is valid: - Confirm that the descriptor has a valid signature and that the public key matches the instance that was requested. - Confirm that the descriptor timestamp is equal or newer than the previously received descriptor for that onion service instance. This reduces the ability of a HSDir to replay older descriptors for an instance which may contain expired introduction points. - Confirm that the descriptor timestamp is not more than 4 hours in the past. An older descriptor indicates that the instance may no longer be online and publishing descriptors. The instance should not be included in the master descriptor. It should be possible for two or more independent management servers to publish descriptors for a single onion service. The servers would publish independent descriptors which will replace each other on the HSDir system.. Any difference in introduction point selection between descriptors should not impact the end user. Limitations ''''''''''' - A malicious HSDir could replay old instance descriptors in an attempt to include expired introduction points in the master descriptor. When an attacker does not control all of the responsible HSDirs this attack can be mitigated by not accepting descriptors with a timestamp older than the most recently retrieved descriptor. - The management server may also retrieve an old instance descriptor as a result of churn in the DHT. The management server may attempt to fetch the instance descriptor from a different set of HSDirs than the instance published to. - An onion service instance may rapidly rotate its introduction point circuits when subjected to a Denial of Service attack. An introduction point circuit is closed by the onion service when it has received ``max_introductions`` for that circuit. During DoS this circuit rotating may occur faster than the management server polls the HSDir system for new descriptors. As a result clients may retrieve master descriptors which contain no currently valid introduction points. - It is trivial for a HSDir to determine that a onion service is using Onionbalance. Onionbalance will try poll for instance descriptors on a regular basis. A HSDir which connects to onion services published to it would find that a backend instance is serving the same content as the master service. This allows a HSDir to trivially determine the onion addresses for a service's backend instances. Onionbalance allows for scaling across multiple onion service instances with no additional software or Tor modifications necessary on the onion service instance. Onionbalance does not hide that a service is using Onionbalance. It also does not significantly protect a service from introduction point denial of service or actively malicious HSDirs. Choice of Introduction Points ----------------------------- Tor onion service descriptors can include a maximum of 10 introduction points. Onionbalance should select introduction points so as to uniformly distribute load across the available backend instances. Onionbalance will upload multiple distinct descriptors if you have configured more than 10 instances. - **1 instance** - 3 IPs - **2 instance** - 6 IPs (3 IPs from each instance) - **3 instance** - 9 IPs (3 IPs from each instance) - **4 instance** - 10 IPs (3 IPs from one instance, 2 from each other instance) - **5 instance** - 10 IPs (2 IPs from each instance) - **6-10 instances** - 10 IPs (selection from all instances) - **11 or more instances** - 10 IPs (distinct descriptors - selection from all instances) Always attempting to choose 3 introduction points per descriptor may make it more difficult for a passive observer to confirm that a service is running Onionbalance. However behavioral characteristics such as the rate of introduction point rotation may still allow a passive observer to distinguish an Onionbalance service from a standard Tor onion service. Selecting a smaller set of introduction points may impact on performance or reliability of the service. - **1 instance** - 3 IPs - **2 instances** - 3 IPs (2 IPs from one instance, 1 IP from the other instance) - **3 instances** - 3 IPs (1 IP from each instance) - **more than 3 instances** - Select the maximum set of introduction points as outlined previously. It may be advantageous to select introduction points in a non-random manner. The longest-lived introduction points published by a backend instance are likely to be stable. Conversely selecting more recently created introduction points may more evenly distribute client introductions across an instances introduction point circuits. Further investigation of these options should indicate if there is significant advantages to any of these approaches. Generation and Publication of Master Descriptor ----------------------------------------------- The management server should generate a onion service descriptor containing the selected introduction points. This master descriptor is then signed by the actual onion service permanent key. The signed master descriptor should be published to the responsible HSDirs as normal. Clients who wish to access the onion service would then retrieve the 'master' service descriptor and begin connect to introduction points at random from the introduction point list. After successful introduction the client will have created an onion service circuit to one of the available onion services instances and can then begin communicating as normally along that circuit. onionbalance-0.2.2/docs/v2/in-depth.rst000066400000000000000000000071551411742520300176730ustar00rootroot00000000000000.. _in_depth_v2: Onionbalance In-depth Tutorial (v2) =================================== This is a step-by-step tutorial to help you configure Onionbalance for v2 onions. Onionbalance implements `round-robin` like load balancing on top of Tor onion services. A typical Onionbalance deployment will incorporate one management servers and multiple backend application servers. .. note :: Note that this guide uses Linux distro packages which are currently only available for onionbalance-0.1.8 which does not support v3 onions. This means that if you setup onionbalance using this guide, you won't be able to use it for setting up v3 onions. It will only be useful for v2 onions. Assumptions ----------- You want to run: - one or more Onionbalance processes, to perform load balancing, on hosts named ``obhost1``, ``obhost2``. - two or more Tor processes, to run the Onion Services, on hosts named ``torhost1``, ``torhost2``. - two or more servers (e.g. web servers) or traditional load balancers on hosts named ``webserver1``, ``webserver2``. Scaling up: - the number of ``obhostX`` can be increased but this will not help handling more traffic. - the number of ``torhostX`` can be increased up to 60 instances to handle more traffic. - the number of ``webserverX`` can be increased to handle more traffic until the Tor daemons in front of them become the bottleneck. Scaling down: - the three type of services can be run on the same hosts. The number of hosts can scale down to one. Reliability: Contrarily to traditional load balancers, the Onionbalance daemon does not receive and forward traffic. As such, ``obhostX`` does not need to be in proximity to ``torhostX`` and can be run from any location on the Internet. Failure of ``obhostX`` will not affect the service as long as either one ``obhost`` is still up or or the failure is shorter than 30 minutes. Other assumptions: - the hosts run Debian or Ubuntu - there is no previous configuration Configuring the Onionbalance host ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ On ``obhost1``: .. code-block:: bash sudo apt-get install onionbalance tor mkdir -p /var/run/onionbalance chown onionbalance:onionbalance /var/run/onionbalance /usr/sbin/onionbalance-config -n --service-virtual-port \ --service-target --output ~/onionbalance_master_conf sudo cp ~/onionbalance_master_conf/master/*.key /etc/onionbalance/ sudo cp ~/onionbalance_master_conf/master/config.yaml /etc/onionbalance/ sudo chown onionbalance:onionbalance /etc/onionbalance/*.key sudo service onionbalance restart sudo tail -f /var/log/onionbalance/log Back up the files in ``~/onionbalance_master_conf``. If you have other ``obhostX``: .. code-block:: bash sudo apt-get install onionbalance mkdir -p /var/run/onionbalance chown onionbalance:onionbalance /var/run/onionbalance Copy ``/etc/onionbalance/\*.key`` and ``/etc/onionbalance/config.yml`` from ``obhost1`` to all hosts in ``obhostX``. Check the logs. The following warnings are expected: `"Error generating descriptor: No introduction points for service ..."`. Configuring the Tor services ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Copy the ``instance_torrc`` and ``private_key`` files from each of the directories named ``./config/srv1``, ``./config/srv2``,.. on ``obhost1`` to ``torhostX`` - the contents of one directory for each ``torhostX``. Configure and start the services - the onion service on Onionbalance should be ready within 10 minutes. Monitoring ~~~~~~~~~~ On each ``obhostX``, run: .. code-block:: bash sudo watch 'socat - unix-connect:/var/run/onionbalance/control' onionbalance-0.2.2/docs/v2/installing_ob.rst000066400000000000000000000032411411742520300207770ustar00rootroot00000000000000.. _installing_ob: Installing Onionbalance =========================== Onionbalance requires at least one system that is running the Onionbalance management server. The Onionbalance software does not need to be installed on the backend servers which provide the onion service content (i.e. web site, IRC server etc.). Onionbalance is not yet packaged for most Linux and BSD. The tool can be installed from PyPI or directly from the Git repository: .. code-block:: console # pip install onionbalance or .. code-block:: console $ git clone https://github.com/asn-d6/onionbalance.git $ cd onionbalance # python setup.py install If you are running Debian Jessie (with backports enabled) or later you can install Onionbalance with the following command: .. code-block:: console # apt-get install onionbalance There is also a python 3 based package available in Fedora >= 25: .. code-block:: console # yum install python3-onionbalance For CentOS or RedHat 7 there is a python 2 based package available in the EPEL Repository: .. code-block:: console # yum install python2-onionbalance All tagged releases on Github or PyPi are signed with my GPG key: :: pub 4096R/0x3B0D706A7FBFED86 2013-06-27 [expires: 2016-07-11] Key fingerprint = 7EFB DDE8 FD21 11AE A7BE 1AA6 3B0D 706A 7FBF ED86 uid [ultimate] Donncha O'Cearbhaill sub 3072R/0xD60D64E73458F285 2013-06-27 [expires: 2016-07-11] sub 3072R/0x7D49FC2C759AA659 2013-06-27 [expires: 2016-07-11] sub 3072R/0x2C9C6F4ABBFCF7DD 2013-06-27 [expires: 2016-07-11] Now that Onionbalance is installed, please move to :ref:`installing_tor`. onionbalance-0.2.2/docs/v2/installing_tor.rst000066400000000000000000000037651411742520300212160ustar00rootroot00000000000000.. _installing_tor: Installing Tor =============== Installing and Configuring Tor ------------------------------ Tor is need on the management server and every backend onion service instance. Management Server ~~~~~~~~~~~~~~~~~ Onionbalance requires that a recent version of Tor (``>= 0.2.7.1-alpha``) is installed on the management server system. This version might not be available in your operating system's repositories yet. It is recommended that you install Tor from the `Tor Project repositories `_ to ensure you stay up to date with the latest Tor releases. The management server need to have its control port enabled to allow the Onionbalance daemon to talk to the Tor process. This can be done by uncommenting the ``ControlPort`` option in your ``torrc`` configuration file. Alternatively you can replace your ``torrc`` file with the following is suitable for the Tor instance running on the management server: .. literalinclude:: ../../onionbalance/config_generator/data/torrc-server :name: torrc-server :lines: 6- After configuring Tor you should restart your Tor process .. code-block:: console $ sudo service tor reload Backend Instances ~~~~~~~~~~~~~~~~~ Each backend instance should be run a standard onion service which serves your website or other content. More information about configuring onion services is available in the Tor Project's `onion service configuration guide `_. If you have used the ``onionbalance-config`` tool you should transfer the generated instance config files and keys to the Tor configuration directory on the backend servers. .. literalinclude:: ../../onionbalance/config_generator/data/torrc-instance-v2 :name: torrc-instance :lines: 6- After configuring Tor you should restart your Tor process .. code-block:: console $ sudo service tor reload Now that Tor is installed and configured, please move to :ref:`running_onionbalance`. onionbalance-0.2.2/docs/v2/running-onionbalance.rst000066400000000000000000000140541411742520300222650ustar00rootroot00000000000000.. _running_onionbalance: Running Onionbalance ==================== .. toctree:: :hidden: onionbalance-config <../onionbalance-config> Description ----------- You can start the Onionbalance management server once all of your backend onion service instances are running. You will need to create a :ref:`configuration file ` which list the backend onion services and the location of your hidden service keys. .. code-block:: console $ onionbalance -c config.yaml or .. code-block:: console $ sudo service onionbalance start The management server must be left running to publish new descriptors for your onion service: in about 10 minutes you should have a fully functional onionbalance setup. .. note:: Multiple Onionbalance management servers can be run simultaneously with the same master private key and configuration file to provide redundancy. .. _configuration_file_format: Configuration File Format ------------------------- The Onionbalance management server is primarily configured using a YAML configuration file. .. literalinclude:: ../../onionbalance/config_generator/data/config.example.yaml :name: example-config.yaml :language: yaml The ``services`` section of the configuration file contains a list of master onion services that Onionbalance is responsible for. Each ``key`` option specifies the location of the 1024 bit private RSA key for the onion service. This master private key determines the address that users will use to access your onion service. This private key **must** be kept secure. The location of the private key is evaluated as an absolute path, or relative to the configuration file location. You can use existing Tor onion service private key with Onionbalance to keep your onion address. Each backend Tor onion service instance is listed by its unique onion address in the ``instances`` list. .. note:: You can replace backend instance keys if they get lost or compromised. Simply start a new backend onion service under a new key and replace the ``address`` in the config file. If you have used the :ref:`onionbalance-config ` tool you can simply use the generated config file from ``master/config.yaml``. .. note:: By default onionbalance will search for a ``config.yaml`` file in the current working directory. Configuration Options ~~~~~~~~~~~~~~~~~~~~~ The Onionbalance command line options can also be specified in the Onionbalance configuration file. Options specified on the command line take precedence over the related configuration file options: TOR_CONTROL_SOCKET: The location of the Tor unix domain control socket. Onionbalance will attempt to connect to this control socket first before falling back to using a control port connection. (default: /var/run/tor/control) TOR_ADDRESS: The address where the Tor control port is listening. (default: 127.0.0.1) TOR_PORT: The Tor control port. (default: 9051) TOR_CONTROL_PASSWORD: The password for authenticating to a Tor control port which is using the HashedControlPassword authentication method. This is not needed when the Tor control port is using the more common CookieAuthentication method. (default: None) Other options: LOG_LOCATION The path where Onionbalance should write its log file. LOG_LEVEL Specify the minimum verbosity of log messages to output. All log messages equal or higher the the specified log level are output. The available log levels are the same as the --verbosity command line option. REFRESH_INTERVAL How often to check for updated backend onion service descriptors. This value can be decreased if your backend instance are under heavy loaded causing them to rotate introduction points quickly. (default: 600 seconds). PUBLISH_CHECK_INTERVAL How often should to check if new descriptors need to be published for the master onion service (default: 360 seconds). INITIAL_DELAY How long to wait between starting Onionbalance and publishing the master descriptor. If you have more than 20 backend instances you may need to wait longer for all instance descriptors to download before starting (default: 45 seconds). DISTINCT_DESCRIPTORS Distinct descriptors are used if you have more than 10 backend instances. At the cost of scalability, this can be disabled to appear more like a standard onion service. (default: True) STATUS_SOCKET_LOCATION The Onionbalance service creates a Unix domain socket which provides real-time information about the currently loaded service and descriptors. This option can be used to change the location of this domain socket. (default: /var/run/onionbalance/control) The following options typically do not need to be modified by the end user: REPLICAS How many set of HSDirs to upload too (default: 2). MAX_INTRO_POINTS How many introduction points to include in a descriptor (default: 10) DESCRIPTOR_VALIDITY_PERIOD How long a onion service descriptor remains valid (default: 86400 seconds) DESCRIPTOR_OVERLAP_PERIOD How long to overlap onion service descriptors when changing descriptor IDs (default: 3600 seconds) DESCRIPTOR_UPLOAD_PERIOD How often to publish a descriptor, even when the introduction points don't change (default: 3600 seconds) Environment Variables ~~~~~~~~~~~~~~~~~~~~~ ONIONBALANCE_CONFIG Override the location for the Onionbalance configuration file. The loaded configuration file takes precedence over environment variables. Configuration file options will override environment variable which have the same name. ONIONBALANCE_LOG_LOCATION See the config file option. ONIONBALANCE_LOG_LEVEL See the config file option ONIONBALANCE_STATUS_SOCKET_LOCATION See the config file option ONIONBALANCE_TOR_CONTROL_SOCKET See the config file option Files ----- /etc/onionbalance/config.yaml The configuration file, which contains ``services`` entries. config.yaml Fallback location for torrc, if /etc/onionbalance/config.yaml is not found. See Also -------- Full documentation for the **Onionbalance** software is available at https://onionbalance.readthedocs.org/ onionbalance-0.2.2/docs/v2/tutorial-v2.rst000066400000000000000000000037401411742520300203470ustar00rootroot00000000000000.. _tutorial_v2: Onionbalance v2 Installation Guide ======================================= .. toctree:: :titlesonly: installing_ob installing_tor running-onionbalance in-depth design Onionbalance implements `round-robin` like load balancing on top of Tor onion services. A typical Onionbalance deployment will incorporate one management servers and multiple backend application servers. Architecture ------------ The management server runs the Onionbalance daemon. Onionbalance combines the routing information (the introduction points) for multiple backend onion services instances and publishes this information in a master descriptor. .. image:: ../../onionbalance.png The backend application servers run a standard Tor onion service. When a client connects to the public onion service they select one of the introduction points at random. When the introduction circuit completes the user is connected to the corresponding backend instance. **Management Server** is the machine running the Onionbalance daemon. It needs to have access to the onion service private key corresponding for the desired onion address. This is the public onion address that users will request. This machine can be located geographically isolated from the machines hosting the onion service content. It does not need to serve any content. **Backend Instance** Each backend application server runs a Tor onion service with a unique onion service key. .. note:: The :ref:`onionbalance-config ` tool can be used to quickly generate keys and config files for your Onionbalance deployment. The Onionbalance tool provide two command line tools: **onionbalance** acts as a long running daemon. **onionbalance-config** is a helper utility which eases the process of creating keys and configuration files for onionbalance and the backend Tor instances. Getting Started ---------------- To get started with setting up Onionbalance, please go to :ref:`installing_ob`. onionbalance-0.2.2/docs/v3/000077500000000000000000000000001411742520300154225ustar00rootroot00000000000000onionbalance-0.2.2/docs/v3/hacking.rst000066400000000000000000000112251411742520300175610ustar00rootroot00000000000000.. _hacking: Onionbalance v3 Hacking Guide ====================================== .. toctree:: :hidden: This is a small pocket guide to help with maintaining Onionbalance. Hacking History --------------- Let's start with some history. Onionbalance (OB) was invented by Donncha during a GSoC many moons ago. Back then OB only supported v2 onion services. When v3 onions appeared, the Tor network team took over to `add v3 support `_. How Onionbalance works ------------------------ Onionbalance is a pretty simple creature. After it boots and figures how many *frontend services* and *backend instances* it supports, all it does is spin. While spinning, it continuously fetches the descriptors of its *backend instances* to check if something changed (e.g. an intro point rotated, or an instance went down). When something changes or enough time passes it publishes a new descriptor for that frontend service. That's all it does really: it makes sure that its *frontend services* are kept up to date and their descriptors are always present in the right parts of the hash ring. Codebase structure ------------------- Onionbalance supports both v2 and v3 onions (as of version 0.2.1). The codebase has been accordingly split to ``onionbalance/hs_v2`` which contains v2-specific code, and ``onionbalance/hs_v3`` which contains v3-specific code. There is also some helper functions in ``onionbalance/common``. We only care about v3 code in this document. Everything starts in ``manager.py``. It initializes the *scheduler* (more on that later) and then instantiates an ``onionbalance.py:Onionbalance`` object which is a global singleton that keeps track of all runtime state (e.g. frontend services, configuration parameters, controller sockets, etc.). Each *frontend service* is represented by an ``OnionbalanceService`` object. The task of an ``OnionbalanceService`` is to keep track of the underlying *backend instances* (which are ``InstanceV3`` objects) and to check whether a new descriptor should be uploaded and do to the actual upload when the time comes. The *scheduler* initialized by ``manager.py`` is responsible for periodically invoking functions that are essential for Onionbalance's functionality. In particular, those functions fetch the descriptors of the *backend instances* (``fetch_instance_descriptors``) and publish descriptors for the *frontend services* (``publish_all_descriptors``). Another important part of the codebase, is the stem controller in `onionbalance/hs_v3/stem_controller.py`. The stem controller is responsible for polling the control port for information (e.g. descriptors) and also for listening to essential control port events. In particular, the stem controller will trigger callbacks when a new consensus or onion service descriptor is downloaded. These callbacks are important since onionbalance needs to do certain moves when new documents are received (for example see ``handle_new_status_event()`` for when a new consensus arrives). Finally, the files ``consensus.py`` and ``hashring.py`` are responsible for maintaining the HSv3 hash ring which is how OBv3 learns the right place to fetch or upload onion service descriptors. The file ``params.py`` is where the all magic numbers are kept. What about onionbalance-config? ----------------------------------- Right. ``onionbalance-config`` is a tool that helps operators create valid OBv3 configuration files. It seems like people like to use it, but this might be because OBv3's configuration files are complicated, and we could eventually replace it with a more straightforward config file format. In any case, the ``onionbalance-config`` codebase is in ``onionbalance/config_generator`` and it's some pretty terrible code that tries to wrestle both v2 and v3 configuration file formats while providing a helpful wizard for the user to input her preferences. Is there any cryptography in OBv3? ----------------------------------- When it comes to crypto, most of it is handled by stem (it's the one that signs descriptor) and by tor (it's the one that does all the HSv3 key exchanges, etc.). However, a little bit of magic resides in ``tor_ed25519.py``... Magic is required because Tor uses a different ed25519 private key format than most common crypto libraries because of *v3 key blinding*. To work around that, we created a duck-typed wrapper class for Tor ed25519 private keys; this way hazmat (our crypto lib) can work with those keys, without ever realizing that it's a different private key format than what it likes to use. For more information, see that file's documentation and this `helpful blog post `_. onionbalance-0.2.2/docs/v3/onionbalance_v3.jpg000066400000000000000000030006611411742520300211720ustar00rootroot00000000000000JFIFHHExifMM* 2% ( 914ӇiL2020:02:20 21:11:03Redmi Note 4Hmido-user 7.0 NRD90M V11.0.2.0.NCFMIXM release-keysHXiaomil  0"@ 0220 ْ Ɉ' 01002020:02:20 21:11:03d d3783853783853783852020:02:20 21:11:03dR980100!9 A d2020:02:20(HHCC" P!1A"Q 2aq#B 3R$Cb%r4S&'EUcs K!1AQ"aq2#B3Rr$bC %4&S5s7T ?z~!=UL4KzD؄{rMl0 rtH|`?!Tܫ~qN'0EDW92ƴY+~jJg2Β5j& 7_o543Zلd]+%h֧3%FcXW/$Q!X=}Ľ ]KTO5GNSj66s7fITml#vEnM~,_s̏8)@s21Z0 u"_$tKy[>XcOMjwob Hsc<}ę4R[Tf {QfA&GΞbjeqf`ӴTg)v*Tf=qRqHcHukjXn[fKACEy2B5܋ۯ칊*bdp4hmvxO壘~TT!\L$vر+w=q:3$_+%<YdZԷ4WtK \ɘ*Mc=l)!,lZ{suJ=^*QL$Ym =m?TY\Ps<9}\J .a`c/IjzGE؋;bu/M[uwWK,쩧)0Vhi^p c*ED;'д`{ȰCiJa 0-qgok T 7tnۅ&_-*ؤ8r%:0ܒ@-am;b+ͪ*di*3( -*gFfWsqo{㚊?g9#s 0RI )?)OCu4d5uYFfh#d(,}ZTX {ʶ?!DŽܯ'j*zl0`%A-Ĉu{v&rqW ZIM5,f*~|rNwgdTtp_WYKE"ʱPU'm'ěd-GSWT,ѭ$ǢqF h>٢hWU_Ne$ȾR؏bNO%6c_\g*ՠC;%Gi;s|L(ኖ\⤫hJwz?˶&l2+tE=XBHEMq=:> ZO 2ZL]2Mw? .`MQ:ъ!7J[N9>,<ڃ)^Iji ݴm?qkl8s6L¯̾dOqd,цUXRC^\6v DVB+rI oZ;X |Up͒j崰n;\S}?CR6ťU0!B7>Uš3Y8S/%<5AB4:a{[Y n 2$zݭ /N1 EXS"Ψ)9)j'1\5i"䞿]7VD+d#ooݺwߋj**}DyP*/}[[뉤 E@Alo%DuJS1!ŕE;H~ܙ?e_SLcЗt;lTeF9Zg'15D#݆mRc*YӉvLh[E Pu{a[h$kp!'u(΁Wrnt;0x;܄$C('˝&JI+L9#sSYTLR{v7Ԥ|h` M8eu"6" @ E/Zrd%dr.[RdwFpW@Q4*HD55K_ni̚R %X)AC= ,sL LYGuԙ.,ӶSlIj~-+q\Vg\%`/{\d ΘA]5M%>RM`&|JaiXTFeP6O4 '-i9rȼ(jU6{cTiԯ. )tM+ff ;c&`/5] | QQJf5@$j1IUIT $M4ߡ>1:0#[CQ(g/"bQUB+sөbd_>Zr1Ŧ@cT [m+8n 놭MOqQ%%=1d ;"_~-eRK !9Aw0.6 HS|b8R17f+G-\y(rķڏ X›7%1OyUUW qU* tɕu߲{& >ƪ[vԍcsZ<DjUuoS2֤?JZNUH; ܋[ WzN L3VʼOI"0V7 @[A-aPOZ(T1>q6U*ɖT,3UbT)6{ܟP=?LOC]qȵJO YtEG~a__CYCS$DUtSMàG Z%e(Je-K07m}})f?ҳR4L^N@ l1,$ WNzXhb*]bcPl=V+Y||rAU +DUF҆A68ݩp:cYgKKO=zDPƜU5ϸ=|H(j!N5U\H>~%tlG1^PVZʦKKQhuNR :j3R௖9P Zdq6UTWgH 6 I\7y_>F[Q8@Diܓ}jLUP Ѣ14̡MB&v9ሸN5%=>k4/_Ka檁Akup58Tf9m>yJ$i#Qnt? 6r5E%LQF"*<n/rwۮ"Ve[O[_ə}fG<gvo^)ի󃘵.QG4Qh͑( mbfWT-?%CTU,)x֍n2 zziʬZLV-; M}c4(lRZ[H؄Cam:TDē*WbopI7p~.]J0IU,b)c@Tܱ'Swh** Q*k5Et<ʅP) ?,k_o|0\Nkb䌇J) ڍPZV kHW'*ͨ+ov$} RH:xMK\[≦y,GMI LuA4~!Wegh|tvP^oo=`V#]leςd:kRgbӫ,LآV'S`C }h3,`ttCKFGv߾&9nsɖSjL̷Fl/נpUV^ʲ̕-gL($5IkӱU]Uk4D,J>{mc|8?,e>)\܆mb=bJA:F-tpusm\G1.UM+IZ -Fo6S`<>ڿ3Ȥh%3fC:nW@6w3ǔiC-FSHU*;p;aT_fM_YH!*;>W_;u8U6{ʑcWJyÈ;)Q$l0XcᲽ16PU{,5*x3hfPDf|=x;J_<,IE6$}Yf,>|>M"<]3 à?1/xۅˍT*eUO]lq:7rMFdc/M_k i㥯̧5 Y؀ 1u U+S0I#$obVg7Tb妞эCmuU#FvvÜ6,hSE--K,v4upך *qM"#Җ u֠ Zc%3 <ƌ< jmx#O8ȃH3`:qGW\hd(ڟvt1w6m>ێV<®?ƪUUG/eCb~Q̄×Ӱ0ժ6[iUǯb:rsjF̦-ZHcrobzlte$`5DNAta/brí{|NFKB*i;~*ͷCpڠ <'c5͡"bpBosmeyM)AI`ĵ-ݱheH`ľl,p@}L_KRe ê8哢6B,w`{ ,Y_]_OIMf=l$PYCr$]l ?L 2f5̹yẕ]:n;[+3LUS4Օ [4QsNbzmbo SsTrO7iJ"-ҍ? :u'pumo%jy.>C,Ttb<7Zy?ИYkRbi*̾k?N0a˩JIuCYk/"*ti?(#Unc,H-#a~~OBy«.:nI;b:eDWvokageiUe!Yy_:8Y" p">\*,COʴE$UXt ;lM2r r#׽{odSftYK^2C"S6e%-na_Rij)h,j$bW:l-ok`lJ10C#H6*SRT4 xd%R;kPJPQYLcO*xZ  4vdhs ]Ҕ5Bݬ,݈؝`Ql1,~MDE'J{ܷZ,:бPv\|$,y Ukv6UjUT=u;6^ǥM\Ylx~a˯Ҋ zɹ"`\UiW 7؝LH,U"1ÂR3 e@#DSQ#[wZ:H(2 E= -*?{툑U|8Nt4}rcVD1򖥬 ]E<Ʀ:U2c{_z1hE+MPWK_nیL~eٝ •lj{A멭Xi\ִRB[bS*d HB,zqD4$igco}ԬUҮ*lA (asa6[QJGJ#*Bm-w *s4hsUʭ0ؓW[EaY,8Jk$A)?{ìXvV"+s,0D--+,X=wccykg&XWS߮)yTI9HfA!,:.;8s\/+޽ bQ=Gޥ=% $`M'z4tg9KI*cw;kw#I5E-cURSҴYuPMKbgU&YcF@Ymbb][y;e4մy[9H$2RIF,Sz3h뒨&RY P67JxaY$uNEcqfY6o MI5rxj&U5 $ܑf0ƀSQmU_ [H:"yntm|.V'ֲ 1f ܭo+-(!df(c_Jq}b}/U(bSL"u'$q$yO.V"vF,傄i.?{a3>a4)MN!_1$f7P DrCPAbbVKd zH{/)i)E4;/Q{&V{ XY_C RѬPNbQA걽%z'405O%3_{{dtQ2e MPwI-|Tf٬$M3TRL"4pi]!{'{ 4D-%I=Ree Tr'h21ku8js*ɚiY~XܜLaZ#QM8S1_b)O9sAV"@-P"FrH$zT榦&M"IdGUb81tuLF["ݴ7Ŵ90V(iڕmCҸ0_Q*ZB@>,|3jYeM!0'kwĖ6j qU$f;C#&a>YqOoĶae8L|k}W-}d%)*X$QbAku=f$4G-:e`z ;_g1J@;#)c7aIQKK&u5VS Ԁ|A>`B@,mn+Zz%*SBH۰mQ3fxɧfuUvNZ.&Pv 5Qnep_TI_5-bHLqAh*,@o1i(]0Ďvjz9Y)$ר]Y7~|IչR$P_*z`m{q82ܺ:x)^{myweYMmI` ž85+FʸLj$)*&({ص{= SSGI9 .T'q rfQym=E5l e4*Ņb0gec8L'M \w\CoI-4<{H{*`wExJSo9+4jyF0G{bSj442hT5ͯsrO_ .YAUUħ12L-ab=dT\g\gdkk-mTE(}Ld:̲U19h(ON5\ߦK=M e)54hQ M[<,Ryp,Y{V;X]dQSIi(jE)#@ ;PÙTNi%I Tl`Đ_'lhkr-:ř|~6&Xu%5TR5lIK0*鉝fwO: 6P{&E5cAiܬ%.1LqA-Ϋcn13jP--]\Kv8q6a>Dl<1fD4IƱ_@2Td'->w ̼s/4pʮE5v-8X{*#U_ķ S%iӈ`)hSMtV v&Ҿ?JiH ˑhM}7bm{b3\U̩en)+}b7r*7 (s #ļ:n̽d.q^HX!`o~p'xcCnITk.rr )6x,)XʲƮ&D@4Kza>IJz]us36zM _p7m؂by )pz*iSyBUHhRXI,Ez␓6Υ妔$JM$/ۺbJⳈ"/'U(һH- zFwۦ*%\Fb;؎G4`(6J09? 5dL pDQ}mo6qcĪ5Zyjj%I$@P@ڢP&閎$XJKMBۖ sXef)*P蜆p{Dz⌴u8nmSGTL/b::F]ΌWF.}\גT4%Mbw IɽAr^I%J) ֚6bcEﺶ)4G8zhS@Xq-:^S7鉿dyl[|Jcgo/Q/*$+QO9rA GQa0%/euh/ MےH)z,:8HU@B:`A惠K+pIEkf)w/#g1THC4{|B2< `Ip [mjr*jRIu6?L=%I*0FZۀz{wY2jx/n4,;[b*G4 Ӟn9J*i˓11?NovA@Kw,iZ4 %T\d{qK,+Ϳ[zE ceLy 26A}`1ReuJs*ͩ*fcVQ~̜,c­ 1$t2IN$+kfRMavٌc U9g pњj+`y%@n-O Te:Ѽ.l..L[jkO1H;t©xrfS$T,Ӧ }1l4G;]zr\1s3(L?hcˌ_p.wG.]s'(^H@lXu= ly(ږ95ڄPO|?҉YY$h!fm[t7TUܪU>gD<Ц+"*ċ0kXy= *SSQA;GI5Vd, z[*w-h&f}eIn|3 e˫b)BӸR!K_NRTSVqJ IA$l?Q1w2rJyf_G *n dž"ZdYi+ϧ18>h8V(+j*:Tw!:>s Y?ye_OAU<tITvG*B/B}" YyA*Ů9V4Jn wcc5yr?3PNߥ=N]QAZhHÄFI!EKLP08mqsErkӪJbRU,@$*NuØhs䦤 c,Wœ7IY {^npᓫi]'?+53>HYʲcSs)M--g#5XrS`>i9gu2[;xi28)+6xdgx`I鞶M>/u^0NŘE+jI_F]{-l`9Yy)*$p)dgXLSF[}0 }GX͛iYťb,uw~%.4ǖeU!<&T3ʠTu~ؙExeq@] TIK4A'clX̩KTIOR]mzUbC{cl33_4fdZǿ_ u)hqXjn= 45k\7cp' }Tq A;"]aZ\> TLk 㑋FYlwk8g9Ud2D>.#lK7y'СK6C܍C=MVuD Uyk $WҤE9 rV#eny"1X25\j$ c|?S,QTSdeH܁?]Cٽ\A CFUdY{oO)5XKuҺ?UOj-!+BĹIQIݞ2xZ*u":Tt26!lwnm`1cg2jip0[Nos3\F؎'˔dƓ)͘WåL<N!Dp>^6?l7/sqMep vk _K_co1jꊉDiҜ;@\bf2ub:#F>ork~ؠ.6BuSUop+)Oư.lop()奪JRk8C#}S$k1HxJd2(&i`xk]kwE#\>W̅Lq4+Q-J(R:l[8#&L?GJZI<ƒE)i8z0]Foq{`Vʱ  |\gt7ښ3ɖ!HoL3H3S{נs*5%L`w[} Fy)&%)'SngQx7 =7 '1̞hRJk o☦k*^a=A *z1;t08hG#Ա~15UmRVH[ʛI F U WEu%_劕Mx),݊b9_CO%BJCv]NWq+2q macQG9%C2sP/v&9ƲDkyw})jF$"] 1cKӚʹM{ !'n0I7ʓ 21m!csp?|Ls9%EW,;2w*I鰽{iȌS,oh%I%f+yݖI%h{Y5OMϊI 樞;*GT>RH6ڊs"u7u?\bf[[O6TJS`7SWEMNG]^&m7Ή2{,sXom$FWQCٯC*z_J ٍ|F\=0uxhfYhsIX\(7cbjXA&ӽKI3[Y&Ե ƽQ#K[(l6,˸</#7N|v76r:7mO)2hIOLG9a2=vm Лt(W H^igUFhو$u ,0i\-emX2S_,yke}̏3UWfb&W7;lnn cԱMiiFV3&j5{$ رbN" I*GdNʚL|ZR|&I Fk4ѩiIt]l,t$,DR]S:2)v,#2iZC.\io{uQe#%@ͫevpS+;s&5eZc4(eu(T&x8饙ZIࠆ2HդtRqT5m74CğK-9 m'7-5%0 x tD2$ ؘAsXz^dU![ jBH#1d`bΤb%]Q%Ɩr:`, WJ!sI-銇6>MuGK4Cdcn|cz)"2P"[mĪka]Kƍ)SS YC] B^I)]؏~==rU2Wh 5Pm(]n6;c (R YkdIK 72m$EH]B~'M5TOW"׭Y6; |1|OIKjn!ʙ֦w$m0幖a9gU@佽wcd*f;}n'`dĕyRC^:Xbl,n:A_-zP|S|H삺P,cgbl>'|N(e#ɂI%M]zH!LTVHj܀:v"%RXbaLV[}.Na$Q`됶|O93/ĨEH(FعQrKTTpF<4ֲKk{tƾ)jspG(M~y73FG7?pG::`뀶NO"RYj#؀>p-u)Gҥ5TR嘱-oolkթMM&Z9jfP= q_ zI0 EJoal>o$\&1ن7u$wY3Y9Ulflz\m9U[Rѡ6s7ۯl3M)$*4eUtWv1u7z~i%_U&bsVHL*칏&$y|k r<͡fӯ M.! dIbE,`dlp38󆆞$ a#m{9Op59=#S.ăƺT^cO-I,e\3 9\B5T.@Podg{,hK :>US40,5kar?rg t+/)M+ǩdHtƿ(S*ԇ_(k_'HSO-:&]boa OvhL"+,O*zWKtR!$/q5:CM”GEJLX3^OmK.^PhYܛ_ WRдdpI3|NgMb,՛2Y J\R,L-?I'PI %$FJ&iM19bOЏp l)">#bĘzWXs Ju\ާ 6Y"k%֥H_Hcl2<~xH,)T@* D ]#<̂UF]WMNNs/_sII gcY_ijC=JO$BX_}D^ (a*Fb eܫQ雙ETհh],u1jIw>c\- sNVH 7 c4gV2yF[7 ~%0F@ 'SO3ȇ.n8Ükf? Dq\O&-£̒ gCb 10rcLwF')~ !t0L☗rYE9XmkS O^ RJx.,_V)}~8vcBP{ f*Ydr+FM3inަS;`k$k{+ר἞4<@0MeR2GSҬ![Pz7'RpȪ:dYPWØr\̬ r2=O;bTyf7Fa`$Iljr)u(UWj:t+"zzבٖ{W^2+qޱ+o-Q6 `jb7}p/#.G$rU:0,۷ok X2a:SZ ;nmu8XT25]cΛZ`xRV<1偖BEuߠ7`39,P|1W ;.őUɾmj85=8,ޡ?'bа4VxTUqaPF$Q$+{Xmꗦ:Qt.[kG:JSI-?yW\ʤ: %_J<f5 `Xq=SϟUT[9LE4 TuE*Quqfj &%]=2T",m#&΋euAv >#h2JNafԪFH66$ܓs|<:礪_;Uw\,AMbUii)}$=kuæT3ƱRp7'~ضц.j2#eiY)@U u,fvѼaHس<#[Ȱ:b]:p>0CMY$2,okKnRf/~ BZtflz:|̪ kbk_ +OO+MO>zZQҋ~v|XLH0T6|T :[xq2c7%KxdLgص2WLT $ap$R41De(m*|A}T \W<1UO<94eFORN)hpspUm\ \JF>ec$h,yY_kGr/%EZ'!n{~3.S j:pTqG_m#c{:P'ʲ^kTp.K{i_C>cR+RpT[#Ɗv5۶)ʐKp)0۫9o3Y3eVkж ul|Esڦ6[d̳<5وسռwRd,X-@=7[H9'z? <(%lbH`B*F@vM!\,%EMoP'BƌHu=>gQϾpQ~K"7&R"cQ*ț_-ULCN;:IԾ5<4H#<99dXgn#lI*!85rpMO#j5dXY`\m~OxૹiV$J vlZO9*㥏⚈5Dy$!_MXq>I-w/(-*W'"Ӱ70j6xtg\F; B% &n*ʎu~gSϜJ"'1*FE^çc8ޮ-#dZW)v65:J*j8;z\f2.Mmnߦ"+r76hvN|W2īF+=S~'3QKsZ 4=8>yMQ\V"զ|X.6\<,KH=jI#m@YUqxTM>aQGo@A ,HYK\/|JsnuxE5*"8I(6Ѷsj3*n2Z.x֟(e%m^[ky3zMITµNI2pwv;}p;+mw|l07bڧ%r2nm֩ʣGS؁p/a?u6e6Gfe`.Yvn1xttgP)1=J=ŜY*$hF.8mGLi p𢉘جďv͞Ew$®ha ,jFl?q8o#+s NbGTJ(HX6 ׿SD̓(%|<9e[ ^sk5W3ZZlL2I܍qIOhAqE-_i76r&YTS"/k qOg^/9KS~ ]wA-dHXHzk:i͈[TY*Kb?%E`y"D*x3뚚dhIѻ-XGg'凙\0J qeR o ۆ?sO/f:2=4a*ӹ }4(~Z#9϶Ǧ Cn` uYD1d.lORlװ6`0x$||Ii!u^&#$˦8 Hl6GZCQ$,I;mq6*8_X"a uN<9u$P4uqϗ#yA}'k0x֗rxVGlV t]#mfVK-F]3/Ʀsrm FeI_=CE'$u'^nYac1 D,_Jr^ hxEXzzw?LSq-U?pTҘ"ʲ ĀuQ~Ʊ<ӎ3'Vl9ԚL5I`_X.wi3VEę%\22hƆ=@/okdi1c6qWU.t>]UqVwYP%Õ'sG,=ֺS;Bb \P |܃,mwĖsDYD*Q`6$]mXvơ3 yކLʡ%Z&n'G+ʝ娒*g=Nsbݭb|9Q2`6񥼭pGS&gdb#5S*quԺMq]|F9=44p=HUYe}lEI E($l29|5, "82j382hC;ql=PnD}g{=U|KQ3Rf2P:Ey;J(I%W(z~Cf c.@KF y::>*,-ERKXHyƔMjC-6#c|J+*m,607'ŋү:yyAn^ʏN: .z[(_k?CSS>mLyucm$i#"Jb}[mYU_"RGGQQ?* h _n؟!8]v4xrh3'gZeWK Y@'OoZ #:iDMAvnqUB.+Ӵ)Q)]rËsDJQD`ŷo檫.^~2hZXaJ4}(Q_QO)|CfɒBQR",V,rd0Ue\1:IkF ;,U*dllnY.yQ;r㘃UFEA{٣}Mokm{VYį)<d$C$07k[z*LjQ_YZm^KPQጏ.2s2{=nX8V/ͮV.J$y_[,6">ɒ*.㪈%LUMN*YNq%t2=/xLjeN^Nb qv'leɗ;'ORR߇,DapllH鎫(a&m:$:v;RDnwէnKX=VG,*IobTIOO#. 6 }EbPS7,|FV$}2J|0O`39{ .a^'4w @WR7{GW=D镉zJ<цv 1'('湑ZȖTf/~bOm~錕i9lRPZ) q7g^[ygM 11EB~]AfE1Jd!S Y^'zm/)$hٚvsb  RU=\nwHHS(\WWSz7M]nw 6늗(, 5ȧEMm+ qo|޷7SV+T@GS(MK Fî7𵂆vDfuߚJkNmlkjϴyG~|T5MwZ?'L%Pf:(*Mͣ%nBpMoxy#xQIQ2uq\WF#< 뵰|be1Į} [{*bcddVԸ9W G7TCQUQ &Y|[i|#>$d\E>g=̊e0xb3ʺ']8g6PÒRC3hTIDmv\uDsgCOEǗU#-yx^V;Skk*ְī)IfCz:GR@;hui7 A0ō̥ZcfEu$5Xv q[C x~<NS*ʪ`mqszlv+(<'rꪦ:y뒶wHI)#卆SʲGQh[LІ|pV i.6cM< '0^Z5*CÒkM\YwoL+t [s_%I>,"k^KY"T~t^|1~8Dr5?6'_T2}X\e5(+qxT=;2|ֽg0Fbh#xvjnKD 6@ A IUã9X$GcQ@qIM aR@&{p4H#D0TFi~ؓ>3yL2!;"?>lӐ0 [t2q0ڇ?*"RďQeznxiiF%$0l He+%Xs-;YSL=kY^A+3]lq4譵*2" b-Տ|,EQpEXzbKc`XQC+ˁ"!p`{c_[Q<Ӻ>5uKl;4QT,Up G)Me I~l:,׋FO8?t-}AK0/o^jSOnjV__UJrGn]rh1'q?.1e7paTKcRa`ڼ`:1!k'7%@3SQ9V l6;[u'û®w&x8BtVP-@Rv;amF.eRJ8kÙqXˍҵ|65 +&[/`/U]fUUGVI Э #n6K*0%ҁKtuJ܏}r3*gLTcI'ZШeG! \7ࣤA`Hl,6LIk^ s:Ñ!-L #,@ [?J12 *Op%f(^!k59XLXH sƧ 4\U6V+,IS[n>sd(:G&uhն+ٙD@Zj]ྮnHp 糆G_{QCYL97L2ltn5Xښ04aT!VYRtfpj>^ "!̀/1O`=/Wl1l.HMs ʣݱiYJ ? 4H~}ͭvP+ppڵ=SO.aE_Է`vݰ\n W9b@umck("] [kb<ӎQ&Bx!5nQI_0[\134q] f^sM*=.H^ Ғ}+ T3d&߅~WO=>s!-~ O1,-c AO)6 }(PLA+8Y|pBlp3m. geZ'Tr/JHBAH| gǦEqcY@qr|n*zuf0ƤRk-D\77+a|MoloB9&y ^} .udK}mbSs:L4'?N"ܗQE;- vz90氅=Mp=@[m0L{Cl }6E|X4nUA9{3I1W\(B5?*Xl@)o'46I%V):q F Beǡ[NmT*\gǵ)>V E[OjLqc-ml5(y4w Oq :`N$u 6EV 2RVEg36?Y䢎8\/~Jg98dq$KX~κ> _MOSæéͷGEyh7b+Vd87$(34m1D4`.j=>|ÔsT]E,m~\_wymu>"7P~4\H"UM.]^f'MZ݆% w (!;r?p?A_=D\M[TCS@(-kbX_5Le= 87Qq_KDQ/BXvCşbon!jc# hkv͆a8Kw+j3e(H ۳1pK^bS< 5Dm HQbnXY;@f(yV6v=RePi<-'c96gREm$u&xUS]KQBUY:Hu X)ښ.LL|nliRv۶:9JTj!nmǥˠ2B=2 9ϱm\9ߊߜD:v*LzsRU 2B):\:Z2 t(LFH4 FRA@.{ou9'7hm?ÕK,"aH9'Ml$mV84KzT}VʤCc~f 4`_ug60Ё#OW&) 4g X؛p~OoV*\PTH4*W77鎽TjV 7,.m9d ڂUk~Y>RSGSMZIӶ#C؊F*@qKHD'n il \S oU8cĄ ^km3%jr4 4VRwԪʢ)b@^èREaBE>*ϙAo%TuBdin|ݤjFn{~XcXŽ]:z-Žy;~%Xđ:i8 D{5yl>4,yܮBí%=>)qd =T {?ux覈$Z$v[<E, m^Wr~?лyi{2q&>g.$DaMqә'O> ұہ Åߖү>ۂ>#|v)EK#]щ}^l-r 07߮E/pq_Sˎz$\u`''74=ƪ)Бi{1ztRDм1b4ߩb$#xNVMqr֖yLc7囀ݽ7o&?*)j9ѲLPp qص؞7 g6轅J,K+hm7uK2yV.w+ãPS$R )->ЊZIꃄfK2Xm~}h DZݱ2hѽ*M1_XMSIo;ih\'sI"*oU/RMƻplEpé=\*ZT-G YMXK|u$ɭQl{{`kҰ=6}k5?`io%r?íᗺUs;L.50ݿõ~xq$(y|W@I1ɢ ƊR$77`haT0D>ŴvfS2ܮWhaW,5 eMTRt8[ {(zrh3lqhy*ufM:E)kRKñA4I\Yghal "$I+~}CaP~ς2,(5Ao<`}[9|!^`5& ツ;펰%C. cb1eQ46餁`,pfYGu<6r>uqdhamTu9+' +óˌOHxz45#M_ %G[قX 4-v)ԼȰEPFuʷTx"y#i23] %7.e !$H#&>;H_Mȿ1|$^`>]b X iUXNL|Fu]^NT@Tч{mTj?qsTR4ɤ6f7|u=; N6*/aN\DNyX/m̅[=`feSCMAS=-&T4KS͞=LBy$ZIP۸BOAS#I,Q\MmĴi]|ͷo 1D \ǿ|2P~>iGE$#P!ߧlL>j$9OO䩩l֘7H펚iƅ4\]_Fq:G![`~9U' //^3<fP2¤`Q.|<G5*YP)#u@ܨNrċ77UIXXo6[OPB3v2o4 GhHyK%|l$qgsJXobe]U=%5<<ʊͩ2Ԏ,-p7;UsrcǒFZΦ^߾Ռ5ёӀQ<(RGR&GŮ-f3o G:~bI$M'Z5ݤT=ĜdUt}6@P C9~~ s(XDqV׹_~iV_QAM.a%"$.&M[ߦO2jP>M;v톓p?*#S+P { 0\p? Uu6uBɗ"PK嶿[}-| <#p䂋2/SJWR}B#}G~ (Yno 6KM$\^̑.m^nHRIv+6˩8gT7ܞצ7ZN--B) 6l89LՀɺvcM2+S*)w 遌#:|d@K/?sI[,GGUO*]rs 8_P^xz"%2q4+ (_n8|;uȈ'HÓD1 n}"אVK]L MC= 0E7UXrmm|Z> $Ti'Q$gt'2y& x7 tw&MBG JIWp/߽l)EO5q[ Vv̒})[AZ~>٩yRh"ϥ,v$w؛G^XT2%Lamqda%a7| ҩԬD ÜT' `; DO(i$1M =ہl\^1|#r3y+bbre7Ǩ[{؟b1H`y:`Jz.ANcI"@;86IH. z(/GJc0MeP%eL੪|9Z -{}bC s u؂m#c5ӵ`10-O6|kK/eiZ|ƛ1苗+XcV8:Yw-uNK@c,r9BJabg?hфG b Ñ*kw{FpƋNfFσ.CL ,H+ªKwxR]PH99Śp>mI32ba-tmD=mtU]vE{ss-WkePZ `w6=LnTodWr,uc8Ȣ)xPx^goJcopmMeCHO4x7Wuro*j_ׅƚU%wHXGE%ٴvsRb!ܥΑa±aIQl0l* Å&i5^R(3y6c;鋩߄G]tyKŕ˝ Y*]O o{|oF,4B ƧQI%"QCkr&54zL颎wZʥI>+rnW ήeDh (|^|%T? et׀*$L4nM?\f!ܻ&9Ll>`:X:!y|U{ʔ`gec1$ FWI$ Y"os/&v")gT-2v6d? ± w Ø:hcKWBP77]k4vXg?"]мJz,lmbIr!Yh>lcM*m85D`ܐI ݲ aXxV~~8".\GC'&I晢UUrTֽ2p< N(߸=#԰wF ľo }o͍c, 6CN=O9ÏoCGQN WyfK6'{‚URy3ÑTO)e );WS}FԑÅjk 5}o|Ev5~;-_ NW\1YPs?!kP$/ ޵ØWTf Q,QӰ,:{^Nhe}j nRmԏk<-K)JjdXkr\erqhܭ$V'+6ZRR9͘g.E\(&AN yUJUY˖Gk_7}K5n= F#)έa|H8+2P ls|&< pTTo/ͤ!\dWUҠeŭ`U? O RSQpWƜ ,N&\2 n;zjToʟ"EyHd[761|{~̔ V^\ϕzٌ,!#>㫭CQ:otB5.\ LXP$GT&񘤍il>dmmFe,di )&YQU%z<յ auiᎂOdY Yik^X;WbLJTNa? .Kmfln%!Qu 4xM׷T9r͢H@{:olY_o14Yuv}ynvVA IpTjp DsӾpZ|ǫ)'VVͨk{Ll`wlћatCOU=/ɘ)?FG7(y!.cה|;Q6SqggZ5jE䅌$6 ărokJ~ULŁ#YQAÑcH=Khac6b#}gtLZ#o(䝌5EWP}NO]CM$<*I+S6iQ pXYYṵ SĉN@NߦA:q;g_rSOo[W<rld,w0p!rXt=If?yTW(*'zi*8PH"Um"t`F'pڻS< t~5KeNH &m̲6yN1DEOq]ptPHETJi$(CPv{:_r)E ?;YKk]܋@+Ʋ0,H}mQHmP,o9fhkY?C-n]}@gn1UOijypٮ4I6O'L50Mmj*`B;j,oֶ7 S=E+jeRړCOG D@!I$ߧ|,ՙ#k}Fk@ZKOW,ٝ4'icƒ:&M~:e0㖠2Z@[ue}UcM$ќ ҊSUfMTA-o'ᝒr,9ni]ʤ(vulvGR 2VjHr;"W$s*wX+8fLl*vfz{l/qI3;/&q>g<˳n*d_6Xcy<ҡ2,GnF;jOxW' rfxE̾g9jEC3&S}G? rdM\EgiβH$P*Rlߡ#-oUqR847YΞM&Uˌگx3zʲXi(#|jv*zjwXj)fUjK_ss!6kc}sN 4(D,[[cKy]fKY?TS){\tPW I 6hTB֚˩2Ee IZ4a^ Ր4IZE2`:؈l4xhbVRP:ty !*&A; /*H@KG`TҘE ;Kv O,sQPvj,rĀl@o Mx3Ht`4{\t7SJYLRCzi{)mЌZe-ChbH#S-t1;1x!*Gr=›}|J*irjLbWk8ǔfUdtRi^z"m]7k,\y4Ԑ5tRV NTRZsZ̯4gz Ğ9c^I}mlLs̥_01R-P:aMA_GNjHK6ok1ѝͮx/!Ωޡ+YicY=Cv!]䵌9-)_1y4'b[~o.5\,8k W.IIFqhCCb_U)`l$d;+SA[]X%Ii$)LzE7`ks< xo?F)#f ˭[>kfI lOAJPkv÷|t3𦊎-!(mXm> tt{SyE d2#VݯaZ<$mK9H%%UeB߿N\Nυ$UYm$`swvVš*Sl+MbdNXqǔc@5*ȈwbcjUcNeZlZnseiBUbZ"ĘM;\?/?:ʗ\u@)O])!X i3™1x26z5Ud"U o}Xxz>v$uP#WNRokؿ oϖtUUy\Y~vfJr⌝~6NWw8( e;0yJ4D åM^tzzO SR) )MvS$OI$NJ[n7^Sǥ0SJ;}\>)8!mAz#G0ebmOz,%+t#cWe6j1;,!IFg~G LapGm~%TmT3PQ%Ep2WVr*AE aF [_ i#!K ~tD`t{0YX ۡ 4Mk![^c DK Qtۢkoňk)dh1If9ikUPClHge@s -\F"mS߮ kV$)!VVbLR\aR:M\-PEPt<;#K3##.ցMՓr݉8*i!y Y^);{vî\Hc&%N?7 (;~"BF\8 AΆS{/{& ʥAqacY47#hW~[-sǸi]QJZڋӾd,g c|Be5EL>y<@/~2"#,EX>vbEgi|V;HXoIFU3Ơi&:l0Z6XRa 0o8\MFS8(I>{1;oH5 [vnyI`ʬ W7ӂG NvEiªH qrmKAm{\`KKV`-Se&B,ڶR {a)n./$G_Ez-h4m9H+8HzеmEzm(SdJ4sPewV `QnMT&o,bAlvÃNeP_S(mowi|J NnX.܁d_J{l!n[t4rAٖ58ܩ_ <&L);܇_ti.݅g}ai[8WHA-#c`m BBTm ڝ݁Aubw+IszP5"Y P: {RCjnzZY 6S{EX(W[AbŅ`@vn֢(s8Z=7POSCxŘ " Q,C;$+lQC/ӥ2UIت8 XSJF0k>؍fUVA"v,, VC: ͅ#k(Z0 ٍ1a[`up*n{0]e"M M#bNL9-װ$tu^ Jlmv -LNz۾ omqT3[)%n{}&WQ7$@U, (y1eXʆEŖ&Omgy`K % ԚrŖ%&˰tıUřE蛆50j_WMCp~H!G[#).@ pLZ.I>tMhJ. Bo.ϧo:ȬIUQϿhg$U ,oMO, 9s5Q}35YeKM"a۾6=gy/QMenQU Y *E8#q$<ߖAUm\'P/XX8؟!|q wfq^(Q.aRf&@$H?-gVSnUDE \RF9"a l{-s+MQ^ Zz7ˢ2Gk0cM `E7qG\]SG,.E)wbUHT_m^o"G}j+?#$S.PNO_:48#B6RE0鈽+V=0;"z.M12F' ]'U1|&ځ*;o~L{ُ`RF,kT3RQf_˼#P#,bvX 鵆hUJ +:_1R1ٕEh< m:F{U[S8*>ߘ̦۞l6)㔬݉:(DXƍXzJq`pii,nN%3$m !rȴ`|U X$ekY 2\[+ ;2ܾX` $RDĕ~DWAwE pSBEAF;'= LHs$:Qq$vYK:J !'`qOLa"J T2,V<@1l:_h cm9IT9ʡ4Ƥv&(g$-oӧخd* 7% @Ɔ,}@ ԉmT eDE=?L7Kػo6[p =xFi?NhQ%պLUI˵1d,}Nw~hԑ U[ nM (c^(c"%1m%>Ujt_6F7wܑlWN(;= -lGk" _+PNPNA}VF4JljuVYaiiKABN;un@qLP-$lP1+zЮ#,$UH8kih6IQG"Sh@2[WO O lֱ4@؍[17t؛bP\ |`8<53dČa #XnSS?p JE>ZSUVOQ'b,-P0I- rSU'`YOaЁ:U-Sn" kp$6TT "[x &s 4$nr*!7;N!t-J{ BreAXfrJܧ2+'dBI]KmqJ\ͯ}b*H̄+AgER泾vJbtڀc4>c]ͩz:0Đ*[H`norS^ oNd-ŕ̰RLUQtӹbYǖ5[|: s.m[:GM=njcӖb,t]Ed= $^USN3ȐǢ5 MOqrwqK5+>\i"3MOX{ 7i⩒Рoĺ ySR,h_U] 튊^ٮ[BnqffRLUb6NSBX `/ asA6i߶9x/|fsbw \ 83ꊈiL9 K*(tN9rs8/pB{pin.u"F%jmJmnYUb[["䢅I7}?.JTq^8ND|p:}OuZKMP]SB!o=GlYoF=~8q, 1F: aO9;O1~:s{o$6TQSW%Y|->5E?|9]N54|GXAE%8xF}$O%|Neյ,y]NWIDU%NQ|c#Tq6m +ݠ,!bHߨ dq,t&xE̾iLbK*4?%EQHiw-al͸eC38M<]Pտ HQZDm]7ЈByJ\k:n iC#%K*˦%Ǥ0+8QA+z@T©{labє ){*n#{[*XJc^Dj% yV H&!NaaӨlnMF :aJ[Qf:kR6M0FB UOApmHB3^+zEØ*+P;ޟnHu>2Y EfP.é4Q/C' {}W^"#^HawѵHEۣ 3S@#%\y%55.K7! JƪbUOLA#+ )*[Qu7\$Pe! v[_Q5]"LPض:߶ c.ހHN&iI UBV,\s Fۺ :z =0غ:uoo ̋v[ʨVAX\=`EkBF JK7 LM{p(>/" }2bU_B3]Jyb"@'ƥ](l}%AJrh }*!sl>D,ᜩcotfݓZ]= ۉ MȬB+NvKjv;'%B#ĎZޅi1ҧ' 5Ҭmv`h-u6؜eEk&ڌJ\&^Km2;:1Ԋ$aĥiAp޳{~`@At|/S3$i%z-7LwXrj-k{_H\iW#%ܰ %Ui o`aR:,ͨ -b1Զ}Rʐ9פ˸y }RYt}ET , g5}y72E7E'1XʩML, fCb-am cch]}i[=>AApwB,oHcΒH #V\D%Nn aYv!H`uRt8]%EKmrKC$ݵ_{"i `Ci:m$a+Ж JUX7H)Tbsa>`5Qt[i2Rpt$3jX|(Xк؂`dv7\wLɯSbdF7,,4p>nmқY,4M@V>K6.om0Q+uo>O,߸\qB{ Kew77`&n 'G^IE{n߾J(_,|#E@턱F.܁5As jRK[K~Z<&9Cl R!Rob1U#`6r2Gjpsknm*4Bak^ Vfb#+4D%wo]{)$Օ>`b/&ʲ98R `>afidCPVʱ}=N"if0XGIT7@Q@ O`l-OCu;}0!K!*\MҤBQ 2*c[ro@@ ^؆u+\t [ie$nlL:Dog!k߶g.Zt-y&hm]YZo"hǙ }N BUtYʡ}u{\Jgt, -q)݋X u u#؁[#uueVy>VOµ'IiCA\{FgQ3M$F3A) !+@SkKG݁w5$/S}c`l=Ͼ0(yCUΗi7bi7u^?(ka:CRBtkawK$+!X ~HTI  $6[0opޢ)f[oqG{[VE28===퇍Ă}#o}LCu$| Ցα-wnaď1ͩm48/ϟ"GA|YY~'lȀ"q`OLm~}Bj1{xk4\瑣3H ^-Se8s6c=V99P9Qts[;Yg-q_TMIRQ,m(U ׸{adb2 |EQ=^IK:·x~ttSMOW,4 RUp@7߯鎛|xALK9 e1*L3JU8F ܞoa]l!ppYcFt<ФNpfqEIр)j^Qq-O|-8%trGso¼7f=c0H"0 4u[· G9:< tQ}' X 59 \UIܜ3'D l>ؿƩq;Lě20CuNx,9bK/ qY(86k1q;Gkطxgg5^W)MD.F'?s,8ϚgÔuWWQLFIt/$y'qf3B|kJRW 5:l^8DQ2[ܳYWv\ GyOCp)"]6_5a/n76ߦ=RgQ-Ef&I+~I40Ufy]EHj3DI巰=; 0ῆ:ϸ Z|&mX(ɶ&20{LD'tosa-T|R|vLxg 5uy?c;Ɍ}eKMd1p0}{ =qabyTr^bK!p-ax HD!ڞ1mHC78t%`DCI0wB& @K]dX*WW lz (gܐc$ 6(. |xCGco *coQovUpT "mtT7fӶ*ʣe!pPŖ4u?õcb[seC&di+_KÔ8V%6$S;}'qˣqfӠ\_0Y!ݥl.@=-i2-6&=-uhcScVv-‹/F!{ktq E%~f\80)6`M, o|;`X  E\4Ʋ:Q6v]B\e:tXQ vbN> `X,QY-mhlFэ#A{D1m#r@V"0Q+}s+97\ON?IRQ {^Flm~Qę5d>aIm }+UC%O +L9s  3<~!eqf`.Y OFUe:P,lktK◑|I4T|Jq[-I/$тNH6ױ$v4/ 8xdwr$) M0$)}|U 죛s8%ʳ]3AA(I 7!/튦HH}k\RCؓl,*/Apizhn/cVÁ;tkX  ލF)81u%,ɰ PAcvN!66툕GU6 ucӾi ֽn`ԇMQV%ɱld%N\X#fa'0J{G076VmM"~e̗# ܥ&P%z=.u7q*ސ6V8p&QHTe[”mJQIck [ #}ۄ6#3(\$WH8$ol 1#7ÛSphP^4M ew؃{z-S ݆#]RW˿` vQ}`I΁B /(T X[?)שo J?{JY_MER/qw®m ʓb7+`,F ia kn0xج4ۓ#F,{]W PM&ZT THtMt6tì/uYC $7R䘔]NGe퉂/̫G\(6)`9<<; бg`=>bj;aY.S"fQ`IۧRamBi:t-ÈYjl:`L-&$ O#O4DutkߧazvU$d@*Y56HmkvIv\`-awb*M[huROt+,Dh]V(5ݬ0mA;~p".=H4D,bT|ad9?7^ 7p:bVv}'0q$[K_aLsEI } đ`bG|{Y{):b7|L\|j8KQydoCHk~ x'>w2-x3,JBVWyS -rfܵ%l,L0H[#6ƪ:8oNQWd@VA st"x JA 2N[kc\>,r#SIJ4UT-D,noʾ0^pQMOePDņڽǾ1ŶWUQgԯ1$iEcpHȸ8ac{n:ZG!dCݾ|8ȣ"1dZAEh@؛fT-[M7FKx(by0G=}u8dY݃a|)W'u oe kT;e(Xz-XM'^8.G`WUx@HbrT#`X~$OfhD+.Vl!]We:tXc#U NeR@76``eo@7kn YE5`J6 q.s}"~4+t]8WV*5n2꾦)tzIٽ0&1(˧Iרm,#~.v MhxØ0eFlBF-f'8֖N D5Xu<4_;F+ء.:x*-U\,E5Y}^y_FWebXN2GxR4'bolPܴCE_G!jXߠ?N1anH8UB4TfѷwN%)vM†-vC{`  t7>t4z G$a~덗A%Qu~p$r4"p(#F,Q؋OOF/ycҁH&Yqz\Y̺b PKL0tƤh֯rE9'gk'{g)VkM(},C G=?5N= /2F ,jĆ3[`_LP;q,`%E"םp*X),tc7.#)Z%N'x/3Ƌ')"/Q]]04OrmRs>nWʧJ`qk$Eqpl #mu5ܣ4z>*Sf9'2ji*h#d(mp~_qK=%0ᤇꞱj867Ҩ@0oě7^?˲jhkY$"J#hZ*q|a<8ׄZJz\j)DASgeS_ h^y)Jld *o-TT=9'YUęu\ib6:٭p5x,3?RORΕ6HNjkpI7닩3N67ZC8fԆ-Yt.spG,8s1θ24K-׷_8yW^qYSgfYA-CN%c`ZAs3`x/崁K.evYMY/eI;砧~(䜾FZ:)${T㲀١^Ue|=TTj\[3[Wׇ\p3IMp 5T.v]hY%PcagV{(Q@(0K \o{cr1 !+qI);JO8'63.hp ՙ/BKxߚM>~,^s',.H@*)D3->gyPEYC_FbDJpGolh~ Rd-96e02CTAc \tEM$P MK=Sn,;L|;n~ wV<Ȫ3*hx&8"0N{C8N.|ūN:,mli,4h2ijl6V2;ffên8K.Ns07"e腆o9u 7ϊ,,z:MfY,4J;◂X1jlT1%?\0Y,Cx98 ⼒:i*zZ[ Q>q1~hnɩ ouUUNXBw7V[1PT%yᯐ(eg{wbg>m~`gEN˩-eC=BG[ o3'O2\!BTKYXTB"7,NAq|1SFDM"~_ 02Jd7;$.cl])ĭ"\|7<Qs+9V[_DcXٴ4aPu; |bx+.U (&N;/o *%u3D@6bE52 wz,JMە$:_SnOKʀHkۡ5KYiCeupeK2aqfSqUC;6@`x5<-x ]U`3Yw?Rfm!a8MFa]Eou/bJPoj`؛f`ͧl*IP(I$lV ?%Ts`,0cְ4_H`2Vرo\,#S0+p՞AkQҤ8mʽl9k!,$pvrGi./] ySvōEEiH(*#`ܡGQJ?4>k _67FUU#$Rn ch7=ٵkXȩ`p Z%e[^L("00D@&K51Tryj5Of hۭUP2i#rp7Jr}@Z{p!~ֱ;_l7yi1~ M͈=A15 C+RRB0iTƑa]NpA,EF z}[Ծa(z-@$[1-N | %Vu*TomR\@o"{^C[wqEG. -l#yQˮY婔'd\1BQ*HKvnqqcw=eu .r7KхҫI}CqoklB aH^AA 'IPV߶P /`l,- m5.O $Sh H]K P{0n"?l-قVsN ETmԤ~rM9 ]Nu|-{$/ Vդ0ĸ}XK{pُlx^Jo{M'($(\!)WCwƳ2:a]؁`/lcFFf3i#/t[v5 *lz Zn+w6OUqoi!W84OR*2.NezY*8<B8N1Z1߆0kunWV}Eg9GrAd Ho`z8`VʶX׃0:%aUː,D :BO,- Ŵ= -J'h!39eIA;3g[pEae`:&f#Q `(Q6?λ,ҁݙ[0XR(,_a,{cǷb`7[_ qXK6ZX[Jݭb< VDoX[e%N?0+ $[Yel,mFM@[H׭t؝p@,jb!BE%u,NT󛐠ۿԠ$ !uً3m}ʋ0%gp.6/ m=R8RC w?8\!g}D[ 2D BYdQMfU;^+dpd%HHNgG KU<ķ. _:cNj'jZhI,mop.y\+JmL*Z=6c%;*e vǶ GyuU%u<@H@ ۧWYTQ=LPAD鵀{GbK@W͂dzaZ6@RicSMR0$20&QECQC%U>q6*`(%*2e!W,Dm1Ia9`Yk8\7dP~|6&Ye!ɵ`,n,z*ZΔgJybb |0ZZAOX(up-2)rQ+86bM%0Fէ)%O?l&jTUΓ`Xa4S!<:d1$Ps6Όg0Q =0T:tq?2L™Z ZIX)As4UZSV1Bgt!>6:c^GAɎa,QTή`F餍csupuIQAGMNLbFor AsyM}BȔDk!0hVupXM?+DGR=l5ʲo\LQE/5=͉M[cLj_R^U-N#p!л[~7|7M%O\TCPOJ^c_WY#UҠj Kcώ}U/;g/ZydysSp]X$:eI%0fs [mNVx*f2:0fWES#~x$7-2X䬊DS3O,F4`7[tlpAȞ&I"Vk@XyXk/Yd'p^4,+ EЈ Xo|wUBxI܋kEW]ffa"Yc$!'aSsO=D0T$ouQHƽWl'`DV!bIa@ ʣko`:ǣYWBA|:I m#0wiH5wcbϤV%4~I,pLĩ(znoJw;O 3UQe_=Yz=Jc_l@3uqs0|ؗSK$,op{a;t]Ĉu/dHoLo`/ǖf}kQl/Rv{?\ovmNKLsTQ X: F T t^`&Qk@ ۜz9$H!SINؐ8@&" .m#4H૯)ƭ;`|7w?LLt#SY-T*: [lwRRg>X@[NRtM_d1Ig%1+SE l"42X[oe"lk$!AO`A$WTkj;w.aegRlz،B8̝%Bn nlńMY{g 3uS_4ʮƁR+8tf"alIZkD %@1"XEF(,TRuxtX|#pX݂N,1[;QmNn]w3+-{<`҅4^ 2[  +X0%nl\44%*Y1`ޣ\[$G\,/bza ZL?,J)G$`t E!5-)&^<9J4.1aP-[q`phA*T_a_-o-NN|1kJ H5kZUBl2k1wk1z+^ghP({=$c W [\:m pF#Uuw0V!J9V(Xj0U@C6ITCJ Ԏ`}%A?ֵsZl&,fTEf> lAQӵ01K)*@A(tW~`ۣ, :z!=V)& vd2 Y=`+jO]BPrluƻmSfچP:{\Ed@d_$ @$`Fw>)*$4E/qEoUz.ow dHX| q55)#PNO\ 9,{b2Է?_DP*V}#-8D ;6K&53uNP57C)B/oW@#ܚbꌠkܝp/R%RNzcf}-P $la*2H qqc͡]ԢᘞĦ=P\l. $:G}oCI jK(:lrҎ,FyǕKk] _rG{bK}91H͜+ V xFBꓡ*jRGPA'?[`'a0aL 왒 ]["s*ȗqfp]c;jb{N 'tU;2r}'*9wSRf2LΫ*12Uvɰ69GkWfhyQ?WS8X`/n댣lfhHHhӮ̦|r>rİWM]~/j|:+\{WtGqLB_eQ"qo.2Y}-탼Sy`!A7l0hpgs J qXۦ ", a'A[8;vbPq. ($(WG ck\̅٤"!{#A{cSFX|okcN a*4+b l;( `MOh(酮V!Hӷ‡8#uC|m>V`u vF2X)b{ar ؛^"]Ԑ =m|9riQDCJjp/#f% 3N:ؖbޭW?3p=鄇r+sk Hd}[LDhcrpV\7tԺ2LRD >[4|,H$!˦(Iـ&펶xÅD1J?Lqc|qf<8J> LC8fY}]ÙfFݠ]Tb9Ys[S-RT͹);lְʆD@[۱Ĝ2bu$u]l]M|.AˮTaeYirz2 B IcBl6 n_{]QGt@0LzlK[sCNuuYVpz_`|n.!x7pS]`\qKHŊ}.]ZbLr*@a+qFUGso# PD4!uNVKNMP3?0 y)4f,-@~?(W4ܿ 26"[M}y=qjX5='TդnHI[՞ #>Jj54ruce[{{i G*w4P;Fpx^1 吀#'t7KHǮ+- M1y.x`IvYG&b]-?fTTP)kWw7ǯTSFG,A^ =`(pUty8m#yt+7~l/?÷įV?2XOO?4!A3zpls qCW%CA4`H#"vgu UM=T]Q*c/\qی3 dp%nmLbUd,e0r}b'nqo:_q ]Gm05RSˉfw3K H6km//xWW.gQKS4f1G=팡%,PͰW- H ޤ~!)HH8ѴAMDRޢ5aX*J1د;Zok1wnyCʀ_JbЪFo3lZ{E%u"_lUYrn:l}ZHg^[+yn20=A{ta50 ;IVpя2OJ"` ʍbjͳe(2d6aݢM4FYCG+Dxׇxx.l‰ Rp%Rl@H#Q9^_m2NWUM%W,Ul#YeU mȱ6į'׈fEP_\wr*gbe!#u ln[/ <9py~8ϋ 0yEOQV2}#N.ť!4.Kp Dɫ.+~IxeȮ5^ng9LMÜG1\GHI500lom=7|rNW3s0qs@XҔm[2o  \e-rΤH"xb#+({x| 5uSOfbDqKܕlo+ëٰ\/#K4Nx_l||Q\3,̿ʫrď/Vwt~1sΛ1Υjj8QT%g#`x2_Wüͳj H*iU[H Cdk3gWT4=ݾe+*#LűNS;dPo ܊ṡ?✥ซk嬒ADDJ}MMle}pIM\/gPH4r619ĉ$1 g .Olm(bn[qJڟ6Y\_r~w.^“sM}tZbUNOAev[(2h)fL5:^Oͳ'N(.:냚F}7،FtAL RWr45Ò)W$mKuMsbTPJ 6M>ط\wg_t\EUqZنOOPub56DḷM&E䱞ͭT5XMc#ˏTK03 u퉍ՂW m")P0$tyxLYl~0*6p5#=:w fg`w%96Y`Xؙ!%oIZ'A-mBKIXOpqO? wN)'lί&~SΆ@6civ ;YQAORa9*Mte#}Jv8-V G?*X(&wo+/ ^/9.NJridIsFJat "n%|(I3Aۮ0Wu6+f~s_yI[ɲq ƶ,!2/a0P76c@r\o?S|3Ydg%3|t̮|[Zڷ8LG1]/D|V9vhԲ,ي<[;Mc<1ۆʞYcl F] 틥~^/qedU_P{667Ӈb%F UcukwWJd %uzEg*Km9<3q%VcEsG&Io(ot.)88[G"8:yLϜ$>Nܗ _k6)$oqYj6k|'~"Jeu827:u ] Heap1QD h *,G| )b4!UHA7{ kv3"&'ٵÈͣpWGPtSoP:aooV{ᑙ5UAm6)9i)}BR@B ‘4ZEl!T8nmTYv2%IJ0\1iS*Wr@EqFIE"E=LI,emW9ŜǑL+M1k` }Ŭd+-I.]~`4Zo6YM] ePoek g^F]Avv@RT\ol)]]7lEK ;{Fo9ufg_U_IK <%f= %̌ R9"<;8Zfy+D`,/,/-׍x#3c 9su.i刴 d`UaxeU͊ -++)Hӹ&oАВڋ3y_臣Z 0D&I;RH5 F&,@n?k!P}M،HarPXH* pJƍ!@GHcmJ@էs(Ԥ BoK7fn[nc@RbdNH`ы#kb$F ّGnAJݣW["nmgUPk;0WS 5-~Qta bH"Yn؛ r׸>!+&ӧpm(l/C>BV5-.VI'ế-d[o,,㍾YGQ~*z:S薑v: wAY296V,Lq NA>b)ȳP3b&ca霆j@xsQVs.Xᥓ9Ȫslh|i`秡|t(ʧEbMJڗ51qVE⧓[VVe=ITrLH˛,FumɠMJq튞 Y%au8,(3<,Nkb8+`X?^!BSPӱmJ90#5SxbJqiH g(FnƯGN{,SWIT-PL{-%5J)$RXۨzjxE=7Io{{bY{oۺTxZjĢH%!yGrkeٍLqGP K)Scԃ+38(CI)FĨpC7{#`.X1_!'u9!.䦨ɦ꩞w) obv=a-mTTB?m=00AݖvmAi( 32y"kQ~&TMSQ]%DIk uD~lYāZ\kʙRdqR0ͰF[][%DRR6o`q!+SMS`3p (6λ213 5 u$FmsXn"U/r~7V5 PLC-'b^c_ &[Ü˩iE6,d%؛bKcVy <k#adJ/Rv(o;4/_3J r4նd] Uvt8sp`b݉8wU$\(Y ;av10b;~<X]~<}u|KWIeNdDZۦR:R:Qnqűpք:8Nj\0{Ɛ$)rߦ,V8Z˹msF"K꺡wLM!YMgC0JUb#<bX-r}i`cxz*DZpuȐ-I+vBY N0U,QP>ŕBv'g 0Wkk8u4,u} 4icȪI؉kVv]1"bH }ISj:m284yi꽯hOC":E+{;`8<`6h@B%N@)t4M".{qp q3M u߶EUo)v?\*9F.A# дzXv?]Hpgdk.YXmúr$A]c#'r(0`#z:R2Y!v!/RbmZ&@BP5|%T\P_"|f\^LX Q7P [PMǑTp-pN[wh`8$t&5dx/QH/*  [p;p\ fʽO @\`6o[{[_._fFwwHJ*ݻT2$Uұ ne #o|8I_-;;uXz@ ^7K,}{Y>SnU, $ 8AykOCVӬ p7t$UBA2~c ap O |%}N#,J iQ[rq\l,bTtk\#Tp"$mrͶnjcO0y,ϕ[v\>JA?uqLNOF6sAwo<<**PeكQزP o,}']yWSVi̯!ŷ${c0<5BxJAt3p̃4n{T,]#&k7n :㰏 _o ~:qpNC2ZVx!Ѿ$ϩ!%4|lR6.^_갗Ň2y\>-.!)}GHօdu[H]͈F1Dw P#$V_E}5( KIEJƊ*퉦dYm69βYf#O#.&{RWwmӧa6aq{ q̲فBhajXdR$ .= ZJjH\Ɛ6Ҷ 7=,]%`;mr.TԲ= idB5{meʝ`i'OljIzEa1PXT2I{Qe/gzZU-u .fpMkXHka- 6)U ɄRqW M' 2;],/ql% 1 N<cR}NG{yB5 tġm.,M1➖*z*hh&:so=x\RQNdv`zkmwqCuJ8&Թ =}K^秶*&]TCO MymK_H7X}1f4_ûDHBno:?l|Ƣ82otsL:-mLYRcJmB#<ǒ .aA/\i<6y6E,/FLHO@O|_(2yNoH {}w|B0-Hnc Z6DqXc폞8\Mxc>$<=nԥu4Ӹ1axkaӤw[NB}R<%o=ڦ ꒢&Wz GHrFTi ':*StOQpRUx)|u>X7$^l`[KI%PM*`Ѿ79mo/Uqz57SEU IVІs4e E'}O'V:+2GGܮ>iwA,qkb"Ue` zP (as]?ù\(tj,*-hg k[U},RF]@ c}Bu>huakDS/@Q:j0klfyEnX|:@4ݯvvƐedvN5[p|0\F 5[*ŏ~{ &2=@ٍS]p <2֚"Kܓ|8JEbmr_ LFp.  _WbBL#}X,JaS`H˩BCk]C4LJB|50mL lT@  -1P&u퉩LS8{ \ȲqN+vE3*P1d$X5ar;#Xf6p֓uy©].(P$ Hc |&k}pTGPH˹cG6]qөQTU<;,H) :ٶ PK@ϰ= v mbA}$P۔ED]e)!> P@5$Za *#v 7Y#X4X lޢն"!MuUye t8㚱9gƐ$Rl ] FMԟ|X/|I򷊹Re9<`W%vk/|+)$f}O|NNx\94e/1̪+*d*D^ usPSS",ͧs sw%qx}#XI"Jkn֊7vGf|?OkHލM}*Tܞ|r sO. %3>3If <d/cJJYjL$18 1squ#ۨ/ e3(*f :9@m7>.6+ehrw'h䕜?Z4 C_9^I4*||SE nN:zzD}:;5@93z׍*wSQSʋ mq651f\=CóOI2@eI lYaqGosv+e] x 4bI;X{`2&yk4ԹSVSRdqd s؍sxͥxϚ+R93:I ZB0SnE\\#Qq"͹ZS ei˷Cլ{XG >װ\G=e+~>_ dJxuf?桳 ДkHu(PukkkXų\7Crky-s4Y7uż#R!̟' rxg8㼺MKs_Yԏǵ0VXTy\"ڀJk[V',T!A)Lncf8{0Sg +>hr$χsWfRQ3GxM"u q avӁ <5 " w*Yan _J>'4tIG (+k}00x|˙STji˨G;$H.EàYll ^W-H`5REK,eFPnqbl5vM*b7%K}F:Q{N K2herc09|k1MD*d&XčaV(v[ǭ3RbhhO4QjqmUͩTHYTNe}F7$%wli"ɹiT8ht nl7}~&)!2.sBbMw\xv-? ռWG|鲮*@sܡZ pgSuIm'ξ#ߙ*X)*$"Y! lEt_OWs鳮cae\FR*ɭeCmZAq߾1O SKS| \?-Qd/wU m׾(p*rl36rLE\Aw6W.|_ Eče5us$ le`n ؞x7Ug!5>as h0u m@SK鶑utRӫ,ηW-lM9ji1uKltmr#o4<"g/<ck9s 򺌡-Xف`Uoe(mo0FmnG3&ip)b-񃻍"5  Z*YY mPȺ?AL4"0>U} zz2uŲܭ}3>8(&*nˢN*~] 7*Uv8؝`p7Wؽa{5nyM&Y!į(h܏1-ÈbTj1kœ""40OE2I_TgM1#wTt1ԩqɠc`HC+X#|5" M¡ Cjb,@}o8yf;j\Xߧ< ^ `I%0BXX ;,NT1‹} aщ#2*:,dW>`T6a5o+ 2e/A6[(=pa;ZXdH[.\H II+f[_ab֚ <@G7~mGfA44Q>3Nfp4:)a@@v;~s>y3ί,.(.-Y92>J::ʂuE9 1 RvMDŽn#•5@j@VC77]-~HU2ۊ$XaK*)=qHC=S@OBT6\s$jܞ5#H$F3 aDL%m)̈́VfkjUJS>u8O[y Jk7cpLFSǮy O]8+ hWU , c3]XFzmuThs#KqUӾa =6Q2u;9yy!eԲj$*ecODz9^2fF2 mkl =lsZdz*R8"{qsEmVcfq+RК$ž+ܰ?vۿLL+gX+$,a9T)+.FRhWDSW+-IB񑠵؎{OUSҤ+!*G3݅XçMFYZkeXk1$ca^-ο XGȍ-Z67 %Zp{Z򖒾)#AE$1F士猘MBl-sgrӥCP*d"5p܁8^i5ԕ-Nٌ+4Sơ\p,A}q+cxS Ibh\lYmÓ?}d`6Ds+O2HueYO& _TaTOz!\"Io{~ovkUKQP,pj=TM/Sg/jܰIme'cH"k܁ߺ0DⲈL/I,HΤo>|xebA$@mBI%14KF>yLsY$TVPSAeHVKHq*B7편bh%mV5$c>k\CKT$/O 4#>ۃ+.l/t9,-omuJ_31=,S DM,\;bb:5nMW *K%ùZIi 9`6!%o, %~o׿|z)V9"`}qjAN9БYuKDܩ6oT%zT2U Ǝ6>c׸.N& mǯP$]  \VJ9)\d|ĺyQz8eMX+G0Kaƥj-YX_ʎ%}kR<++!W:`%E OgQ!q\~mTHXx;e$.}Ț hO_2@ eb0Gr&sU K,r"Na(@KnXki%YI #3"Ȯe]tDbUahБ Gv}VlQۥTѦU,w PJZoÐPRJ= lݖHT{t%K*n\۷4PQ{$bk!2H'Ŀ7V8 Bn5 KS)Tb cS3I>QՓVyLct$qY >g\v{aJ1άf(۷.'*xU21),6c'Cܟqn g4x8:LjdjLfUEb:̪8^ 5K=%؂ڔ[{c||(2*j_k197:NI"[_|;xxg ~ω7㻻|mȥtr/CӴU2+ZŷO3,*y_NPi6Z+3i#Hp"Uv%B\D5Ok嗺;wz⯨)',ѹI: x}~ZjE$t15S$r S~#P &[c *[Pc7 ӾTnJlMT!e]ͺ'~CJFޫ.b96PŐ`:u,MOu#}^zxGs@/.Ľ 0b Z@brF!Ey%YMal7Ip_Q-*+ѺEE߮d:0 `Iibi}6): Bo}MS[QR`t?,66=IH w# 0 j'vaqKYlHp@FHJ'f4Kt7A!t N̋k*UP˺ZK(H4 |{ 4o)TK&$bu0"O {26*?OlĻH{0hU"gbm{܋ɚщ|flHH ] $~\"?'@qqղB 6؄bpg]&t0Yu]%PBK* T]P/emb7$$%=b)Pp ([MJ\m{}pKeb j}JwSЌkiM<)x*UU-L'!666 ]SֺGCGu'1ϋ? !~'>"7r'/j<8ˊ(e IK;;؀[= "5hڷ 1Rd~܅f<0'&x6Y#S;1cU|t#D>K;A̒*̗u1.I,.zm<'3<2xP2'9LQM%%e"g# ^Q-鍚\Fع\-O0C?x/w xS&ʸ2ZU(U Ҋ,-(N0(꾖v6}73$+TTT8GB|@qC\?l% r6zK텪JfioS싢Hm׭5WU K[6]Dɸ=7@(E$Qi(#Hky3iFA> ۦ(cE }Lt&)krN"XbB-Fř&ݰKY8Q5=lR3. Sp?̱aA,INl},S2 6fMUƖ;u-fy%l>˞ld(&P=Mmem+oGq,b? 75+̬CGv*{M˖R)+:H*lSGɎGAi8VpUYePV 1:ib_ٸ$HMB\uuk^FKXϩ|y˗fB0dBI `-fR6 Ñ}Ck\tǁpWÀM)2KocO'*kndwKYY$H$*xۥלŕ߿|ssJ~FY<EԊud[wHomֺMS<Y YqkeِJv#7X rEk29\!d.Wu$m[+yE2/ñ۾ q_Li-CًUʰ :R9IL7⻯̐r2[پ:8_x[#xޛ4y*3EơJ+[u:P:uYy>QYY,!^Y7`oE|Tf59ɜKyF[Uem _s||ƦY`1|?Y77[9r>7k@fԴ|7SV Lַ[2 74 7_ۥǧ7Jp,r^GgAS癯!`bJbGVح&x+.Y4V-K17E=}%tsFkT4`Iv[!ɕo2zZXf (g=mnxL 3:!_/Ð.E[-@oÉ,5C-mkNJ)]4좮G(PF݉7'ʾps{UA1ͪ>gÕO1U,TK8*QUU'R?_Lʩ'.$P• G9o&xofqM~3.7༛#ֿ!,SQ|؞枢)d_)."Kp[ܜm8|m) /8L} fa+O}J>RWvX)!ං\c˯ï`C}sM|^<+a1diT=J.—}uDo{3m@cݮXH5*ޮ؊;9v.۵톸;$@J)dm`OrqMPsVi\vkyOo+3d+X))h3RIi ~؉4 ϚjPq *8jr//`|=WƵ5ંmxɩ s}FO g 'qOl/gJJx,1NvUs~43,gԡg9<З}#mbd\{<˳&{[K;W.-GG h vH5IV~-1W*ʸ NyA‰,I#Q ׌lsғrސA&\d! ;2Z1"wbavaǤ+IAdRFl (k~;M{m jB]YBk)Lsa< P0nY-,IbpÄ*ký&I]AFƏR(Q:Zpdݱ@d v\j/c4n%#*sţ8ٺohfv ! AǼq 5Yy|43n̄_q OS-,cc+J //O#0.32btXiQ~I￶ެN3$s2*s[5^dtyU<;Ĕ2XGkXGzQ-✄NT-Бss53m؎ ꙻ+0b.@܂ yN<ԕ! [,Y]sҤN68R˫CK=pN)2,3訨2W*MN6fAW"~_GW۬_>Xr_̰5 PȊIԤk[ce O-u\,2(ͅw/OįRzW0g+'nqڣ(!< H|ts^ؤg8&󌮢: ʝepz}X_ˉm\lq7 Ums% G#F.ӫ|;22h|7KF=.־% P]낍@YD: SVEq^\3-4d!M_僤NXc ) c&S,6)E 3yA=WXv=Aq0km(NقiPD=( c.Z룑--L.2 a;P]tPP'{k Uja̒3,.;@Hd``1JR,ml ['.<ƣ 6pT!wmٖWRpGnī!Ն_NXʙ#BꑖouREr?Nؖ;)Yu9 Iﶢ:4dDRXqgc2snF rl IjC}@u n9I$YorooV,ޛ`BFbájTPlݭqT(IVT 2췳X)$1 LVPb 0pT+m7t RlGCn:3I\t7SkBq#vfuqsa#k 0$* 6w6تm 6;"i܀ĂO &FV$j|X`bÂ,ېFl,(#bUE`$lq<3k$K=0ݼ)ajK>R$i,\.CWKVWfVmC&bRp7 [<+E&IJgH -|x`7Gok¬k924HKAp6M k BO}`m0nXF \=z>"8,~tu 9_4 vcYdy\L L0jp=G~GϷ0xb6t\H&Υ= 7TϨPunz|R3F[i PO_{tN4asr{|8^,WYnm爙A:5E`/bFr~:D:TEQy C;dz} w$ɉ,F^JjBu\ [})c4N_Ad$o|srA%8=: <,MFJ)Xmkth&P3$ec~%{ɩH#A,m `Y!P]@EǧHSp8)2Z3{z# B(긵*]~Zp$t$f:Vw?L ixD$ :Ew[$kxi66q,`C\,d.bX1$p yoetb"<2BŲ7=G|#]MRhVYXnvsϯ_*Js Xm`څ]wDmPm=#|]p7K_ 8W*鿓Fb>z%ʞj:kUFіB#j_NN"W6kW4lUX@nv=·(jEOfA4Q/pEջl?LnG¯Ng$2:V$X 6ZϚ$p>&’Y1&? xY},.@&t d%t;T@ZL i`Bm=:NŠ:Yw{E*l-$[@}D;I(VPNk̒:"Eq2&Y)PR1I*R;i,H@GTu{%THE~L<܄#P% u]}+aa1DBܑ,{{ +[uYDK~HTb?#y HR7cFTzMEKY nW}pE5#F%b!C4`ZsihPALsL,T0U܂ \.4$$b4X8t^+f8˱OѱQ9ײp`F? &`GX_,Aٷ=Bn{&FKweU?4a>RzElY,/k,yulck^, n /q$,BJfX8*jlGl:Ni.6AbI gZjokc1$.0@Α;#郱nR;1~CGQqjpj M!'fJ =0ٙ@\t xH뀉j-m6mTSI gc`O_ϐ]B7Řpe5OI92JJ+@Q{r@ :*Ө Ħ h&cJK4O3@lS/U,gJV\ g_~c\.)o\>-Feaj/zHѬ6:ltm׮6dlT1tG7=,l:lVZ>~e`7PȖNOO|:C!xтVǶ%Tr=D?In ;%'E30YqsTϘ+ z# IwpY2,:'%8S/ d;M6ο#gI'uO~l=+ҟz0SgMB,C\ZUE)|fȣEW[ _alXsy Io@K !A80x0kn=_S#zZK\؞_xx]VeW8S ZƠMbzⲯ]r.[خ3ѥۇo51wOG8dբrTo q.5o ˚PnXMBbmbƩrJi2~;2I^⢨e^I,5s Sss˳+n㼥UW"HmXc^|]ޏ*|p ߅I. oh ˤ+*aМkӝ\ICu_Ҩu9U9)a YMAظLJ.Vnm,=>^&{Mscb:[QPI~\`y*EE؋1\oH E3h͕N|i/Ṛ֟~*5>]qU6K2a^lʍ4ĭS$BJ6`(t#`t4CnR2t'ç~"Fr]l9,7bZ'%"ق!){; 81c%a؆`Ǯ%KU$Xz[NԡnnlDsdMVC*zueEXq)fGyt8MD p3)hrʐVWҫ~PʄXL%2ek۶`Ick\^ ,] Ed$UeCz/ciuoam#J|4Yg E4~".Xzi@FBuusqqO "ĨoR[j7DbҫAkUmLm)#򞈗#I}’5;` k)&zjK0B@ͷ0m;yV3v+2ke7مk\1 .ʲl꧑~yӯ 5U)8}hPb Mkjb^IZ?Vۖ5j߷.,>/~,|' s)X u{⪏'sĨɥvqMJ yYyӾc(rEUKOR Q#(a]KJS'/ݴ7tVju+ l?e$ .cʖSMR5 UmIqJIjlI%$kK6A7 vcnOV49YddUkfq#c .|R?xnOWqNmlg'ŗq.`g7n_c_-ڌ54 vqu^QU\qMŞ ;2:s+ $KX(zA$cQ,CpL W\(@>Y~VMChA_ 2]4&6ko;~Ik#& ]9+i6%G Uk~8I ӺRY-V IR@! ePH#a$uWv30&:FN`blۋu8/ % k Y48]P6> /t#Q䩣=A2{̥ǧ0i^4"əSڥ#РkwcQtUFʝuB$}J7e|R ҀH],ۘZr9RJ͉_b%^ci.26Ý$(srrNS GO1:+&bsWbTPc{^ fr)f~;3$[Miv]QfߘYX]؟MaF .O{2ykMQp Q;'PHvqFܦcIEUyo/m$^'l`^y?%O>5m޶@\dSW;)VIkx BASY3ƐT"l;lN<^O,,˫W@oQ'JޢsAB9M6|%6t܀6 qUNhwE."enXmlesS8? f\)DZk3G+==S]'oc+r3n4}P]Z%;[f0va`P`FolKK^ !,#FF]G:ŬnBl!l/ V-܀.Wb>:P[Z|TPЌ&CAc8li|8q'Yܻ?QM["Hd뤋 f>]5 tQĠ`:(d%IywiU-Q配Q@w`.Ovŝe _fR,Gppp=(lk!>K8A،4(I)blkaE=-e=7Bt=^IY/xy`ȼmfܑ-H̀A;P/l 'Q<j/kI\( @ @uš;X=i$,^`knK{aPGku\1@EX1&(5sy&t8s'45sIj\cfoxЕUK77~n%PHt1|H+|I)qF) eQoktnNwp ^mTUUeeا,I \TxLmHj J7#~geyuM%|I!EP_鎝ԕp4  Y}Nd}:펫8챱Zt F^ ͞Lt>0~ XK]N,m߶3a5o8GRHEFI;no1#Hʌw6 "Ҵw7VPD=Jui7snF M$jgivouم"31*.Ezc녗VaFcMI=eYS{m/|Mp6o\AQRТuoLb{kf0B1 EvOuG1ziIdNNX{n S0Y6mw +>E_3b~vV&gs⿋!vɩdYVX3$Y`3_âj]QT+lN$7[{ؽ <Qr,xy>RKU:NI6o AK 9G0yAoqu޺x2'g%*XƓ#y=sS!<3tY' ee ޢ:ج!B#7ukgW̅N-e?*B_ǣ0:!m hkZK3˞M&E1, J9#*D;¦xQUlԡKn4T^ ВOYPZOPV!VKC\*Y| YT#)ÓT$7dVoHS+!'؝%UeV :U3eF+ $Gj^J 6tx鱲5ũ.cX!nOK?RYV_aa }dopH~ ~\HYd I-`Ė҆6"Ct"I7HoҪν^ nB(_) OG%_X,~h:pt[(]Ƕ+ welt~ eW (m2ƯKjıU>$R銑Iaq >Z0 VWudc{f 4;,Qn$UWy:)="Tev'넔]An[ dddD5\(V7R¤F!Ab561kg#Z|A[]V3K6Cs)$* V᭾X2Z@=]Va+e"˭؋=/ݲؔ;RTQA*#ۓn\;VEXX[dtlafk((a}=0ƥԪ=?lS]YvMWQW^ZxRޣ6Up.*et;삯;ϳFJJ7qfOBbyi 鐵÷SlTPHsaTkn}e[q0eAbrOT13w ̏/WY;ndǨfiğN_Gy Zf'T}-kk->oƔy_=ztyjoY,#~V6Pg|wSgZ*WIUmxb(i)) 1bjrYm6\ܹ=_#>'1lL*!MNvے>R-\C!# Cۦ&嘨U,$Q?|K1bc` 72uO 4 f/*T%Cw)k:cjf>!vd?"Lf4"$sbVb ,i YEI]X*ŷ#؅YEʒ5K/{ೱcrV\Ik,˔Ǜ%Y}J>\%ėmVfm\яP;7΁Ȩeb 29 z; 7誋`c1QoJ D VfO}c`,6Q ,ʪcdrɽaD;Y|K B5k]U{[}'F[v`` Zf#|Y"L \j eR5-r ܌OQ-T8^fn8Ubl:=*mŢN1.21͆^y%={H)Jb㵱v!F`\FH.S-Zۂ{ V;8Hb.-L!` N{`j Ν;Dd! w' u(}@Av;@=%Z H,Ȣ@ %vIhA*UU7~ca 2q`}+ՉYR2;)6*@g ַn%IVҤ,BQQk:\ ]T\nΒK.aJHFP\ɸ[~!ZU+vjvP $7Ɗs7AǹaͬܤБ/M6fXMZ8ds9f442AŦ0˦XrܙXdyd<Bg@,IVNdZ{c}a?´.T[Uɿ!WFHrK =K[ML/Vm/,IHH#p.T6cYT~8?Une~uA@D~TmkݍmK ,l9f _ʎ:_"zo1@e6`~/T1{w\5VdP:4Uv"7Yo33:bzYIx)Z6S ʑHIP F9yTS?:2ԄjJ `oĖ8Abg=XL%INa*Qܷ9s{ /鿪g=,I؏q dQI,cwŸ+*j8,ZcʏC=L27Exfe=zeξ/^YfC D *pֽ›nM=ox Swr5֋$v-LG{u֫ߥQmR&T2KZ(t|`LVJ򖤤{rğ_}rjW1ɪ(%~!YLFpAإ!fq\L_f+oo^+%r\MU߅2˪zzXHI$6=}^<pe)8*;,%,N:zN6ՅmaɮZq$k+qmER iTI.+`leJb<~Mǿܹ$yf5#z0CQ͵CG$x3 >\ЬJblq<6xs?|SưHeB,I#k~pQ>].[RZ)fX.^^ϑdp2@ײ]O%o;qky͎^Ok8ǙSIK 3MY?IV4sL kGE$Ǫژ@9@ 7rkG¼ѡ)OLd+%(#@s6{SreI`9kx_- 9K+zO(pvQ~w5rcxu%,WCf7}!$_cjyw^GO”\M,'Vq;I.be")f/ }-5[w)<1H=GGpWpR1'[M.۳r덂:xF[*Xd;]ee|bị e|2Krk 0kPicGn.$#Ңy ݩK@,QavMoqWj?TyuTsY5&RZݽ>aXcc&8>|#(260rmi(N$ 9 T8Kn R7 wTKUyimEQ5G+SyT垳+ \mu{Y# Rs<9YNUÔT,SPȭ'`f#Aozߑ|w+y™V]1Ο+'2fX`FKf}Y.>T[kT.Eu2zL!IS M\csu|\ܹ5>6ⓙ,o/XR]v`=qobg.d{mt~Nt[E"H4A`bm/SƜZܗ-'aOsUDR jkNجsI d55">>px>|ÚȸMɫ'-^ੲ 㙟Nωk_}'|S*VG$$1:2-_Ln7(<3Ⳙ|p ʣd HȤ lX_opg vOdòx8$ewLBh|Gfl% L2 SƳ57+辞b_G\l[:ht*ie˜c۔vUVd'7y~}Gy UpIh!,FˬP&7Cc/ï*׆9%KS>8i\Z)_]K-'E$㛜\ycx<)s)aS o}펌nEets/sOjeg9oPL!(V$-;' )h*%nwX߉ć ^XCMI4S]>gSS yvώizݚ7═*@5lcoOYskr*?)Y 4W1q-ЛP.|cxھQd<ϲ<+D?l,gq+ pos`\ qJ77儖tqx5]&[I.c&]T|e6]<.c}uMmn_B"4Lgk og0KyOe5ћf8F)jb擳OugM1;UŒT&?4db2_k nć!ϲ^%hs̨s3efT=I}AC H1 Ɓ ) mc9.UXNwÖWUX\m|3, F/rEbA nXBkˆ YXIԯa7S8*t&)eja PHۮ!B1 F0'a2)eH;I=1 nFFYuDORw8dˢFZr;(,cVI$t#N D(Ue%! ; c2螀J5 Y4)~搈8E4^iV_p>ƝW!B\}p2nRuC1Yl،)I(k *^@ag߾"s]~Zx/ϝi#fTEwPf{c?~R4S&I*:&w7*m c >-3~Ϊ^-)6PURĸ\eG)JBOŜ嵼SPLDZFk{Ox|sl׏Re.yJhm.WT&۠8K("v0),bm/ż\4[I%szxZiU*8C}Sci>u2 ٴ 2|bc"gY5!A[wJ2( U>4|=:lIcph<Ɩ:j5YT[ uC+ݲ/Vr[jZ 3(Ssoq|)mP{_yOXi)ܞZjPY qO2:.*c]Z(7=m7#ʾtG5>ymy'ƶ8S:n 5*Xs%g/zz)-E "< ,5G}0&/ '_a2B[|jx7@[~o>gN˥7 {iNu⃛P/iV$$|1c˳ +sjkQMT:w'Hb-ᑛ*۶3Of%7i]V[۠ʼnOH6g%|0F́؍os:lƱJZY)i@pM~fڦ*yBiFi#]ȵcF8=K~f52Dǂ5>'eEu=.OE,u9PGY&^92I53j_*bVͷQCpl5.`^c%%Z0+{銒-'u2%HWEK/׵]l{7TU)XrF{}:Ux9*CfLTQ%EǔZֵwg2E$٤R̠#җ(l 'e_5S[2F"U3kUaJc7=_W:rkPTSϡN6n~* Neq(E3ڦ2MOMqBd͘uDّqhiHv& Um6k4:ԅR 1N{<uf#}nvzhN#'kVcBWWW YxaqcOb]Wǜj!E]WK6fnJ8|'&qE=k,f!U o+h&:jrz<®kCV | źta0lˆJ$o.=\C3ꐼ-ArX`G1BRO=zST'mu|2L5|тX-Xv8TٝVdxٛEFޟG UMPA4D^Zdn;"FmI:@OZuʧ暭&@I 5Qsn߾*CuN3JZQ*$>^\n/ˌk"2KQRK;t.\2ʏ.l5L.Gl+iԟ/qu}|;>74 :m닉[ʎ+sPf bjQ"yEY\ MIEg&b}'9|ZQ PFy(B-b7۠7M&i=o)t>#I3]ǝ\pبeK1JV1YN..@7.:><1fyS9TG>w+ԛ~-MK7t9SbDi?o\ƿ~k i &?);\nňZژΙsFsG]2gHߎ;9 ϮCI-6kK>rRYHvKA6*NXs[Tq͕ ne1 K/f=y-lsf%3U+ cW֐OoFzU0$Kve,w)p={ s|&'F#p)reR@܌wm !}[i{vQdoSMP{u׳dYl7/Fw>PIۮ8qCŵ7s!ԵWO, Pm=iN'#ok\r>$3z, (gk}ѷ?oqCSQXٜ<;Dg +{\q>">Eweu+$^T*~kpc: !Hs9?p<$4]" ,onAƶL%ߚqmUUs#QDBtp0Φ6a4IZP7SЃ/lg Ay a5&Lz)i$yK,ZUN_<l5H([/LLUԅE66\lDD1FnS @ФS07hKso0J7|o. 45SD 6Ssmm|NnU edPLWt) Dj]V En7LIc#JE(>@_@$mq APԕwtDb#f 0L-+hEZ){/!:8.B i" 4DU@/16-厕;OmWTV!H\:ʫ+e)d̥+ju'r~_0EZHrѬ2:9*S)z[[m8IH*M3kO;-"~0i&ӡFzlo~p1׮[MzTCI=~GCAY<^( J_1]-`mejZuwJS8WWt ,:m8r>z"Fz\VBBk?[ UT>ŭLUcp^oPk%Hl6;9K0+sv\|/9}e;fRk@Y*Z}^~9J$2T<>`))Vtv|s8"nt-0\ ?SXeN Iji# -71~a0HCcuໜ<CV2~ ϣF54\m1П8o)GKÔ@Q2?\|yQ ߆)k2,33$+"m-;琼̡~;&o15/rE?X2,sJ,nd㕓Q+QPoy٘S4Ҡf=h5 I("3/P~ }E1ħS(j@6 ^~klMLC|+NՂNT5;+R]ip UI+S`Z nuy pб@7vq-3N.)uQ{\y)@%:Ӫ0;*I+4}A cqW:P>IVQ) ߧkbڀ?Յ-]M%1 k$BB {a=irJ܁0tr2Iޠ{n>uI7N1 Id2~R`ϸ}"nO{t\D"90b:14M,Ki>o8F[Ēm:zyÒ LUi3ua25Gm)X0*i"fkݺX#m{#K 'bc1KO[`GjF>b '0}>nM€Һ>oӦ#ƭsu?)Lj@.=@ 6i" 1 _ ea[aoP $ .d]GnG|5āpkB[k}p,H cn eE]T\;;ݐG1 yK:q2gt},g*-`mph]lWA6_U{%yHԗG]XAQت׭i"lA2"=n;V_)K& Y ژ R8T*ofӑn"a^v7;,bu_XR6R4R-bX-p1ʺ_L:p"h͑qkb-pFpE /ab,->ߦGElzM:I[UD._FkgR[`H;`5D*K$wٕrQ6|%Qu 7bo } "7w4PWw7ooHG!p6*5:EG6, &ȍjKQbNR.:K* o. !*Hp/{LDs{ap.hlRJ< 1eMz忘2:a:`PE1ISYRS"f%ԁGpnUMaiPccl"8ԫ0;F~s`>k)*Fb8`]+ 30-?LDNI5ۧbllU 4.(eUz9b\Y3M`PX|7e6X`"}tZވʶ yʥ4F-]Nݺຆu:"O&JJ"Ǯ" $!Dj6M,κAV#tD2MGVeMA|UhNZ aB^<" ];=IF%rzBGq (k ݱ7(+/KBvUwD.)"b[u9"#H$g+Wb;H%o\xw*_Q>p hS =<0~TzaۼqvPpTmC3 #cPbV`ʖ?;) 7 NuWNgI[i#&5e׹&p&鉍v'Q|y G`-Rzϥm4rVf |n"KbF0bț(k,Eh+k/oD}7e#8+ݰ0?0;, 7kF\HKtFrWҢÿcb@H` G.V>؁%PZ='tFe#$f߾j -Ԗb ! ]=BWs}*浏o%ĤTDwMX]="V܂ʿ.m'Њ}:Z_96B9'nwYYN@>^ KBď־ uy@n6FRGQ-̅B#̶ q~mLio_‡Pcr\,wBv~A"lβ;M ]H8Z kcb{P|7%v`vÛ,Q΍!Vu%(e͆[(,6C@dn!]}Ll0ψNwxUM@ B,ʀ4H];p (k3Z:h;H$Qw=7F̱9Uؐ]qSĕ5Q騁#Psce6ʲ.R(r(dx`Wl5՚pfcKF*1K{\k^lH[x^V(I)6۸&SsEحYv6[x-¸sC *ohw&2h+3.os+̲$b!\Alv[&:jZw 6Ex5]tpqMYJ^_ֈeR/X~ة)5@T8VesKg(2%|:3r$p̺;ob/s~%5Rx怤`B-Z਽b Vpc]q4Nfnn6c'ySDy95DѫRlJ\Ǟ =z:#`N֏^>_;ς_?^8Yϒu*C`ŷE٩nK?$F< Z6wf9K=Sg>3 \7o2s{۹ѳhQ$kg6$7csc>8?ヘ95vgx|E5`˝R *ώ^7ׁciW/­rZbyҴ|V9?4^lBYy#dg\%(eJ`X>cxB]"AqP?ObOx 4PfY,q La!Wӓr {`/>eH!mm-|9ʼљp _t|e&,C:Uu7UMI ,Ž|Heܳʫ)lz:1WȪPq{ο$VJޢǏ3}u>'I,NIl{$e+uTJ@F>l&N Y ʭZCY`6lzZwdO]HPk_Ruli=DAFn>F 9<%E;1ǥoG,0_l()%:13+kiH[`NC-K LYc5{tu&߾G;unN U`{LeSUm1}° %Ⱥ(|50 Uma_|/cӂ^ћk }^/|UWxqj.Eyq-MU@[~Lfʡd%aA ds0,s .x0JQSmrlQ}(en1ܮmՊEYoU|L83 JZLi.EPݾlm|gIǜ9qZe^&R\w8yE~^͒MGSVH]Bӭ|x\,IWeTq K),ePy+]s pL28ٮpgz-$ FoiBˊ["Ψ1BɩX*8ωYԣ)nluJf}9`F UOC+Ά')U5 鳇lqVcnwx\,%0n|01rޖ)ct|v⃎imn:ieLX*#+i{^o9䷅ s)<*3K?˳^k Q䍯\ϼ MTJ _S`q e?'fPQR9^QJLYdoI70߈h;A2OqK%5`-bY0PGᣕ)r DdUgTF^Il򌒎(hiM= Q JS1l}U}WVp-a7ZoٟR'cfCAȊF\XHibtIs"D# L"pCu[b oqoƫLٵY < 2ߝWDj3WsjIi $1`N$y>0c~Z@)[1! %Z- R#{$-z4 Z)) Ua0m+4 uSf۱Ƅ1|XrjC2]0`7'pm269u5 ZʚrmYFT9_ 9PrErڒ&I28V D7VK'VCxURRX8|Iӄ9#dL &4rl7,> [ct8cRxB ?)&hJ)iRF zSRtݤi2zz8 ,qADUYTl0  lw*? v" BnHi0GedܔC,g,"jC(E7< ~/ʮ:ɫ*,̨& yc&EV\6ޫ F (fsֶ̬%E|zoHS"Gf"$ `g5ߋ78ff9742"(ylX1c}V=,4aFğlS9.G=,33>!xl涁$GR0%5X_I12yO=kvn<.-y/s\˓:Eh#Xzzk 3\O<3~.ʨdAf|PXI`:'ѵ7;mhR!譀:xŬxJ:?ׇ_nPp%\&aL.IqƺmfH6[ro)w¼T4+EI UJҞ2K%}r⣓~8LW5%IY_B%uc-X6?avEae{"}QrAŐi^3"zZJLL.ډޒ}Ɠ38|'1yvqrfu1QRnm|`?/ ep_ďCS 2~ | t"h.Mt"V.dlU5ZD*.wq h)щȖi,Y׷]FJrAJ-7s8'M0Ӽ55->-|H|<;x\NQ?Jja#KC2Nxz+r,y}8zU}ICO$i>) aÂMƒ\>W=3|.lX! YBﷶ V ~d5I]VƲ6",rXQݕz Vf$m׵`$ة%P at\B3H K3r5:N \]J@@}D ɽ*{.ACnvÐMl4{+ij*(Pu)+`15)ړ@`,{b 1(:NF5,M[ }`6Y*IbK]QYɎoMr?l7d@^fdbɹ"XbWkǘbX67`{ U O d㍘#XjbR?2,-!D)R,I -[nad^q,jYfVF%I$Xpf:GewO<)SK>IR};DYQB7:| Wr7CYaIq%9]Q,4l7+r{2h? Q[sj(<*-:طd9d"!es ~$ fj&ۋp#t%5m;aQx؅ǧ C0C U\8tZ}*bTsEutUg-amZ|D+`nQkcO%ZppsENFUQQ7w8棞υ3&k:ZˮJ3omluP( )Ţ+ef0ƌh m~d𲪝ѿvizɄ :j{خX.9'z5J;z{\^ ֛# Ĺ{}m~.wrx+3'z)RUQ}6'YZ+X` k[JP{OzW썊?2VrGު)axcES(#IƠf5m|–i`wݝu\*sX!<>ey]ZЛwTYpS)Uؑn?ܳ˩%Xt`$}DO]{b9[%T9gS Q4ׯca#i`$H|bͫ|/*]rڇ7%-IzW'mB{[Nw)jI\ޭA9rVqgLmаV>t%*yU ̖'eonF}|BaBI}cmSU.1r,3)1ù&_dgbhjf4"-HEvcsloM2:eִFyFeGoN춚/F)/,p=5*.zIl*kj"2-k˼q5lEpWimWmaG4Pn;nZIvӊF8`L3BȐB.Iaߩ?/>Yᣗ.ruT Nl2)U׻) `E.7 \vSGJrF d+jRrAÔ,Z۶x)0dr556Qs낋/\-Ǿ.aD>m0e ᬢ}9T٢U6=Fþ&CMc(5[PU㋒!XuQ(N1&,*1VQSB,Ɓ \(n"1.1q5%[>e 1}֏3``r_U\XX[6%k*#:hCk%vfruXSk`XvC67al,buv7!,!NB$lp͑Dl!9 YfخJ.vnHu-QPYP#a Z/1ԐSRS*&K'Rm!vOMؑ,mqL䌵A!<߇sh/P@u;鵈XO+Jjy1wV"TmG|գC(]4|k3Uy>mpfuBQ@^sYB s7 >m )9+j6,,W+kVl-mZ7N1ͤ0''8NL8(_SM+z  lnӃjl!3J 1KLnI|GxƄUw"<]s홿YUVTtkilPرOR:`BB,,cf>yك&R/갽\CTde_iҢo ,886ۡmrv>l,3* EϤKZA yl(l@BΣt8Yp5!V  {{NDGF*lm t.H j:c4Ԍ ny`:NX[~KX.&25i4-v+XH&Z"a*Xݴr'vQGtUfAUQMQ?t]vn5jawܻUO%j"'(3k,z)Ԃ6(O8sx<⬿*ꖆOGEbbטmUmQF*eIQa|e/3R)2Ǘ0,9oe/FPN?˱8ieL=V^ap_ ɳ̩rc W` ('E yq,.GRGyl^YT sHީ.I麼*Jb/2۝l!bѠҥ>[ ݀r,16`(n[mKqH(%Zh&O)mۦ8NS95\ PpEYԴN)[|v~U4Jc7V>#=nq/xc+<9sS%\-h8JroC)ƣ>7ct8-NEC k~tqTQf9=6b39y%ŭ~qOw%2~ TTlqp7't⼦/!/˜Y%(Fn. Vu;⫗CM幄QXjB0V߱ԺC3xpT͈6vdSm,0""F$U2agled<Kl7*sFkqYSȫ+okǗH [Snj#fF:Fu30fq{_ Kе;(*fb]6aPHJtX:_s煆+cD@Y6 i8*) ik{ȋMM,"@Lq}[=4IkJ@w644 ʍ)"NW/%3)sXE؝\qbUu'rVet I 7a5|C,:c,Zָt &_H*z[x8~/qUrIT+*DkpRma{c  4K4Z!K6,?W֞#bI!XO= 6?.>~3r_))J~'v@,F˥xOE6HRdH0e[ 7XJw' jeIRX[Ib'+同ꞅ9ż)k;>@RrS40zn-F`m>c=cdeN!8cDr,X6'e&E[#2.4N.Z"~k'mia!8fKկqǼ%U*0 m7ռN1KiysF8ƳS IR$I#, UsnhnѦDR&Yfrҙe2V].6!m[l*VOc>lp(K>qC24Uwaumrk/%Bp)m0S2# %͈l(扌Vspb E(&Y1RRM[[*]8vl5DA*߽x8tt=O#fM^YS!cuC"#:+G;Udh :"З^-ָV)b=J kElҨS&്{_2 ,%}[mI P Op}.HXv@O{'`":U@!qI‰YԅT +ɵR٠ݽn :[RUkN;8`!-b)m@6_xôJM\3v%!\酊WhRhq%R4꿖{k1#(./`MjA#9U^V1dR?$2c+oﴋR`}%xS6o[zn6Gpـ~<Cr66!Z#P,~c5`uC$zm{`syUbp(a.(`B\wFKi$c`AWH5E%#IlA 6 62npcfdn,@vLkwOͧ]qsʡe,qY!]-kmYYAw;d_5T:BhΦ okvaFԤiQ=QN^Dm9+/3 ؋ <1mЃ|U.oL,]WbTFIGfTm{cs}|-@ʼn$պaяL p bX R:߾'11GE*UVMLEǪ 䶋p)Kk ,b 2GA `6:- =yFA‹B=Wm#S\ob/(qdY5i%bql[HPu' eV*7U $vݕ aq$V!%8 ) 6uxmJ#:mD+$n ۶H1ͲD9 qlB0 3)y>d p(vHX(%եU۷\ƸB4&Qr([C>/|#YOr"fgҊ Nܯ m|S"iE:ue!KF37Ķ 5"SѷmF8 ;Tjv.l#e'yAx)2onX@BH&l9(BlNqRdd2.t?\\FmWԥ< JnU3Y^1żrbRP0 ][Y 5 wq1C%ԉqHuarm9P..y#Eڵ\1- \ 8Z{lA*ޛ~Ҫ%ʝ7a:#pu0n.H,k @,Ni "k o}|yf;alBK@IHF"Ƥ0gj^ ( ޓ{X[큼Ě<@;l7rR; ,[ ].iaS xa{msXȈbQ؛ᦖp륺BeXM$_Xtg 녰o-vA&dA^2 {qEiT&Vq2}a iѺ#{>pСEFہü8P6)oZ\)@[jrå8o$YSvv7V aߦ d?gl5ܥ܃ҠX5&{b4{i7a/2jU{}N0FyM3~QIc v#%I>}QeJKv*E 㘸fԇ6Ap`b _Nl2??8o uIK챧~^ 9uP r#4rY$VJjj8P B]F@ؕ5ik(ѤR4H9;H:'Le >aO$eD["&}#,Wu1#\ԯDHf5*-RIy\Ƌ3)L7*z׷~bF%j8cG+/fT*k=3IHڋF[jGrYj>gpK܄PI6Xض>6y?O1X$d`6jAD:K|֗u{T[]4scUEHPȐzMj a[lk}!dU2S&NJ),ڵ^aapg2ISUӳh IU v?qS7Ux]Z K G~`[<^1=ZEj1>('G5U-_ y(#ŗ jzcA,ȳd9g O2mŔ(jzɂlO':4o m 6־탬ʝM_)f*hL f!`m`obMzt$qzE37*[Rmt+\pAUxNnJ kGvGegAHQ`4Y7pdٞ^W8xTSF#f:alH5?αKOQ.=slsgَN?O[UU,pkYbbe N8f_OKSRRchĎ}"}@zw uԖ}7;Q`S\P{zwŠtrkgyEu3IM]AK+ao q9OZK0,ߩo5qFqYVUM̨,=MG\3Zp;Xb5sMa{l/_~J/!9g.i3*o"Ydu7hji5TjR7 8x\nZGYTDf%8Q86uoab-6g7YxYlVwZuo~3s:̟02XKIC{[+cC0Q$*5#0^-2\ʡl:z' .s|k-j6bjc|zO$Tn,x͙Sq"H{-iUd3JԪa} \}1i*ZZ:lFF,#n,`w;ߋ6CE/'QT+3!Д` cbz#nipFeK-E e5M52GѲR.8F7.7A,.&KZH;( |}MZ7 IzHj*InnTX we 6ī-FMN8m( Pz{aP{l1qlS|B2LA4˦PzO@v]^ p޵!x$F̍m{o jo?2k<@x:r3#4hK.˹=Vv|ilx3d<5k4H̪o<%-A`(O o+Um E%<2oQ@:BnFEn@K_V8~Xt vV: 2p@H]eB#,?ʪ]7HѪmpb_ Up,Y@QГ1=ryJeM&GTi"˞EDͨd:W0HHiꕙ=6cKƗ0>+Yt3e̸q_-P e-pvX!P*X}yΨڅ뉒 |g拖prttyRي; Hn6fv&#kcX> Q?xLSe£Y+3Il& u XG&ЛL/¬ģH6DžR7oYg+38֭t7(ڈ@1;OsWr'9V`9Xj]SxV0!CX.#NI|3_\yWX$8[ moT:) n7w·#6deጶiU3]Pq=k<ױ@lUM pWyr).v(UfhuM3/1cG;Iy^Uf5aS%gS@tMinUmlMp7%oQ ,Y[`}% ,Z$ ts3\io؂ \3 RQnL&cMJ;5+4],)@}l +*u{5"L[ l1dTS|JM5RAQɥĎ:.VQ~_t34ab_8 tЪI`6}LTtV.:{~& u\MHm'R67L*~o-(Z{`In@$IT/E0ШqI jgJOI #Dq5DPD#+Y@g1yg¹q6kSe5Dʪ dPz㔯?nq4<3q2C84`Tˬ됝-m]YHmkEjuW]| ꦦ*35xA/h&FMorg|oř#ee }@yl:Xχ.{s/gļ]K䴎@&M1~>"좣2s )4ISH ď3_<geѠ+CK;;F٘u1e5SJYf;YUQ=)CеDr L;'tB\qrq:iu9d(_vTE&dEº Ius +Hn`z|by)Ԫ)W${\_G6aL?APr{aڡj6?2 Fܩ/ rz9: @0ܣu1[I?;5-ͦJ7olY)'$ZU.& q\25O btrbf~&Sϴm\CYx|#2rƂl)MZ%[EjiVq]U5ĒUM4u?#`\i7Ss䟾zsw6~4Ɗx1 H&|Gʪ rܴ5EMLŞi=cϕ]ΐVw to}WJ3ܵ_ϯ˩U6%Rdm,7l32@p~{m걶7-KзL6߾0MF +;<0TИK<֟:K5܆v*oķ7˓4\D&*\1QњEnBǨ#p"pVtO7i |l6FQ\w EXH O"CJ59m].bd~P i5 IX[ Ǔ!:1ja%Pj.+?Pq,2Lw'g$AgqWSBٲm޷]p ͅw![$T5rU7ĨSo27ATcژmc* 2 8):TYY[t 9!tlz8ơ"" ()=nF(5 2;[{b:"U2*ʷ7 إD&p-Bؑ/[-i[ _dmEK.?\&ւQP~vBqZN-g(X܃#joM̭܏lJ ֢IRb=7ot6V֚F2ceYn/d#lQ=p-DFT?G3-Ψ˳j|΂uQGWPva;aeQ}`,MLq)^ 9n>^?~3z 0eJ$4K."v9e~GISS1ii" F`> \iuYYƐn ]v׍B,cl!e$Xo RA}0OVZ_t"bI_KLѕhCpJ* c~$u6s&pvJSc,CFce LZ}q/&94Ҟ=)v53mO#/@wO<[Z,<=_-\z͘,,[F"I/ۃA oWGQS4i!9E(c4~|MOx5cN$he$<~YUnl/ yŒ$1BgJ5-U/^q_|9o&ZivP4Eށb7k=Ow:VU'WMW~HVF-0#YƮhA|AW54Xm=c#hcbU:{1pQDѲR,- O(Dl DrN$E|K TClF=f*݈CWGD0D&))jT~ 0@ُǮ1<ߋJ9J) 򑩁6&l}Uɐi$,I%N/ Z@t(bom;c#rXD0EmcWQN~r+CE$\iKcWUtΡ@cCѣG Ϡ!|lSl[H׈,E?KQE4ڑ4c&2ֈZ=1AeTo#|{8y0)*kʁAOHa8R^A7M2%MmUMQ(oX}q'IϗLaBԋl>4:ɥq] Op"M퇑4ɰ{K`Hmry&!`QkX*~BzMpѭu8I*tt`G,T%WR}бE㑍- l.#9X:GUm}Jb^BnT{}oI 17O|s}Po>?QFٍCdyk~ǒ6'=a {Ő `Lϥ8ŕ#~*ͥI`6D m*H%ag!mLIev$gK+ wLA7diBO[{aHܒ5`ȑ>RJ;P. HV2,&ޕcw8;OL.fI}kX- `CJCdY$`Ak^()2T`cc~'e#AbnA)[̐z@aLκ¡@I<IEWok7: A!{jP`"1K0+ǒpuVBR?|;]%nEQOy7   {71*dSam$SOYwòP,̦"nx\A+y)X`s0&% Cj I!u'1F[7#Gtͩ\I"7@=\6@BP7c X=-su fX֮5!N]GFV!Y <*1&,b3Ub)+wFXon+ MOGI`kT?r}(; jlu\VG[oH/u}CQ?邗mA{n#[I] caK*n7ItdHY/s XV;B{}ՄeT6H #ւ{_s(捘[|8Tpdk]ZyLeY"gHzq3`cT`XIbI,qnJ]HO{VswNHQjI<2(pTpۆqDLLLAM> RdDlIWeA$>gvq3vW1ԪGpKzC` c*&@Ĝ.Fsp{bDN1J~ieU,_RY jGE0٦6=o: 8 OD= LRI`j#)Ζ z`odb,aot>yeӹe$;}2! شFV:L:쑃_KTY_ O U!Kjb;ۧ8wcbF=7z2x4*PIL:.M8+|˃$$ {o:Q=ɱVUvuLQZ )PL0눇̊=A8Bt"Ҡi߰ h(.XLq&c"xPi/M_nZ@B#o?84*}ĆKJQ@t{CĪl)VD+:ۥԉ %4)@Ik^&N; [I,m*".vly#1cz&, |5 7Q.NY@̻|!OuߦT>frk]orXQ%ŬO@za$1aܝ7܃#sbF7{A>grCZְbhtjT6#s΀4WH.; ʺZ y*e&z\*OAAĹ=le[A\u`׈yY~QU4N2mr6[^49 U<`ӀWNo4u v%v3ay+%!^>(euE<#ǙD5nySS]u6g,r˫VmӾ2S#͊&+(4Vi /QO+C5R2ބԯlLH> *i!QvaH(_V<0P׬PG#f6;,;uō<[x̌G#X?Nvl N-B2J|~fYDG;TDo_xCJ3!!+\8^)L5PSEei nk1ks~lNnh\RW ݂n-H;r!3˩Þ_^ HI\7+(fN8D*l.o9qn)r[_#J;<_I۠ȸ7YZU,W,%%n_augCi<-2cΚaoe|V9Uz807$Qo2ω皫mw60yo%Ed~6)YhVy̎*JibD2u2ѦUu)lTIs.xǗ,zڌfIHM"JHlRyWtI yy5*G`q_繌-=]\6n:Z֒ijs 奚B ^.nqF%wyy-_uA L.4`2Sק$~?}RdOܟ5j9fMo ΫIߓy=Pf)r SX}ؙ9f$cPOW0m0>~Llɦ% X4b _Y.a Y՝}p?WMUMK9̨4^:Y"3pe4 nʮ,|]S\J*Mm|tw Em8gU%+vJ97yʦWXPH{ 7bp~=4SF*4GQ+(F;SpGi. dBN%٫t†Ȇ_(}#Fz=:NVΉ'\p'8;a.WI]5aTQANZhm}{>h ˄83TC49l鰰8/ eRY>Zeq}9M2~ 0;yi hs]7I F5ͽ<td??eMk1ÖUzm`.|.%08% 2!o8Gokc|}:}Mƛ)zŇ ]R1ߐZ~:QbWJVAOO5xj#/DDcJM81~H)Mk )p00;v .X4ܟkV,9EMusvU8iLTB2 nv=ksC9| ׮Y*Q*-٠:эW OMG=K *0F2çz78RJFA G dݺ\:c{FApD"gJ@cSDv-8SDW m-,J K~A9֌|񏇏 lF%" #wX{^Bp,`.3C^g"jǠ; _ǍH"GAn"lwgp0C4Ŏkce>O}i˙o {)h ,ORq߇3zQy T c /9YF3(FAeeli'.LndcbuiR7/>'$襊XR-ygU>l2 tuZ4s~ `|6Hc~b5}k< \8jW7IBozuT5%o ˥sXzM ӨN[b\J:ia 8`GRDfUk p{HةƑ3X]O)i-vocHѢ٬h'9/^G.e.KINɓF^Fmw7U7'1~%89ij񷆾WRrj::Ԓ[?+vS*889Q֒4 zpk᷄yqo*YEQB3 癈`WPs  =щxZߗPꗱO⿕>i".#ώ'ix_8j:%ft"e@exRx+mU>SHV w kb/uΎ8*_yg(O zIa)8]BX bw|NU^'ix>ceY)T:}JfE Q0FGGS +_e408\NifTqT y7hY :sn P +n򏊳+x:g{H $WΨo̊Nn8᪤MYRE57Ъ6kv3|l9qJr zZţSJȎS半_}bE50lŧdiB7~6Cߨl]\7JJg" Mad8ʕ+Ƚ\cusˁxFFϲ*Ru4e6#~ xv`5s'W1βle+5upjh ^Afe,*se4١H}7%\A$qtjI??Q Pcu`m{aOqFMyc|AZi2\0_ 'I  mC/pGdUT,'RMhE7 aʸMN/21|>ȳH]YO@~Nةxe,[ci|uȔ <'kں>YրMrJo~LW><&|^n{>)r/|h 8NAaP =ڼMv.ی=6Dl[9r7*얔eU6]E E-$ "@s̯PInrBQ6J|m .FeNA݆Q6]tBv$2D:وj;#46.v]+u bToJ054!KňUE%vaq.XQB#aElEᓴ5i./k0Lt*Nސo$5gӶ' SSY?wNA,J؝B0nYUEv`R]£eC׸'§ml6^7(++܀mf #uK,TWC y!bCFbebwsqQ lQruF˶6pp)Gto|1Zh`)(i$Xi"}78oɸk?’Uk4ғME~祱gr.* CM'uΝlVbۓmV}Ǧ-ju 1|b߇lW swja}TQ<}lĆbH&m%\ӇfwRl_GRT=&گk_"B0[%g%1e%ރ8\AIؙP)Vt?UβIgRtpSFsxĶ!t1/a u3.,#dh`+Q%UF@zӿqrۘɘOW^KK=%3ƒIHw* 'z*'aS$n F9٠<.3\ϋ 3#]mc<֪ aK}M淈YMSuw ?\o'p}TWUURpÅX=WT` ɽƹ(!Z D`QؐcbtDGUϨ[PltΉcQHnߍYv ]hS$pU"}x˪UW1+H'`NaYTK Ai V)UWkUPFX$2'][^;8Ȳqj]Dp eER.KX 0tr-.| ٲ+]A.)6^1HLQj&YeNiCOMN.L G-ⷈ薛3 ( \) K):strv%\䮯^fnbBVeyDU4ұEFm}ܫ+v'QO_\iSgSge-Sݙd1 |jm>jr.;~AtQTtX顙n8?e1T +pY@EKNBIT8e]?"(:r6R7"j2 V5 f'fmQԄ؋v?Ai)Řl{`,@bO ņz3d }Dc+ <YlS@>L.@"{M1 |S֖C.߱9j[S(Ǫ0|F.,>bd*&v`l1Ehx'J6<;Zڔy5qZA+qta}lIR/gRCN5HjLl -^斝 _>{4Hi" 剃0)rTŻ틃3)sO)s 0(O0lJ@=d]eKS8?41yE=4E~O\L) Z+&gghPdY/_lz6QUtOo]TgrJ8Q`UCQGRTFE1$uZ84FT%:jFe_QyL;c8t*Br5AUwoSO\I^.֗0 AWjT(E}qJV<B1؂#-iNCB=Qx/!Ϧ5h6F@]mwB~ I`J|s42*Z6*TVK#2Mk?\tbrsj܁߰"ŭXZA:blvKmW|y5y/Ź@9yGPu V?2 ?ml},~$׺_TVxf\*^iVH ` C5y_50:@t=÷(&#Fx9 gxmͅ BKD.t1ζTymZRGh*Ih%%X.fʽHY,7cX-vs`y$ h)b&i0zO_L XQϞQpax\hRyWvnoLS.aZB#J-G n/4n*rʞ[TpA7BiXc^-3!A ~!o. )Hfb[Sl9u6-<9f4K&_ξ]W"ql N5>9K6>j]rq$uCnA ~;p 5DV=qD!$\op9Vp{W<=~FoU(LolL־Aj]Ֆ`F=(e:y%d6[kv$ZKocXMzGSʊ_PqcTodo &9j3TU\Ԁ߰|!$RRm4riA&2i z+=vcKE+FX1zhrw۾,kNH'*&hؼ< 75-IQrQgUVGNCP &,$&>|kznl|YW:+>jFf x[09< %[QSTktK5a}yrSp \6ܭPEdQ8s&ap$lNUT_5PtI@|/]l\]}f-#scc|\:VPW62 `[nմX a1A'!5p[T"$+%.I 6)4Tq"pX.`-{|w=> Zy\WPTG%QQN-=UO%2eT{P3o5% NirFOfkp_ Re$__-dgQbG{>|hyqq0FVrR4UI_8-IO[9%|_{WS ~꺃S0T^ӽ_`򥚒°A6,)ˌvXcSsa7~ 5ыwtaӓ.E8{1,;?s>ʈ[9ycYXbe 5=u3492*T  2HU[@=:%}N=-o(:T~ Fl9<0gxkrjjqI܂7kI|97UFθrG-@mLnS"`AC,R:Q rB-k18t ;8/lr~kH ~612*DLJcF]{X'Y'¥*h*"b*WсHZ52+MpIR@b[2e2? =@RjW|*M:V9̟4K6R^Be=F8QJVJښd@Kor vl*FP)+w$H*ܑ$3h /M0i1m5pxP 4e7ԅZp{xFK/SPc/eײkGB߸N;.}:Y@vX,_(njr֥{rmӨ#& dr2,I:$K|Wߣ-kaKH)r4+lؚ>+p f  =2F f@LLR>M.npQaы6m68hRI;eQ*7ߗ]]P_bzluH`5p`cNJe"MFrzi(j#pQ)!/ DDD FTyMa[ }B1:>cI&ݽlXo tpp,1(58!Gg #pU67k{^AاO L1K,uU|'}`a| RZ](u亂 M텕aA EJO Il.G˸ԧK mcX 4Y9TV8z];+ iDi"#HYZ3Ԩ>Bvj;ȎP'(\]üIyQ9K_8Iarm^6 ĕ:A^毉^^Kd_] zQE9f=sNlj߄:dGy{YT槁| eY|Q ήl .oŸ,oI5voS<9"Ϧ]*ےU8ݿ੖X$[oE1GIW67\lx\Q|Egr ܨ kb^YGHøsuU q -XA<2-[$Ψh8/+"YTҫ 6RmDVSKAR$qʶ'ʌCb/i ˲ 7}=E d.>3l2!GR)t>E ۯ1"yyG/~<x|7*vz$mI^۾\χgS4ÛCGeLV(V:I'|lJH[͞7z/;>87d[S%_[LoC*7 O\kJT'Pj:A>bĖqt%S~`C>z~Fl$)#`GCDkI͸Pں~r!HRocRh.%\mc­NцDfX2}0*KҀkFlW)x) TTAwUHtumw>˲'༆TbBy Ie 'NɦH]Z)7F=tc8-|nazuLIO 5Z@/rqp ˗y#^e hwv)*"ȏNc$> Lˊ8$h,ٶg:)V?N-srxR<冥V.!v*-Fvc1|I9rs.0mmK<mpPm*rXXbF:1s" Vu3CfĚ®\ӿrwl: :jF]r_c8S}&o#s!(h PB %VRl_06t*2ƭ~[|l?q*f2>0O-gV.Ӷc02 *n~ hkģkj-ԃ%eV0 k{ *)$K J{`r2}kNvһSh3)Ǡӷn;SOQOmx*\B~1F㴃A2F Y|--(ꇿá` ĭ we4akvXxg_0[pt u8J𦑅*bK[ @vg`v=?t%v=b*h&0^bT1e4#\þoB@郲ȍuCm mv l ˻6u6Ԝ.'H]"=I*/a[`:w\9v,4 uAvB:g3(㥧.w ӵ뉶oe5TԐ%v6P)If <@ut0٭4\oo_zg_)fgG`J*2#+@j{~o6g F yWAD 13 rk{b FSw-(Ak..K$Lc8moDB]|*0Vk]ݏ\6J }[R=:A6@Y뜹eYn1uU5!aB{BXu{&4s̼tIl}66I% sep3PrG@ȠM._b\,Rീ6ܑe/6 ךЃ0PEՋ\ ehc`l"3lb ?Kp(i `1t&!'ciEv 7UI`X\#'Uw0lRY5:Mj+*累o|8P4K8ҭp9c9B&ǽ\ۢYB`v61O2xag}cS TLPV#QIM 3_l8rznYx T֯f%Xm= DQ[kCE0|+xm%,لLWA ?\UIs!-㌫ȃ19aW5*Kņ-G"gUS>C@,UH 貘iQ"TQEcG_*ٺ^ڈ7N= H=}k<;\#S--$RH}G]-'?BeJQ]em7-W<])5&!$2+ "c`{7s=KJHrI(i^SqμyM&_MGŕPRG!eDcGWԭ}^Vu |Ʀ =K/ߢelCu9_>wSMMTUhآ5)() aUA 6@Zjjʫ!EZ8#vP ָ<'kyHSx&V*$ؘB,e`ď T uFoCsTjî+FlO#"") #A,Xؖ~s7R Hڙ$pI.e`}0A Cw 1q(F9#̊]+wRI$ ;<֏ճ[Xa-m'Ƈ)LA6*.68*s<s<\5,jy庬Hң`>l8.%5߳mL4y$>.9A[Ե2 ֚Y#ƚoqnEB9fdy5H2$RFk؃q66ٕS[NThOW*87AG 2C mCH<'rom3+;YmWg9%kVM8B9eu$|bF8׉j[=s*e-}'ӵ[.w`a\ƾZRR>{bܨ>Gӄ8.p f4k-{[53еnߊ9otdm8)iAYSv: V:حr?" 4BꌈBPNi Hʫթk "^+,VtN( MkV:v*U&mt6zμya*{RV7<B>(>3vi+c.z) , ov~oYQ˗od4qe&L1gX3'vU"̠M_l;sDҰVlA 7'??q6P =06۶%3r*x^5˥tp5 0zmzA)%f~2djezzfc2vSX*# HS4An$@.,!Y\<}SX >ؚQxpqd-I.ɐΚF厍_ON O*CeQ~~&ՕO4Rȣ/ګ{SC' UZYbϟǗPpK,Yu.S$@.[qS+3-; lTY$ yk8m+ԃ%ռCfU~^a;K1e ~O8ϨJ)w**T~]yWEO_g;w%82,dԄn}V \uŌ9vF]}T_[ w/.$ix?$bqMLJb{+x#5c2sÉ8 4P4lXnEuXN>qGQeAIe-R5BI&C[al# 5)?ܚ*y1ULIH{ch1GH#FmpX{cbd]cw J9;[`wev)Q53on@znK"- غV !)%8Qfj XCujI*@I?zH? ZO7 dB nF㲔:`s3)>#i%YG_:Lc`nX3 W:<7"0`0']@*#kaz77$,BXXu[lY@fmQUɵ}JJj,lz,F t)%Hu}$nd'0H0.Be:?\,^ +Tml8x"#`t|7&RdeTsupL$@4o]저2!{ E4ݻ1tC`rƤ 0gr$[ÀSe݀lN"m=1 {E5P@,mVԫ$`XOK,Nw F饗)! Nj G[#b=,H0m%f>M-!cYQH8//K:oQ׋l2ް|@(/GhQ˵ņ Ɩ;le˖/:w xʅz~kn{gbnv.0M#@$I]vݡٔ! N}Ay#-u[>.5PtE.wvqe$;o–6BH6pW&Dn:p&BenK~`e1`L`UY+@ب%uy;)$38|$flA5lY9,XflQQuolA+jԛ߿uĎR%^-{mF@ oESq"{ Kl1eJX I|ڵ7,IyC)d*3lɀ Ioh\ceKX 0#VMF̂(]^cn\5A#FY*Kc&}`Pۥ1n?w>&_?0AJ Z:&A8%-艜"*IqїxSSOUG PFoI!tV>bђok|"̼+Ο!9fT@Җ1J0u=/socv,fm^x gfvqLG ԇHn ()6a]F%r6U‰v";b۾3 ~59[=8s^nn?9[UFcRG?Ԉ${JV,PqnAKfyTih8X[>bT1og򖮏.2L 7GOC MpǠ-rqV NvEdha8*I6|%J n{ }pKnɠNƒR<Ϲe\.䯚eX:J"PnIR} =1ZGᛍ@5hagG om99XuL^6WDžYGdt&ufeP#f&> {ۦ-W~o J/.*A$oiݎ'> ׋s>Ȍ[3LR0 m<g2\*\iXUѴVĩ[=D8zr3fet/`mZ/㯧&γ3tҟD6~h3ꂁ'̚UWpFnl(zCչby0*%wc7ԥYڲ񂳙I(=,ve*n$%k;y{̋XgY[JspZ!UIPX ٶn]\eGoOœ!ʫTKK34fX[RN#~#/Ӆ2Γ&{[bOW໖YT>k U&qoL6&Mይh'Z͸<&2pd`QmACQ奟2i2ՑKVXX?Nc|Yc $am䋛a',/F!eugrxZ뻇^ܴɜq7S5iO^C,c$ nq_q2x;sX Jٞ!Jc7kŽ7N#f ;Ʋ,zb[/RCJB_PU *weԤ鴋npܭ= Ƞ!E|: Bz ?U#X,, `%T`).U:{`fto{}-FKŇ1|R m[Ɔ4ۉ*fIW Rn="s׮7jl))v4iغk{o=f'_,ҶWY'zfj&XX/K1&.~轀?UwF/p!oR|^-upNkYYRƁgyL/E_$kfrD?\[ \?2j Xsl҆7 Rnra* "${[cu̇J$l;5^h5׉26إۗ8mr}=rV FԠX>S،t@=ϪA ^Ṋ`,pM*Pu^ kl@%Glm{i6 %!P;lpR@,z_!Ye(jj˩&Y?7cI\8˘ePQTfK ˪VI`m& nA}z%XƒjцճBꎠЯz,$}K~##evݍHUQSeй$%\zGmV\ PSunq? ss|ȵEg-D"'R׹G'Z̛.P>_Hnƍ{0bP4:DŽ.e0hCb}*S*i&8B: cqu?nQ9,` tmU:逫+e\Glbx$#-}&\ Lk6"yT{yLMjFY`H#F1+9E7*h\Vk^1$v+pRtiUƛ{]-mXwr1kTQPҥ`Boktm*+D}OnA1@ =.DM5meK4ܞRyס= р$ )\6F:hOUv6Qp),Ҫ4@# + 6M[aWY~ۦvDrFA%I=0&Y` u uFgfmK ;@A]Pu;`S<+Y,0m,z¤"M2#W~,MTϔTocmv#%ʹZ: 编{l?2SC-dd$$5K]W>^ONDIY}TA£:#r4O-!. 9wWRʋ-P1ktlI46 :n:GM:2\N?31,*/k ]fTY}8~(ⴇˣ#1'`&,?CSGb\rWfˢ-N|;ТnHms 2zx*j:YV$/+X&rP?yM}GrǛ<2P|SXVgZ q|H%KQ',E.4-c=&ks][ZUo[ 3!($ wbyȈ/ƾA+`JpVYz0ΤG擤,nnpv Y?So|D]) Yr!g Zb*es QX"êGh L"e?|[nhfd|٣0Y2̒t:vmko _*2J[nxUWM_$&P`4kWrHOS4]HX_댴eǩǼOg\W<\-`Փ/% NkL!2YB#5UX4h7?\jϕ%\壁ÁùdPĜI*xR2U/Qg'I'pH_.z:x=zUZhZ.b:XcYHbͽؤgt)aK5x÷FZ:.6gXNk2_7=v,6eK?\c.X$ X[$J#(K9ۯ0H[JH]@Ă (#?UvWRΓf|Rud3K3(le8xk>riUe kA%'RoqmN7*DR{#ϮTWHԨ}].+ߊО{Oyss&H^Q" ;{88zfNNUfYlRa{D[qDe{ꇍDfwn smc(N#wW*\)i󆟈OdNӣ a' FΞS+n)ʸ5S2x cPm}CM8DBUĭ{WV4Sv0fYX6k3¿ oMIEܠL*8"4= @Y-S9,ˡBވ@{,S?1H.iC[c$g|30eUO%rʵ: r;pbU$D!Xs#{Te3XVU$o *f"I_ԥ]%R,n4⦚1mT#a#ؿcŕ<q_L*2ܾcMO#V-䃱"خ~=W ܀U***A]Ħ;{bvxJzok#j}ŗ+=ӷLk'k~.2(,$IICYs*]n i6$ 21g6*A&ؗ>/B̻FʎR%L<%4sTϒ$Y[؍jTVrׂ']G)Tyvs H%퉘SSN̑J T;kGJbAYɮ[guӤ3gRMZ/k@#x {L;eJeVlf0%L]s(ݭg6q۹qBfO.+rIr/tՖ-a[O>^fM&R#:[##D_\.Xu>sX$o v-Rh'v~W|0X3BpTf*ZvһK2YI"P[!EaTEhcP&Zl[D p 1\@`Ziy=窊K( ҿaP8mImuz/g |%| Lr@BAS-[H6[*v"aqƈ/ӧۼ pW-k$˩(yA6:z>Lrc7p];@&SkVBc{t&u5 S긹ۡns;}8t+y/(#x2Hln$; :ଆ{COƅeAtM;oo1J6)cI9o~ᚹoCϑ@Τ{;4ղW p-||rHRg~^|\6H'pm ʫ&]l/:+)g)-&IOM2ܨ~aVYATH2n Jumm&p2;: _blziCy8-eF'R<^S+|e_ ]?-8>FG4{':SҌ:nN+'%a0Eq/3_0?Sb7\'W8/ ↭jnZFUvss57(H,I 'a|*4H=Qۦq>5b18IE <5qH6r—(+9.ޛ)*DnuۥL9TpI+H/,.B5-ɫspR%Dt2U.Ua!#1ގ*֐L 'skw鱶;NL8DuRg}F5L>gEqseGJQԭ8vֲS}qvn89\%j41db} 1 7clh +,!٢2܃mݰSX_oaI!e!h@?u~5(=+E'P67m2 Q˙s']zwߦ;b,O| #. @%? z?Sc"m('ܐF#$kG[7؛ `YʉrՈE6&M|,2V 7P,0> +X|U&UEcڮ4 +(Օ}ضb".ǥl#Y ^1* 5ʃ|"%R"@nW#RP0R:[8 a{aABNn8JI!F9Ej.ǖMQzca.rp ]@ clӫNE"p0%7 |ت7޶-m!VJ-F56K-lY`H @=>1f!t/yH,|)f ]wphmzxu/YoŏH*\{|yP$(>-S-ҮkkQ4W\EI+Dk:ntc>(βJ.!kL$ʳVᑶa'> Ɠ}peP}]A-k6Z21-3,{>U=^ 7o^ױ=Eۦ0 "<׋8G6z^J귿өUK#n.kW)݁aɪEQ2RAi÷ۓ\Jߙss5($;E%!pÐ 78 #er&Q|>F|Dy93qWp(Րt>_$i6nÿNZ$'P3̥FZpmW&hrG,oBt6 o_Z?E)80<sD\k.k十EՕh$H\)58sw'<w4ᜮ9+<(e KjQEN2Ss,|ëзFyfEI"ױ?LKŧ"oc+aNmܴPAoanGt{anouͯ|,~ޛA\INP`İ#&FbO8V?lyX&H8]E&tSͰ `btvfpPo002hrjJƦ"J4AԱ=UE'-*gSXml٤/k[cÕ6Kx@&|RS)]AͿψ'vTfYSU;e|/ cD y Dm:GH#wt X1T,0ft7jjRI؋߮',Iﵱ]oqrb55ߊ6˪Dږʥ<3C9-Hrm,ŭvRl X⾛3˦XoV8Hji\Y.20@=F$MMYuVUXE5NR/_s:Ί'^hYHźgn 鍥8:q|n-pZHT| 5CfSSV#u o+)\t3OQPf%RzXmF0[ x"-v+'I@B, lNm+J_U$vx<Ə|~l1?)s"{fONd@Un |UVC{r^`D>'[-_~Gj8Ǐ,4ep֬#umw/LYWrC+:Z5yxjM.yOW\esQɑQQAqCRnP} ^xtU9sc>EASp({HonLkƻG򛥾3/LddbEg-Nd4!iZ.b~`mbI>%o9y|dPS8RPmlnxMeY>mfr"{\u8yy$v[u1^c-fShʒOSkq`5k띣 zQ>(}fG\Szl7 @ҫ3*wgudu4|$R "!0@e(6YŴ9Ds 6eTJ:qk=E6'73yuY$34:$KKuq*Vy3بN#@o;W4їҬlMojB&G^Tk3/-" x5)%B=yyF[@ "+6MKdFk caj3FΠ0/Sqo*L U֣DcevO{lDFd^v_T_0ck%pBv:t~hBXβK_L;ґƌ 6*#]&PA6ıion؛2,7kKMLsZB37겪i+)-p>Wŧ^s.#2iVzuͯ)VUX鶢A|iGOH3:zyQKVqf*F{C`b 1 HǍ\I[Q992E2QVhY aLm:,k:va'xGs#2ʫ3HoFA G!eZ=,sF6$5h%{,u72UfS:cN6lB \\ᛢK).Ij'H2=?\uv͗Cʈ/Kkc`@Y0ԺC_c۾ 6sWFmƠBI=m~g FAF)mL;% 6_0) mydi4B:k)y 4Uk,5Q,΅e?f81Υ%"uAZȄQKvBo.3z ڨ [1 9)fWASRH`ؙUf'McZ(/bw‚ v1P#|ZSR2 fY^wv 7i&0I\FXRv£M! Ůo0Z렫^k-v6 ̃HDJr;+–U?*Z֐VI M_nzzK~ |Ocq _\L99QMMC#/%cm_I<5)}-u sۂj~2$ɡ(U.a(t4s~F\>et  Bm<9s*HU)iuU lF>BmAz!*H+ơlFS]!zY%]-ZР`UrP ݛkb&Ce\!׺DQ*,zH78%cm!{v[UbF#Pāt%rl5zO[ Ԭ~Rl$Ay,j}qNHl7`ڙTW{|)ikYv [t,b(N$}톐R X<M{}pw^ᝥ lNS }8Qk_̹S` ǥRF|y$y6ֽ /ћڎl(gW+ʪZ2z6ªy "[A YtHY =\l "Dmk17w툪HCf`?!P Us)bk8 BY$5 [^hj+-FOoI"Y Q7 Ofd,SO |Ps36ͫjڮt U|"9I)~$2cgKXQCJ*9Yy03D mV,#y"H^8ˑaԦkӵc@? Kzef<)HcD _,*̿(jʨ֤"C_H(bHէ,9'@U`çcNSC$z,OR`0bO$"Xw̥Dx[\_WM|RMKRƌj.Vq$o:8a̾Tm2uA e<16$S<247RG=(b&1e1ma?TBpw6^L֠ܪ ۑ9M_858B5!Pf[qbz8VIDgF={{mȴ4II)VdFեWk0']S*TH$ӪON1߼ 1!Nɼrɘ Zb94Ut*55o$zJxo\. {l .S KK3/6.⮩ZHٮWq?ijjjhEޔcarqNeRE o]_ Qd~yjCӽ[T@Ⱥ%"׵];6˅1uǏzY{QOp$JPZGi$v3ɞbSPpS= aE}{u파IGr.ulkA2pφ>aWRM,Y{+8$X=L8 X!N|<Ui==<h?/H#RiW#J-8}V@cak.R̒+~c0H 67)=d+( O_jh U;bW!vC MAm,:C葍"V.XHag CyVߖIyliI@*m*i v xQǥ?ʥ^RU,Un$~$dKu;[Lc?^'d9d9X"%hVhSmf H/4oI=DuqUx^)WjR6m/*e@zϜ 48(j%|Ǝb!ܰ[6?Gx2GMĴTpX~.U _pln}" +L6;!}iі%Ddc# ZPI$WBEͷī$eƖS,RP$}1),%&v*Ťcf qnp u 4`} OCLbA%D| i0amVA5ە3IX:F&R-ֽX)*,oqb땦ό:(cs2z1U˲.ib -5\1姊Wdfty:ØC+0VIl YFmteS{ZT?0lOB:v {a ;jBy#u'o =!>am kvon0GAs;~LV6[bk ""ﵺiKaVe$ 0X ͲPsfr8K/EI =.Ip1A7!b dV;{"I@Mj-qqCTo|I]A#ۛܥ¬\I H + ;~Z`fSr.}߈eLx%-[v'{mNWj?\%=;Uxji*, zmL|ów18%cxʸ*w,I! HOqպy X+#x#~Kz,%SV)㧌H(Msި$efn>s_>8̸kwg5&]u–ΨTS{Y;\}!%?d.da=Ӕe_Clwe/e`_뉄 ʡ_OcR,nJu>Y,@3G=~BBK);$a,A[|N$8l V,Cz` `+4f#|$(A):l*"bVǾOg(*s\(V=|x1p;|H Hmcaz\XbYS$ͥaM[[bj-}1'$V냨gR[ Bqw$#+S_56ҲTM=BC [8>LռYN-HJBc6nw̼ƺQRMfXlmoI+=< k-08Y; Nݱ9WȮМs|"$s[Z22j[k{:hlGr{(cmo'͊3j;P5nB N14ZR HX BO]0кWe*y*OBnH1$Gg]Y0f{)w2H"Rꡌ)IdkYpPƕm'q$`:Èj}}p,Q(%l1?[VSe,Lcɥ :B@7X\t1qaVOA786I%$naB`n6p;K^B/b66}pud7mA)'IpfM[OC\Ymt4rwlJO)-O岾 }{ HXYz?1wÙ*yaשolp'x35d6_<7I-Uu|ChRQF)C c<59qwwpCJYkP"Yq3|σ/PT^i*U+$FYp.ƕ%)7.yeMeZl,i3^=Jֺǽ/2OlősĶI.Q<Reӱ!F] xdñ1_kK޷>3lvUřc5+/O,n@ǨXfmn|e@.H=J yW pWEdpem8E}*`{ +abL (,hS'֬PC>J1$bNåY{\ol#.V~b)2\oo@s[+0[IYza! 1,w:GN,Fia!bG >ME:m05U[#marmw2l?\ ->3C.Tum#`x6(;cMֹ,N@6&vQ30e/}ig~IUƵ>iSиeOyHIFF>ؓ 2K'xH bS C-ɱoA aRڴ'T1a%eQ:l/v>ltA !n/dpOPan`u' jck؉WkX˜[R.=SQprǑͰ^߶!,*a xMf&O׾ KB%P$#W]]$Kn5p|O`p[b3,}Q (KrsTk 껕 бݮXVd9""!'݆F2 ߱l3TE0 յBb/ka<R8 uk*{i.;2t6DRX=zJP{>7™ QȚbO/{JUT :}p Hlm2'H5;AKర]Яߠ @@=g X'@{1eT'm~^$0ސO E{X(zH&ϚP|ya3RQSwm\ wRmҼj[A>/N]O\xOs>c]d]gSftQ'P,Br \28*(`98"oLrOA4wy)m7ֽ!97}qES]uN!ghzh# w`sEfW$%0bm_&nq.Cܪ3|7I9dut *A_ov>g6Qt{G58ajijtq-+.y^ceUe=3'p$,ds|q/1?7[UR,UٴQ17rmOtrL3i{Heݏf;f5i+s BrXM|C!dPe%yYGm0:q @^lMWJG8\ؠuUT׿^[+Ϋ@ūH%,m dWfxq]=K)jd:*J0b. {m~#i.\>9{P},VK9&WC>c[W-$ T5TnI7>7#9c6[9&bJmI՜z{YwI.sfG5Y[V&|?.-'y<˦] by3 F7 )%8xB?eP?i̗ !(ܞ߆W~ 巈)(3L)ϖ,Nml:(ki&`.92KZyMKEKa!# bb( U ѲIO -MAg=Π: 5k@ USI>:ͳ>ɫg)e*O=#p;A:jN]}*X湔I[I p@74]6_NtyY~Q?UٝsE$[rZKtzNţqV=Wz}4tY.ZxAܫ>W@83ȲdsJzHs(p 3 LnO͊lƚ;aeaSd\`FEebah$ kE#8ܨuj ?_Ǩ YUGQEر %9*^TTQ[PKYz[Fޒ7af.H8Ą$`gIK{>q톒P$lFf&| bmr4 M*H$tf6X@ ԓb a * 6np9ɤ6X@1Ժ=p !K.kmE4g -r-(R1:ZQLbC+%}%\ETbʤ@p/(Wg`n5z@W Al kQ1e?NVB 6Sa¹77P)t\9l/&Qn>Sk\c̪Jk(It#C iK)׸@hePTp i$ ߱M%ϖ@9{ㆧuRijgDfդ ; INlV0!dr/v>#uTq7̼>↲4KuUf-mʟ9CӴNw#9{.C$rA15cbM&lneQ|Z5,щd,ndlUk?yS.ktUUx;q"q^XBIseWI9ffRG7aç]ET}$u\z }5"BM $1++Re鈚h#uuQ-fGaE:#KXV]\>;G$DŃkaZu* ,Unzf^ %"- H'%kL8Zwdc BFڎ`z_ n lP1R;S! t|"FdrGXk @:KF0Kud1+>w#aXYU#W-~KǾ5cH!ermmCA|L1}Iu: k,,\Q{cP>"3,:4Ά*YFoI;"Nq%xz~(8$NK7!Ӊb܉+o5PO).˫bO*#X`@P `݅ǣV0XǾ50/Cvbè7f*6Io0 te;[=ؒk_H*SS)g5C&`ћk5(VԇT0HPƬI,I[(';aeTOR`̀{GLqf؁֍ J وt} % [ti$RvV׻[l4:ɭ6)!<,.A#>i UؐL_f :HF c7sbIIEVf:nn{ ^L7!]-a/NukY}P/{6%#! P| SsN#bK.wF@oC;*5wk[5L.QPĚ~4M6,L-toq7%w&ԆMx˨)NF'8惈i$jf6`WH78^֋ UԹl|jfV+PIۦ;xZoՕ5Y$10)$nzIyˊ4g5%O,@"# Y$z {oaM胐NMH Ze,]@'oał,id}CkKI'0 }6/C Eҥ(VV7n&i\&1S#UF\ǡlj_)ծ oDXWSQ"+%m܏鈵8ayj'HUT-+l 5j3D H4ptaW% Z)`_ 6vŜz3eV]A) yZ(-:L6od_;SͪqT{Zi)3,̭HW/A`O뉽w\2A n1@4-@׺\jØJR9 žǡsj&jdFl:2h3!,R鞪[굷m.\eaQ,AI{[^eCZ]!d§jIi0R-qV{_ʎ٭99T#H& v'SGMMISrJOYa{GM+>y'gxrը Rpyt$;Xs8+/ɫh<;3 #N ˄(%UP66]"::G:-n~{#ʼ8r,"xfXYʘJ/ˤHLgS9 é>yoܕڰ&p7eԍ3 Ɲ>2IeXr]ujDk2w?7+ӳNbl?9d5=X#-TA4d*nޫt؟(5@~Ėz$gV%$o牆]Th'H) -,kc+3Xh[T3Kh)vq+3Y+dd}& %l}aAB?_K -$*dgU#*èTu8+2N3-unG0Sf<'{.biejx]Uͳorz5\ES*b ö}1U0LqVY9Iy=r6u=ambn'*)4SMwi`ʥ'R]7t:o0<U{aG4!t7:}vBl 3 p[CX|oE}\"%6-+[V`ay%,="u.>\,WbpyjgRAc_D9iAc gc%ȦTu0RVAfK$/^z!էPFŝ rk()*i8rj*)Xz\|MD̨֫0sUqi-UEK)mF@`,g#4My貺 ['L6~kqY,T|U\+^61iY=Ns|%3ڞ*8L [nc8gȴeKeLL('9*v@z\f|41e277?R-di\!FL|m2JZ H f,IèđK0uV!n<1y:[z5;e3pP"x؁gyG5^upi5VEbz9xT6aTnARXX(,/Ǽ5pB Ԉj7v*y{cԜ'O+A7Ge)a:!Io{#Clm{ p)UQ)r ghcQg#i7"LI 訩*^Rji_ؘí qL?+9y*()C)&Ea_J, 76|n^>]ÜAIi5H&ECt`ou?Lk{O7587 gqpv7u!R-Dܛqc_w6Mdu{l->1OXL2(ft`Hc5 YFF!œ?1Ԕ9FY.hQaE_ UH`mo zH '/IS՘U~`^bIRULAP|"`}JQ5{ܟ rb $ } D@0oPj)(vY()M63(mѢ][oHك)1%Nq$S,jG|*p&ؠ}C녘0};׮ nlt/O 26Kӿ1bSC@QrX}0 H,{큔rگQt ,I(]1RY4U >A`?\V_,XM~lȷoR]AW;0?,um3+Ԡ/hT$}%l@Ż GP${a NC^pѤ ,'H{)hHtckn0D++Rkooop6[L3Co +GcmGߵ H$˾KBnH=Yz7D "UR hER;DˠAq5M9K ܪFE) Ģ1xRh喧4^:s F]b7ז@e_W%kL7.IřGǙj! q{ljrx^.aqdYo3xPPQ=mG<mbIVd"1~؏O~N 󄦩y~c*YFы caT?Q=%:P(#]y=Ace1na-o|QU7d5CYdFZʗ}.OqKymmM-\ 0Uw龓8UWy/GTc߳A6'u6$\]\wǡv&;a\U*dhXXw"]7ٵu.+H[#>km iBaȥ?܅Ů$Ԍa\A8` \Xlq bidG.(TnOR Ylv`Y=0onUd&i%(TAE3vE|VQwtqmyK$Q{߷h{J[Β1#¦wO_U&cYR%+YR?@D+-t/CLy14pT_AQ"c>uSUfb֒lH B:H܁Mҷn÷ 5T]Üc&W¾cӔ8ꔴgIBYp{n`]+t%Z*5\E_@͊Je}Ï3MfQּKN48=Gq_Vfl=nw /R@a7 Jܸ.۹(Y i :*eK Z,еjKpf:"z=20$YAu8 KQccTs 6UE@Gu rPsk[]*&ID\=l{%ŠRLEh݁ ,IjP7DFۇ;%FwHT.l{f:[ +̶6c@}ZT)>{_d pMzWS_e@{{0 $ B@B{nMF*,NO@0$ى۫  BmJUsun_b&[WVE b=!嬎"ME䑬߶rjTWe@Y~5k6]||i4kBE'VliV~x*L,% 'f)T7bx?*n Gs;w6:6Pu}?U^9Y_B>7s/34rn|9z*AI6 X bF8u3\ 3(h,UAy}mHÓ"wqצ;GvH-{pӆ.M)EpFc=`ͤbHe;\|1թ^E~G*BإRO hvCm1oԌu[oeV` 6Y>bV;4R  +;qEps:))#=ԃcۮ*9%E 9}L!pC,lp5i)27<.7_\)U+pe]fdMiuo~xrr7sE&Yj(64Ko85yKǃ!1]%{28XmƁTp ~;/dguPolsQI&]gI.즖R2 ^G|0>㑷hcwO_.0wCC&IEP9< Z.Groapet6ySeYZw =ob}_183(~k4r1҉BQr16%秇{ef1S/aR59k!n;mb-U *#wj'Qpyfٹ/uM d(%n/D_Qc};91Xr3 "!:E+6R63ꊾ I)憢,2lpWİʃѐ}$PJ~uye¹̋I$0ER2Ŕ.F|m[_>[_fues*5Qߠnmp9Cœ]dgYtGD&Gtuf_L~4S3w;Ӻg7׌U 1E~L\M6<3J*2r%&P1lOE& CUg\iQOSMMm""6ِey>YGS eYln6DGnn2&0V_7>*5xDx}I&=:o ) D=B3NJ8XxI!Jarltq bp{`R%30ԼA~&5\q-*3 7W+ON@3yW) %1_%!y} v5HFH*<8$v[U-n GIpmG޷59Y_P5peӣA(t*b1RkWcIx症L~j \7pƜo|Qx8i23QGf=]'`}.WŗĢ(i 3`EC3 uS\> }$̻ńEm@{Jf ٮ; "V΢u %QŠ+h੶:i\Q7M ms}ϩ6p$:I[mDN"HӤ"bBAKUQ.bI (j8XRYǭ}zPaxg)JPZt8$+0Mq0 i:u\XC !b YJ'm:x0q bFR,ٍ¤ w;i$)|)n?L(3VTܓkl @<o,H$ b9}Vlo V`مxKs[}_( Ul F&D`GY#mIpd|CMP@VWUTH뿿ʷ|WVJjIMo#x^3g"Ȣ%&VVctiۢoJR.G%]؆< j1FS덎c1%rJl5)k aU1|]ali;M̎dTY• o x \ӆ]"' ,ui=eZettt$n62qjfm7LrK0k1/{N pw_ډ=p(_SyVb?\{˖*%%_@ k}:Wa#{l=mjXm;4rG*]]@0TrAT)f6$ѻj[ puFX PѩѹLjȉ ͥv'm@S] OA:K4l v#P|Fquؖ$Do0)'52=\6q%3aESx̲ jޯ4ɳ dB \fktß7&TÙVVJ답79r~>8w0Xs 13(+!f-mǏ૟ d Zutl=u k{n6[X`wvWtdqjkXut`nM KVẀjFZݰĚL*'ǯ)+>Bq,eAQm(ᵰ)d0L@ w۶@MF-Z{o)IC`A1:@w HSR1$Äl l;=B$*wӒat'#{6ldL$]=>G= [uPX1YKRÉD$4dI\`ʷP:0\X[}+aX`MΝ0/i[cPnzHbřZ)HlwbpC6:o`?};sz`VX5Ќ 6"g-.CqqFMKQ*LLt Nq*f=t4w< sOV߇܍?7;h@_7/|c,|dUGMR+j4Œ5۸rQH$IT 01*~6*'5 9FFQbqE!ᬮI%D0=v5 M]W(+RIpUf,꽘tG[zG^,4(q;XaD$`[vнv>P-щ1, * b6$KKH=ǾKHG+H$]%r@sX(n8o@.'b.#Q8\\>@}D!@n{qFKcenp"F}nHLP6VMpMcUr@l4KSe#hʷ>m1{Q=+}I.P#>X V2T!EEdd$ck`esRXؕ $s,++&Er"vjB_Mϱl@$b}i0珳˫865ّe@W[3-Hům,\;̮̳3J1-ړr}6l{/j\}~ ZikBERJDe%k?wOQRΨeAWT76#|^^fM_ : T{z;[qkJPl@^3** 5WfQs踃WSUՈ"1CR.AӾ:WEpɍNh>i,;-}.d=,ʌͭblc}ň_ْ99#^]CJl1PPKOjhA[&?|ٞAAB$Y z!#(Q]-ǵdN@tr5WG=XC\YA}2S._-MH5unEG r RG"J:k\_8.I"-Ә󱹇hOTH$D@ t= 75X_ ٯc3MxvcRَa1΢o-{{\_eU \WX> =~ 5hv ݣM.chǠyln{>[`0.{ ڰa fiA7j=P.e¼E ThVlV3ҨI_H.'2Sp?L%r RĘP Ldrf+0̼Lsڮ*J^2IjE6 l-0/I-b*9 UՖĒTu7/е*ßvwbYݮI߿L_/ Sf,5OWO+7# g~|z'D+rkI<6rܚ ~/6N9Iy%"2(Rnv( s3YS> qqL-0 UA: Ʋ(K EM,6 8jK:Vi Sѵ-/Ƽ;)R_&s /aʢL(+`YWhNHt`C9uxJpfFյl)#,>6AZr67 )GS ?#)>Y1r gҀԴٞQH]rm%4vM^:Iײl E%?0=NEE9F Q ܟ{+wwǹIe%e{*7UWEfy_>Fi3ɭb59۶4\?G*/S i#S `3m!`1ئ0#@6e%, :6ۦ-(v@}ZM?7,.Wgp9;Ӭ-&@)WRKX[li>~!^x1s/51&S ޽n#lw\ J10.F+;9_;!5Y."Hc@!Rlr'%:3Ix9VN h-*Lu,v6)za1քQ7]RHت;|dikf=@=cTn1{a 7!28d2nW #fCMΗ4e56xC#ӬnYmQ19XʎC}~3æU"|aaXEܱK&:_bdH`n:~|n#P lH@vg{ 6ÈB7^ᩛDK 6R}N A h^ =y@bv*n_]\\v4X3Q~ؒOy107 /+Ed2 z`؍*6bï|Uo}n{^A6[>*i6WO09H ͈n)j&TSn7l}9sͼ8gsȸ*ZXTT[ԌFiGLR~'y+AsKCSkƻLD1a07ShI\0dR jV%𷨈Ġn:*$g@MևJCQ J";K!N :vƲZu_ ~ CWQb|GҪ vH5Ύh[k&LJKKɮRaq+}נh4Q%@իUo6%4&5i?57/krmn;[]2*䊞jvgcr7_; O\ CE=QDF//qET5I~kxD+s]^(XtmOo=$j/3cȅtnHQ{! T 7cs8S1INl`}'{"2r t0Pt!.."l;vǘ.T, [{mntlm{D1ҷ!@'RtB_anweܰ~#)R\72S-alXi:[Itt*RBn.{}KH sm_|[WZ/Kه UW'o۔&*,ڗKQ6 0U)F^Ҥa{넖E7imwEk)=N::AY6|ANe$|cU(@mpе4k\uă42jjXPldIa ~KꎥWHl}⥧KI T(/a;@[H Ap506U! e#h7 U ;{a@}LAge8V lyPKB#1{bI,BU;j6[Jön(BYMl=5{{vKjPGoQFb4 [ o.dKyK2 gK4U6p :,Ҫ#ǖBx7 A}`܍c n095Ղtc+ha{my{/eRt 7]C|u'Y"ڍqF백REVO~cA̠Uʆo'5KvbjʷNM}/bzN1R@$ea4RT l#$ݯǛ[jq|%G Iژܮ ocQZ7/BU\c]_~mrHhpxHҬћ7#d 2wv'Hܤ]O7[`[,U.0vo/t4QrNXZlF50݉]"ޛwÕUeM]'S-J6MloMA&oa#l Hd7\DҰ]ʆm! 7wp}6NlNqtwGPI6oq=~kwReǚ2bT.1$5YK`QM79~GӛY[\u}VTrԪ٘\loQBF;ħ;2)3 :(鄱9mJE$SA8}᭖_1;٥r4>g#DC#$[l]F6?<ʹ?)34*k-xEr-h f/g\ʪ}ʞ8E %-VG P)i.EM"_9ü9EMEy&SG(㦅WNleH$th+O FM<ͩ8Yw)(j,Z6?tR7Mu$kZ؁34 kbBaإkō?7pv!^=;mo|8tTR7Jk ,a7( IfY6Pi*t b܂ڙ}F@Gl6joZ+]6+ ޕbUI1(3|.Zje?8[8ǴPd"uF] PᎵ|æZ웘-WPP45sOVIk\9+TFB=_\.DHSRW3'm,(iޖji0#})ZXpnfckŘVKqK\%Og#k*+r/%-l)١pGĴ6U1$aBeI{O ʮ yΡVf:aZ#HNuش,A8S釋(Sf Te,v'ƍd(Kal] Q++. ۶F B|t q&܄l{L5jh`%[t08dMܸ zrQWS+q|as\V5aX͟:T5CWW0- ًb.Tx1S-&2ڶOl6`׼auV;-(b_GGScuLU,a7?5k6۫~ ii#z~"s=;-s*:-%V0HMSb,م1}Q v!Uԙfqs$,T)HsqYR#). "fX̓[l3!:@0Kr srn[YP7`lJZzu+ 9 /`IRWLm.Dǁuك+|&Hu_۩8rːoG|Y$$ , p,P[X;Xp5&BAR6ܞ HxP:>"Xr*S > L%d+gb{HD/-=W,XSannuoSka" /\, tKX\JG/cїb0UĖ$c4գvU)j(`^S3gT!C#pzHl6a7%_HrX$Q%1FX6>جd̈́ ΢n%?_Ug2ͺ!ṪxXTHm7ҺEmnW*]%qRqSFP~݅䷅af|ygX-MpRa[Ea|e,PQ,~]ɿƱym@߲T_X!>{&QHTʋJYUHǶIFmEl0EQSsӮ6 ZʓݨXѯ'6A}<PT(RF4PY\IsC]@Mr@70UVB;ؒ{ !d pHzDpYEk&!6,pc`a$k1 f x"WUF&i-7R,ڬv4dYL# a$FYQ7=[aFP0)Hq\5ǓO4Nٌ"J3LP>xygTԳŒY&뵉{avdӝ+@Ow_4)#Y}nO^(m}&[VxEʢbpdDxM:Y 0"OMŮoHD bP𛅺(=;TC*[chɓeAP4b@]>P*u#~фнED2VPFq"*(ma|D(@&>=3k{`9A7,aBL8Y5zF i YM8V"7M[[<Tbx@_MnЎ؅QC 8l)dǠ{0BPuzUcx}UQ 70j}%ŮL1 @o0`47E`qxtF%.Hݮ:Q##W@ܑŒjAÃM.,.%ceP8N3\M u n) mT1l )+-Ԏ04ց},7 `~@-[lE!cؽ !u \J(*w'x髪ʼ+s0˩$Yolet$_`/q0W3g xLva_G|rP$pռQ,wfPMm( Y l8YkRVF >$^ëؘY{fto y⡈S!`XtSc &4βzv*Y̴>ZeG$WeYh _f6"^~X{-NPȲ.x`Nk^UfIV+OeT5$%X}?lΡ0 )qcrf4-I:h  H$A댌e*yjr d7"Z_<+Sf-+=5yQwQҖb5ME\1Ϙ%eRY؏ haVi*5LN;niytsRy`t 1뷿/󬾒˯i/:Je$,mͽ+tS+2eq,BXyw W6GVlp\`THN*e1D@\m?=J<ϫ))!*8`2=ϸ\}yYCCA_A)z$*mN돝OJ\ZiMKiF\NãT%!?[]W5tR3j(fy%:fݬ{/'j3of]6ZWPR=A鱽~ز"RI>:_t<)$^*4fHQr7bq,RFqpLlj쾅\jGtSƳy:^t A$9W^"[O*-q?ŦyJEzhDm45趂 |㲓uX"%ª{ǩꣷFޗds7MVF2K1i{[I5HĝDr:#H"i\"K8F!r<ɩ;(3#E aQDFc}EȋFS,l-#Ql>R*?k,gڪ^Ks*jn ЈF:u2 K|qf'͚#VIl$Q~vyvPj[z:rǢ>eKK_4PLh)RBGguX`ֱ#VK 0`aGo^ǰ8 c-ҊB葋[ .U/`lc nY,!` 2 KJ'")!̥S|PX[l7(Fb]ܞa -WeWk -*bZ@Ea MhDU]OoSٝ6;LH 7$n y ؀M9jH*VK*:8iQ"bH) eh؆&x>eQyqpKSQA2֖ɍat5#Y w>9.M-tP]VlϿGma1٢MsW.s ̳ܮ:<,jYe/sU1P]QJzu BAuS" h^A_YB֮n H@cvȣfbJ 9{2W|)l-a %oS:nY 豴E~SX}ҝRf(N|O|&U?L 1(bl ?SJ Ca7KC|׌CƆ2zl0RWX#QR\2h7NW'$C]I$l@5%0ƜOpfy]g~PoOu77ţ\sg᳆6**$u4'igVFr._'a tԵ @>ʆ8,MTE?Ms1v TedT.2"!sbS{eY'6|Al IaVZuu۷xRЙtQ$3:,}Ѥ[dQznlXo0]u)7dR@u8x忇ouĮ*ϩP~dwTۨws-D5e%ba qc?r8Z,9z\,<) qOKe 2y*!AǵZ$AO)] y&gE m3)MPݶ~& D3Eq>[N#s,:ZZdF@C;b$E<ђV4;I0lS%; T-pOCaah[bkC-q $b #dnۥ3yRcp*=.NbUBzAz~)) z; A TTa*[ XMNRiE"P<5!!wej!d"ޒ0}@.n} % MRmlӢ85u03"X :P\ml $B$]XMөpr4xV{k僴l}׾lm ] /R/]fYi:}Knkre$*LL4)"1wӵ2Fژ8_OcopYg<[1>G<S@ɕ|ѧUY#퍏/^UN)} g0@жyLEPG/QUPedULI}Wv"* ֢Tyѩz[ct|y1r,48n,]GJX;ç.U]&+9ʕc5Tפ"In-ԓ)~CguTKSC0USBMn66ܣ3ڊ3Y̜XSKHLEO6|+c"gذwx~(|簛_Q 6`͙PSIEeEW7&9/ZL̋a{_OQPgq* \)WM-݊t_{rc$kx ĹcN k >r.bGu깸o2|'1H1$c߭b$GCdU5plԮq$DžFܒ8v u2Lr%YlbQp e)2 ˍE 6#nحCk.ׇxIǧ O<):x .Wq~gmȹH*Xe,>̳,;k)"2bY@aFOO ~IpCJ_vwƐAY~kҶqr_y&MS#n#_O/y]QASIM5sz hcBtaSDaKYŶl*oDqݔNź8U= 6/S圥:QF !ؗ_J٘\+G@8;<[9g,4L='d[LFf1\ݬ8K@=h]bs'TK Y0#$ S®TwP6KԌ,zad bA0bLH} 8m,Xm-–"4 ‰V=Eb!P>?HJn!\DN,N4쑖`842 Zޠ68r%{tboÿL1Kk}/Zы m1ζȌq#rzmO]JvUʐlz\Jik{$"1Z "WBfa,V'\@7Q"}do܆{aK*q[c{6ez"k AC_Nʺ7f6À^;`ow%[Mܩ@/a̎Y F]:# -RP_OSjp.Azc̯bB[}"h.yHM!I S@qA%S/H.UcרH tp.l^ܨ,^mrſaM5[J0ơu'($ȍb_彶Dzd Ѝ%X !݉$up2{%  @6*n0eu՛A i 0 07U QrF<7=Z|;W{%JZ7ܟ@ 0-@nN6 FFBC ^Ol8}1w`/ao|1Rq$e6> 5gtF` I@PÁ&%;!]D'm_-m=H=pݗ'f5c AYRek.oˊ6,!8T 6$6$JѮ#{ DQ(T(u,t*}2.6qVO9%n}CYm ߭U_W GYY9GY^[r g fk-j߁ыI,X\-y~2ln"r(%h_Q0Tmw>%)xxs@UR&MJ,>_'xq5e5<\/*+\.;{XǛ3Q` `_5z\WM.!\&qS{Pqm\jgJdU*N[os?s(rn42F au.mjh :@#\uȹ3fVgh:'dؒɰ XOQqY\7ǁAJUesi)[]"pM4nQp:>}VM' f4&cWe*)bWWյ2[KSk.xwJaW!*{=aӾٝO?/{f1˄3s'[z#*Lz?|zI+n1F08w2\Ӈ |Ǝ`}q[U|"= gGY\ |ni []6웳 ؎Kl\*1!M. %IǩLj@6%$wfv$(mlsgY.W -;:YR!`snظ\4t5U,)ᦧi& j.I1}\<~Gܥ,zY/i7l/Ќkn J,}]^MOrsE= sgveUBT$aX7 2ז6FoH']AT%@뤫lJ^dH#QoM5F'~❡ j[!I'n p8Ug*rO텳FQCa]lQk{jA;*:cB܋ll-kqh">DE]GG~`$pMΕ {>n` +ZlBc\APw,x !S8U}el5uŢ$TalaS j%m 65taI)f6f/ZKÁJ -z. ʠ:%#w6.Ǧ &JJ-Y:#2Vo1f(Z=֝YOE=܆׵ب2[H. l PUIܮÉ񅍊q#r_ eU({1;0t.t;5pet`6o@"K`tr[˜X@ۥ$X|/ ul$tpr- l] @1`Ld݁}~HdyQ*u>l5 ZrύZ ž:xZLXjW=6]1k9r9) #/齭s|g ֚z(uTSHR;pvcy yN. uz;(`A7) F)~AYt†1XTE+ۏxuOkFQ+IeO-:I'@@u)܌z h}3/Df$4-RkgpEWDEQubT탡PXe퀷=ËrTؑ|6J%s|.EmOfbX MA}q9,騆b=meԽ|0W#8,ZK4Ń-֧VNW2Apl@"ְ"MJڬBMd]EhxnpJ \PNdunZXxݐz4' 5ZB *٣ʄqBW:NOU1T\$ Mb} {Đ{/4d}_smd)+k˸0aT)Q:? #=cS k7ٙ\d]}^[OK۾4LΟ.3$&jl߰zXGMU&ց'ۦ4,Ɏw_UYVu<3N1, ǞFYKmr$xR>ha$CD&:ʫ$ loc)# c4+?5*+"_=E{t{TJp b*1$TR7a>i:MybO#FpYcc(نc{l* -Aa"ءIǸw#ܝ k]bT, OL"36o낗; Jb Tڴl7$$vA*I;a d@dtVMPD(#|Er,kV%8O|49le*X$uǚ4{znDbŤZֺ,ocb9ҥlŻyU[dNl|\ [. 0}N,QG 둉$_.JkU2 {Ë"[/'oV%WKbf8F1;4)"Ecg6{6XX(DnEF6[y\@ʠE8,C+Pbx~NE+ * +$!bT}# B|Yz '5yW|UYQ&uC)*NA-pY+k jC^U}4U ?Ũ \ ob7ņ4T5-|5pNWEIX*S}>Sgs{e~wW+|r.yizZ@AkM덇q\AOeR?WtiP(4Yf1V2am}s.Y]L*h$[ :gPf[=Զ.v ,J RFߩ3)ꨝy5Kۭ&s-5>idTC]j~P7`nbKQSrRձD͘3xጋc{܍K~æcx SUZ4fkଦ`dH $oqpIUCJTQz%jHYIܵeajxgVMN1Um߾\59b!gp r m{I|bָ\2M͘C-2Dۨ$~2)'h`XuI(e]X?O9PeTc:aTt9},@y׈mKO Otx(d˼k4>"9_OI aW_4hI} n>ªɩgvb"Dtv>~4kʉX3 yD1i Xoa${6[m4lYazi磇T-`Ae$lH'yoO!]fQ.cMD 7 An.S!ݗ߶ DpBWkk1st31 XDp!%ZpM7)t%V&mc|HR_I  XUpdލV0 p-킱AcdΪ'cװs]^U_ ү8XJmS ($` 8LĊ:r'ixȎaAU;WkEXKfȽaR5،fݜ(UM.V:4Ofg|A[kd,PXDA*J=O|u5yK֑+#2TzH;99^YR2婋Ύ!Nm:ՠM dƪduM%\D[UE-쪸䁃-'B kc D.#h sp_ 餦G Դumxc#>fv[gkrz+?7͡E0$װ67f>^A]C-ee5I@$~ӷMS6UT#GqmݜL)*褂z j/%vv8߆߈}sOOa9ngSDrHF(Q1R !S}[ w0҂&Q[==%AXRB1o6td˨`$b_Q=rC{|ZG^3sCGO]u}]Xwbm7{ϫ@rΛ%IfUG ɚcp:_P Ve~hj!bbp@UPSҢESNWs 1O zɧs ez~afA͓Y!-KӧLksy+eVf0?L]` uƏ\GV󦝩Vc W&zcF#:mXCQe*ͺ:TኋǙ+q@رH'ɳ5؁Q$Hu'|NFvپE,Ԥ 'knE{Xlޛ*{T%lc\}tm{0`o]FdGb}8Yu=)ı+ Fgt5-H;rma?-/,;pVžNru4 ; >pB,^R "@>a iW=n:aIZTVݱ+A-Y5Vi*keBf!HIvrxf68"'gAa{ r%HpCO/0cd1.PaP+vA`}DSYdsmL.'mĢZ,,-Xcr6PL6 "Iٔt? vcbV"Y$X0}+\\7aftɷ[fm}ST7uM,oV)H\ ӃV-{3͗O}lF"iMJE< ZL[xH9۔"r6'{d i`z^*YAyY RFmԛפnEBu [oF2VobDni:Ydb$Xs?nbRoV +(X7qmD;]t@UlYX@ w%ċ O{`BQBOЈn')-`/k\ j QrlCѶn;|(snNY{L7m ";SKako ; ol(:%В{%*;:N\Z6N6E[$ ܎|82fD!]]5=]erQ~\dH"jcG47`[ WR>LOR $i`{Űw&&*79sL7rJX;nx&yEe4.WiR Mw8& ʸK&%*q*Zt߹\UdGg55 H 1uk5)9O湧 l+@H0nMnliY~c͊yd(g@jl-km /'ˇDa'$d JXqܛ4Rc4qhζDuqG 7xqHOmǦ5ož ^!e|1TN߅(۞cӷ:\0D+T؄H}D&  q7H5ػ{q}K6y~?!Y\(faf}lʪ~㡎Qx4xs ym\%/VPm}s%iiZI|Md#TYCq#1 2*U )j62J۷)* },s09zb)[+.7E: T>\l|nwTtO fpV4YC#a'+?r6-93Hu u=9hƒb^:m;{s%]eCahΨaEWM{UqqWPz $,tF[mq߾8~z{2buE;?õ>#mU14yk}Ek_0FmOR#f u>knqqh?"h֒&b1φ,In9Y6Z ( ؓCa;L\öAúQYG>-4<,K.gfmj4Jd4fƆlb߅,,3-Γ%*yL 1]Yz;~tHf1%i-H3Fs7R${w {u0@RB܆0 Cb!ܱAŪ7Uqdy}#Vj!kh˖ENo݄THEZk[+%;t&{ad P\Fc7*cQd{Q`\Rj" .$RY6|BIJ;l! { os.u,HަS\8ok.:o(ˠ1qlI@u-E #r-a*@U؞ãvn۸8[h‚@aac `6sѓF^9 I#I; 26wAv#TqLFۧ4( fk;imt EBA7 ܞƸ8lmVX* vHocb 9H^d^HuH˽R悙ܢUNkpw$.l[ IҦB/KGbpcKbM݆!p-\ERn7bYC1S 44zbBlu7gKP$gbBm7~m'Y.n0Ǜ0ͻJsHjj9dsEW2-^ċ#ƶf_:Hڵ+( >YEݏSnnoH!ɫ]8d({m{5c$AμV<4V汑 m$[V 0du :bAHI.ea :q@!qjPIإ'WU J.5n =%|}o#̩@Cv XrQn?qp'+ Dc:F9m%toCԠ~c GH'Pk逦Q% 1+ }иVPa" 8Hb쎲m}𱕈nkjsypaVl;7qVg.g::rc\{opgG 7)snoeutR@GX{l(QF)xl .WD2.o˱=;ctT !O4\Lfxn8]S(;á1><5 T9]щEz}|0em*l6WGs+8J '✃"fk &ǭ)LҐ,xӺnŰ%(FWMȵž%xRȄVS"*fS<;C1 k}so/JۉIi-J\`_{鍒 ԴB> rVZB5 [@GE0H66bzj76pR䃊'SOHNDMQq3.l[u( k1ʸyB;6WUʠ$+.1 Ǐ hg:ԼFI$ [Gc_>#86z8|:e%`Yb.A.BY3Yͥ@B|3,i5]Zlk+~$*̢ Ɲ؍IA,ᎋ-(k`tOW<#2s#ܧ)F[_TU egO)np4sGDsj\8P}qtio ZHVg,-cURxA╧^hh:V%9>vՎ{y^Y:DŽF( N+E {/wSC-6_'TJō@C--.Th&`x2k{z\|e'x_Jƙ|03,k=7 cI-4Ƣvf"%IؒI/ljBᙶ6гfK7MUm@r*Mu퉳KY$qZajg; ָ6J ./o&':r]\Ż;bx=ʡ/q$\ݠ&=P}ZlXxZcqMSCy G *.n؁>Xڦ,$=IBA FQC|\3EQ[IM_m)jIzGX\csysJbs#N%We &.rJffI`M0>15rY",s~̉R4t=qPpM3MSQ`6ģVY"b&@͵FH\uVĴmbx'I,J[U|qS UySCIx!S^ֿqb'Y9GKTSP(&ifInZ6V#|r_<4TUۮ?Abf C$wZJе2ȒJA+|qp)傿M_`w$H*jsD 0t%й(~YW9nU1[} u-p- ԯ<뼺 zc J9*6ELMVfkƣ|u}슮|כrWCAGMOG ґO!p̽i{&9ܠZv!u@8[ #J+&:[Ffti ɷ뉉1$E@Pkb [Ieet(*H27]{z0)7|XiGĩS[ūPZ èLz/kJ=ucv[*8.~f'-:Uݿ%UMeq(UkQRGUnyW_&QIe lmqs3i㳙Kf79o+D8º;궠?lxQ8ß)&99{:Z(I$|刺{u)9ǼQǧxtniX 0w[`<MGEXq>eYZ$2K-92}6Ŋ53D qOuI-}Oa!+YWu덋e(i{eGV罄l$p'^OM?.x)*XHTidffSkcaoChR4$B'M^Nُ,ޗ.jᖬ` oMv#? X󿓹\2|(p nN,n|)f5s*Xu3j\|qĴt5+yt1<)`/ 68˱%,ۊ͠L<#f̫+ke"A!sR |u7qm<9dzLfZwm3w!#Qp]WlsK,UVΌ2Um;7 <ǖiGiIuKEfLqkk\XQy,9 ;h~Z8?Ėt H6퍦 Wd ]q WK\ 3UN檹e/e u[r?>mq^Y2 ޻.TANc[UJ?[ EcE^.aM <7i A)ԏ%D]Wglt=?_"xOiY+ysCÕ JG2A[(% 98uY'毖yj @l*UnoӮ;/ nJzI*i *pc_yY#E2&4F[E|8ʹO[PAHa&I i(;{tC4u8!E5;{[bx߄}s8㤤vaJM5I Dzu8߼M .]RwXZ9 E:pU8 ckH9_@ )C/2(pղ`"hm*]T@W{ h .e€Zv=56X/%pSK p~k '$1(RP0\zY>Fe$64 nߔl?Hv`u9D&̺t/|<\4X(bk{A$RF"gtqSrYb‹8f ,K"v/MU|5f@/ea?;:WR[N,+^\XeեS >^VG2Eӧ틐DeT) USTG9=q"x(3Te|T|za%UW#I$M),b~n|{Ut2U| %ޑ!6c~$<=we١PqRf5FCPl[ۏ;oyuCRAC i2k>DZ7^G-Ey_}-[̋caʩ;SMSjd<Thmr !: SwHqp#_Y& ,Pa$%مota}!6:zaίIP4gF a Du-3v2(׬Za:b` rlH_IcVY=Fbq Y}˶ܯck}q,cPF'r# cRFm>[TĺCk-k~VXa>BK :\`R5煗&0Qs;N~ST k GHId€^,됱fB,m[m=p +q6L9fp>NqWYp]w%uI jbmkw ,|ds\Y,!Hn.UqB\ j>ܜu\C0Lܭgϙ ^VH㖮tZxՊ-k?qͮmf:bVV1ϥ5)M2׉iW:2K>`44B{KclzCLւ}Js+u QǟynAŹ}G,Y Ǚ^e=:ttuI9}qUUPV²SEapXUȞU#d 83*QTX0JTAqb>>yeSxt)`?J@(m87Wxg{-<,yL F j\@ X0 kwLDiVwBl.iݺHI*l,...Nѹ7ҥ:[,?-N"Ϫ݈mK` ehТP/ |,;3..I[EU7 %Gw:+miyV,,1SLTv$m|)6*Ŋ CE3 nAӥlIO"V$mp}*mgE׽[ ct{)j(d1!m7D2܆}+F䱱H6Qkcrm#I7Y-'D^wl!V8H?߾!Q8ԨCiqmJmm? O ̒)hp7U10* =DeH.[=y CC 89 ǘͲ a!-{[}9y)MQ6 x&Hc#_M;+Z}Q<),E$]K>j;1(چmkpf22-R/g 9jt -rxV`mS O|R]#BBҒ^U`=5e pٝ9|cC ~hTl, }Gn/|+\)(ܓ#Ngd ҆f܃p4~bүpJ<,`m~4Ha,3  -% O1O) hk en 8!_o=ʀ .hpѫKvßo1Lİɥ ||]u r@!tJ l~xF 6ܝ0% DeM.4)Xآw%Q! =Tco{);xŅ,llw|f \}_K],,EUf)U>V߾ YMmӁ3f@C_rOQ Rp?lSGeI6ee(QBsQ=^$y'_Lۘ7J)S̫A^$Ђ}cKUFDp*<'w>!d#̬Ol8Yһo|a)=Ҽ<3›;|J<%dVa̘3P,f[4t qCӜ>OOͅF.fo,֍5#{HSE6?lk[+x;̦s"t*ibwcN>dNTXoarNQlmb䁿IH#d ڊ3].cxN,Ƣ=61nnn6'll Guy`pZFQ=Ʃ/\8+[Y;0lz%FpW(hS/4U #{ ZتSg#,xn=DflIsC KT]Jߙm#5 IAB166l °7SSi,A\|Wgjʡep/ackO a|kUQ DAY;{q/\b֠%;đj'|]lkk)4 @ֲ)R@;c]YWlFhks摿Fcaw™vR `ar.{źMcJWq :E͍d!@b 7;kbYI'ROɷJ0"@ Ynq̅Pٺ4ǵzCs QbnXo%Y|e5Emd54Lҹ;䟦'GW*u#xg$4Ԛm 7l7Ƶ0JK!iy6d>!/ i#}"\E} pNz!ܶx3WcllB,z9f\)6O_ tʢ$2//e<͌5jiMD꽚#STShufxʨmvlp,K^g9Ugħyk }5Fm´TUT2~.>E,5~#%fo=Ң|yCJAmLG&-Vf8"7(A2MF筍·roCxi0wm=o|/xȲ2^yYURقcϧ{t|m)Ψsh&IP:LX7M=6qFuj>b1kXmB.7F^2,X]؂}+/rHBZP:]lXta&77}#:+$lwK}XYSI_M5-}$5ΆX)[[.xxK8.3`u}mqonxi*8ziqT-:R]؝4| h$~ Ϟ!5oN=$ h2˸4zdF\؏}cOi!vg12 lƏq]UWJ3LDZ 7x#ɹ;eTPMIQ4bD8z\rRW;^9iPf۷fd5V*cgYGmof.de,Md:UO1R s}/7csAmLI"k8$S*#8)K$1*"h@v" %A:A; k%!m\F&AaaѠ:f Ebcsr uO1U^K]`!.Se  Dtn-FһH+[q2gSō pM?[v9Y&fpT\/KL IԴ`\/6vAqphN1FRn6p'$X H+ZcQu)MGP#aI%[11Yz"&f;\z;`7avK"0/Co;[V/r?Ằ@coV 2"&䓰| BrčtSqr0i!f7 _ >hvG+X;%NT`bA낪om}kEvSrHaWF?wCvK$QX,l eH8XS3o0nQ2OOR^ċaGL3r R@ jrCX Z4v3IIdE~l.V0 5PDZ幺Sƺ)JVGOPR&q@#m0?5%Ɠ2('XrfycU\caXk٤\jo~ ֛.̇ܠ8|Vr] ؞x|?c .̪cZ\6@ |l)9wC1X~{COFi2+ { qC^5Q37D P 7]>3Kf!V۝.Kͦ8l#YG*igK8JZZ?L"=Ja|Vp~_C:I@1{mǤY96{mLgo}5^Sͮ9D@\vYݖHψ/6BN3l,X~+xs4.I ܁k_?<[ŕęMURE D 1 پqD\=J#9*+`:9.|YTSS3<@b:_{c^+)2D4{~%)cr9HZivOͅC%Q{[k5# z n!6X3L5BfMpְR@cx "}#UM'g_S=.>M璭8+snYq ix^,LSHX?g˞o<|A#U]#`v:=/xFVC:4+ NAHHsr|i3U+˥ euw>e}\sn kl&Muk/ 3 jIbF*\R`Xt-:Rm Yֺ,̳ ʩ 0Jtk/ьwې:c>!2ngZA0fbEbA|&6A<8M嫍k06'E"틸z璋}R~领WxREA 5$lsY?ZC$#iR;7b2F#0K$!EEo|uZKEF/uSMޫ+]6Q7c,e,VJ9Q$=vv߮ش Rlm >P6\¦`z͛N=];qbuyi͌( *)3LhJmF '` ۨj(O |Øy>qKLt2C(Mn#qNOVIG^f1" )ܜo$.˸`2%e؏Ĺf-$cJBV(Y |ö(8Ƞ0~[;z,NysC8 ,+P}_Ni2,کHԙ\i@A`6.G0V[XZ{s.(HMpdS )rmŖEw!1|Oke~/lsځ\m~U&=gyTք s;|2fIc2gbl™SU=1h/۾폦L l ? \{V>T42$xi2*pkK7(%ked+2zwc)cO'k3ybtu$Eq4B? zd&Q~_]&j4_(xlw!UKDdA#6v1t}polkҾOD4! ծmqoF51 G [I j:49Լ2Bww&[H@ g:f)AAS]UOEOR@UH [w\p HTRk-bSWfkM5l.nEk}ɝ*@0KR̵͉;lvy8#Yg@y|TC*ƨض(wm=H⺞UdW+2Ekkl{# Kc]Ks<UGmkoo率>L|AWSEQԐ BMN–ՙFU! ]7T?2>*uFwCÕQ,•SEvVuꄌnW 5.[H$1x";ao&>[8M.$>?|1 6c6p=4V,Zd*mpK:SWě4`k$^VDh/^߶!ˮ9/mÅӪOuEMb9Uhp&!&a!*u|m|i4H ƭp~t!\u" $=JRBmFo{AdUӺyIxXhE&otk*"Dȿ7}GE߭$8Z>ֈ#(.ã4Gҩ_ 7 ']hW(;zޙ V!ܒaF]o)|*I\u 4JJZyw K[m]wÙJz5?g+7 ħL)d7?[cg򟎨3J!2I6@UI7::i!#gYQ|uN$>Bյՙ} E4m h@P]{2vN +\]v*FC!$hkYIo&MSQd}nkFΪ5B=p_Eq庛ͽ%w*͠" !Wë T+}đdD6"SH ѭ~Fn1,i ݘ M=\x$vhِIoZo SS*HIBk1V""ݏ遗dxi`m1:[,Qtpe q4Vצo/?[x%Y|" (qɓS[{¥3,˫'\oi4H'Fx3h9.uO@ydQB]RJ$$ҨUEA@ XT,1`a[?8f \-1x_/g[gԴog^HY{looc_x{ypQGXhLQʆf)N{l-lv@CKܒz}-dcK`BWKM>MCeɷe< 撪Ϋ NjMDkOij[4Y$++=d<08fz .$hFwG:2#Hm˨Qc"b)e“S |69d1WMI4yHH(l @\m:7DP#X76qH$BCiCk6o`wIN o[-a.ŢU"v߹US!/ cHT HaSQpE%1E2;:$]Tlnq3F@u YʭSkݰ"MN lAmD) Aﯷ*(%K l7EnPF4SoB7O VMmt'AA'hwoLtnONpdvԁ.al3ukdSnR@bEʖ^L/P/$;;Ydt[#l]+HA .6%S){_,X "MF)q%)K[ؕOTIQ<+Pbb-qbb+i+)⩚@YǦbfuP\}'76*d#ZM,,:!Lꈦ2ݥ.KKlmy;, YskJ2X2hHaGVYcfX !wsMI)\GC*}th :-Ħye ܟvr8B#nʄe;\>phMmµM,  kb+rb5i|8y 1F[Hbv=|+Ma#12k ,u8Vc .H͵RT)/ÄNӹHF`ǰr#6Sױ;HoMceFě[l8Q"P0]7mEEs"F5$n/^1'kE nBH1k(3nn70T+\WCp79+ عSNb؁BǑUA v 5y:oql [w)d s+F9 mnw+O9; >9bEYLvmZmFW^l|ټ#*ne ^ΏC(8n3N$|Jm$Ϭ;[ChqcLγUutD@ !&Ʒ?̳e eo"|X ٙ$y_7v'EŁǦ.?rc<#12(y~ w1~ms#9o/;pu>yZ1RZk XUf,0SH`3csݰZG#9r O|c΋SBGc$@a<*jj<>Gu1G,575k!جj/mc~CxW~y1܋2u 9}-$O3]$ ,.*/8>d]MIs`٥YEG~K4"C)lܼ:!"1PłPAca[ E ,ʠ]T&#8}fG|cw0`jVa>Q<ܓ; /^fǺFpcaA,Meddх{|Lt-(\:sυjgH4I#0)$^b@פEEU>]3#=ʋGO|[l0i*!Xk9U4fO-1}EJy /sQN05~"*YJ >`nJ߱ /w,2%3` $9k qq­ IPqW3PP$zQA!ck0ֿ]yOT7j7f![V㜤hadiI`M|s^ 19?[%=D:ԾBG&}qGΗϮʹo%mRUJ^bH61u.Ul}~EtI4HIo9ѷwjrOW0=f>[pR.gVl$[~ 3jļ7ٔBrҮU$:i݃c P{6Aj?E28@|E|L#pEʻwLsUܲ)SKh'Y.7Ũ5ܬ# e𫖣^QXX^൷-Gᣩچqtu É.Z}Z#a߮&a 1Y+|lJ,[^r;},ai5oxCwQ̀q3AwvKIϦ} Z% -#2ixV%ҷ!Ȥqӡ6|[83 CBTec]lNgaά8QvEG nSakv"bآ]Dv!T>>> |:邷7ĩcBuӧ[qDxgj/\R|fR. [$ Y7 p%=Bx01jV7FP;> |I*FM?/N&^>mqLnvoon_KQMXtd($v=1~d8p-_0"7?Pu[1KRߙ4 e¤I!VLrOճq28Ƴ5HISǡ}W*ۭ6Ǘ:iix UBʐE- `nT,sYvP[ :DӗXQ |$J6"N{aPGqe&|zKSI?43*8xnVE)7S>$9nK"u0E(I5[lY\7k?PԒY?%UGH0C,: Pu\n]-]=-]!d=3u0zܿy3l8,2J6{F%G㛍*V.G3!$$C)Scpi*7]#/ Dsw訔ljlT\#Ռ/\eEc2)a)F=|k!IxgJhs18<I k4ScZH44\=%dǫt/)gU?h#ZANJIV&C4y e -TSHQzO =Id?sUUg<Ut=隋&_fG7+ Y6_-,5͘YTcX_~|,UE)bf1Ou'߯ ?Kp>SgGiV_&AG @ȇL`:b~8*0| I**(},m֟k* Ҭ-歙Qmm'`.,sFmmP%pDO&URzس{}|5d~glVu_oUxczz9%[d{_H uA@?fVfcn#9P˫#+2'F0-} Hp ƑEnf=Ld`Æ<Lv.pr7\q#0}qx?"x+ʨ^jq3:. u-zVQ:1[^#ܽmL!G*Zױ+ß#@P,?.˧2QSdq UͮASl$r G5{)Lɹ_SĮ`~gCFDEZI #N> ؼΎcGF]*(=V @× Q@XpVc 5گފ(d}:˘)~%z œq' e6β\Bх7fE6 kq_+:K?* e#IMEH35 j'Q>h'}ztٸ#x8+i:  S90 ䷎8J*VRH;7l] Zcj!n{zF-n67]8j7Mٖ0fo\KMGmZ[3Iu+c wǙ'HXu E-;R8Ӯ}KYܕ }D%wb+Ŋ}2RJ-^i GVߒ>lL$J;{DjBjN΄?A٭MLDs'` {-˫*h$QKNMS$/lb<.nzise8t ٽms%q]Èv^ +zXXUdnkT,9=YUr笥Ye%A5[Mtl5m%4R0(%[iٿ*Ú)<;Mruw_EP}(&q-wC TvC|2 "=Wy%G p,R.FR(Ǡ q`=VdgZxHcXƯ#4n_y|%<4NH@>[cV|~JUUrj3jxk(6d˗Fج˩iDIKF*@ 9X΢劸B7o|YT_c}cL /XU 0w6_輬vS#P/zEm/`, HViA0]I2R(ꑖ&pH/qjEAa1ĮEO?رfev \om2H u ȶ(=)sNJZ̦)LlAUt"ߨ5􍪥s\. 3LS.hx&8?<5yϖVKEPxbl7-s|n?wdӻPSQSEM#\F`㠱&1rpI$;_O'O[m~;ȪH"rE &ĞrŢpL?#wlSu&JI-?e( }HkqniPFæ%4]PDu ڏPt/+Fˤok:ktݰ"V{V'[X'#5R,.IRXuxϬ_q:$AucGp,%C]@mPd@EU l]ra:U0>fۏI鴧w½J Y-*s}|PP6\10"oi&%`Zv2]C!@\-TPl.~jcgni &R! ٷݱB` =p\ӮޒGAJER@6}ҋQ{۾ '6J=bάn!SJEi:J tP-k_|o s cr+-V:? GbY } n1rb_0pT_l-K  vn7酝+6෫灒 ,9,5HGZͣ`MminFu8,F1BtEɃIWV#jiaG FOiMft۶5z< h܊IKZdč VzwjyahUPH#k{}qFx_Ey6^/> 0bS*g\np7p_'Ufl->v+vi~N ΝXla碓.dƨYI%FsJr\3( PQ1c"DU&oס[Uԩ&yVTOP I,,=1ÔM&&v;}!-~,).+#k9 IS2AN2G.0rGR;>^qY|I2aEd ITU'E"Zc\dFOĩa{c% @"PF9Tsf5F9K[nz8u—䜕OK jys PR|UyUd͢P0ʌ:`SqBj))aocyˎ^Ne|?y1|_?8f-N|VMh*w1s$*mmoKHLNJ"2sBڡ7[s063׃|Yrx3 UgT3II^"-0Q1|\uk1H2(uydџCkICm GDQ3&].lˢq?Y$qՙ[,/Zr~!*Dv/p,~2!O{?\64L#qudt"KwtHRL Ԫat@\r۟\22NB/[>~<͖El($;wa#%\e[l10+$hKSG3UB:y1M9I_.FCoLsW9nS-BB|؀;_:3 x(n-RP3|'1ڭWi c'xѻMܼaTB/+&yAmbcxu6C;$MS,|i&,fJ"^Y) GZs <2:#k-ÕdՙNALU M#"jbI}OKsۘS;ENqƵl%8`i6P4(2]mI߯x͊L SZ&6wg[` Hc_瓋H!wlLnSřQ[Q&c .oU]M[4tTh@#ѦXQc?Ckqa< TmDfUEdHՁIbMc=YfLw J |΁3 @w&~Y0ᤫ+ d&KNĐAgGVY;I]c@Yu^xSI&G^fs8ijNbo9I]PlF7 9}15=Ф92NҴtzCNX;gY/QWUeo5od3L֒(ƊoWHɰPrrḌVhs?ɛt"0|jAmʻY}mo-xvG˶MXUc#_0nM=4NlIA/i ݒ=S07#uU 8Z<|0z QTCX YX1#m6u~"y$+בooo|P+ɞ|*8i@ܪXlScܿ&n 9a\6zMwkCJW#lnMt5YX';UDp-.r]Tv{ƥsijj))dQƺ^]'{g\&J&Z|+8YX8f9%-lK#W*AU`ĐO]Ǜ)|fm\;0`#Z(t+To/%f^YY_ReP誼"H­m YOUL4PKKie@.:@bz,=F\<*MK]D^H(a{o˛.5Q]s 8eCHu8?r 52EJGo{i-Y4)"@N/{؞0:gXgMF{-CtW5v{A4*3&?KBj!.7߾7'Z*|mBi˸m!o5EHݵ[Z\hO*z冝j0GY@$t@??sK#c5_M<#'\w=1lTn$W4`ۅ %K,s,4 $HH5ڬ1JqԴHoNJ׾ytˮ@@iOKAWG41X WNjȓX k}9nZ5Տ&6etfU;7;qEe"2MCOUZFHu}}A(N2iu/azF.;_URNjׅd4*@,ܛ [ه3hEN!h:Un">r8PR0jW35y’icZ;(#o)#I3m6'm/RQ$˫XF|wnݱh2q뇬n3ָ?\&J}{|;KR:L)C#s3] &ع ёMQaޤpbd)Zu<וouVuyJůeUA6=}+ˊ#:4 7;=6{!򠔗c(Ի#oKŚx1ZZy)kK5bSmIRwk@Ć.WSI>aUJ2ԃ nRL9 kyKKSCK,}J-9ͩ&o[ VI7,ۋ\oo}2e[z^J^iZO,{ nIM٨]8lʚyH`Y`,;m4qF;-VEz6V^B"A'Q}N9 J6p#Q65xP`/"™b~v70hѽ 3i\;[‚*ZJj鸰>Fe6}\bRHGc{oJ f'?cv6AZţl Yk|"X(7 n3fnUX}pV) (FUI YwPv#p ] CML+( .]B[AT̅Uaī0: {[ԒƭclJGOKwO2 P-Y#fc|eM}pkt6U#]iI Mk/VrP}{7H>Yp\ }2/Ub dSIIJJس|E1 |KIWDLuS ùY2D 3r Y;8QnH0>uK!}F}jVpIIdNMw>MG6R=:~]evNW \t57D`]l rnA!CclyE!E]ljDV2a*!D}MXdDRoc0YXI&ᩨB0CS:X.~h-51HC2쪋Iܒ̠_X:_4+u{b'%N *̠0#] wIH"{el@if*; ;"'N5z pbSۨ YB -Fi֮S<nQFGe-eӛQBY.WHpGf,F[Jڕ ~S!xb5`%XrGC\U#v~Lˤ"n) p+jd*n7kEܢ#Q6e ),IHKRM2# nH4HnE[X9 6e͒g_X%Bn\5³d m7#s;E: qFjk`@ {`%zwM 0'U XYkv]622eIa}$J@ љyIE; Y H5c{nS4H ԵdG{wd]0HD'C +o.+S]#E'33zZIRC"R㥘o.OkgͩySAˮCU%YSvB}0u{u8_E4ubfg3Y9/`- :i߷zKʳhN] ,TBk!|G=DCʋIF7wy+7DpYkd P[`q2#/,(x%F7w;>R'Iw7F}G0 ڔXl+amrNܪ,X[`Gf]U1, aZC/~8#RĶt|{{Hgff#c*Y$i!{*FN>R.l;n@Q IQl2.b~fcąArK|ad@=BNGE"/u){GAbU%[]Cem8/|/]O$T?(U Aqpa1Gl:`A _cb w,rԛNkZ6%vEl"Ye(6l@wbnocPp7LGBd5H71+9[K\IqW e-eC_z\6$$>xKQp٩8k^RNAwBq_7-ϳ_ YbQZ(8'(7 “H#l\.8 &ck2fOU=g=II` ==qLĩ@k ײ渌q9B,&bmS:k%<2(Re]Hmm/<5J"s&r<[2` `HRz?LgLJ>I@;EuIj X%E݇+N!W>S-_S+S><{8W28gcHHvEijKwCU_=BS U#PtTsu /.=ʭ;/o%,eM¹.f'1z(ytwwS:6[5dZ\1nA^YkYGJcr}Biݶ ql@8퍹(TPqhI_btl( e3\ܭ/iFȲj6Gĭo}퉥-"EV\?}:tE[`ugHR_s})U GLf$iuW@m^؄ti [t؞"3{UZ/'u:#5)̍ u'tmR0VLw>(RKOqgT!uX+ <(brPkioKDŽ!z~UuR) fkf;\_ʹ>AYNSřzQfyAA]W)h"鐚9^x1etQ؋ }-2J**J7q!Ǿ(lT0 _Z˭gZ:}:S_3.f:s+ifʪeiUEzv_+% dflWN訆BYH*ShBƅ.u\^iDŐ.mf~d-mK=I''XI (cv-`Bz8$Q΁\D"UQR˽{*1{[gVb TXvoqdEO|mtsD{A lG 5n _rm2`T 1dX/ I%0pe :N#P=lG`nl쨏 ev1nQYQj":A%wM]% 'mN%+FT+ 0(:(#p!Y__-r8<2YAb }W616顑|'kDž.GpK{^J#?[鍇f "l4cY?:XA• 5T9ȍA`TU=Delg<78-< xk夞V-5ػ=C[:_}:SԦ.GJa@1Q1LyO9@*jdcyM1%K^p­B̿~h7.~8U6/&x3 !IpWE j7v-\7p 킂Xl>m.[0R[_W_JђC17Xj>{ޮֿۓgVp \ b=W?\Sgಬk(id`|J!$$إ,Luzp qQΓ.m9ŹOuM%\3y[ɨa$RHͱ^W+|OWa9Y@a"s:8)xÊd*Ldzv{7&Lh( ѻ؊ث0}%Ly6G:ӵ4K}!2k(cU+Vk2\Ef&ޒc8@m  0Y@B]unGه}Y< a8@CL *,WUt${ې-fJ^hBJ ]TpweddXn;a7q{'|bpnF u`={?= C}Q(*#p;9ΛE@)mp>m(FĎ u:MgIX&aeCuy[v,xY0l7LZt`]V䝰L-V>)ĩe!T;|%[Y6$;[n lZI /8(NJSJ" PGEƬ* ;t AR[`d#ʃb 1-[2*=G2fh q{\˪EɌ\#Ao=xiM ^B̳oͿ$AXPԿlIn) —Xգ;Hf6[ Hƕ܋om$\#ZV!_q,d`11H:o|1AY꺃m9Vp즘hVSY$Mpé i%> $XϕBOIU54i1%YX=$k<]vqJ+[5l D8 ÒꈌHo}ᾨ"GoEP/PHq 9ᇛ|5)n hϢۮ8 1#WHi[y^Bl\m'.$߈,i;JЄG O7I GEEAe 4HEn^8E k6cvf*,@w$ OϝqOY4ci:w:X M6Q܏3^umƮ[=婆,kd-6cLKoSLj#s}ܼL>YFg3Ee4 URcƴ>|/Y]#7^RS'GO#]UZå6zCcG wbǧTUB$v pт*,݀NR R52BnvߡRwWJBˬ|T~k6mG;O$SNQ u[c|k`cbM6>ӷ,8ʞQ=ЬTQƙSR3~-ۋ_{}uƻ!^JNx#"N/\ (nȤFϖ@yk%GC?.k9}&sfr-TH*3X8!blۇ(YWFa!as34Nj~¹h21hΐŨB)NZ֯)(K$82{n>(ܭq|7'DIRmW42@BG~y}UY0>zβ8: Mw\_N$>"ʫ)'|zR/'۾/J#:[S;quY 6[-D<0B)k\6l[fsְ@'\)ׇxJ.Ͳ9"-MAD`bk&jݺnH@Xp/N9 PəbӴ)f$3o R$9|0ӣ~)%7M|*G.EC4B]vH/f;Q8+zH|EQ`uyT1=XpB?(R\6G@XLCm_6 +%|@h_ )M@.Ȗ-a,iBҟm3x̯9i^/ùMTo f)4ͫA,#Sd,PC Q 5~3𱈡y'6FfR-а2f4U%"$iH._ Yv[[QUeyV$`b3S S#PP } }3C}\is,Ƃ*˚ 3KHH"s~鏣o:h?"ZHpzm>o\>ԳT#K Df!eA {>caC43T9K :ƶrgHbMqc'}>bRtu/Y^SLHy])rc#C'*Z:6􊢊4yjPTƲB´raqbc(9bԫA ߦ:.O gȵH\"M/sqfS;"VǁIZ֏Ue4(U" (RNoj mu;l~dKF{_cTUE KEDŴ߮4]Kc g^vy"JyʘtGB 7N/i*h,BH[ccKS3u)[)jk ]8窫E2%E9ąǰz?LqIr*D $k*˫e,DSFɢ(LC{*Xmcܞ!U_YPIµ)VI:cYfs5%8J`"#`.6"ZᴫE #J?[k4al'Fÿ$HrFq؃t!$?)G,냼)0GrTG ’<ר Vl:m6ôQt$65m@  G-f;}pBUٺA:aԏ^uR.iɣa XwHh jgef/=@yՌ^F .$e-$23x)jʄQ Mߌ}mNIӎ"e d/  xjh\Ep:4%\[˨ ㉹R++Qab'xu o(ms8UXWr͘f3V95L-|uօYJQet b[ltI-㔏ʐAFFQ'K|Cx34|_SGQ/ 2Sw5/]@;㼵Gժ YEGfabO Q`\5Iˏbe2/͸2ZAUR\hdEs`m2|O SK;Q<9! b+f@g˜LeS# 58U% 0vekT#+kXj\)~ǁZ4&9mM9R')م*m!+쮧w,ded O"Z;%5lkc2IU/#FIP_Pߵ^Yv QTfT 8rhctF[q4"P *ܒML LUUu,u $m7qg' VxD/38M[;bkzxF٨YVݳ:zXAq'MͱiM*g('+2 .z%d9l"g1ë`ֆjMTn99W[Z"ʷ=$)Z81 楽~4eZ>Z?uzC\P}O= v7Gp=pwϞY*DK tBủ9M of2̦[rA=lwM q6gW4KBjr&Q u~}"*~*>qKQSPIMl7$t}IB54f2Wmh׸>j֣%,jmV']QO Ф{1Bԁ{5xgn\fL2y!etE,e|;%60PT\<@xoqnsT&u<5S t8ֿ|U2i lUɔ'{g[|T$j> j$ bf?%I]5|TT\rL7+cj0$YO{}}TTjT5;ǩ&f"鉑*wdHyw? IH,9WKjm~ki?4ƪMdҖKz@$^ rmTSӵHKuãUV]FXͣkĖzgyJn~a{ejMuc}gיQ,DPHW?N#%ƴ1ZX鱄{cOMU> ʆ (7*ba$`]RHlt͊i_Do<뎖HjO>a \9$ E柃)(R?_||Ҧͫ%i-6l55M0_.q![ϥ\/--!S^clB^viQy ՚RB|W?$@N@ CO̎zqƜ9l+s7}]arl)sJ* JfFVs 3ixl+d.borA\^3kDU!j#Eu>@#nFE\}G!a-V6FwxnQ,g"]Aan mz'`|QHq/2Vn/|m<{m7:jZ<4'SP݃;,Zcb<"7%ekae8Į\Kkk氐>e'ŏfSsSez=Ři-%2H2|6FKknI6)EdutCp YO#U)5$vFslR.I*HnMn]z<*`/6 m|2ksʻx§4ykx_f;I^l}?SfR+ȓ$ý\H"6 =0NpvLI>cÔ:O:F-Gs3]f %=fc]!5-#I5qLeMW].3MQ/Ӫc]jxhقpd|-dG;BfYж'cFI3ևV<ʆ:[u~JgiNmNKRv'S8y$YYS_P e7yUqq†+Y⤀LpA;=&̦IaV$H ,jS͡;W ~߮:>|'s<:\/=(9*őupYW5j)kchb4TZ1,hh㊊ib0GMX( <+%T׶8's- [$.+ɿ~z"j7 .b9ҧ@Q%zABl8+Ī0UC0r0g"!!%db/Z Pc4.SPc"?16E{AtfV'Ǣߚc,mgQ}5:54.ݷHWa_lA&0f*^БS0Ct,=15vD$\F"mͰDȥف%n:aϔ|yb6Ǯ"WB8[sO(n}J$Hڤfook`HMnA[O]n6y5= \܋npJ4]jҍ(L'P űHhЩ*Y9R4ױpH_tF݉Cf*HS.ְt[qv@@^p ~` T1Va#턢gmܓRd:W`TRTq[˷J!Ҥq0 pbE  GFfal=WC\T Cl@)2(H(:qH1Y=?OnǧbP)R `LW)O9B؋&i SM-;w#H$#.q#(64/-eTz;U5iV,I<#N~78N0s_ ea~ʔ5:dUmexUr-OE$;mnU5O8=$U5̣BSmloM_D?U̫9Eo]TB$0ݼarb}q0򤐡,tUnտOz=ˡ{._<@gմ.Yy")忖/f.6qU*U1IcnlMſYjl!_)bC#[{ ~"uCK2"}Zݻ(TdTbP#!E>`pc6(ޣ}Q`1\ pJ srYpP*}VĴEf= ,n6h ,Hu^V|( ~ .S H<6a_@'b>\Q2WgE]EµMلsn(*jH-y=u .OOSӧ\1.i']oBCzqC}sfXzd%~ڝFSko{co6oܴdsISM55&*,~Z䵂[ܐ>\f~%xg,IMRJiHzχTwܭ<X='vw"{o('nuƹeUmVG Hn`7<G Vd\e9}x~.ͳN "ۂzaOyV2V23IoZ)e(Ņv#R>Aaqn:*_{䟈>dҬQ晇Ԫ `S; GǃM 4?g7jS*7T s7 @f'ZeDˠaGAvvIYrVe?CU'to|\>Dsۖ,<*5jjY} nف6 ،Y?&c^9LGWRB H6YNS3_H} ͡#͹WDEիRzmq֬/D$ƌ9toIP+qc9:^+P#pB)qFV:(O>EʛpE`*8}WU裯-4yr{MITe35E\y]/;({L_t#UX$uKF&Um'y7_l `hJF$%@X̪D`_P)p 9>rN wS<ZyYFHZJg2T$o(8A[gneU[*QVf8ݖ r6ơ9yÜLr!M_qQ_I+FErD*~qOЩx,hr⊂(+UaÙ7pE-t9v]CO,H`vN[㻜|ExyiUWO˙I0Y,1/¯>2]Z\ptIgMLQQmBi~ /{q\>'2lc4ҡHm(qr`suFY7ʤ),,M_\!.WȜ22QW.`E$W MreG65_.eHJ$)k ]i-#UQckf7%ܝ+[Ŭ:+ð嵞 ._0)e&\;j 6~ 2FћaKb4f7h$'™uCkT0:J0[=pg[Z؟{aƗW . {Ge@X  o]d 6+w ,@bUTGnEXY9%&EVl7t+6$[Iܓ10֫}JzZյH ĂpobYmu(nUw`Kt0@ 7 ^/=},å큸UmEM{>Yc/8dc+Hbv:`3!Y#GVA;o爝-_H,pwp!FE"JY>7t飜EUjfYQ\Kc$4ۭ?Lrl; dtfO#W McҾn5ߒ}EsFP[xQPxP55,U'7 Djv?1?񜑐ݶ68̩8V]3Gt8_XoF 0V͚8myqa0 3! |y H88m6گ=@nYS`ڬuΥ DֱS{opi6uflJkq2. SbS* 6њf92Z蚚@*z[p~WxjeO:H,Td'j{M>"oMVWG5boO'~ C!ι°S{e}!-Ly2\?.03QsK)ΧRhRE׭%x;a ᦎ DI 3(B[b.Ft|z?%}[H@p_]fP[Pd!n S6+`lw866C CXAv FQlzHW鈣2ia< aFfU"ckGt؈" >` =F0tPNǹR >putLhYX=Aޫp lQ{ m™UI ř?탩ݼe Mb Tl5m3(!7'78}A6y<6t`?%!fD+r{{`Ut`}$JuG`RwsDQ1hTV*K?[m.ȨÔu>i9c,qQzw`o᷋l.<ݯU]"88ѮEb@ >PQVq#VpR#XuY'T*.o`<Jou[[(fDrASr{c dSCMqmZyi0 @6ޤ2tsm v>*2M2O5/MLcFf@c?ش~k{FՑ%&SMgp%=n{MKQRDU`.5N1FKW㪚jZaIBB;G!_.nep_2Gzgj4j cp 3Fu&NJ6sr S>LqIir_B&u J4i w v#>[p- p6x>{S:URvc{lp i!~+wU^a"a<>hqF_;TنQ:̥k8߇'*,Z8a6WHPuu% 5M>^#jz!Y]jC,QX儏 z}"F$p6LtQSQSR´enI&!Xj.[ 9n,ڻ>*g2J:Ͷ?LJZڈo%XNce) 5o/)9C7]ln0x^b֎~74~$j8J)B40{)F8<.gYMM70IM*0o&rqh~q%g?0e"ho e9`EQE?==61O7~dxQV.j 'vCM61;C V%mtqȞ[+X;^L91+3d| fU@<28W0ʹ9v}Paj+(jK3#n&*cqHQ#F 4,{v9Op:j2j)ܺ'JVh#;c+|Gە|̡uOyGY*I0klH{ T*K(̘t9]q{ǧm鼷R JXcsaϞ$X.YZ;t1ʤJJ\_~O\n`n#@*Ir]8p>r)(j2z>zگCMFVUn=}fA|3X@ oO0:%k?@t/i'Ǥ>V-߁9^մyoQUP-(d |}yq?8 PTyF'cFJseʨJv 7ol} Y[1xyN_yl5(G^оc؋~JM 4uTS'YA;AqӘTaLy^RJ^. .: pTK/olcߊLʃ,3sY"X,=Cae"T|]߽F?>n( 73CeSi4feտu]Iq6ik-\l,cQ,J. +%Uk=DeHh̀\mUIGY[j;LޞUMe ٌRHY5)@kul}XL[e˨"+*[P%3 sӷ>תoa /uH<,ҽI>|4Y˪$TIM)XjrAOL}9NgP2ecfjf#Vq=#6ԧax)S^/FB"9"T }qN0i+ښH8]JS>ňaэޟiOO Ԩ@7kFy kf7T&WQE1/vR,OKlyiZV~|qjq[aSWt/a=͉Kvl"XP~"= i >bQ:ŔPRPJ8܆QdO[:!u40
B9embZCwt;>d9?Y#fͳWPIIkkoB@҆e" + . ‡ Zmr-e ,2dXc3ylxI}--0rNf[6%&8Ň7y犟#lu]N&WAU-KH@4zr޹7 {C0/= SQQEVqGEH ;bP ^Gnd9MFơMƒZ6! uµ5) ! X[\[etoH;P$j1 t**,$YC>O-D+ncH0Jz<½\끏ȒW}l$*(,F<$@R.BӺ;)$̖hI{&BU0cM]lAHјIUT\=" 9$Joda$|To{6$VHY68!MOf]qdHAeo0I#zP ¡FU2::mYJ42qJj Gƀ88ɸϗ57qG W7/QUUOMe 3)~S,!ꢎj9&jȏe64M(xUqBK,9nE\)se&qJѴeo~ TD:k`O<}KyÏ<2n`rsJfU#]cK(ο3yESΌ-Y la\^^c0%ت2'}q~QաFD,{lHjsiA,;tts?qSWaW9SȖ;*4DH\bVw\QSr2l%c2噜20C{7q \Z38%*|E[!@Z؍E{u=U]D*9Nz ^=rIa䙮yv7+_ώk(*[6(YSfWU 6Y-x"ȾcGk*UJ !`N!M=a a76SGUd5ӈc&I%>c//wԨ*9ERls*N!5E8G[t|^23 . Ef4Q=Fg2IZĝs\Ls%f.eMv?o7l r:*E k(`7;-zf:o ҹ] ]E\C 0* `]}[# %ͤpNˣ@0J{t2yX)@S^b,§11SI'#JIb|]ű:?cŐ,U>2 X#I.#kZXA &19W&uNT7P7ߦ&TB54"tJz^Gz.m}4NRp G: hoc[`nNEv ÛYL˚D.JIP^3w6lu U(+jΥ&pr܂ZVgF ӷ]ǿB;x ̶sz$竖k7~1<+?+2ԉ+u5DBn 6۶@U?,^""vI4Sm/(i+28Is73w_53 *l[P>QyD0EA%E(v@;OQN`"]1FTXb;>FC'p_8lX2NhIV%k^CF6;إ8-z Zy"pLX>- C/ͪ #n|VnfyX0o|ê|x≲ZF^(z2-Bui_kq{?7cY7:ĬS4"UJI6'Gm"q2$;{U4E8>^íKYvV ڿ~ 8詳yEfL!eNiHB},o-}o-z | ͲJN"v4E<7[)X#xo$a+qoamXQee(ars~* M=dhi:_lEcs~T,0,zYyqN֯'W' fS :H@JYXn]~Hx%&˥bgkn~\isu06W5̍[rWz4CK̍j>ozܪegeY >OUU6UD[C VڙiAmk߷̾ ML#lRy)y{18G+٢r(dR6ձ[zu³>I-=C@"2<_#ΐus(TױL'YgE5 {?_e٢C:X]bIžǙeNQwH%*Vo.(v:᲌ijZͬ]SMH!Օ+nY\Y +& ;\؝j2͹qP%Et r+.z@-`zZyXHjCŊbI|\9sLfv\ ^cPM0mC#*QE3լPmOOYMPr?+W;X\m1|ຮY|3r8&V錚؏|u& Һζ=WUT/i UrA؎؜R}DjVYX@ܛ[a[c ~eNin&8|ZX6n݇/_x1)DrbM|I4ޏp{`, 9wGSk}WUf Ȥ, \_0Γ3 f5bafܒcƾ ~,'.sieT|7 2Ƞ튟|xlf]K'&})5ViUXI"ĩ3!Ѻow|hyU)SE-? ߈ ޝ.i)jx_ϲ.ષ.l]}9G-B|+ñ‚OzvLL:jVY#A+寰c[?$4X &@|x⣗5?D2p=S͘fAy6"H,;((bXrJEt$(T ؘR^5ַh$CenR1F}KwnTde2 uEFQu:hBt,"8@,d*_}*,|rD+3k 5`sஔ`jN";qp$h#M0y")w)@0)vV`,ƒq YD`݁\&"=ttxWKmW Nuzwĕmq|EʬRJ&@X0MQ툳\zY@ /ꔸٚ[.R7Ex2H>Ղh䝭QO>'0{-t (\Z,i$FŽ>B+ :q bZ%KyºMJ}#Ӡא'L!+!Ub@$ y.p ӶLDj9B0.b*źt56ġD a 2*-;K(#o鍊wPmdcC%`lU5w˜E' ԬlȸŅO]T9GnkL"ab>Ɲ>"(Fxj[I tv`0{N3̔OTGa o3PbTc aL_z_4h‚77c n #@YrƎ?uT :]no 0;l i@r s]08e_c^gOcs+rHx32H2L#J I&oŻ6Mӿ.Ny%^LuB/᪽JV;G(O*{ǓC|'K>OQuUJ)F61t -ـ:fL(̾*IEH[* sbe_bXhZ"!а+w)j ӓ3L<c9sM%V\)m[c]^rrZ+꞉sʘ]S:k؞#3৔GFlw>vPTYON#sx ҆}c>9kqAŜA2؀6uOܢ}2gq]cŹl8[hUkkܛj#s@A2&|;3W :Z 3y$VF}t(.PclY$pmxEod=~Db ĺ^‘}U/0N,_|,DVfI"j ιOWN"&1*߱ƯYqyK97%yD9MMe2=(arr̪Ӣ~%'%hޗRA<07%| 8G|y. sf$0Jhfi6 |4 rÇPq_Me=} bpc w9䧃ƟOܝN(yU٥W ",s֭IU@ًYFu)xC_OW2r]fYS-.Y͙,R0EjIW iDMaq|YX+|8=9;˜:ގ4K$^@onLcY~wuusl淌)*A槧ApmО=!9 Sg45Y|)H?mƙ1nM65yGJ_&Icpg^&Gbb~ *{Ѣ|&4.N>47>got\/IJK5cTTcBRFS+m`71#WůL-ɼsH/yA RވjJ25qkbB.M\9L_sJ|qBJZ5h6V[zyA8 7 I8hg\(ܛ@l:G"d{u%PTG2K]UY:`(#qM|0̱X Zj?Ç _bJDI"0MXB4mbH{bY&q_Sw?md7e9^wfYe|F:ã\c?ş|GTS^^u5 <56\kl7}f[@k_[x [j8\C=<5DllFX6kkm~X;nӪcVK >*%RqA676e㣓6o+2("3̧;ˌrE9]H7\GYd,.1-|M3HëB`|&xstZs +g&%IҪ.TRi lJ+Qƞ į;k/>:C.60|`oaGqSjřpXȷjE16V2O?\2<[,+WY1 yUUH:Nwkd .KoNj6*8=Ȟii:E*HѨƥ|| ?x5|\Lߏ0_-^FS!叚͐BH^לlJ47t`F_rnrb$D%{mjI l ؜8 e m;2Dvsu3D2-V'QNJE'Q r'sXXIfM`RL5djR F9;<:: Eb\@d:[mml-a-M5(iFU_j;&H:JbW OG܎VLiI;,%QT}+I=p+ A_U*#% e[n\b#9w`+n(낤ȍ+m\3U?.B{۹ +*1ߠּPv'n.E!hbIlF-C#Kؕ^D'tI+_A$=G4nq.Y*1;GB$뾐E{c2c&f0J~⟟\Y5<}ƴ,̖TU"C"Qc)zFqΥ(bWa*NPҖ*VZH]6 -o/ls0R@"- &_,Pe4,fڬ*s(z1|slzqoUt1R3ҁNă< ~aZΪ]l4ڮ>-ZjHx1m")Π`^/s0dE9Obnp7eyy9u4hکb@&oxq}o \^C6$BDĆ!HmfK @l\WnYxtknI,<ϙ|&3c 7>] l9 ~fy(/&/.@  abm~:Z@W0|/2xyU . N[B&CyZ:yubmO>:X|P$rY?Y}%#=g[Q*/6>n` \~k_Jvpk[  y(j&0M$jB 3/_%p0HBS#쌯:nn"(If-,ƃTWva Hk35(Gdw 5307`kŒRn{O/ 3X(=QϾJeO5x !y_qU:%렽LSp6Xe ;{PE-1(H#bmzVQI:H̢'܌b C&RA#'Aՠ.V u\5`$jQbTݓ w m$ݕNz5 ^Uͺ0ǓBݏQ=iy:U8ʕ*äLkj+"ےi([I6`T cx#Q05R:p@ePle^L Ҡk C2+\?)axw 4Eu邮 `=d~cvtH$u@=FW{o NZ H*ڶf6 5Ƣml5 Ifj$?L<` 7nm#pP)wm,I+tKٙ%4{aviy%/5)m1-?9rj*kMPfU`$dH"vH^^>~ʌQA5CYYP-pwbwpSO\YNR:F<5Bx7_8q ip??5USFQu7ȩo\w Ym@~& b5-I)}&Ze&=YM%xΘG-Vcq#f0bV;6q]K^ KnYK=O4x>Hj?̿Δ4 2v$ocHrX)ˡӤ[`2xx\yuU:^_O0xK8RѱXrYC4ݎ'IbcHWKaS923XGGR4,M@7&;9%3mAU|4؅e\~p~=ͪj>ʛ)T9v2vtB,zG._.4*rzi'ctyQ*ylPysu鞐PcǏ~OrGY_+_N$sΊj^imSLBoT*|)IEf$UMYݯ_1!a!y/iVm-=>#O_ZcOYSYك[Q_ %@twh~hǰfY,>1(0_6jjue"@HM*ܕ'm\s_Ɏ}p~Y&yǹMG4Peq,B)J)=c:$|a`qVfay4`Pdrx!r%U1C1Fsl]ɵq IOüPMDidoF~6+Qzs4s*%Yvg&c vUCK LIgC$I>Lz:4qe*]CK[]Kz m|*1ΟS`P;yzn-a3du4!<ga!#@ .nAa$0HV`  v'bH_" vUlkgMVmEhxZU),R區#o6wca`m1ݗ4G 埋s_[IV"Yj?˥Pdk!u/~ >E῀-wugӊ4Vd$- Ræ+qaPZ|#"{e<_f52]"! `O&QjZYwEh𢤽ؾkcz` rJeQ *o#J.nȲį7z%*fCJ]H'rlEcQ9KもW2 !S\֯3Qe:" ҵm1>eܠ/zROiZyRV~|7&86~OSGWSO J ໆ)fR e/HQ˾u v(0D`BumPPX`)dPIBXJb1dLOW6G+>ddUo5.Y§'3k!ɲLVSgB4=!R>H\+D=wyTocsql,+|=Q?XO*2nt؃mI",O1P rx2Y~%>]=\u?2:wXp6{lpӉ⊚beR؝'m ˫ >l3P>emܜQJܧ6JDL @ש%Wml}ci U#KRVA籞9D*@Q[YÚ*Śy|c{U<'8jsD k1*:vhU*sj*' jv~TEDe `;XkgNWvVm"]ck5Iq Z1FFߥfUf*wc|WtD(O^ZH.e1CrVFsJcyP&6(H#lYU!k8*JI4vb3i 7N+q` T IE 0%NdrX)V=l8bæ:%kmd7(4rDf>w D˱Y5 #~߾=6dځBwYsfE+"(WyXy?&9|O$k.6$nobU35B2EF Mk-V򙁷~LJicŒIs*( @p0HSҬ\LkЭ7)p[냲$r$jXY$hOE<f&(L D6ʭ$Y42֡vTP4m8 ,CM_E][6 ,DbF+C RF4Ƭ{ar8k!a;,{Eȿv;y,9wgUTyH [Jқ&s2Riy]7?]ăĮY|WpfU&Nᨽ9GQT$pv1UTE^~{ >ŐrEN湥is7͸>3Fg2W&{88iz0i]HIP>;ьߋ¦߄R-ݚ0E*yp̒Ycq+ ePau#P5s@7ēm&#+̤Ԋ*Ut}0SCWP?S042z]XNgKh%ϣ2,Yb=XYGlf:>|̼ޟ2|.jG^_)c;Af5ˈu|>g|ՕpJo%Mř|FiAWIji,&ޥe?2!YNw+|:Se9dIKKq6c瑗sZ3)Z3LUZFϽ]*qzeYo6|.0S7{KDvgTF?`FFR7 tn] q!p`^Hđf0g.m#}w"H/,{bml%H]Xu?|-XjnfR@èX[6۲HdM1 Ӱ&j[Mc`BIӸ pEp`%ˮzaaéPu߶YeEkoo_`gӲ+H;'"(lz~F j| S(k0o1eݾ Ie6Qc  -;Xd^V`{MSa)Ā 9n1Z4KmaʥZB؍|-ۨ=Itw T*6؄R>\n𩴛Pz`-\|p7y]p/bv"1; Nmr\xX~UY<26k` lLkpv_r2x%zjUG V۫+HxBRŁ7^z_2AL?N2:n/Γv͠ @Y8v{K@ MReԄa:..w̯^!o|'3 1ϵT=F|, .=*Gˌr) ?꫼Sf^k!ʩQ ]nM|_L8· W#/-O,fZ\1t\m,))R8fpfAa#vPcl/sIZ5QcƜ#WC%>tN`G[<>x6ኊ>Nχ2\!uuyj##3zed.jR{ F 88x(oe_ o -bp,bin-\kc-2Zv5>^hhyp6KB!Z>$vŐk4 x-+ಹj}x`eBKQN=lH&Q$) ;;b0RC~t}Ku )B.TK[l0(Qc2I!@O?JYfhiZ-,5ذ=nR!T#[I; \ ka%Ou"e -,q!Nk pn&{7k~1 4iѲ|$ΖP<͉+2C:xҔDNYt { FJˤe+HS` c  x-żaüjjI*2"9┉WopJ>mKļuq%%s;|)4YfS4K `=@[o|R\]㋘J"lςE4BKi6'/Uʔa""$Ҡ#r<,a%rjQӦmk2ORqkG=5&W8\M}\ggTҶb3H<@k/9{Ỏr,2x䕒aTiP;:"_|˦FT>ڬO8 (2 %5T4j.OM a[=Ic}ka|[\*1#7$VxDGʣKk,pV),}VEZxB${EE t衕"H=s`v͍ i#aW^+x[ihb c~Ɖ 1ڮ`E7p$:6mMdȂʫ٥ltu\q_-qR"\ M WmCPSJGVFFJz : bH {`r;=aw!TwU6c'ʫ {`HK;>-}Q/l9Tj7L, 4bP51[blY=7p7e5yT21/k.oa+#ȝ-1md\%6Q3K>4܌Sf> ԳSOYrxַ7>KUnH_mM_*fvJzX!?,iu^+ARTD\RAJydMj_{<<L3RvA_;|,#W=l.u#V0` {*dmb{ZWH6h_O 8e:H[k|83Jk% \00s)`l^rqutTQ[tREaj"3#!ʅlE}aUlo,P"طoW)wԀm5CMͷ7i (=jo{wg?Pqe{Gp%<3L88a.V"AߡqIfV*E/aX|cp(9r,C7**Ĕի |N< Re|떧7SO:cflo|.tU/O03r)dR3AbbH kQfmҕK l>V".m[Ca(_v'dzn?9,8x*[-2NXc:W6\ $5A>+k? C]eզ/Ab/o퍣do6 , p-4xq0Nqܞ˫੯Na4dR4#G2sn Z3S$$EUa̫#*;c u3Rq4VcHVbM7rwtǤ2iYwXӒ||dyɾȹuƙu2LYUFk$CO&׹ tï3~?sLx~T`bF'F7[vlq.ds_89g_r3xӊx!Cq=ɺs838ȸxz)Oumnb#{p;M~ Rcvrl&η!uk،)NfeSb0@im*z:4m$3! Ao|),S8*KQEuPp`H8k&yġGToԃM1uNr)+t7~" E!dBuԨ^ͱ8K(ҮGok!(+{\`FT-6[syRB|1_UL{6zb/t7{TUc ںl 0.^`V m2ݵnu'SGO*'O68x7OFT#!ivaцPXUî_c`XBr:M-N@vp0 TY ?Q+¡oBuVHذ:T۶( ~4Dp>] Vs.twi۾4MXE4FuHs={.}'8q9^NX *ηV۷~Ч&atSgKD4$&6A?x<=OEƼKLB3YKi JVUU^t|툶vH[~ śGv0/ۄ@fpV+)9 ʬ~`pIxsvX\_],dR:ޕED(B\Ѷ{`P7 t(yH?  ;K/yIZt&Xc,3r9SqWOHVʶEΒB{l}j*(i怑Щ߯^ô|u,Lk+N X3i9cc 'ᘳ *JSKW;W"u#RƬa{Yj3N5 .A${ܜp9œ]EV5:L2Tlw"IdQSV1Fu;|5t_g$N!EԮZpQ#`+\"7m//P~"`t7%y:+Y_Ȫ "*$ŒI5~Tβ:X&{|Sr4bAYBeTU|ݽ-Iﵷn/g˝TyQ܁$Na񱯈uEfd+iA(w*h%,ŵ\$1|2~Ͳ$+ nĖq,K{ ,r6icMM#եA7$/}sx*zϳx4Qg% `G_s <> ZX8EtƠ5qWӉ<9Y: 2bͮVA| .n:)"ߘ))a7i#vp@͍|iWl[* j8hk7mMwzKî4+˹לKA,U̚h۫Zv3@pOefk.Z,\ENh:Mw"geUum5?B38 9|9tukd1Wu) @Ѓj*edHdu۾6jjQH5ߛ*&9#HPn5}'aEfyE~ x"$%CELvt$POQ}c5䆪JS ڊ Dn7 \2"%]>O$!hr,U' =r 1"YNYۨ ß Oϒf_?0|,q&[ 1A?$x_!Xrpa[*}/0k=%Pƾ&y|%siZ|嬤$c`H!e}SȢ6~ԧg|7eiOV`cj"XܰE'8ZdbtYHn? |:)fd3\{%9L **(VA-9p-V3!dGDN}HoȌ\^͸Ǝ,$XB.$'wǿ|×qʐeoÓ)49`WVdcT_}oHq/ 1\/\I[)u*G$S03ՠ2|u*wKIoUm4%,fWRf{HO`Fp5me( +0Z6#T`Ǥ< 7`c((Dw$ Qϭu)}%>[c@knz*fI-}HDQ] :[8*K%I FXIu7LYCtFk+^=\ʪE>EszbWr N>nj"B%΢k1Ol&zt:Xºױ8U$pn a_ mˎM#DЍrB07cnpHG];1- VC jWH]pƻO+#O- AS`s vtNC$naZxԵqqav5ΒO5zIU&` aH VHHFAVTWT:Gۋ'h#4J9c[n%DRFc_l% G _8Xq=WlnXl0V T!\_p$`,9A(nI"倝0XcQJDʂ2:Y `q2R EBg-a"±QNNUHtCR2ޯne!n3 { .b(=6k}Neo:=ι@D6 >c k`ȕ<)A+6TLB|&4@"ubG ٪PEgNg@j8(θg7+Tlg0\0cȥ˳¢x^5(`K]US.,OnQoHFn뀽!N, CR}#損K .`Oqlv+y-"4~Pql5̸١c+$LaRmpG}Ǔsh31q:{s?(dxapf>IDJʙAyP==ISFmYSU[(ť PTě/sn<21ym_N䌛鐏LcwЬa$P{[𨘗jdp4W?>ih@To׾(%f. lr )^RڐJ|7 #khzX5rTB;@T'W_w8MU!6(2,MǼՔ1!Nr'PѼ P{avrWrT $I)xXUk%Q"6&a؝c1 4tW+Kg:Fx'шKO\pҴoRea0*Z32 `D'KPm=(31f_cˍ=̅X/m`=qŠ<j;cx)o1k %IR6^JqE-uk1!WL`Ȧk w1喇[kࠁ}66#+yB#5,@*؟ nH][RLn-ԛ`7Sf#r nݷloCPhZ@P4MWzX3}q+[`\`n948ܦXl=I Q4!A\(n8 q Ж-}pu;@}@U{*%\GƢ]-尰$ol8%yI1,;.*xI2b`6t=OG}p͑eg{';n|*k|69BT:q*2!Vb|_i%L`Oے}18,E lx2]\km%lmCKF^W.Ic u 2*|Ufwi=[{bF?6?P!*,$[VB ];;j1ҫml-B.o`rJMA(4݁;TkZ/2(B_QTj;ۭFF ťJ?n]y)=֤͐SH-c%ˤw~  .LzQI*=g~2H/u4WMgCp7bҷm[E6A/l9\?/UW:g ?E)gut\qP w&P$dIZō/B1 Hc u8*ܓ1ƶks" 'hzw#/^krW&oUVEYJG.!qeQWn8|HPi)COASK! ͬooqe<5Lyq/.leG#= :~YOm~uX[+oe/9z~x$߃j|iQ$rF- N~68{Ⳗ2dgKEnQ&Z,m%4eNycs. 3aHa۪!c<6x{*˨c"JXudh)jV@ {[,dm|rTÁF%|Y-?VG<D ej*+_Kլ-|scE!VQ,$%\%/sƯ:+ž͌29 RO$ BTT"(WI_Q۰G>fnZ pe#RdEIVPY<ǹ<پ_>si~1Zs%y_O9jDߊIYN mm3~3t<%?x<2x[/*XE̒j[qf3UX'7]Y\5y>_O;1ԧp5 3'~AxOxˏ)y%֓;jZvA]I CF8m`Bء4oě7)|[p$|IM}gTtYQCOSTQX$E;؇="qo&ہ2Y :TW \x?Oh?eSef:M2\Q|uϲZ.\-UKRUu#nׅP[|hÙ=[U7pZBW5DMWQ D}[ۄJ#25[K\~|PR?ʟ&ZW;X*rR1*\j1[^\r_ 7eg*|r%Y2TH2ҍ8zvVHU!q>iq;y˾hq ME\; veFCR4}[؋}EG$2bCFúT{zaQiRz۟l)B)'p:S}W]^[뛅tfKc#Tٟ *mǸ$e@EWac r##DV"}p7M%-mL) 4); 23k]%b%6I};0(zl:j!S|"]a\ 7K7DTxW,u^8A%,n @Kp [N#11w''lR@ 6Smm WVXF QQՀѤ[O 0Fxq}ʎd_*5e A@-rv GBq:dȾ"Wwb_|:}LwJD $hir4ܐ-)+FEA$_WhخsGHp >JI! X& teܓ3bi.k/cAIi(QL ̋\5T£Aäy P|DJQ7[Lk3xu53t5.F)NOXԸP!ӿbW?θ\xqy7t||r3"=rh3q!zUٍ `KSY߂5R<Ù  ^5]\F ?[ci DY'zÔ˙پvNRf6#K2n8tP $ZKFFǨc.@:& r٩cѰ[12R̦ۦg#C0j}?#n_h&kX9 ؏|8FA3%n@:}Wm XݜoQn{{,vډ(Vk\T"b{}mt財 q` $F^w8qU.6ċmҥ$iRT: :ޅ(zKFK38{j_ob4FAhնmR&}-j4HSk$^j`1.wO 9V] G_0%M:h*y?4rY"27sDn1u*Gǔ*v>[%φoSVDRya( {N8¦]YʎTuP-  rqϦYM|/$|%)iA그;lt 8zL,rx ǥXk8KxUcN=[PFWPN? U) ?w=ؖRAC8݅GO"zH,A8t I/!]M2uN<:Q[5 'G q&NÅ?a V.0\'*J3}e ĒmLXo{4Oy[z4e:i1@ i3.]^dNK iQ)Q4_=fkc|'l xw/@TUV(/ؐ}olf_&*wʣˤCR Fo+⌳.L;S4sl׸#ۥT8 oQa/xdyw𺳣q9[o"ky_] ]:ݖ_E1pLJѵÂH%c ;lxs+j#2dGZL/J7VkfnIQ#uBvާaPKNCZ\.ỷ8cs j3^$@fV+ ]nW|?:'q+aqZW7F`.U$/Nlg@,/k)ق#Yؾ5yFC*:v:ứsPRҔ˕ 2hE?|a!4W3\+?x@U.464&PT#8x /qetKRfRi]7[w3I}ڎYg-.v _UEF^_T4T~"$>Sa WkĀAJdQ{_!Z7 v ѰE>,k7 ѯAr롸$@#mjCp5eǧ!9)~o!1]+{'ֲG*d(IO{cISẤQN{v XVFEp8JO8w6RWH&J;J=bWvS5,mB Ћa9H f{)$|Ku1* ͍@WqyMյ8G$FHce78 UX Q gͷZ-icn{. m`/C",bcr@y[Cp0oY/uhy)yHƱ)GqNiX(Q(Z* S1mQk(aџ ACy)Q+Ju ؏ckc/\*LibT.`7c+Z[^Di( w g^WB5%F;2qk;l`īo\Kinnݱ ZhWCtb.@3n%2%M;?"E5Bqn>  /⟇#-8rIpH]:Emo%y/|e yE''9OYZXE_ C%,Ig@/ ݚRCPK$ Jk~nS.jl `߶=O/l#>+>S|Jq-$yY?1Pfm[ wK&2Wv L"^RnK wK! ;H`}BGlk}c]ieOD8,xצ+x2y("̿ lk6.' y,/UQU"[0mh@.:HcRMk~1gU t'Sv6u953" -/s#38̑SMʈ|qkcxUS/ɖ]T#M_Ml`!1܁ߦRety,Hٲ3R>\-|2JNNsO!j'|cwr(ǯlnQ.gfeᲜ'cyLg)TG۔h(ŘH:Н(TRJ^QIӈZX_}B7Q7n#2GO]ԴhH:E~^\3uAo$й$6ƫ|jdp'Eռ1~=<#{sc#KUuƯl3Q.@$ZG']"=W@MǾDvU$rdIiTIJ5ЋdeLSRdtUFQ#C"?Mݏ1i'+jt\/ !ĞSW?g]y'7Tj7 pܶo|80SvY>U2;KSZp=6 M?(4Zr<ί5hگ\Ы&Eܝۜh*6ʳ!7G [}1%)5MXL5BBP1&WmRAh785t=Mu¡2Hu!7ԋwlf(>2yO;Q4ْ?$O?BA X{cȝjU2H!ʱnEn۾29r9 8VGP'k|sΔ`C[lB/nl*ڤGNq! k^o|c*rN +/TM%4t(ug`}񠊸[ȳ ԆFzɋa{;#¥lն x򬣆xm/, Gs^E.:l8TK4GE? :&nIj8/0+$nO󸸾Z*F@l G{\l q'Ƈ^oYAgtԡԚ ($,Hu"7fcYQ:/U檺80,CE]5[[TE|I3)3T#A^rƯ*eU-_TFڔh{N(d 4&AILgu9*kyO,EHUlNtsK?px ;FJ:i^5 դ]A *)[.,# _pz_G~o2G@Pߔ 7Q{`/a/kX} W(I_Vf4Pg9t9[ . :Z=Y~cSĆ"23Tezvyi$,=Ϗx *l0JDxiGwM,<8gT$&~+5Y$->D(bXIV$7kk.ʪdƥob/n{ ls7%O+AFfGWe@_ Ʋƿ7:") B]w>:~^ެoNA[іMCKUBbN Qr^tp9fRWET)*ܯ]K`Kw徏0sZҪ|ւH'6P u{loSO 9fXMv{COUDA9(rI\sNQ6Ux;AĒ43aHƥkRI$VVdSN"%:^\m5ԨHh׿TSSYE4c͈H6s#Qî0Zj[ϥig3h|-Tr=AFr}?,4 F0 rmk{, /*棪XO%\X^׽zb|Q3RЫENߜ{okFx guE*8[ uPQK^ʚu$>aSNQ&Uu23;&ࡎEr.<|>r|Rr(r|<.(h ;+@c}CI*=-%XR?ʤllMM9'iQi2ʣW<XKF~`HLw9b@]@w_\k͔{,sܚN뙣TȕUUbB,A~Ƙ7972aWOaԌzB]ݻpu+T.G|sOpOpɞg zIA);u*݉zb"q%ԓYrǘpi#Ҥr@IBZF$-Vv ~:ԑEJ <ጏDeE b- m 녊yebȬIac=N 3 Lsu&zGrfrJ6 ~8+Ц^ʐ`V6 +#%w-tIg&.[ AEfWd]~i{X4ѳ'Llos7ńZIPMƀڠA'lIVA2iJXAqm[p eܰ;;&#`GI-f0 ^ʽ@•cUi]f[[˱)nn}[B (FI%'$H'Xmo; V]T#.S8E4U0DF~ Vu!$#T.ZIa߶`cAğ,ZKUy.i2lmWC G/QIHR6H8i,h)}d2>%Nh!<#)6P874*e1!GɤoV&gW50S*?L؏!I#!vt V Ud*SmJC=ip Y">Kw kg1eo1a82PCo#!k0ؕ(\$K-5\CR0T$ @uelnzZW,Him[aǨ,$ZHP`2F zoa]K2JR2T%v&ܤPe12'tRXu§ 1F a) F܃aە~\N&jҮ@G h}z"(,s'&TC^MD d'_6q*42wr+)^}YUDoguMI[4׽sIQ*1E9CIʪZ^(`lYE؍7ǝKq:g^,8Je5 xJTyg:J [V:AQtR:)镬:c?fsjzj]ֆ6BHOmq>^][z{ٖ=/< ,|¦hjsCr!2.½.EЬlToM$mIrF|lf̤K o x]ipH ǤUW b@~eez_kXZ!;DHݮ= t1-m\2I:HUN 'W07PA%R)\TRH E=䇑w_Ss'QPl$A~P:m;"1DTPBhQc{ʖ<+ S]_+^ARzc X,JKfC#Wp oNkFSajl]ܖS~w$,B0, Y5MvDfG"8kLX A89bƚz03*$h #1nO]p]7-|!̲K{WFI *l^-B-kTK1{=yoYq=F_W43=ɹ:7ܓ2^{#p5\+̥H}+4P&(I تsqh[6 qg&{|xo*L.zZ4.*tK< !xY}_Ŝ̩jZfp$K c2G?^LQ557Z&BtcAaI U,7_UTHKV˴lğSQx^ 7q_T[hXA6þ5iw=색qdOMݻMI12)Ϩ*Cg>O-_r7\Gqw rW,Kԉ8'xSu:dbؐH"keq8 ɳr)jaMRQ ǥ1yFXUXtpNX7#vuAƙE.g=(j: Xn/c|T{C~Sde,d+/|f.5e5PW *skWY}vh!Y&X#aߦ2h;X֟k ƞA$ D\ePXQ-ն]p4h 0[a4(P"7Vk]BV5<)\bn *d*i%-Ž$DkK6+Y yk&S5!e'HhfbyipkIX^5y/Ŝy&q$}ðas2FauVG]Z(wE;.qV|H2Z"Z:tt6Iaߦ4q;|e1Ab,1Uʊ.'4c,wUaɐAY^1!"˫CR %_e[tu/ S< s3Hu,TpsK/̲zzv,<Am‹5X` J܋Б.kcUT([cGܲx`O7f\O8cjI[rwhɰ-Ţ1yVEⷋG|QÙ?$~#qŇIYACnW]GI9%abnmvlxl)?r>3x5P[O'H:X~56䞪J:Y^E,$օtiH5ʹaULv3\1Gx0J>e'р<)r-z;f wLq2)%26y4:u1հm`}-51Ywp% L ޮ:Ԅ:& ~˅Asv1@"cخH EJ=0A{ Jnm~et$4[B$)Bڔ5+Ա}1ť6En} c^fu +u0:G\c#sE%)lH|tG%@`aH0"%d<#,)qX+\/`Bֱ+JlkIVpFl\ u8CĎ՘/ŽA _i{I4`(M۷#{QnEFFZn?c l.K#=oBoPB}Dc4u9m,,ӔaGP꺲 2^߶ء̴͟ LTƛ.9||_̓X3c,./{cI$Ngh[$$+k~{0%<ډ v(K 3׿)xl19l&Zu,[{~%8uO.|tNU(ѬNHl(HToU o ):nnzjcVТ]iǠ>kk^k;b1K* >aD#cðB6۾%bw{ơ6=B[Rk!8Q‹ߠ`] \00٣fg*lֺ8+y#\ap K?pg,sRA7U Kzz{ƞr l*:( T;vuߎJNW#RGDO"b;;|s˚y(Q+ZA.\fu,  Zߥ PS:r俉hnku룀fIxW)d`<&G"H%q^.H%zzйB~ʨMzjceVqJÀ+fOf#isZGsbսEO7,SHDHRAly1v`1,BD6 ݾ)5FP18Z4"APtmONؘPIR|9Xa#K17$]}F"%#*>nc m !rLl=>efل $9ف‹, *\LG {;Xݩ*O=c*HᡍQ)-(va)"܏u' D"S̳( Woty$]M$}Ue}V:Qypq%wceȡB507qqxեˣ# 6mFzen u̓;ljۀ2,ljjrj ̯$Ծޝd> /8r*Hrm`;u;}o,ª>;8P5)Xm u3,8fT1̚ƿ!mLxסEC;q 8X,ŧ^o NJXo[qo0ϊGTp}EPQ*NvDHˀP:L2Ui΅Jre)b#@+>X*NfDtԙqمBsckhrq%y~d[KSV뉎ϹNjt?m,ɗ}KM1R cRZ;b|%HbxJ# sS"dmB"0U6G$áyw j&CYʪ8>#9lZQX;o¿ eQ4(⍵,9*I#݈_n]Kkv•Yőƅ\>Ԯ?2e M%mW'xf ED&e{w+?PtiW),`I*n@XИctƧcn_KIAg8"v2:ޛVZ÷'j\pl5D.[GBupw %UEK3h>aP 7mA@_{bRNn_ncҕO4$,LdY;cl5d:X܏ 1BV ev'2VWEyX~g;X mþ'Q{5-N ύ&C?~&견3|/H$4hG_|j˒fB- Tt۠7 Z1T5⻘5L5z#QUz SۮI+ᦫJ8^0EݱW",R@UeE%F~l۶{/-+i|Κ.i9-qpő^Um tej5$[!cck 3T\/⟓D4#N^a󍍽k]Q~]XOyY}yfA+Wc c[@l6JB#wP]GcvEY$w3!bHUw?OQD d:n~?*C"2TO\4nE!JP,h*9nvØV b=݌hNT9,[~Ą] FBI'=mq"DR8Lm$%f1F {t,hJHF~0t拨 }V6EVY |-eKOǥ F|0arwgIPrBӀU6[% ,K8Q#y_=l?A6DX [E ԫ/tul %[;HmvMKm/( Fޣ'rDg,,=Z[b.@L2e/h̗vZu =JD^ n؂AbnW(X˰`P`e!@RmV߮x *Fk'eJZn/Ӯ2"B]Pe][k,9#co0 Cn۬6*lYa m8 کAa]N"N0A"2+Lzt@dS2܍Ee;2|ΓƕT1?,G\o8!<ܤ_>` 9Hc 6102T\3I3,X}R6`MAW bJl:m9e'sM<|Jal)c"/'g̛!5\kᎯߋ84W @;}wL@5`nb$ʡ )@C_E#$z#xΦC8P |\}pᄣbBvO^(,E]7uYYPjĔm. &i)n#A}WfOϧB`j44K=Ҡa䍒/)ɩVȀ0a0er* WJ݁N6D !Kv醀,(J*>mt ah,k\?1,厦lB2^/.vRz|} $H]ʎSME{ =vd~`G= c&5ۧIVc P=FNBd[&1t*.pkmbt{!Va$5Ll4s’FҚtYlN'D NoD Qʙ|\RQ%Y; G` wnVlW-FČz2·R /FMՂN*RCP?lH)9Q(l٘l>m;_@S[|wFxe]Օb&?6<}F%K@fREL, !Bb:60PbRa{[7|d v!I{ -zs ;!d[H { 0[S\ݰ.ukP͆J0m?@ 62k|J[Hk05؂b+|mtגTu\zMʱRRk :UJ3ol-mZR(@ b٬:;<|+iR(ٵka{&JM|dG ŕI=mGV&Wbѥ[<(6ӥ ~ضuN"a'#KJx30񭍇JuU;3LpkKTȼʟ:D`i]]$D̰^Lzsy9ae.i:g9uFn3_s DIqo~x2ni <-gc>ox{r>+r|&WPULVSb1&]k6Vj*e+ur  J#Y "Dsix"̎UYRfLJID@YBnn0›8'=l?3abihxbc  r>nO'|fq^0\Ǎ8>,'h$+!WΠ]q^Zry]fys0e0tWAnB1'-g9uLݕ5nB~ӯjiVGBtמ9eQastL p2AbKi+ؑ#^xFm\ufctlJL3SPcb1^|epLjO 9?k8xqg T"vb \̪.Uw9]CEP.ٙwl@s\N|=ྩy]I/H&<ʿE`*7・&boȶknKaN2g[j5ʭGAb l 6סW]u|nZ 6uXJgTS;^v=UߖA*%X xqJnv]rIΩjoc]yi6#p%m!`/췭 k0cg=UC>y.\V%^zpGHg[}V..h'>9ܸ.rʲ!6$SoT@akϙ>3Kdy;YQQTO3Br ͅ&/bå4w%ymAR$|<]:tӠ?T\ k\Ї@1#Q<hH/o?<ӝAnys8 gS8 ))XХuI(uJ661.6x姉]WUUeOTFu[9˞(~!ܓ*sY<-EVbtK{j=[,*e?6j$4ʴrlK.znl jk,?ej JRצ/ ,D nHc b >J; 9Kqg@UI3E$&#:M0=#>^ߙkMiN5 ieuMw(yu᫏;^Y\ԓCb v_[)5xfyKWQ尙1}'O'|HfYÃ*訩 ],b0;oh|fn{pfԏgWYGQO  %06h_9~1o9S_fMGY\j!`'692iÓ574W.{GOKyQO*V`U0^$3~ZPeHZ΅dS5ac\sx&E  JzsݹCG"j0)_s>lmK[_rOKl=|_ ӂk8(\*̲"HQbݘ êb r/1ɼ'qLCU4q$rF7$t yTa3 2 wk/y]0/IKĎ1$FĀw#| R8mcKt5 ̼nK PFlwψ0zR}? J*U]E,ԟ p ;f ǟ_tf~ ;DR-qʳ*6",XH;[2&Xu'#WHkik(^9-M;~@+\nb.҂f*&0k{8'c]/3zN̺N f N]1dRT~pp#\IaXFo,,VzÒl:`vp>p}7vv js-z>v98pE1e|,I6 $]+_¸JQߧ9ydv9(csabM[ݎ-14FirR qY9 KVͬt\s+Yr4Ղ\?)H ټy$}_gA"+YE:<֓aP3FY & \t;3ys'= S@REmHnǮP1E>Y$tQBYX$L6S+_po$[F pfV7fnB rz6:c^GpX0a9.W[{ EE~U{a^iD\vl(vB.[b#xُ 7~loHXKql n7u1(ϔitդu^bf W?\ k cZ? 5^WWSS.dj ;v`N+JBUo-6NYTUT@% %[muƬx T=J znDzuS|A)*b,Az8 ԬޭVfƩ84h*I_6dXYՓPNrz|zEh8-b!,m{dZ,I,l-CزMxbptKoalxj3'RWn4` e3\m)d#Q zX V(-~a+d!JmMC(*o{oT/QnDQjkF]d }lMC,71Z$8$ݰ@T1Al bP (Q59.m:]w'Hd}r4C5 kX[k @ܶ/nuz$]Jm`Q.r}ѭJX}paiX,}eI+>՟7&9UJQrr"y$1̲[q.@϶0@#s 8"DXma*+aY[) XZ4jͨ,Jba*@뀴&TM_TeIP7ث-l!(MQUpl#^NFą~KL?)X"v^|@JAwnȀ/-LH ȺXU%E *V!)k|Ԁv7$e;`PE$BmZn3$ ؽN'<.1!e.83JY֣[)$kZ { Ux]g&#*GtN8EоgKU^L NQ$\u㝃NiR)+f 4 M.EōC{E tЩj,*,U1 SI%bxoVkp&r+Q8iϔjG ",tw61lxѤp<㒔^=16=K(=&+sd$B̬,]XVo .'vpiYRc}8vH2 @D5ՁPA7/ͨ!CQ($\]"lS6'/j9a[.H(?B:{zIi\ x^~bOF)a0LcDK_WUTeVIL"Uk@&*)j*"u~ ˨a =u k [cpp=H(i+vU%^c̼㦊SYurn6}P*V *8*$rL(tfn-}#X@MҶw/@ /` XXY \ 60VbLPcȅ$F\+X튊!@6BrEMIdw Nsn \jr_yZK`|lvn}1ZmLԞ\FMaV!nU,kЏGI4J2>!DBJ@'N#F1{a8br"] /;DKm{w=n~[u[5k{"w;^Ďݭ3liT Fuè`㍂Ȱ2l te\4@Dƻ{wN~bfl!w7v (tMA=]Hn p1!WmNr5W[H:OLGDSlGK*NH`B?]godV kfS}"i$MdA=Ǥyd wH~VrOo=MVua"ɧh齇BpV JY$Ĩ"#]T1+LbKSw~+% |g04 co~|r+ȹ NԵ5OP $R,-cwUNC%TIH JG' #ۀ0E@ ^îSV`R%7JĝJ8ۄ푝kmDl m 6c].bYmᇛvU8@5' $rbA^ gTM*䝶pksY8 zFhY oөQ:I ol8N7W[,H!ml-!:>en=Աu4r92 E`ڹLs)QBT'+^R >#$DQ"ָ{`d;N;6fWpF2. mk 5X[JH,HS-N,,Ӥof2]I=! iD`ڐ@\ ::CY@b;jf@5 ~{m8;1 lC9 ^aQPF䋓pHcqa-LGʤ _lj:fP67UKH#I.V$d7B*< @T&py$TU;A8e昊"f}impK=0^6JӤ䐲ذe@,ƺU]u'c7d -)R諹vsƒK5>_F+qw 7ìܡܤĐé#ŶE.ʧ2r()r!s.DRG |URV"yxJ$-h{z.R`~`F2%ȗ|~Rx~i呶b5YJķHc\-r-\mgO{uܔ99[Bi^ x^HĬO.o|jS71 %3T4<L A?K( {b*98:y W)G`7m=eg`汃eIJF! N`Jwk"i:j׆x")3J\+I"D f 'MLs?o^az*ϳIa-vm A!O6#ɯ;˹CO.DU5&ZtF#`X\ ?> o&9O™7 f|59i=]DMM>KN$Xm+gay"ߚk\^mUvs0sL0J$:JA鰷l_y᧚<̫r*.'ϲjއ;*yb54Q&'7 pM;㱎Lu&9)<<.ZMĊl \؝Cvxd2*s9gs ecFP-u^*0 5Z=NwZjqo?~(Y>mY^(3hkY)jҖ0V(:ocKG|QS Yx.̲ơ(*%@E}/o98Qͬ׉(g(rꥇC#1m ~E|7j~&8*LjiiT6&ʬ5zA'6@m-Kݷ$,Ĝ>Es77嶈 9w4G}!Qlsj`+;3(x%, 1J_=~gsk9u \ U%%MGs!3pԜ3^MYɟk&qaa64pQ V9cɟg fEÕҦmB"l/mHIak|i˞'o9k x)*Ne9mڞL.MvOӇ_qd9i_(] #’:<7aq`l7+?D|.IgFaLTQ ؔ 8a¿/ܤ͹w\R Ǚrs  8P0um@ x#~89]I`PGplkDr'|gΏ03xf.1t4DjI+b\=wE=AE| \Z8k-,C. d`7~!~9&Mn^ #S'9u@OMbD% ^lrLχ(ҧ/i*呁,x۝C?yr3/$揉35vrܩFH:Xe(/5[;O,^ j1̸igY`S2{>yxnsf$yę%a3HS. βӢirzm|>C9=pcd|WTTMJ&hԄ*F=D2%ӱmsS|ϸo~(&H⍡`exUPg+}7ݼ4n_eˌk1eMfU$/,wa} 5,6}$c؟pU}βU>CS4T=Tf,Wp>_4^h9IT˘?`LKtc~% /1Mp'* 9fsl1<|7A׭6OwG/)* r*婖;=KI_A;)䨨6S#,69͇YO $9}jɥ[J,q hWWp_@I!iK(Y2p{c~Lxw+ʲL)C `Pa9Du(>&^)|#/nU9$e z,gbۀOŹ,3n 弴5fYE&F'2F_̓Vmٴ;K\TN^9&V(H ?.UbQ!cK ;b#\ƺ:cG%587:$y2 aPVܡ:-778mg )P鍭F&GQmؗgGYFgERg6mJfHn=vTܧIZGW>θl8'Ȧc4xTu(\ؓ2ljAs3dkSҊ<ǒ( y#20A sW",eDtME_BHۦaۖ/|5p eڇ*ʩqF;w'd0S%C])٫HrWXsJ#餍]by~˯  o 3>-4n5+_;'TeЀllmT3 t:cα'UHvp3R54, yM7ɹQA˘t5*kdiX[kbInbv%[~vBC$hOK#I.SY+ lb n*K< + Hxeo?DFԴ:?5`E6aml8Mq#.9A۰3lhH7Po.1o,TL EKOWYXIqAo<م,C{U;b/2xs.7Z9!,̕A1&*J2'@\gT*G`H c+每=㌿38D#TɠRv_5Ɵ NQg9,91ed@4_I703U=+EeAWpJH/oIc;l Tmo}2WOU:n]ݺM;ۮ,sKYeF[GͺJ̣f)Gnt:_xYzʸa>v^~"YuRItaw- 5BBJ* &o9"*K%E4D(~@ߠ-Q[[̪ zHe> ͸H*?l=j#`?.ex &f9 ?n5jZ+F<}oJgQ ±Fѭ({z1K9Yosw(c6[,Ŗ$@:lGo6̥Ja"6eN\-u߁ UGQ I"Tݥ]_[[)IQra4{_OhdG.'ϣ+JW+rI sk7{Fn1ɺˌcYڦhHٓ)O;!../ u0]L#f|%B>k[{m q4VEb˩#1{`_1! -v$uÎӈ): ->aC h8ƋX(C&w[ߥ|߶Rc|a`p&0Y 7R;vX4t:K@pE cȀ8(BFlB)OYPM HQFzS}^M@PAL)H@RX$waֹNN"VRE:V|SY2UegRm{=*cK(;#ang".y)ua"׷\6sⶇ8Ͳ$mbZV|cj :nI ,TQrEk~]H8)-, #DҒ V7v~g2J z-˰~k+'@%^kjxVϒ&@+p7Ff&m(ٙ+j,NOL]o|vkse-Ҩtso|6C Duo'YbHkUצ&4]<]([KZˬn׾*+ܰy`,.JART M,T*\q+̖@ zN QJ#)m9n[quSA.O#(ϩŭn"7ɸ+AfJ=Q]M^E;v cڡW..-/)Σ4$5$M:0=݁_5ph?eO=E/ҭRwiYTS£[SZȎ rmӨX{a"iF> t>$L*$Mݰ۬IB$ f(;02::/}nݏ t 6=#Quv-ahQՎOS#RGȽN<]C a,\Aا+638 KM,cSIȭ Z%VvksH&\E6xG~̳I4*H,6w&@eoSג)XQR[jXɸuPme3>;M' Z=OƾZ|C8_7z^1lvq*0K{;cae\WRY5lu5 ؏c9?.81L,KȢ l~ۡLΡ3˒}&ݖ1b}@g3^nhv @moY {2L?), C׮8m -#zڞ9fTnV߾(<8ٝFI"!,dQ*:GŞ'1x'QѴvwJjTuqsu)lqG zAuJFe^X Γ'STӗs #(PL 7"oƍ3<-=>S]Ĕ*@e5_̨b@RVP $"2WpJ[q}3(St2_Qa|66^ ^•E 77t%K,`U\E%ò!\d lsHpN%blP؇,\}pB+|+ M ŲF"[}7*\7MJy K)+uckpM1GE1F;#,@>`#V2Vċݻᯝ1cQKibn1;_l.Igdc,, d%-a2fQ6G IT)r/s#ye}!:0+mZRL2 ;kN VHEQ ^oF (; ${%f7.C^rȑ`ImaY}] hu-,}&0 JE3tb0v_&;Hl1i#* ӬUMwyTϾ1>dy٤*-\>.H><9g^晡cu\2IM96ݓ߷%5EB&Y;WGMHh v9vc/ gG5S+™ tG_9ف{t[b)aHU$Yck'}6@V{2)};TVT%: 0;qcl .J娊q|=+mL 0ӈcz7*TC @Gvu_zJֺB"EMSkЈn؝,uT¬}qq~II SUd K2$k"SJliKc4_> # &m'+=SEKQA],j*:uXq~ǷLmjf έ. |4aY{GhooJ9RHk |YiԘP@}1$rzD2r*&V]M*1*Yz1K1AdLŖ sLJš{/>>ω8Ĕm=EJS~L)9-}_#.TV1 7 ?]/޺,kb㪂}B17<5V ud^PH (D4 F]Ჯ9,̡Ij(HU+f|o>YJVUB7 RõձUUONH@UX@=ll8P5=%LdfUnWA@o])h<_udPrw3$k9h2{(ԟm}.[Eli4/ J)uc}L.Th*K+Ӹkؓ6E1μYF{; _PUd;TUQDek0bGSlpGYWODɥоedNoq78|.L.ҚMgPUw6UeM3DJBhiTJ̺c0# +F’ 3I`u:nHo–Zi"`Fw>jh}K|\'RRA"wiH "m 3F$-)$_sF2\-|&!!FwM2 ,{a1,#:`j1|)$Yr,}lon1XM\k Rnn_r-A4}7%Wo "eVQ鷷I Pߖu`::W` y*L`\cpPI&51,^݁0XJF!OU 󼮲!:c bp/j_|Ad怊QQ5ªTX}$ybVVRESܑS̈<S{lB1fP걦S*;,rː >\"#1$_crlKc w)4hՕc,B]J|1HnC[&{5UQ{DҮ1u' )F $DPr .@HgI#Ӷ" d4s-C (x1sHX|j4YVΫ5Z&#ն2l$UL68cM96`S My&auUrßn?egң#!] A RbNwc`^ r+[E g5MKczecSPh~hJYN^T{(qhk,$C~{ :#zQI2x8/:NSU`ےz:ᴵݘ*D2Ȁu=&5u=g4V[Xaۏ鄥s:)TU{ ~i*ɺ}L::F 4՗W\Z,VPvgi#?L5gFjUCX`M\U533G%x7:4'n&E0jm~+,))D4>l8z)  f:XG:^vpXl,pQ^ZDfVt3C"^TRzlji J| 6xY+.I#Ʀuc?<#/Oo:[f8KK.-X -Aaf-;`y o-rM`5 (B0ۜ0۲eӸڈf$-s+~DvuN#Zf4Gvp/c6#u5LgFaHF_^F %U ̬?p%b1 *g}DI\X"!Y;ȷquǢX:XXj|ձPQ *ݥfe [A <] Ĉ@,暐YWψx ! s!y"Sm'}C`\-U S"o}1W.6ϞԘPT on̼+RcP:o|1չ[x8fSFHS0d$j^zt'*7 w#GpvmoM2ͫRn{nՅTw- ^T:drT"^nH%Kwn4&k52כX_O$_P܍RhpTa |0XB.;,"!$bq!g JO8Jdi#`lYa+$jdbU@-z Ռ6`vnFg+ iD*E{{hYe{ّշm6O"JUm_aRBKݓ H6Vc{pR&:BH|39]l5\M; AXۄ:D[ \q*%Vɸ]tHL!gyd -4 #I]fRTvB(03YęM vcKIS䬚P Q]VkM1 OPCC(06>ؓO+okMCQu8X)PGq~.caemk[Pch*ըmAMVHD4ZI fMF@4@X_i$m$m*NvP%UR\-.n-ma2$@YWH߭mX- )? yE6D 5qg #,.[ th;HNlhm;:nTkUm&/g,}#"{(!7%nÞ򉵓Di-@ 6|\g0ѪPTv'`N,%տ9H%XZHGQp.=}F<v%_%|\2[e7Sp^{sx%Vb7 b<Ӊ(2sR^Q#4UM0U7׸lewu.u@-@s?bV}jyY,b׿Л,b-qfw p'i*(rA _Z-:jQOV;D;㬤ʧʔj2sn8~^xLqUpK8I N^o%Gˌ(8~1XӅ%]~םKuT#¦kZ)q6gaz8J5fÇR#\cz8iDd̰V3iQ|{vO%H}9z#I[X{Nu!ng 0.|GM}a _Y$zĚƈO'*;^cٍs$sRL,c@ `M hpGfcU:M$j{}1ʭ7Cلѽ'+*NPָf}mqXP|pySu\1EU _$e`~[,c#6~UAjkBY6zÔ%^./bp[k|8.>plm"|e-ܲw%hGy%pWY07F=}nnÎ>;/2+h1n @Y*rjYu yw*Te{sVTnq˅>(~1[&3 ܢ*8}QY,@ nw8ofjl$Ύ/D ,ͨ[[-DžNGo*u' u_B F%d>lh:HWIS03xڦgxr, [e${f|⻜RWvSReU:MΞd>6 Tb!qI<U"'K`,w_%Is=>ǝV+˰衍LՔhґe2b+_O2UB#,1C|KC]OSO jB0 *N{b?!WTShDi宑oUFxvp a?{C/zg* <f RLi~0̲xb45Qܩkx9I($"VJʴU{-sgc1ؤ?֠[jk\v-"}U:?v7NF )y7Ee~ T^sh9UA> ~ݱ˽7k R[,RE_ItUWSLٌY='ɛ$W>U³}hn:~ ͙ؤ7)u^oa-va8Vxti35lDɜ*$Mc9oxEEW1x3', w ;[cyf&) OV OM!]Kat4gQ[Ͼ!~67=T(OWkq[bf/ {,5!S%ccr1<08;!W55R-ֱ'AZ*s>9ibOʲ;!ma^m~Dㄋˌ:CKP*O~?꯯l|=d͘^::VqS@cr_,WvJ-s4hRO[ zr"&I#N3 KQ>>I {O?YfScpt.lE~- r>@S{n ZOߊ:3o(Pe'(|Z !iLǙJ<\t6 )WXmǾ:o ܊_]ܾX̌%7.%/e4]҂06 1hvR#qf_\sWȩ29CDw+礧N ԋ( ?njM/8jY 4uԤr; [:laU4ʝHÔ*@Zy|HQ@H #SrٕEԞdLf&HhxSI7۵|~U1Qc )dRb$߮:+"ӢfH4n'NONvO _/hrjESW*䦄0maRl/lU2peNޯ|WWr!z"UUwlBp~U[\&E IA pIm]Rkq#km Sa  Ƿ2mqHJX[oS,]$Ԝ{Y:;}"IYKVpvٺzAz 7\[VHb{򈎓؛ |A܈':lMߦӂo?STCLQR! X N| 5o1`hJʲ 6?[is&=cWՋ0[4P׾=Vk\b]Ne#AxG#dVzx'AN6`@ o\*Q͍B UlNSHBlWC/êG$ 'MV4 ձ(館`Smq=BBĵg| _O5wqQBI8i,VmS|2)kyM-V^$UW&1l1oy2sCeG:k#YĀ[j \0yg_AQR@~t/mC#~ZiqjQ_7 O[GBHp (\ Sp/$8bE@ڽVvYz$z/ -m:. 3voLyeHCu@HVEUM:m{nbIRoUҤ~ա*lIk1"JIҦ躈?ױf5.tsTkB$ƃ8Gs:3 d KJm/\G%<<2RXK߾:Α_&eJ].cOrpqfkQ-Ug"TWmٯ+q8 ~K2XEC!UOdS%޴t/UEeH=oEz@{jᖕ4GeQQHbKy@ýp}L` -xntƖq^g )e- [MM VBR]|$)pMԋ>\:fblCj]r@iкP1`$ 23iB1vb{}>nǧ|1J$lSJ{ fuduuR)hi$HaA9Ǚg/xO3ԧ᩠cS~8O'>0o˖pĪcJ-5~m]6C$ a+^|==a|ϹG~<Rg?_Ue dWʒ6[4S;YOM * ^(s[+^794uq(T>kWb2⎞sR.ǭ6ox6}.hpe5?2Ug( +C\<3\mo}g|,\#u82d:#I (2H xcs,(iC,cւ;I\Hʳj|Hя#& Xbŷ\ oR&q~-|#dˤΣ>>#5 3^E}sL;ڷK!8XkW85g6mCOW)>Ky =@ŞIIƜATUU3S3! x,j\C=CYQ\67\-|ak6wW4=MMG6cQ{٘ _^ZK|˛/X^**ZloYW̼!\|'Vji5n{88yWW CV J.@>Q8?euyQTn[FDecsu-pO*qw O? H50ҍmm̄<$\w`hL ҅\8}2-X%,Ё=paկ#ePjE΋l9i'Nu=Cryo m; #n."Y QHƵ,Q#ro,mVСO9a2j}D=_6#J0jrE羐l8H@FSUMڤ:(lmIQ"JK[QïʐӶm"wp(jvhف#Y-=[t?g^ԳJdW Y.P[tK*Ă@.I[5ϣpC%UNKȇLP&~ uKNdyT GF 03iuX_iqp U5`$7lN v)Wb"c`X cA.fS U:&h!BZRLeC :ή/c~=WY%Jdb[/,+ Af;2l-q*jWg94UpUq3 iu{0aPTM>[%/WUGYO49>c$uqKRKjؑ|3g 4t4Ԕhb*l#M|h'g,3,a3K!Jf>R܏sQ|0Zn#*ܘլߧ\y&CQSh7ܼ?7-Giv*Go$TEwc؜8[vLN}j/r-Y=+25ы%]/#U;=nz_TMb"XήzY JipH6.s'-j{. l jX@lHO9gUfm taYQ[@kK\pvIϜ1V'RTJ"ˬl=ZGkmǜS)QMQWTii+ePCh?|JcdX8vh1Ad,FEֵ7ѹ=!sj_;0gYU2ԊNzJ$dLϗlI$lmyIՋ+IZ,tY\ܠm:g1:[Av_폗N%JdE}K<ԪI"l }`1EaI$21q=W%BXȺR{L5"OuI*/N-xL,^ yQJeϨ)'F-y# >E(y9n{dlO2#6S~1{4kUHIKB؋AL.Pt`,Hb|V6+ΎY! UyULR^aEJ`tҏ˝,MDl.TFpҝ`BuLdY oj"HRg+bQY1{_PHQ7{.ɩJyƺ1Uu_|dH90 l0Yĕ"ϱo]dJSs$ bA< W$#釪Y f,I:ǂHXA m%-B2Z?KX Hm2Ġd l=$t\ r,#HpV`M5@IT H6QtpSm {FKLEm tt&Ss|8KB,E𕊜,$3X*#3i[S mIS#[Cj. a'vε,t#" "Ň?88o5fR"ҺSCy$Qsצ5ǘ2͘Я2|^::F\SjsC, fqT"cA.=[u'xr\ƪ,[GGD0+Zr~~x<8/JL8r&y8Fǭ|lw rBd49xFsZ,jbTl=HLzUΉI;Ǧr'JT!?޶䮱xU'NNnqʒaeWiYG[ v7qʥ 6}$W,#-ZoH7`ܥS'CWIm@4R¤e! 0^íDQn=mrF;]`G+vt'這0MK=zqH 2P7;ZF3r])z!^Y=  R$yPO-ouG`l88V˭g>a$86Yk"=PqH:BupQ#5KX&***: HŘ̏ H\!ҍ6P$yݟA .؜%aVYR6߯Å$ oE*U)׵tp 7Dc*{6ਊ { rs2NUJ*u5: c!dF,b*l9U'8ibz\4OOr(NǕdžU+ylWl<~K<.y|h>cD$X %8cBin17`ɸ*LQF "c pr \F>S#Қ}//~5c7jpOY zmjU /#Hl]̀^3~JHD73G),_CZ=eXd:uyOJo{^UuapڼHn- v@^o0Q+$- ̌u!M,Wo$`LIzb9VI67eP |tMK,f[ʰRw\3ˢz4KmI%[ VYT vm\%efWN hQg=vĸn\8`o Gs޺eLS^!qܖ&*<((IC_#aĢ cu›De7*xeisݥ*E4M O`=W70-o]i<_X=8Q,Pƈ!#zOIauQdTR$1[#Å+4%3$y?)0 4Ѫ_R-J)fc"%R~JPT:͎BU$X]~] CB[Rÿä޵PlYrZɥ}]f0/;6!;]KAa{pdz&4-}b5V]ѣ@ t?%$ ojfҀ$.Ӳ:܏$,jYY܏+H͂!X7z5"9O\)Lɵ|1GT)d34t[ol1Kn7 ^m+K4R}HsKU%44*^i$h1 i_{r9[n'Y$j\׊jjw 뛭㙓5J _zY,<}xl.xSlѵ+.9wx7ו#V 9OWTyjQ9i$vWYTٟ 2˸8SM̲p{^gnO0I-6 KyqƀEMtl7v6ڻb$#>'\U7"p##jR:7>VQ`\ mvTk*1FZu ! X_}%#WЦ_,Tn >XUĵe(r )LE,y,3ES [űD򓘹Rd]rZ5^T+{u7#}P[a+G^0wE#TTGk2fx>%NQd,*eRW?aSC T4 T X3=_oU[,m\MkRr=BߢNgtSKqT eDRw%HAaa" 󜾿4ꞎei2R-rGܢ[} wak{ǭC-"r^yw'P)9?<-ڦyXkۦ> h O ̐7f6Em{wUdR054esf@*%yu')_o-} rsyg5qĽqN|$<WQ>X53[AUv.H6Pc݃-Zca`AQWϡl9Y0[ok^ 8+00[T,uW%TcM;[JEg )MOzc#`arpbӫ Tu:Unqљșm+|?rQoj &OĖ\lΟ|3Y̗`236unq/L ɹmo jg ~m<t2@O1yR.(rhHTkD퉊/{6N+g8K$w<"?3|qH`4K @#b P_R~f$XF"7{rOHdȷ`WQ \. [TŊkurHBjJi$ЌA|6\DŒ@]7b`d \/,z!$O#0?x3KE=*,v*; dU QW\0#Lj66“;F'U#opm`-w+= wXGAb#)&( F!砾XM`7(Ǿb\DcmlYP btc)Plpcf%wl1ՒmtD_3K(of| XAR}uVPIQ~4mC:G|3ϑM⍕Tw+. "t퇰7iH5ElHʪXZͱ ʮI+d"&-gv4Ce٘R&Y5]rGoTj30ȯTzDmG|; A^ְKc$)! &[lN|Gɍ ʒHsc|VR(qp$z鈈Bi FGP@f$*"%( ʕ;kB꾓 gISDoT*,`_FÛk'X 66%Oo`~nʐ(Jz@ vpRB cl;J=R1F w^d tzMmG1;QRucK  .RAm= EJ9P-DiCi%Ym8Hd=Tna Y@Qٷ)s|)n Xl7qg_kD{#n`xE%Án!wC*Rf*X ݆@էv| ( u0$lN|ڬ Lį@1JgQpsIfY],cU[P#@o` ;u8]RJ@Xa]n)R" L 7,3yks,rȧi/4vXn{}1}ExjCf9lPDQ6Q|_>ϸ3jXٖ0Tv@Ljz.yjEH2=/|;Ne5QUg;kÎxᦦGm@:wŗ_,"l2R%,dx;r;c )|VUYRbu]v\e \G9^S,9y]V=9Ba2jy2i%sY}:Mўhj88 GoS |w%2cˡ`ѾklW1~wŷopKU5mv[CUC#Z.m^Qe! cUv( X3i_3XÛ':O'qN{f!ks w:E \O'ud hVt7į3gvX6#;(Ub |Jsd?G>ܛv8ԏx,Ғ||K%U{;mln|п(QVO?/$ E: &2\wv|q<k>oe j߷׌V/{`@fͬ=Ϻ_IBY4mbU ȱ=kʃuai2%Wfa8KlMԷA[,40`MM`HGOT M36ˋg%~bN?q,nAk؎s>E$ ^Hg9|3H[˗>?θ<|fN)Pc`ENZxs9"Ctk16^ħY} UU(8Y*˔bXmabq:S2́m^Y%=Oe/1p^kmrTCQ2#H,ͻciA6ZmcGb\-5Q,p-lmF+HM=s Sk#W7шYJyeo 3Yv74\>A*M 6cYCFݮ1g:ueB#Xp)I53DqG7"̗DaOQ4TzةYN]7^cPOQbYors}QY-_tb-o}N~"|qfaVv&:c;/k+7/7!{쾖5ND.+ 88kNeEp~Y6k5ljc.4,?Lt|9n Ȩ |Eꨜ$t, 3 ނ9&I* ]MiMeA҂=(VP,Tl~1UԌoӸָ}cͼVx~I<}ށMJl45Q6 /r~葕mJ [+^(,;߾=̦o+,R.qBjvkP7: !8FXgt7NYjl=KO_{`r>I 6 4j=1U1IU&[\I&w8*,e.,SCͮvr;7T$^II YQAbwż毈L\(VgFIZT|jLjb82R=`l-6|? j~'ึtE;_Kԍݭ4~.FSb=a6k4ab}T;2_.ĭ=~RJOvSr{]|/9+kJ ql6Q9"FS9!|UP:GQz{a#,ǧ3\ ӿm p*8j2 aPX:ΡoNz4)L!k$pѳ34k w#jXB :W L4ݘI<rR)rOjQ\[:j{;=8.>jq^)S5R6_]UsIW-"ɚI'cc}q/E7VQQD2RXYH}V?| |TqJB%*or8o\xwJ&G<-PWNUopiOqu؝dL*+G_TVJA :I#q_o[qoMRDD?6u&gC][Uff)댔 K\PNoliY 0ca^Y:׋V͙?C~ް7N\c;ј' RXui~}<&iVn/Җ-ArcCI8|^(%1%-Yk2ȳL()SHein۠Ӯt֎BǪ8¼Dt qa;ߝd| )z.#ˊp6w./#*@ҍii6_5_κ9&  M[P>v ^lQs!η:SĿ7),-.w=7 ]oz'R^O<~qiL5T;\I{ -P9u35ͻ07 %S[GOg]Qa{nZRcp5mQi5~`_2}6׻<ßPcL+ZA=,)m ؤڛ,nRjD5u H"6ܑؒ ԳgX1Sx[Sf\I) Yk2TS^4qE:=sryWV畣}AL3Uٱ|9MCGMYEUSCiYt2?cq =7Y˨*sQRtnc\º96^*,@?ﻶ{{־9C*ͥrV f{:ay2*l$>\Tt*ӯ(ȹ(X%iVf>o-FP߼ߨ^.Sa[+2{FW}RE6g$|cO G5]]&Q]4Q=nFڷ8{֩!ɨk$SC44ٕةp?N&xQ;Lҷ%tyL׍΂ cO|?#u}nEQM=IVJfS~ '{_q—ş4g+O@UĕTPc`mP[#me*gx4lc;~z=H {ozx&zJB,A?|ϟ|̲J!MTJtW{\4dmD/w}V|21* CTACT1sf4YR\ Soe*~  ,hdw{#HB}퉣A5)c:U6Rmٍأs9y:RgEar/yN4Ԏ+ ΁Cs)5[\lワ_Wd-8\MAQ^8/Q£TSvRܛ(aF7/nX)G+d )5䏁j*L+^'@%w8W g- t )a}^7A%s&r~:bBHk'wƛ3ZLz::]I36;`1R"`xخ4 hh,dU[๖2ϙJf*g1o.J֪j2j'em=.d0K7I*Lu+(yc}:S.G Xh;/o ?(zʪ.ODΞZ, \_k 5Ce7ʼn ߄R+FJӥ#P, c/\$f`C-L7k؇Z@ÙEOiQxY㦚:6ZIّLZX$^?*$h[H v;qFqLjFc57奛U{܋t=e2P1KQ+MDt'{lz'oWW?wCWϔY8$OHk~Z{{᳐dSqRU&EӰ̠bq(x0UjqE]q7 Mh̦*'LZ/xs3ˎ;x603)*2FHY.⧬DʈEZLcofWGoqԼ濉e9puk*rA;[=&[R&]QBSRS$aE_l`Ë~ p-kPT~ai0c-3 WvéLߓ+:(]<3˂S8stvѠiXTX`?#MO+4`fU\]U߯ y\ivslW[vj7(جe B.F\Ge a{n^LqP/kH\'H@:Z?A2fQd==ー Odqpj#[u4eF[xTar^tVIX;o鄱UFԬ6[1 Qo]0wiQnG8G AV!i؄BK"ynJZ;AvT f>TمAQ; sߩ84Xg*na/ShIً7K߷@4m? : u&'~q)}6B,E؊$ԬHRpڔMݰMteBXs!B@u06Xjt,fplXآl/ă GY8uYIi. )34]oƈ<@f7Y d (# 5rKr,t}Y Pu2u}~%1{o6,УF Nma #0s,X ;{tH Qk'c퍐Gf-Kh[XfI FǧIPi@$BHp1TV#M߮&}WXDa \^z\a[آzb nyopo OX,)H"f _K92+@ bK/h5*Yd9]׺Ģ(ieIFGb2a )}=G_"=0 c|iGC*p+f؋'5v&Q!r _Wm4(V*A[uSp|/1ΘTz:08N8fD@zo{bgc0sc`[U PLZwGE 䍘t?SH euz~ yFQ¼K_*u;Լ&Efa2s# @[1\ߋٟ 'j7>S2v=1箰DN<8P,2o'm+|7"R4'o:9̦ᜊJWb[!x)KCQM%U[R˜AmJp6dr5 ̨|%vrK[UәP w'm LoC!cw?\J{YP@-mBlEl9k\@`E\[[#{\|yi2*1 :F7&۶`S]ShՐ'u84RH#ֶ.ꌍ#q J{\CMa( #"*}^6dIVgtD7$X Y4,Ҏ,A=Wym@$ۺuGPEa0egpXu6V%$Na|F2a2}`._mB=c1zæEu`ޢTH ԁ 6veŀ=MJo3Xuafӊ'Jx#N)T ~ H;ᓘ䧅d0rX8lUٴ٬PY&}:펪~"ܵg:$ fٙꦧڝj&m"FkT6p i操Og|ȆWihQOJ(. [}YSYOxuY2B:3QQR[3 j<<>+ɳ>H˫i9R. {_7ia99<<š1 fL2O!չ[m\ϼ11AǜO S hA=^E"݁=3.:H#H#Oɉc(N4{qC4SAz/>:qǔ"IG\w~%72L.˲#nS΀IC4 rz0`#fkk wZX4a_[=mCy$rJC]WQT`Ĝ:i ) -' %MaTp keMӧSH 74A߾!Ev`1v|8 yG̊6X_n]6;@e.宭ЍY.H`mau^D*T o3辝i%OC+]#w8IVXC{۷M,u+kH'f[FYH]#Y$Z$D]1 u@+Zu&;* D p) Qp bnV{iӻ/o µ(Ĭ`m+P[~NvRwϸVI ~無(Cѫ+꾫? ڬޭ-{oUP؍8 ('a pn fF_, pG]]NoWS%BAX!Ow>%/2/q}i`ml.I A*./,n LJX,ElBIDg ѯn_IW6sw$K+d:Eol@Vq{[kt8P˰a86UsS,w@]oPu7`)Y6&{۶ H>FnFT+J\:(e`Ͽ3pyGK;),:pk0mJbp3P݆ X KA1RItWI+Mu22nch+P5\3(pWe~!A*3d`Y::lYNbˀ_@s( L9g͘Sf o^z(]#ɵtIGLPahh HF̀TFK#,bX.6 2K!bC&kw$ikHS:0XՐ8AI;(;Ie;{aݮ. K2eڋvѤXD{.%,gX y 55ť-*`_pdX77#xSLLA%R0ZO`; C"67!䫫ܶz0g7pI I۩-*PiTIamp=w7HPݎK`%`B\YFE FG0cVX$b$J[-ͪ VhmW|>.,t9DpPQHb4V!?/78c>02ZW: )6~.;-x;9WT;ÜC; J@m烆߰M Kdc rD i*M.:YyWVS뉞fpBtF:u Zj48<ҎZ~sJZ|׌傶):]M$m !?A`/᪜8I@{۠lsOĿ0Zgi薬FĀA'KOr`#c*BoK"a|cE+|D7Ub*d7"`P8mޣ;\nk}&emA*| t5(*[ؖQ'8&0Mu Z뀰'6lIoe*Eʛa+t$?VGybz>d@դ U(5M{t˛cmCTG0f <3Do;[+s>&\-PMc2y42mq>Ą>dyL*L mI=lyckX0 /WbtQ>;grEBKK~5더#jW?.ݱl\YtF:,,=WsYi 5#{6΅F24 oou~Lgtn+SG&_%d,\˰ 7و6t#Ż׷kT `Vll$a\jխ=~t*,mPڣRki@؀B\IUWVar(Wc vOBYT3mKI#OwK  @tA" t˅+,k[n0m=mmX!JH-Fgo7U6;| )=ɶ+v$B\wRc>Y@A[5BFdvSFO#[S 4&Jd} m8 rO\^pgJ1M5M@lUl/n62L//ʨX*tNؙQSEO‘(,ð;3S-6v!M].qv ܋CSqJR#cF~U+55p~$~b;[G*YRbkCEQ=#|)K`ˑun (F@T㕌$8 .N} N-'ޗPVϰ>Z%G#[/ęQݰefTDN^ɤ+1G]AVM:-% YM%DENV һn{f4T'Ce,~b,4`ӵQ]Qòܯj[ Xdp6Xc&t'bGࡨE3@:w~G6k4Cp畒}{l@L J*YG۰?Np UA,1iW4vO=0"{'~x*l 7|M'V^4&U,-ѪJn\pZ %"Gڜ,G^?ιUɾn_ ?<+,5Dv Ib7=|7Pw2sgθ81eCI {᤟ᆝ(}\!Y|ъ<#O5re͖RWP}'~{ GRaM)^-}7SaQ6R]e5 Tm>f?liԠ}H eZ]QB48Iܝ09E´ ?RJ%2jH40+i=bqaM*UXQm"wjY衕i<ʆ_G\̒_T}Dt'|]Bl0߸lAQk'*>&e֋}f$"PBj;`ra o&r"R/b!o1ʷY@Ao~ !T J2V[+9QWO#elS;{~e?H2xAD?/xPR$vv0#* KQ񖺙B'q=eFYV*ښH5lѪ#%M'ooÓuʩ˲\<˄Gvlي e`=zm$TB4#l$q6I!pQ6yp;n}|Lֺ/$٤W?YGj4+I8~XJsI̡FIlHk e,eY6w sBHjP'm/&Zziȹ:V`G91*\0H c@Bf#j`1P؅ϪOKJ*Cd|H-.)8 ܺi5x@U*A}4I,WU,_-CSó-U$YO.hNѢX)#kmeQgY\=<\ߨƉXuN ng|4hib{z=1eՎ1M7In;?f5|bg2խuTyv .3դm#&f2 r7i h"D \$8 Pvʼnv K=q=Nklͪӡfv&8[pv:9n0ֱ7C8 J<]m#lƜjj-"T I q^~o6s.XqEQõ"`,J Q?c9- ygQV jQ 66'kȪjytη2Ȅ d[61.jҮUQc`%<p_TYE7B-~WgkiZ=J *&Ii1Yꘁkl﵎8ԍoUP,!&{%drE>S0mΒ;\*$*E9 Y]ijokc|{c2S;.\Ue|dՑn_௔Z:hl"*#v aPS%Qҹ*XicFMQ/~9ņM ܱ ]dFfȧ=N9?Zd3f;Hѝnf(}{ LZf)*— ;=ښ3Je f὇kaOi c38CƠn͂$$$+P Yz {}2ԭ~fja*]Vt~5Vf)B\ V *S,tE eGp;"LP&\E)~w Qhq摵/:Z_Van#2 M4YVY.pcYZYu]V 퍟DWpSq?fUչ-OEW+Mٯk6.h508'#k WhYwvzh) Pc[͵,)J@=.o:tgW+@ {"suS4OU\+;&r4 SL4Pu\k,(V#f6aǣDn\/YQ9FU{2:~e6c2:0nKCd^ErOGP%3&z߶CRk|ʦuJxk1PRT.?"8n:HOZI'Xԉbm &ֺ?X*ܽaؚA#;j`06| auﺮlT8fZƚ@?,ҁ8(˚ ̇ <"Ud& ,69b7вnJ]؏eZU]UU5BF0I'>ÿAƜ,Re!5Q"Bm -Aymk&^j̐1iI>t,q ^O=5:~PUk߮7CwBǂKi6| ָ`4ic ݘ27=$A&i #m 4̌: Xz㤱q_OBkff۶X3 bt+}DTb K|y+iS뉬`nre qaAVbV`$AƶuF5m/mPv6`XZܳl=$ d(rW{*wl %F6d v l删x]I 1K}Y$46p,>lzobtX7k$~`-+0*Y"7m)̓y@!oaЁX6}͍TidnB6.m>i5/s{kFWkLѸWH [ceIym~쐺djVA` lrB}N5(nEuKks>&wUŊIjY)X`~R;c38NLa?Tz%п9ӬіSH+;oԾaGW1zOd^$(%2N.D>E=!q)b/Sfgkv M @ ) RI+{\;"5(~UU"7K$Hi'H7$\aDQOʣ1*}tQxMF; X 4A}0=ZHz{m^@KKcEwlX^KF yiG: tM.(6*Nl"T2BteI "ib.X\a ar;HEhKJݭ4q?2F%O@|y. A$  {xNUX ܕ; Jm`)Q#c3f }0]cmLPn+iX(g{ at:Nt#_`C/{ax$99,FfP0Vٴ{ڈ 酳+IqD;Y`iØĪY1нe  aP\j=_1‚JX@8!v=nR {ۯO]1Cf+k(6Q`v,5Wp/qIY].aG]Yb1SفqbuE#W A摲;튌[iccVڊgHpAE+6RҰ-IPA{k 3w?$ r܇.hчS}*u(vVr@|+7ljYwy3+dkO o[jw1SkmLLąaeڏ|jb;f؋ޑ#{ th4vZU}؟*:d؅8*SqR\_A\2Ev{爂D#S1pH%ï:am@ d#*Ơ\rN\8)lA1MOL]toc[e"E,Ab)'nšk3tb|f.@99/&Wg F[`Lt^w—C|J|<<%r]%!oV[?9Iq19-;ӕ^t76x5ʸ-󙫲"TP1RznY̾KE×o N1esГu o]H%ʱb-ǾoKdH۶_*؃BƘ7_Xcىo:7 .kv'*-o55(6hYy8f:6<뺋An=@￶ $ }Wa|MX$GUAyWpE[ h`qm d;Zw z;u1!*bd%R99Y%t˨TQBŕʓuMg-pz cE>8bs:|EQm6~'7X$['L>Bgf.,42LJ?i`l>Sc@%SGnE8пã3q!fzs³/Q[\鷾7Jb4wXQ-x']+./80Pm_:l5C'SZ21 !/:ۮBP0g)\zozMְ#LuCҀKIF iEe`*][o&DS(UҖ>7#6t]+HUu@V1uKkkܒ 8Rm Ji1%SH;^WU`dm̚NY>ZX?| JBK# AZ .WiQ(WA}RوUl;&aTE}8P R41*7$_LdQI d!q WK8L9)^ :=A}F\ pγ+( ]RAEQ+2,.E~ɳJ b"G$-[oWRzd!T[ةc3@_y=`8E0߽=;7 ʂC!UqbO`fHS+ܛoW BȚnM\iwwE<oGxX7/i$^_gls^8|4r#4(4c.τbI8~eT=/nw wźω,rNWI1.iI%4s;J+WMqk篊 /7xN |E>M&wNk3z3b$yjnt;M|68x3^Nm? AA[FUH,pߧ|QU{pnˠJjzȡ9iEzyQuF]1a{?gNe7,<ھ-j&&!Oͤ/!%%ʋhacWOlqR>r0PAXU+Kؠ7#è RAPIUU h#Q P8uf?lI)%9_$ f] 1x5ϖUSUS`0x$q˖_$B{H8/ (>sSsGqO-kL 4c+4u5oz 6GJ̀AW6V6+nz$w$6Hn륃m`&Ad:Nç_)U(P'aƜԂK 3}sp#GAx+y>gAZҚ\}lvŚ姊NP{8Ӄx 8F.(H;j+rY@;_sbP̌ Ae)4l?aťPY6bElOX$ B% HmRc93 ~*N.['eT2HTqb.olf/')G {FB#x } ykQ|H =+U>}Le\翸QO[ԏ|h⇙EE3.zPP>PlG}ac`0JRۺ( I)Zv)ɳi*:AjP4kkw*ڞJ˟pAbh)[Y5Vؒm]To9q.k5C_>}ijLT[)6:]FO-uH9RB/,>hO1F@q-F/B^bMs鲅7k۫ tA1BսvÄM*#ܿ[4vRv84*U$a dv nNa(2(_fGf%{(u(b) wl6- !AeR"yGI鉄a VI،66/u? oEѐ$ *r[]9 "'BT݈ TR 7N 6/}.mm#e-!&+YFyq,ʷb0T7or?A hђX.DCt@/xYHCl(1iђ?_ %}^$Quoo8#lp_!ۄs?e0(`7Y\lq|<_<%xFL*SiJ ߠ8@3CbŠ+r, UlzdB7qa#U7S:[ZG !í),9-̶sM1 mols0'}$9 P̿,9ngi8߁䆟"aQyc:@ ~◉|=IK\ z*%QIOն;KMM :Y}[6 |VDҺ.{mֿ6H` %MJZ\"Ůnj)b)wTʰϚN@OjInzOK2 }ﲬVJr|*3J QRn`v?Kx.Λ.2Ε~[ekL,L i}?||x|)Z󦤨FР ozo~|w=o1<r%uT:Aѫ$>W:7}8(fDuJBhI/T*yH"X[kʸh +yRcVu25Ǚh[R ku5S"N\X^_6ɫex#i)ԬTʭ{Pn2*FVTT\X31퍐|,O|(䚪01 Um%l`/#{-4N2L}v<8SԱ*46Qp6be]W ` z@sR$Q休ƺ[a$̈́ ,NDߥ];N1ɡ[d NչĒf9_WԲ}9[eadT]ucDlxvUL4+T%ͬ4sKy_0HVb5( s`\Co@WOɨ.kq KKLELPoqԄMV'U}1>k7Aנ:r .vm%&c7CF]Ӥ+"@YOFq4uJE\_U£2Hx)tq酓$e(T\[Q$jPHmkFrDG :ʒ_I&/@8Q4 (A]ǫVrA|]i"s U\wMoϾ:u9r"G+)H8ױ0\{L|{}JԧqEw/x:L+ˌY55:40wĻ:n4~5J ʦC@,Y^k1|/4>O"~oo{| nC-JU@u&J6;f)1J]́cŗ.;+`d6ck7U]AECIGE$6-o,Ij )%cP[P>tbN@f*lXdzh Zk /u3IW1CwsIg]^0M_熥,;[F0zxV^9t#R}D7ͲH@ Pq:"˂ ())#P~;KQJFL`),3QRVʅOȷCl/-y `-rHeo(A߯\UbX2[J +g5CK97l1PPBcjuWe7u/Q rij;~i*.Knoǒba) Dd;_F2Q'cl/K o0ֆ]%+?OV` &2W ;6vHV5BJJ0 0a`R;1{ FF aVv cUН7ng" 4T 7o|9dϣ]F?킴_d卦r[ LAdei6RT_Zeb^DWUZ5';moQl$WBH*qokP5kh1den44[i| {lҜE:B E0w'HN \13+1g!k_ y6RVmpwN(%4| ˡ^1z3⧧xr{9]qr)Ж1-ss|3 ÅztZZJ5y X[K9Yc*t) LHsqnDv ;io!f@ɸ"K@aev%Ɂ$I 4zuq|; Ffi@/I)-Va VX #Va pȊd΄–%}[u%!j ؏l4YVm6'+֬I%N`Z*$YLvk` P,H b 7)"άu4$]Xlz8u1hBF,DY'U2t~>2IuS be~&F1i`>Lr惺bB,-.\o^?4bǡ,tnN:c `d6(̬ʘ4sG$pַ66* gHP{}p’`9սp!:\jaC H7k"?ldb@GïÚxRr7 [\d#LfV4]oC25L-LѨ}C#'n:w ,Q6\gF$@;xh # 8,GI>6Y?;=Gǝ]^(ge@'dcMƺe7 "5Q87p֍GtĠ=.~p#zj;F]]Ҝ &fP{O$.sfR7V.[=,c<6~=."6EREtGAR) ib jn0E2l*`-]ntcBprrBƗ!6ȘKP-QnJz w @)pv'*3i6k}?p+/S_ UD!+k_`25b% {8ŚS!y"UlYfe^LA $L鈼2Iv%OZlg=mSN FPVL:NU=ZЕ k$&tC FÉA}^-oa!V8;8BK/ga10Y*[4BnZʿ!2(#C/A JMΞXh]Ept۬Y8pC'd ǜX]?5@-`qٴ3a mpzd])7)RH}R 5.¹@:tI+ 0䐺!$OaIqy̮d̒6ƍ eqIXs׊$gPFyWTŏ1"|;".Js U4(bǠ6`1uxgr^ ʲ쮊0#(*;uHioCi.Kz/Mhĭ%Poez/Şp>  wya(.pY$eFZK# 0b$0FaE5x9MF ^ 2#w "M^oqc0@UM( ~P!~j^U}2E]R]H;_t,at-mͻN&u )t#1[dAN]=L-nEVʑ1:[HS`X67 ;:}p)dubS^@qN#1 mv탋ȲFI;lGDcxFEңR]}1,P%6l?\6@cvĥ.I iV8wo.4/X b'PcfcaF6&̇%PApeMLؓBm_0I#p4D(aWNM{v/ *3w)Kuu:oolBHtŴ_" VEe|RcBڮڵMvCF(O|#H tܝ#X51>cRq"θχ~z6KIU=JtxeULc (o>jmbI*=䫣\/~nd|A|.rٿ .3`,#c-Dbf CU^uwZn Ժڕ.Eb *W{p67(z1c`>xB:@1ۯLD,)aӯ뇴5&*,:Z罏|yfن Yw"TL7e}}VHLZjWOpU\Rw.*76jPNIKGk}/bw6iwSeJsϚ4ʷjkdz䀩QP7덷085I\FfDCƓz9yN梦2م$a㤯5>Kxsg9Kqf"߽Ͼ<'Τ8q+w jOTEY s[Qԧ"e% ڀEF_rq=eN "$t^XeR NlZے\IQnUY{/EP}%b54n}-m8%~%h . i{!Z€(] #-%@C&WC@MBRJԧa8#:VYRV2 ]c79Yc h=yOWGɫ(TA,DbđRI_m:a/ѨS ޚ|=\**֝c~hBIm]|o6Jdcb,56U s .WI\b3ȵƺR;b Mc 49(:UP*\lq:Yc{fXNRP!MQR)q}ƙIgeVr"tM>`q? ۀB 1 zR[A]}bD U I xԌ-u$?PŽɺm߾<48b;pq4vF ?-՘}$}uBAB/"WIS^%$7%@T6Ի# Q庐uoI!}=s_MXWNB)$\ |\X P** Md[F8#C 787rZ++Qd#퍇e[A\Ik.PW,+M 3ZVea`Oon4RqpI1אi9}e\U G,A=I==c$6|-{<7NlP<SGMs2;,% rsϙ4pfJ) |.vLy2:93\%̲JQYQ1Ÿ%o<+4]vyri姬!,}Dkç|doRF~*sSOݺ#鲜((i:JX,q*LOHPX\nzS; ij9j2*ʲ)'}C 1`13µZp\|BrR52'x&ͥ&ᩌ^gqGH\hʫ|֣*i˫)D"-lc毅: ϘK'Y7}&m=YaKˊW6W7byNln}]QÙRg5kCSG%;z0_c>ygkY5[Jf6߰#"qq>j?nܶ⏝yOEsy]-?-HjVNkO-̌ⷖ8+8Hg?JZ*\der:r8ON"a)+aJ18=D2 7׉NQ;ˎoRUCW9J#1f"K*b!aw(u2Vi&[¯ 0y>͸yjlѪ#ci%ZGϞ̸J+,ˢ %Kv+o\"s >~/+(+y̱ QMI Li[sW2 xwA:9 Ί*tӨUٵ MD{mU $eɜۖ2x~Hxφ8I)r9|iLjU,1's|Q|o/C026=1o,{>)\+[@"PѠ!m`0dS;C n/Mz>Vc~VXE[eg-usD"?#CGP 9=UvqWUӾ8S lzHyL7?c2+ ?Fc',s=8w4(x1 ۵f|p᪉󥗄>fTb<1>`XH;5y5Ox(~_r[/v˺<2VR2ҠܨlN/$ #O< Gw|L ~.g09FuTӆ H٭:@'mO7*|=C̯kw0Kr: {;h]vE]#Y]K |@(g?eyÕ R:a+ X ܋_֮zEd%Dyt),&R[.7 '(CeA|(Lj4hݧF{#:/Pq}|W_üx#Nce±IWj*6e~|el4l5:CH3).}$iL#|ح(e\&WWrd<ϙD H(kv| ٞw^yő]|'I$Yc}ǫO 62)N,@F&sopvkŸŧ`|ACTdC| jVFi`SboqsIs+X] E@$:G=2h166{Ʒ>!2*l˕6sRd31˄bkPtI"lmeNUe(44Q] "y8ǘu-r 'yxn冧Hd.\>ׅ抛j٩r4`bŮr|3pN332>UQHmp ؟H58iةsMMqs_4yü'ӄxLSg D\mKl}'rq_O |Ydy2xVL0Ԓ3*av'Psbے]\+pSü250xQ/eac}|\nC|t|5_N8gmWKF!dud7O_\X)*ji#Q mIo.(w}P:a֞i:*+骠L5 [aS%}M{qHث@8ЕVf icӮa * K|y@\X|$&3c)m|cyFObQLH@T0|" A16T$vW:SaCxvɅՔKΎ6BO_paE}F'kjRtfK+Y)]h)'Ѵh-}L7VT #Vq UKep7 <˴S5aRk*SPa 4eg HocdP/ mi$@57¾ɦऍQ*\)þeh̻m0U%Pܖ\+@) ,Dnb ,5ȡ͚?pDu@4ݻ_{F"- `&Ohҩ%2n$*`)a [H,ؖs]?wNfdQV*H_]QkI!(ucRFŽ;6? SiV<ͪgO B l7\I$`;41m?\7aͮfϹ[jzX?pw+ qxeKdͲ& km Z;?6[iAͮ&:VJtjbHV[W#='o:_:ʳE%\j"8[{> 6m "-r)2Y*C;zc&{،j!ZV'+']nyÙmOۈM}#OTHPXBƥ:ދAd3ZwvN44uӫ/$~GE%vY]OSNAlT1đ>c {-G8x j PyIBߩnNz5v8Ik+//f~ 2jjyyrLI'`}v|i+)T%LaB:.i;zj팺s93̸.TgȌpi&wWEw"5T{4 V[ݬ;~L›$Vy7]o|.9B#M]#P8#/ lö4}W\"i5B$TP%4 D1[vsww%ni,&Gğ2ff:Uu#k[|b<5 kBGFؐ7 -=L$h#q %͋F;ح.cIQ0|V:xx?ixR~!̛Ϋt`;]@ kc&RyE[_2Wdqi6XIbbX;`8.Bbm8Ny*/ȉb3J0۹7h`Xtl*2n",t-qBm*jx c@_?(~1CQT 7sqn"Η('q",)U$j _,q h|; '0&i@hiT::` SD )WGS-l~Z& {pu}SLi1ؓ[n #.V]A iyfqU\RԦN:C ]%r+%Tβ#GkwYdR4eʻz41_,vGB֌ߨY(#1)pXۨ=2,l`1/mt6'jc̃ҝ|(u:h_CJףLR-*@RGṈCEJ~|<( 4LmEN}:}$Cp+ʾ-*MZ|fpX>OBta|Jgwvv'5xjl։F C׮;6o+xJjyp mN}L/4$dpgJ0q*N֖RFB 9F2#>o'ܢ|/W[`9a|u(k}&_RlUb9T>}@Y(&T7j.M}õTVM2~Фң- }kswm,Hk-^F$dҀ Ȓ@RT?ѸY\/r=f>y[MKb_qEE$seYFn" u)o%OUbV>6azCp}[mu|Uxn)uʎPV pUXū!r,Pse`Y᢭y ʥE܌Zd1LdqLTֶR7carCFVQ\k>,~ o1~l]jf v:=>N-NgIDը]ŵPD<8bKLHoA-A VSmKrPK#ƚ@[}Chذ#e$}}ƉϏ\/̼g-}kp?._|i<'q湶UO$~fs) nտ_l^f$!RܨPI*OzŅ_%IQ?lc/*uèI,51܈km8k\\50Y#K _#)Q,hm+A;~22SI?Xcốt/킬d.um ε1T 8Se-vS.-nF\rG~j(Y㍧tѰji5ArAc!|]|AG5 VwFف٧d{2+/Gqd68SiWp;lHm&]" Y.m#2xjx*׆1LXP`={=<,s=i ecA$bE>6x262[d<-uhpMpۦ),.Iӵk`TyњA[HD*bZiwtفUfH# i}L:UH~\˦A0m* Il:$o؟rq1ZG0[]?\D+4+-bK[K ҴFa(z}˨.h`pxK̭{0)(# 1naZVfcD+(b\ΤfOkـcy }]. u#yhCbCF9 ȭIҤoĮF2HwFl5sC2'Rŀ$c#Gv6 Dp}lGUl ;miMbH ݟ}S--$C}$ Ha l/1ꉣ &M~Sn ӥM< cY# lv8QQ,ū%D_0gdI(o$onΦqݦEc.0B vK]G21 5<4)gE#ř-YR|겐Z:#XߵkХ54ؕW X Kko$7r!ؐIh0kʵH\?'g];ͳ$| 4iXYqMS>a?dWPᦫ -msGGz: b]e0"McYq3(`]dl3T4 %i[iMR3S=~_ńTsП7j|6Xţ' XSvf.iS*tKpv Q-#Tfq1H&祱%~0y$#obKpLAC~-O}>Fк$&EmnOaЎط9wpt2&mx8m}[IsI#OKKE ;C5y'} A᳁yzTKZvu[G;jLފ/1+Q=bROs|08wU(/xp5Kh7YOs&ʸKcy,|>4,uM,ia r0-d])IK43L*lt}s*9R.\ˉ?,BXsoaoHce$tvOO9cF|ꓘ1KB'h~;kjѕ#佖퍶/ ]L 䟒饫#Uh-f$v|{W>])1|IB=l,h;phѹ`Nu4[lk c_ZN-rmbN% Nq5#sM'亪}Zb>PIb%A2`?9) 5s:Z={鋃|x.˒~"4IZ( -ԇ{mOᣪA;7 wH.cDj| qL[Qg78|'ʳj)Nf䚓9=}/|vrnKsI3Wͳ@.u+Ecc[ᷪa6u/7{]BD7 \ )bRnWcz[NUfvd,7 ƃlzY+y:N \{m_ }D GQf~KEa9'EdUӪFN8x0˹QE'9_ܖ-[$a UT#3WtR !@K틘<.cC{&RSGqebS Bq\//1y[K;y T:usoAr/A~'$y-ŗ5*8?ZKƬ.֐%ضQ_Ȗ.&oaJe+&Nc|xsr|'h2E(FbZ(T >$p.6[E(ӻT.q\SaN0H%}Dmp@؃Ե8\_MKW<gȆt"IO6EP opR Fѷ`qVxEKd0{4lq˞*$TથV-n\ q13xjk ;%%R,A\uV7A6)5dPj#T\>CYv ~a*Qwׁ^1x3iüHd *dzI _y%[EK\K\~W /I>OgVEW_I ,vƳޯyȌ00I-YdKiu*J~۽qKjqFBI nG/bsq.11;)BHkᤒ>[XPBFA!r1٣,~Ӣ E: ;&g-s~I[d*M!HGTݺ.ԥK 'p.J|B>V8*H:'اa{w]k780\aAZG$7[Qw`$'kpUG=t78b7* *+)>[tnL {Ӛ[KXݿ,7Skk+`@)z ĮJl lyJ11Vbv~#[H+T2lb ScWVO-WS^1nf2Yq-@Y$+IicD4ֳ$lnYqwc']4F5 ZS<$4c|=+iIC(m?ט'㦉9]!$?펝<5չ, .C4.v8'Hhk"I :l5?Lkc6(Φ`,[~#3\EoEw$t'aݗ0@ T[fǕl {(B~K)|IղxkUX\ mb}*sa#Qp/kv#H=f[wY89EҠ'5 !blE2+*K ^p%v"Hj".q:ǿ7mf)!NL\Z"i)ES+恨`mb>Nrq+^1ij:9T؅]i UMPs ):]\ൖ|Ep/+(Ḍ9~ W4.T)t] c82$'U]B4M_S{Oh2HS{`QtX-4Xi]ԁXo`@ ݱ’MpnUmhB^= F@!Y'I' %Ɛ/o|):p!&}@_dEA3&/vly*A-mÈ*7PG|T ̾YlEϾjhLٞY5q#V7n6 1$G~%{&rI㩀.< N`h;(i"d b/ŋY/;}|:Gd$Sqt|=7ҚC; GO|mf#6$ƷA䚉d6mV;mӈseɼ0${{N5썇Y&Iq.YU0U%H*J5Ao!燣%\,YݙA哯k2 Ҏ59ji:wu(f6]7?t~!zH*-]a4 WK2^~*`Y'k ۥb~p7Rȕ|$*K%> mN8 55e% ob$f7NX *x-O>5r.-z"x7EboǏ~"ˎ38=|=N22,U4Q$g,I6P.K%Q|7XԱj?4Lpiz,00ͯ׎ xȟ-3UˣQ1ZO0Ϥ06\-0g\\߇r\#rJe_e_CE0EEk9w8C"Ta5~[vE<)gF ¢g 2̤6s{p;m.Z `|[?&XĿpO ]X#к¾/r.)]oWWQU2P+fhbУ6' jCiGCsH㑤+? ^9[*z@dK9 n3mr'|eeUUgyu.iA Z E7R>0NV#eb k`5#ְ¶NUp' \#K]XTINH׻0Q8kr3JY*Hja0v7-X71kc9o⧓_򗄸 54G" m'I /NUnx6,ەDxcΤE0/f>Cřzfp$F"l $m,K\k}Q/R-b,=+ʩ*VjL8g@;bn]J ɾ6$Ncds{`Ug~!MfZ> K k[|K"kpՉ`O"4v+}$u)+U} p-F ƃV%2 :}L:WWL=Z^t -i.l/cF;mi%DCu:r[l9mH2@L#\nBƌ cSb-DdY>Kl͆!ya!Ub7у{)cp ,{=UQįH6ivk@L9H]N{XS<gKqm,5ɦ]l&d 2 o! h@Ú Bs3+Wf8aBi@$H yI+0[". /C@F>AV2o!DܝLQ$xdגeVa!oSXDtkY0,ꮙ<2:t+O>4(⎒~7c4 i#I\|qF؝<1#/,[ JH)'mkZⱒUT4M:SFUTz1mhT^C0 ߦ159QÚT a1ԻT{c }ȺkTUERP,HL1az_鎟?sqo5h 9 LI432#oL9*cPR7w-cy.>.s[f-QSSM dLگkCk#ìPI.P';}B+H2 9 1o|ẋ4*2ʔ(y&*=X,j>Esmb_;Zze+K3na҆FG_c0a|ai*o2K2Iv7 Na*:Yee}&DIP4MEsPT@ʫmaD?>9̾.( g AOF \$W># <.uxZ]r Qfi#K#[s3i\*f3*P"DtibD0# {-~s|8EEL5YߙFL.}(v_Ybw01e:Q_u )%[sg0\?UutQԙ rTXSm&*s6Wr ¯Qf9EV/hvRk?#%Cv[MN'MPn-/n;9bC N݉K"yz6>\wC`F@H_<GiX d( { %;`$n== PIUP,w; ON! Uc,o0^q2G&Ū=;Xۺ^Vػ!`Xw3Ep_ 鿰jKk+l{pUU~`E䛜a*{\*0>/)i)]b6_D\4G-xK9e_N9)\[IIO|OV 2׿}ݯ* g='ŕ&Y=(vT ;u; x=Fm&WY5j83 ZIr5:鍅%~Be1_Vl8KP}q=퇛dau} Ea48I@]'b~Y*.߶*$qܮ7@I Gw ,HCXjpy)M"O(R034HF"|3iHuܓo2hP e G|"!l$/$B9%+MwpZ`Je"`(#bW_[h1]?!eHTuoe$ Bvaӯc|"6uKXm!8&])%}X v-3|kWSM+Ԭgv|Ls^fQSS=E%FP)҄nWjI/Y6G9GUrPQR +(&6Q69f66>8w.Z~%Jy$7O&P{ ؿ 2/ CSS*f4+ /&cPgygRDWԕsmb%yWS<4.YKk(rqmd4uoR3׹Šx[CUo~.I\1Nt1:eu EA:Td9 8!51u 4ӰS5 pO̳:8"fK78 ETQc~")%EL܈o}[l ϶(2* < [j!N\VfH)fY  & yʌs2?%K+IUr/oT'dqs+LUPiIc T=Hc cocY򳛜i!8\<(-V={ ]uIrbJ4g(4V^`}7S%cH6/U)2Guz %%em'JJAw0j4@uUa J3E.hJ%Û+X%A~ "c|>1EFr6UF@ im "H2I"6pFnn[x],@a~TN\c3w764.?xyߚQ^Dd * KmbDWgdٟ4Ύx{*CQ& T{Rnqg4y#8839( چI#{\`1evhQWV;ˈܮ?:Js{|IQ\,?3&1g)k1kWxs̫8;̲L.I: Ɖáu*Al[\Ƣ٥BSfT=@SoWaR`Ѻ[`cnϪ-ŏ'ixks(S/sy*o5u2UaIr Fq-IGPau ۀ}|r[(? N5 p}^K5/es+C.f^D7J{6Y"@6xP2C#uǩk6>cswTe:F K[21%8qS ao$K@vte$a؍On4+~G(TR)-4IJI1>r7V@"SQaKw8Zn&m;ŕsY֒9W}@6^rXVT$Zu=-5B1kq|2_4M> ;9r_$e_yq*왟l@퉔{*`.3Jw1* GӾ;LhVʬ1NNox$?àh*܌M~"t9TY6b4Reed=/cl5☼ybz6R ';bB5~`r\vSA.t0ÔվETQ-#" 6t.(۶&4(Ȱc▽àMwN3\RAC-Vq{9Hoo馨>#̪uA$%pT6~GqʙyLWE.$bQHԆ.oz9Gc l96ؽsC{I\8hcʞg`,-댥(ޚ7!!4A G{}7qe$; 7I  Zjs_K;lH1f(ߴF)2e, a'a¿ ;K%-G͝$qǚ,qVm<+7i^JLj$#~33W;_ydrRFȻX]xyT<ٔEDb&cXB\1En1rw|2|RS, .|MS(6'fۨUuBkɮ2YT>M_j?5j]G9Lj-x& ǎ2 Id䎗gJ.j)]`txfe'g|5ARdܑj܃! pV71퉬>X}7/Wz|)` \fzXeuQdz/`[$xk`9˪xNk?2BEwz&^rEgVV}?Wy\0u9z G{;&{#! .0EILTRؑcS_#*8ϕIS)5 l[A-|Ҫ:,AW6|*BYP=^Ziy7SP(;MRTNQCF )GQ򂷰C+ʖx8C]FTQmeXcݰ%Ts!BYT?\49Dllj3i(V) P,GXX-`\K$ 86ۯ:jSh $E \7sS )t SmY} ܒ:mE&UoKWaE;G*ĠHvrg*k*h(scLՄ)5%dD*v+~ݒfZE3lF z}qS!8ǶMijOM[!ʫտ-YjI-~ .?u8͍bKm@hApq2 )?LQ\ RgY$xkX&D{2VY!z8 UaOJOn&wO#]Ma>b_WS{InK(@8RX.H\ YX EtIS$-؁*{k&Efy6`i^8sDVA`.m!Õ)T;P%Eů] $ĒF-m|k>+rt }p`Bn w`]BNl?|4mLŀ,\wh?Hq1/|I<>4 i`9$ITRz\=Xy颍zg^;I+u%yVŜ~Veyt\ {`id4 y2y]g=Xf m[$fT OKesxkQ:ۯ'^0o$.sL5YkXu\@|7yb徦ڷ+5_ndf'`~߶4 Jz4U4gxR\8EST7U̞s8u)9 2}cQ.~FpPeMȨS X%Iw*FF:9ed 9$U2զ[URR03&釼e]MY K umHNi^?*Q=tŧp#o4̸ZU#ecPvc#(J5؁MK%L6K6"R8Y^j-VidmÀY 6GC|Y(i>Iu*ːtk6;uZLVLx*%xg`"6# dt[\QfUna]FTY]ў0vr(HjbțV $$:Uv4xW<5uM5.KY IBJvÁkyL|qYILk%W{\5,lFO5[~'y.KQ9iU ۣ\1cYqż[Q|?>g_](HGRo˙PQ.iUu1reo{{a%B0BPl~7O/#֣rٴprQY}ز5~#Sr/.m^]:V#dàcmV;a!S٦ޫ?7, ƇNǯ ImdVcsj#[ !]ns]:RWfUAUZ%E[ nɯ_\q*?Uɸz$ɡf724W$#Yb N ./0V !}WKTW>_]G i[GهjWď 嗚&IWqo9ȼF>p&IfQX3$ ^j G!Oj4JҒ:i) l..E\`/$73UVq* E PTTXeZ *y0^"IhLoԝXrFxd^/q?S#ϣJ4[c0I7tc;G:G+r9s쮧9m WSEZ*+F,N'6ᬓ*+'2́PF9hιgϿow/xPr<9W#&oAËC640z}$yo)9c8z,wIV;ͯQK;j%+}¼}eU lz4 R>XA/^.$v6ýj`H' N|a$N:i<"AV\z@)fu Iz*ܲol+5ybGp1̫VcõX$YAzujǙF}RəfSH1y݈`5^s⃇6NCpVb\ToLM*Z5XEm:8#,M,,`Gb"XPI#=ab \%bq:l+UF +[ =,Hk?A\O^QMO=#D*1uo높|2sSyhF 폞7˕+s`_%<"jOXPd`-?:MEeNc,Ee5ѕ)dWA\MYrТ}E $cjp~sD ,դL$mcֵes!Ou#hWZww/5=+k R*5|/:V}rXHXz:dZX$Q$ qʏ|-PgVW qʒCrWW-{~˚#kҎGَqK4>i,RMEM;(ak2J%=Gi^*l~a~ؠ*a=,QFoǿ TVIGT8E k4Ǻ!xm;[TGgi*|-rZCôLAGUDB"<Zq>ɚ%)&e3'Ό w63lQn63mK1ڲϘ^0$&O*0kpl\HV?1ŏ] Ҽ$ b@;aʷfб nkjpM~ȋʣ̺ -ˉ6JR T\})ٜzȰ iK.OY*eP:2^ifvu iFiboL`U1To(d7Tgc,^ID$l&RqrW,[H7.29'4ПŠw+:p͝4 CgOsob2uJ6HY55yU&,0(nN{$#x:TiIfU;|7JJD=+J?| :rS\?3_~uMPy9暬ˊY݇;'AGAp@17kbq-*2% qkaz9,"i݁17޴ixHq@@2X]GAl)ddf6$ẘOYP)HFK4`b 0ͦú`)\nҬLV2V ?QǞUh˨7 2Հdw #\7$wp¥;ȅ-r:D 1j>&(򌶎0VU,H{|@Zj&Mê,.yr) #XڦWYzšGIJl8rEB%\ꁔm{Ӹb2s_%cuYeʈRK;Aal&cQM7~j%}+)Ri'-\O.:P̺"[EFlv KO%>hCQ=\LȶT@G1g1xxÓ]MBO[}!٬{۾:l>skqCyrO7,<5|KSQSUf1ByXYd:km 0w22&t*4XX˦vQw77?r8[+s ɲZ )GQQ?LWLFZ1؀1͈実TVyՏ٣a u2\8 #J* XsNս02D ңv~t^C=OBW1<:l*z , ,Q$e(FPhY!{ O_egCBQl1a5m[ *K}zOb @ #K}OG}{$y2aU}7"_ҩ$r)ΕA~zE7P:vm鎵EbѼHrB~=ƩB 5?AFzxT-aJ$V vclH؝ U@>CPC]F`rĢlbר7Q|@KbOk (l`-%0ayLA؅$#1ĭȼCqUJEJӈ h7S@Ug$, ȯ[c e?jjL$k { i"|Nx^&]cXSgEIpT/׶;(]=*0T |P>.-9b9) yjژ)m<'rf2+W,Q,rkZ[)ZH=߲"ֶ;r*~iP.3yġK3XiY]X$6j q4:tc07IFҪ}v5)sG1㬶,~$.H*ㄲ2.o 8؊j!m>R=c |L\)oMKS+p=]:Ԥ++#K@UXMBEej.Pk4W$Ϋv4 ɺ^m!mWO[⻉((ڞii ]Φyb C8/Q&cP8+Y #Nʪv}€2\&_YKJ5^\v>ucx%4U.Cs̝J^-cC6YP=ez,H$BSF+ d0)./ӄ~{VZd2**[^ʤS#Y]Ёap*IP49}b>`fن8<1Z߃<$8<:pEW]67k7챴Spm t_!w퍛I;CPy2;m&fCyK X߮f)M'TMwe!ځ7r) ?5ULmTD6df n߭ɷ9_.Oq]O;,\|ʉ-՘WU֫ ,(ߗo1`+H;u։C,5= NOTI αE,gqk8yƥ]6 6C\ol KMMf>t./m"Mcd r_qaQ¢jPT釸6J뻸sa쨻#i}+{ô.XY VrZ6m| '4T)FKE/"MAor E a9gOAG-\򈢧VyF*1]~%IR:GԪj9 \mY <~t {m!Sy3frfnd-4,^NxBx[쇂sTtټ5 nBn{_T'09^3sN$͓:L*ʆ3`V(BQx+/mf!X`mF݁V|+x?18ڋ/"?pQr^m"̩*cU?"R;~/O \Eq ׃ʅH(YTo#u1jx(5"E il:?C1r{cnB;r!AgBWRT>MA&#,|{m|((g?'Lp}G3 ơe[(2*?,bi\rL9*`Yެc]w,ܯ$((rܺ/Xh)("ETDP-A"a4=G*T1Dz\T*J-5URJ\J1`$dy΍$1!~hEA?ý C`ܜFJ1dR IT#hp=WǦ,a#g:kj)FŁa5^^0Dv[|ĬfS^ Ԗb.WБCHwB\knART{J`Yo Ӡ{sILxXS㗇3*,'zʾ Ka4iSvzX>D5cm;o釘ty\ih'+bL`0o9sq;㛉Y2*dY"bI_L^zm;4rA@R?n6毇gO-6,P>@A`H_|$x|πyVskJX&uܔ1"IzQVO10.5gR$_;Ǩ,Kڶi8wa4~l޶PRk[m|L`_1֖!į%Iq3i/r sf!3*-ܧ3{a1 h47pyh°[nvgB`T} 0#.ޒul0D!O1:nؗ-ad"bDU%_N0ZW$eĹj,թZ@FRs:Ȅ 3-_{ sWyg T#?LҦ촳DPeisen9<=Qge8#I_Ba]:W{\,@5H#n݀Å--eU I, v䙶[]WCQMzzIFñVo%o/។3^:n8͹zLW$T r9_ p:!!8|g^'hx682j8$Som"lOqkE1]ereցʏP[9⥟M;>Sn|!g-I?2x_-˚dYXY"v*Ze'*>%sH9Jb"`X^ oaI`IlVs4V;03_(I8:8-ΦeATU l5+ES#Avw 8[w8:S}GM 1AC.dӝ:Ub 錅3>2?&"A_5 Fޤn鎈#Ākh7-SvY_xk*)1'];EÿM[8e ZGBPjۯT% $#ZJ6Sl8@!<ň銃4(ڴq+ oe?nqmך|TpSST.q Jl5d`6ej++tm/MŔmD8+F5L#̪dEAK$4)$aH9&/eٽ*eԅC 2UX0M$=ԍ2 !}?% ^H_}6#(K 0e(eyc% $7ZoDŽTFN*˸y[JM,0ak(!I "H[Hh/vzh)4ܧĨEmZXa WP-]؞v2;0eK\wkSTXw$1JضImOTu({+}mԖZW[)0eG7@fd_P |)$7EmX`܄@~krjAH$,` -"8SvGAĝ~NUQH.{pb{y$$m~Uhv:mdmEdQRlnƞ0y4/q,Q)u=vW&RBϵ}_+ʹ^-b@K$b4t~s1\= =@&iVbf̽d;b[{bY#ВXn/xO<!Ͳ3|SS~qqsF!fH$xafsI>`g}\YyaS0wd-em#ϸ"/#u_Zpl0x]y%#%>mtdU(Y5o_玨e<74te,K*Mms.5ʨ96EVy#& c?ӮMHpҪ?`_cU5UsG}6˕T87iH*{*_@SpAUEʌ Y%YAպA!$ cL46LQ<@ ~Bf@.턔`Pol B:cp8AiDp6m8)5E$ZsCGbp\;4wJA bJR,rI ,CZhpbnZ J5ˉ(hLL'։,=¹Xo9;x1 7xʳ:aQOTA+~d!5mN kHYiyĮ(3S/굷\AU\a.l¬t'|=ijy)YЩm`9#vY4H'ň"@ ;Oe.[y,41ECXK(66z\\渇EU=Dm~Y: nX5 `J ,IK$ll{c$н8FS͉ ImFQ00J؛#Kf]:mt;4 ۱:6,@0@ ]fȭg@m%)d$Ƞl[1*n'@iR oSYVeer]meb-c¤/{[X񲳇l{ T.4{X BB zѴv1/Y5wd;YMNb ݭؚ06ƷFPIUlj&_WU [i:y%yĜַ{zw.'e0`._b sQSd͘ȒWP'*Pw:YAԃ7 nvƫjOjʚN^? Ƭ|)f8U7$@aR}M {㓎tIܢnus;zls?ΒiB=Lt-nTEU|҄mnO5e~<)ؒW{kŢ1 tăJ߂R@dpʏwq03n),I(or̬Kmo?79y;_ɾ*6wo*xH*-nW5RG4^m?:_spqi6旃֯&e3 =aVVm~'jaP ssׁ>&$xÍ~%ʯ:x8x*(P2B(*"G7>#>)<|^|CgyuT52_DK[9˂$+(#%BS{b6=1>%w |@sUI̞MU%Ƣ6jbF{ ſ9d9 /@I4djQ&!0$6Ƃ8~}pq.qPKM=7>h]:u}x9|dgB9Uj53W̕lAkd9O^T[NM7:+fP|Qfdgx#Wlk˚3HR:OGC'7aT󣁸JyiEA :ʞ<)okml觀5}c>4Ixqa̬qO&t*ܘ1]1-3),lx/+,AW,:, Ǡ8g>Pd>'͸K#J#3eFXcYʄߍ rwUOygKI}H"^AeHd22 ռI^+W3*xҁ._QTjkwMP.IC7' dl-gƃ\G|o9]Of@NJN_,,)DjFBZX TvW+voˉ3J:c$ 4STE';1ɏ5Mįܩk*8[% 1 FQr~.<-dКʬf&+[MW09yʞ,]Qp鳮^*)e`6c5`ËQ$Vs`u~Rs#).x;ur2<‡]d[Hmsl]X8 >_21_ >s.Qg|ڪ\DJѕHCR?}EM]VloB) B_WM; b:C1u8S0xsں9uŕ-NuP]X|j?1SFbD_I'{}aAổq.P2J.%uZoI1:[{ajX O5pX8et1Z$ʳ*XTnA6s_'A9Iq6lTUUP#Z0'QcQ8'%<8ߌ*k*0̔TI;?F1~^2>缙OSiz DUJq {܂}5C|5Lt!|  ^2^d\`yiʾ42"jȮBLGi kUTQ⡘?x]y4<|2\9tfөJˤ>^ׇcu]=AyWm@vIK n_7ݬ~f;$z"<"će$>^2c) aT,hAc*KhS7E!]";ܜs8ӂ)'OWAK QM*:tYVpw&sdI9~mAM_GڴVSaʉCeuI.{>^IxX `Q%GMdKX]pQnMc/_[9?/A]U֙icQXc[\*ܓ,)+(k4!!VQIo?,?ednMEbXl1z%xᎁC w ܸ/p^qgd PJG($etbd'yxG-6nA0dId5u;0iŴ[d!,&(y hH hL\Ȓ\捔NԾ=.i<dq9dRYPM")դaUf=l65Mb(ZBlΧj[t20W$)]큵U]'NkFѤA+}H׶Q!4P:mcv#عSbG$'[VEQ崌1Xfck^ؿRo,:8;&/,3ԣh4`K .{MX8A O0?jeXͳys ƣG; [~MkѤKA==y"H^|\a)2Xi{j(McAhO/kwkŵ5SV@'V/Pec -2a Uyi}gS/vv  RU䊷E]p3*?-%2SiT2̮u'b |U4rCVA(.hXQkw[dmE!fU$:Kؽd1\UWQE4SSOV#o(8;?_1<ޮtY33dX {u'E%?q]&ds<撖 *hÙGUM}W 3ԧ/ԗPZңk~G&dUF cjfu&) G dZH2(@Y!UGZWQ::c漳4ռ0/C]$5Q~!eYR4ukl1׷K_-_1iE&_[괴IdP,H7&ͫkXOC1̉*5Iتȫc}i<|kW>ZmqUDk[d_D {= 췏R$3 D>I1M$cUox㖟0h$J[, S㺚$IjB8]}1s'-|ʩT*K~bTi-UM F4otg@F .cyRA.//W1L55l&LK/)؟y̞yu4Uem:.E#몍Paz(jJr*h$l?+:i[ ( ̌40G7L[QQ/8E)<Ѧr('Oԋ.8J+R/}6<ƌRb;]x6M L~KE7S _HgұdgЂ8t}/p2L{4oc TL X,y\I054va$Z 4Nq n@8ث("0ҵޥe{߮*! oct S-\j)'{7ZaFX 52fU3Yl3떞UzQn?l_:L!㡂Ufuf|:3$**7ᣠrAė"ˌW%y6NG# 5]kNLA!.@YKЌJHfT%ʲޢ>cQjZi'.ө6bQM÷l9e6%Nj1wY7oVeIʲpIO0lwQ@/r+;Aa{ u[lEWq6B1$E0P>bO[tbWZRJ0L x)pB@ pK LK<)r8hh2UUQRO5A)pmFyrΞ;)IYj9UHlA9u&_,O;YG3TQOO_𳇝OdtWĭPa`^g`k?))sZ:8vb-9yqaT%RS<^-:> ^lĹ=nIsf.+sIpgZEI}qK̽Me,/}6K|-y쒥{(9H23D&+ZԲB`z/n-9Kh(2kkU /9u\l8?)i("+!@5Jf>0ˀѿ[u1>2$vmS1Jicl4}6$3PvǢhW` ?߶:cX"7^dxcܦyJɳjí:kS`"'TbB3hȽb@!C eR|F^Bb1\8HO,LȻa1HuE[?M1B 2kZnR{ 6IK4d\ mep+)U,n.[")YɨRޛXXlTZv/ 3/SqkI6Ev֡fGiR;[ GRFY2# ,p;)4DädÂTHS IrvyB%D*DqJ:O.Hl%6YbwF1J`p– %%6"Ur@W6!&*unGa5[tAz W;| yF5޶$1 ؓ~1OT 7[, Bd~'T:jarЊ ϨX]M?n53p"]c؏m/ģuu~lԹYk32=;?b=˺$tle:ٯ}TDE3@vxkVG(X$e0om_7ys氼4zT) c;?v:9WkN5Tydtu2y R0lPH eu`-(OP>Sj20VǷy"eŀDYUL@ U{ԴA-B "]Ӥ\ƺ',^ ԖY^C9 -}&?a*"B# QO]_ꬿg9n0D$pl='({9|s@̡if:c^PMOBi}v?ղFS&D J%lO_ H*L.N$Tqqk7e撆*)g^Zɹ ^VHX՝ݤUPI'߁+7.\'9k .d5rorXc7rY>ev[u}d8 R}D 7W2%w-jw|ᯓ68^yDyV[QaT Xܞկ8~;{wi,dFx%4) F ,\*M\7zz[0֕tH2uXv|=fȲ{^8$@#`R,mlF=ܫ;Ṓ`;Dٕج[TX\ ^m{, fB"*?|ALg9|U542tu kZ5+\cnMETRԴH$kr,/a6+%5~n=m4%,yIYp1?y#a17$a=Zc.:]DIB:7`[mZ,Xsq'9y< *DѶie.U@Fpī4=`ǥn 鍐l6TܦEbIRO)Y\yN6IHj  &"6 i+x÷sg2꒗z3 mC4X!ԋm4TTMGOt1FQFa"ER#{olY'g %EF5z]FzX ݰX"O@p6p u*UmNNKHBunى(8[N Nڵ)BI Vl")n6;#Œz¤Ham᳴rD̍=߮1) :VabUV FN!LI6 or:31Guh]X mʺTK7k{`NI3d_t0JP %/K덀# ]+&!:$CKúx$8]:Cc ְ:T&)̲ut2# 2{~K3rGkpIF_ę^D#Q2!?(_M+ceF8]¿βC2ɩM%cHAB7Olz[M%I|.ͳZawV4EF {ݱWLλ~ 5)6ߋc:z28;ݱUTpNjkS9;+8<\*1ɚ*)t؝6`ߛ8֊+"|ѱ]]ets^ ZUG TɎwbY~&ܟ0&\8UIKPlpsx%揊8F<37f:QAk.Eذ%yGxeNe\_ve e9#1VD*y\2lVpJæowė§!hS<qֈ+ްKt􎠋ιo<Aμ2~0;bq~#ԑ \tKwIUJn Ȣ1MJ,y151 pJvƿU:[p CY+?'9O2^&Բ"a@[sß\!^G|MH)r I, ݉%v펇~7Nux\ʇf6, X*@Ri!IAE~'!2NkaQpq1xK:*xiKzK]Aki67?{ EeeyOAGt,ӵ\hOA4xU9CBfչ-?᠛H@X갸/o[㧛1o[YJ<&ѭKqnp"m-2\t7 Y#QS(93z<*kkAk5I.[S8?1ҟ!ysž?+W%aW+46CGzƓ 2iT3p#f)C{ؑtt&`m Qg?ENc@x{2ZBHS,w% =-|v•ym]Xmxl?+1Qs1ڳ& 4h2R+o.X#s^66* ٠6+Wo\5CYefJxS+Y krzQQ <38sXon*i̊חPw1oʜ!883jx9,UPLwx¢ #xjf_ l1x/3URݯN@ ΚW?ҧhE"|T'?ᧂ3^W֮=5$kE\2:u܍BmQx8oNg/az.ɸ~fcJ3:N+c} ^;ca]ŜKcF8꡸3."3 .c`wl9tjemܺ0&eٗ͘塧WGeJT[%9 M\C*Gr@7>;~`J.|l82xvejv[2:[c(kܸ8s O?eԚk&`,CM0*dJY.Fn]&wֱbeH;Ju7۾93^'rL-[LԒ*A${|tE}SX>֦YYCe_U+V]QHP$77f3v0R;,;C\)qLK3"~xF'aFgk؊0;(n-7YeҒ$Ir3ֲu@]$+_GM{2L4@ rtvIWAH ً`8a`\meܟ`QH܋XaE l#Rw.=9WRUg˥R6=QBݿ %P\;/V~:yu99wUJ J.Wz5'WM>|z+w:\é2,oAo7II; $!)^܋'@AcՊ |5A7/'>tZBYT ADŌDߚ2DO!(p&mwVd:?V̹ԁn,Cf錖z&a2Jw+!Lluثtl9/"2'b|JS'w=X'|.]팏࡚#T P~bG$7-mXtN6iO4 [ >)3()zp; -}  }탇DջzW=qbܿ8hGR}-Pɟ\Zx]fVPMkom큿7/ Wp|4nSj؜Xtj<+gK+i)e(F =㏉ գPWʮEMEC[kMm59l:iEQĢ|s˟fF^T aC̝>NJێjjr^|sj( }׾1LⳚYÙN*<Ŏz)L]p5F|8dvc"鎉|AxߌW當,H´]#q+èq<Ӊj%t%fκf6\s6 (hV lKaZGcdQaaibh^HjKX8]"ibnPYWٌ#-c o0 w&Z(epB-c ml>:u}\P>^R[Q_Q`p6Eh&+{?L0 'T\_ u2 N۫+wO8`x>j C`h܅} m$uMq௶ 6؞%bk!%-Č.\u?#eÙ,nkwoV B'}g %D3OM8ܹ Aۛ[q B*,y^LkRtvl]^r%AΦk09Xfv$}frn<̐]T0ɸ⤛..X\h7erwQܹ/,!Qb xǪdp SԻHYEab{DFk.}]Ն=h.^FE[[Yك WӸ81ݰPEaPT$M)tāoGΓ7gcqv'JX$m(RN{ 0;6^7>UU!Y? WY98Tt$aңFl_#4a[@15"j=. hfBN535zǏyhdx-m{omA]}lE5ށyS\ESYo~w$48k0*xbZĹtG3S#.%k2^(ʤį]ԺGk{弩\)R}{c)n8Jie~-pW8?>zΦ,$wNpȍ LN( dHA-cb$6W`ҋ鄧"Ѳpc 0h7[Q`FiKP,:t=Y_w%9Ü; v#s9?~k\h (p DՆ[!U E tհ$+/{}0˧20톖M(:>m>dpmI&,k+ li[4̈yx @VGKLkFT67}QVUǨXE[ac p*\jkmi&&H[ѽ(K7L6: u 6{sł)'4uHAp&'Q$Hs ii TEu/d`K ( !ܜ L[ 0]WE$o嫾J56^x+ GJTFejs䃧l3jfF#z1yzq`R$2RpX\hh&s1ԥ#㱰vRhKb#4oH H]Sj]2 $۾:z|+TSX@Eϒ}{t~o嫕znfoZﷶZ;np;o8 )+?ݏQ~`[He)ȅn-Ft׶Nۧn&F¨Ӭ !Unb!@Whd12 r#quOV601YȅDZz nQUX1#ӧҬd M۸f#_0]cۧ49Xw^r1HR0t"mV*ʺT}Ęwo(h;A R6ݵDP"o𚗎]SɠBXZΥXRbl}Mgm%.Y["RȮ4k&z⡚Fw 4Y-׿ʶF%Ԣ~= -7_7*ʙ*2.efW2@P7 FERda )PWvt_qSUzXmO'vHGvG2¬J)$>ex lpRB 2wWE)S9>l[o~>*w¼^&aēGY%<  [0b9-Iaulw .I|y <#on7feYo3\)fwe]v$" r٫g<.BᱷrQV"NK<%ɕ>a]<1O$U,6؏<SG *sG/ȒTkQWs6y;|֌ e\,Ģ c1͉:$뫚|8Xȼ%E6.$+:b۝0MJz()Fzu#{c3| E]jV,yPE40Nd oeNQL*ƴZ;tƭͼMr/8*#7#5d~ |!mPDVܯ SUCl&`o;%f)m-g-_ !Ϧ.ຮo7K\_b$AXb GN('k_{"TWpiJ2o*MX׎58*H"31-ĩYr(]7# `S"I%p|v FFH/dUU2Ts EPeG|XKbΐBv#Rۯ#ԤnX1}`$n~ HHHcnFA"9%l6l HuG(K݅A gFT,}Á6'tkVUXj u#lq(QRghʐ?q3`$XFـl5OT h`lA;Rqr\~@]txQ> kBR:?. B * C\?KIuݱ^PlM3un{[L)\4HZr4b~ =RV=nA1\ꯓR) 1VFgmwơpQT2>`RKe{ZO_y27ͫ>H:ݤ77@F\Ʊ2#ڊbr4PyBHEna7g|I*OW,34ir@ۜnOG˹l:֦U2=:i]-߱ǃ,/0ճM)/uM|*t9fѥu=^:c5Refl:o*28V6 /?\%܄pG``Jkn Y5{hUz40-fu$ [X=SI"rt8Ab-+IՕd,uq ECML-`?g"4%#>֪D$\Ƿj"hԳ4 eql;2%>D)B|3tyF; sh /䨍6#-Q 쯽 ]E'nu$gf.T`kJҏ4# n[{1O8$sdK Jmb[MNAn"ʎ LMY3Ldm!Qo̖70꿔Z,`tfUip#S_*lO.c?^+RΒU1O@_ag<٭%D`m:w(Q%Q s |g`vn=a "YcPIPX3ZIg8,G+n\-Z8ifk:oa|v-}^aJn,mms3=AWŕQFIK>V;uGМljU/+,Yhii/OQ}0,2p}2YGxm9\@kj ,)}O%MQijzv`@5\u2,WrnE鉯 BYŞ}J,)fdžk#~uS3IOS2*+:o;c<竤Vvi v= 矼z ~42ܒV̞X#Qe/!\b?|X1&as'Ա5j "{[ơIAMk^] ˪b;`o 9_U6vyR9A /r?όڦCINFyTjȱF? Rx*~< T5LUAقdsŤ5мqh9)LZX$~a>6^[Yb-^]Iz}1l^/*R}2e6Ps1rS~mG\ٕ MlTiAkvQpC RPLK+ZPgzLGWO=4 e*j=¬o2qV {BJXhY덭pix%qj ÖUTl? >lѕ[Q*_Clbz_@EݑA2 i/^ `+];rY=:<($,Idѭą;;4;X $ Xm؀fr(kmbza$&6d!QL8Zud^&n lh KbDRN-?L"ESL:\6d61 :#@L+#.qc qJw瀚rVPX\$)Je'K>P.ٔ`3¡eF (J^94Py_CNV$w#ǻ26\3VԤStb"*o6+ w9.I9V&e {ؓ,N@t;DY#4 U9vA0BRī8}0փ:JkyfI2O|qb,(egtl_PIF¢&IT1TN{ʷt+L/<<1߅MM=R'(6;mC^g ?γ/LuC2E>SQ"q ^/0IGx/ΩoǪiIx ) Rp;  { 粖1*I![bq%-+{1T.rO'8xVN /ENIuod ߂q¼VYCUd oLͨr,BIWrwfyyVb@,LJ?d1* ;񯏃>8T7D2W2*RsK&\Ypkz@io}b ?iժڃ1,QLUN$44?F}2YO [+ۤrFp}!SE be#QbmK҇S(}|"CqLjH+`?%@<3OJ!zdy'uem$4ŢF &쾎8dE$F0 vAs,V ^xv>:|撯?zW U N*xp;o^$+/.j_H-:c? OUK-6"OIy忄Dxpxr]@w ^ NbS7Їz+1б,īY-Ԣ4S{a~|K)@,} Ox d6ɲ+ͨ`֮ ȄC)؂1=+/6oy/561hԳYT\\ܑLc{T)!8]iGS§9)3υ2yU2ڮ+7a>'=s3↤94\-Zyb%TB , ˁvieDp)V;ЛvBrw zwM|F ~K-·DԱiSOuS"E͉ۮFDzlpoTW@#9ܧf6XƢ& ZJ`;ڣl=*z Gz$_QjB%1텠l|(U3<>c`Rz~ IotnuO@p*ȄHolBMBEǶ0 Ov탅(Gd+r, ugci0o}k[ mXW]J-`"`ut<00B)[m{_ר[{ԂFbJ>P(sGWVw+MóU=6$"Y$A*X{a@Kiȿ .< @S"8bŀX[ Ay)2|w'N;-cOe~%+ Ir6ܚ:JNf/,Sk8,|$tHWQ.@m>O c^̹3sA zug Y;t鋷x4TdO3*]\Yc0i~|yGcsu\0loR5[hk];}[]>,|IMTN8 :ʈеi)rFMso{|DKYMJ SLhNg+rnMncCGQGY+Earl/!3zuu.8`uoyΕL!:~wZTľ)'M!'O'<(ړ9jˣU8 bo[|A9ӽD -qz/jxrw OVB5&Ƿ,GW0e/Cw)V=*\/i婆G@Zv|SLjjKQCuϥ]^p,F7|_ ggy%P8#m7AV`Fr|t Ÿ?'x7g:5 N9 =dYLӼc^n:}1`K_xŝ9.us ye9WM$́>bwҾ8hẹ!(y6"$;\NF[ZU}y @kyVQ,,jBQ`gJj<ʹ}@MKE7okK7ëyiqr״o";'66AG ?QAn$SÙ&"E;~]~Nm{lӜmq&d)D6Խ|tz*(]bmtTpJSqgy"9* 2##CoC*vɇ;x ~NXnr?.L2 YIa:P-f~EQ34uc:p6$tƞR'Ŋ--O4̎ TC.L@+{[N`\$#{/avI‚\Dw{țVf[$zO}lA kl : էdi=GMśA-(/rNf=pi`v- O)F `=L—5E*i0`ŚJao o--cc=ox)sa3VY~0#6ҶkQHd*7Ӹ#jyIiɐͰ9ɍ daJIR;6njU,s发S|s +_^ۧv xJ@NI6E<@2r~ub%bZij\gtP!:ůtٛC`%I Ҟ29KBuQp!`kBLI,酚`*Vp?L3y`I+apF!$F?ۄe| Qz툼)Uқ`Wұ@;QUݘ3SO'&ӿI?yًo3# tn[FURZpw ?6@As\LGE Th pG@4F0Ԅ:m]ܰGpɩd 1키3J]"%`u>_+Ԡifc҂X_#{-p-sim:Um#=M4M6Ck. H#+YZNd5PI-$JzA#OKzu Uukӽcȵȉ3$_2*0}ƁWag#a{ )h*,I6o؏yh읲4@ڛ:P H-lIf&,@8M^h|M-/{~aR2 3GRIuS}k%snS"i+1cxHCMBϨ_2ZsrGlx( AN)q;0U#).0I>b=L.):5{`bE}Zt&l [. #Q0;jIk۵*!%|:߽&`he0^Xlj$Ҏ{p' \~cl.&S45ob:[@BWclK&ФL1H3M@a#31Γpp=LPԏ_c\5@ǩwr=ykR0ᝯݙm|G3'@OٌruH!HC+|6icoJ1ܶL-U=a6FtA ܚ}`p*2g\4IVT.k@A]&;Itmě] qʊ24ko`&@̀\I_-}rc+J0SE1d-D@k[:?3v *_57!a*Jl#aӷ)x'țo1X޲}~kADJEîpMJY⌕57q=@J= ʒ,fk7:w}zؘTzFwJa11PTڶ6ǹ VP$19c^hx5㕯̛(ϫ!%2r"e;{3gl# t/4A­jEVQQej%w*EWAIJ7sn}\hc`+wÊ9KqMe fymT/,aJW*8a%FŒ5k?\ot?z`xryj ~"Sפr^iCCXDJĞ=Qcdl tbyO8Xwga5B6Q7Q".D2 `W7AKG=DO( -C̄[UF-h`Rptut-mZN_krCB ?b*BGz*]TU"JJQj=G}aS$-u n`Wʯ$T<5LUDCw^o$/.S$OEP I'nӶ8'֪:j8婭ET5~w: @"n|JdGT9HT&~\m"].-e0iஓϡua7fb@.Ncj xQ Gi2CQilj6H#tu+I' q4IQ⿅h x~v1$ſ@=v7=/&6Ap4jYvS[!(НU"ĭ#GUnѬz~XKYA`Z||^]\z'j^Hlm,z T.̤ٯ|15c` !Ԟbcp:VqdAd٦9;e+Mj+QXjz$Q0r[dF֖ n=eI*yХ{XH*\<.9%Y6- !~f{#FF}%ȲOK2!G c#1ku(3@u~_9Yb"":1_%Xa ۾$K,aD@8pn>߾B|ݤi\e@F"}- [\`wVXZZuR"ȓ!RH ĹpϦ,, BdE]5oOp[9-fS~"wF7RVc`$nސ?'+Cw@aRJ Q  Q,?_婎Hbcn/ǡHPcf^s (+ʬ*w4UXI},"rD7_,'ntWRWV?\ܨX)MH< *[X^&Q,aAxfȲ":v[%B%)>GkL@U{]\1Hk&m0%,c'o *c# `kbG pKLDPTpU24obâDG6{aQx:F2E6H沶G F * t0 $dhKG,p$Hpm9Byiѥظs=)HҒFa&(M&PFS_8lZ"c&AP5k&A̲$Ƚ-_W-faY4$j]6]XcyutOvr#1045Tlw2ĖR~תeؘG{WM 0k*X, @1;-he$[p/{{c_]GG$CMa}5!,2uLD:{uP ڒ88㓵2<+9w:vߊ)341J޶ҡLc:+JQݗ߁'#cxp-(k3i%(dI G %4e?~RT<{qYX啵9/ &[O[4a%e ]1_|/.hK\_US,9u9,5nDz-jb)Zᦂc8kŷ~6ۇ8g|#q.O^ij"d c،|QʼnEuҼWtŬ .\r3|?qG37Y)h E#KI̳/|)7)"TdQIGvșY! j#{[L 44 IMRL뙱E#'EGK"yYG#g5 ft: AUjRWW|uA %UE P2;}%`O 晪U>]ST_/=@;Ńe٧6əRet% fad27){8#{F˰/nj]ylyܺ)-³Fne'9d/)9SZ"}+e(8cөDF}cRSdˉsx(xV嵾hQhqb}dmצ:Js3!o:۰خ+iPԼ \m~xx\s?}O=O qU esG2-j]CN,kHij MnPUQ'9H"Eo*!}?폠+dBds}3G.8e jĒMq607q*Kewߡƴٞ{~gV%LXMG0~ʫ6`ݥ>`U6oקl|-$hҽ΄,z? RVo1{Fz{!?)ف݃;u LaƪDdl$J$`@]2%v0#7 7 S}bhAh<#bJ4Ͱ0A>ܱnL D}E?Ed Yo7L.r6\٭JRitSb `!H/a~n\SʅFmZ[ ! TiF効RM>lȭ'aԄTǦdBȨM}apH[7L$UFSc%t8k;,"ɘb0K3nXtÆDҪ@Mͺ!mLqF6~E"R`ykӥsh̍-}Q\2k1bI(!uP׵vCUK+5o8cYēivV>/X+2X*|7F;H *Ycr>O-{߾"{Tr\B&?ަ 7$[WTS"3QM=o&;* 4G \L6:[ӴQQoo$YbبZ6a$;(P krh)bQ]2aLҦu.Q.vƒ*Wsq}MmML $Dul#%>b%AWU2ϩA>Æt+ *en|`wq{&z4Yj ۶9Us*<1!:O,6U~hݿlik/y M 174"y)A*7ٯb=:G+>ɟÎ֙a2N#1ggy!L<ۂiXc*^?Ft_l .m#y;V1e#$=GfP}L:znޢI$ıawɄJm㺑jDtdz[豇ħ2<7ҏ <^͹ZN ebT=$6mcLܵnV/ViZ斦ʩĎ[]P3li&QJ*#5$0.F`7{bpnyÜ+~vKLq|H"FI):@wƄK ~;<9rKxӕ\>N!$g&9,T$4鸏#*6Y$*Y=@Hpuw_!KWs|Lˊ3Xq YNaR$u؛Xc,p󛞾+|O|=qewrӇ`XL<1ȳT8Dn& +&m46{My?183ڣx (I&*OdپA<Du_槬6W(:!gθ[h7_X!CЇ)'^zb?.(KE *ZYrQKй#Ŏ(A"HrapG> yaqYʲ~`SV3:T=#foMx^1_\>#hi4cÓc?s0y+6"BhU-}Ⱦ<>ygǦ?+6Vxf0)2F .> <94؎P ~L{4'?uKg[@ z{y7C?s\.Jq%.p#Pnq•F .Ȇ OCs Û 2y&_Y O様Vs`a}'{_xLo KȨۍvyg45(åewQChu/-q;ug|;nx|:1Ys!qVf;[;eW)ynxW x90p((@mk=g\_7yq2O=V[,uWl2SYkƭn'3򧡕ReS )`W=&Eq۲Bӣ3$fơl vHD+Xn7#|CqOı'Uw#&POlGekb4vۦ<usFSm s}J,020f(v 6'кJ]܌@Kӿ<*J:gO(ى,;F6')JV!B#+lTʓop Ώ.ڝƛ-y$qyn~ۜC9G$}%o*F`ą! -"t8JH`O鉙͊ӛʋӈb!TG8de6tc)[l0']LU\)X \#bI°"bTi.OkfBl/G-`ٺTYv Ėt1#70$SF}p)2PZ?c 6m1<e:wS;ݙ_oO 䮫YZ͸?\0ySX7)T y}A#\rUɥA?3cߎUKVWr VO/܌uZ让SoL'Y`YrZC,Gc I~u7rޣҚ{{_WNMyuTxVֵ{&aF&F|Y4T|*֩kP\ o/AQrU {8p*Q#/w, fKAG.\&1ÈrD $~뇋UJ,Sjbm[|km6`ԍ7., *ɱk (1D@ ` <%dsIJӳB86RSWr`-q|Ю\2j:y#`N7ؘ<2&ǥbCň lDƨ6'Q {!#(H!tܱ&lEo8S'k6:73"B{0=K6]Ḑ~CсaBZ?-KF`mM@6c>~L$6%F8HUSm:6V# 'Z"b| `%*kS]GwG`MͰt NԞ827!ۡ`!X $/oWRMr1 j]M6 }l  `^.HD*U!ccFQr/ԁƖ{bX\+0Jzj:srYM[{oey9Wӱ)0mDo,qb+}{'XF(vB`F=@:Dk`t#py2zȩLXB0kOq}k;>( md{ή y8[#EYY#{ntlVW:1oyo4M6h%ˣ7@7=qp\onq6:7na6 v;ƠYfU5p#Q:>EFV=waE(PdukO W<|ycYoYSJiT3T/Bƒ|qǛ|EPKUS Qfi;< ZW^'b>-)RT16x*v45dce9R&plEer)eaI"V,\Z)/42]7}cYT 7`}?4!hrv=&%+{qE7nC^ F)͘dRfk`ZtϬFAe8,5w K63Q=RjDhcBIs l֒PFVHIBLZ|ETdU֍oY,l `ѩAc8$(wPw-ܢmЫ$Eظaum e"=[OF2%'nk]nb1 AmBm *)ǒPp4l~㶤jpNZF$֤lC\R]S_<)mV{YeYw#I,OA~r>#ɸ,8{2β|34.#R hQN&ɸfx2\ϊiͫNbG3ѧ>C^^y,ȫb{:vd67@de%(V)ӧo=BK{`QJ]8 Hlu6R ]<* `P4wTu :}q]R۫62GRAm[QgVahk#61l8%33c|o!zc*={<@uf+pAFP(dYTm~뇒X[K[r.n~=8╔nTS'is@Ӈ h @HSleI:[;8 }$`'gUG,iuJlyNڑLHhHe@T Xnzfpk[@ BEƒŃM8Wm@%0jzJ)&1R_N[\7r^✓;0<^_ZJ1G40 `~­5!dWuX\iu:Acr07T2Y^ᐕ=Z$ A|W:0%&HP; EKiBcSSeP}{`đ`, =D\kGOv}G[RKTE\ZXƞQ;\ ]MQEjQ99|sYj7fQms>MGN)-dPsQsh+ih%19"s_"JY*4lkHЅm~9<1gg͖W$xڐB^ZۂJsG4JZL$3e$;lu<-yED_žM>mPRH̯ꞝ-qOEC*1.^r.?]ѝ<5x;?we9h?P]}et/MKNZ;1o'G10x<"X},R){}qfq428&pS1;{QIU㏃]^2^̩V[F+JE]3/+]3DMWP5 x v-p^Z#Y~2⌳E(]cNۅT=]%HX8;DRN:|>*ɘ& i-ss۠-'* _a_oA?utNYU#7+ƺXnvIOhxY֋zU5&b*$Eca{ TttֹaeXƟ,]r~-ȸoWW3H$HWF$(`@a0tGDi I=AڏϾ'ie o*A,;玄:&wWROT29J ЃQseTIH\%!fU:o, ia~|U G38ڊj*jUW Tr6:9Mjم.tGy=KZݶat,Fn^?oKPJpY;j}_#ԕ*-o>q_qBve6WSf# MUjC4%|ePѝ=mQo1Hob1H6D!$28( -きɷP-\Te%Z緾M 3R@$ -L,RP,wMUE$MO2뱹ǯR.QX+iXaWDHu$G 4${۾+Ǫ͒HHr#S~-8Z &X i$WI~ "řPywb8!KSx< g4F2Oa 5 (x|Ic%!;)"@ x8C)f6I_4bHPK-Rʯ6`:vcY$)0*yU,z10)%i.}b 1,]҇up_SFIam†E{'Jh/$dF<#av4_Ss81#je2 :b{oatFi!@54I* L7C+ٔ6zm d;T=#vhz~;׀ h;LQ H[L56 ^ #7ERcpA !.3.w:a{,R(/ )f:ؘ<%#^O%b5y }Lo l]ʦ~FJSBTT)=#vf ZE%\/baF+* 6q0lU,T6HhPv UUHk"69,'U_1~ńK&T7oR@ GN b=Z!(^X+RTEu$Pq3ð#X; `3hZK"r ۯϚ Y -qT+Qu=:\I8lChe-/~Z|0?.IM$$$(ֱݎ1yǙjEU&Xb*YJw!q]tTsTZXY[[n|9GF`xf ;={y!O q^9:%2wZ.Yjx- AO,?1He؏+n~ɩ/J /)iЅ7'Nֹqg!x4gyQ*E"TdlApqw Gd4(U !|-`ς,2*Z4OT+Njf\i)/ &."+ 1FA"?l5uW +c'۱zX:q/^cҞ.}{B=$mLu< S`'Pjn]) #O&8/2WWX\fa&&Q U7So玠ß~rfДrȞImR<'oe8Bl 2IHW_e9QNDM󄑮MǮ[7lK D%c\1ysq-WGGi $>,6\\zGQ',ZtèHա;w.']|\qO:e7+@ 7m{ "q׀/<"V? fO5~|;~&22/ I=luoyL#` nݱ%LJL,'=a)z.nh; /LJ>7ÉgBhRWUc$1ƺ6_g5_kK|BySL qRXFATÓrs8*KʱDhts4FLv'e3W!ǚaf]^+@oiW*˗3d&aE !~iIP._9/8Z&MQ!) FxϏ#s_-agu1E=v-ΜTx*j㘍S!-hX޶E2F` }!˪+(BGOYXjŠ5H+l$ut,%,|űe*z{mVɈyAT8_+ ἜӾ_K(SʭP3ipN7;|fh]-eG=7\.h_RFEďe66=?!6)4t1Z9 Wt@+A$.$.}S^7I?"^/AxJOk2ML;]Js~p j$:,2,I@P*7~uhʬVĐl;'irJ9J* qd\t6`D֖exX&=؝lJg)1d y{ۦ e$#-,ml0uP&K` ښq(Ům@U;O6f75]K Xs<$cwH3;T ^npO(#'b=Bu=oChH4]u$n(^Y"%UUn];b"ֱi|,c0DOS$h+( 'Jd]~X#e#Pe]$:3=TXXb lۭMcX0ffD :s\ip d _.)I#hw]Z [ pXh㴒 u*o ؽlEɅ<ƶ Ԭ6뀢dzR&ڎ"Fyvv 6y DRcQ;v#vwf96 `%Qn>] A@:}o),t>!t[u`JֈDK#2olr'cGG|㌰ZDIW1 n8ܲEa̐J;8~aQ.@9Z@fRPq9]"2D%Tk㥔$`u#k}'#XRZN#VkY,M+-9_>KKgp (y$Ómpqfſ~1ʸ,ZLfRf",escVeYer>kIKWNd{<!tdcbY w!&Zx?;xs̹ jTu'9L?'qB3MƍyYnRIt-km$qߡx q_^as.*_Uee(z墢SA:VѪՊw5zִ2~(.OpgM'SW"41&R,뽶~ws_\mˊIW82.\@UXmqlbO e-y}Ǜ7FiQ" M[ c?)b!i9,J}ki( MŁZROx OyÜ}Sy4KE DNu(q%q>8|䟉n,~!<QKPK5nYT1kᾟu},ۦ)۹`[ ukp)+eb)Zwjs9yY7\KpopNpek:WjsO"BnnG\fdž~PR aSS ReWɪIHbI'v{t1eت30[]5 ԬR w2.؂\:cgQw2rV=cebWLI*8eŷ[^ Y]0V m|Q }QY ,}HwMMߦ(8fW9ELf3\aQ O_e]$PhXlC3iK)#I$b^)ȸ踓Z ,i#,(4..;oZ9#r0M0Qae$.EDYA]٬UR`*1XwX'\Klo <&$dHB7X0{1.g|GQQ)Bs9ώcgY/3Jh-`H$MGN]n6[O̿|y 'N|ՠ ׏{.RxUah <.l z@4$f{ [#CŹETTyWWя~WrSps3xr 'tݍwƂU-sGf?1TSMkD#ʙ%G&S _ <-pUU|q/xhW.vEp1c$tuu#u*gHq/fIxP橤Dfo O3ʷa_QI.<.(hP5.}g $Ső"P(*mtΒvk\j-G*g8JLRNFoR 6rKt|y0Z{ߚܰw0A',cmQ%Yt^]' Tugefv뇁NtR1JKwW{LdWS#htvU#UB{ n6)Ӳ벀F_lz8m.\XPb`X[Èn&<,ba[e%Ԃ\XN 7%ԋB %C'Wt9`rs u8,t%*e!%(uqeadA;{_ -vX$%l[I~f5f$3n5Tm+`BT<€†q =" a+b q"λr|@`TTXц;MޖO눇$5ܙ<'tv&1. N KmA $.Qnpp&7.4w5*e[V5rUfX%{~݀R$OOѵ6FGTNWplz@#tpU-f҈ZX 2bIb7(,X(r d?3EnMFۄ4Y0*U ŏƇ|qК`L 55(#_X>=z|&$G 辝_饚^')TbP{r-֚ϱeGI輣⪊:܄ϐK]GpeTJ.TXo:ptɨdf$XH08vlێn9M{MM=4kga7c'@LSU?ä,]i",5mUEƤ#b>鍔@?! ]dܢBHލ0,fč+4/qj$ָ :[aS׶.o3"ϯlmԆYYb"`o"P!Tg#O@}X< -Ʋo{_jIFn#ibo͏ {68,R"u0$+@7ÉMBӵJh$t: \[bI5 llm,c"),.^9vWT˙MRU撨g]*u|X:m|`o y1xAUY@偰M#u:Dꌋ41d\e)dTCHc܎GL^w!enc gwʜ\t5,K]:t^ 6B 1kƻ!0%l3mvLՃNGntF,ȊkbF##y7|Lә{4rsGݎ 췉"qZ_Y7ĻFP:H$v1v0"?7ʍ K'^5|GJ+ Hu3U,_{l_AII=et"<6>F ݘl8z_xrkFl Cq$8)Z2Ȫ4&xON⧚xm(_gǺjeheOMQQD%r t펭7HFI5\ʩ oGPIT59h|ڪ9Tn^=Vs59g49U0{c0IZa겊2߱szܟ|(sC(AGUaӯLIF: қ7ܖnKM8Y[Vq&ێr\uA%N]q*Ԍ N6:&۹6fȾBpG1%^+z11ȗ矈kqHcs tȷ EŇO IM$_ķx7 S/^Tiy/ck\XŽq㏔8Q3=6*pZee\zw;mipyZf5CΪN,فbarkۮ4/~-4}Ug9Xs,3/|O sV2Y:J*Xbl},L~-`tER d-aSX[>Þ+w51=6g5<MeժiJ@vq >'7#y\w= qRɛQ_zt7.Fr1e o󬛈rd1T(xI7on:10y=fy˪(^:p1ͷ}ŵ08>j5'(YTxbH&`^0I>LH$3'E~4QMdb&K+&9kB0ӆ3HMc]MxYgIWCQ%|@eTԈS( 5 ^bg~QUԹuTuPDijE_ms}nx>!%?)r8 ))?jijYt.^1{^û02͌7HVJ!mVd,,6m?A $k*OG99wœ Y .#Π Uh{w㓓ESWl88O.k,t3S Z'!=ok%~|5o>`2?O-:03> kQӍBF{\9u̮.r^73LrŞ hl{ч8_)@~kg3EK-b.[(߀?2* sac/gOF,|yՖpA6[Ioz^f ?ćPi )'%cn߮7Ӊ1B{TPՏוϿ 22򊞢yxFyY:\ _cwj*LN0\jmmF>i uDķvӆږHV}?eOFeMO/f\IZ7];JDQv:{cO&iY܆d+׊lR-[\=o84tH*iC#Ԉ my )=Iĕ c,QHD^񪛀 >Y Rܪ%Sv1 oX&@Қyw6߾"%@LJkaR]K e`5(^VMgch`$z*L#Ԁ. lE-HȀk  2HoaΕ7PXU1!I FfsC#~!*B&=ej{"On k`-3 Bu Zh! "p$tQX>]J9e@;~!ulY@:3Xɽ|=6$n y>|m&cao|2[%lTlp(IwcpN܏{}HPv\Mp zL36[q-I[ #B,6^0س"YlLVhkM4T$CN6!e1LXW{ V7{YYlB&Uo0%Xv߮Z# ʨ7 9O1 \"w7* ȊMLVeا B@>aZdiVM2X݇~BHɡT<`sacP&,#so߮8]}I12ؐ<8G4Lze͚jzkiɛb-;J>Kx1nnO]G*x6XnAaӶ dӺh[MD}4TK1:ȿ*PK57)(驩T=4q:;uU%iF ˨v?9xFVR B 嵁þ R.oPxG)%jXȆڷ X#uFU?LT[=6HV&(I{ĀгC滨 "܋ZA)2J:ClƣȒh (m>8"T|¾7`%D #|݈ߦ / ؟{[~28/'^8^19)^. Xg`|J\z28"̲lb>bQ4hvPA܀{q*E0'Yiu1tgIplȟ. |ؓ$6*_7Oi1fn/1X'"Fa(g(U]a0Vb\1Fcڥ RFAoH-St+Yk> $ &QUF%WEDyJmUEM}<ܗ1Nnn.72 vewz)Jt:O:K1&b e {.nFϹ0G%^ pwHx_zt4o,Ьv@Q|usҬI EeAj*Z:vplx?8Hd|!:or[3o2z(I)(W-b?8 :e=6]uqRF;`l6o ̳\;ACHx"r+3عv4LVRĺnv&x˺1F)l圻^\At.ia~V,X#s%=W[)؈؍?en6| pe?ynq]T S]>X:BƄf#Xq%%II%uȀv #yUF÷Ҫ!UAE\I 9yGVk( ;b`9V`?̑VSsψ>p6ggŹu-ofΦe{A,vy2K5na^ȥeiY܁ж rlu=B:ݒo ῜ڭ8CkđDI.Kó 3@BsKYpnǀ~ADpqucmVM=l,! {7=$T h葮ߧ6kn0A7ȇjD~^DI Xl/GVJw6ަDh'2!}/TM3BI }펐3;嬍"kѡ>kT`{nw\f*?ynjϘGG(wr3# Q $C2.ZEV.G|5"XLwl;cg?3J,2Vr5{Nڄ#BӵJ 0 KN%6x/^2yٜ%o|E4aD3Y}+  Ni|uCôtռ"ӰTS͒5ͱ'7K 2\tŔc$/E9U]4%,jPb{#@6<ǚ&bRcb+Ȗ } O4*긱6AC#Ҩ}k!?<Ӭ/GmRu51Ty$ áVónl# [jvc4HT$nw}It]j=8`>8}J ӽ{b4=KiVDb\ٵ tEGpɖ46!,b:R %9l&LM emeƒ5xI1̤ 1#Q5,ʌ][ɚm,>4N2[rCG4+]T&3v$#0K'!,tFq Kʐ=>l Ń }@NOE A@akt$2*_ ʲ- 6w+vFfK2S;m!/=mwTb-rnl?3yEb{ "rx(TIA[k`04^J6kC}Lc,R7089)<6WEf]wleђ|; `^@ۃR9#-" OB\th.B톂u?-˲I٬-n喖F/3}oTb{o$qV)!i?nѵ1]"kadԛ]"KU"3; $qȱXJ7"'β!h13jPƠ$4ߏDžZpԢgQR,tjdW:ҷ&õ8mt]I qooVP8^6bp7xOPOTU~dt 2` F,w,Ԧ'8=m:D*]~Vu\-aJو>r?4pl֣\jn`s$H,եhPר %#MqOǓT)ʪZcN&ᒁoA(DȤؒA$GGp7b48]VDBgb!ר-f,U-&pW c_Pg ga]O5Ud546i%Hcg*#tsa)HoI8jqmQJ接ѵTi,Vkl/Iib~}|pdJx6i/K}f=6LnSoG>49n. 36WIJFY\$B S2nٮk[:W84[U+Ӂ~.uurIi}%fi49[e)h͎ɸ8#2L|7c MCcs̟^O0n5&e2 C>) {0;p=9ϖd/{'58iG֮UMun$9eM]|E|h\g˪D|] KI`ޠ>1G( s% XA>~~ KxQ!$~yr/#Q|ifٿ^Sdt9=N_O=? Wy@b n־s+b24I'e <ll\h 6Se2`ok7504ʕ(=Q@KrßA!4k# *Guy7~gat${~p编2+MKWD:b];+M#s!Nn$Ho sC=\aROISp VfUT9mRe4Fk?Wh?f<,Y,6_f/GLeyCu =OKt'_<-3&_Vf1V8r?I;FmPNʶ6$j|_ˊLYcK.H*$|[aB;(Uucc-|>&983/>C•Je J*`#yFu`Ϻqclh[51Aroj8]'Cbn[c,9}^oώ6_.YS (uq2 /`nwSeynwLNj)+xK:8 ۥ csɶe8֛|XHpG~$. "xVM݈;|0KZ-|A TMI290;[l`s_C6KCoJAY^DG+GQZ[$ :w]s?Q_6idYYe X9^QVr FzETy$tg#\د3N?$l*ۦ$ן6s gP \`<@rBnp'a[+G O7cR. [8hw(yV4Q'%,2TT:`AG %ת=}D/ďc s׃l ,4,R4K=G뿧o3sz~ ,\ AT-x*t-7:gN)o ws^Ia= x/)+)kehZ?bq\ML Wˈ$*p]S1!+h䩝OՍ)vW챱%U~}LŸg;[VU58\O>,j)UR[OUv:* ^z#2@B4B>dVŗrTؑ< [=FԺ5v0,iY 8ƃ(/m@m|" Lr.SL 596;`G HN,S\H$-]F9Hf`]PAzk‹E,cq.f'9)̟ (AKask0fduXؗ2*ʾSl \X2m(o!Y?o1leoR.TInOqs8Bu>]E^+ I ߜd/AT(fCV߾c'e:Brq4 %`,g1'DX] =] A lRuCQLUbEb$$wÊ.!3#QhR,p0nXSC*Էf S놦A\,v8b3x9 ;l)dyCL+qb{OHPBu_B˺.,=6v%ϡo߿,;1nnQ,BؒH(ʱb cl3f2z1/,/X)2m(p-L Xn yq-.a,*2\m{*ww?A|sG1>6MEBqz)=xلe(Í+ǹ%lY(SKBi22Fq뎅Thh)T1Ae]1_%ǖA$HJX)$;PGq"T]u{m  ed"qWa!3EM#_6?5:ŸB]l[ ""$`'l>rBc]Em=`zHNVH!)p?Qdpw#u#3:(mi5^*?YS+I<}3‹zw8S ,H?k E$ ,a1 GFȀ*WP&N"3͏a#SPrN-$"eVE1ײ}aD,R԰~5KR#u^^4fBEͮǃ'5#r-lLlNJvo[Gr8VJAbm&̷{B/Aħ>i220yMP׃._T q˺4ӏw$N퍧85̾YqG,:^$UVy?7ca\Wy=<85I{zlZco5;+Ihs><Tnjn}?NA3if3)=aVXYeV8C3/<_mrgF25YBL](PwvI>*M"CQR->d`[|s͎x!ےUY,x\~"$0P[JM%sk=++{Z\ceNGʼ'Qp7ePOZ%ƒJTG%AcP{i3O-q.OPs>|sÜPIC=iSG%HLƝQf^æ5ϋvi|W- f_¼?U'ZPBʐ,w7,ݻnssĎYpftY}mg,6H@?S [+O` #OW.v''|f[R~37h ;yurGLfUteK?/cs/^)>*ʳLbkE.2o #/"|D|?Լ5e<*21 Hn-6x;/?6rY9%e^W6W.V&+5K))_X:d q$|cxmG* ow&)Jz3VߌJeT-s?%+_5T?UQ-XcE2&q˓^ BqM_p-T S~*Ji Z$2cakM+>UFmcIKqee\ nMO4W'P?O@ⰷ̊Es4L)zjzXai"cb|[c~-$x'*e\x8>cUfs,W6\QgydRśg#9H#SpXkK8|28ۇ#<R yu_j%VE/]v1rUpبd:vP2FֽۏEѿ/\wO,ޢf2Y,m9s䱙slƃ.YXEӗ'2Kpniļ3J,⚂*zl`Ȥr|d |YSr48%u9Ҿq+ ,#y ڗTbvYKssU9[\oSЊw5Nwbe M ۶8scQg·s-dQȇD Eŷ덦pCQɛg8i19\z ɰqm!kfq^iOei ټDŋI䢾61& ]ߟ*] Nl6e.2Idljޝ 0XIsqf+08k]MtG7W3d8V0$dN#_4>a6GfPd;#U#áFUroܛc)*|'%W09`Fe1$7Mܛ%3+{K3OZQO+*><<-󍧇:O9 +v6( ]X?rDx |"^.ʹWO%=O`k:܍C+d=AuҖ1zxE .n 0W+onrbrN( !1]Ma[o\ۃuRm }.:i#Y#2$t Q[~?a}pu(ܣ;(PD$o ]$o.:"r*< ,%B*iiU$mU_M*aR%ڟAr/xǼ%gSp]&HsH^Y%Khe6|G"57C&mykKe\ij+E"D\CK|+!c,43dS"S,S ō87_ \.[d<3. J?e4T K1tZ^R *XsL4J|_#`!;meins 9g R)>S(R=* 9e ˋ9gsU g2ȱY;(]d,;>_(Z8qA5nS@Y,S]dGR Z4R\66S=u9;|@qUK@!g#lYV9:w6"gq%,t<<>g܁ k;bx#e_ I䴼 ~ɩ䨊$lde/aa3Q^4 .&e]A-^ i!ѽ+ʾ$(KXɺ<q5'VC_ )OVV$ko|Rk<`zL5a +=!G{U:cc .O*98BJBO6dUfaKCOېϸ()5ʃj=niw3n88873wJi..3z& Hnnz/LlSWGx\JIZwECX2*y)ztyt;8ESv3†Gp('MEDe FPAas%ΰyC~Lu^Jr2VRN$Eu1BR\1J>㫯g,7D*j'2, ֝\(KO#SL yɆu/R@a{v1šW@24L#'B Fnw6:~{Nœ&6` NA͐Xp~)饩)PFߵ||u9gjksUǽD[tn:qwպbܩ,O Zeo <2Skh ew aFj6\Tb@<VXBy_*+@dpG1V#i zàUr G C)mZ:gh GȚHSŕDgɌko{:9{Kp82 KifM,il#A#k}8EӋ@m(Y^052߯lLVH>ߖ۝#rDUJGᝁ{m#0AVuVEG{Dܡ.SgϒVty@lpYQ EbƎtW醱"5@p F|1ՃLs|zK/Y4]᱓Tu>Yac`vl@XfePc] #U /`'>nUtu!Uyu?U`XःpED{\gfYM_+%>YG#it4u\|^s3iw/Hy!-n7p~K1eYL <[^I;O,wz:?'-fg:䙀oVXehU$S7Sw_ŭKN4ӳ)*ĥˢaOv5a0eL ]́d$Vn>a\i v=[n/|yRr#xI2K~!E, P `RiR豱7;"I&S~¶7&#~mTc[7Ǣ ŬL`,=cXlF! X@T ŔQ킊E=6fYT؛w|koXEZ+s\Z<&LҒYDҪR0QEȶxoksZ<Ϗ+_e#}#'K>ss;I 0*a#C@syEE@X?_7,g*sⷘ^"y_08545%Dj8/S*[;qNpw漫.wYRflVkhv c '= 鍇*˳.#_FPӃn"HqӠ|'Zօ\,7%w໙}|i6O/qVM]cd2SP` @=qFmZ#i]n\6Cɾ.\pvEME@sSDC3~m6ګZBS6M#+<]/.m[.r,SS ti(ClR Z8i݋K;]&F=km96VMEEΐ)Vᔂ߿1?*06}]VŵR@cQ.nBw뎺Tq%/3F/Oõ e-ZeVeׁR(onnnE͎:L1!]JD鎫ErD!)R(*[&& 7{"YGOùg&~p&\lPhF܋8ċXc8y(al0 UqEPXhE—+~=6Ną@Fvew˜G慖+fE ~\19o1mNXMQi:@V$=}¼9tKI~qMVcH cpOCGO* -T mcuSzbnvZC_.)n9㪊bS.mh#hXOf&>ORUN-&鍱@!a 8EkCf>`қ,%;M'xM6T9 Zy`ԫQWtS:!7;o|d pKy5$q邏,HaAk|*(iFy% l;Y5{+3Գy?4FA FQJ*ÐM zтm3%vaϑK;(y9 ׌rwe1O8*7Tn>v6ʛ5V:(iُkj;cmOf? 9Ǻ1 ҒR\3wS%#kTM q*m[TQpP3 v,OM@X/سդy:U]XޓƛV0yfW_ ΐ@\t780Ĥ} &8R*+]SFV~fq:A(2$Y׷AVpqeRW&cTs+IX5o{kbkG+Bx*h)(}\?E׬8I'dV(=)ܩ#9YQ+Y)߾{j5ȸ@'*ͳjRiVB71_n$4IAITkdg#A;){;`eg(ro m| 5ffo\0x`'D(XexsFoTTG`u^\Qٷhqqu5QkM v+qOg *6W[pU'/p7-ym5gT"Ûm**},t[eG.3L<\_9q#O>m=D 34UN䰵Ynj*ghu;)KGkb8u,7}HT.S"wzbDž,̙fe.I _߾(ZVmMeLԹϔ#U%$S"#Ku07r5_ȒRNtH.?[9Vc\UÙHL7 IbvOloc 8[)qSKCM7RQ5){_k }m̮,cꢏPG}>+ |/5_s;)RRj| IWbV^|tO)=:+2㚟0z>4o.Ϗ0Q3:<1@ܔ?[r>( HϒPs>M"qECpfNs)iDQщ#QsМb/3Y|+,&> %07$cMr([K716VЖ80s`U-Hlr)ƕ:mr=%o_OO&HRq0tG"T騥ඵZqrlq\͞pTy||T*,n`v"ⓘ\|/\n Loo2gdf_akm8g%znt)T._%c0DXĝH"Bf#tX4<]r|?HqVA*qM:.WɯΔL5zqOR{VY<8iW`UM׹:{x[/.GGͤ(5/$56ibmqͯ0~$]ř2-E-o!U+ªK7K {i 0%;T2#{s Uqd1_ .jYiSy{6G,SM 9/*u}c%yZKmb>ᗆ9_OM'lR53:Y;59䯄oywp[s_R)j3Y=3gS}-4{M^nT87?? n^sW%2l&L,'0L+b'~0Y#A1!c~b|UżDYq>oaY,Փ0s߾-u5$]~) <,%e3CF:`675\q-⿆—3_q'ri$X SqrdVϜК)W#kr3ogd`kü4ptuUW㤛T :,l@[VeVR~Arÿx}xgȹQpa92eMO>c:B%P?D5{7ėǐ)C×0?2>.ˢuDP$H}:M.Q,̢.2̸:3X$*+Mdًߊ!%e?/WfUuS3}Z{[fi%Veó&F|CITWqq`q7yC)1:̾52t2a ܡ9ku2œ+IAS+R>DwVYR5}o0yMwPMt. 8irZz(Җ5R$}*>lswg)GΜ 0q--3$|KICRC*uM%Ւp߭6Hx xL ZTh(αu\v{QSGEN$Ykc] S\i]Ya0GjľDoTI@) ^bƣSZɫZr?(S-^OWe5*92y(O~o8"G?ǗPqK.k95JS5;.L:f6=BۋĎ>lcд8wqsMbS 1EsWJCt:m-#2yYȮc18s7ᨳbN>[R3ypƨbk nN㹙Q~\ |]? )YϟT55J\:;{CÿKUEssMR24oİڂ.E յZS1+Mn3UcK3?-AJX5 JG6ߨYEbGSCMGM~i=aO "-/w>hjyfi,?륒n76de=f0pIUm$q2%RzX|w![[73LZ/^+35$2E JyCX4E#|`Aⓛx*3Z_e٭lr[/T~uڞ E%\Ͼ2kXfZJHтŊcغ}R @WL xPS5 U_.:ߙEpA"h\R)  S_)33Y96mdᥚAev>Į:y:(_xڭHX.Ǩkc`|7J3(6u DKuo|V<S+ie˸W,H`:@7 |h~e><++|IFX7c+uWyMW3( &[e[<?(}jHr-Yw"x6̪;4X`,P$;\^{bT.m{j>8h<|.ak^-O~!r.`z~#ΫdC29* thf:J@i cCMhbXⵕPmmo剗UL@2 6@̸75[WNшC8@9(e{`I-kIH"@_@1=[񯖂#qԎn5} .}Z cpN<.]$bJf>fM`*Yck,G1q'ditm7i61ۛ%y3Ȫh18c|c >g*%kGr}lWQ/ : LM 7Ak8K$N.W=J:Hw5iyri-{؜NٙYEi?K t&L".xE˒+aU)撶Yfߩoߍ;)  eWR o4lrzFA{Xl:ji3mUtY(Ӿ퉣ў_ˉYW^.u}821n"u\ͅ. l|+N=T_+)1ƖB,!$;[6VV9%LM=“퉎ɘ+4 Yl;+38B(;֯/F&:|ƩV|77ݼ){98 .Kt9cԳ-k}X?h惥Pn>"L#esāvPQC;{iZ"o"K2DQw^w ~N.oPĶ"2JX:B{`0I6\zPKX,vIIbUDF'`/oioQ;~h" 0ZŁ!H uo}8O.o3-#tRw0i F} Ӹ\:J#](F5 &)A{2XU -nE%Ҩi,E#2 Ą "I~ ǨªfHXLsM| n7/Cl HHs8'Ii`.-q1ip73&u\$>`ď{~fxL40A\߽oÔ5T9qSLZ771a vr)5&]vcEęe%.},)X"`VS|_\ֆ0Z zz%Rqc]|ȫ9-T㏆#( /!\i| -U#Ә@H߾'1||B<)L__/鈃DZ۩ `ŏsG-yi*c ˝u=U ZLl؟\rJ_>s_}(G*E\k]i#*|&](<"M ['~J!-#wũCsCX뀊NES(tEG+t?o|A V`7{w?< lیCl7F.MY# 9վ'$Q|/mnMQ-umLcñ\|R7sF5-s41  LyZ8@i~A%_}$YBU]n:=@m}`ёp`XFq P |-GV]QIMW ŏ?0_WïNc܇7il8 ˉErI|lAbGf]H>_ bW8;a쐵2S$n†SAL !d]8؍,Є,[bj6]Fɰ,׷u 4>`Vr]k{ꆲGhY}2u,v`cll끗F "2l2'8kPO2Hoo2IA0tGu]N'Y=&EZ—:@@~\A&mol U:ݯ̂HBDTUt7X#_, B3.&kl:1$^eP${*7PUTq1oKQf.k}:o-5'k텖tHݷ..5ݒ/,N6H=o3}؝W",'XlN~'^8@Z۞&0&B:b;l2VIU {I|'/ 0ܮ4/٤wsgY#,}Ma eq\$ٌnOy=kȴe٬|-QeYV`2*i4bmp~lβ58m_fsI'[ |g?"$Udyo+:efX#!(6c{Hr-(˩aU86S>PwRViL9WhD,ognPr(i8((s˜.guM)$Vt2"8"Z# "؝ w1PUt엞nӿA$x_Kx7xg.<˳)MI2L;)*?\qswUw5]G]S^Sy+Ȍ I>TK\A1FsH(#Q2322_..v࠘Ce.j=S1l9̊y,3*3@#'Q 줃埔*id\mקT`20z*X)f$;}qQQ{z1I!pSP;vk2N WB?$Fe mͺ"0 /+VK׸ݷu-SEu=qO<4nl%eVD optѤap͵ݮ?dp14z&q/G-EW$veEõ%Ua|${&b=QryKG컉 ܭg 8*t g\ɞirMFljq&ڤs̃4RzDq{ \ȣ̨+eȣd=mT[ufev & 8'ԩQ Xd*hUE$wIjQOx5cH0{eRϺo.Z؅LJp;"LkPV8RگKyz\wp?|6)@%0L^$u=(, ѱ*0I}H@!#) I9Y pP7/K28=sf?CuL`W?gZ⮟6.mDx*{UTIH# 1"ח 1˳`fdfjP@2Nup+/x)N=f#fJǕG!tF.K~% Z?VU췿*gSį1AqNd3g9iTODXANew |bu3浱WԆ =a~ĝ3j*6D]mq 5r\)'É!&/ىe"Tw"m,n1ulň)ׁ<͜x{.Y~2ɢOEL/N.lׅUp,{BvWNY VT䑭s{ a~ _ ȹu˲+H%JP*+]Gi^f-}c#`Yj>Ly_>Yc]~gKIߒ}"y%%KR"33WvxLJl8uyM%JˆBbt4qHL!%N}))r[Fmm0UHR$vsn͓\k_Q ^BqF_ RgA$5 * ncs`1fl/mX(9CSTSEO}MSN9U YdF*I&1+3]k˟ؽIX}V;ęwO‘_c}.BN4)z%V:y* rEzQUQh |Fn[){u=,0OaH2$v!I 5x&5(z_a@ !A]rZ8B-PnuE{8c !}9L[I*#MAn 7 ., j:D$l L I!WYd+AuVݍǾ<55ăppXR5DA#7Mbl5kw%29* !+R,#`7}@ɸe=q(>1ǿO#&o+>@ky@"}! p0 9TȠ\L+᤹qyѭ_e4>p?1$^gp>E܏DnCԾk=:~ Ǩs>K`HQEF$m!bFK]g0WsJFdo؍o)J\@<4^(LNGlWϛ0M+^Cc?uIW̆5@q`c6}A*-c8Ƿ#nWS+cɠlbn,Ox":)G dW:?9R'[+02hqnt 4mg2xd)>z~lP2[l}<=LNKE151ej" ?{|}s5ʳDr)I: ݈bH+19c>1%$Gq3J5C,aTV:V4!ZrZ:pU"Mrž-bx42T|K?(ݏLZ7iжU\PTyV+K79?1r1^8 )Kkc+-K̜Kňܧ$3O*OĐ>Z`q&aXiL3O*c[7=|O8^2RR$jVJBsºWq.[Q:fS^0qO:D<._H ?F(o!J8?ECNcO0PM?Wp]}UHdL9Չ~ ?3˙+GPZn; ꧩmýH=UWPة}WEE&3elFe0e?q=(=&z*3 iy*6gl,=υ4Y=_ePoan™gΔrVeB;(}}r96<*9|\k <3>m?.a׵ts}dC&H@QS#Casu8S47PR^-0vzʙ ZWM͍#oL=ϫjkbVetS9/߿G+>q4MPOTikP{/଺H)ꥌ5iʢ8i1Q==jGO]`^Ϝcdfs8P9]9"4,cb/pYVWRɢV U/:[&24iV1,ˣ( gFp T'esu=fcͰ "+,}P*̝܍͗4KɎ-⊊zXIS̛cAu֋9irQEU311( _:828oGR0BS,v'eJCZĖ. q>4+sنMUf< 4"OB^ف-M#mm$\Q,V(RnqoOSǢ"tz[hi(, fWoD\͚(iҽHzֲ6D:]%Ů1L?^vdSŖp-]h]%L1!j(2 ] m JgYYYF?Jڧ]]:l&1@q_.U[ (D ],z1\>sY-ZʫN,؂n ɕFѕ6�z-h#Hv$aW6$tZi{G:zy2$-!H|MNn,H|:'݋% op_·>D"Le S ӊ. Wji MGTWv,%2(M]1e5uYj6(#w 3Wn\fMMY|72LBP>[[a.Q=Dp L39߲rK(smqåޞ<^ z:Ʉ RGqCe`\='q9fKuVQP  oLɮ+'~5i(bYTP;Ai1%â-O]t9eCPԫX "Uw4m\7Q ;7Ju/_83%S y@̩է|wM674>B;7dJ`FPW#q aO*-ߦ0GRW̺yTKg"t`:Hp:z|H4>!_`-zi\ˍ?gm,gTE<#P=,5ϷCxOȏ_ξp /VLJaJL ~Dת饁-lw3 /s.o0nNpQy? K"Z6k؛qÓqrtͥi)3h.EkM,[; [ 2xZ/\9m•TqfchdY<$o} *Hٓp2Q7{GUMFjHCjR3]R6'^_iǜ!Ŝ799O_/d,]g<'p|]W|JE-r-Ua+ :=.QnۅZ9#WODLyA@XI>o׈cxȩ:⼾tU<*i)K mV۵ӈ4ygIhu{tŅ/_f8X4Ƚr1aMӮ(s0!FE.>k ᏋeC*WSVIHœZSKH_v:Q`> xLs 8?|/UW_%5kUGR[|x|7eU"w+S(YE1eϏW+r#4 zfv65@ l./INE&MU|s_ƕ3>K=iECTA$: }ON_8[H c$ {_{n765?iZw) QiX.ہc+sC0|19@(3Y;\7\ ~uFǷ$f?@Itp>Mp yCP$nYC(EUTv V"p%:2\k)\A>iMpYrTݾ$IrL5TeA=.I} GᷨHJvv`h+xQ 6XbsJ:f YYMGҁ.s;J-|pKeLnl/#,i54j|ĥ`cl_xi!uq= nDO$.< 7:̃-D٪*;-6aξ -Ai }6an1|<||s3\fQY621dIIJ|PĜ &i/M<–&l\GѾaN#LnEWA՞;=ES_("U*)4h"݈>!~36IxhfHCStw&&]]st/% UW9Qrmoo9s\s;aR_mct}[2<2*$nl}ߺ y=t*<U}jt+Ӹ;fysT5Qٵu,dS쒰k-dW*WaK;Ǣ=+URҦa:zj5}6ūx\A[ @31Y?uG9G-~i4G9;lU=}fa.C˵K@/P|*9dT|KKgReuEm!" ͍,{&i9CH%`KUY48հ9xmY 줌7;ԲM?5η3~(>6x=n EpZU^m1꿍u:xc,Ze:vBKfCa¹t0B,=:})8%"˲*8k,t fNcӄ4 SJꪚWǒ\Vi&=,ҒM=pn*t c|#nEx Rx>%X}I5\o1ٿèp;:0|*34-+H s 'sw6iytAJxjcyﵶgQno.Թ\df,wPHݰVO|kL-) w0Vrol+2:PT˚fL c"8{WaU1ârԩ@$mɿ\e@@KDEߧEWSsi?X,?uٷ YTSɞoKMð%T_p1Df~|/f?NYuC$PC\tmtATӈ,Hʪ,mtyr'0_Xh9k36O bW~w2.8{򼦑o*E5:/(m:Qq-KFzefe*Dy(e>@|c 4"赾QȷBَá@@RM DcJY+nj<\hȎ7y.v?o1 w%0*YN큠$,?sL jDQOc|&UA&gYdlKv4rS]#o&45 AUi6m7-op=`WRަX+*[F$ZҹK5*y8MMO)0w:ZH9@˩uv'1.Ue^wcjƂJg3F!Yo>RAOahԼnUyˎP4s.A&qTR$ޮEAՊE6o-|; JsQʂX ӧmkx^fo[K&f` EOk:)2%9uvаj%6ud: #o>>( }˼f.$U#{{4?̾]S95SE w۶;*)I)x,879^# g-dRa$6Ӥ[~<G^R8L,"ݣsq{c#[oDV]FCbz`CY褳ܮErXl˛;i"/!B=M|'^)8㟹gmQ';gk~Did؇I7bqc`6FyD٧y~HBVq' #J,rG0\ eKH*I di&@ =7;KCNyl_NkXl|х (ҴK\g+8k+sXY`--sx`3|:(ES'H,܀oE|@gONr,uS۶> yÏ*)Lͳ.tEra]\7*z5CR7کVV"[;j82Z;Fz f Alywah[TzC9^YGQOQ6N9ّ>$,RO"V&a$IK =$c@\-#t|II$|Ye0 Gf#Rz"G^K騢p/߀TeH/"*j'>zSQ<XwcztVbKzBocB^jsPIK#R#ݔ[{ߦ69}$@džj̷3w]g£L]. =rBoKeE?(N/Uju*7܃m I!FayM>_uT%M2B:\1'ckw7hf64^62N/L)L(h EJg˪H^?UVmP@w[~ l)%U$Xv&Q$h4:I=^ސ7TFU􋝾0پ3{걳GL9T-,w8ʄ b`LYZl{bL.) ?2PnF{amRl9h@1t}NtuEȉh*o?&,-(aXtM-߯K ,VduPۼrGߨďw'⌦+XΉԢ\Z0|vjG;v\=5MmLcEL{zv>gyI<)UꠐF\ivwIIl,vƫ9.7day\719QJD*.Yq Vx_oO8,GBq{Uj膶@-+v^`>L+eMNDܹAcN q|&hS@ݍ.O|B'f fPwX6ޠgnoBt[_H*wlk|{=|t3RÙ5ʌ+*.3_. 2˪1K5*MsmA?6VN⭫> jbߦ'rf6l m wE[I~RcFvX$Gbl~:96*R,GaV{[ le 9utw0YF" ]MD\nM_ $i`Ҽ &8Ȉ?kACOX>jQ-)W~`-cCOuIH@A1RKPe 7yob#I2B0X[~ݰ_5Ej!BD fč$z۝ɺt7UU5b:yXS_H 7)8- z@]sq55 , MQ!"\ğ~?IC1|Xa?0Q)H$`NV`YNF0;9\C t 4Xo)wJSIL59(--a`G&cmE5p~l bsb\vGS"*lG Xdx\G}(63xRf tqvd : z| ^IJ:אɸ- |pD[+\F8a8XDMwm,=㩗2 K(Yw[^i7>9tYCf &%{Zb,;']bofmq缳;PߨW%}"C( wv5Ԝ_TSGrVkܟsl7/:EGĹSyT#kj!wu킜`3(9eoPWxh!@:z{n)x['jcK#Bȥbs_)IԜC|OKc8uk4^qgGgW3ٖw=e"R87mpw|+FM0rJD&OP:Mse|yͮ`ǎQO_QRje!9 X*Mxpɓ8̖s /uj]Z"]Vu;`>'*rg3:Em MIU%ȷcqf!aut f"s6<70A,cF#%Wak ޔDb @sx5KacT:rJmcHfu6FKYhc !$1(/ |%[8V_+.Pљ{b&psr]xyT3ȷ[_]3 e˙ fGNJuOeCKY_y]PR%Whщ:QO9͙d7MVQSI<mo|uɝ|'UECM#Ei#RԴDb1|qEG̞ s ikI拹A WWܖkˬ7_NlfʩO(SB%**lj.u)_M[j.LPk pZeph Gcg4ZXdLRl=c?A T\?+/ *rA鏞6[>j?.<+F,fYo=6폠,-yzZd1ee0'v wNǪa.Z{[\xg,s ga<f3,z N?cx~'F~5axRL16amfB#wtP6-|z0Ƒ}P`A*<zc齆|'<"-rA:ACبQӎrήLSO,Si@ ߷ޗroĞ1dSQCRBܜktl#f\PDeEG:X"i琷RP’HT5GHo׾<3YXC1]Zzn7ǃ Ni$[QMP!Y=>ªZ~^UE"_ee2ʨ"vr!( ]bԤ估dqqs#oSGSH@傑n*\ :xoWZܓ(^ 9w۔YwpW W_GmSD*s $]Ga8ZhJItm,u#;Wjw(sS)7QW+еq[FB4MRbr o4 eAc(f*m=OM*HkR'$L޽=G{1bP:F."?l%BH$Xtp)oP$'6lJ+DtaB}ٕ^Uq4Zjc ("yM t/R\}zGXA#e_KYrR5.t1Nv w<ګkAZi];{}Mگ=|O=<'7d $#Lo,Wr#|_<č!XQ@m|y00luŮG72ȱ ^ʫcޤ3U!Ltupڈ[pHvv`d9vSCUCSKNW7[\yuMɤZ Iet`+dԔ3 a8}7=>^?*^#>#%S'2˽It "z [WVK9s-0( R^},EM{o$ud|7cgk;6V)禝ުH 1R{3Xv7Ĕ E$Nx,k펅&Zr:ez:'1E&H1]}xK"z*Y8'%a+M6gwi۠wۦGOhX游?K].4z_.Z̺l֮Q!R#'K(=o.㦬8rŒT- ,l" ŶX_IRBGnxHZ,tu|VW'yFC¹:O@om/+zp8LMv\p^eq5>ATԁ(} o}bd? cWU \hb3Xbw6UsltieYw$hM7ӮGI2G6F.EiۋNދ` rU(mChOas&uer:Jk}:63b/H!5Tk*6lH3)66s*YTPPvxe -b\X Ƅݯl;OPP W7k01$ѩP+(e.^"F@ #DE#{9"9.X7sm#S,zd۾#mISDQeB:m(QL"3zOY3[q魤HpU4$I <(C5ntGh]| :ݻl8]A'ʽX>A7cftQH{p#&0J)$o ZklYYY}RyaA_ u> YFtzl 5۽퍁IHJqCqOppgAI m{_1Y.;ABױG i9%3•Yv]Y4rN֩\=\_Q0o:`Hq8S7#_9x&X$qlgY!tB}9c5BAZ/79 5 j$/{[17~(<9ԙCYG u(&Kn ۩=1GQU4{,1Ϥm$Xc-x3&p/xc#4Qr8 V:;2@.s1m,|Ixs:pi0˫ ]<-ܑ}Pp—RQfy.chr?qFtU=fw=n`ʲ2^[j&ņ&-% @J١ ?G!\WfOºJ?fTDd(݁p7#῁omQ[m;(7^5S!R.L a!M>qFt ㉿vd HDorRHuPBo0WֽN׸&0w]ƨ`&ǵMޙ\+36Be PǪosN6. %ؐMܖo"d'vU@|@`5e^j )RCzJT2G4KO!"`^5e֥V:P,l4;m`ZU$}c`.^65QFe岞qX)sKgPGC <԰֎5Bk}Z rwC|Bm]H}-ntevcX1ē1^0b.#ı xI`ǵ#0AplK 2*} 5o+V 9gU*G`07ﵭ %$pf0K#;*RBIvt+\3 *ɬۥ`eZBar ,0_2ok[lSbH :E\.װml.Zzp[qm׮Js(Pt7Su ЀE{Z,dnI@Ьʊ5j]b&8@ ۾"s}%v EU욻gi\2 ʿnK)rɢK W Z=3U$oYH0j2LG=x7u+we5fUBxz V˽ @ 5 >8q4|I*hx* 9ؗ= -H)C}VP$aDB0+mHޢ@V kUF87]7'bJD4ž@"V$nm Uˎ'^)*8iJHv:EnSʼ2pFe WAK3q%+Oi.`A cks#2, TT|I4MVFđwvgI%me_}Q`x_L>Gw%+<ÕCO[JFt|If5|kM IeDS]aщnI A)0 hj Gdŝvb%|W=pY? %YCmkMrݖ0.#L,mkuۭg |VZo3RL}5ckƧHo9y S&k[v:(AܼŜsfbt4B6b}zzq:g-Аw0=͟9{ˌk "N$Z9U87wk(#2.Crx +YL8*XD@6"7쥉f\qm w{A<<<_.r]${$)<@yenw$2]-LT m>eR,{/fatPQ& 4~nPl;dUe,CIRZ nN(F?1ؤ, /턂d DG? ;8!@8~RI=U|ҾkMz$YXDd`"*`w `M`QX8ڲn5@r+uʭikn1MMTqcSASTSԒ.x>7OHy5Mr &zAPI'Brl1U|}'x+gluy1U\e"-s/{G$xnJe)h㣏))#}QN.7qRߔOdEOG5HH_X,Ms!"bt!&|oUbb&iG쬬܄UF?蜈Ju=I'rmo ,.id#R{#Z\[ߦq bTOě~dfA+?g& :h*ī߻ m͎〈3ʪkZY痤a?neϖ?ZwI;`lL9$?012{cWCS>SD2ʂ]BCvb{\f${)a -™"tzv<^>%?Uy]FϓB׵* }cf>U|5< SKYPkYVc#$t1КEl.1>X{oc` |;٘UkӉ#i3C/NG-M߯~˛%w5t@  Shى7"X(NFTy$ץX@0Gg-_ĭ] YfƎk3SjPovcvOlUYOsj) (lT|l\Qc\A,"5t$g.oAZ2}]U\Pzl}=9~=AZ=AZ['rE6 J-+`|~voTغj|%JjvUpVP?/#2;2SCb}($>bڽHWw:WL׌?UbΉ0oax;̒C:EmύL7Hzy·2M7) }-r>xJ",F5B-ㅣhY,͊4$ 5mc\줐ŽS*I걸lKfn,7-kUMaēVVBj,}}44Q,";J*j'mB TTadDhF[sMtoס#CǗSnwhfIo`U%mnnY~($zF|]lE]66%0/|ܫz*Xmbz6േG9j"AwS$/v?|NIFYUSNȣVĖ~ w*D {>ĦgLvTvtp[J/Zʼ Myo_?&-LQfU,OCa_J䣭hUd'qlm!l mʴ :1=oa:!@zK@lD֤),jxEDق$DAo_/:AaI yt4*C\7퍧ozɸ^M;3YO@/z t}J緜 YS j& [Q Α{{1 T]%EvGEEK3ES}]/Tv2{,+cUV[R-s1Uf[E+t_,_팻`BW_JZka+Qboʋ$H߶ >\h=mJFgcC8@ :omu*'@U屵~:P~ 8X8͇a-`2Z(rdfZ% ھ8iGIC~`]=VS0b1| 'H"lڈQ$#4בp/v|;hG]kFbKKcF봓6XR5lԭe=Im@ ~lk'!8+!MUZbWCnE' y/l\#[OG]T h#"9pGsSnCONeΫ)5S)K]W{c;+ g١$ep T˱޷[Z OQ9}]P!o%~SR~~l>{u AG{oiQpGeYwrj|)˔ѥ:$Um6Ͼ/!."I\r1Jz@W:isnH[ ap'ZӮQt?b3UTH;06.;]ϮL$:[hsG(H ũX&8m""2qf[.usC\jׄz# ob*aA#+ă>~!CJUHɵ@VQs*'k\{4D0W@o׶T`J1Yt0;#$l6GUʀ=?+A˧WVSb}كtQJ[*i[EyP*b0<1eI+=b)% + :v*5}L)&NADC+HYߘYA[sJU.YE4,%䭭R>)O/qGF2đ<_7l;x13Ym{B6b;Y .P띝5D<Lgp;0i2hҔSrA"{ tF4 ybVFh4D_SR)# @:`X% 8ʷD c_>9\Ȫh30+&;e#ok(%d5[e6~a#:\'u28 F1 7\2ghN6zxzE697*Y->o،vURfo"10-((?RoSVcTdLjٶ-7ƱG, %xg>68cZ67\ &aPUqKO$ +qTf]<faUMDZ>u_Df 9 G唢Olͬ:~qH}ElOÈT!e%P^ci٨eŦQԶϩՌ? 7 s:مM=M[E=U].7م;Fr?89oÙ51Һo*=Kqb6I!᧓VEYD>e@ZHK/CPCG HA틉_T/ebu!ho#H+CuR#1VLR!7o}񛼷wx_eUSFoK(3KeЫQY7VvxxT`ඛ_g/IįK`=8:p6XixQI|3*gaZoGtzta83#)/2GEoL3U oKM|([a] Yw v k>& L,bѓe] [no:RA?O qld%I=U>{ѕtK-ؚ._e{H@J ;>(l?C:{ ME `ֿନjVRA*}4i<쥕{tIN{)\u4\4ZOIiݿlLᢆ2,s8PJ(Mlm|]"!{;FEOF -D2I+vXrɥ7O%(21 W:[-IcU#;|ՕT=ͅ.F:B}\b[ v%D6i!.v( ^xZJj/X@x EȈ^'ˎ')mFm\St-4$Q: @Pom|zY pNOODr#=؏Htʡz:YL$FD.Qsw*]ViorlyO,81P-yb`:1{^'[ocQ"DQ6:n8KVv>j$m4h'AQe/$  E@;i2Z@ 6QD|ѱ؎Li,"pbU2)̢cpKfDe \{_"4:X _p-Bg"kJB E|< .:~< 2&2Nd i\YNN̫0ETR{bg \{_lX/HlF%|Ӧ%>a'5"#d{W* ÿcnH7 dzc)nY$@z]dH\ L4Qpx6ȇIjtk*,RP2"0X 1:,P0>^At`-&Yʺ>v6u:v/׮%/F$݀~6j`l_NhsD3 4c9:M`;HJm-@Kֶmmpoa DX(G$P Ep9 8`Tbn[k};d*5!}04]Lb`bBX@3}p+*n8#:21*HܷLJ-׿}d8z'FȠ@+c%mno3рw61ۡCH62Y큗l3*zUL%h5ZķP ]aB~Y[a < RŃz\ $ಷSӾ#+%!!n$tӦ$F{ d\v\7i G+9*Q}E_fYgpO,55U>qsޢ 4ujTWPEyenmTr)-uHpܮ:!y,p!}>F?܃i](y}I qu(;: ge,̌B sկs[âغ9 LR/BB  m?LXD`.5nwk0Bv"#:(n o."s<*<\Ԅz:w<Eq\qU$9dhnеׯNg\`Jx'xW:.lCO\`)+$@V{_>j=a犊5SlY7N>TmOkh'ehH%ԋv{ts򓙼4kV\QJG7hCtQ`wr4T.]luO,Ŝ(9[SE6\m}Ik~=dqNopŦ.Ll lW~mt,Btn`/]$$I-ȴhT1*#.!oLqes^Bk6?珊jL7RikLIQ]K ܐ=x#9x VDPolsǙe~o2j%W"$ocu(h̟VWBpo[?vs'*f*#+*9&؎TO(l1?u;]BG$%q%6L~j؍[O|L|S`YW|1-Bp)ls9O׉i8e҂vr[NcP /pAbXXp&m-w7EwI4^O>f_|CMԨب:N_ T7e!?BweI$n5t8eU4 ب(b[D~54: ._|`pst'X%@ \@Pdcy 7/j m6$@+,V ЩQ,tQ黆Xl/lMb!X7o=57dWOD:a,b 6^$ܵ{⬰kZ_-5gAԪmm"P&Ao1RY$ B|d]P9 !nGk|,N"]O=&'$iQݰ䲫!Tu=(W=[\ .Zd'p 4-;Uh hV}CBHvj@rI-~gtV.E4PE6=VT\L-3)p`(R-n ӖmΔݭkE,a d+pwWu ЩoRn rUBD0/Dol}sORm$SEOO.%eRS||8|E8!gֱ_{}q뎈?-TԱw.YKQL5ԙu<F?C>T V7kSQ-<+k5һ)$IZ:|x$ryEٴr]L]7Y1cjf?p4/U.-jfXe'N|ZNdv `i;_7$-T*A?Ceנ"Su0?.ʪ筤ސ-VEm8٘sAs4_1?!⩣i4))!o/NWgS8z ecr ؾ/|GOn&5N_ TuX̰ -͠/ƭ/*t߆N {}zsCV]kiBai(IT‰_QۗPQ sQfFG: H\']etY\ϡޒar7ং&,'\1QcRb[*os^O []v㧑SK~ej.WHSb8P7ͣ}L5[I Uyg)-& }Ƕ6՛dyRm-e%K% Y֏k\~ጋ-h KBb7m۾<'↤ Ya!1L7`iiyWZ5* ՉGSk5=TKU3DQ Fz~U$+,0hݮͿ1)rBmrHA)x^gSF=䐫T1tsLc$|;|6zW%-f֡ $ۧO|:MM;ܒ[[Z P!FZ7KƳYKOS)+9? c䒱acZׂH5&ulnp@C-ۺ4wZE6kl7#UWJ=@wj\^h?(o9S.}Ű.6AP P(RXtP鑶$ԾBNJ:@'Ao\>rl6An4HI(S渿o E$3&In&J2FUE&tS0̹(ë^[܃"%=;nTy@}: eI$jsX/"MD u#%L6 G{FQF "5$9؁(,3#GXC]݅j\7~M x֦DzLYEvTQbrb8^A"q"2b  =T90I&w"ie;\܏KQ 3WTg@W8ϙ*7-p/0-L4J6*vۯd6Bzn m}GKOd(u.$h΃|9jD/mC2F( T;d7EU"ePgrw70Jl,{5\$,Z5DdE#`PKdf11e ֲ{!1tE 'ԫ!0-xXv؛$w $ !:ٵ{ -4ӦPH#J- :>Kb.YϬ:]-?$%$.J"|6'cP7RN?!|GF{ UL L FT"Zl%,vMA&G}H_1Kb-r~ਨC7bBZ뀣 (4X|G")FQmX}T .0α˫rh.I~VJx v=`#UE0!uae+mpI-"yamHX 8jФ(UGb17BcTP$_ Ȉ|v&bbYkۿL7Y8H;7^|GX%/olB8K 9bx? z[Ipt#ceBqY"SR1jD,O mLm}_>_y$uy`7lA#IEcrCr.Q"I Ems9wIb/aqkoFZ XYk BAn;߾~ ӃIDCv6FR uH ȱEtdݓmwFG)e&ř^/ Վq_>%I ;qYdu|Eĕ C{7$ C/՞kaؒhHS{ zUX&{DZŸN;xh)d4ٕ*JzB﷽?Q# 2*H۾ģi5MK8Sx*ѭBw9gaoWg U:znwl"o)Yt-; 3IHAt#F Xf dJ&lۏl<`UK\}E~0H4;Qk[KIwXb0  `C};̀̾+t(pF,P|Bm*<*j`OÈ*jM $b@+\L. A B0[pA/SWT`A"it?B\33ȑj`xc)‹(W\KknӹR̷OJS?lB%<3 /& N.yi0P;?`iťBPgb6ً56Ğb؃m0 Mo ]qws"`os*ŀR6 1U]b/AQ2$ t#qFlz`, ׶ lOKNm@ᄅ..$l.&zy^?eWYǩ:0%HǸ80 |Yuߦ -(XDy_l knUth?P;`+F8/c]I=}M7Ԅ6bk0d ea0T"I< (v[BƇqMp$:6,HjPEN"d ؎G .@7|uYoHUݛM},l*Y($AM= )cfR]Xas~Y83ok8$I<::NVQXYk\bCZ6<$z,<$=B DanV;Q`Z|*x,lnNW{{dDp:1h +kOHRʻ6IS +3HKr@y0Ť2Ln[lR9Od}lK=U2/ *OPiM \,`DA*d܄ 1YP눲(76"kp id1=䅒҇'_ĶYmkgt!VJINS4aiϷG&D;w#XO<0Mo>?̉b5-벒=[[A q,EJlLL].;m{74Ī@T'e˷n,3Z9/Bl귾;(I !{;nNl.ChN,_/UdF;Hdi'qq#|~+Fݶ] ی|FqoĜ)jiO:Q(uv$ lpqL\/*WAU5r0[H&)P[7]i"9ذ]-UGEbE1>qqgC:,n1ʞ 2z Ү|U[͹g& 6 WH0Qn?wÚpuO2kͩ:4ɤ N8܊ IۧcQ_ ,i8+f)M]T33?j;tc퍺SH2.>g\_tчH$zk>#:EBNzY7;ύKf"blAN.A?|uvkW48ܬ;?ි~+G2gIW5/|_#0QrDr}O0 n]2gWqsTu iF 䓙w o %j;)G1'`qd`H{۟Y0T_S @Ml2`THw vӥ \8+dcAEfB0A=1vJԠUP7 %!vSZz]v aX" R:n@7^EVذ}FpBò+)bk̲ Pۭ\( Hكviq`F&kuQE uµ g26]nУ #^q-&es~q,EKĨoI`Ece #VZŶ -mMw1HceR4ݭݕn.F rWX1~"0]l%sqeFzol$H$%oeeԤ  (zp3k*ש#ږCi5c)pX'>B416}!P8 lNNer@Vkzu܎8%$鹹鄐UmYݬ>M w,}Iol0\ sd\xǩ;!*{ܖJ,H-s{î4%y]_l@Ȼ/˦NޓeRS"m b̈́ 'ՠI7|%^DmEܐj$K0Ek)s?L v7{ Hlpel-we z>Vdl~4nNlܟnm`)Fqo|5TH}Eܼ,7uB$݈oǢ#Sj$ݷc?TocBP)z5(Ki#nl^5`nT~wIJX3[BĨ;l#2]`anG|$"\l/\͏ Em%Ϋ_Z8aaun/+ cp[aW, F'`bbQRJ8bBcqHyR$ZgICwB,@27cRVBb,@3(ge:}+k!=8f,JNnpZ۩)m"?-fb8Qmj 04"^vUس {vX ,Y<6HK)Ȓ\~[t pn2vĢ%%2ǡ˨25$8e* [0@{,Q%uK7 ol0YHI g b;MQ/)mEl% ҈t3KShʬgY:IaR,I0ֱ4 B|8ىBGNa@)oUC Z7B/%+;_$X|?"d /YSkSãQV]oc]pFh4O=l5\ȺScqܜ |2$Y6̍8F@40&7X/4|3QgT3OXܵ9OԹ~ &`d$H=|}~ 9[>P$mu<1y)3%^0zZ6οis֩Z*%nbzRtDup -js c>oHgiVxn,\VO.T2kn鍃8 PZ\oc.G+QTf>0P! ԗCuY$|6"1<Ԇy'Tll-<~f֝rUI/Ln Qר49u!b}|z&Gs;.J?>hҚx T 5nmo}+IܑLߋXl-3;y+vV76~ޚ7'wͳlpGMSׅ. nRpNQGWfQ'Trty ŀQ`,9'P-r^C.C%9 ߶Q==|S!Xc8E%=mMlpN#2;:/}ƅzy]1Hj\y>&kcsKKeHSnғ\P)q~jd3(Buj{tøM% eʚXhĨ"I ޠ#:/߮"-곫5l3iʱj\@׶,csCCoe08 QeI4DcНRɢ>umD]cTs*@y퉂uvLqFI`qb5sG*HQ +nL!Ru?0#{-`ha!\l-g:q2 =$#ںx(1#̐mv.0Wo P<P7p*O  !IN"ʁ8UF0d,Qzo%H01b[ G#U]@kĈ@]NV 3U#$:Z]pWew$-+Fā-mo#c $(޷dNKlbܡ! L#$`]vaFF=(+ok}?h.6LE#ML$W?(,}R5%*%+Jd]|LPa0A*=lko}]+QmJ}[jNؐ.zz]7^()o.83Z (D"@g]r"0r3ԣ.)f 6z[`kopt~JLQ>Q|Y vi/k`AO-!8JLr2IQY ~+r'!As7kIX{o ]4va`j@b#Hcf`7}d1u'uly^=݃nX0ݘ̄Zt‹b:ek>M2*}Uscs jЮdiհ|ԽolZeĈJq YRXŪ :_G15}nnIQX!u$Xm<0#<$.Tag BO,L`@mm`e$y Pܑ.H!8Iq l3kF TH,mf6a؆HRmcoȡi"D97oDDEPa谉K+mrz HBci <:LE^tY$S֮}mR>] B3.6ᜃ1r\6sY<.ϛ=m#~JgOE:>׋5ctSxF9ܒ z"ĊY]>G,lB0d. eK*]C Fe)b}c v!Un-ifSH3[o釸-1$zء1zidXVR클F' Z6:P noۦ8" 6w϶K] s:nIݬBw>ؒq?䍜qnsd4XDJ^ہ~4xKp/*3[PL@ʺ##P?ua+9㫸pQ@|}t_s~>K5y<ɩ;Z4* |k^q-svJ:iSO`6߹$T/Nq?jY)c$\j6/wsb<|\#eRjYXDW^-nn#Qqk$1{8/D9k+Ws_P"bV.ce%C-r09PznaeK=7b C,*8()E i5ƁMVf=ӼWrEƇ l팽$U]!RH'`8lM+D´ww>re)g*cfQFC0ߦ v(ȯ*#31pc{+"FʌMM|$*h{/mcodV s}6al / !Ö`WA:XcG擥@}a(ErcԶ+ *0.X,mlyeTuck{/HJlGIZ zȖHR2ke}r }7{=)ZE &C cl{BI"eRov]v6X$Ld@QОz@ʹ wbf_;X kXgVAP7W_s X+@aJ\ua؆ge}6Fy#\5ϾXE]?,k mciՠi`va`~̺LJ[E6DݵR$`:b,x? g 4S\8MF5*z_ $jBCZ tl+)N^q.z#{F%mMQUfӭ5_ aD`1Bz¡xͿ6 & 㙿.|fJ)Ť~!M'FV:Ct rpH@d3ݶbۓQ1ؕ:\~;O lR68ch6Zu.[K<1,Nu'킭@@іAv=ľ a{l#dH) B4 ױս :K`vo|8(Q"0EQDhd ǷL&d*=E>U,MG[T1ҊH냬 $ 6BoHJ\0*w`XtǴj}8E,@nAL󭨀 {F!2c U SSZtJ@We6Ǿȭ}2z$~Lifw#b"M @@H'\@Nʍ((Hqbbpe/{#c#,z @&Tk/NgɑWX'H:a$bk<OHw85q`lTyY@,{F1:^Y\GYf`w83t i=/.@ Foc`5ۥ ݝo+Q TϨL4ʩJzX)fw(Us}E5-;Tyb&:;:M+WUGI~QnqUKT BEEC hHq,KzcLk؂=ַԞfA+aʒi"$ֺ ])x:1&lӊae@I. ia ù_WI'gI`V4T$KJGE+ jWS8﨔c ~!_G~\b=~*&%_P#qɠK:EmDp Т;%$( EDΝ üqHC!\{Α9V[cӑ4QM,5\}b A! Z}([RF5ҡm[>w/z5 'ELH 0[~2D ܵ%Dj:ٛ}L.2ٕ}w^\Ki]#`oFʄ !b:,0fI¯Dm=I$7ƻ<q4YfeEQXtXk@Rz ゥeJ"$) MH<%'c q32i7sc#P~ ())OQnx/SHO|f3Y̢˼Zrr&`IJM;)S >+啑ME .c;~.s )ejFk5 {1\=ᬣ7mpVe<. sls~Q+]Y`mG*e^Nqen} "ʮF }c>}.BNaL|υ KM>T)7PI F$cna噔2E_TRK*C|yJwI9on76-_7ܻ/;rnjES$Ł -©*=\#AZhbʞRIm7 wDN᜽tdye2vUU}`eY? OYv22!qub3MJூ.*&k@Jsʨd$j .8"L0,0)TmMdmm1Si",t\gU5X-F٦s]@\>\2^1K&0̡l\tv؅/QRaӴ1 U;oK  P,lê:m{ D;Sʰ+3,7rȼ ZHcab_͇t9U\RfsUPg vӸ>_T%&ep3SL+h0e Qﰾ>w0gx_1r~"L*mAP%xV* ;c] %l-qq3XBlƉB+VGOZ|A8g1gre*{ݽ8CDd TKm~/Zf449?Wt$CFbolmWa9ZkwJNvjM ؍*<.]u} BZʑn?1c (Qqz%mwQȼꚙ eCHa|sQ%T1qCTf5"a!'}FhOTwuǘ:f6go+\T5@J;ӔW-pz07#BIA)8c2t dMeeF%ll]5dyz Mͷ~f.47xb íJAu?L yܯ][]bx1@x${蓃~G)e2VZWMTzw2σ9|gEd9&fIN#`C.<5957pIzr[S\n:Q)+_(3 vnJ+#@:=Lݝ#L-GEKҰݗ-ۅJ2Ҫ4aDB|ou;јonH=v;p / =yH%_Q"FuiF5#}MLn28ƀ Cq"TkYXX%X}9UUǖč=QF%ilIµ)Poya-f@V`Xy}+XS֠MN%ZO^fy=M?@Y} Ԥ*{aMَv0eAe X1bM`QmHˤ Q*{[z!"Hui_`XH &lbȤ@5ANvvFe\u5~K0 lw?|me!?n넫5wI Y>pe{rC|!WEl0k _R~ EF3i$\߸|,Xn.t@!"ױ`122]cpOl/ k5b>Ӥh!t4H &  ÛEHѪ纍Gh@,cPc`Gj"Dsùj\ʥ|€O%.WEhWM=$+r]A#p'EpΦ0>E;"3Iuջ~xyɈ)rG̾l94 a³Q?j] [@D+8ef#]P˹O`3`t?@ N4ȅ,$^1ҋYb! ?"GL T1;lPuaR EU7hy+%<͞i] SO~u=')(*au[%@6鏡/ľ.q|**h!P/P@U&o]%ԕQE0*3tF~'C#A)kع2=352h*#Ŵ76 \%N:ytʩy :ώӎdž+j22ՕRH˘Eͧpsn띴%)Alqpڀku7|y: |R iFn0c =W\ft?5"WLT1'NGN#&.vX,;vT x753-?fNZBȬۦ6h5` hg~%6cTHYQq`e ށ߶1%YM4aLbk oldwl柎9BqUSSIRA+ lXД3U0Jy?(y`D.%[WI%4-wg;C -db"\5~# -Dʒ1uL;yk'yeF̾!IQBBA8 jk)4 2N{mp?'jjEopad5tվ\E ߦOXn%uuw(Dk۰) ܪbh)-:$b6gjDlI˭$菚*Ueʽq" Ҥi:D$Uɡn,ُM"~H"w2Xo=zcʤF #j)p+S jIYc#'ok%<,z#Ywnt3ӡ,'O 0'uB?- oD8c=eU[Q{0᫝c(YE~R,btJ|˱DsQASI0v1 "tRĵ5 U&w F7 KHû\OЍ1P;̥~[Z_TGIn {}xV']7o~$2,#HR>{ 4hN`=\:rdF, 0Ȯ- -`߹ihjtg [nvF"hUS[Q%r_cbb}m++-k!f5jU*XU5H^֌NْX.O7Q7XrHHXIPHtK}#bKF{ta2(OVE;aO1u2(]—X4S!D?Pe] b"뉪ht _0 Eu kv8@cAeq$$gsnFFj6lfopЊ*mK$2+H(( juA%Ɍ)/遭Z4>[ӈJmprIW5,{oAeJIVURvo 5rT+2BdX?S:b}F\29NŠJ ֧ppa#,X.>+#QK$(,XZ.whٚwQ䚊f4$˨a|.Bȱ@ 16-aQr\MOHcQNI$^+H]WmRmh!PVk\9n0)! MoۨK]8$2qP[a˾]V̀,sS}RX؅'skp]dХuq2A7h:(|L!БŬ:&c@@k4ooS*Ӽ+eC`)w<3 KH z1`eU % ,0o0LΠ*?XSG[,l:;;ģL(To`o4q8+3OsP~uDo"N K"1a4QmyHKǮURʨl˾l4V H@ W'Ol;J]\Ho|2i )7ۡ| a";* S۶L\]#`zL"$Y2PUK8L7YQdXu]M*CDuA>߶4k Lo `{y Lȸ`{`H Kƒ؀;[/auHDbKY($]NlsN"2c큠}%^K(Ä+ErY\BnJ*E0d$E}->b40X{\zwI p@ G;=jpPM$d0?(:0HBCXam`؋éTESRD sHF3K 2HB6/_]M0Yk8,0iHߙHh$bf7-ZS',VKԷ!B@'k੠J8:m3Xxfڜ:4 h-r}XDZ,o\=HnDu?1톆|ՏƇuEc9Hoi$Z[h!B[r `TeDRnaQU6-m q7 dcy"`lal{ A /6{9;3g*ue?$jn$ 1!h+A?fE$[bH߾+܋ H#04ebmo|x(*lpQ$IQdՑj;(/m[n\s'P1̑۽7^8("̍e$?9(bsFE7 5ň6/]dM&MOWIh"0 ,:}17V)8p,|5tfl$57T8O='[z盺ǘU[-Kh{ .6Szb1Tl#cv)޻_%Қ; I',.D\0azf~~؋F𲲋g IyEi+7B=a+kU}){}2CGpw0H$/¡'%A"u@${RX'RF*/Q-Eu7oGC>5d#Fa,~cFtrmb,Yn GkwHUdBJu$Di zoBn jqk>ݰ ,bwL!.#Q]2D[`蘱-b=IPM#2؂/p@;^b'ԤGnHZ?'M0`Jf Jl:$Z"w)%/ ܭ)s\Qb 0:o;XȅQ !>[/;pYAetGC{*H~+P34 ~8m9 //$ډNsNaAzB 2MjpJ]8RH K+UF-u=}Y}Z[LDSJŏOxf  dx(ĂTU%"56Ժwk8_ !H:ǙPByñ'E! bcK #̌5#l,R؛16][|KS)h|n7HNs`=1Y;kAplz .0&ԡ|o{,\kY N¬PdUJq*W5鲰:5-`u;X[$+-dfbu6 ǩ9pv;~LJ1q+O- ٙu1kmn!7N,Eݕ|&G"g;.a>V{} T䍸OqbU$fk`j f`|2 T6d |OƓk,ܣI{` ( 90٘MZv÷@.7O)ddَR!5ȍ"uyp!LDlL7i#v{0(KLBA }e%YQ#,wfS?4TrHۖng\o.RXɅn7iB`R٭`("$T!Ro* K ;- 4l0yW LM dVVo.J,. r>jC(x7

?1X-v;rȧ:8 7T?gu0pijyOY8%7rl Ħ<Ū-7[w/mue:9y$4$Ơ[w>uIUc =JO%9X `qIV9АK1}<|LMQۗuP2ZAQY#$IboTԴR& Cj)u 4+"$t 24k4`V{lmU#GkF *X䕤Yܬ};'pW.8NzʊW2秈hnAl ܂Ms($$vڔH6Q}>~+yYfAO+5rQ 6~}F\,wV eItKj۸dBgV }JkhA4*zH@J}qjz-ݖ$\_.A*UBȦ{$1 ȥ'gc#yy|=Z%Vޫ_|ra|(<ȨE#;UM[o_쳔\E\g΃J-*vmq~lt%d ǎCHӄan>O g030qZk:+Zj$ Rf&va>|gWxꆫ$x#-MS#zt& |VsS1xii?T4Ur ?(2/%JjhȊ9wJ-muM{n)|UdZS㇞6{-ۿyuˌ2:L'):X!(.ϕVԂ~1T#!^.XǼp `T,k@N3bXk*d/{&ajk):+"aDIbW}v@$C'+*\bV~1V*T's,+$c h(TҤḕtDl7i{#/#)đbH ]P~$i#w$ ?<+i+ubl6Da$(8K8-ql.!!Kz/q Noc\Zʅ jFYYtÃAb=,=F() b%X( 6 lI,MXctP4; ٙQY78XcX(UAobOK{~xU:}dXl46,j߱*)<{)}$ 82 ;˲lZʶ`(^RBm_QD@$,^6_l%bSگ`~F-U탃'o˓elT),K7 SA-MIy%&뭆=і Υr<Ϲ ޝaֱ4/ e-Յ_WՃB&4*$ ^1hr^%` :FLde +լS!u*r1ne/$ޓ>}*)28)ȾGv@.=îh@zoQHXCŮϒu#u;~b!e+dN 9t:AȱF\}p04&W% TLzUnq,YM݉}D E(t2X~ؗu ơ>M_M3:Dd-Ol"2QZ44_ULknQܸ9ߧl%18`j6F7;PFeD'T  M!up#fpRn΢ܖV&;؄ulFPd0H2B$,2SV`^WDWX|#+ cu.X{룪oD][f7;1%XalV_'lƚMH%OL'v8TZ1I%KLyE ",.]L̋'`ja}ar/:cɳ&sF$ "D-^ůsJѾ}E2z6̅Bi$Q!X.j za֊w|3Sj3@󫷔ʬv#Ğja3;DB!H$ {.p7e^󗋜I%U9xgċ_l: }9P/p5S5d3eTm ϋÛjРoA`o{Bi8{#Β'2yaF%@۹y-&~U+^#Hqm@l5{u8-dRV̒Z% QAЀXTB},XƋ)B C.(7%uvǛPу5KcOì G [*R?A&ϳ2e)& z~lsn!DUb PQ\c"q1Tr$ V@b4 캦;R„{Hp-)ٝ=6ؘSUԇ2Ii`)1톹|<:v$3XFkvl r "-CyL41g$,ml6, BMq 72 d]>u?\Iv(` 'SOQ Q8E{̨Z-nGO*94˨2¶&oRZ)=7[etXȖDr/m#{`WZH+]%O PF&/B=:6_Ck=pyVM QS~[Te Z._LbEԶCYECCQ<33'sAUgGr-!7)ɐ1I^8S)J M)DIY 0*ã#BM o}7;*J$`aYՒ%EQM+ؖ==K>!H}=ߦۜLUbB.k1BCPcf(c})]'s&rRؔU%Li!b@7i/Hұ`ԌYSBue]gGQI7]ÖH#bBYAۮuFXiG ,Bӄ[DCqŐCHlf%Ԉt! «@oe'{ b4lK~MHr,|PYaK?fPw&Pa4$r@y?$B6+e1 8KJzQKWkuF L1 ?N!dWP/L\$UE m5;I#졽(-ߡ@֧b }6 gX"bBo|@J]GmLb֋%}bFfW m-qk}!T4'E_hFRdNVۃf o!\19)$ډ}10)q FFuWfl9rY4c{a= Qx]I$V$P'"f Fo=G=:_-u*lvU,F+蝡`ՃWM؍~jFѭ~ZyIYKҊo%ܡ?[v-3 pw YB!,* 7lboTE1zk۶ Lϥ5H.ȇwF+)onaK(DX%,'/~M+PEdM(x2Uf|21!bR9uj,FXYbKU]@]$mT+!M+ǽ)T }Rtpml"+$`.Ϻ!yyC pE&cU5UIC`zzq$?L %ftq|uUH T߷FȲXtUȶI;}7=1sKs|==0t^Zh#"YrUJ:.U8HѨ(T:Pukj s߮6kM F,Gl?׵Rƫʻ]=FNX>K4[O+u+X hENȪ5n킑V.*6fdXI[ (iXR &l\:H-͚ێ =Sek^vb j۹.i$`߾3kZPx놲y -M1Wd`$&` gK ' Q^Immn:0)EP#6V7o,HVy^;kd*ҥ4#ˑ~mJ̱,lAǖv,@ mdt۶)=< +ߠ?-@Um?fԮl۽S5FE#Poo׮vj*qO,d-STL ,YwTbv6I#HG=Idb9,=5Z_~{lWV $0tya++-۰[&JiH)La)|.Q \A/cAm&SHRt#ĨA^kg6>oęe_3mޓ}0w$K}/wcź P'TwVD { ($bȁ7@!H*r.Ly +HGk\UYb6B\;-%;3dYLG|2!:i*:`~`P:퇵l>Eh% ;#7];|@ΐJRchDZ ;9=*4d۔P5*᳆tUB֌w-SD @8vZGTwXӨ#[ s b9FD%_pe I-oTdTٛ :лR)>Gآ=PHpt _ Hv[-dV׽oabDo1iCa^poXhL$mۦq3ro×u6q*਎R C C+E 7/0A;s^2 P1m5ȡ}4G=B!N5eݠ VU\RdgT<5.Z)ڦD;3LkQscoY^YTv_I -2 " l>`44PerHEf,k|Mث3Ŷec,p* XhW?T{T?7NWov{^E݊6{EUצ7gFB#7$nO_0+!"pbPP8$JT`]O|Ŕ"N0d, VDpNQң,S*R)u'ol;)dH@CJw`  lA="A ILdF/mV+U@  MdhjU*|j n.ϺfOb%fCEſp `mO#1 ss0]Hk0} ܐ3;SsvG#;FHλ6,KS\A:wx0YwX\ q/6VQR zn;3܍WeYSi*Fu^ `l#gԀYV,h R{Kɛ$ǥtUFѪORn \ơ/p|y!$VuUf6à  !u@dx}1$hŜ,HXB<`.warT Ƒ\' (۔l`rQZczl VMVWg^GRalΉSI1y*ȡz\:6wUhHgcZoK`%,PYm}GvDd&A -mbiiJدoJ+\A~,n..Mȥ^ks%/ BB:I ȋ#h7'oZ@A5^w)-3GT0Gvpd|3aEJ+LkPb뇸MgV{V8 2b6۾&mC"$5KY,l0^"I(q[(V܎W$sOQ$e=ici˙.UZvT#_ fpyE4hsRVf\9f5W@=162ICEHۤA6i3UÝQ|4aC`QˬURU%F;\.ZxJS:rTӳ~*)[{_>mOD zGHD[7 K"1|~0d1fA,yJʛݶ|Z1zAaVOd;ۮ>D"e4g#׾$cE`7_=~bx̾].$zUzCۭEb>]U7-xDMĐ*kxR,B2 o pEq|ȳXߘ_0IQJLis nI=1<4+8rUaj lv](t.F5EI}J1]e͓~LJW'&i)3UڥRIr>h }}8'i3+N+J  !10 B hqmp5u7;{U%Im(ZO#򶪡e**8ۧ^F8➣U VЉj[Z+@e}N"QB٭8%s,>pA+TXmi v[}Xۏl$gSƲ=ITt gp@ق;Ok\9zJh*Edpu[ ,nz{fUK%NOC 7[{1^~N!ϦFu3I6u~y xuhp?k(8׈Vnu_z^i!u/~ {9ҨiS.imq*t'vy[{y@$lX_Rv`-ű0rNW;0d%}Y8r4dp- G m|jwŏ0x\D܎4IFʋY} wp5<_-6 +!_N6(c!ms-4sg38(笋:%zX@e2煜湶UGRGQv1SA6Drњgem:̞K_Dd Er'.xbn \;.~b{wޏ㹏2HqN:7z]oaHf Cu{8o(L+܇/28)FA`umQ%>|A(u#;lE\dMZ-uϚgj{;uU@Wf7K+qkmlD Hݾŀ$؋ @VIB]B+s)#J"z:YX`m.z`:@ELxɶF;b-;"!=?#=PI`TN$@*.6k\Ќ)! $##"obiiqPBP7Qp $DNᄀr=fgP`w*Ml k1o؋%A&ȭJt W2ea t1+kabmN{}Ë+GA J%0nǹ=bfd}!HScr|(pl6 ;a,Hl[mŽ"۔4KFG p {[a{}ŇgE+ >%Q`7Ed]$Q FR/'{5 kʑ+p`n.Y_RAOl:)I}'#yO0S_]\ ŵl4u0Rݭ~5>{r¦kup_si}e8FGBd.nÌ`V!íGd${vct;\j'qS`ֽYb2G+ZAto$.zE[=ԋ_7*Ƒ@\t?l7GW*$~jwB}"vL] 07;"EB(YPHfGSYDA!X`l^qm Ut) nnFV6{N(Ml*[0bdPk#z@i5RKߧl8 ;ĩ#9uW n_|6,[42kRdvu$: RygV}h L#JY=v]/p.GFby_I`UIh2S_VD<,ҷ((x67?קR**VXEve5ejjJ[ ڶr}A3i~Ol X(@Yad -PJx`LNO[-b>s\O-[^Er#/xŇm1>NӃ?P'diowD/ќ-AJEi˴i R$%C1𿋎G%,.7Ii#CռQF4j|[/+4-rID즙L:Ze{nZs>zM&eAC[x* I!٘b]d"7⌲SGס:/Y3!sR@ĠIT%il7؁*,ڞ1ΫZ(z,r\{ **în@e'DnVǴXnRT+͘L8J)3O1A&5R̪7@}8w((FC3inwcSxX'))s ʼMEeXS3V~d~WI/oc]vOS?+ \BC)qm/ S+:Ȏ2C[Ź{f=W(9):lNG߯M'7̅f7#H5TVSd9.Q+ 4e;~Kd fdbyXGxhzz"xψAL"sv?+X`Ee@yUTUqSNqU"{ to-?28-_O/f\U9d,-$bB"@8{φ<7,2~1j/SYyWRp%eF9[wr ˞CN/3m~K;)s / Ŧ-Cz` $w A'5}-z~%4Et]JOO|;yABG?-CC,Վc v8ܕ1r+aȑ2Mkk~oF`}bIAAcX>Xmo닁Nn/˩)M\*he,oM+TRoe_LTcX:NL,tPg>dP̨oPMSK"`!lWH"Lx ZIIA7Ӊ)/7;cgCȆ[F=%XIt"0T#I]H+`_èď1j{ʳ.MnuRȩ!Ac&89 D$h Y6b/Q^gp.-@B&ui^or>Ʊl.plSm=$FI.UӆfNĒx|'ZdU[ ^N1;~#%d\L8+~ 0A*nQ<= ye,PcTZ`XlXߩs|~"F-4dʬ5Twovx8B N3$QI(ɬ,E< (.X_|xQrtE{_%kE+B6H5Zq5/Yeؓ`Ƕg,1Q6[ ZDUWdRFC{a* 저kcE:z@hʑAR匮,,-l) Nv&Ōd-B'"ͭ)`.?[_;0y e0FJ1v tuiΤXb;qI0o1VP1+.r{L~J|.ѥmc\^W`ʄ0*UzF h6[|:",W1r%A*of2Ktis853GJio \IK~a`>`/#…Śn0fEKiSr,ҐDw4BY g[ b$HYAVA Cǥԇ Ӿ j^d # c d43fQp@= CG .I(b1k$JdZ!Gl%K eXTznbf+y^"YԢSrSm1[ݷ+~VhPImo…{HGIoC=_L-솉Ob'mB…8<Pql eIk[_Y3Ȅkmn8ˁ} !`DȍwoA?xTBY[k[\Dv@H%fVJaqntߠ10{^ nsj i48^Q[{[©bd0e+vkµ+pŗb>4lodti>TcP*_o F5Qmw JHȥ'^)G^1"\cuQ<, ^"3F 0H禂&2 Zr>^ 톑bT.Pq.KO?Kzγ0Jͯ7,-016iy%` -۾HO.җdqSXFX2 `68Szcd]lA+ƬC @AKȐE!  ӱ:H]]ZQ}T_)08[m1ا4إV9VV`l6?} X8 68ʿw8";"kʋ@TEM+s$he]I_QQ, Di -+|6% P'Qb-',Xzo*HLKm.L:J1lTHC`Q-D 4ظ\,7{}pU$V{_[\,܂V'U,W{q: =/.oaIcHKZUAZS#I+{YEխnl7jd!P[Jߨ8D,NtCѥVAm 2{@VY"7ck^|xT$Pbv;aEC $e6.Q*Xfֶ6#lFt2ڨSyZz!u"},pM$A΂N..? $Ar"jRin )w ][TʠcRBkne³eR;}1/Xgfa [O j,+mal2Hl9@}\NuՑDd 'y60Kؖ=iL7b~<5,Kxe+ΰ ]P?ApTܐv 3ep#3#邥}8b,{$-{aUny)p*ȶy# 4si jA(?\SUeS4ٞFSXI6N^O ،Qy37IUM) 2ظ]sK^ *x)?.Ys5 ~?ԑEWpR0f>qw]ɧ6ʺXImYaԻ9 ʬ2<yNiIPa9VcM/ej|+df.(`Zx|nX7Gs7gEHR˛0$ 6}Hm.ˣHfIϽϧ|zy}y_YRTW`A\W||E?&dA#z&I[A٬ߟ0`vy?%Ømt!]Weg؜**<Z)F9cƕ~F[ ,abe)eTWPŗ4$Nlۏ~h<$ j2? ?qCXW=*gPX[1񷏯xg/I R*Y?7@E"MMWy(AV]W#4}Kn=<]3ut-J8Dlژ\A*UpZik*B/rkㄬ/Zi8ØfyL:KSab7L7+jkZ5PΤ6*b/~(𯉇bk'oWsºgBd$(zqp du 9o;lՒռeH̨ʴgi&'a\}xTttrO, mͯ'SXAP;x-Ĺ@™P7B? =(5T T+}}8A^$~qTQY@frv{kʸv\tn)u/ֈkVӾtb3~3 ?]S:ȚZ0qn_.eU2R7LjyOKļ͈R̆c{Caث{˟*G2yS:h2AT|wGihu Izըġy=MZ/;9&*$Ss#>pRdܩ+24TAl/c[n%@nf?Ln~u_[C<ȩVLsC{TĀv= YThe1LXr;=cF\)}2ҫ\;O^qEg.mtj?̚*q՘eoq1QT8he,FN7{;u\5x'S9aYj?:o4iiST PȐ2"5*G&LPv.DxO;69Y+2V*^ڣ22 I_͘ ufeeb^K#ܳt'C  QorRY:g@XF;N}aFWCjg k[bk&^8[2k Rą$dv )VF[?1n +wn *)(@Xmp=Qn9>NٵvT c7AQ5G= tc`:#rGԺ6v_ 1vEaUw wvNlá_S loo]"  !X pILyQMz625+ 7nXi )e;ϖIm$y4Z̅`05kIc[:P&Eyfk:l0`éS4م7>wbwlG#) U@- 5Q1s*Jf/`TmWFX3kVKK6V{caJLLmV,#:Xfi&j|X:$w)uZV*~{aQUm(c 6`lVI)+{8I>jV2@Wf04=[5euf/_>dNć=pX#1k(K,Qn A\D;D@bp L"d!efؕǼjkOc 'r C|):IA) 鰹K#(:J\A0FWbGo {O JrTFX4au×KȆڝj6Sztaɏ*:ڝZ[oyJ+ @)#ʹ /MDpXG801sbp%=t5#`N}vU 2XUI,%Qg 8u^דIq*q)[cmvjEu}lCx:Jpq,m(l;0>־)eSMVè=byQ MmrB_|7"9I1fy5*V=#!6PqIkb%XrF He&ޛ'WYu chM/Vwxz 683N#,Y[U+./7@v|5=bi7;u6=1+ 6 ) ăk||9i(V-䖆R~%-N7;]H 1( Ed^$;]UBmGW4R6=mO d|\ SDTOT,QtkĝL\8`E%8L {4j{}uNghIh@/U}em8զItāGOm:~N[|. jx _@j]uoU˖[[Y,h(62DE{㨎Wq^[M𳪎XMLiAT )=,Y!q7^ y'.kZZWx<=qlr#q!oȄj'0*$FTzp2nGK,D\{B5]>+R4pF;?Bi,E:)umkmP֦vk0!c"{w8L5:4蚥3yq$hAs( <<"(]ŶJVPUWY7=_KS4DMEUx‰ mB3cv(Hce)!'$Z)G\YYV6b.L` Q#HXn{0=u@uo;m.RiD=QI%K_Q|G ߍדFe#GNY㊓D]c~WY<;t̥̞Uz *+op.)63h*+hJgdxW(EW6&rzvWX$A0i6Wc Ŧ򸏈(]b" heeblwV,߶7e$WI&]I@o}m;UKQft2-JUIኁM oR۲5mdf%sLs;['HcNF0\ʫ B}!:RŔX+fr3M\wGNcr,xO"Hf!~V$gM0a&ɾΨȈIձC ED.@PSjbY4UV O8$;b_XF%Xv8NQ hCu`dXl%΀Zhĭ癝"7Oߠ&eqx 0?|K ;.@Mu#kCWqV,TlmKCMI]KbB\=U@DzYbF{X=d\i!Xb;yKNOP<+e1ٱs{g^Z+~xj<S,^l@i%qw꼴5ПdeC* {IRR̓[;FK3ch|qC0bRT<$y)rTĚ,u鉌4HtzZ~ = iMuTE)&؜lÊ@?&py%cD"9eeu~[QErO u`7b]ÂKF#t ),;F=% ,$5$ Q{ooձhUFea[a ,BlAU{w¥e`l_| ;l#&Am<& !ĦEI4\m-:UPeI[mDkL3(fF@ilHYIa{@PPp f gwۭvfCQB$j#(b-sq K,r=@o!ht$;_߮ZYQoH4ɬ kJ.0lVODFP!Z/+4-2nW$XeP1H!$Lە^6E1F=FGPp{k.#^͵ޕϤ?Gt@ HY٣ _oZv1P m{iIJ+F$E:;߮K3b l 0=~â]4.Ldt߷|C-楌jnoǣwTdBH~KwM &w46@Q}Dt8!Xitu!H告&k_kpL+IJ0Pp 4֡w6&,eKJ }pLj N|{)8sFUn'SHE<UBiRyĨhitz,?FH/>GSnqqZMBSvkv-sue'@ډmڢD1MW "M>'L?%AK :؂n7#b5FUI~U,RA,b>\ mJ[ )[ J)$TrD슭*׷NX/raJ.4,/tf֒C]}IFk@O]b>\ BB"I7o YDֽ6ַLz(n麏9:b7YØ LT*A}dB(f+f~ %BH!l0t4`33+}V،$*$u;H|=+c2yg:-&@NjDc_QndpW1T!VU|7 +rA`p7 ;R8 *\ 0V1쐁XӠ-鄑YFbm$_%N'̊le%F{ZiUjAxD˭!2(f ۶#ݢK8}GBpH`H}D*@;_dy5fpTl8pXEӰ0]S۸ÃZ6KΏ'<SS0[cOxkKIY4BL5\GP-Oix?HrdA&`S3>qsxֲ,7皱bJX|iغis95uf1LNH[ e",IP˙pGYъF]zbUWU)/Sfr$Ec&p@%pbiR:i&C?cܻ~jK!⌦Nhi&; {>]^UNѶVWf 08쪪0.y5̿_ES]$VTkma"ؒq* Y.SVd|I ֽœPvSĹ,f)IEQ:T40Lκ`=fva̙Ý+[Lʪmwۓo-0f/\kc3Hܝ n/*O[%0a52Jd}P|*<KE*-[SfM ZP?gDsE/)rEǗz,n-}s}~CN oom|IpCl@]-Dz;?Ƶ][fT ^ o8龋6nVb]`C*YFk~ uZO\:fe-7 CuQaf{k|=O7 `'Ԯ}j~/8sjh&DwU{&Ħ?glMsq-@i5oĨb;RE/?/O%N6[l6<xcϋIccGK[u`6>{G` }2hJ3,؆3ϲ &L5U4;o=oƞvyO눉W.#s{U?a)xZINJp =;_0-* |)xu1/`V$N$_񿤴|@Di[wTm+/j >>*%Y'!˝PWH#~k~9U<3Y)W* c v#^ ngyEoT̕tdTиVH* rS8kyyõy~iu?9mLs0ʔ,6 ~#\`Ĵp7$= =@hݍdFO~|of&[I󆦞Fy'ItM,dV#~ީl/2 9߅c}p"2b![)={}?(q?U0[үY$|AOmc!&>%V\ՅTbzUW{6.tMoC"3 ӏ9ӜW4ptӮ9FXJ 4nP7km11_F6wzѿ |=p 爸:XZxF\}Ƕ/zZOYapGQD?kcj$~uHBM O'{Z}jz*g?ExOj*REja;BiFԃ_| xd"Jef>ZUd$n,=.Aur67;?_m md|k':տbEp WK=4)01@콱XA!2:S! "mmC1ZjZcfzʏe뉤uF$fvX@n`$\~jK0V>[Vphjʄ DruV&"e|ep,jL ^XUƲm:i 8mП~'Ps3/^CSp5dQS|>ӸjY+\l76~jx|[BCa*zTmr'4pi#km_Ca3<.@E+$.4c?,9Yh4?K}1Vesk:3N }qؘ?޻Qӏ <&ٸ3S i-Cbm?L0蓕N曀xZ*(b..,fU ln/U `70cwԠ 17 eZ_-̪D< 8etHm nL].YBn=!#33 .]ml5$FkpvKژǛP0P-OObGL& (gPyje:Ld*Aa(LDk>eQAKH c^ݰ(rUhX[r,IԽ0)r{Hnax'd/!5Sakoj QDڀR6eWl"Wp" |1ؔnMԏ P-kߵ؁[Ob0rDP4oἓ?ui7cԺ)>FPiCXbd(hԪUmM42*rknt!msE\vmIRl߇rz,1KTRӬ }Ca9v)#IJg`@mդ;gʱc;zH㖺2Pb>jXv3!x5/*$RlZ}|;OG_E_B'P${9l.cWX?j3IU"}(uk&U,7P?ZTee;/᎕R16O slwI>uV?-#HFJgzc IՈ@#Sr@vQu $IH 5j[IP#Pf`D78`f !X5clyW(V` 7Oø_8 VEbHPG3=e*l;.d)) {#v$eO4,,,X>T k.z VB!p,=!H ) C3$7acÂYe!HAL"y^BM'±ot㰲TR?ʑ}8%ovl)$@uiN|OG d}vbp21 "=N8YѲuvKg\u#S S‰XVq`m| r> ! <*n}яpwbE08ǔL[zE QpI(m%6DX7Fͧ)"r+ (X؝ b, \ .TfQRFrlC9RuOlhW mWXY'N @Yæ7PG&'IqO^}ATW3Ī$P%5t;}겹 ;RB,j.hZL·3J"6m/{tlu•ry;ϗ,tT\lroS:CVWN+zc>_!$_zF9Oژ#s/ ; -aNΤuwʕ{'gr+^`W {mWEJQ{(X1I]Ȱ_3Pa0- 2^*1XK c! D.D1,Y{bRX?,fUR'™"^8F)2Cά@Vv;$-K9*Y/aF$ [tpڝָ ` ^e\% /=qؠ tcH@.O=$lE5K}IEH{R1:A~ ;V UQ@wKN\H)H[I_6}:X\lE,mE;"b+mϦA2iAsC}EXM\.:JDOp9)ܶ`P4j^L!%1!`d{R?SØjv[zMmFZ@ڀI=6HWa46JI)rHXW J 1UYvmb`O=ŁP>6Ĉ$H#2FTHʑq\%cBPk˒˨끈t$W"[@]!= „j5PQ{l[CC@zKørX a\dn-; ;k]G${kQ[p@mDX[ꍘ70uMJM²1u'(*۹p\v eܡ)br_& 3$,;4t`,! $V2F.[ 7+,4ݎ#Q;AZ-TE\,L z𐮅lHa$ma`%[6*܏kwǡㄆ 6F^k|rTH @XƽDb '! `l# l_iT{v .@M' 2jR.I$OD!cxUa`xcey3}Jz1.R|5=k`h^(D56 u߾9(r' )Bĵ+b]L.#׳ÆW+zSmJ~N̴i\pR4ޡZO]Q? UI)IWBiBAwL$Y &B:l1ͰuU i`Kmn}0h|DlѢ)+!ktktR0.sp{ .JqJ*~`sˉXhWV"n>pҦFc,B ml.FgB͡\nFhԥ6rq6+(F5Hsv톲Ir'aLDK$0QB}0֮E!iƸ *Aohus?8O3w&5_ʅݬ:4.lQ>6♒󩧏1- H$R=폥:|~Csb f} :cX^ݱY'JRK{D#YA-c}*CS~V8'̨fU}ZN0 #-E|yᣓqGO\uB 5'γJTeJ"Kp n=Wے-I6 4p?QKi \ RtwvP:|t{"$̆DߝRBH8W!~cf*MuIQ ,(~N; n%4R׼Ӗy*#c@k -esO.j;w+@YOW"G"-ɠI's b$,G j%+Gee}q!]m?Dd;M3[`0w⃆rũ\i.D5T;];V)*)԰(U r7k9S3-OUxt46ʋ=tziJtx_R)xsB٣ꏇԽݷO֢$es.YEt bjSUJ:$@7aC,zR/Uc퇕q;/mơc..cn F!U#t''1f-)eeYRL;np6t jSnp>VgE2>؜ٙߔǼ[$eRLb@eeKD4QF],tll.=w?Pa`o"ȾkuUkPSw@ 68j#i[p4 "FB78pdBͩcHKiv vp#ݼ ?Aț#c8FAM)Bp7oȚ(CX= !}^RS{wm8Q1 B_/ڞ-ew[Xy&X`B@< kM؛mt#ư ?n= dE=sJH ߦk4j3坵2+tUO\4t`dz3]kG#`XaMo.':>an7BҺͅ,R!WXmqk\Dƺ|6+ {j?^<6$&e}Q5-߶G!WA le2 & Ch) HS5C3 bR "TTTr| u8=q5*Q!N{}puQH{l {4fRqd"^LA$(WuR5E&eM)mLNJ;pw7nݰ`DD 5ʅߧ$O&鱣UVVɰѸO5C 4,m|Lir9hf,2 :@wsV"ְ#7ij,Į8zJyhT<lX[uƒ>WL9^3fArV9߮=-ӌAA^0lr\w&z]uK%!@]#HZK>^)bZ#X#1UI,6fGr:*xSƲySfhɺV'K,^UC駕5OԱ6=:v3X՘,n$զLbiT(-"`XP[NcmlU@%^:(#r 9 I[KcbiJ^{w8I7WΫnzpY:iV FP;ܟTn*#%) {,q |hm7-rKXVդͳO-rMcJ$2ը~-&W 3A=oe{1gNU59Un] Rn?Ҽ\) _> sy._ĹU5MIs/zG|]~' ^L5.}9E>}N}"}5_H[]Jfc,k>?, _r;VTU ?%30 ls˹gBǨtS4X)nޓ@v Ϲui a@'M pSRMW%5.c=GXaY^܀{o:K+v4'4eW,z{ai DQUlAQwtgwE*Uqo @qQ؍[|7828,kIl3z|șz%f*ȑp\+ײct2+*+w=IdRHr}pmr6vx/#6sK_dSEkdr ' ՓС"s{_| sk?y.oTdz3<0[TsHf? \[?5+]W?EC!+QCץB= r .*LPSArD\5~~gMyJeJNy ^Fثh~@1Y⧀ɲcv{eoO |+9LG.YuaT"Iv{P^~4g:^UNɲ7.fE`7Ƶ> ZVfQV,7ctyaM/⩪KԃG\TJ"Uj'cNn1<KQPxbY+G 9&oc`1H+hNKil[A!{7U,3$vfbn?i& '}bSDDHJ7g` ֥8bG }0;H(PTݔ$ 6V%GvK׿邊-Ab,SȪ TVIn:H 9vQH `@{b2Fk|)[V%+5&oX "-k{!.Ybc2[s%;phAC۾  lsb=XdKGd(M=>ؕ'j|YRvUU(I$qLlv*m# ^0mM"bФRᇤ6lIlyN!LEu(X& OQF^( o,')Q#k {JXUbBB}0컪B(=ؓD]JY[DA XrLaE5FcGR_I;8kk"oJf*R t3T:\9MN!9_WKUEI, qL#ѵj7n>>0j( &l)Da|Y{\cǭ/Ռ~a}o.s5\ o2rp]RFo\qW4Ueo\#2 3-BY$E麝4ͨ8m )ۯ{}qf//𸏡[^ Rښ8;"$Ț@ҝȹiwM2#f!؍e[he*35l)NISul!ER ;_i/EՁU)A,oN!'^ɕ$ =m=0fޫta˼BָOlyIFod`(:N r/qblpZ._wҢsK aWfGQ*J$Xd+}!FM6;LH 0ۮ=avBQH}ZO5r$7P.Q4LXm[=VW_NM:p;l;2E,w&&ESQ;|8Yb4lYCh*>a(UokI08g_>XAcn cm;C$3}7}e:Zg^6;nNg Mq 9]k (`{^ftJ(k0lHDCrE'niZ$H-{[YCSkߠ[ÿ4HT;gJzɱ#l]bp#;cWOs$% r4l jm@<4QT=Zkcî#P0>:?aMZ7c("f-un{]S5:; #4q'Vr.aGЀNJ}u7C9GyXޟ ܑe? E7REOz#FUmwF#c^nle#0[!@8+$6y ĕQ8oJS(uv'뎯yq pST!YVTss sNfICF[JY@F$=ù#cOSwQz_VAqsvm#k\L X AD[P^FkCWihbnl _4` ITF#*A^*:vT=aI%(CNP mG\pL#1Y[߸톽Yp%:}OaB3Ia^-da}e_*Y N{a) Pee[kU^ cH7 n#Dc4 vy{dlnR.EQwXOӉ#D Go ,$Tkcf7xi \0KRO1YYQe7[ss3ym*ƭ:7$$Av6lmմ`as.Wf\C4⺺(b@e`MY!Yermul2DK4&9N?Yރ5gsI5AO}yDԒ.\ NpfMNnހwMR BӶZJ %Ǧin@ r]-Y oDIo酲)`zi눏(+<-3 I17LH9Y@|ųEo9U̡'jo2OaʺNwÇ)XYCOA KwFfTqLA4RoסpH܇7=}ӶXwfҖwo#n8 [ fv d7@tDN $oGIDXFI%kidFPH^{ `H$7^äCIKsWTpUnq)P$_RJk>\!DWf͵om1"J 꽾o*r' .n\-x*\Ik< hEQͽ$ ̬;1Xo2ƒ5&wXݖ!G ,,7a_=ٜ6@@ ! Ϳ-=ɬamccdo*-N5Mkf1`FQ`{J1(fxʔbr.I:P_;"b[a`:u>a5D(ݜ(.NpPEEf m-c*_` M.Y?"I5ْZ0l, %5Xo)_HK5 %2Jl*q:D$J{X8KI- Ė HEXWk ,X;lH$TIkDV4qsaLMO8L( R`hƊ^^JP@TCY\"Hs<vSb~$CBGgRva[S9euE A;d;J;r`IZ H~sJkq5e^]'Uuh8Q) tߦ>&".@735%ePGtG7ds33df `=8F .#U[Q0 PM (aEFz":(pᶓ1 234f"$dH3ӭny(Y%f(V؀c8fSבbjJ>MIL#RӍlI &:Q4oV3٫c°E_T՗&*Y6U٘o7㞺&{$嬠1fG:*B{݀XƄxJJ<c-LJJOu u{o|=§9}fa? EQ&jL(R1 n@P,v[O3M?#F-Cd%)h=D;w&yeѺ<9˨lv\>wRʨ]Ibcǵ䤙eT"]Ou$}1h`([,I||jj!OUH^ZV=4K@D&h̯9f|)Ш.à6&>mTWe Fp,,~ݱTLjcg4дJK\je$YZy '03CβO`BpZ6zHl/=y.7ںyK%TZb"ȭ7jV#;yP MT*+O"|~@bjXT[.o*2BS%tx^ewKqooLM*YDU{`4O*ɽRϫQĒF, M;&=QhV7XRYfmT%8 *5)X"Jygs#욆Om4$#3! \EbCd7M~YZ uZL`ߩgg39Sڇ2u=N-A%YC6\S1΅7El})#XJ%G]ZQ1vc:Y8ݩˍݖ6U\2)ekiY (s~d:c$poICL",u n1j. 7 [mAw{NmwbQHSLrjR_%*ʀy[f" m=T޵+ Sv$vytR}|3J";žcB(,\Z }#T,(#%GQ}R(HJWʰV ?]cTJ(% mao|QtzMC  4[h?`RMOPh,ޛ}?#!?0&Q 6U7(ԌB{'H.Ic#6øʬ-T5y.mA&D#0ZgPoa-1{[Aa!s4YQۿlzjcơZˢ@o}:N"`-P>^h¸OSVj[m帑`@LʉVE6pX7+ژLl2b}[ r#mzAAa2K >b;ZFWHcݿ\ ]*ICV}q.HcrJ"ؗKR9)?䨊8ߠLF:d PFt Gol82iUFK)iU^'k4@;F,Un|wyF[K{ۧ톲`q%bmL]; )u)68kp#I _3F8W)X4ؒmboknpHiDM8$ZM8U3+Fu(=*b-xZ9/"k CXۛ'4d6E,oA#_1$8]Ov?%L UmbS WL7!m{{_i!:Dodz`,IaR3b} Cdt1kP&7tMN ~s[A_P'fFe&(;^:a4Fh,17MN" o<Ѕ_lyS*2 :I3Bk ou xhw+($M|hXZβF6NP.p"D P@8STmkb qMgY5F&3:\KTT5h`"R Rȍ})b1MG -},UC<)$rtu >LL:1~b& }Wi }z 4f4Lo#.ɤmAE֩7T 䕡Rb!nEm|LRI:m9f[s6r 8MC(Uϗ +mivzh4\U3.WDuϾQr֗u~ TQ P[R0?g(y7"|_Ugqe"2*Bq~ر/ 6)h&70wC3U{#l:q?oxߍxysN 2Jا:(6ս€MpL,ʼB 2oU/O9w4yG/rXav'hf,5q\!ʬ,̗|'OMaSшb]YA 틣 9W?\S<7URE<36"`u@NA۟5&_ h/6G Fu5JxLŎ*!n y#EI@źYﺳNUxxrܲ$nsZYV 5=};g|~ifg28Z/m*.)a=?q{UÜ||5̮7%5%MP°$ZJfO8?"g+nq! j ƉP%Ywn|Qy%,=~]p p-xG$˩r )Ј 1WDdؖVQc-(ڋ-vUIR7B ()9Kȶ8L`,cb} ZI\(U:c=$k"*!&0uklB*",l!TH|]{aDk2j~nl 6ꡯr|?3HYNYbѺTha}$"Mt8;.bquwFg-cf:E=|x1D{+\dzsgI#H8^!ĒQ Qh~&Rc e7cӿc1Jcct {JI}6"mdn%2EtVGM|6I2n46_ث\)[6ہlJ5 Nnx؆f#[a|Ց.ʲ180RE{w%5`y)J;E=pf+oZnTk+Y[g)*/}|#H<"I"I%ϙ ӷXr8X 2v'2lÀO_Puo-ojl0ǤrVˠP,|hUso3P2]T\om.mBGP{oVAxq#ͫ諸!JH0PoloumYg]ȭ+< xkᙪR*U_NT;[q7 .Y1rcFZj&Jmt7u!UoOli[[3%pXS 5RTÿĆq6(RЫ U{a.J=/{3i xSth{ZƑ+ _* IlEȌPGRG*Pe]6g6:5gxX)rb-!Mo uE:5Zw]T*Tj")p1. (~; tDC"7!_eKD!{O3F̱z/܏ľ)N*C78 b2M}:X\YT`l9oY6 \gjPoŭT"1Πno|-I(,0,}0I$ʡm4U*=?nt`r2BOPn5aO"\}$(Vn܋N LR.]\95Hۧfeb נf V+̯ifW THtQlis iF1 ŸJ+ipV,EA>cfm<'33"o>7$GSVE{XL6C%){U;="~ &[7\B1@uq 7$ؒʠSH}󍅉(ٗk gTgڈ;NƲW96aYQ*$4@@N{f٧g|O9j䨛o-䐽mbzqɸK$g.3+0*CGIz^}\s(.1xZA[Q=lB?oP$#V|ygZOLy^-(":ͻ~8g!bM-+Yd)ݏ`1ߦZ9Sڬ/۪}pi\j ]&,QT|A @lE* VGk:#Z~kĥNco2#[I^U-Q`/aX5h>0U|U$, e1c1nlױKk]T'{}=θ(Lhژ-oCaJi@ =VaXS ȳBuߗ| { @>m$LѕCbrT8Yl8*l˥~J6[tD F'Fzunq_d12E!$?&l[J?[C# %܁}X q.,C#C"\n$kTGf}M%H R[pH#savrE  ' tkj#rq :.^]Am[5\P]ćPa{:M 6(َo l\z.,ߦ ,4`-#HVXc HR²:!ĶZC*< XvgPIa}VXl6xbd,ac^pWGGeKuڙSSRм[ 6qu*,Qt؅!TƅR Z[sa0ͬY't8f2 EŀLG;[5ao[ūFdI7bO[!0Ğ3@^ dEʑ`w5+Ђa G1f&]oMAXlAA\YqC*MUaЏl)6K"@o$RE̲$F 'qO#Iؒߧ(D&BLgU DFPKTT$Bm٤_HUR EcMg mE]GKt~u$_[ta1k3:{R:M!upz᭶a&,464nWa!77d$H^B\GC$*&fUvJ[cuT#K(l/G<$X+ٜHlNP]D5qF.pL.u7`$rSRsoMN@USmr#*M@zGQM'  7`:.B*ۭGQmifD2, _ֱݔwP"bsF۬Kd"RwcpBo|xRZV%F-qc|UA]H86,HZ){<!#]-\(8B!3U~dgIcHPNq%9a G餋^Ӵb2UM{,kt6r& *(SlfQS+YZ؁Tii v{IKc]߾ w>8cƛ A.p4 %vRLlD5_Pqᳮ˨t"ݬRTtZ(SQʱBkD̈r$dfi@r}F0XJEzl ;[9S$Dqd`.GIL.ex8,m? ԊjVJr :D| <P?xsi2MPS|AMKTatڽCX( ؃|pu]qU5\ c,ᤱ7:z[ (yÆe$M `1~C}#3$Z0 &]TQw\쾓US#pbw5n&9d/|H=E=hQFmZEbmj̲ZYH@rWsS(,FGϯT4,TnnbaC,QH'|J5\JE T S`I1,Fm(ܜuzv@^ <k?¹=7 8ʲWq$F|! rݺ ?xV $7OO,ßwBTznNpŅK$9ܣk!$BL!>`\)IX*H צlJ HT`onl"67e6ڈʈ_t=Ʈ2=zaIRHi@wfb?|.0QxLcJ*hj-"6ԞZYte5JPK\QlKydL!v oZDV;y(o燑5UPjL;Q! α8SFT6@ Ib_Rtk}?\)-*.u/|!Z(vQd,,H\ - )o{E̋E;@~ 2H }a'˗E*~oPc(k}TV$ u9Cak@v8 ̂06̕aѮmd |,žQ`E,:Ap, ["yTwf 1FKU d80 ߱Ö)0-5b?l0Pd)J43/LL(T7 :ɞFG1 -sf#ߏ]f~ EK'lUR:iG$lk .{S2|4Bj+gQ_8o>!fSq$&L*Poӡ5sW?>iU̱0~ic,3$Yb3'pEլS0V 1\M;_]:x+3Y&E$FP 8w8M÷+cSN4wE Eͷ$pFPC˯am"y+]b 9 eQrG.bU1 ˤ>^ V:>KӇ\F7&9*;}$cu,O\FPI(F*lAUBGkZ;6$y*Rs3٘I*G3̡ߠ4Ƒ.u䑾er@9%}!PQI"X}寊zdl3]5\Q|)g\j+5D$Tick ;u|fro<Ŗ̕ŔpHǩ/< pA.g6k~&ܗgBvO.nk/9yL?QmH/WȾM1L7L*fuZk_ A+) ň+@%ǵa%Xrʅ@䎲JErIS'XU<`{pT̹Ϣ }{0@ǟpUF$|+5-ö@Onm#y =O|=M tjRL5HۨH}6Y(HXnL>HZTZoZok3..x\:SGSvSP΋$/ dnݶN^0#2F/'F UWSSqh=NI~T ko>97k!iR18]MMB4n/{  Vrc4~=IW+i:\2ܮA/*Zd@4>7L]U+ŭ).6\s<<ጯ1Ks'$3%ɸr0œD}N t ŜKEQy35^k.i%\0vp3b.zAȱP!~csF%uEd7a!=xsaɮPp_.3?x['9관~ؿ99 |I˜]p q$IK xY?1/MHw'qb/} om# W5;&wlty0 R#dPƠ {n§)Q< QG\/I k?%}nZ9/le^rbc㧨f(gfn:-lPU׹{.#ĝ_EӾw9MjEO4{}1ύr=忁?׍+)cIuI Ё` !zqҟG*%e4|SsjH8H,N֔w܎KSY<.$)2I곧Ç*_dlP]-(C#/@r! O)HiX2v:{)HIoFPdA,28d*Djna2!%;[e]n34evPGik Ir0%4._0o{aFS,;` mV 6|)1>6,I700ցMcTy6=4H \(05 I,E[XLr^\؈Q}:Q`Oǥ:14`l?`K'Y`Ԏ @cf G!(ǥ|@;,*-#Sp|9³")!}>HCv.[|(X  k7+'mmAS2%0EM\G{`4'շal8ɥMP ]%ZT$P))a+įB ĺ*}ϩDFw%fQ VQ AOI;aIeR].nafP0m%z Ru;$2D/*1)oۮ9͚p/I8j$ ̧::w;Uoq-|v)sXiL*9ٸZYcitS#Jۻct͏?Ӟ8`pʯSe^ **k##ֲyr4ΐbaƯ>\Sq#!v&M G0mL9.w7;c[?qkۑ88?U?vJif42GkXl}4Zc? ,o{u-,ef&aplV*Y`.,A BNsV1]@H:}`e f»p;aΥ#ZDw8ɩƤY/aq۾-w(yU$EzԱ- !f}^5ǪGy]r^0 oqq3@]hyMb ¼G#&OȐӗ'M젛m{8vb^@xi[ 4:E`6fb,}[f>kU,uLF3"}P.6o R=Du]Bz(M_׾"ug s.'o 5i>*| lW ~LWF`;0yƲ\N?䆨0P6@O4kCטyP.Ggna[tY{+/Kƕ<ꎢÙEmeDQkR۩6Z%sIjqEs ;]\ȩkb/*R,jo=G~$?Go0HkMgjچ-,4⚹#^\Z3dhq-7k]͹&|GBjsN =^a ;H ;~~*9,Uq25U"l@'v1É>˪̛)R%jG=j$틥Kgarn2SPNjeC 渦bv'=Rs)tIͅ%M> r*j$ͳ 3TFiuby#d=u>o r$d"j& }q|1+VFd{ + n0܆؛;ub"_M- (-E}B#sۦ)psUw tqn 4jc(|r2|JujR7 IFMJ76tR7[tL,X==U™Ownޫt4!f4X&/(%)RVB,mB Q`wq0Ih6VYiKEe]#pVX"Һ+~֫Fk'l{_1K6{P#Yv}'`FT" } SQNkolF&FH6:G׎wOk6HECmHB6KӨ6 #h* <'X,@OQQP}CiQ_ɨ{\ ٭'%#Tl0ֺ3hȽq!U5ė()JNn6'\(жjٗK,G Ʈe QvfnZ>K6g(PEH !uTv[4S~pY4ɓb@XYpm* āG-e{05 􍑝M" &4uD|_I ,-*UĐ{a]ZAN*kVOmȉB%U؂^ :\l@$(Lj;v>`$IAm2@b>IU6bͽ gF!mmI%;S'iJA|5dd:d (3efp ]^2]4u_% yXZ&Bݻv߇XI.msXd]ٔibw|BI3$OR]|K4%A`tHR# -i#Ӱ&5\@c_2l9V K[!mEǪk׏7%{$%. U\+c;.a<=ED>}>0yk<]p1I•0e͛Fk쭤- g̞+hsΪ)JE9,JLz b*57V_+-;S(ym[uRg^ OU DJG[ F$`"u 5sJJY`\K̉r 5}CKOercH`U@Sѕr/ȦcSm.Ly5/6g+L]MY5mM0)7#ny:Lo03/M2!(%PFH3: S%'m7/|YnsK4E ~Z]/Z[}qO\R˜Ufd$%#A#F.AhQsJiV)HB:F!Fd7`=oaK_-[Ś2/*{i;Q[KOu_[ELa2.8r@*9k67qDL)Mh6{Fr^Õ3_Pƪ2IFHn $}O7W"đ荼Nj:/g wR[S&&uEZtG*8$_(eR1&H3BJpXsro.3mSu=O#*T}pOMCe%XvxR]w#Q%ԳҗS$r .Syʄs冰.:*WX@2ƛ?LKi๕,m7pq%xA0%K_CM)>+fvJ,w}0:Ԩk{dM-bXMŻRAQM橗̥ |4n:`sMa50Lqh-v6}~&fjhgoGQ?Jk?k\&AB b/ 6}wi2+c[-"O-U;[7X=:Z&6Ce%jrS ,`^)X0 l d"8QxX6#.I-9`q!70U%)ߠ>gUPBiJ0,%xF? !0i٥p"݆۞n11sUs;DN~}eHECIs*\}q'apFRf4jiO2IoY`iJg"B$VVQ8DOIU @d 咬 ݱ.lVEOTӐzq)tkxSfOH⦍ˏ͊A Եyw)ڤOv]=j~oJEM YҲ-=LC(0/=f@`Y&4@6,IfR*ʷ u!i!"4iewzvVP #I=l*D7q%Dzw=:5 "{UI>϶~ҹ Fտ里k E ZIO9kz0#u!/sa\ Q*Qmk~D(c%>b5/drT¡}-0`*FM6A4VV]n f'ՓRWW$7Si$$ aę eٿqiE\Fzvi\:4$"8q9 M7γ.Br7Xuƙ,D٥r 6Ŧ? ;7ky\y+:Lxo8L_0(?YOR_`>5ğ'9)ǡ1xc֢B/3Jm@>yVupI(ͿV(7{t?4s?Y' ph/ں0=|(T` {\NaT9!4;u /r*ی~cf(*ꦜ)fX#R{kc)\`:/}Ğ&V X=PVWwA#da#S;&6l,zӤ##$ a9rD-*/M,:E\FB|UKZnHlG/7K#D.[[gd].o\SKJ欎6 +>} zc-rCb%'R hMF*BܕӮ&,OeVvKf^rGG#~k5bqLus v~l2xJ…H"}0!bӦX{{%>_#II#l S(kے gvUwm'4w O*ޢ!N5p|530/۶_yzgk*gjZ캨 12)`X܌ox).lUNg&aIt=^MAY3` "B-۱ƻY>SC}2?A<}#q.Vh̦gDᥗQ(nO~aQ`A-a>MA1,KG c?.[DF@ Osca^7tlۛ$ UZT(\ {! 2N@23235PŴFWr5${SIRFǷpwUnaiO$C#Zj]An}=jgyE\sw ځ@6wkLtn?@a2V8ـ'-T2Br5@ba}z3n6d\7Tg5ׯ->d56Ur=n%ɸwj(?Xù;k{[9xar+hճ ҥĹi"iy`d\\AN-Owz߈+uR oG;{r+h5b6g<*%Yld,FV62X;20R\BAɦE˹S ˘{)f}޾g \fZK搒IO?&Uq5B@ m (]]cH]PKPnሁ,a o;k@A6F'Tk׶f:SC+ CWiHd [l:Nl/ኖ(&WPK$,9Y_F AbHvդ eCI=[ߠ-oǦe&V{ C׶2\I&o>F TX kba ,nǁFf W38SF'U@VBN!6>OT_AԀٜn Dr=ي{`X)L%U&u#p-킬M#yx {-0mB~HĊe6&xAcR׎gҗFmpc3T&,*iIUfuG[HH;|n0Bt':ɅSkaT|i Ӛ+D.^UOg @N*vUTG$R lsjgND%{Oc}_p1 ,s?\Өҹ6øQ'Q"0UD <;u=7 1 DƭԓxHH]@3G , =Mks" p؛u5){+rBkiHڀd= ߩɊ?r4fm[x'$Q|\2X TH OL'[ߠ8PpØ2Lҧx9ZX(,[3p4sIߞ+?\ܜ2.+^>TJJ{ꉘ $XWxH^"A™\TԔ1JuM'rw8Ԫ?6YooƔ^8_i×{|6sۙ 8Slˌk`xz/HbdR7cqk |kWŸ9Ϗ*揈|:ͲY0ySAܵpBuY "ya5 -o"x5BFm7?LK0@8ùTTQ% G@[-v'" #`{aBedYs_89=S6HM1e[k_ݯ$~,Zr%QA̳;A~9B?kƑxgӨXUpNGai-gtZW1z|6y& JŻp[~}q@ZꉪhrygY|%dЍ`wn;cx=s\fIGUӟT6Bh O=c$ZTW-$i$kMUNk@?N!rQ Ƣ:c̠X]o~kƟt5<9wT0TGM[f"c|+xf3#S1;V%Y`}i2.0' ɳJ0/ek5Ro<#ɚVpuy43(RQvM_ۄ8j(N2yIMJ6S`# .YFqt3>LO 5As]s |<9Ă(\*<,ZfI:Bl@EZ 3ZlXW( k`1Z:8#gc a9h!"|DzC?G~}[COrVrTE4sTJ_\::mK|)! =f\=mP܁`0#p5o e'\&[}h=&r;m'஼T~ i?2 ^w)@YCl]-rT̵ޛIo7댗d`ֽ@Y6bl=7TqӴ|R >A03H` 0rz:n^pȎ)#U6ݷ6?q$~s^SOm1{jC*~_dtFyD!#[ )NBx/Z Y#!}LTeD#$Q .߽CVA"u9$*[++(*qfLxenq0xQGVTh¹fUo=l:|d٣u#찰X:8C$/ L]z5b`ʏknJ\H{t= ;m7J)i7FU@$*R14 i/탶+zXj N_ #_la nO{Y5mWCFP4AkC2MJ6V? IdPo7]89Ž$o %};[ Ei + Bi$Q5Hj?` R: !(-m6tl4t`c7N_d̈́͟`Fl}I:-g 9'oeP7*$ E'u#k(CHv,w}9 |cHY H`0Ǹ1h k:I :d+_|4e"E t8ʋu%B,LB1v GQd!߯bj"E&R5 (?nui`o ]+Q,Utl3 zBػm{tF(FUmE-#sÛ{B g w^o)0ۓ}Ⱦ/cCr[_3\0MM$}ǦF^a[G$4ى  m#ɥ)bXԌzэ(=WoW"0 ؄_ Yw*iM0MOo[+lG<6?54RM4Z`?=6@StLN\j`mVb:[Th!"o탃!Qpa﫨?눒:6jRG,z4{EضkIcF / Hs{ 9頒\A!Dltf'AhdԶm,"6rJ \|:+!tk,^}^d%X l O\+(#L)JB!M,."6$7od:HqM4 #YY 5DlŶ/؍,GOc7BZH vX Im'’ND0;|ULZ-PJP`/JP N[!Uȸ3i BDvXiʵiM϶]K)v(.Km4cB^4%A}JT1X0[Rf6v n-RҬh`Mb:AY7O&R`$cm {+h*F;c1*l0F3ڿfpA$nCfa {GX[hlMb2GT| 6TjV,ѴEHv^;!E;ԩL-s$O %cbTz9QDқHkXmQHm6=mHH¡ + w=+։MG"A,OJe;o 9kyDZVH Tur,%Blс?L-)X)U"5J.RQ(PMؐw| $0`lrWE gj]}S!e2 x\^iFUPki,ۜw1 UT9p._|"Lڏ(+2D[1ӧԺw-M*QCqoJ< )u6LtM/p_2/x80xK%!;&熣-JTXSm VAMcHI=e`/tV1<4rTj󤞑qL^8Dz4=_rHώ^r *`b p.!Y@I(͐8k0k*{[mTnJ@ $S"5}1>YU_/Q^Fe8$:~бG׎E-%$'āꫪJ\P{MQ{ >7|sooVq'浹&Y@ p]]j:(/X4ߦخ(-x'?jjAd3EMK c)%p6ƧJڬٛ~ao}_SŽԎ*,L} _?xGug4! ɗ/ZHԍ6Tפ|Zî;BQg.j9~`qeDdaWR1$u8 ۰#|7|"q|5?ptYOxTgq`ofw~,!HT,+:-w;U #aVU5C%Y'LgO9/"w$O3]C]tʤ+bU d[cYҸ9\@O^2av]mH^DbG$q0tgHG];@Chģ on)H$rԴF_KcX3u*c >Q.*`>0fYe_tqlZd̆drڮ5M7B=lְ{IW] wmY 9 =-׮_r3Bu!1ŧ[ ʎaZi JᦨB40bK_pIgDSon MG?2iXF !`FY: Gp>a 6,<Ж[ >PMa/yxQpk E=L2WFCp6=q&o_I45LW.OrnoOxGM\X]W']tkeBcgg.8+9H2iTҫ N,I&g)cX== -YStLߙx.9OE.[gpo(wq@ _kۮ>^ k|\P=::FW]ݷ&#W:xRLMJY6KV[^Ikl7 {1\%i(,j7@ =^>s+Z@ *VDIhP|0zye%ƈ}S #XT SWXT8!wKBѸb`N?\/S#.F&?_'d.u!^"i)0.tD΅] Z(^'||st-d[C|hc{/^ r~T[y?4u&goy&PaK \[Yfmrq'%θ9u˃ +u Lβ MN}Du#g*his*$k `Sa~m_+]pO^R@ HF.{bN%O9ؓ.wnI=k#h W\/4"+ ~5I崪ac'0* \5GD-<%DJU>鍁h^J!%$2cv`^7kIDl^ eJ ,齇L+DPu{m6GrL0剶{ 0F^vawUTq;LBhZeUf~f|:V 77-NRWIGE>cS4tgr.lsN)fY Wq]1ou:vm٥M5Jk1xPwF%f9ej*{,gfk4Ru[/xiEe\&F#b{rdžxi$q[Gq6 mPM ,Ѥ12m,: ()(HSC@ e󐈌JX?,Qe<)ܞy?M&+\KM?tv R#-Ub/ }ZNuR3(kWN퀬+厑 ?2|1=# 3MHU Y{[ IX-u*:;/s`A| 97(hFC]߮%Z[H-pڤ,#FT(^3$bM&ɦeԈe;[v2zD !yCn JRLK0YZji4"u1 x =wIJVq6"ĭ$jE swWoR"rv,<ԓ(Z^{]vbSNI4)R A~@UI7_"˗ rt߷|WHvH4H@e ;j=>،h eaxGi XrL|bzpl#,F;欌 +?L,Ya[1Ө[o )yBManб3Hy'Hù"B|ԋDlxdT_bwbyk۰?1ōeQ JE͐9GUԺt܀6aPT t^2ѳju#e c<¤"R& ;lyXH9dRCb}Ǿ#s1 C JLzV  ;Aٛo?V=\*R95 `ar-Ik-$ҕhF۷I3YǤ?AE Dpt\9W4ruS5OABdn|r oϧQWdF,*cA1ұb5\E]%}tLWTJoP;s>7翏3=XֆhbEȽ1 'W\W\$zᗔty7 CPLIeƿU35%v!,ażlxk\M.]Mߗm&#kA͹'j'?͋ x::1b*)ks A`-o|rT?xs v%Jb5,Γ6x:ZruGXorx&+hr ՓRg?"Ȥ58HA͞Vw pkW ~\6QI Un{æY.zx2تeMD.25jnNa¹6}OJ]IO S1b[SA4ޝ-VaKsi܍ yUTAixc/O "K7bxK8<]fmTIY Jbar2 ;mÞ0>(䖶(- ,l,7r7ug-nEWp0XƔmӴDAE͐lҚH> 9?iy^Zx罵 ix>7\7pߍONy]TEԎH17nO 7wgI EqIƢ|dO3|rEqg29snqdYE!uFCCCğy]k{+(!lP"u.~co}s G|8c3 i _F+΍mp@;go!8yq>W Q%Lֱkc ^. |X5OWf@Pm1V4 anodǩ--6I4iV<@>G㻊x^Hx̢B*I.ŏ9W6q!x~2[QT?窣T*nCϑYQpg*hψxJAs5nM6|d ЅP :l4bO;/x_^Yf* 5"-9 -7-*3J _['}c 4LB cOM G5 ÛU=v:FFvZs #}[CC#9g\)kdZ8`[͐f6qPH X(cV[!  - \V=<Q/4h.Rnޱ-o7I؍;)ǖi  19KƐ5 Eeui^o(^pG!HQ*eUZ2 ҧb^>WWO 0e .@16;}pe.XTn~n |6KCeab}6MWifw(ُtL#bT{0F#M7)q,kKI%ZddB@7n뇐lKuh +[통=:ԯKֵ&հ!nHe ,.p}j3 MuӋ˂]Fы텴2S zǦj|œG,s"22؀BVԻE1.9S p?Bfkj { @&+" HaQlv /ʪ`O{vĸK-mӵ#=dΎ$e;}Ƴ>+UtxzxI}Ll9K{/MD'RtIpV6Dk!9hIk텢,,FP}GnVQ5ن㥇l)Hֱц o1 L%rEΞ}%A PA%ë323*j:bqh`Ӿ $toaD .=XRC{`L<}J.wc۬s'J(;/T@ZM%u7rpRz=!nlpa`nPKF4C8@FWy [ *R QIy(ݰn$ }KqZYbMF Y$u ȈAf:[vsmS Aa&a%s Ҹ5辀X݅;aHtIO-@Pt-p1 Hn[„zs&4p^` Q\(d (td"#cQ."67QѴ*9[+F*s ?(TDJEcdxuq`Lo#J).Hi922jv¤.X^hELR.t[*A؃`-no"{u@:if! O7$@+BH[sa#]`2`tH=JkSj@a bJȩ,@I GSamҁ#7[ٛhru*R~ QQB>vP\6#t& #(b({#d.ʺnT J1R[o u+f; C`dIZ .pe B;4ʷr@[?LF5(ZKT$&kpH kupǡmW;[1[]![)6,Yζ顺_mWƒ4nci@XX5XYm=TBU.=<\g4ܒO:X#2 bTi2k'f͑5 L~߶X Y$ B I ZuZtbp@LU[l1`䮦 K(%b?:S=.8m3el$MW"&IBPP|){0xq#JnV.d}W=>'2"ʉU焫Z;yjn|qSs[m-EP 4ZcFPP9Iُ?<)s#) PT͙ 3I"ݓVn74.Ȳ\.:jjJXᢀ/t1W]C#K+R]> >yTy1"OèԫB]@ȑ'l83#o fmFX++~,BDSpU@#XSƺ+lP2ybG }v)'>tH)wGAn8u3)-{RX'0Q#剤5( {=6n.FǤ#Fʱp@mzu1>A {_\!p;`Ua#Cץ_Wy2 qeayOFbѲBY]k&EMאy5Jx_Bj҆ V(Ue b; !.{y4m7,t=28(_6տ Œ?7C(،4?%%uDw7K(5/bwCHIp:GفMeEDf }wSy S浮O ?Q8C{%>a@c@lָ|1V@H*=̎dpJ ,,/1Ă, `ox(FWGvHPnǷK̮e.rs⎆X\zbx#r/:E|6#4Q1~6$u_0G<>*zH0 K q.o!Ծaksb:;:Nc`.&ې&*/Z0N{?lWLl 8Wd-+&F Nm@6pmLBs 9F.8?iK?͑+s|F"!BPcb4Pa.i,ϒ ? -eoXM[OgG8 =] PFH#rmo-w7 pfKG-9"H0S5=n}q)rRF+YvU[۸\ʬjG*(TxURX]}p4! L+b? eRY^pɫ)’0LFN5&`l(ij)I$rdy'+j"wvRtu1j:k*)fo1lF~elqk&҆ZC.J,uJ'V(1UR:Տ|7E_Təq<MQ8~mqKƷ<-yo,/E%/gT TPl4<-\2)j1hZZrsnc}Nl؞:٧g8r}_M|2xOþ#D,lW.8ל\IO@UMtPF,I'F1鎈<#r;1/axuK*Mr5wsز<{*sܶ 6`dRZH4+,eZZ1lfBV5qea5 !y\Ň_0 `Lkh;{DmYn6:[3eY6 7s*ZzxMDm |h2%|T7 YA$}Li%QyӲ4QKGSHI\qq: f.Dpp4"pBI2bn-Qg A-;~L>7(Erl q&5(;n6K~YopQK as} t FeX5!Z,qKV z~!]DRwt a-DlӿlIFm%|Hp"_ZT *ܓ W[qD32[@ D-*I)[F5͐7ԁ#W3M[wXf.=HUvER \U>`YQb0]w7/!:{ '$VWuT-TZ:S+^oROQ,lU)#^=֗nV'4Kc) @M\`Ηܨ@P\%M' q I@Ő:h8H#Eo4Y:P$'ma{|v(5DKP'˦P2ky DMQsz-Vg*, =ih&ñV^]ijbxB*#En-2yV6wHbDoKpKɓeIE۵S*K!˙'AH?QlpN}Dk}pB-}HϾ, Y` oEae[xZȲbUCk66vMդ]X@m8d,&7m$b[=l4Wd梠-<2 Prn+ j40Qq,$O] ,~XөE⮇#6ˠUrMY$#}{a~kv5alnedo-t1/;nڍVm!S3kc@_oy2}KlL͌\'mDC *7Q*܎`J݅GaĨ/f ܂/l$dg Nv):i+bLQO*p}{_1ſ*/ aeSCC:7[۵$72%덼Ve3yYOK "ˍ#V} oG8ޢ>K_DQ"HB-m\bgT|zϿ ̥&$tFVl/ש/}ä**WB9?? #`/5 ω~3!SE%m\r%L)Q9c6-w]VO%XY -yMbhF9mZ轏o[$S@bff$/ٛavҝ䙬v8pUNu:dlGG6 koS4Q_CP&$Rы4}׵N0D b۾CBF 47GoOCkz0ڥ!waO9d?ğƟxAN^\Aqn{$&1h0=nc'(E|%,|ߦNjaW*ijUl۰Ɯ6Trf>2,ʙ\>X+ohMpH߯6ہjdW/QSA;ZHbtbmŅ&{D<,1xk#ffe-e3z] ߿PAǦUB:HmcM5|L/.<U3df2KUnpAbB"}CUKhbgo~J4`1p %_^B mBޒ\b35Dr*CD4_S_&26pA3;Bm%I.,;ʸvL9ͨrO0's ʼn`. A Kr;HQJ-`_=lE?$o9]-quE=U,U0:Ab19YPE!VHݔ-dTsn)x,喇tٙ)'wpfHcblygVT2?*:k (O(T*`z.Y#Bɳk-vW|{8K|ӫn.e^U'f'7JPv +`zGg rar櫌+ 8̣IYN[\m;EuLl})ԝ]]~lc߈^Lw/3SsY>iDsFUk}#|D>*|σrAfນزY3 E \1<8~"w̪뙩eE#\Q@KnϾ  $ʋ-cHڟ~Ar/WQ-| rdIQ;]QsŜ[⫏x%JAy-<@.#;~`cxחq [M, Vc,Z8Qr ۥ1y͟׉ TeRϙQMR@%yK`.@|<`_eݢ˰q*UGFMW[cK.0_ '.Qs$Pp ehM@*[1)̏?^BGqKUU_wÜa?lJURPbCh0TY1gJZlo鋄~$cx_k էMkdn7o]u%WzȪ)EM>gЁ1Fvt}Lss><%Q`d\gjSk5Ł }\}sJZWavZ`@؞Tol9 {\n+=* 5n/ T1\X|hdto0>[ʾge: ꕏyBZ1~D>obJ73DpɢCE" yb@Bu_"<H2̰[k%<ͥ2($*l9@PZh&Pu?󉬔!U}bI/ nFEczu+p׹=(,@[o PBw速1"LB*]X% ʾ#\ fF7 xfƹz|=3iBC%%kZ֡滗R:bR݅> ":/>`oIQHĖ41 :)5ب'af=*OBzw2 I(5HmPp݁i% #B-ToJM.["Iou{bL+һe.HQe^1bRW-JEJjG T+p[S-'LM`AsU7Vj1%@ & DCha$Ӈ1;0%Te4mu DC;u@ူ.`:m` (Uf&-V&*U%HTLwf@z`؇{6 2)Zf1~f:M$̡A8qbW}ŮNZ ;Qu VHǤ-{ eg` c( v KБ}E}H${ȤU>N C_Y j ٓ۷ fRe\E5kk/`. $E @Uŋ~a\5$Z#UF;jE%X,s:*7툏 -G$v Z8lX6| ]@?nmyQ#cm.bUTNT'kP-`6B䬚/l ,9eULh( @ P4 m [ lJ#vbzZvBslWt.ɰ߮3*Z%hY 4k50}\Uels*–b0Z6Ҋy{|E"Lza>oHJ)fc~~u$c$s]v12A$LcZotT쥙;?nI]^`BY&c 0D͸B\ _$eJQd12Rw/XV@( ,[a'u@?f}%U'ɚ\n4\ڣ2$ 'cHyGµ,4tJXiWfz'S3.%ͦ)iJ̾O~߶9rn++ZX"141 _oH1瞲"|K6`7ô^DžeHQ:֪!y렢bw$1M@şeͤ⼣1I3IrBL8[nEA醴eyQT²~\D:Zd?qwui+)Tpk}BA%H-'s|W,ݘG:Q1`l10$Z3ݷ(eBvoO66ַ}62M4,Qr~kS9bIG,k+ t=sSl/*Ӽ9 1HQRƆ$nIQ܇nrI%GhUpH `$a-'Fеcoasmִgf@28htYTbJ҅u¥i" :Bro}0ݡk}eH*`;YFRN ج[z5ZeZwC8U7q۶&0RZH 8p־CQ8]@cԃBk.7AhgkGM{#J8vn䷵Z {,:@d:]?(z!ɺ\Qw" p &Y˙<%NRwaokzPţN-AcER*,BiRKM[N@݇5G{e0lK(*S*kZo`VHTq uHn^eg>-j&j#Cx`lv^Ҷ0.Wַ+RмYTմF*teCƑIq|O+8yhގ8!B%#,>A]<+ p1X;بU\4;T-TI"ǩ'vʬ<[cRkkgj2l2ye̦ I@S .-s37{}q^s͗&= \-ز^ؠ2ggD8Ī@*f3Ym gG`n ~?8߉Ip]VO]_GG-Vo "@VUdzGj61 :EJzI%{ֿaS^x3,'4iyg5rT[G/_Ft,g<.#Bvv'I"厄 h6**v #jI1v% >fθ͠z#VZ*rF*;rw2tO ƪ"˾lRt*ۈU7`nGWHC3tDv윪T)OOuj2!UWkZk) Hj W-SD~1n:y qܧu"Gp4iK!Cʫ`aU,I?*9Ew؎ֹ4 أ̡9s6fV49udLO~WG[ BlSo퉛*4H,r<,K4SH(ia)*_}1*9q)^ľHicGU]v78D4DhB,&+"o"),qaS60:(DP\+Y.;˚;n[or$`)2FSP?ۀMP*XS``G_:R Q3)K]*L3 k_F<L.S{m r ,sGk:-X}^PG4Q3#I(ASo 1x :V[od\-t bI1Ƥ0&Ž4Fvf GqX*YI}԰ai2Hm"O+˄IDXԑL`=>cOX5@S4;ks$r¢袓T;?lk'{\xsx.-Qih)n7AcK'} :Iaub:X\܁fk@H>S V6x X4R1j!ei}2T o.O3CzC/%TI׫038>cت`{EɌF5aP26d5vXoveW)ea g F[MuN]:"bW\+;XZv' n g,mag:E .EdUHT>b0(❤-UR^)mP}@Ƕy±,nzI"ea_0ȖRł߾!p{`QTI TQ[kMr=@60ig` !Ğg S<:< *Y2X$S'܋]* F)bm% I"Kٿj'(گ̒58"yPI&唜fyF40,s[Sr#JHA`Nm &wp6QQ -PԬ)i@ `S1N@p.kdN"hv:k[FB*dhx!Wd\ '*x7j|/OO:4=gǯ0W_1WQ.aVY8GR|>^P_ Ž,ަGWI)mN ,ˋ(0kP${ o_Zs\:)rK4Є$~/@rGF|U[2ri*h6TЕŁ!/o_e<*J^QgSMTҸ o>YC#ſ*o48_<ˌwUQu]$OP.HرDGI cGeļ-ɞsLskj2<+-bȩrb#~(Mp743~>vnNs ;e-cE,;1=uqr^~sφ# 8N)㖡ibT IAzk|-rÎgθ2nZ:쾔GQQLLZ?@~lCbz,?qy.;z<0"2r#W= oq⪒>TrչnI2+U 96v龠8K_yPfYZRu&J8,\ۯMǒ[ώf3Ð ˊ3AO12K"EBNˬ\ϊWqnGS[IWUrQuƋKŗ?9##sNz~%%2*$-1:#p.CG*9Aᗐ/GM4[AK@jsSP6=4-p?)o57Z2ҧ8у\;'P_XC{M?s]n(xih)89lfb.H?5# 9?&^0Kq.a<,k^,Sp&?1 Ew'r/寉'I)i"+ @65Srl2ၝ4|;Fj:J+*(vM/m!4TO{>X~ -< \oyO]tYK)&ifP+d<w,xURKo&`+DAqx^ _덹']oRH:aC;m8qi.qV(**$4uVU'lC "JCS7Rg6g]ғ !2'xMi?u#npo\/NXrӅjJ.,̫&fS˧ڈuHۭaEɞpu8G6r̚d_*LU?uIe=%3VUѶy\5gyWC#֫2XоH$ۮض<1 0:uUSfGSoƴm?f:Dq[6TLLaA(4R8;hwY6qC?62c4E@{ɩ{=K(TB'k_?G |O:IY.h7c M~)HF[Y൧ )86(8iK*݉5VETo{:4k]AeyoIMbCu$JK"$Ȫ56v っf%IW'tVeybp,l!x0Q"ܱCx Ok=Qd"' ڐ+ji 0%d=RR-QnYNw,,=Q@ {qyk )vUTXjopTLA7y482ـBc 6$ӢKΞlrK\_p,0eV[kb1 ;,.%xI6;} "cŻ\4R.uKDlwpx}RmB"rzIrf6¢pt4jC=:`" dMQ|<0 rp̾a!K_qmI"Um/qɚVDuWҮp0_VD1& SShi.%B]ǔ,oPz}߶Mds-p |[ē+>%2QI6Ȁ2'E!-}XX|zfwר^Uv)’8nQTWV*PEᮐ7pi)@Yl7\7A[湍6]PҼٕ}d#% ,̶|5i 4,i"OqWS⥜ 7'sQAË5=~a"d+b#,Yv;Mrf*}JnJ/*jOK.OIh+zJ-ợ,̱,VR^ 8a2x ;M?|hW6">wUFm( <2&Z._,3A$#&mcwLe`}KXGyNv#Q\[w]xp.`iYN'SRYf}&Tf2Ҁ }cE"jk*F,[邺T7Xk,{Y|֕ĔjF5ub1X"!d,KuaC RhؿKP'XgD}=acFKĎ p1Fl/tK+nG=Ln ?B1 k%a y2w"}.}e@Crڅn:bYYr ͈#F$ijqķ.[vl7KQB%Q-5jb8(p0ONl&vu`@b:mjZ$<2k{`ժfWkvÿNXZćAP0=0:dD|в@XF%K'$SJ.ul&z.XVnOa|C' F$#2r 8vrև|I Y aQ) i{j,@tqg%L,򰼀ه|>Nx7uZle7U1{+_BqÛm4ޅsg˪J5ZXE[@NItc"3zY[s'DrTKEK,1)Ò~ ˩r9t±Am"`咝Qe-KSCEpAqϾ*8bzzxhyK./ynܭQ j"{f78 51ΰ^ݎn{t8hʚYΝVi>~esTR 4d&B(.rGXiJHU*+K.o%s%*v/R:uD%҅Kik50IU*%0w4 tLF'OXFh )OJi("N[WI#PC,jRnֶ%}R(>bc0EJx"|Bd埵NRvʤh1DVoqצs)S|= Xv=F`TOoLD,׷CaXW7J\RV:U0C~)$@[z.1[mӺ,Q.#wߠoJb)`$@:}GM0앿yS9YUTVUNvƏ_8yE%VU%c=x{(Wvdy5 @ݶdpPU-e#;{-<_)b("Xf)g,'JǙSW+o0X"yGKʎ^e\']8HMܛMbRgDzn( U,㈴)MafӦw%X_Gl)2ׂi, k쿾 MNӦ4) R][oII1+~h;t{'("@ޠ7z F x .I2 ,1wfRUًh oۦ'9lQh 6t‡-#Z׹ a̧S7D^;y* kF2WPoGP;mIXYag3#Ok5M-&ݰ)s)r_A"" 2يw'ґ `~ ]E)X:0'O=(cж:fؐ~/OO dz곂6m#đp^#u(a5leY{DI'LBi戮wOm iVjegfe1>d6Mrw0d@&5j hr C[!zhQk؛~Cyˤ$k0}?\041Xȑ.h:E"j,@UwRw8DIy[UEpGA iQB9Bč@?|L"40 .NO)79]4L!.[\bIkMV+e^7K}[&7$s`q] 5p+p e?G["iT e! +OOЋ ,oAPeye*[E V obz[`k_h~uX䓻faȓH)1NdHoDͪX꺵l5p".aE-6>'8_= セߦMO#0.o`uHR MF_HJrBDіU0;YL"l4@ZTQ#/,p,y ɴLm H7MJ|Hv_a#AP4`^K2Jw 4V# J {-syJvHY,A MX*+res#k":AԪ#pԀ\sM2.WK]7!QH22w톱ӳ],čm{|9h)mX^RB$fZ2#Jkꤔ'{j_,IFA`l/'4o"4 qćm ʁ%+`E ͎2#W ĝm+#ҟW<^8TmoTEdyeW%4n53I#]'OӮ <> 5؃!m-lx&d7-SyK/Bnoc}y h곮 \OK!4ȯx].@l5'gٯ4y{=Fo":kY@X>2:f/<',Z&Λ8Aw ű:~ LYӾdx%u;-Á3j_@?~ :5Xڢ8Em^yT;Ɉ+2PdޭcbwBR݉W~2ԢQ`8@v$Ʃ,ڎ!5TT_E*] +]&{6HnCT1DCl$+{[KǯU):t64YqǨ*WF WQk‰qtLf1+^e1[i.UF\KZ׬F)ҫ{tPѫM"s,OO򵃵ONejbH]1Ģ=@pA\- Dy%X-7a̋ ] cdM m-\{F(IAp ,#d;"@'B,X`5D֕ %K;Š$0\lf 1.7lkc]?6r+TR-+#='yـ*\bߌeu"O3jXK뎍MM)${5涶\"@} nQH*UtmP#ff?{~|[g<<5eMǜMHщJXLcs7S~Uj+id2v؛c!90c'jEL@-d"Kԏ{cuiʹ!ô_煎GO4s!|i|.8G1fUbFEljSg%yg\\k3#Z!9}r5lElHNqśdaOcٖ_U8CTm{n%q$vSMMdhe@t*1Ӌ2xGn$|eн`9$ FRG'8')h**0~i3RKD݁7o}$yA4.7ho[+<;8G;(+uUUPڥ#n-`+u97 MPYuo3xۅs b\4j`B1,q49+FuMQKliA= a~:|vfuy.8(6DynR-A!r:x'o +8\IJ9,1SƪiK#$-/[N iC1Ծ]l~j'q$n{\?M9r9>vl' Rj8M5\Lz}, rq _WIO'̖` ; 8[!x≠b+L9ɑK,UH^-q*a}@\ ׂ2<ż4sSZiÒN{@|2sW987癷,8tf7hY,G9"/w:EPVAS/EJ"5nkQHBlva_&p59P3 ʨ`&_Vs[XdyD1-_1g1~,h|7јe9#;د qZ^3N^yws,YSf  RnRF/6>%pSSaK5Iq_]Aթ5 Ii z|MxC+?9řVdr/jd3T$$[(4 t9{W^5ᓑ(~\p༣"_405ma4M ؁Y42)7Ck]7=!j/Ň/-x$,>!Q_F!P\_^ f?p^E3d5 Za) \:|G1Rm/yõ('*O!*{1و$c1a^[o9{3~x37ȩШrɽ+)<>xo 8/򊖤M%t:´.ڜHmx9qW-Y+=uM]Qfe+.KIYVF:~eORb>H6L9Ŵ3&Zyc1cJt^gr䔼f|QQ zK7 69}[11x|1 wJ @} %]u'sOl3x+(]^eW= 62JBG6#RuΘ\Β\C܏z1 Ε^Hn7˰i'e%om+<=]/x-Ϩh0*CDB0Mi(I ɨ Y =?upJkhY#ʼn$ЗH/{skFm Y:Rb)@eU"xԫI1Wouk[r[1]ܮd|!_}AIb:ʚl|х1%C)Tu| <S lB"V.M؝\XdcҚ L!mR6‘BHu p!"49 &|,yHؔPV2ie`OlD"Hǖ)k"6Y l:-|IeRO)Bߦ0}HVBobՠ.B0̲ nccQ?:S=KHoBVG %W%,Iĉ%uTso7Eצ)ٳ}$FBPAo&e$s1EɱwۥhRG TY2H jHG{}18HYɶ|ť*~[FE`-sauZiV:l.lp`U`.z,Xm1”"+ 8HPL",9w"'Q#e -a em ecq,Fp;RJ`.n;; _|eD1*zȍ՛p5e+ pF8xb&u`M;'7(66夅%!db-&!? vB@C=1t;|EDOo o,MHa1=>`Ft*}< +*ٕ#J>=34<\C${fj0uo__U4iKUG_|ϗq =Wq q{W+H~P7#H7;`JIrG:UC`SiW( mI2HnI@nĚyr a,Vʎw8%\XGmbX&yb`C5dβ3WbA~rj9)]@v?ϦT ~Zn{.eU[!` 7~BVEu'Anl)56 ' #:C P A8UBژwt覒3v*/(̾aS-YR5"Sت)頀$tU<͏lN HXQ׽*~ [*cs^(p-$O#Z0t(iV̪ v.OF۰wB#me8ˊAI*[p}tl8ViÇ;<)4abGJE`/ $aHrĝ*&E}(HF RvLUg`U߱8@Xi>}9jSkxoC Z, 27=HcA5@[0$_lLE+ \P?N\ >baAz&? !X'I~4!GL3ZEKBY\XnEIRDG&z=oc_7{eiᩧXQgH$hܛtkǘ֫EZ2dsp.7q(@pV`bc#'7,NVFч.IQ \_nY啳8`ڑGb{P T;gk(,euTfm(b}y -r'y5viK9.ͲC+2z>>d2xmyr,UJDb9qp]krA4,cT~5֡Æ1ª4ivʉ]aE5łoac~K4ْզ':۫\I$X `\` b;_UaD=YRYid5;YtHwy¦hT]}Z>:(è3Gm_cQY qkhFfmK/Mx$%VkIjsXe:TDi%]sȀt"O1Hy@PڨiXk7@Bkutw\l{ \AM9/S'9765L(k0 ~ZH}(U167X#He\)_ۡlA2,it~fHH]P4Hc{sir@Z׸LCh6B̄(u$u+14ypXŊiSH% "]EZF%(o#'s%qCLaRK]p$ -)aEMOH(Ɖ5 Mȑ,۟V/ؖGY) Hդ\tB('h)U,E=Um\ZRCEFm8XrJAC "C$&xl!Oqsu,^ sU=Pz,1@DQYO)i3(4>9Lm^(gYͺh)]MI=ijZ@ȪwàX- #`A];Ѿ69&T$8FPTt,,>+ yM)P)h{Q#Y˥IB0y] ˉkKR<`6( Ir=+`"`VH:Js=^ߥ)axU`nF3G w6NDŽ.dg[䍁pDw}R4ב bLv {*vwxJ0c5u}..UT7z~nc)R"4ٖDm~lfX]2Xv8rve*U6ٯ`w X;SESX/M L^PA!,JzۭǿGS{Sx51V8Y<#DC3)(ZVKk+ݩ%0>Uo;l1oۄ(x*3x#C"`;_FF!h,ufUw. 0k1,Egպb S7_s|4N 3"Enq̎sZ(n[pkRkJHHGOƜt ̲,KTf0,wOAYAFVdapC-KZflB^Biv}ާ^m쳃x,!ȣ04ؓ*7&1)ОM-)រ(#Hh=+$x m߮6xqřNaQ+.7$"ʑܞYֱe] "2 Cg"Cap ;vć48gYLPuE=6lbxaEB#n7cB lH28[Z,߽kCxQd""ƚmbm;̖=AR/}#aLwu*)l&2m?BҐYNڷ3$5Ӯ˱:>aēLW qg5s]d1$+3z >[A*O|.VpEpA %WDҨ5 ^pk8P2­HW,߈Y7}m+lzAIo.Bv 2! {aƆTgb0|C[)υiI*\Vvy\n6ZslbG<2%zʎw CX_ۭr5 ]- s3𿥏6JHAf "`QF o~O̟ٯwUs) -DѫYb6[c>|&8HPIC-Ut4xEut;caR$J>AI|z/ĔΛ9B- #E 3xU{fre+U٪):LC\Qo7 q$LE[Nd5ԨX qnAadnպ\#d09(eJêg"|yɢeX~>(Vxq"x%w TІ#|sC8ttpv݅t=׿Tt 6-n[12?L[d|ȳ'defPsܹgGMq߂f]ѮҝIg qB13#\U&9UuPwcWeUTї5 ϏK¹$Hj(2qFg|/"3n2+8}%/ pI4-: `u@Hqhx~poP"Foq1{AQ Fq ̓ Z._?xylәMTUkDf&BiU]M|5[ľ6|}p- ļAϗey4CdU"qvyⷁ̬ۙ xf;*Z|2LvF0X틏>'-y9ʮ<򳼧V@ B{zr-{9]Op7 p}95tW45:mF.?i(r$/.B}<㧊swd)ovņ^|=LY5Pאم-.oR"lJ6bʛ~c>mMLq|\?pٞi5yu_N1XYC,6#ʭO.# ,aW6ܟlprC7iWKüZ ʤXjYf"Gn13\ P9tT aa[|Y?fUuv,#EZ8Muߘ@#eOώa󤙤'?O ܮz0.CY0Hh2ae%"{?jt?@cYhYm{9A,-8irV/aC'7K e- vJ"v-n2:߮&\3JW|GʯÆR),nǾȜ3$nd&32=2#X o*?`{BtYQq+y^%Tr'oD5,\3gWK\AeN1;t߯^|?!*L FGQ7GWJAt,Tw?|i6bO/w\A6`+U{T=osƅHJw'H]b2li(2+ juabwxUM3# HT7ج~5O'JMK)‡cnODSêK{arB)C2-퀪ҏ Ft&P:I@"PR<Х:nTT.QlD?t4]5N;: .խw*@D#^ҲSu,醲XķݰwmLʅp5y=cO?|_}4٦_".>Yԃ*@w17|ip\Ϗ3&H!o`-bs=0.!Υkjf}mpu(9>ygü5ƵsNkq>W SG 47 unD> :rÈ3J<\ATpNS ]$uvm[ƥ82Ds>h~#1_!g+ci }q/ʲHk8xpX>z+ 6~0>>Yft6cFcGu :\ "VC~6mp|Wёj{7bUEYX m6lL㑢sǤۦ2w Ng/j<6 <Ɉ\ҿIN.8 ,JY,bX`[ {ϻW& KM Ћ@xQ"F{aQ]Q0AS0RBYl ``hY SQo6BM| )J!#=0CPFXl=R]Bm o{}1Lm0aF4.Z6I#d{sUd*b`y.Fqʋ"HO,"maEioK]pW+MK*.ֶ{䳱zmTSw=4+T\Mqo!jXaIaBr߶cFLHT\LBSBV{ ](]wsmw-pdV^O(IEv[ 'afO3@OJ.{m_ ʎQ4b\ZT%r> !ls"k GffzqQ&?vţ(ŋyjo`o$Q*t ¢y"gBdG 7u{tG&GKFlDRꙍ7kX%,(`lxQ;Iz)jK@{19c [t?L /&#Ҫ6 NQWa僾H@* Bk}坉!A","`ͻ02WAuV NX*V@Xb WDHâSDQ3HJ[M* BZů;f7CXA f$ Vu?)$h;=-cv%MZRpѾvŠ8(+IhHopФũ-l1qP}ȷ`,ίON3# C Xצ!L")+R yb#gWJK Ke3G P0T,d.;G jhW< ESTT[M Nz_;($o'R3,Tm(dȀSzNîsOl!0G1t0)f%#.E탛CPrl~ؔԵaWa ´v} |}8SYil 7 ǵ;Do!il&JL X/qPHKXu= G]l@ήzA zm׶26Pe@EN]w}z'W4eQqjRa-\42oLz )9Z[8iR yՒC}\%#h!"Zs.BMqS*-{sɨn1,!RWPI]`d`a$j MpR)#j `blbl ˽ `qA,Ɋ-^{}p.,0ҙ$lJma{xPdqJ{Ī)JQ7IcXL* \z[SQ(Xf%]vbJPPu!r6م_֔"K-a~]nPaHR)$ĨBTl0X{2wũ *ϡd`,ulFvOkG*qO*w8$^!ͪs?\"TEmGJ⩦a˪ }ߦبeUVٴao|y$99]v]V#lk4O[3F ^^@Ia##+!{a?Ⱥ٬;$9N ٦ghxݏ/{u$<*I906 R\Ftk:^ù\vNZ5M\)5MCd&c;?|)r+k/8-t>qR[К`IՆZcXl_ÖGTi2a(KN{]<1:VbENJԽ,̟]\7fmݰ3 b/J}H,}Bn9+]h:|3BpL]l =SU@SL#V,?|7UQ&>b^>8%r0kB,?\SaU[6{ *UMQHݔh,J<2؂Hkmd {֕{^a^Bdei[jQyS2O꩚SbuV9xD+QS,wXi~T#DP:K݋\zF]l58}zJG-$xa[wre"yg̳i2nvtɮ[SZE#&hc  xm5fvbKa|d$y*u*U2Og(4<κ5b=u"c+PU#HIVXgCG4uZꊒxIJ.[ӷn{eͰn3\΅ %Lb9ZS`vЯCܥ"<D(]L>Rߠrr9$ͩ;n1(Up"9m z=C ;&{53\F5&p=߲ʖ)RbU-ޒQ4KTZw-CM;(t=KP{ jIn6\yGED#eSʍȹկA4M;2>E} "u208b|ZJ:4HZ5\{}#=~ɮu8_,i@uq/je,@kaܴEB$Cwf4Ǧ:20besĀ?Y͊ML5IMDmͮ_-, <E6~sb{0zZD]07>`2Sx@2eWPmܣ6!ewx!i$nmܞSq Y\̵V1t#lq׈)g?=KOG`.:Ϩ18޲^Kij]v[{,揈n\xUsϘyw(2#hZRK2ȥ)8eUP2FK"|`חϢĿ38bOS+JBm mAƒjE5RatԷ|M=s]6)no>AFu*UTEܛ~SGW񲒳͢yfYİOSƫo&XLZWE('k1 SJ:trxIa~aN0līm KF)@'?QeJ&:mS$z˽kcyQo9#l:<add]R(QGK(dH@Xq\XWxF>? lB hB )Dy|6',lͨEth $3QzoҾQ#bl;o `)RD1ErUĕ04 ȡf.Lg{"Q7QK-`䍘:#,р=Dt0ٛ씦.^%CaѨ)VHvf6#˭iYdc&å2,o2I)~tᨄh$ 4Pc6S~Y@*ENR}0—S,q**<m1 TT\fUmj'n9excSʳFV2}*m߾C-By|FI F@ԩacwxn#(1V|Gpq RE#QER=R|ecvPn톑_3MDyed)nWM'sKtYLL GOV2 (:-k;L3,a9{ۦ,m <1,ڪSTs$)L, ?T`ݪ"5㰦HR lkc02JzLa:zQ2(THQeTak W%-%0 2Z=D$4QlmaC8y[Vs+l ) bR#Ԅ ;q>)bbPn_)OJ#{BڴwC-@V_|ZR0VڤF%XD!Αw--k3CU[}z =u1}:Y%(ަRn'/9WR%F%{ %1ӿB#Ek^ f V;"7S6ZV Fو4y Ge$&؀X nXm~Cjuk+ma+?%6`Li2BZU$-`ͪ6Z L|QQ#X#{ 0 bV׾d9ɭ!i h&"܂oG*xQ}E{bi4<"S}$ `]`b]tlӈL*'ch*Z&ujQuWۃnJ CsC ]M#U;]>;):?<:);0H:1nkְ,-Vm,{3b] 2+9KpMI+G$foR1!eb<\%z_ eF5pw|;1V޴"`GLFEJI6u-u=tFJVBwj2YonX"g\ I"/kp$S GyXHScĠ4ͥ}#U+v\BŁ'qѦb^NTy4pXl-pD(@ sBWc[\7 ai 4^4+U~d1rW@\8k%XaD__iM0x F ,>@ƒ=b<Ԧ蛂>b + 4h8$:I,l FVH*AIHVþX<{7MJ[Gԡ?w©%G̾n s&L o 2ES2 BbPmpPAfVuce ԟÃt_42y~tR|1996B_ۮԅ$-$z=px&F"{=G@+U®)Y,IO%i,`{LzS4$*5X2"Ғ)q)&bi$tz@,F0*(3T!X ci4HlذgQ>j~p61WCy2#(2M98+yP渴2Q2bpZH 2GRJ\~rF#b:/RdFS=H0{ WȊSm^N>C@܍UZx¼IkN:~kyKF$'rwǮ^QU:l>n$LP0 qOÚrNvbp./k do Td,^%IÙ Z mu7ؤF،L'u&Ehw1pE牂I7cw]z{`4;F]Qda!/¬Qb \w DEgOկomd\7크rB_̕ 7}wXMps{3ka*č@kr}oS7`6ajXٻ ~ʼnB镞>fOxHQ "u-KZqF2b5j<,"%THMl |c̛2<7f,: HŘ%팎)h}Em#P ƑT 9TX7oTm%Sbtcϧç>(<2xȹVgÙD9WC7HH t{$xWA8j^ ʪV"cS8|0d]YK=#Gb é'r.8̿+VM,14uXX6 1\".@m4X^;6G_{|vEue|̿3[SV%66>}Ƨ;'YיS^#ʺx~I$:^W/%y̚Lڶ_z"+PJijZۏ(| xg[/̂ƵuEWX־Iߵ\u 0_(`qm>k-96SH83"SOXVؖr\Bz>aU<륚buG}㽬xO/DAMđ߰[u p_ vԾ],þ.;b%0'ǧfP7=a5t7qWRx)̫4َa V9#\UMyP]ld^Buׁ8N9MRpD*Y;}tb{WGOQpQSFL[ax ɕu7kxϞZyQ,,$k35kHAw1¿Ty }Q5MTaqۨc.a 1T7h Lq+iUzc_[! |YאW#k3 eiHzN:ؐ-߆~zdmԲyyW$(o:B 5\EId1'+EEJLy' \?9\~"5eC{+# vc- Wjxˀ<ʕ.'.XuF:Í%m$TMiY.BHX5"n0x%;}|Zˑ0?U|GRRQõՖ79ԑ[YX N) {01$|#TVT=V:ޫZui 6BNFEkYqcO ;shytr!iYkub@o|]ܷ 4Ks/Q&UmZka{aR6%7urzo 늊Iy M, | y'ObqЅPE@`nYHqY ~a%dyHD́*/_H@BKٺ}WZ[?3ڬ~Rp@?J-~V6O4ș j}ðLe>O==Eu<`Ve b541u>0"iX">#ƑfÈ\wWXJ,)tyF_LV:ZhiH`UAa`vqo Xغ|9!H7n醺4oUrb\^QZ 67"rR8E!]8s2 $h/zwYiݯL)ԫ f/oO|/'-$ӶQ忖 +-1#Qv{` 5GCml)8wCrQl7'O۽D%Yo{j/Ka"ETi1f b|n{a{XBfcEtܟ,لm1^b =HQN tC] x>C}TM9` N;#dU vg, d$k_ mQDM};}T^GB0mmKޯ+*m奵\`9iU ,Z3sKwU{鹎r#ܖ"1e#\Y/wF#Hh-_(xQ=1붮~釚;%5b*;AzC6d֒]}'w!"hQ_/x!\.&.}.)CPn7dZ\a%ES줃fuzSP D4nv#.Pju f H'q߅\:笜Ϩβ5jL-;ӣw{[lV*{R+QQBu^]lnlp@@:\31 3 ]mjmM-p&bDYOa d|]O#WSCk !3n09npu( E[\aɘi|#)qboӦ[#Jڈ ؕDLR˹?l L 6,G\AZ@Fu7P`+أz}V;4'4S$1 ~LM ry +1 # ÆX@)}<!] *> l"`?TcI捔yg_[b1) ij' WjL"`#SRĮ|3ռ1@R/4Q{jF0"w*KDźn %Y#Anv{ Lntø!"BHҒK*@(,{iI 7p=Qmγ sH)#ٷ=z`$ԇHc;HWNI0(Qͽ߯C+9c%D/9 T2; )a| 3ibέۿrPOpéKI{?\8043 +{+~ef!@ l7.kR}Ao$l (NYY5tdiYGclzRgl3섮D2*J2M/HܖEސ 'پOB)&?YoۭW+HtI$d/tqe[`ub\H B?mǖYBT'M>s3yM߸\ېGtZ3ԏl оT@ݬ@#1&0,/a c&(;-l@Ğyc )$0i$$5FbqH* iܟaO ~@_n\ /q)J1)F7" G&ni~%!` }Jy "yʉm6?j?G3(xk-:Z*He);S&ڮ?jj6jU)e$N{&eL <.I&B-r{uM˄َcso3pIWU]]PZ-6 oGXn43;AiT5L@ RIH. ŎeM^asS#+V.o|*Wei0mcO$@NiGij1fk7ĽOgY\{pyKw lf2։#by\C(PUOC2;L[?lx)}%5#I U7N./}ZYGPn;lz6HrH톫]p君!:~<*F> ;*5,$&ǡ8)y#b5unzL[ l9nQ0 n* GX6q28D)¢2" sCYu/_oĬ*]NY8o1.!FsAd/MH=%GOsUBBʋwUؑʤͯ\<.JHq~&D} \>J(KnM"cn#yCK;ΐ-풧pQT,EV5$,)[ #Ic Xa3;X Ժv߮wyIJu]CŠK}醴f`H`vED3đg餅R LL\( I% D$D!,UlOKޙ$g[d]V7h*%i$bH-qsl= <.Ua1yNس"0$ĦIg%g| >mZI{o~lImdA,j#l9ЂێQ# eq8)E7؎L3}TBe:d*\(mLL<crӳUx66ۮ">7ޛpҒ -ӄqN#i v?COxInA705 3ʱ<~Ø[)q(4%u&@O N&mZ7>y[/K+y SSkm3.A%ǰ=>BMSSQRG$0Zakluc%w,/{ÊSk%{~L4#,-,z`aϹXVXQ&Rw!(B9m2(B$H`RO~o$/QP mÙ XO DmB=C:`5Bl|%S*yW.ZfifRR6CsO\lh[+M6wU o {[,dT[Y qmLkv|}6p'7M[[%.QPS͛HfEuO\ddYspp?x? T9TCOA)cf\p+0~*CuE^cY( b" P|e׃^  |U4TaR0ZpU GE:cY5NrdSæV묝h=V ᬗ2 (2+ #,qaO닝$15relp,O+r'|+ ! #CU1rX飞uuO!e6T7u7UD3"Zu6H2Q7X*M‘&֭4am7'eu(!kpk"5/_|lO &ewty:q%/ETTA4-">b|kuhT$ 76{0(iȗ[pu^7xZ8WG\wL:%Ҍe=09)i,&PyYYUWbdwV-{l=< 8fwPi6aykQ;vm|®0鈆,̚} X>KNZTO.&$k~)/ҩC@лcGqrXjU4p@p%&BQStװǷ| {Jv%.cb$܁0m3L}[C4E$q]>C!7R}r#I,ΦE']WjdaHISdQP ،үiTw LZːPY@ )e=-n™t2}%!vI Iv\ۯFi۝lo!cxHfؖ\-6aEII6T xG"KSʢp&6u}3EHM)lA3Ibnm8p#pK󪵬;TeԀ!wSQ?:={ ܅7 ڼ!E$O!I)w&;S2ERž4eO@06:ɛꥐ5+M#)PRgv{t #r A7,ol(bXG[ju'WK!*$,SlE@di-q}0xXo3Dv?} ;W15T ^iKhP !`R?iQ{© %mW'`-}CJ\Lm]Hm"KrfL+GqC F"(Gܟ#KlSi#+ =bcАVͲXଅn@yK醱eX=ZIi7n't$3|v$hV1b۷B>mK#իK*ZEYJN%-}"M%0Onᰆ I_QF ȅ؏N̽=@ ȿ/qc01܄08 -߾=k BN\}AMkӿ] JhS!Bl{ac\sExe+uvPЙ]A=l%$c!Ibf"U]ѡ 1,v=Sc?0d)܉dƫn4d=;!16l19iv4lC#dpK6E A!^Koi)$!’ v@XYlȺ},?"@yCt{J ]CIm%H8\fHXGeug3Bs=4cIT4Ѐ|4ͥ##ԑ!Pn-k8dԒnZmw3 i؛m֊Y43־A)<L^l w.hC ۠ he`4^m,mR;b򢐥je; X6vWm(v?[`dD cFl +J{#mQ-QIIm S)n=2+0gmvLqF#FX{}z-:/9mV&dYt`킪I$lYVm8o)Z#lEoԨPu#R(AYc,Ԃ6o|?A0!5Mk%y^JgE`IXu?ʴZٔ =X|$t%\%o<Ъ5(7o X#4X YC|]\o~ PS"VMƍ6tJ)ҩ>NC^goԬ.s#?ͪhik- 37ɥtnZ uejfP[1"Q{RΠo5;Sy032j *nTC&;q  v6·\*l+id@ 4rOSv$ oLI}B} RUxѩ>)еzkj#yBf;ਥ%G϶eU-a,@{ }0feU+xk$ܹ=rl{dXi V#KplMpB RR(!]{F -݈pYыIac8Q!]]ػtPNP8n4XkD[ {\zcMQJ'EkZ7QG:$-{؊jd_ ;B#C I= c/ `S'bNaEQ&{ߣ}E1UbUYj \151:+F Ⱦ|M!0 Pt80*foR>IA\ YV J$(bS|»0nva| )|NESk5##r^?mlY #nR }LP FF9 !2$=Vi}:j:僰7v-$Yu(R|f } l3w+@%< {6~y)Ǡ?\ %6^ja* V$\ DzY:˽"*6NmϾ SFYu_@;m \.7)lcEfw# C( fQ(=6Ået67%EQ@toǿ1" 僓qk`m9,r o$Sz񘦍ʒP/l-ͬ+Q0k2 F ? m!B)}~(CMɬɭUP:v./,TzC\|` *2łOҏ$4[l"jDp?R'Vujb% dEٛl9($}q*_7R9% o; FK]CBNJo"aXkU+FQcJY:ߧf?2W6}Ga3Y%"1޹Ŕ`EaN,R' &>3k-$ԯ;|!J6_큗h;C"$1!RFG|JRA K)3gwê,D0=Z"CS,e-f{b4Ypg+Didw=OUSPIAI<_dݜ~}6cujhX#= yձ#<܉c6!pO"HYH]7 wD晴8dRGSt1;ME 5^e.X&E]a|$K[Kgo<&$2]L+ YEwc͖m2BIn.tT9[|Һ=]_.S͑aL^EfO[_6>H(C+Q%\NfTXyWfVaSJMJ}gk Ĺ d,rUTW+Y~{ǹOJ[=TpJ-p,XVkY;f QW3o`=,+O'Ӟ'D؅#ou)GY09[fϛ dHlKZT$㨘ِ4{c<+rⳔɸK2ai]{smI^VK+sk<{;N4_ \V.~}Gi]&'ֲcTn,:"E,۪lRTxir2d0@JZlURD-p-tHdli+߾LQL`:ŅOP0TG5!aNHR@~s RDMP7pqɶ E)&(%>d$EQcQȎ)A +uݾ%D3#RnOp/)dm*%%ړMteIc$mtÊzhu-P}3E1ʉVD+a{w|6) N-ɲH]0E۷l6RE`E2$w{SÄ0wQHQI o}Boo<˧)TR$gj7تd$DHw߷턘%ʪ @clV0VWSIVCIg\ҙ7xw?/Ol^D$L\E G弲C9DZb1+u&'?-NHc\ۄ>"D@-Xta9`ja$7_lA^&yхX߭F}˨>v `+% YkI+0 nHbXI* p9-,v{aFEn:m$۷{mo 6`7Mb zg2;{0W{,$.'̂EFRQm۾#X]rkik*w=YDAm尾.{Z _y ,bKk?#OJ*jYASY L|&`Npuzrҿ(|7 F= 180jZ##,L1k| P&ϓ6ݷ!IfDyJv4Y$t=\\HP+j&a- @XZS# r6bDxe`BJc @9r3nhH[ȼܕ" 5 3-[n 9/:˸cͥs$Txkm{|Yb`n04K,,9\?[jwYSx8d!eѦ]AEa_ejH$ɡ@9}'#3 y@ia~X\c Vcq%[^r%湥^gR(`2D+!3댏Q!n <&?賶txG]Yysp^K-R7úrn"QCvC.:j\i*ݐJ;n=o4c9͵fx6 -~Әꦏb/?&US *_knX##/,nR [OO#{$SO%EGRe\A~VGWr)⨆z^Iƫk_nm$YD,yV(ݟrK[`@߿k`I$,v2 o}$YmLdt9YH(+6mĖ iJk TajjW͏U9#Ve!=ɾ|#$x5SLap*1GS,4nk]LfD]Py"DT{X[clW_+AL%@fDq[l/<#82ux`qD%P%{lN׵KUy)mۭ[܅͹^Zy<)lҩzx_rkjɢj*)$YMVB5M#%l>oF0ŏ"8 Vpu-m.MWFA*H>؃] IPÕW&w&.`UqUC' efu& BF"ů`UGp6=1טEet͛AC™>UVIInV3I[be |c;MSHzDϕBw,CX{(7uÜ|ۆ's||%J=$lF5 }-Ι,e,>O2G`lp/(xx8,,=.zPͪI6Pi,$漌KP0,emH񈠥QJLam\AOR5e}^E=8 mk@_#qI+,$P)%$Qko1 VۂwnL#'Ss3i12m^b(=RUPS\~[Ig*"2{୭[ kti?+yj.q.[`FbZfa )jAacN_, +<{8["GK3;H (O9;/SEQ$P.2lN?\?Jwiڶ8d T@ԌK 3 i8ISy۶!W <gUlckZ1z*YeJALH'0u26hȕL|0i9}:TzIzs/=:=`sT.Rf `)oP24!"Lkw .%͟8맞,%QZHi uv@nqeO0h`@ lOaos͎g?:83-kkjkQ"@Zx"V1XX { iy~ΆxV3 1 G-xf|[tNtB+x36f5k - iSS(&ڞͷk$zEUxdUCo{cyn}]6{UplCCXFoje:ܠ(\,+L3L@Rl4 ض«Dmno^/6,>NM,55ӑ4#`In,Ffh$iN2Wt?{i3Ot?OH[!'TP ӎŀH8Of32[F {u1p'lEuU&7r* :jr}A5np=᪥5AigmNoa<"B]w `N ̤D {n,*:&jbT@1s FZ1HՐo%wX[jytGEx@K3a,r'\IyZI l$$qMOpzjځp^Ǩ'6 6Fm-;%*c06<)J:$niw e$n8X@#tiXdeԵ*Pzu~%CZvc0}MQCF# I@l4zYj"TLڏӦ'E&Tg1@*c0 %I&qCDk$pڪyo,5Xm}\?&JbX #jKȾjm=l-mcica"`M;_r~4 LkXY}1dA ۧ&rnO&pd,[lvۿ\4zCIfUgIzO,H%P$n~|7."Q]/p Hj{3j5Lzw|={ M#()3k7P$[H IFͥf|?O͕_0GZ?Kvy7Ccuסj #ųXtkK1KIF;>c4@N.tO1=<$dQ$r9\=7pU4 ^&#-*<CJtʪ01k`~e%O&)O6VjvY J1 midjtֺ9;BUBk K{7,)%(  f~tn:k:lzb%Kop7F9QYDI'\)j3ʁGl3Zzw ~--A&CkDM ܃ KiLO`Xi#e TX3IV+b@b>c-lp*=Q]) o~t')i.^1C447-S{ h wsݶ`qTiy̏IXXeƲiUZn}RZTBdI7-qÈhfhVF|0D ,:ENX٘TI#( $rb ni,: J á<`.j #dTbUi .70Xٯ!Eld.I3rk[~`Ehdr]DX}oh!p`SIXʻ!;8@07W `G/94_MG>rER4Q +z~pi(H᭑.QnMTTj1qr.\# %bAX@YH,uXtVfդ[ ~lo-_4SYJmD} #u@02;25S\CE$s%p(I;\4 .UFa q6hedyl-vH Q)h#meۮi4 QŘw )b5R$ jAĈ4nn22 ,ǩlGCʼnJA7 1xUC˯yJ L?Y%[nLJPFJKS)\l:l-a7}1ZdPZfMHtȣcq4 o(l7R s*F*70 mLw1qKAU l*A"ʖ>7|a"CqY!GM T9kH'BO{1H(=ZA|K\<$Ȩ!q0V.z^+ZBCG~3<+So831SelTQum?h]tHIP ,EN9#Tf +Gm^Q p) ُ,\.l#Hm@):XῚUX &[8s)$ @ $mm)r-/7`(c\zoc U|AA* |,V\[{b66 ,3 E@)9[jZL"ȍu%׿_b+(m qݘ۩12$kb.MͽH17@}@ V*R6oY66, \8; 6I%8A;anI+!/tStQJ# [7hq?=ܛ텢H6߷!8Clԋ)k=I QPl@K2׹m%n|Lt,R P~Sb/X\l@(e dk|DpA[ǡp7"\bp4Sb+[pG5tuT6 A'')KW#{LHELnY` H0O6 EXlLDU;XA7 aH̊^ֲ\$'WGvqsBPIg7U]!Q]*nsnr¶V!\wۯL7y@PgX)awdC$%P5zp[k'Nv¼jXgLJYK{>\Q't pC:: G0V`b7k~nL;Y=dYe6Vb{[ \܄Q"ŋIK`DH(upC)ƪ5mc`R!+:.w;'i"?BHF~DR F釋. F 2mo9hmHZh1.h**#T%z7hr~ی4r LYHe،Ma(h/!Df_[k#nXA"'WN{K)_ wELd*-{ۿ  RH'W[}\EFVK c, (VpB34VbݭI 5[h߾l%MF%bltܥliAymkv5D*]ۓI$=e{,+zI#R3ʊ̒+B6{SH$bΆ;*$m-͏|5eUX'N~epvo% ,=:3\t ٗ)T@J-.vFV%F+u%YV u8jF݌duCJK"h2ǥpں!LhJ6}5IUډ "V0T{`Z9lH|*0dWEEk.o (f0X][ "MEmOh-f'J8# (V7È8&HءH"hEDhꌶ]|4)m'{aК6Iib$Ukn6㨪)bm=v e2=juNňaM,UDqj/|c/= dZx-S)l댄M#QWK !7*5" wx|ƻ:u#ye^McĞrӲԌ֕H"7Z_)aYeg: b*$ JLq[_0H__ǔFH,6a}!х]%P_ï-RvRw\11CJc3'B6k*đB yB2JF߷oJ5t)Xy`ы=quy>hf 'nCFQ5`zg#&'2H1QMr!sAO . 7Y84mL?:l77"db,B0:IXE5"L uZ=:j,Zt E]okm{}:a̒I企lǦPkOAcJ P!&K#o0x IkhgtOk{_.#MJZ?Hd Dɦ0]ѺXV0K>;`gd76#pT ׮ޯc+fK {Ͻ@YKB1J$a}B!sofc{ߵ%!ZP֣R"2=$\h@.~JfЋa)Gqܜ&qQ{øU*6[4{JXJ4a (TBI,n2u-ᱩGuORFA~؊IȨM 6WVu3- f\t+G LtckNo=bϛ6HPf︵ʲ'I"I!IgbJ1eqeٝVNVDiŤ"HoZBL.\>:K?'%/$k C7ዓ]cuʲ>\fj8!ٔI$}7?|+Ԁ8'pS|l OM:dSDWKۦ&I!V[r;l5!gƥ"-{$sA%=eI^sf2]-aSQ+$&(mcЅ6H$guy@I}2 BX9QbO~߾?CJLb2y@1mV"En-|KW1)V(b:(*;ɸw BA#f5l9J(J*}D }/U@$UH6߶Ff @:T#a*cS%211|NXuJCl0QʂJ"84MM(}EG'SyE1^Q+G4(,롊:z/۾I2SJt |sAH&n=4tc1cԄ.PFsSV&G73e_J1 >!G&dYck[Oɩ7 6S\de&1(Hɑ}:6:&KѩbMq߶ <s>9%q`Wigd1K͙I{8Ac<%|9~Y,J" .V[2Dme/-l8WGsϗsf(ja:`m2˨rX)䢦OJWavѳ vQC]8Wk#*zI \vc{ۦ=U,̨Q鄑*H&TI$ȈK\^O֍R]I$5z'g*?&G . ?5DtբIZ3n}SOZ |4zI%mO{[s+ac_ƑnKKNϝFG߽񫯆|c2LӋszz¹ˣv6PufnS.jI4GnW'3x0MdpJi5-٣F}p\\KJպeQz4xpKCTլ#\ O' Uz~ 59*Rꠏ-OYA8{alxlqɞKZ,7$0VuU`?ӂvPM%K,50t ŀrWhVA{\`Ut~M@5sN㩷cRJ;U-L]rXPUE /(ZRQvsb/FSf1LOK$Z69m 9\"ɽDGGB>{⸵_b`b܁'yRN}ҢH괤R,ΤХ[E Tk8jHP}j(`zԐSW!u'b xe9jXLhbN"NtӆWMw*(82\J4):@,N$_>O YVM"7j 'g ZmkЍHg!;}[a=l4-k y]̎b6\23E5~Z1ăr0%I}6Lh38;&x`byNoYFF_#c⟞y._M)T~;0i` !1@=>* luY䜲጗8c..i)ї]€n}`uDHn/xFE%@{lNĴ|%WeGKѮH&*;#(U?&bz|MUPK0ju @NS J\hBٵ__l{y_**fuDܔS~6ʷ)@Vc;Cuqbe_H +O@#D2U9C.Fl0!4Y5n\^X:b]q@]&@2eS={{a4DO#zAO|$tU "]l4te7o%tw}`a&{uMҞQC4E˖zNuZ%SM ;$_xzv-[HM26&>*x:5aQ2l};\4ŏA$tnղhLk$ci[{$%WJs"Rֻ_k\߆WNʛ00LKFTDGO XwJl#mO%T ^߾ L4|Y&OYĮhyد~0cj$o`77a@. )DVgk=K`UTC Hܠ U}Ď<ʚc2KuPlwc |[w\ΙiORzQWP'YI=S (8%I hAokv$eWEYetZyR8哔?Nboş YV]0,x'/4XVGQI= cluYEKUTЖx8].ڸR&Yg"G2Imoao{_2^S"Q?QJgTB@A-I?L.i2 )hY?N QE=H]AIrH?7JȑAX:ľdCP$ZFS)H%Q6~to#k&4 K|$2) lNNjwxgI(t}LGInO,&w !p3H,^IؘGd~$ W&Q Ϋ_ {^\25@npT1h;)^ hQa&8srG4ac7ȍmy`+ rZ%-G4qܖ(n-_;4ˣXkj'eJjv 8+X. TSIMҩF6= d2|Yn aCKQ%M/ 4@bzps첊*x7eK)pVЈkLna@ߦQZ0D},ae b4*ltۭ)Q$(̷d nUlKvOALR%D~p0‘ydT++v$>DL.)6)KT I,Dab@6bI(DiA'C26kܬNeBjXzċbLY$&e~"@T,׶"IM'l%@,;mF ")@X FU{xF3 p夳k{M)"1a&d2,BE }###15l- fq`,m $zU1!]*@Uk LO%1U~|. eiZV Gh@x\*܁}F-BBΓ,/bOSMXi~D$l˨kU;;3) /b@?0$*. ^|,v'S\\7XˠbZ=~^uкI6kcseBBV8e8e)|8=>ŁKd#nztTF"1*Hخ Ԁ+faauo0HLN#= b#7=m() rHi#`DS]WP' d}nl7H%t Z9Yʂi|MeA}q?_.V,:Za 3. {aĮHԍԟlM#t K(QрWa +ɤ;+v{7"@Vp㧅͝6tMH!+@%EZ_=0c'n?`p( t%oq8q+ddvXP܎aJP$4ћ7Ӿq6m- YXd&?k"Y+ >ߦ$+R{D(ٙ@ F.E##Qek\nMtx뉍0 I#0=oRYĄ.@߭ ZbSYxՔ+1 ŷ0"uBoì{,JIym F*$`V("P#)cĕD0 M8ED$dewA) ,Uz7sQu3;{`p6bn/q%&(FZuIm`LȌY`%H {`db.n~ X(PcvU;Gq\"fx#V#C1@OAH놓+-At7,EB9մgB;7CF2D5\Jf1III)P h.vBi`+==a#:. B6*/|Je ǩnuUlly"q+ "-i`aaeTUUܓ{iq=)N07RZ >KE(RmbI '@SJaq>E`AlXe4N5F.yI P\1exlJ JhepuFG]k8.gY&[N9˘ ,z}I#ė\9>oM]Hcm`~ĖF S-ekE,DNTpjj#W-qqXPn|3ZxKkD " 4<qelɊ?ğ#%:@4>`[7XiC;7ag$?.ٶf=ISE"ofh!k}L6Km ālz$7K\큘|f@Ѥc=x:mUK(af7,Sz݉bY]M, lz? # [釰H鲆 Hpl Pc#F{،U1Z8#Z\.}6-t& F["DhTٽP +r0J(\9n0tTϘpD`ŎbU:P)m0P:o J#eBB-% TY#>`bOcøb8gmTtXe13 `oЃ~Qq)MppJ-;I=2bZݔ6`yѡPCny ߸շd|s$* u"ȖU 9dd `w8Z AzdD!UQkpA"G,3>.l:o)t#t Aq0eMlH tn7Ld5Q_f1QK%׶G'{k'aa`)5+q<7dEZ7 U \m؞<>UYOWٟrV:7Kw@|&mJ؝ 5+$B l6o8tD? tߤuf.AZY7ᗏ8_CEG":+ ;),n:N &6wl?L9d!Imoّت!,n:~yIh]7$T}];լqZ ' kml,ȚQ iO L䡻]0Ugew:vW($dXS̩T"?|"]ٔ7MZjfX"܀> UjR жL~ؔnod'0d4 E۟ MK0mQ.iuq-*JN;oC9]N)M,mQLO0!ʤQy )򅐆$w0ɎM:LQvmaryqZ$~adnwSlI%ʙ]w'ƬǤ !vpTtHP 7TtTŊښIcXFB'ިtbwZVB/udutH$q) <¨yN|{`D4x. u4H LJ\T<`n=)zxZeT5|,*K^WB 7KrF7F Oܮ?CR7Uw cWJ醾|j0!>u eiUqL@}f0\ BZb cht݊X7V1iͭ}SH.lS}aQTYy ؂0q4UVҟ$DZF}tf:1뤬Bm Q Q&N:">o%F=k,V.] ieݭaI$dq+"'$OE"j# 16Z`:WhEP\;_ 1N2yiF'p~$(aXZf-N.,EvQURk\|Bm hX pxȐ.NwLcTҦYiBZGJb> Y!eHv rfdE&9X\" nH0ڲ $ (o|VHFH-$lS+ikh:<'WTD+ΝͿ{D_$܍mPIQ\Ա;VBU{⿔S2J#B{H $wUk2z?>-`E)NokuƑd.)eUSfg4r K97Q~IFlhږGoؗReyM4+]Wz0[P1'shg^IDm-7A|y¢Y[)%9U,utN#&Z-r{X[)<³;-h RU~fHPor1OMo=_l:b0i=lrtnl,srs[娕u"V2%&AoC_dY5oQY[GfZdU۾- $::9jbP q3 $i8t*3zk!Y5̽!~/y1% <[/=]u8Pi"!NJx;yUOx:dC*#Q- |t9 r_-Ve=7Rj0\Q_HmǭNǯ?#Ĺߊaiz|8m-4y=7@J#ai l`"[̆<+iڢ,%O8]T3MR QB\@ԐRӛ rέ},UT‹0C$t{ iu20;n-ʞfYm=LOʰIrzMCTU-Н|bJkVIKG*~؞SOI7'f SsZ*ԪJ$^#{߶IRvL{}zS]? CȉV,|i,KYK) # G_b<f/AQxqCy$6c(m ȩ&3Z^%fTy^0X _)+:хU]CQQ SeVϐ~R-3kP2#ۣe5PI1,D5/w났kpfz/Ď$*}`o,@%S4"4kǤL 3( \}QI&;u7TF5bQԏkÚZzԆO3ɒ+]6-ӭ!%EZEY. mlp娤 %F}L9RnWNh)&XzjXyH@7u\qp J[mAȜ\gL`Z SưzXL`S;\ܔGC%gQy}U}F1E2ND($яfGôPOOKSC4#g(@};cw>u rs`/6, fyn@{|}.ɲ.9˩e,ݔ]tġ8mJK*ȓ gI4sqӶ,Ƚoz uݍ=vaqC A鈈CZ?jU=XxJTwS`v=-(ƱNX긹Œ1iXeW&E~LJ33I=$.{YV߾zs/1UO0D.$J &V `鐭Z*|h`Eε'"8J0RP3$6UJ1V-~ @ 1xRmK`2T e#%B߂c #UU01klG7ƭe [Ĩfi.Oxuy)>cN=ĘM;Xاͥn+z\ψŪ'C5U55aPm30vܖ:woldͻ,d)$k 1z:vY`m qnc# l0|ѨjW|*c]bT+Ǥ3cEETJy):bnoaDn.h u'aLYAiJ"O d$;~ F`X9Hēq,ta}l6hym0j-1KmQGOI&kĀ;rJ)hV?5CBNzn\6s('ƬT {. ȆO1͑oomP:a,lEQؑ#SEQ2?V"q=5u桐h1ѯDn^Hܖ~HYdIYpC8=6PM(`2^vY/GU q*GJbtI*C%9>b)i VרN(Yج{>[CE q-T17i,P-'HJ:EO"*.A -SV3( =9Cn  6X<ҭêfdU1pKc(K@Z~tx1b3l {qgBuuu7P3\fKHD5"nے?/m1N̓䆓_1Bi!Jzħh&X\n}$ntp(tSjܲ<,C[PhRI#23۵JTѵbE9QQuk/ظdeEj"n!29i gd$$rSFJ.,o%diX(&[{`k B[.n,zO f!E Gim[\aȥk3hXnHtCTl#`j>lLЀY"CO P6r+jp/l6):ӖHHXq-%f 4vg:qv)ܩȚ 6iX$F$$b,:{a%% r&6\D.yMqp>h˗i5a61Ii Z60OZ:+ I`-LsƠ*@? 3;m`yHCQ!`sK_DnOLFYNLr4r$zZ?|.)U3[%U,*5Ƴml:YA(XGsNUP1t}z$1s%k.oc+ X]b=kJTh RTbv kS2/u*5iO0ЈddVI&߮46}.b$ J"(,FX,QHmSэ<0jwu05nDѣF$`?vMwEQ!bAb' {"ic~lk)Q`5 _=MрYa~#-j Œ H A0Q9tu\8ux@4g { (!lL#Ks%)r݁Ě <"8/3H j"DR 2(qtAJZ@c,-:@򔅐\]o I#DlH}`8iXŤU 3vlIKmDb5f { }VXItS"ѣ$q@]HS^-pe2-1A-=W%R" Ar74(2ꊇXr}SJ3Ts (#$5;J[gRpFg#̖ͬ[SD7X l: }qEӜǫe㥄JJe[-`ַrlq * V)N\vmܛu 򊟄ysC *[c=1@@ӵǏߞtPF YPkv澃u{4+`7@Sw]1 c }'iY›X?m1$TnFl{c#COQ#|}jJn0 4oK=@ {}|?R$lW8d`7d;@24Ema#4D};5GnYc>5EmW{{!fgTvZVXK;HђO$ScH]T6`t`iw'PcU {`;4AHUs  MZ+j_##j`CUmJD!42|O\*,<_Z;alMD孚H}skÛu 1G]vNФ1#cbZI X>7_&Bo /p{DLdX}\*dat|&ED՘;X|mclMy!B(SLq{PF_ ^%M'OUrK۽)+Ho|e-7eBRV"o; Y܀H)ՠɹPr*YKw5>KnMEIm&L&8( ":l%P}bbn}a.ܔIF`{hX$oIvn~7t`E$komLfJRց#KJXaMUշ[v*+Hֹ[0B>%Ta&BTI0s, Y!äED*U "`p%_22Qi]/$ȣ@PI;C~i2.'Y4?lE\HDd"?H|~W[4@_K|M[]Cq\.YU+',nmSXѲeFn-%bZI ,pĄm}*ZX"iifrĖ6=,_p+uf<p (K(1 Y]AV uIJ M/+6o Sֽ\>m^.D#Y}+ 'Jٍ1눜]Z;b,j$rz\2XLmdE_%=H 'GK,_R\p$dD_ ڤG+PTS\l{wE <' ̺؆RBE$sSN!M]z(l$fdU#T`=x~!I/~MpV}$ "]m{[~j;M`MO84HāÃ\v@iSrUPE8:l0OG!,$sѽ0MdlZq\{"4ţ/X-\^B# &[-fP"n{xx4̑yM)osǃ0Pѫ|4-4n݃%wcXRD0$o{j^ֱQRrrd}%xLcIȠ5m_)뻃v{4zv'Xhe&DDWV|F#nBFt4 d|<\7E 7:MۓnSƦD%lQ1oT  FX#-v'WJ-/\;L3~WklJ!Ff(ԹX׸m T9 )PS!-?Ad)jݚv\26XHu$m@|uj DOR `ZS&=O-jXb\X>xZPэCn HDmcD$lBV Q"IG34|ȲC Ȧ3{wrP{[bO2f,)Pk}o큾e@ʲAF0Te %MiZMz**zӾ jb$Ӧ%1fpKwU*n EB,cCQiB/ʩf_2% !r[NK(u7Qf4kV_Qq&KPׇ1qZ` *W}R6*7ζЋH~]g,(f&Ćq/N'^dTd֎/!f=M1.8%Zhھd"ia{{T`R;H&$YehXfXEOJH<5=ztŹ^.iQ.aw0yxx)tg*AZF>bde9?y\61iGO(5I$ oc:>=j,Of ۢ{℟Z'j**悢j$iIPob}k+ "~E2Aԯt-,I$}Υk}Ϳ\Xxu4o%e2{@7\ضZʾ)ɨAhPO3-jZY)Z\ڔMfQ*"_n %CqlbpNŤZhQ3vS1ݑz\}}~(=MI_Qƙ ||g$⏬ ʈuM#nnoW^nFƜf'Y;hT -IfxagT1g#Rj&x)JKHװ={bKľ=|5dU,1I&[PƱn{=])|6+2֞SvCF>lw6"퉄U<#c`.N߶5O |M6aSQVS2!FC 6]/jrqt"eR8`v&_NJ<ܱj)3dtV^b,@=&OM,);)y]m5c ,UUy4}Ɖ[A2j8rH?j: !$t. }زSfNSS=CU= ̑uv'ko-,tfWY?qNy]S39j"I=]KO~_r.GQFW,:ڤZ祏Ɔu[Wdu|[OVжoSNVHAbpŏ8F+ .iyؓm{kYz4\*zG4>JP zeZHT js Llb$bN,;78#pg^3,+P҈bAQfb~7Wna\#,R5KfkA@̇tv1/xަWO+M]WJQtTm f1>hk"U+u{?fr/'8R@>gnˤغcsW/+7 MG'b5t;uWQ<+b@2ygZg T1ln?LsO(E=m|sM񼡂d?%h)&F祻|p*jLRSs8C;@a DV{0;%ͰnUy$UxԦRl0hUAXd Mۉh)%0ƖL3u k\ ? 2엖լDk3JD:E}by|D[܉h[/Uk,N"1:n;q-X&0oaM~.r0 rK Do $uq E1`.WWv^ťHոUZ_1gX5E}@'_-~TFA/oߩ!sDӋyqk`R""Ya?4̿]nU )ur׵᥂ȪHcwF$A$Dc\Y9QİH7?l+hdnޢFcKI!OO\zI*t6I]X,Xʲ#ZGͪV(?MoEQf2tku0Ri`k/43 ^I)(X SQRΤyl\ hdډSB-DE8L{s%24*Sʐ\^b]Sf$dd㣧X{u;u8Y$OX=HOٿ ZO<-Ȱ 3!mHv}êlr;F1-EI'LQiAc:`d$0Zi&Eŷl#/ mgn\GiO$% ks,0sn#RMu2"RAiԿoS\0K1(䮩$"i0n@oaATM5sѽ<#:s}p0#Y4PIiU;{L%G`ZaΞNpjHjzx`wa-b|r4d8Rڢ8%z SP2G0]D齶'?VA\?{vGe ꨊG%2 ,VliqCk屐3)_.\pUfa_ _0+<1Ĕ*JebC6ޤr|tl_ -v~*1H1%IhBӵaC\IZ53M,"HJ(U{l:b /q-FLyW^Sט޶mLۦ6;'5,ռ;E<0LGPMv,k1PapǟDO:lwY3xjqmCY`&wio0$Hn X QkyJiۨ@Ě ,z|.Vu"S-*:ܝXjIK4:q}8cDZ-|[ʡ`Y"; ~)̳^ђFa놴BD,D<øo›2x̎dLRjicQfM]0E<~ /Wq gY<@ YO{U:LcZӏl9N,5H_9T} UK(mGr->z@vٯm8cN'ƩX'}71e즂g!)5RooOGi|2 aK΅ҜFNo@h z \7v]bIBv`kobV4W*ƚ'Ҳ<)"2$P&|MQgUl ڶv%!I Sof֬E[c# 9_0]u}?l'&3 D7ܜ*Y (I%I][M I)'^Y0:mn,ƺBOATG ВE'P&_=&pD^.wanGS$ң;`P=G[_R'Dr%ccYmm-;+ǩ {~}ЌZOI,[xjYyHH3CFE֫s`.E\ B-fd[XNћ^͐'4NYkǬ  L02(o0 /_{32$1tn dQuR= /QD{y M tmW;NDO}"j.:A۩5NQGs/K.z{yQb_,/'.o-=f),RK؎8:])8e!uw6@^6v  Ģʩy@r{a<# ð%c[c'ak\]mӨy)^U#X4^P2f=VLp`HoIhL YQV،0(q˵aI heVT~\*1M$eB@Y{w C%+OJUfTԾlvP;`RwO@T)6V!~J}:鸞l~P'e31s}(v.}4 L@Xc43$ ;Vl]MmNZ& =:⨎UxZV:MvpVi{."C-܅8TyBJijWh*^f7݇'ĮLeV_kۿ\61thcmE儒Y/#!#b$AS' jbcq0_TmsX"фD@ʵOaoHѝgنls '%Sh{h%![ooXPa${}X^S{->MUQ#$/Byces*EŎ& QO!H܍K!k([Pu-l'! mGl9+!(~$& @&ݷ*n.A9{ X}XcrBKSd2j4ث[Q E.!{@ }Ao O$%oTq( *QfY=qo: Fʬf߸'5D[(vKR?Ej[*Td`k^{`\KR_CC#&]v:TUp#C*)"u]%H;2=0ΡLXMR.>@=ͫD.r^π8G:LKKSf".:@ԵIUv-LPWnoquNrUMWX&M^ ;2 Q:檟H$G;l3gQ1wI$1; }}97zP$hN8y<08id.V]+"̸6 /Ћ7*> >Y<D*O;@)[S ]XzA{254HWkƠ0>&~ 2(Ta(R5IMȶ)&O?:r 2eSBeq>fzw} cԶ]p4IҥaV2n@Ilk&g3G*1d \345 mX7x4M\A;EiR-6-'GҬxwgیi"RU=wfp?64*5)$7Gv+FHʪ[P*qLlU]NU/9aI'9=%4 yh+ ʊ YNlJB\X9?\G 9{MwL[t7t}BCpۥa8iBʌR$ /u%fy) ;L5y|P~a'enCT "i(e%{Όf^Bvu5<d6 =M<@F5c TcQ֏Qԯa98.˦ U19H6'dmWn{cg,K˭FоK $s|3@e (cJn8Euu7%b EWL@7*,|J[B<9i**,VNC.Jdl`<.#R|?Ҫ$ZX1"lq]z[fYeW11 I 7P~n~^4iȴѩ>w{۷k%_dje]$nr?1K_ R;~S;־8xU-?$J qpCea!k7߶&+S$t9iXDsϲY?SW$H\$ ^y)Zƀ~Y=}yJ R*ʘ8A* $ w$xix'RK7'QPqkt7Tbϩ}u5ZC<ĥvPvk(8h?ύLayM9(ɕŬo}i]ɮ.=Ab!nUw#TNE., \|)&~7i&*E*8s 5Ϩ#o/<֚THPvphan]gH~G8k_CyLb2X~=J0Q<_ۦ8(^3OO[N%m+23+oȼoᚙ*rn{qUPe4O,gZBG[X[>kH37R N%dP`k*:uy 櫠1\vT|EtY?UѾc͎8@!xEֶ#X.oөs۞9T k)W?"W}$<)󼶡nEzz`,![ę< VM2Ur.`z8sߞp==?7 2ηY?66nvs>͹] G9)H>7{q|LĤY~ ÅO|JxA+ǚ!KÂoK v \?,qQxHѴlށ${cyLFC<`rYL,K5oSS Kf騷k6[=3ϔyS3JAw9ʾ9fT9aGx$1P.I$_eeh,.wxZrdqS5}zqbl*G TkmE2cv?+*o*_-Tu]%&tobo{w'ǣQNǬVRI@M4\5<7Al,>$Zzg]IS$jGCKX?H+dֺ7wB&xZ6 >K{z=س98j382.Uм,rʬė]'pqͽwr6BLs8:n %CW"U U7|J9 DFR蚃@э<ǔ9HeV)VC#$lʦ io߶-uLSAML0UUf:ڄ`%1ϭ|Uutֲ,A%<' 2gbMi:yy|H ̛*0 |KN|L8{YSer:GYN _ߦ4BA%4fnm@"'oRIO!2ӫBX}p2V^e4{sY7hʓ0z-sZ8( zw=qD@Z?MP*e0`{K)嘨Fv ᏿bDꀄo4MQl ܥϢ!6j<[Xe#U&# Q{Վ).xx沆JC2n,|i%+ C]*kHQ`0l(E-DQI jllG퉐9q" le\e7wMSY%mT c Kl*⾾UeM-$P*׹"2I76Ƨ =:4tqMu%M.^+i.bS:Z9- iVew(xȫIܦAcƮUsAN:o}|*/.K$Vy}ĕ~'*cn|Z@#z-ߩmmbI2쩹)NZTo}~ƾZ)aR ۦ{}*935( ik~bOG)1w+>2/oֆzngGG;ĴՊв;Λ`A߈W~WngreX#e};;\kgyAa/$s H#*c 5h2Y~5/+68w8**(`3e(FŊ_W̧j|uf%L- +粖xήQ񊴴4Eo4b-kߦGu.k2|A XXlǏ#ykqd NjZ27!~-oTC68W#7uW!H#;חQ-@>]B.q{6\O)xjvY!xaVWGnoO3ML^-՜49x:6pK#* b_U4PqAEc3̐$,Or/ŮH 7lHҒ:*z{mؕ 6) ET^#y_,|(cRijbf^oZx`G6Rʔԛ}!6Ӿ1-15Q P=dY)dSNEt1yCR :!o^v3+~MWISNE1(xiO1jCJw[tŸ9=T+-ByƍP|p2ibQIX%ؔz u,OF \v*9;IzPX\Rw$ mHlG5ौZ*Kz O3Ʈd &oWp1a1 >dvGCK%8vt@4?DTeIu ej*.w|W\)MgOL|a)$i $zgjkd:Vo,[U: l56s?Z+/5E!R27f :-B4*-m*mmR×3ᴄ^cO=4QFjN?!> }aOUixVuyo]Qq5]%*w'J*Ű6+iH2 B+mlK*t`w&V]=NֶkJX#<60MY=ӚjOrjHjVHd&*T"r=/fK.Xs LH̙K u`4^Hlcul5e*IXBn\-Ije̤+` $~ۀ6W?Lʏ*`Z6=e*.7Q=p_07URb vrlG@,- 6slN? :(Ck%Y[GWQSpq8S1›`v=Z=4ŔTWerФ71CަE2$QRTK8ygԤߠ!vt؄ټY=/2BSJ{7v!6ֲ1e)cSgUbSěkmR3VjZyn4?F6EU8r eN߱k+i[IVT:޲^U'4o.VHVR8߈E8o9Yh5TJ)F>aom ;f0PTPCTAc{"],TʪD,ָb}"vAUmWA_dSCZJtU>07$!؞Tⱨ(cEi䬥d {1lXzX)i #; mJeZzKs/4zFt=Y)WښT[d{i1Kq|S0J)rj nr*Yhi&9ʗ퉴T~WR#p{qvcmǃcbfTn :O TK^cY>sKIAUDM->]I,UMse=E\5Dժz/Y`l$H@`:Iay}H@1~*#5u=T&Du7Ql^8Yq GW6X9[C%.etK#5#`:^筰8VspǗ T'aqc4pB;wsEnY|F]AbB>$yƕO S f9]AcPeE&[[\]?\}_*| 9d^g.YYJ;8][ ?x}Xפ܅'ڊ-3LkBFWvӲ{!x[yEeLK-dBUWӤocQᗩr7Y S=6}# &֍}V(k! k 6+撗&}|UWc*-MLL'9q[G]d̕ǘGߖenm|dM_1`otԋo{<)e4ԥ%;pRYOo&x“"ƢrZ %w%e(*A[C9v/Ӻ71zZǽVoG XOb[UR,G,rfu,soxcL4+_R-C.J`?lI6'2z/cLղ[H{nv;z1 u17ETN\ޕO)P#I7-}_R-#f0J)$ڴ8tz rݒujdye7JzzY+W1hծnVæ6_YfYF5vo IdQthv-:Ţ% ѵN`bZ#ʩ=l-UG4cCk3Ejeg0.69W$' U,U**%zTec0QM>:$etSW,jҙ97[ 6< ,?DѩҞai$q!Ero퉝My4; Kj!l+ ]%b帜p;-V jW ,'I4UK&pxWZe<#)tfEf$닓Agyj%[XkM:`t7_ %hVG* Di㊲4/ue } ٥caD$6,ZxPWӳPÞA$"T䪁] _٤/QO!,koLd['K֖*Y}!nvqQ覢9 GX-0΃)yeeMEzEzv`@qsm=73gIMd1Gj*&hZŀǨkN&VS$b |+t{}>1-S1!".*^ocć"ꦉ#U$Gnv#~i'e%W9ҪIOn/tfa[(3ezZT6-ΐǥ1diPCW?Y>IP2 KWi-kn[>9!^* ;MNw|CIEb9Ѭ^awy .   3}}:gPpm OsHyhY͌u;c_?YQ1CSDGI4 V+S\tG*3 QSeR Y*aGet *N6o)'2 y5C]1U ZyQ2դ$Dm5~&rc#^%4M$қlMIoEŷ1z'7g_2LCr,:at_?E. I+d%Dk"b`+`vj990c1hs[cZ]| ռ/4i5/x%R>jQ4%N[8o|c% 7)af-T1Jn;iK!j.F5[`_ONԓO/yEjJh;joUl-v/wR_pFVz)A!*u6[$3T9WXtj$0B+.5zGRO5$7I#8R~<+<ףHQ؛t^<@E0j)AIx~)Ortg^֟gT[G2xMoͩTJ*1mƸOSPyLbOSG" A{/|H+- *<Ś8aYY2=>YPK`,.7tDy|ofE? YiS<KMs)rd1V`6ƭsl0qL]R+Ǜ2mwA%s4_KK{(GIJvdhmݩ& X#}╤i1Wkvۮ5WEviQWGI]y0, UGi 66'lLk~.M̊2(yi9I6k">T4!&7ۭGM'h-poȫBVBb#{cTfҾSG2H}1QW_[?I72flV0R[lZ y'Ό-D]22%U>P'̂q蝊ٺ5i|`MKfus9)3Õȸkl@ہ[8x3ỴxԻpU\7DXBnLҶԾz3XiQ} ژDqVs_ςLn/͍MVcd$RhؐfQoq>mYiD<>un/Ӯ t6Bˈ D<-C<2:GJZ׿q w]qސ޲@4;uaMc@e+u%]TA;}{bmEuH*"Yf*|S5mVL1)h{uJd?K4SyST.u[$}=em`@ )ڟDŽlTq:9|y-k鵎;.f2<(k5QʮvD-T 9 Hbz}6$~UWz2γN4"5u ݌]*SG1a<_TEJ1qkȲnz_`ç8@( eqtk-5T mpAq6T*ho-cƒ2(&o?Dje*Wald E1Vg59bAƟ[Y> G(:g$;FV:V‰E]_Ifv8.cAHj*3 /;җ|jys,PEIOQ ,H4f$m\j/;e-~Y<2C氭EZ/u{ #-\3Js۷uE7<0l̑N}|.qCm&摴O=~!)*%\ӧ^I@T\lQ>3HL}W=yQ~?_ձZo/be5Z=Si2|b3T]CasHZ‚("5"%͆~m#3[Y)WI)>ju&2wͼVب+KE#^A bnS[;[,v$9RAK'A[9'\buX7ӮCΎVTQbu,affEkv>m̮T5mU gsyؒXb}▨COq Q1و' GE#u9#2XRs˕NԜs8֟+nۏ܏F1wSsP}V!5hyq9|;yfSklF[abmm4y%eUcw]VwŸIXڧb|H * 7Ӆ*;)qr@)IzSu ؍W7 H&*TKɨl?^LK=dXo$U!wG\3Zy N56:]GS'5)#;rـ>q%o,. &7]L.*%Z/#l\O!ĂW}0|dRI|I 242cYoYSt{*>zw_/(G$Blt^lp޷o捩qvRc=@;aqa8c㬍dv-LJZh"|*;ů&Qp?BtW؅Q53ffi[`: 5Jܳyi##ܛ&hW)nϷ}nlYAӜZBMR!tW!m3$H+zP.]w@5l7.RP]-p$^%O^bgkB{ fBc )UUC^|d|7Et?KTU<=t&ފg-[kLI>>-*f0ѿ RB¼9w!l,I@4QY]J̎6ۨ]֥@q 2 'Lk{|c&OvYù[«^% .8j Hl~$9ex:;SM/ SY$_v?er4p ;0b}%tSAZax].(][ҦRkA4%,9wS `jp=q˙=T<٬#]ĺWM{18Dq#kb_I ]1C$nVQo|Y+=YJ{-ůc?=DY-:LY49847o.j'fQEI#s|k=XC:Kpn 7b?yB8Q[_}e/fٴ^[cUM$^ y/9b bl-:y̯攰g07XiLB5._so$3y zUXcB^Uu(!nA +tRS?eyx RH8 ^9T2<9f sPzab- Nd4/0+DP k=vŃ1^c 5$d Y@-ֹʎ-km>96ph0-6rMUTgq?Y7Q˟-6k^;M:jb|QojfWO/0xIcYK+ Xbqi*rO$*KY0+#mŇm~p"|1nFknbWفxĒ#In=;b%d8›)Z)_QP?Z)"zH=uZ/s7؃QmphC}7U}1oY#YR(Q#m1&;E5T]@ľ`gՃhJp4-ev#ʸ3Lʂo."Ǩ?}QUU*)&h[Btu(|ꚉo]+q{<;(Ǻ~/kpO b"leyNl=4bɦ+Kt'~ئ51"Vg&L:#F)iZahݕ\s>aY3 CK};lOcW"R,N 1V/!Dku3V8XQQpQ}ŬWf( x;e4$ILJ/ p0j!iMTnTኴDEupO]ի$$)X6ǥ!t:Ӽc+hdUødzeդqܵ̕-TӵDqw{"KNd7ڬpQx(d 5T51ĦDB!&\9Wm-I<.sKIe""؛P”hcv`5bp7!5 R$DY @ ^*P=BFos17J\tA箲~؅>1IdRڪfB 􆑀=T 0Sd}6 n|2'I +lO8⒨Ir>KA|[e,éd|ƻm!v뉾eXT=!'W@{ty]B#XՒFHjV)c$r?9k1c.% v xSK a,f Lm3fGZU[mo/Q@zc!kt$bͩ%HЩOVPu8,tcVRKP55z*t{2ì|x)3਱M]aqE;O DNƗp[Qkyq\5#Y{#a\,8(Vn@Lx3d O.&E}a-U5E\ryMT(& '3QϮ;#b4:'(DZ̦qS4&G¾dNJoN$IKUT%D`2{vo|ҁPCOOw:p<–XQy_:t:A?ϾM;KqSe*"b$ .;QV"E'өsw?KHM2{ 3MdӬrŁ;a$&c47;dVv*SSD@a,i 8fP_~fՙUh[*ӱdB U+QN<,<{k-ӭT:T2֞y\2i@NzmFc1(ɑg5i9;pΣJ&{4خatIZ)cMeoz_e^s&sSUWDԤIx0n;ǵcuʙLԴhѪqkE^lʺ Xz`Ȓc`LsSrn}e9ea kTk{, s(mʘpl/<լ4P[:pzd1SӉ-fBo/殸#$ W%S)jCM=a*yofM?CbƒAċ{apYjԊICyJop0ZJ@S"+{Y[UX y*arok-}}'> uER3SĖyZUQ놵U=:4L .I&v=p$͒*w1Lrn@P-$aERØUIE:AITл>a{džD~.w)RL|de-u>ԯ-rJiYB]6=v5ʲ)2ڹ&bU.; wl Xʍ\ RT$Sc!nG-w!gS#=awn_v爠(jND8T,fkzxؓ , L&ch?<1ty&OQ!֑e?A \~ !ܾJLΫ=<$?!oZU-Bȶh=x&WiJy8&b\8 #t&Qu|/P[ JSG*_ɲ IU$TyJ5XA ʞdGNm7ę)N"*F=QQoWprZeiS s8r L aTʬlM}C,ZyRA.@ {'Uճ/v"m}V~M|1˩g:g~M,\v6=OrmP4YK3VGX,:X]7GB RxU͊6ԧKi.YoVPŌLZjrTSbX: 9G͙mc/SHe,t{`mιU2z+f=DuGA8"OV}l>"A j$Uܟ~tr+\TOAӓH8chV۶qVG3!hi93O%BSSIق )-0 &^{,r-L)2e6O]=r5$/(e': d.?MA, w*| j#(2)HGgkn}ͭ\oe;S~A-D6+u,'H78U5kno~닷[O%^[ŹjyEu6 }? yJkURPF[WOQ=:/qAYVj|{{i1MbfkŨ7Uj8!3덇 M}?-3(Ү2:$@`˯aL\Ꟃl VX*':$>v|BwUrvT4}ԕG4m"eꆏI+*(igC%<چٯ' L`LY*Z#7 {um>e-yBԅX4,I=تԐǴyT r{n 6|~ea $4s[vGKPSSš\=4 ŷIj%ډ󌇡/7|YL>f˖EJUnwtGMs ~=5,K n ;uTp4H>[F#I29銬u- 2 7 ܵqrk yފ; V&iU4JypV 1WHy|4BjØaR)*k튈|<&<|ࣆ1@ڕA꾡~ 6ÎQۇJᶦ (H Z\ z A,Lr 6[us 0=>]"p\j(#!Wqt||7gc>W{)k0 n4wBkYqWΒCKyt ! {a]h<5#0V1?lv<3eR3h꼶9*A`XX0KFI[@(s.MpE};STˑaD|P`:Пs"0;l`Xef9&&5~ث߅x55Y~[_E.m wﲓ7%9E4DjsI [Q%I'{߹GOtj碍ҙ3L9j$nm#&r E|%f[#Դ{;,:vĘ+TZdgm%Z30-؏>_w>U]ˮB S-'7V ùXC\@6H%[!ތG{ zM\1MI#%aadkߦo*JnJGOٔt꺢`u;?߄ΨIy%5>f¦. i"|eKE-t4\h+5aS\n. ]swtPex*喱2$`tĢ, NƸzwQ1L|&83,ChEMQ u ) tmhzh 1˨_. Iؒ$obZ%#eEm/f%@,E mМ/4~`ԻΜ%Ć6$HcPRGC-. rMI ,*K#oͺ2 rY!̠1~I,H Em[i,r8E4dž+օdJY%u  a/\̨e&^"L,GH$O|w%&GYO>Y5T|QÖBX P&w~taZ,=@g,$`;Zn:5y7hD]u| dljaSm*zM#rߙkeٽPhrc#1%s>3)3Ù|woSs0 w&% M CFrn~AIQΒf|_ \-yƦIˠ8vh5jl bak%UvOpVy_20ʖm>g)8.Y*":_ T $rxx~ey2f4"1P nl_ONu("˂98#vyHЪma`@?+H~%kߗ5U9DYNbQ{`QU=[;^L#o+\Zt PDvWMG Jl-X}P9r}q)ji"( }_`|QQkuOĎyVd QVjU}^ꥶ}K,FQnt6Qxw9|3"GY Kw[a4, ->]e q]G#a6p%A6_۸ ! 㑖R:_f#ijco°m-+{ttz݈HNa6KSKEQ_MQPNU( ߖ^[\-?xښI9Mu3[FEdw1&0vÜ?E@"!l@`GsKUC^rcr?t ?ZjmJ;) #ty4tg#z X3 [} Igtey]S֊O&WF[xk#f RuÚ\dbX =mڟ6^"ւL߉E lrib*ڕHiuޝ՟LԹ M% 4UD {};:JH [~[lG_ $AGL  ߮*k:tF-O'r sƇ)a~/*(eCIB[ ÇoP*:߭%Gƥgr>sS iRTJY#3*ָ=8="9a^bu:T CsplIս:j%UvxHKٻ?|Ijr5Z=ؔcVՌ?gƍK<7x_ɳf#3:j"G/F˺ǨM2[~l>_z8-t۰23BԳ@]8[J0Ix)*iL5I k\ymn i\e B/|=0A晪<_vÙ!+DL *+ayVM22X|LxW'*̓LSCM1i$܌AU]&ANr7Yōq-!v~x:1SW4 X5]~M'gyzcrB X[jWaHR($obdj&ʍv=za8Y Hv#zfU,i2b/gg0B;Hk {tS#!͔P`?mQ@"/u]^,+1yx!`&c[[J % a$aߦ OfӉa[=LPWNd>aE[ a11AxIbM[bb2G-q R۾߯\UTńHR*YM4U Y=fWku?|. K hƒǷKRIṞB XҥǓKrarqlTQ=QM㫑 XҾ۹٬ist~]~O]VO=COԕ0kX,irH>Ȩ`p)^k-k"͚#4.$ /u|t³R$y#FH-{p$T~J'J)HDIlYK.$4pf4]ܕM*PbId@pէ8qNZbpp4@$X0˸$ kpnK.4BMpTJ{3r|RJY挰];lE7,cBUT'KـK=,s@9q-o6=P a$2 [{_0f˧tF*!%>> |`^Y={7ik#Y20Q`0~mph9 F=:=}VdUzC"ӘI# SK",$Ewy%S~&alɊT= 6M 09qu­Uqet/XG W~;__ˬj3hFY cv}č+*+2OUQ3IM=U6i_"X( qbar My*Fߵy2U0H? U+RJċj,ʲyE'^്n3# Uw!ji裟)馐,BviI<2j2\a}+qur]\FCI`uq/n'QQVS/'+,%NlNsvWB3xf?ɜm74i'p;b%̫D K81/؍o[7+J@al@0+*$IV$Q&Ď /o+el)0=>WWOF6Yj8+&(=z jhuY]X׭jdURQ&>퇫VSIRMPFÄeB !<^ܪZEI6i-Zk4w%$mqĊ*9%yZjeSm{a㙣PLZkohb -lg,r9/ -CCDN0yQX՞.qtxr@ 1qcB&4sD5?:qVT}8ER].V7"Z& L>])=~k>EX $H!J@p~RY$WWTQ>vl20lq > ;&f$o)7i~߶VBgecUK'Kǣ0(j)JӶBT3YD^>)e3ɣ\Tie> FR@[)*be'}f5#_WӉ Ru_cӨXwWS&oVz[;É~rPo؛efeZ2jAF)v#n߂8eBEf܁nĸ x&}u/Uynv@ $-u׽RSqGS7%#D@ mӮQs'0i3&2 W`QFg (\67SJ+c*&1vA$~ ")˘o&67q}L332ANNK7ӧMӿ2ܬ.q_Mulh.u'Y1nOLB=;?l X !I/Uᯞ\Y@׉,*t&aMf7뉧xB)2TS.-fU2@ AڔXw6Ƶ[|f|BR!!֖H$idv1OD|*| ōs:m7SCM7"Kb_1PlRUfjqG>(@`yKҀ`/}pv#D,нOxE5SS֘,bC-ηGJ +Cz3S3F.7o,X#l?0`O /S_Kyt3jia({I&%?x׃)儏UUU )[%dk(6&8)~ 5R3Ox('[ՠ -[gKFJgh +SRa(8;5L*бkt{/ᦃ2)j8m}'c*6M‚jD1N",WM~n#V5fed\)׹?!,sTsHL)K،n)+3LΧt2\`FA!zMY/Ys>1}QU5@H %7 ܞؤ^[s@ CƞZEMfBvv6=Y,r,Nei=L<=3Wż3,fȚY/<3Zzi$c\æPγW=OI  f{Apxa,ZT I$R*i͗Ҥ*pN2ZRA;Xs.>5<~61&jګ Hi]$j[1 ͇\G9Mih\C+enigIYwdW7Qgʣ .@=irK.!x$][ߦ+*πNEU_MQq5$3$ Me.o 7Pu0.4Pq+3 vjhYrDhE| SDSKٔZ1`zs~a[#SRq&zHʷMzvkmXueFޫFolL)4TK< U(Po}VG W__SSoeِebHӨ%-}޸>"34R$XY܂ ҋrmG֌1%rUKIG=Txmj7OqS 㔛F㲌1*ʚ8gS)i||$|MOdsz&vb6دfI #`w 9rZu e(E۷ohSFjdD5/{Mi uSÝUS6a;GbO #Mq[p?oLjt-DqSUbPŖGHE; +OZpy6."hh]Ỳn?1v9-mKS|<(SlHZ@JLү =!~Xn_Eˁl MfLA sʎDIłiѸ\ euR-=*yb#o0*LJeHAk!Slz|}8gᾛ&/9oje:gbfb~+%xټ3YN{{TGK `M)Zmt#/XJQC#T#_>oW/z*&niIf%Vhݎ $o|VT՚SeOBRMN%{\ϔ_ n@e ^wIOӬ-ddK56@샑\H闅⣀uQ}Kb"R? 5$RB||9Ĺ|5>|Eɻi,uo/<8vS]K[kY<$z qWÓ+N]/JXXaj{*Wpn(iS#T HۧMb qJǎOssspz[-ʌz߰qnbMMcW绲_F}u!)J%;G@F7e^E?'gIE[O= ,M 9J@4ĸnb_~"k*1JzY.̿@7"djX2JX:@8r+f俕[ψp^ m#x)ibWp5 Yv rKSSSVr[fAOKu uehp/Ce&RIN} 7I/85*ˣW?g,#Tv8_j>SqU4QΩ;umG:Z~9\.ncpwq&oW#5\z.S!OCH_Idb}x#GqASCgTX}:Am|?8"`,Hc2ֶ]--7Hb/;cj8%c k8 Z2uN' y&MQ"3DMmvE =Ϳ+bE1b.ۛ* )77eQSeA.maI:,96PV$͕BA(*;v0cz2c 0P}/& 5*<]βQùm46l'{^-Ӷ5 ƶsLIekhՃ(#qrd TVY,ph1ZW ؏,+,+BKFKpR&sb"sLPe.ii䴇NSn68`UD}AA*ƥPz,wr-tӇ 6h>&*WO孡|V 0VT߹MoyAno\OS._/ya>|-MbSǭnIE|NH}aHQ# ~arO6\'QeG#T[3z 7km=⇛-0 $iIU +bŭbnOӯi|ʇ!EǘnT ORs{B" =TbFT`*I{kH8hTQ[E&K\\u,i'cB7sW;)I3A>q'h`o':8g4T+L.V$г8``N bly9SFiLba5ޝC[tX4+M"QQ^ gpه[g)4F;.k>k;Sʧ}Hcv~ܙ>jZ8"R)qCfbjOCFݰH0#Ԏ6Ă}:=5.Mz-&d -5m5Ta Tf(Ackؑ})߁Oc+&d^YBmb/zh扙`؁*C*jK-o_5?4Dz;ZA4_ ͝qɍ]3&WU6$mZg(Sc(UT yotnqjd{7r~,0I7 zOgQ3H#EmG[.iQ:LXNÕxL3W.aLfK3&Hx$bR$Mn.}>M^62 [G AirZX՛P >]6vo:21N4Sq EI (t8ݘVv !]r1цw[16JoJ!-(vaU]57T%\^Ϝ tn,v튙 ^& ,HQ`76cc߮78hGSr\ ƖxuT#qk|9ffRɈ i/*x&Zʈ1yE+ar IHyܿ榞ȟ>FMkl:=ՀZBљ[O@?׾ B'+"4H}l2>W]U" ƴ-XAz-J>T:zgH\nf7][L)g(2:))YnΠo [|lP/b/{ 饆V(PEq &~g) s,V#b79*$-&CA b=8m;.nHeC4Q iċ^|gPimR/$K/\5ь2Lʍa=?ç=M?%x<;. Ku'K^Ii? 2,]e*^1z!@'ӷ{fLRB-#ouh1/ lE|ZWp]u4Z`#,ۑw)&:\ r "yhf Y޳귶 K yYc '>BA"57,x28!1,4K.[nQe.S h9&GvB-"R\+L7%@4 1KԧM+ M᫒Tm(\ #; \|N@rJ89{ ÔBUBcmn}yiDdF.Yvc8C9A%sah"Džj'ycҙ+bR5DD Ӥm[JpOO0W((vظ4+N'rX?G鉒SIK an|G3O1M-T,•4XZU(Ii,(FB)Ģݔm銹Y"HU⸈t-& "B@{osl8P=|Vj\KpZh#zOť"L_QPmⶨɲBI>kt=juD a~<|  mE%K )t0-9%EWhHIz. lB ؚBQ+&aM0ZilpxjvX@ռlj&̞l殌S˘KN.z^|\*l/ jj:`c#Fba',X@iH-C+0:ЛMeaQ hX+6iz_!LJ$zlM]5-b;m.ŽdE2 k$Pұ_xo18%n=2:>P3)jYT0H  ˲2DX؜H){,_1s6D ƌ7m'rfXl3MZ`.cL{Rm†9^k!&XV(- #~FUucx#fkvlFb$QzbLQ=EθKzfM- N>O D_̦]mbdLH =="cI8f O}gSEdEG 򲟜 p3*GGW>2 L q_&WxՁk SƱ7Q7J.6uKhcaSpz VWRV]^~PaR/1PXo釱H[q3D.|b?!Ui 32} 4L 5%nmn\Hnp6AI;O"6:N PE5Pȫ)dv -6KI"!+*!2+d#u=pjQ! .%v""h{nR 0*HvDi2J .K;bP6as&ډPf$J ҤcT$+w&eG \nX@J"6t\ '1,cSwn :N^idh{tPc3 "@foR n~j*#GBֱ G.:n3 B!YCc_1K5@,>XHd3΢u!'U4YqD[LM_tP8Ra+5zFN""",VU[u'tư$*N(KƠ퉂J4ɚB4ƫ{t_ GYGO%Hj7GwWh%\a@%b6mqUӪuKYMy2J4.\D'DU.;'&t0O)$S2FC-F&0D,r Ћ}/ Yΐ#lHkQi*a}ocogEAy9SŸ-^ ߇,BnÊy]# v mX<,5kZ!=:1T~%A{|o_%GdS`n-?;$`{=clB Fc`T YHQKH,+DZnqsFyO$`P܋AS,WSnq{ͷJC,5>JA9I]}3"?Cc؂DWEB8MYOz*qPvP~Z=gX-OSo¿ˡx5u.HZ[ۦ=bC s0 S+3Z`QF$G_=`썚@Ij*G ~p5ARb^ck- pmN1G,a:.($+&bw8=[СŹOi,f$&jF'J)cI3Y;nDZ69m%Oy>hRdxRW5w䷇>oPVq$,T՚&j|NdEO[c5WRXG9lΏ)m_M!᭤ⓄY'58Ct;7MKA٦a, gy4 ~7c4 ÈVHk*#&oV4ٴZ$R= m8~_QJ9Mfii}6ʼnӦ=cCŁyҸ:fӞ=)*8%)GφGIf5GcD&ϛВIMc,i361vY>ܪ{+|0ns-"|2_7 E{/|1[C9l"'/@#ηB:ߦ=c5Ҵjy;&I,|LⲒ^j `T0` : qǗ=0fwyBY%Xď{ǰ9gړn0;9{{sG=GJ8n(`б,&4𿂜#Mԭ$U,G(c -u qDZcx#0!Q&]_JERfs 8e&Y$YV`mkx7!Gu|Usƾ5iD{.\^=ch8mL䍔vYiŸ ~*k91)2(7pH6QȎ]|1s+7sceeYz^*!Y neQqpq{.1]W +]k=g4n#"r 1Yf_5Z&}k~X7{a_ *zjff5cL|+:o;c'ťқNkv[(࿃&ȥ*2Eaԥ (\{>߄EG@1KgFuPno~c)1 |+xFSxQN 5Х<&i+E w* u/0-E,D"Q#.Nkt=fn;b/P$ѕTT ,ٙE[[|Kj>J9nO:%h ,. DZNgy]hz'jx]`V[9p*d*AkX zbI|7d9NoNdbJs- 1{q%Hbfy>Cplᚘx&Ld _kbw߄3C]UȞ(DJ ntl{JƻgɌ%b'.9 R>!4Yר6+an }x^.yK(8V (zi#gBlXצl{GbxFnU`_<#Uc=D7 5€IO\Zvr'Ïf-K qC_P<*ʺƂX#L{i:wU8tT'igiD&)ESF-7"i$;pUKqUI.T-M1)t{lq{BV\W]SU\`Q~?I 0#nqbqRpo RInE暠i[z[{~)b<NkB'։A1RQNpH bn&=AQ79˺e2X\+Ob^ۮ=bx.F;V5O!ZVo2Ȫmm~ؑO ʎ I^_S-cDZXJۙBpB Vk4 dh{Yt!Cߑ5\JXdl {v)k J6o &q*8=( pR;xEu<7 ƬX*=8#Ϙ~_hsZ~NTHY䧋_52>{+W'̵7(89ji`ɬ(HAnA؋klmcذq" e9KAP*_",KO$w0%M\hiN^ȹ (SQ:~=bdՏ]JSr(W8r8T2T`0b}Mndv 4( DZAeMGe2CStơ} lNLrُ5RG嗉VQGN~=Gllt~<6xnWrK38y[,ͳ%g, u56[6z@iEEUI+RW1ϱOl{\~iC\A<ֵZ>dx_dGW Iâ@ :վo<x#X#p)ԓcc]@i@t6u^KA^-gu(鼸˙Pa|^ZhK^ ܑ.kR4j4T =cgk=Ě2ʮY 50=|cڙ¤)ɉ$~p! # VU{XF ,Q.ʼn'8UAٷ=`?1bsPHUQ!>:6DZF&+,kXTY {ah5U 1`mmcDEenlZuI@PE#DZ8H |lYS|4K c4찒i"A܁.P}–cحhim¦kS b: -“åL{xEp s#dR\^ U ,ʲk;$@GP=:?l t!bp4Iq{ n'>U%1 ٯ׽,4 rCScW1,LrTD:zol4UV)=Dso=`F'tdL i.ͪITӡTe]En7{ z8PL mmdDgWLnF{c2bȂH H:wci<IKM[R}6l{gr\/?̸ř>i\=K_TǢȬP}/DǍ Hl{Qo8&_R??ؖJB(ϖʢrwDZcaL `]L(buU OU؟Sr1{t `R'_"$K\]~؃~1{/kH&(Fj5&VL@%nKrmDZڊ,3,wtc dm=`$Eskfg${bef UM0mJbzXa(+s8= 4순"I@"×a !Ye|{nK$3W@$.aC/"PIF=< "K |ɑ_gm$X X=X%OCwY mzYD[%zDZ=Ĵɯ2έ#!lycPol{}Ta@RJ=>c=T4M|{>e)*#5DE,ۮ%(Vpdco-=`J\SS d)!] OO@J v۠DZb:AuJ,U,ADZn" " UE$++5OADZI0 MR)d;5l2h;ۃcG}±?*$Y5g#Rh,zVG]{@+xzygi&bb$U6\&QrIDZ)fSyjnoaO<*L{Ę9ӢXnBVF%]u}{bvQtXt6=l8`bi$Ue=a Bo4QKTCI~׿NASC >35U̇ac# R㥎B4NIu?_&_HY= XtDZEsQtۤaYRw&ē\) plwcǹ;jMq*ApobO_Sѥ-Ƒ$  c؛ CԘӚ9^E`P-mJ(ܖ;;ǰ7 ka](ڡk[{_T05"A8=#N!>m$@Qk*&ZjWώfHî{ZrƉQ*N`Xni*d4B" :ci8,QzZ?#スQAG &T;R=cخhs ZS@q$0F1N@11M^cdhk6HN'ām,^ /p`. .. image:: ./onionbalance_v3.jpg In this picture you see a setup where Onionbalance is used to load-balance over three backend instances. The frontend service is on the right side whereas the three backend instances are in the middle. On the left side there is a Tor client called Alice who visits the load-balanced service using the frontend address ``dpkhemrbs3oiv2...onion`` (which is actually 56 characters long but here we cut it for brevity). Here is how this works in steps (consult the picture to see where the steps actually happen): **[1]:** First the three backend instances (which are regular onion services) publish their descriptors to the Tor directory hashring. **[2]:** Then Onionbalance fetches the descriptors of the backend instances from the hashring. **[3]:** Onionbalance now extracts the introduction points out of the backend descriptors, and creates a new superdescriptor that includes a combination of all those introduction points. Then Onionbalance uploads the superdescriptor to the hashring. **[4]:** Now the client, Alice, fetches the superdescriptor from the hashring by visiting ``dpkhemrbs3oiv2...onion``. **[5]:** Alice picks an introduction point from the superdescriptor and introduces herself to it. Because the introduction points actually belong to the backend instances, Alice is actually talking to backend instance #2, effectively getting load-balanced. The rest of the onion service protocol carries on as normal between the Alice and the backend instance. .. _overview: Overview ------------- This section will give a short overview of what we are going to do in this guide. * We will *start by setting up the frontend host*. We will install Tor and onionbalance to it, and then we will run onionbalance so that it generates a frontend onion service configuration. * We will *then setup the backend instances* by configuring Tor as an onion service and putting it into "onionbalance instance" mode. * In the end of the guide, we will *setup onionbalance* by informing it about the backend instances, and we will *start it up*. After this, we should have a working onionbalance configuration. Not too hard right? Let's start! Ingredients ----------- To follow this recipe to completion we will need the following ingredients: - a host that will run Onionbalance and act as the load balancing frontend - two or more hosts tha will run the backend Tor instances We will assume you are using a Linux system and that you are familiar with building C and Python projects and installing their dependencies. We will also assume that you are well familiar with configuring and running Tor onion services. Time needed ^^^^^^^^^^^^^^^^ 30 minutes Recipe ------- Step 1: Configuring the frontend server (setting up Tor) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Let's start by logging into our frontend server and installing Tor. You will want a very recent version of Tor (version 0.4.3.1 or newer is sufficient, as long as it includes `#31684 `_). If you want to use the latest official Tor master, you can do the following: .. code-block:: bash $ git clone https://git.torproject.org/tor.git $ cd tor $ ./autogen.sh && ./configure && make by the end of this process you should have a Tor binary at ``./src/app/tor``. If this is not the case, you might be missing various C dependencies like ``libssl-dev``, ``libevent-dev``, etc. Now setup a minimal torrc with a control port enabled. As an example: .. code-block:: console SocksPort 0 ControlPort 127.0.0.1:6666 DataDirectory /home/user/frontend_data/ Now start up Tor and let it do its thing. Feel free to tweak your torrc as you feel (also enable logging), but for the purposes of this guide I assume that your control port is at 127.0.0.1:6666. Step 2: Configuring the frontend server (setting up onionbalance) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Now, still on the frontend host we need to setup Onionbalance. If you wish to use the Debian package of onionbalance, you will need version 0.2.0-1 or newer to get v3 support, otherwise you can obtain it via git: .. code-block:: bash $ git clone https://gitlab.torproject.org/tpo/core/onionbalance.git $ cd onionbalance $ sudo python3 setup.py install # Let's create an onionbalance config file. # -n indicates how many empty backend address slots will be created. # These can be easily modified with a text editor at any time. $ onionbalance-config --hs-version v3 -n 2 After the final command you should have a ``./config/config.yaml`` file with a basic onionbalance configuration. The onion address of your frontend service can be found in the bottom of your config file. So if it says .. code-block:: console key: dpkhemrbs3oiv2fww5sxs6r2uybczwijzfn2ezy2osaj7iox7kl7nhad.key the frontend's onion address is: ``dpkhemrbs3oiv2fww5sxs6r2uybczwijzfn2ezy2osaj7iox7kl7nhad.onion`` . For now, note down the frontend's onion address and let's move on to the next step! .. note:: If you need to migrate an already existing Tor onion service to Onionbalance, you can use the `key` directive of the Onionbalance YAML config file to point to the onion service's private key (`hs_ed25519_secret_key`). You can then use your existing onion service's address as your frontend's address. So for example if you place your private key in `./config/hs_keys/hs_ed25519_secret_key`, your YAML config file might contain a `key` directive that looks like this: key: hs_keys/hs_ed25519_secret_key Step 3: Configuring the backend instances ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ OK now with the frontend onion address noted down, let's move to setting up your backend instances: Login to one of your backend instances and let's setup Tor. Similar to the step above, you will need to use the latest Tor master for Onionbalance to work (because of `#32709 `_). As before: .. code-block:: bash $ git clone https://gitweb.torproject.org/tor.git $ cd tor $ ./autogen.sh && ./configure && make Now you will need a torrc file for your backend instance. Your torrc file needs to setup an onion service (and in this case a v3 one) and I'm gonna assume `you know `_ how to do that. So far so good but here comes the twist: 1) Inside the HiddenService block of your torrc file, you need to add the following line: ``HiddenServiceOnionbalanceInstance 1``. Note that if you do not have an existing v3 onion service and you are trying to create one from scratch, you must first start Tor once without this torrc line, otherwise it will fail to start. After the onion service was created, add this line to your torrc file. 2) In your hidden service directory where the ``hostname`` and ``hs_ed25519_public_key`` files are living (assuming you moved them previously or started Tor as described at previous step to generate them) you need to create a new file with the name 'ob_config' that has the following line inside: .. code-block:: console MasterOnionAddress dpkhemrbs3oiv2fww5sxs6r2uybczwijzfn2ezy2osaj7iox7kl7nhad.onion but substitute the onion address above with your frontend's onion address. 3) Start (or restart if currently running) the Tor process to apply the changes. The points (1) and (2) above are **extremely important** and if you didn't do them correctly, nothing is gonna work. If you want to ensure that you did things correctly, start up Tor, and check that your *notice* log file includes the following line: .. code-block:: console [notice] ob_option_parse(): Onionbalance: MasterOnionAddress dpkhemrbs3oiv2fww5sxs6r2uybczwijzfn2ezy2osaj7iox7kl7nhad.onion registered If you don't see that, then something went wrong. Please try again from the beginning of this section till you make it! This is the hardest part of the guide too, so if you can do that you can do anything (fwiw, we are at 75% of the whole procedure right now). After you get that, also make sure that your instances are directly reachable (e.g. using Tor browser). If they are not reachable, then onionbalance won't be able to see them either and things are not gonna work. OK, you are done with this backend instance! Now do the same for the other backend instances and note down the onion addresses of your backend instances because we are gonna need them for the next and final step. Step 4: Start onionbalance! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ OK now let's login back to the frontend server! Go to your onionbalance config file and add your instance addresses in the right fields. In the end it should look like this (for a setup with 3 backend instances): .. code-block:: console services: - instances: - address: wmilwokvqistssclrjdi5arzrctn6bznkwmosvfyobmyv2fc3idbpwyd.onion name: node1 - address: fp32xzad7wlnpd4n7jltrb3w3xyj23ppgsnuzhhkzlhbt5337aw2joad.onion name: node2 - address: u6uoeftsysttxeheyxtgdxssnhutmoo2y2rw6igh5ez4hpxaz4dap7ad.onion name: node3 key: dpkhemrbs3oiv2fww5sxs6r2uybczwijzfn2ezy2osaj7iox7kl7nhad.key Backend instances can be added, removed or edited at any time simply by following the above format. Onionbalance must be restarted after any change of the config file. Now let's fire up onionbalance by running the following command (assuming your `ControlPort` torrc setting is 6666, substitute if different): .. code-block:: console $ onionbalance -v info -c config/config.yaml -p 6666 If everything went right, onionbalance should start running and after about 10 minutes your frontend service should be reachable via the ``dpkhemrbs3oiv2fww5sxs6r2uybczwijzfn2ezy2osaj7iox7kl7nhad.onion`` address! If something did not go right, that's OK too, don't get sad because this was quite complicated. Please check all your logs and make sure you did everything right according to this guide. Keep on hammering at it and you are gonna get it. If nothing seems to work, please get in touch with some details and I can try to help you. Now What? -------------------- Now that you managed to make it work, please monitor your frontend service and make sure that it's reachable all the time. Check your logs for any errors or bugs and let me know if you see any. If you want you can make onionbalance logging calmer by using the ``-v warning`` switch. You can also setup a :ref:`status_socket` to monitor Onionbalance. If you find bugs or do any quick bugfixes, please submit them over `Gitlab `_ or `Github `_! Troubleshooting -------------------- Here are a few common issues you might encounter during your setup. Permission issues ^^^^^^^^^^^^^^^^^^^^ In order for this to work, the user you are trying to run onionbalance from should have permissions to reach Tor's control port cookie. Othwerise, you will see an error like this: .. code-block:: console [ERROR]: Unable to authenticate on the Tor control connection: Authentication failed: unable to read '/run/tor/control.authcookie' ([Errno 13] Permission denied: '/run/tor/control.authcookie') As always, we do not recommend running anything as root, when you don't really have to. In Debian, Tor is run by its dedicated user ``debian-tor``, but it's not the same for other Linux distributions, so you need to check. In Debian you can add the user you are running onionbalance from to the same sudoers group in order to gain permission: .. code-block:: console $ sudo adduser $USER debian-tor onionbalance-0.2.2/obv3_logo.jpg000066400000000000000000003275541411742520300165550ustar00rootroot00000000000000JFIFHHExifMM* 2%  (14ӇiL2020:03:12 19:14:50Redmi Note 4Hmido-user 7.0 NRD90M V11.0.2.0.NCFMIXM release-keysHXiaomil  0"@ 0220 ْ Ɉ' 01002020:03:12 19:14:50d d7190207190207190202020:03:12 19:14:50dR980100!9 A2d2020:03:12(HHCCq!"  [!1A"Qa2q#B 3$RrTbs4CS%U56cdt&(DB!1AQ"aq2B#3bRr$SC ?cW%1RU!m!`9 NԼNJ}ˇI?mt]4ƚAC-ېZ'>Nڶ./K |9}HOR=O!DsTQ}89@תK>c*rh)pMu:,U rd+OD`N0xK6yȌGa #?M4eٗN`mY+&&TYvL|( \8/rhb].3UC)?0W˝o-I[ͼPu;qjf+.Pfip 1>].\.#=rthD)qYKͲJ#GU)yN!k*a J }ӼͲf-$F[ Ae%]#מR!4Ÿ7N.mњsJւ# HELj@qXFp>c-m\iTۆas֪Fܕ, x'5$I“uY5|0;Glp]M; 4tDvJ@S+ 9>]VhnKk?X6h-2 $ƓEtVS)1$;G`FhnU՗̊[.7~\ǏR/ )l۱XjJR p14JiC`BZD% h #EC(`qNK՟}/4҂4>j[)\-(&)le99#MX'IIp7x q9s⢫RN+0RThO5%~2=C:%ǘBUAYabMabC,2ьˋϑNT za) #?^4SK݆SCo=K}FTc-{OэOʭ zO[)vE,%r.?O{!90vt!쩷%|3p1GL=%ƐGO6ʓ!JCIJ@)Bs?C5UC*)xµBkeBLx,9 JR=0{q,ͦq5hfD)N4$Bn=/ˆ v)H m1-jTf@9pR{ rk2oh`V|U6ں۬ȧRFD! d{gNT=uUJYTm,H#>xZFoM%?r?M%zuzjOY‡ןNTX:[.e.Omk)j@}K {N=:;lk Ќ  | =hԡ$2]ʁμ~:fT2A`3'^h5*Ji2Sd^6 JBHc򟦉T}]f;]*XGn@҈_[ɐBV{%>ϿPRK!,+u#?T<P]Z!xHc>ƿ I( 6tםQ1Ev>[ ZC>9pzO 8Yد,'E< p? <]RFG?m+fԄShd053w 64%"i;ULy(nK4H3i(RBݚn'2qp?_+hSJ] dCBIa }$+܁LfJHusTiE)G5h.-F=o4vB@%C1Ư(mČ_i534$6m-# ahdĜh ڞu9,bH''Π6.pܻMEOt]βN!NЎ3Σ6gCYp (fsf0%-P7N̻TG;ޓiTX(~cμvET"T -%}:%2-8JVTT u0f)* 9}V{m'HZOsc;WZ>+7p=HI)$ _碮б=x;qPD}sRYWbR{~(~k )hBMe2`Z=GVjuʾgS1x&"PcHd-g8 g}Nqj G IR]AӣuF^RppgźP4IXG/6~k5'}Q G=C鴵8@27nBRC$_~- P A_9kZPR_Au.T,ft܇^?Kd<Yŕ d)`$$ 3LY I}:V yҚ4P ܞURf Gwi'?Kms`@"4[IqXC2IVVF?/&aۉ@pX瓩\[ rR<95i( dhN!=6/v(@%C  _mn(i#>R@g҇T_dIQUq7җ8fQ*SCM o)VuLU.3%k$`y3Yo ^n7{]Il@C BގOΞJy>ݏ WzOubI2!ɞ(9 s5#?*JTšuQ[5tRJ=m}L)2mp䀧VxPIJ T)wUGCD˩& si =<=͟3e,=u,]WFPTB\)-!de)mJ) ,T+a㩥%s[ BС$q$iSA2\)6QrpShhk،~lilGjCI8uUD51ζ2#Gh#wdcVFi/2ը/u/Oit9lSj;tsAJs?]{MiH=$΄ֱ]wAImw$Y+ʊH 8ύMu*J8y;>c+> ]$t)1RnP/إ5'-3)n47V{+b\sq%@b( 0 ?o#uv: PiǦJB@wJOO"çĵOC> S3,(:YVf$u 9ۖ#T{nÍH(q%MRA.ku za I%h[רsPK*πyy(FIM)By qߴ|VFދ3..J˴n[dQqjC`uzsxиvy6e+o ii%dFfSILm%9yS"2rRi8? u}vV)nrS wu5OdgHMۨ [Vޢ*4Yʻnq) HF=V3*MΑ Jox/+0ZB@u}ܻ&v"X F);}!`y.Fn%`:Oõm˒88g Km#m/97Vן&N)i5P '!#%qGSrfHri)6uBڅPF OLռ.μ쵮lPۍGu΅/2Pzeƍ{['vMnJ$Kr0qMC& Q"+VvS(T3|5cW׼]mE޻R~\E=v-BFy LW]L \X[ Bb]*m=ksU&kT-Ia/GPNr Jw$|: Ke5wxgSـE~xaH#_dt%ҠEU*ZpTL*oz's5WgXN*B ۔*xmsBs' }Uu9[[b]7r1b4miH{=e¹Z)O؝xjźB3\ZRݒAW## ; Y[{oI[g~ǹmƁx-2rĠ봝|r7Zq&vd]XeXJnGݻ rfXpTޠ C  (>nKT m"nWݜáEX1ƺeInVMpٵQbz\OJi<($A$$S#758%LI;I%ũs1FwWPtHrjޓw2NjwxvRU/z'H'%Z-$tyaLQ#<]+tsnM^uu!Io1\C2-SQq @8Go"?I7PEZJW}r I${`cZ+F3s?ݘG(Aϸ)qKG{?_Jէ7;)17 IZzr*wJjuA>C.E ^j(sޖXmqKEJSaN3H;]m~_]i-NmIK`d4%fEu!`i.Zs/rn,8ݎ6S{2%\.J %_vZLVI $-QMGv%*C8$NNv.-Γa5-lZ(e+O̾Pk/G9TC M)A d4Tvzݾn%&C)1Ҧpa#jA"!O>?I[n۵˓Ԧٕ7ɳ"2ڧ%1J8I!'b7QǓ=G~'cDDFK8ηv6BI/ &OGw(d {v3q!,41Sl8vv#v=_j%M>0tR]2wzܸDIv=.e.C4TFJq5m J.xD;wMqS{gh1o]D`JSܑ2j BP2{ sƧF뒟*NX\׭d6ʟRj@stBtaɶ?~oŠY{hGEOpaH'E5Jv]BXCҩ:@8*)H1cռ`P,BMxr3)ν7xMβǨ.F3OKyePN)CN!\J!8}sܶpƹ]i=*֠Hrx!"siv*1ONK-''Q'4enF_nnм&St)'9:m=NZnf%oNFK4<0 (^mUz"}=knXZ$p|u[1/=$ LWA;uR8օQV-I*in#}|Z/3t *{RUI%c<AAb֡2TؓJJ:I [dRkxM5.[ †hYh-aĨIt[[7zO<9B[KC 2}^Tה"Sj@hs<~`SV [4XlS~䁕|y3aG 8(p+5OMKg-:J>UNFŮڐbbA\0­X4fDh y@}FuwF"~e{/ƣmUjn0ڣ˝I|J=7C>a2v <?ok5?Tk5R('EN)jx(~N'w)8>Hu 8rD aKmlwʽvEiE1J+ O}:0#:MzŠ@Aρf9asKn&Cd%+qQ{RV;9quźPJE8ʖPR#z 7M=)'Rx6e%R0SijC)0uY*_V;]ҥ(ǻZK*#X2j#xZ+:X]K)ʖDr8YהAu":3ޥ8:笹TR"b+:Iu<x'!Z1[z܏6&;s]_Wº*l*Y.y-RVov0mQg}PaEWfR+H$g:@h>I!(/=nZv/pvF5ꍯLirRJإd)()unVilT.6(R{Uq[Wrvjv~UJE&_,1'[ d|iz$Zr,!)cdT|Tj'2o(eE^Hm %JUZIJSՍԼTj/l%Ü}ߧQE\ăC⏦vO @*m?ۯ]r[FA&.ZگV*PR VZ7.-%Ki$ ^'T|-ҶnnMݨ&XdV(4 CSsLV~S/mc{I&ݢ\ͳ&5j22Vb qHjӯS`lmܐ6T*wF0jy۫ajK+OQb 6İ.&EiD- pSR9Fh[v`(QϡHq!@dQm^mJۢBw]6-ETgWu(aդW'C/lwC~7RҐz!YF>e'9)nUԮJ<6` jLINYH1B H BqpuyUrh=椡73' w%XhJʇ5mX{gIbq^gޗ}W6\PJFG}Qrz)Bp%qWihiC[)[eVi yUl+(wF4J.pJ&&u?k6x* \QL:zGZXpA}Ɵy]~2M{ےCu$2+`P+`9;c=bͩ]W2LSdrLU7! e! I: "tu=mXvݸh zo{O318AE]QĖ"iw6zͰ- uSݻKr[ 5FKUiUNCiqQ} :WTPj1[ɗV?1*=HTlǨu#Uwd8ٖ=+HH#ߌqN a!”ۭH­݈^DGS27cSZ0ݗ[J_T-u^Nc62gʇv%C#)#Dն].p{,i- ˉ wc񟦪.lYոQK"?9ˁ E]R u@zZ?IRUܓ4,S*uB˶^@$t*Mr[C^=D=U$c<뉽6;z°/-EӉWXo =dpJfۂa9r[S/-Ȕ [LPRHs%(JF1>wFE.ݽuǣ/7w;1O?Ou[}qT-zNVU"R 2yjBYؘtXA}ùlU{ҟJᮔc2X!axY8&)'O݋޶m^P܍їUw.Eb=Zj0! Ga2 LO합1,ݜTquf#J-KXlW:ZHL$iקܒXq* )?o|i.LeP[o2IHЋH.Jx=4EPӼGzr3Rp:Fmj -dž~<8:$%A.S_0bJ;_eW3 lQܕ$$1c#*N9#Tm}=uZ^"! Ȍ4{Cn})7(uِ*"S򸰢A8-O%+Qp`m{iıp5Zӄ)`%I 4(\9t: JR^azZG]|.1'4 #+>[t!6I:PC"Cw2?_:jLU>6N4L8s1烌}/ ]J#F1N aT5lu{OqXs*-U Km1j@He'(y+#o%G--&+pB=:p<4G #*IGn9>szv ]mU¶EGJï`h_:? ӽ)&š]\r<4f\ĹŲ XqX!$qDFsXsBM`7خaȹ nBw.kwD N# +wQmXMɈ P'}` TkJ@2VpO둩PXDZaHdH1ZSQt%Vo #5*##SoKC n<i$ILC.VO {yvǎJ2!ٟRM{Dk71`M&Ǿ,iNEm,*LUsqj/kP$-ċnr inSZJPT{jsmɒzXܼ8=ƫHOs/(6i#F<+I$T/FZV^zZػ*ҋj[;oBf/\ ~c ֵ(Tu\; FT[:mR^+B#67c?:lS~=fRQ  2!qP4@ӵUFʽS-{]LLi.Kur6輩R%VP}NSc SU;SK<ɝs%M}gh4u܄~KH}]#(THU^pAqpU{I>LkɽiOh%[Q { }"B; dAaNHZ` J(+RݦfFpbpwV.E !}qrD䂾%z&hm)3:y'ςyV]H8uM +DAZqNe IBJ08uqjҠ6{ ~Z/ evBeV Y$myTHUb{@'B϶,I:\Jp}?MHia=U pO9u)CgQڹ#GL-) p8;l}Ԋ FQJ-9PR"{;tKPqu'Hj(] څ=Hz %yΛݻ}r]k q 8H-k_;~ڒE]zjtIx*1Ԡk_ۅf- ЬbO(r#ȇ-Rn6s<}fs_U~9V](/ “#Kr&K,+y {q|ZۃPzVɭ[Y; J| 'LR]toEh덬ԡw)e/h` uCg6JeHi )KRת ̉u))N92W8KXK'čNEs@Ek}kVwޕ'Zj Ȱܐ:2dժ?^ Y)5XLسTS\ld}^p. r\RK BBʰe@,~Ej}5 Jڂ6>ڔlnY=DXGvfl.[R BZ(!ۈb K9 qySPWNu w${pqQ)4\WpQM.!K;,Gj$pJ*tߌ=JXRu"ll c.ڵœW'$Iaԕr4UTz`Ə)Sq|3JHCϑ^+ϗ LfJ@U.LfǦQ1: e27 IS{R϶ UHU:fQ֜*. mR۽עSxKp,)O< %ʉnLJ*Xv{X3zSN)h$O8ָ  .FyHP2Xx'Q+Cx%nʙ*^* I<}RvP.yT GI -}$c bS JXWr:NSAqȯR"8Gp~ 5&۹0 ):1mkv.y^ͷB$bCJq4Pt"/F}!]S-e Vs慠2eӯzkr%i|r[%$X֞uKuu$JjTHK~└F>u(9ژ l8M+-8dEWTjmoM_R5s$J8a;F@)A<+C+Q-K7n% bE.m4BdV;"S?- ) ?32w)6%qM*h mS u,sӱ7: -.JTMy<$' -m.xU=B5ZH  *BG9-/(IHW;5 Q#yE4t h[~oEaڛfreB{۷7KP)aL&UP몑Cj^>6ړv(}cD`dh>W*qg2I9qv׺[ji~Rܢt !e|@ uWOwދvomPS2YBG) Gr2>4Yi *@1q۵?Uʋu}ذ)zn-;n*24UzbAt 'EFu\ ꥟\G8^9'{Mv^jnz50|]&K0Sz1erH@u{Ăw4VTۀ#~Qu=ds.u)z^mbkT߻(T')RWrtݪZû醡y\[;WԪF nx, $y9:&*Kb'4PZJO')iRߔ+o)c:h(OF!at܈+C~En3D\S. HPГU6 9LWaDZu-j\#}{Gʀra䰬%ꙷxHŐH 6X tgT&{ɺh7lZv$!d9E#iVۇ'i]zZnKqƵp D6I6NTmo8K_&-A i@Hlcr?GP ngg7=ʿ[nU@ýqgsj9|p4`ַbk][h[$2IujhFPBSyN+zqY)qQn\7 Qj7RS6U/]+ V1W2umٹ'h2bYw=r˳UHd PbY;ѕn %3Qq !.'F0t:.ziǒm$2(J{I[0:`tH\B!*pɐ1f~&s'h VDI-M=p)4vDKPSϾ9qJ'׺- pNARFLѳq(&aI[X?=+(\솊 S%}AΞi)MyFht"$8s34=6Y[ {IjmH; 3fAemFܪnU떬JHf,)gԖJ~y IT1GdvԉE¯§U Նf|P"GS*hiyT0<ă w1I*^i*ҙa)en~Cݘa)*Z՟&eIi9Y|*O:2;1d8Gԫ;͝[ni[qQZHVOV\ ?S i]Tv{`ST) > 30ߐ{VnAsWOpz酗n}٩Z{t.nR2 8Tr`C|JR^+4OKuwRW@\tmcej Yp5cSjR"SCqԜ~fUVܤyNٝI*B*Iԑ˕tB}C`i/@ä)+MMΩǔلÍ~ΫKWNB=JyM%iVv_K`zSU?u->K1#o0ɉJ"W4qP(Op`g[fU!Ei8H?^4UVJоcZfjU) }?%h`P 1Aa(-(6PqLqN:5oP|Z1weI=-9h' ;i)#yJeHQPj1]ZTZҧPZS1嶺%V\hV6"X*$ }%>ݠ7qzc)NTe->G[@o&z: c!Hg,')P؜5]5L7#d9o[dzU[_EܫN&Ӵ4gma0>5=ew]cJs&B:" e*~B{ tit E2A~iV/~ʤ$0<8Ȅ'm ]fQcmiouL6=oϤQ2x~[l BzZuπq;UʺP*64ULi{\cu<*JB9^=1 S)Ԉm4,rRpS'ۓh-r"75JdGJ7rm >83VP4_!YWk)*dcq*d!-gQ}-ziچ{ ζv!!cpxǏӛE p)=< q5n}hA t wV??f~/ˣh}y:bjb}7OxRx??}:!)=ī}@`g׬:/@#M4ʐMFPw<8)n+MUҴť>I>tr S-6`_b %IHR1ifVP= Ka'kqt: 8-kYhv*ƷT O4`sw4[>HPqt&JS2A@ǟ:PHM"e\gSi͉o-Jq3q"e9L))PJ0>x)h5[0Ut.نT*NI$~%+ mZ0-=ޕJ<>"Mߝ.o%On7Bl; Xq qBp2(  i3vu.Ʈ*՗c]\6aCCMӪ /pP94zn+l. Y(/1ޤg*ua Ra: yߔ?\뒳GzbzEvO()=\RP3I4 WS5dEQm<e6'nYՍ*3ۗU#Iv;S {$ss{l~O*칳 69Z}9/i[];D7AtHrŊt7Cey|B{p2xտz^*ꉍݍ=>6`1:r#>HBR#5a$n6y%=oisi.5bԻq`\{j䨸f;nG$$^;M5{y~Yob JL$?u.APf\7x*ARI|p.b֞Z=5[:stgь>gPOdCeO-F{lk6QTZU38t8t9U{ X%a$U86ʊ+ |j[nW $:lVܗtyNd O'Kit25!F~:a`L4 R!!%# Š:omKӞ{[7\ZgKYTj5Zbg6뽭) †94Y|햄+%g?^ÕI EDnvX <'v:\ŧ*ƒR4%*VRp>i=an,F.${O8,\i;J#ʔsQBKe@IO:nkX =iWt8: 9LY(4Ӑq^YS'ԓzmSiaӣ! Y式Ɓik iuMeI-%x8Skv%-.+ρ{6ԩn>,G YS--\6k66Rw/W*)! %ǎ}\EAi-pH:afY />9 ~/5e"M R22y䑎4]ԠDJӧ 穮R{cji Tڼ->?oRQ"T"ڐ) J'9P-=C[B (r9AOZCGy1#+*RFGtWcSh?lZ6*M}A>ir ƦEj He(Ba@BA,=:'ºo=2q}sZf!$4Hy'Q'9xXԃ&mq+h HZv<YC` 娃ϊn֔bM껂r_c:+q'M@=EXqfkq?Ѐ0<Z +ppleZdKc.2yp?io- Jr1:UhB)}jiS*W]yT62PJBP H'[eol&T,޴bz]쐷o,6뾗aPHw ]Ks?h7<7f ܨ2ߕ)2R9YǍE.>peFuGpD3:{0;-H{l%)ʻSNUzj6,a{_S*}){Uo˲gm3o*o}Oɗ@&j%B$w;Jߜjz!j)m<{@NYi6"pޗE&;Jo!!e FuINB`˅IRj~fE+xYGq[Zm,! [zj1)ïSèNG{P>RuiH7- yznLVOg9.0&ZG9lM~m'`VMUbƐӀc) |鯩ɳ.oX+w_[m"hR%DuKxZʒrq[綔߮^a]4(-w*%ĪGc $܁]:.6;V򐩴E= ,{YsS[&q#L\(QϜQ{%uٍۻƤU;uIKj[g}8*m !BG8S+*eFx6c^?.uZբNroFKt>ZMXA#H/LU([Ɩu=!ʃ zCOyGSy=T|T\uKDNNIόcVMm>lsjw;J7[),'py֙1$PTCjRF Ӽ|B!AC +HTd3 !:GO붊N97Gh-*t0 w8gνQP%vodc(eمK@ @ƾ)G<~R;*DZ #KJ4'dgmLȨTJpDӚNOsn>XSLS܄I/- Nx)eC/l #6 +=mtj=ynMS>ش,פLbMmST2 b<}xN39U%{q-9vH*O}i ( -JB*wfIBS)_·PBޗ)\QQZ2Y/Gm>usAXS ~\I(Nl~\~|h5u.6үxڕ_rK*Im2KQޥ(yVBڪiVC*tqUq6IH녶UϟΪJ8NQU_l!{H a9V)JR9'9:F)!՟S# uhOJ|9e=̥EA۔+>n߷JgP[=-u#!]AIcA29sR!.w#iޟ.*ZBT4V3[A npvLuhu71vm>͓bZoR))eLMFILprJot{ta+m{nmr7zBUp;&3hqZyWw?v1nqLv䌣[NM}ȋ-(#v;$vF|cEJJ˰А 7"]Y6OzJEjYoz;m5n#@C}qCHiH4[y}mD&n5\*?:=/>^myPG:7S`Ȳ֒D*SZKH`:8- Z..ZFL zOaBe:ĨCҤ(osq>S^uӞݓ!e&XVwe9 [Oǐ{+N}NH+Ǿ7zQnU(U&RuHJ3Qjmşl (eFJ1tI+UeMܶVS`O;s1r*ܔkE mq]lu-yKw>[*6 )j(Ike5 M+RJIs?_c:pTuHT" ٓ2[ CL0;}ϰ:ݔJMNG\bI=5O(e#r6!)mK 4ۼRݩ뇶ӤHd4p!d,iTpdԺَzq>-GRb M)ja~PIAL@rT7ebyQy/!)_S~ 1-|YK"r>pRi?8]6i괪dՄZ=-+iq v"bS*JZǭN~LҎXC)gh0\v@&,nd'>}#\1k1"){NWa3Tdv-A]dT)qm3!+OOmV;[Hc?~^&T)QKVp +PoNDbhC9u8R~qelV4faū$8J{L8=e3 Ԕ .\v:"A8Ʈ879)F1k-8dvjI wm/ZRL(N2L )[T]lB91PyiR[ϓV "2(g { m2>?!IR\<dTv4Ny!U%P[JB]_B+yIqIǓQsR]gu?Z[3 m+%Gi7S;r)iܺXq='ϾN&fuKTjU)T&Қ[!-JOh|y+'G=F a[l)(#'ݔ)lpt ަ\C;xS9ϓL[}P WHӌGmBxL֝jQJam%2%x =ύy)Q+J5EO?P%ǃ+ wv}2Im6@1%Ə%H1ܒ{9š2W (Ƣf$U-(>Sqp9/wˮJ:J[*x>6DT(;YwͩICB”8QmJHD&U.d{K-㔜1zi-h۾/u*;AQ>p 7ځ 2wo|HA i,/=( #W~TA[K.T r=# `zg8:Fy_TW]%$]3i R)ȔTv! T#) IP?!elRR~c)?(eHsݏa5hRY'Lu)\HW>AyDXAPLW@H>2~hȂ{()~^sP !JKqߟRgqCY%~ʋZWJo51ϚA8F2quS\e_Bx0^mb7F}(v1鵺Or{y>?}vn_PnI=¸/ԡ NSa%Inwz@!! PԪR4v:ו?S)A)&-쾸+U~. <;ƏqEa0uOwwu% -(BRI\nz٫rj;arȯMDTqyƎw]6[zl؛K)WLl+~9`@]IK>Y|!+okAP)lhA#v^L9EB";>K*?QԭPZgK`(92Rׁ逕C b5cHX.@ͬ7*[qφmN{G_ !l)K (@ sR)jP .uA6FRR-jiq $^cY_1~Cj"I @#4ڛBEP2@SH\QyϾ!}$ܠO*ps+<Y9VLAly~h,71lSٌ PV@NS@_)i.3#n{eI#4cuEoq B!),QWr5 Q0R8X!.3ͳچ s9HcP,7ԃ ְF[論Q! cJގUB[s@BPJ@Q(dJdR:Gon9穕 yN|/>$-]a`Cnq.lJ[ -N-~P{AsTO?R|mhwthnv޷iKҷ{ar^۬bP1؊ܗ3}_1 [0ד+r.Xa)fY5 >\;uYfؚj+unzꮠ;%-YZ'Ax4;QpOz-銧6v1^oJH3Mvu+Kj6(Ui)sLv#FJ.夾Gq%@sjzwEcޗeu'C #=|HRHPƉ>(9I4: @gY8џ̇oVűxr8)vɋ`kPMz4$my3U-%%ȸSEtܶ>SSv:.$'VnwӃκ=E'@JJ)CѰznd~޿\N&އڊvv$0Z{V A`._a2S P |c7 4Nj䔝mM"̶vo$wm3nJ"v#M^y=8xuiTcK u\4:~.Ă;T_Kɻ3o(-@.:/Ykd qkɈICK^H'αAv:b Ka<:š@ (W{2 *An{XCft~P qA!]ﺆȑzWt!y/0Ҥ!)NOî]͸vghq*oW5IIS@xPaa_H'. b u۔m]"ۊ Rs G@RBUV}IJ&\U+^QRU*<\cy7^)Xt϶KTQUPa ALt$r3[kz}mqʛ4m6JU:*B+. i5Ctn;Pۭb]jS-?zFy#K銫pŤ^JR2)'KlGbO+~g*Ly &ɸZy=v׉}X Jܛt:"ErPfq Wpte[=jAOA -4$`%)OSԄ:R/q d>'"RY)O *ނ/L_#.TĞ6G<}pKD{?bX,\tN;͉K]SlmߠҥUSPBp]Sn* q_3iHiAе?В=1by9B9mq5&0%rskjwMrV]n=jݪATIZrCgǞ8hɧ]CJ)~{JfErtGZsja=MwTmQݾܸzKXL15@}O!Y#'nn%_tnn&NM tv7KEI !ynKdrRuBNJm**I= #Y/3'8ܻ2V@qzvdOٍլMڤCJ^#O[MJg:(o{1rDGz4-IIQ롦w RpVF*[!P(Ne<(atj :T[*YE$90|L=ʩP\y=+.P9p$!X 멥ؐ=3n=tYGf/eejB|{q9VnNZyQ_z E~ϲ !Č$nk^R/ƻOLƉ8񽔕}m p5u^pDW=طUmg͘jR%Jր!`+V~;}^&oWE ID*u,KI9ˆ#G=Bgˆ[ VidVz;Dv;S+Pk'>uҪ""KN8k'ݤkU}t >GHʪ.)mYI6wl#b:vs7~SsQƮW~o3cPJ!I^FjkNW:X;h+Uͣݫe6۩T:pJ$6ۯ$Q KpuN]Vж Z:BԔ=g#sQ/I.rĽ[GfvrUj+f;aX[Q8V25JUmb`L襹5 0-(X~wr=#\Q\m#`}ƴגr *aN<5<8YD!JU?>NYG:zM6*uQ!qR(ݶ"j6j4J!QG8#*IP@T rdVr{sjLW=Z}Ot0C1I#fi8D9IȷESӤY;dHYCGSrW !I.YP$ (pp5pG7uokW+5FJfK+}pa8 JJ$GV .h{bX-Ʀԫ4~RiRĞ9ME܄2R%~#OeKЃFۛp, S=QڜqTB\U&8\j@JV'$)R/ 0m$e@%@1]s=Pd!AE)o IoN~ׁXnlE))P:OAՎYhvEuG>%dH[ǂ;Vu*M[SXڲ¾Q뺚$u6mhp~qQTr дܑ3jḒCZrhm!1VӪ[ k ;܆NW N3< oUerb{e+N0U>-!Hq ?U%@ƶECUv]h.F\JC{M= R#$#0}%G!Khv.t&T6q-:fW =xL+l/?T֭(&۴*Ka% -HIv1$p()$Zm)ԧȎ QmxjO':-U=W"hpTJ]B>OyZ]AG(xU pA% Zw=eZl.\Z[~q5ٍQE@Sclp7|h}OnE]jY)6}jҾ0%.*`d7>F?B(cTgt۴@y]DuM%8W]\3ܤb{xj%ԉ'H S\ZxGL:jKK[ǃ?5b-!9S]'h!s+Q!N##.M7+ w_v?ǹᓞݯ1M B0{Oq ]@@_AQe%rKYD?bTTx)F0=|6j V dյwDTVNye5e섃EGN+;fڈ VVx. }tM}Y8 ?_}yKwOd}6(%,m+qhonVt.VT* PyRxxySڝzznf<6ePV&!ՀJrWp *0VL77]ݯV?Ө,&fAtBDOW߯@P-*TBmlg(~iV)C' Y\r/(QQ5T2A*onSx1㓌0SGH 33Z''_An6Ć",\Chvֶ+vTeĢŽaw-qd-+$N[~;)J'iՈ<5R$#霓O_QÑ-yWi8Q>32ԦDk:sZpn} vvfs.7R4>S/C_R1yT窒δV[O%@hi%֗,QZoiݮl[˪Q=gy49y=NaPUEhNlԩ IJ* kF;Cڦ{0AcЛ_OIyĕo u482 {Wya^hK&kk1}*vEsH6Ϥyu{%nKFMKq/xyƏrQg#@G[:\ ܝBIGu.N3ޗ̎qq!}R9xՉ!@ɏ46bJ?:fl*ˎG3&Mx!ǟ'9:{Ź56V[CiBCk{"Oz x8" ܎ls,]*KS* 𴴆e}Go+ }wٺ-(VbƞҚv4y98ΟP0Jp$WʀqQۺԪ9Udq6\\#JN]&]>bL4)J\D~<Sf-o9 ;rZNH#v7(Z5)6_C.=森1C Wkխuh*ur[[[6++IRX^JN0ΜNS%M F}r˔ I6X--ɗUoomT@-o ' t9%> >WYLF[&n%F,Y,LJGV:x^;\/Z fmVANQDdjSTqo:ӰL 7(Zy[Ebm[u؛G5Bkm6.2YT5*\QXSM^-e*\-ж=Ԓ>r*'N]hۓ:ߥJY'$~eJRՂT~ Sת #r+X[Pb']-8vTxPC8δ UVV vP74NGjisԥP(+~::{RYhص7~rHR)>}ͼu'T*UF&C Ōq IjvQjk5NF1N4O$fC%$sƥ~\7u}x@SbPF]J~YjI]&NKHJK4J•ڂQqzeM*T$J ##NTv13!4 ˯h$u,5YJA #?GWc~KLIn8 u3Uˌ^ {1oIX CTgbZP R:^IږnvYޛtZ5s氩)(̯OPqѽ}CkrwϓjZ5<-XZώCoN%NݫfӧU$j^s3'T.>JRv< ' ӒD ^6$S݌^ۛ;ovxvpD)phLhS$;6Zc tuRn,+ݪBj «RK̼i88^ۛznE` V[ I< };:z򷰖Sڽn%e3a?#.Gp@-XO)'#}}b3"ۑ"CQ*Atej rq:$ࡷB}B}r D섺w^5!&Pe><OCj;tP r@ *>8KN ) `)Xg~ڥ*T̶(Si{O"Z]Fl<\$Tte\: ^J|k.꣦j]g = |8#ߞx$nT`AkED-x!)uEASʏjÃǜ>"PCݩuR1?k)ŰQěPG?Q@2W|9~qv1=, .S̾wO5'.Zi +uf5yŸJK.~'(|?I&p*^ "V8wxGkzqJu |F #E Nojz>$4#1R&CD9J>$]QN28x;_[%$zYۥ*znPR%E#4q6;67l{+U)tx"ГFRxJm}#hsvblӎuKj+TYRq};Py)XP8Q]E{ >Uw=Ⴓ_iYSK>I'Lm)V9~]2;\RSN++nٻ7ߨ biᲨ8K%dۢm*R+ӠQȪv\Ra>|g>09ιS]hW_O)b5LZo$ZJUy88:E\Bj i ӑZKo(t+$qWhUfB@@+ky@ k~t7:[En* ݽj27jn2{##꣢;өWYUʵ.C1ةY'JlZKg=nEߩTGRy{w@׾2}\wl˖g\;?G,QS3 *q!x%$}-#=Y(B 9?[h}JmKJ]˷!lvТW,{"K z2"t-KPk؟BVe]Be+_XYpȧp{wU)r¸^6tұxKեTT-菓;FH$Eu5蹸 K-7M2T2IHH@k$t Nv`ɐ\' +)F~:,+\va%u(IƓc y׭pMbR^*h#UR`r-n WiU) EX.)$%`{{}Ҷ/Ioҧ|Sr%.vc\U%:SMBr=)q|$MM5CV]M{8h|yզd]`9ޚw_rZv}Ϣr@s:O{bl.WLjb3%`wu6F}ˋ@Ǿ}HX=QoՓN=e o؄?ZuUGlj< :u r[Ǹ*iGμdt#\S{aNm M܊R"Hkn2ݜܨ)KqJd+R %K(-{}_MۛTWݞr~E%Ƴlw?4@ ~TQ_ دȼ.-AU$)*$!:=Y˯KJ*(RWCдȌ!b/AΠT)Z+9X[HE+=8 M[&BUEdq>٩=FPw>-{UʂSG̕9[<%id%Kl4<~^6-kۅD*.+L |D7B;NR]W\w*m\4dnm>tdۭ,O osuK%@ZWˆq%Y OVjaKu6:I]*; _N1TR:h=Qe-%t2#ۓ>:˪x^J>SHr^jSfOŽ_ŀȏ־–b"˂mZ2(PlJT ׷uo{-s%b5ø p ySOn8钧YStn1^++av1g@wPעۣtNsԚ U!ڔה?xaSqa,DbSQм ؙ6mv[pIwU+qSҮ BJnOrZONT]~V3fyvjR!jtUg>x_䯾uԥM)㌵亐qNԺ<S&ٖTHږJ+N;o ԋEqQ#u h$/QU>qE R^ZE(<)XJP{3j.]A'Ld+c!Xk-$zeEf6GBcgyz3DPKrecB#A0d@Kmϑ'Y>S&QG6ʚr\IJ!% u|mE])+$ƺ@e9n5MN!?{iJ UnLLd[sY\}v-?{Csիڃ:{DqL>1L|>4tNrO S۩ȷg6Uoݞ|ˬJ@^>鎶*xԲ;&=̫rT"g;_[e@xlpirvƢCp5܄*X-ϑ VųY#.@kў_j"pW`j5ӵ7w޺zGKJ )LQ3O$8?7ϪkCi7^U>ݨL*RI!`jA8镄x)?SzG fU+.wCm0xTVRG?6wmz}hv6T%m]+m;;$ N* [!RN6fm۸[ek];_Ԉ3jR.kT(98@WJOn^u+zklkN2dԥԫR >BTFTiBC. N=57⬴`@LJe Iu@!M(!C?(9mHMqh.+~CIQp#tLnR+p%HHé'=S)-:m""\ XXdegۗ3[j:ƻB$ei) po6H#mXh lJ@_qKh#Py!EJY߃|}uխa7-}[q[!\6bTTĢ<6OХΛ&yA][EBNQp"ncDC+Xƺ=&g!T;P}H瓧.vfv}Gt7ޫE`Z}_dW[hQkˊmDJr[ҟT9 FKAcvFuFIhxC"%ɋzZe.Ce: Λ)Jҳ%}*ppx.Nx-(wb}.r<栗J֗CKDԏO>@-$w2-VTӡG  JNe$pNb k8"v A ʢފSr"jW#\ի;P":cJSmӢC RSʒ>HcDm>YB)RP|.*)%QrB ?瑪T72#:%{]bSGv E4ҦQn7һEO}v ,!d!>5:ZuTS`T.c%Q}8ɮ̦AiRZ]u%LW*Am,$Alkȩz^H1 y pAz_H{WmPЪUaLF.iˏ\da !HMK(=H?BQ-E%,e.9)_@TVul2Լ?w<"YO'Y#_hEWqZ.5 "ζٹvt4fMrwoӾ$?P\i)9 G̣\7{iw\[|ݯ^T Jm}ӣ+a+G'k[멠8=T;15|22:I@[Kܑ΂a:],K5(XϬeUNt;`5'\\wR†}^"JJP|m1L.(:~R_myUڜ[.[ o!*, ԭuSkKo_`eR9}uTG0s1ʂ]C'!\+8%8Pt_}*eԻ)U۫Y_ չK rΥ2j!:ʏ8[.=Ԁ;O MeejqRSeYPCdx4J!IRdANRBGEQִ&+S~TDA H天q#_bDi.IO J$9||jJ=5zD- ;= p$%7|Ԯ՚r\P]mqm.BCYXU~ :9!5$8ګ=<61tȹ-qd`;bLcyr]0#YBώAId8qA(uL%JDg0ul]tTʥm<ȍLmZ$$-)*I IOjaT`ԫSn;th7""˪)N8A:bɣ)Ha ĻתuRi_%y5MJimciJw̠=ѱI" HY%!ڐ>^5B"u Ɗ01$!ǟP ǁΠh!9ZE<}"!,mTᢵSt>)/+*H9xZmZS9QSaİU+>鏷 z9̑MSTT~6\ʥ^dV_eEG%jJ==dt2Yk1b2eoxIҤiW+m\wQÒ*[ PX'jԙ N᫹3R[Ѣz-l|đ'ON?׶-luɋ-Y/-cDNu)`Þϸj )#% zqA(qI {GQ(a]̊ *ENH>siqƤJXfSB#LM?%/* x4fwV-t/X}R]O#irԤGV]9V|eC Z"GlO:yiBxĄ l"_߽|%l~TJP?=36G4ەRDFE&䐆~ZUQ%' 7A+Rvuj؏~VGcm. b4uԩ g9e (v:tʇ=NQIv$0sqt6ӈY*یkKͅ F6mk`6 #qLh6[>d IW@KlWUǾ4B[IK&qrX %Eh $GQ mMn ãЭSju N$ѐ)G g$Uu-LQ̷mAa.*\I!!J$۝sT"gmBJiuR;TJZFO⩠-'Rjyb(ëkr=Ztylܝò$:掗xt(jljQz֭F}]n%QkmHƔr^QXqhJ@ BR)siip BH!jʆI>Wenmew-6m6" 892Hq$^tyIWݪu62%&ThAro=viOS!R7&@ţQ{l08!{A2 e-)]|yj>6ۗ ĥR7R&&[sBNU4겶Zk-%* s. "S5m[mT^]8BZX I8j:b32d!rҋ[ @4UU'8\m2>T}M'}(N\Q-B*UWRnMsʷY}6m;ӫ9떣_ .f;R}oȋլ +Ύ;O@ڻo.I,F2ZmIuR}5Z4ϑ+0[PP}=D9( DU#],PfIMz$_u_[o (~z7i߅KJ[aa+pA_MQj;Ot8Gdi zn{M|ƤNnuZooJtތ6c88ӷ]_X0FY9BmcXG˨e14ʛP=Vu Ȋ=NSqx$cuʾ7Kco&Եm-mζ_ X)wXB!2PwpyK3V?ހxs *?{scGX`"bJyiYU meNeJǜC/Q[[.ݰC[ۘͿVA`)Y 5[\xVbwTU-VnthHNJ'HD )TۇbmnÿFzMTuڴJS铞Bpq:W6:qa9,1NtKtzsmR@MsɆK \I9$']We|Ewؗ#`}5FFoU e5t $cV}ɉFmma Jv)$A.rҩE,#d1 FIXSRʃصrJNѭK)J'6g!o\Xlj D{~~cTY0d}fi1$'}y>r TF+J#~JxQ~{tn |]vnnqKRGq2I}M}(\ 0]9 Mlyk#6TJUn1*dG6QzX >1Dv,i˶םHnD 1  :wt6Ons݃m$IeʞJ M(@p=C^><ҋyD>`̤8wL=ऎ2s<\-IKm)PR✠!_\g8Df Czu^$Hm->n}uV%بP&!S}ڤT`S88 N1pGV{ޤgƙ)v}]KMs\x9RXyI-< ]+*9ο?9,#}&|!"@qT[Szj*SrҐ2{?=Xq$>Jӌ x*bHR Bϝ-T'dKI:H?/Wf9{DU~qrVP%D8̟}45!YNJQBAW#P3侀3Y,Ϲ9Z?a oJ>ß:%.@yn)ueBi$O?QΓQۊ\@[}K!8f]O7Hpemu2=:Z :F8RaʨBSrRR{#<4 *RR?Πs*za11&$<}5LMT]RFA:"i ٛ&PZfiĒRBۏ>xx׃=[v+~jM I˓e9J\ c'?Ag{MpP:yXDګ\ȧF~b bX F}ΌinN C5UU ӮzmFy*R]PĒ>'$;,x'PV͡rҭ't'@D}~U)ɔ:.xV 3liEAHkzZFeLs$JN|gs&7XUKuJOBVul&qӀ)Y6Ԫ#E {h7E!*.Z7.2=yi-8m%IXj{}knZB.$UW U5Fy,$!8R #8k4ػ㾑U:}R0רH>viʕ@o; .[d7AW;=Q餮yĆ˅Im ;1K3yY[];nMTv'1w@ Fzꞛ#۝ʶݣzH8ĩvIijL9h'V=M^+?-,h@B@xdgI+mNߟjn uҙCsz_ͬ+C' ByMk:%"{I/v=(+i Eq+ɹm-ʝTt %NvԂ @1h_B3P͎&ףѯ6LnU\LEIISkdzlVz]6-ʩHS$X_+sRIux(lNuYښN vkSdFTL mĬ 紌5T'&TJ^Qh},m2Tj! ۓ [?P.Q.KiZǶ c62}7 TtoBu#c #DtӜSay]~ێ:>6|,nv-ʭ mѱ{TkƽO^쁒Uj|x7R}hL הtRlQˢm3Sce' p5 ܺozҰ\_; (q+KT*{m[vOZ3l{Z%1،rӛ2$>2 WGX0>6~l:v7}!qk(8>lBqJiM]I6xJ0¦P@{gsauVVs p$2ZVqDdq'V=Rm[y^B%`ڮR^wP \Fgi`Xb~^r:'MjioEZවpO4uMg۴i[JܠD`8QL q奓vYgVn@IVd)lRJyk7tܑ,)QLڽ{׎ȍ!)N8Z>2cgSUت%IK 4Ad8]Ol<{ELe F2qhJ_+K]e -CR!*9ߦ%)8fu6S;B')9_X0lN,@I)v79/Y6гk*N!@@TA)>ucж{^O7wzT`mdSc1!*-#@ύ]TV#$!KakaX;{LӭdS2nVY͗StRNխjDۊ6Z!ƒZR#M8Qn]>T"fa]TQ57#}N𶇈KMKM .m{~WOI[oEJuAI,Uj"S-CϼTsqܠM8ʢt(ۍc Gpé[e{s[Y[ȵJ:]v5%jAk FOm]R΄m;K^p2i:czuR곥hTU&STI.A`0h yfժRH&U&~%dNKn4,;Qqxq]]B[w ivSn"N%yKTJI T^l1ڧ=l_NǤfU:S^rmWOXj궭x3W}^&UJ=,4SqZXu3;;|oK[v([7wcFS[RTjmY ^rsfNcN1*Vm z<f]:R?^%>t{skFPME 66Ci;y P0CI噜p*@HP)vijhu`I:A=.QoJTQYRdJ-*Hc?44=)m,%J)Huw"k4mP=ӛi]g8Lݭt in |ifU1SnISЖBFGPeP.m>xR)[c{ۨ)OP1%ryZASrkӍj[KeZ:-Їu٧޽J]R r”imė% 3:39O2Q#)R21ﮮMSjJajsD%i#`,gk4ٜZpH:nPkTflo;1][ΞmRZK‹r[۩ZN}]AvKB\qgIƀo%2TO焥ٍ5qUw. _vU:)$ܹ"+kF?&Gr~:˫cy6s>3$)Ĕ.}V\&J.кq/eMo,--reU$v:NII ݡJXuH3谒{$v1ƚ#]ٶ+HvѧOR!RJH+u_[BI%z}DoWh؎:+n~ӜA!RRR#m{sLIņM񶱁KPMz}:eTu(P">SݮC((Fx)Da>8uQ[/@BFv*.BsGjJٸ{\qү륪K_ZCA AZ.-:ֳHmMMj 큀< gD))We?Ѭy$jv1۸;GkJvzdQUI8Dr:=z@ 3.M!ٮp!ğe$\r< }}3 =m JӘu<ꯂzU؋ `/4%._:EO(9ZШ %[#wϮwlգ\LQҥ"&}(p䧽 )͏}w'p6Tp}$~vVQ !Huǽ}Lnѷ~Jm F]ڊqZ*-"#g* #^‚MۇdJ k܎6mu{#R)N21ǭ}It~`Tx KLRiSYy0yOlSI]?VIkԾT=.}>qq\m!TFOT19(6}yy3 UzCmɅ-KВA&7 ]D @Ax*U0 + @Xv62 i1.G *S[Ct) AQݸTZtmVMݴ5.޴$:FyYqu]S9֬l=DZH= J }qՋSٖ54󶭤:UתDJ8'#@Yt][=JڪaK+LN0FIjўșBvP껻p$˹^\!xByRβFraNvq9#Gנ[‚I>0q IAX.4I!̝bn1 A(uQ;GE8rA#YwcwqGtm~:E)\gh74#%3p@uHšd$}FޝZw!m,%!5hFu @@J$Zo"=k j1p.h8$(ku8bZB]Y!+/kr 5Q]cEm:d|/{$xS+o+fL8JeqS:osm};L!۽8*y<;Ց[qJ[rBlǑBw<l{Շ{)RM\Ri$0RTP20ˏ}v@b[uDCyx>GOc"o8L6ד5MRlBoJQqEc˱渇, hPpnjYv}.dKQ# N2@6wpK}H ><ѥT0S~ueHf7Z41UzZQДm7'&BRn_9[otѹU$]Jtk=l'D^ jZ΢;KmXYnmWt FԀpO%Kʉvsκ6 Qm3y--6*0HII#똛ҭN*̷_l]7P ,`8A(sk1_ s\r<,mRoSH*Ϯ/ږKg{s]Ѧb!5";K%$($߷[曵;2L4n6};й-ĸrwtaV)-A[fӡ~L²'#8VwbۋNhtMĤL]E9`JJm+PJ v2|ZFe]5X%D\p )j>gH4ZnO\rJ-9R7,)L*=[N}&Կ׭4]5G[Wn2ՏݡJяCͥ촪-ӷLz=Fվ@y,ޤ?N=S=Ln"3,tZk!|Pj(^k`{j@ h Hi/#$Y,JN -߹r5wZgߨLaĄݲHUJ iG;Yiq_T,q'F ^۷WrcF`[;-{1ҥKJWf8΅)q\L0/F%sYz $'V+ReK _~@mxJbeHHpTj3 4+-4JGIƭD9ݧ>2K@a%'2}iS^KnurYm!DO?SS=uUv|Q*]qeEs@'52p͵ ˆK2&AK8ыI'Jόy=Z=* -hi *e)w}<5UR2I)X ?"Ԁu*2 \r+Z_K 'j]> ˊJiJ!_P+"zv蹥Ft۱L9x@JRÔJѲ܀"E\s@V.ߘpƇ>HoB79/D/r. Be!ܸ2+Wg8*e<{2R从.L&h[HN_Ғ\D_Py2DŽWj !'2?big Sm}qM>j[ ^uo![mZT]2X]m%Tp (BB{ޭ6pKlLڻc6 6S"!JO΢)X էF-OZhoLٞ%;puGQ9&2BuJI{5w]KaUӀtbkwvj.jC>>QBqc5ШTu6.iguIO*d~ɻGJ-6ј-i0( QɪCIOv{ V9:UDv-Nf*.TBH >Bckvgn^ϠR]S:vDeN,9QbΕhq6ƒ7H?{p=NAmgs>*i{m5ƾ ;w`o[zTww&O?XEӺlښo5K , ^?"d“4 uc(<ޮ콋CMJ,˖Mko7GO̵a -W)W uG~7u߻E7aJeHX}= rHYQXtɜBtbP9 ƲdbGX #BoǽQ]trt]ۖ/z{ҷ$-uɎMB8Ph$W*x͇V$mU>fz#2F}GWrҬ0vkVX x= MUz:N]yL9'!Ĩk@[ 8 ‚Fu7UV]q+U_ k6-bPa.~,u8ߪ˽Q䧹~< q|zTFMGrռp\}ı0*t JT+}_MZF=:D.Kn-];o-IӆgH oe%ڭwQn[(S@jL7NrԆAIJ NLI|LĨn5߁g1&TrOOMQ7~ߏUܛ6jb#(-868DVmy'?}Cٝ؎-.. vW4MSLs2+?'S4it>Lb.xSzaIJP0hxtUԞKrȣH#hX>w3>0L2FRD6(4wLG}#_h BMg- Gk=(qW}XMܝRȴ,ʄVm~a#AECv:ޮjIwϦv)G*z-#VUSa[^*b^nkpԉa/'Hct)MN=ZU^_8if\x1}"ՃmTIΝY[;U?UL*wN4zRe^4[r^,$! UubtJPTvV!}4^HK,緹IQRU1ӮJ5_źEܑTAEuJf l޷rt9pQ`nH]R4cC  W~oIcUv}AT+.U:3! :,(um页DlM\'J=f5F!?PBX>#>aqem) Eˢ쨓/Qeԭ^oڶ :^> G{ǎ4k(n3C( 2?MrS{*wv z]hU6t&_ D`Tw]`|q'g]m-)]* I| 4 J}F2Tҕ>6zFAie8JV130u*N&6B\6w6u:tu QiŤ{IBqՁ{ɩu+For✤KˏѨda=h- ^FY3vڸCix5'(qKw:ev硹--E% C"GQ)2J{#9󮁴'#<>ԔnV>dBնZUY|jn3Nm'Ȋ{6O:Y5P]'VJl;(!$Gyܘ)T՞`z )WxXƃW[`F-[ {t֢EoYlrݾzl2߃զЦ*P[O;_=zܝmmAF0k8Cm x{E+,~M1B+A-am3HN}Ƈnj\JuuvkmӹN%Y;Sv; Цu-#w{󣋪djJ@SaHHKCS0|FM="IKBVmarF133 Gvs8/vj¿kr1nG-`A ACӝREG`6V f_ }ځo6T/o(޽SP#Fd(Tԏ9 Xc bY1`ukox>Da yIN1a,![ $!8HIE@}/%PK<0TaP1qjգ{;zW/ݭЫɬ? 1 2HBxHƤx:ppF'jo.5M$~M+V^sBZ O)m- Ou_JKKP*ݿS#{j6ڪ'cnjEEm}b68o9JI?]N(w-ٖ+]\U2^@R1xn*'J8mKKA:I i>gvFw-E~ѩ!*X*S|KIi(Vxw*Frg''~]&P`^[)֒6++}d'!Qb Ӊ:9 Q$ˮ1ro7QȢYVSݭEҁ ɔ2 qcqjo.(VT 6z%N23ɊHJ$6r@W>sNu:j\ܽ⻐%n.W#fO?a%`']rߺ`ͦu4jM. ҁG^ qP^M2Wф%A>)Y0 =-O\mk*Ά췦E^ᢷzUZOpˍ"R sj%=\ֵuqn-c'lӳL[m6z()liߦ+g#txš;/*X,ZYKSXii4o1ܳfeä:}|%l#MO*r )@t&Ԗ=Ìb v1ma(>c~GۏʞʖjK6R(_Jl692qˤk?ikg5 uB)uO%AI˙R Cy8fKԁ@[J|~Z̩G CEZ)%™r*O9U,KÓ6/R~ymtqI\;Aތ@x#O~5Z (\bKԛpn"m8Rnǜ|p륨L;D>ra\S蛥`{5q,GJF 8P Hg\r[͔ϢwB^fm)I}ШJ r;A*Pn\u)Q ؛YKPIm -f̹&7Mf?3PRX[EҼ830kplJ4u~js>SAV :';qYRT,%PʊP(JJR|+zTXըˑM-2ke)ʒ3uy3Z2Βu{ iJKapD~K^v=Z]V*RT;EJRHm#έޢt˻,*K@~ڻ*[1hPT9%E#8G6{h݁Av.\Tfd$$+?~} llԮz7u(2o;ghjdB]1O䥨`g}SM dstmVDi*66mm;b\U J\|:}kHF,ۚbv}WԚiJ\R-D_3嬥I (FMo.ëMT ߰V[0PĩĒԥŤ0`MޝFۭSoJEs\GU􊸪 8ԆCIRByҲ𭶤-{}Aε_0:x~N׬h2IetHؒ<|u9۟{V&;<"&ň#: m.H>:Ƥ/vz2Q:gPJJ؇N}n9 p}Ώ]06|,8'N>$BPu5ƢjWjDTR >+HV?0\jvuZ:Wj@m (c ܤ[,uv5%kF&|J-.+0 .Rü|ñGIDx^K7aOGCgQ8mT3{uƮO#T[IXqǘROz^9-C 5|6:%RKhN>< q_z{ܝnmv"ڟX W!=u'};'%$WW'=(3 =KlZzgZV5 6Dxⶎ(%>Ors&+]ReD[\\ǓRMSrǫIBP5ORTJ J>B$r]iZ0_WT5. K[3R)J*QjPntٕ)2"dH*B;r{|#8Kæ@_ CdWSMŴ=?tH!N;[A?mmNi=<:/MĊ ldڝQ=6SYժ}vs~BВ_͐,JNf,]f'^x_{w&bݛinfZK !dc9l՗nu$Q$Xyl>X+Ɩ2;eY7O]}XR=YFJU9Ć)Cm|'UɤPkɭ.K.U$"XO%ڥ%t>!O UߠX&MGp^ݶګ|{:AZg;P% -|V4MݫIݞE2O+n ʐcT2 {'r7L-XA|ΌtmΤMo BCTijLuS’A:ϫ2s 5NKm*@p17PlS)7:j},{z\7\XUisƞA ygm<͑m=\nAs@rRm8О,ͨ2 >]NJ`mfCxGineۗJp=VWJTNQoUZt IإCm }D~뒊e wxt@>ʫNYUI[fֶ ezSIIp?v`\kPh] nRw. :ym\@xcC}lz!k ;#y.+s`Oy%Nn$eJ᡺)|BV5`[nef,AV%Ǚ$al Z/ĩZ_ FEѿN4TZ1V}1_j&-Aڱ^J1LFi*$g}\l#حU%RM=jt/&Hh9+v;;vjXBrC<Tq:0?]*TfRJ ~B]){(6=HxqU nŅpy ʹ+N.)iO*9$0T>P\RI%}0.d @^x޴U6BWcKPA c(DcOWݭSn|YT&z ZeXH!ZR[Gn\,oۓ+M9NiP\R v?TA8Ԧ.L5lDIQiU>P]ZO#5RtҎNmGrK=q*uMI+}GGscW6QG$Gi>IΣV/-#.xmn[ĖW1QTbTLmJ2ӏ)RIN$|82>.~gO: GqcYjyhp]m $wyO$Jt M`=lEJiP8 0|hwlݵlx?XӨyyNvNݤ;kHU[fhM31_k cRI?0O8,oJԭ]5 ąBȝVp)m[R֬IZ2Tg t}2f1쩒Cm!$j'Oiq{Rٵim[Am$ca|$h.뎷]7[U)4Fsk !n;eF)X)RCwxIsֶP-ZQ>&Se#U(tg[4kBIH)ŭGKKQb$ry''V++3h%h#J>q׷ap@Ju<+ꃶ6u~\ N&uRLGr#VciBcz`OAG^eToA] [Պ=RjFSCңUj^32b>łMsn]:.R`<"vP+I7]?-mڧ&j-gŲ6nOw% JzCVT2t Bꓩt k  ;_ [)}:c)2Ni{f5Flm>SҦeMPV c?oo^jRD}(iG[qH<jMDWW)L\R%눀u[S) ?&KݵE5@(ȞmI'/##]"bJޞyQ cVSE^I%6ΔoxuQTU/b$"BfH;DZƚZZ߁yُCe/$$$ Q>CmKEzR?'88ѓL۪&%^ZFdMγKr,&->RRJ? pw6K`{[r--->`ZTp:^+eFBs}vv^Νt1li(DmuRM3-!-ʛLY\ilIM#UmgJ"}6HXR=Dtv7:ϸajޑKr3!Odg') 럛EOnա[Ѷ~U-n.wZTH,a!=Ψռ=Zaka#?KWTZ]ou =0cQ\mR_ScרTWe.Sl%5n4UNyشvg{@H:h-mUZڋ5I?midioG}[_wkur mrCj m].u&0׺|[l5Uj^r5;J ~By0<j"2AX=M!qr?Tz (!`Ͽqn3h(t\%,/8ҹ-h+MMvC--!q#/ {G}Ƨܿ-zo+lqTP 91ѓ좖W!Qi^*5 3Um-;\VrӲlRV~AuB.CNǫ#+'bÆ]lkPf@R\JTTx6K8% PZGTo[z~=^m)m([OR;~չ]ݵ{ yVP1iT&g`2RݏnF|eFÕZeJR2txǦSiXwD>ȻZfnZAo̽8S8|P(!%'r|Q6r_qFfji߭ lIG>HS7NNqzrΈom9&ԧ`%iʔH%< kTăNiWHiR1{ h cH;d I:GWyuϭriЫ8HiS;:v"%n?Y*NJcݰIbwFRm"[ѠLCR)^6ƩݕnL>tT%wT*uxfl6 2MA 061 RR*9T=깺ٰnSߪ*A2*%5(ޡJT3Y"ta (6Y(D #S`1`r'BΩR͗)[~P!Ԇf$9%N#B[IHH'Do-ޛjtߛG1G `JBG7u\T g\WtU A@29is͠Z%"ةI-ڜuKUT4-=Q9og"`}h-MFgm(ZrpcÞai˯'%)wi\PpF䚃+u~}6*,R}c*$!=J)M AAM*@<uV UO[ҩjS Q ۑ4 .;Ƨ[DYBq'Sr#aXkyB[gVMv΢Lk\ 344_/fJo-nwJ8iAZ_TçqڼtsǕ%E+}%L&_TcP8SR+_u|q֕4rZ{L`$ UZL}%/ҟ!w!cTko8h.)i_ʒ>oF!Qd.Kn%b26N}vtw/UF7Ϡ~)H44@%8@VIO`1r;L*[H9@EУ(mU2F pMQ<瓪qى3dAv 7fs0F]FZnu)pnzEZ!oap>R8t!Iq]`^|HГV$[!>>EIGc^IT*;as k\Rc5}g:K~Ǚ(H0u="ֶ4"ڳBYq6LpR!)΋fߴwnZӁ_K6}!)<ʐRԆ6% c֎v HXqj$}dSRmr e^Mw7-Bۛjo}.'KSLR_k 9O}Z42y!mngHRj䨹mz8)CFVT1_05Щ@2|[jxuK )I7bTdmDo /&8(Σh&JV9-\tnKnmN3{zŃ"cehy*9AeQ JDҒI܏BS9$/wÙlR.dsӃK`B)GG03 CnB]m8)'nARSJВxqUT멹 ؙ ~[BOOs+mg-$LLp@))p~FpSE#=ǞNeH2\Ca *.+?4 )-!9q89ϾBT6{IoԗSF J +yAKVYHP1].m2Jm52r$6 4z7N}!ʼnۋ_scܕV*Ik#>(:лL;.O*ciCbcO:mSoBÅB@JQKi2>T::x!S}`'VsewvfߥbmJw˧[> uUSAK}Xaw]y߶]ϱϺI/2B PJT|yN/;e~ uŷ-2͞ʈAIPa5Eޗ53RzxnGckJO |ʆ} o,N@Wޝh莍P4q5& :dfUe?3` d@)-M>+K)0cYm#9@H<6~w{=ܽݳR۴vi"jԠm ZR{t@u~McE(7NZT{E="E! É'>0`#3MҖ mkh/D]4SdF~}M%iJ[VP$4qlm~ϮɸQj<7eUܙ1\mg ՙo[mUmԊ 6< ܄ Igkio>չ.}ICǕ2*3 <줢@Og9}FŬV* X 7G֦S0.*NZ2Ļ56**[)D)v:QpZS3CqeLuqWc( i]-:ԒX|D#թ7ȱg(1<$4 m·aA?:KaOm*9@gF]t pϿiPkS*9#8R !\vʄyhg\4ꬅ%-JQ$N< ̋^oM=&<һVK1f$+9ǧ#gE.Zǟzb0˲3[)HQ$#|O~5'E p$iX+7'\Nnʬk|fQ\vcM4[?+|͝yjẕ(UkSǍd $꼹:]U[[Z.nZ\'ֶZlX!dsU 67mVV%6e?YϤ dkR%8VӹbO\nagvpw&Rd\+ %Nѭ’{]RGsO=[uu}TZ e" :DSb ҡ:>-v3UE&nU뷤.\o^C) D rDHH2BV!iDڍCG2RvLr]UZU2e*N:$+n\YlYu ) I*]!^q\uV* .ն U;^[TZ02H.X5Qus( JBң*#Ruk^.B䃇Ff؂.*U{6pZ{Ge!xfm}UUreBl= f]R[{fuU"@͒r*=YR?n7&DQQIcǟ!Y=Xqioz.&F^N/&"#c)1WPQF4s%'f ^ߥM0eᏤK"ܛ#tT(SJ:zkEJҬʝP_JCᡖ <~ Ķ< iSx}S=6_Thj]iHJt dh70VE4 n<ԟ8] g{FmVUJkRP%T4<2H@#Q߇p_R}*SM@^U *S.<ڏ>x|gtʫhwmuRnwvy+JvoUҒ̆FIeX_HJv 8mDuY{}}$4^>[ KO7.KMWB[GA+W\W>a?ÊqbɦAwYn*~S`pH@OBVH>x:Co꿨e/>߶gJ F) ._g 1ՋmLʱ{]@$_h7Tԃ N ㏸A5r=Ҷ\Vړc\F}-YQ<7K!y KRN;3Ml8QʗkVqUm|՛b®[媍2(u ӑx#iE%&FvPC OM2:Yi Jc0Ć{f$$vXPCo1>f$8¸ίrTiE5Ndҳw ~6.rP*S{ʽYvP2yQʜjf[wtjnZ;pnE);( JA*ܓފ|d +&˪R2TA hN8Q\Ē4s)&Wλh7ÞxA24_p8Ï8sAOv@-Y1Hl[9o4F;Y 9L% :8oE2c<9>͵AuXW!j$rHq Þ5婖ud‘9ˈ7EW{G\RبlOSTjtr>B͖*a=$FNBuQ1j*mBGr3 mIW N]=NQn;ITWinC+Ƥy)IR Ƃ&Vv+RޣӕN>אʜZpI=zOS{lSޒuGsES8!f[H+(sj#}6"KqaޱQ.,Lc2stm_qq\cteABGn5OlZUnd.L)5KmڀP5 KBĴ'dCXϩ3ҧPg~赖ˎ#8WcU}bvuIbwVVzHfBRpJJӐ#:mtWyct,wgTJ]j춡-A B $r ;|́UP p6(Ohμ_n;2̴RQcsuqGkƸ! i$:-ў j mv$J-&ܾ߆~"Zu} 8Pƈ.Jb!NTr=h {c6P:EN@jk}Pmr1TrZYtYk~zYu&CjDh[Jxפ:+֩=p7d,vy-G%ړ*HeIyc!C>uMui핰T6r_sD 7w-H>uAf-.Dݝ޻%Fѯ]nvʢR1Hԅ;܄Dq@' ]LF'*xzĔْ{ǜc׳gJB[(HFy#1ήKeAji/q*>:eSB qioKQ_IlsIQP.BW 6IZ"^vI[R{RKh#~jQC.{2kNw; :ͥ qLp>VJpRS7|}P>Q¥ӂ&RHʖc Qb{Ҕ ARH ݮm(6cTSB;žCD=uR# Jc?ѽp$]3κ4ZL\vO8nL q 9:~[FmzuЦmqtmH GHtmOO3bvٸ5* uLʻr 9} ^ׅ׳Wj7niz]n# -@yp* |;\\Bssސ-Nq*fQ#2p|JHL)8vO0lm.cRkg*/)k%RyI?N53T[z)a8E]['q-l1Na;Q1_*s?/$Hέ&!QbyS/vvŻ FFHD)y}s׉ G>& Hԕ%ť'$'?cd'-2KڰxiMr%hG>P ǝ` }jmK0{ik9{`4m錹JCb?SP=))kA. +C,Ʀ eAK= :4qqJ|VOpu@Z>M ͧl-Hc S%2r<ȯ tZGrEӢ<ʔf߅1%\Or t{ø1>yd'XJ[}% ?QgbfړTjKU5>' 1k'=䃌LV(MM4PM!GM]MjXLz BgTu!8*ph ߮nۓmIՂBSmTAJܥ>tK>z[Srh1M.Ch \aAIIm=`LʍspI6L[-h'nSth>Yn>+mn ĴP;QDTNp|?nlvFYsIzϙij ݖVN2p>jڧqҧZ`I*JO~itUɘ"OGa -ӑ”>u\-Z~SK#4n&;eUpѪܴ<2,rۨV4U.M݇uFá-=6ࢲ%#9ptv7WH5N.6%O?qTLR)IJP<Ntc^inx*J;N޼w7ݚK)5seR#ءځԵbN Vl%\]֋w/˾%' \veFBsuV+aɳ`:/Uw<̔%X#^m :i]`A`}`d&mP5 EQŠ)IhW!S͒S: wZ|T G>UWe[Sd%jJ<s4?B ܕOV;4$jjdLo$ 84ހ1D Bvb~{)-MGH?nOͥZQMbrI+<[e+#rvNwDZ]vuf)[~T)w҅>mSM l,Qӭ[EpB4d3Sbd$,$Ғ2+>z]V-Nn(ż]JO.w1,,6qm!% tQDXZJPě.%Z(4DH&,7Kx&-^Cn Y ͹-^AE ˤX:R*s,|~6b Գo7;szo:o27e;\~SX~$+xS΢:Z$1T޻>Ћ$n%-DI}I _5aiJRq{CS)P教YR F_qOt9ӥgQPhu0On4 TOd;e~#,WE{;Fѵ[O"]5nݸE o @v3_OqEY^7Pw|]ݤ0-afP RP%Ɠmj^ՏW@sU].ˋVfݹ!Ǫ4AnT\re58/9|utM'( 'J=Z@F> PH̛,t?޸趄emRc2[YO"4q(N3yf^V5[/>poQfN dT8mD j*4:z p=qSY6%qk[c\oH*P;@_>tʿЦe*aӪOz ߡOɤ~+P>ʒr4-’~{ MP jq'c17<;d5`gvMXQ;W6[%U}!ЬH'*y_`bI҅hȈ+Ś!r_XnmZݳ;tvEuJ2R]q%(QRI#'55to^q75nfձCV%dr>DllmMl(SnHqY.-]*Y$>ڜ:Wm6_D- SǏ'ϑLO,D;N٢I֦8#' R4S—"# ަQ>u]'.Ȧ܎nh)z*r鯌rFPm2ҳZgQ=^şB76温_ÆuxdԸ#T8>89kfzNUVū'4̸i4Q)mR*ڠtYBRn 8U=NQjкhYNdPlFIR6R}u>:7bW*-W~Λ^7<)Ttd6:^sz= n=Tj훿돰[i)OJ ) x$ KvX;Dt n7ڶO;V߸cHո[ӽ=DZ+[rIξzqXdS c\[%yu߆~R--)X=xoT&$et)$V4Bw ly(M̈́9nKRBQ wG!6~EBoߔI7s[ڨm_)}NݪWW7uB"M+i-~ʲ⾒pF%@`s݉ (h ҶKl528}{|e_,v4>/It}Ex-C9fx o,y1 dXz}:<(4$`#ڷv9h}a;ݦͿ%4ק S)8#oB5Iė-)Jd .R?md듧5ϋNNm*զ+nem%O PL;R0`ǘ]V}vWe:ϑ*XFR@6}a iY JrIӅ2[jܭѪu [L!6{󴶎n bb˶7֐v8=VUR2jjٝLe%;xo~uS Li[J[_qVT[ӮrmE+qX-ZES>*Y0YlH^JqϟVXvm5}f[ANHm)j r+J>D RGEEf?uDHK13(18Kˆ JaPXzIzV-J.(W7yJͩƒ,C,dQ(# av ˥cyB[ }InڈyCHG>%:,)=2lW%Iϐ8RojƳLUFҘ[[>5 N.Lp:nJm{a(ryJSnRLu5IRC]Q{jiHBS)/ 7li7T?ok4U-? ~F{pVpO>Щ)Hiĕ,D= 1g آy*|;m)+K'|dԔ[u@Y'Hx=2`@Yʉ?m7M K*>4henIS8vB[gZ Β>VS.h|'+=&붭vM~Jѐ.GƤGÎ)$ -u iB~t8G?C? , 1*8$֬6'*ȨNAGPC̳v깫\5+JsͿ. UpJ$`ːc' H|뤻G__j&dvB"l R+몇;=}/f7EMBETIjmx:*JJZJd`8nGxp CdntG;6wkỔ]6w,jUVجEt `wvEM;{:ERJըAyJnE.[IISg9;ޝnM͉=d'?qB# #Bڳ6?26\yXJ9}FbsꖋI sΥNWQk")ykоQ2T ]~MedGvd Ƈz{qƈo:6s%  0;Cc*#.6)3q(B_zT$SohfK0S);A4\FT)m%]#ܟTuwcvۛڹ\6[Z(cd2u!HyύN$Rta%廻Gt/j]:,ʈ 2xN5k7dwjlN Cl2jD_3hCmXTƪMjt nT@ E i! unRv•ի E{ةQ y6Ȏ-ȃ$xz3Q ISIYjd &W ͖z,76$v!l43=4%j2udYsI%j`%_v u=TɗMKzar#5dSgԐ,H$p8I>Z9婁c [_X1ڄOtta-!A#vmE3a"m+u@#Svm#f7E2oa*Hd29#Î%n8N:+R]]3΅jr̻RRb'Օ6 *Jy8eLˇBVvm ԙ!-قpq_q'%iz6ⱹcDWJi_i9)wBG_ ܝjwuWVE:ʈK%@()'!A@RAWcΫ*mѪ|XeZ\6-J#)BUNjRg3;@As,?|`53jwf&lmo)\qSa{V;%Ҝ*>:v ȫ X˶ͷn>$- je[OI?25wzhta_*U)ۿiD@}, =w )Hep{7ݝ|ntZ!ˣ8[1s*K R_md%i8k |-mN ^o;O"R%Ej;mT; HrdyHC =GK8=٪\^.30tx̬h8y1IY!3~R4 ZҠ‰8!Bև&j9$bG'y Rp1YR&ȧ%c(T2?В?MEIt*2dXIRRw9\ NxA#OcT*~4GKMx'ssնvWg羫ioZ'=5cy*uR}P8@ ÉlG' n [ss_Us.vO,EsrkdO tTFm)D?[OkBԵR̋o4R{#r/''95A;Oh?1 x'읋\۞7[PcĹ!M[ ]2|h.Ь6A}Kx$r8]Ô $][c1 Fĥ+PsduoO;jWXiOnz;YBMB- 2m(Z{Uڕ+v[[mD)DF.P<אmU9H1q,uI<…ǡrڂǴ%kto]üm~/R3Рu}OIy!H(H e#s̅S`W->pӥ O*w]ErA}-~i V)y֌)Cl bl:EuVg* f6jp^z2rO4r^$@ ImĪ tǿ]#n\{wsty6ǡfەmLjW!d `v`TWDvcv*rd[-}jj}7Srb-c%>T=)i==2.y˾)mq\9JDLiO(|B_`!}AIC ۸o'Y{vmhĚ]PIeԠx/RuFÝa Rj,[j$}E}{EޯR XZuTǪǪ6R dڇJGud%c Uɫ554?"WHcwCPs'۟mj q#HB..%4yG^1Q&CCq?'P:pIm/~SE#6g _*ρbT71d&<^K2_H-jMԧGg*[jk]ʆAO4!ԘPp>8;G%9LN (XalPsfn\2.;T8A*sx$0&C\uʁ'^@#Ԓĸm^y}ai ?j_AGĎ.<"' ~ =#.XKM4@l%$cMі}!E$$t,( v4?ZHvC0"ERW\OpQmR-*L,2Ҥ#J3v{f7 ,) KvKhJIq(]Q\rulc *"2\6IyOM37SrGOn5-rqפ.Hn:2RA'"n#j ?"2vy<~Z07R!XXG:OQ{Ηv^W%^nSfV  H/ȯ}+v$X*e)Cg$p1䲖T"(q୷. T43tfStqvReV)n3ڤT86}Aq7߄, h|bG2XV8W2tJy&4FPIR~Fu$6HI_=?M!u}6i+S:ۑngPV,YP֓m"2۷c0sHTgsj5Q?oiP;_ԭQyT:>Kig<$|niT␴=WoCԮfs-*!jtʈ<4@Whq$wR}ppSg 8Z,JcXgqloUGO;w [k* »yWŧ=-更T+wV~h֭IH]+BHZGz@#tQ\ǒ򤸀~AdxxW*2":?ڧ mkg RNI~7S9^ʕkwzCmntzueOp錸m˪.WG}7B~}x{J#:]*RdRΈtSpS麦ݜ:%G=-znR>(G>=Y0`!cS.]Ut i-._K-i:Zii X1jI+(IJ`r2N7xmRS }w9^F{¹i Cq*BJUά(qʞ GҷSmd0‚C)'ِNu IBS Ykm#J@Or>$2^aLI FO>SL~2,q3-P}b9Blb/(aąi)8Ht%.H4'jFk r8i+)vgҴ _y9)R  >5꒝MbT!OVjiҷ Y2Q Ky2$)hu0=Hh-`Y$~|ZmdUa$:Rͳ[%:?oqcq +T[aCA!{JI >]i\4zJHǃqh?}h*JmNw%$q'ib)|H>%JB,wd(J AJ|{{:^ϸA@BV5 ˌ Qm!A_($U3ᇖ磊^5_KNLL?Ց .#2T-.p0:8/< UD.Đ%қp#Oh]Z.nz6ŕoZ!EJZ/Kw>:.gSeC *mwr00uXR@?X(Jtv*"שϤ)YE:eRԗ"W)J?a ѻ-vרN.'pw,G$Ķd&CfC(P BN G  Ã^o~wXܧAНDD+Wèչ,Rl7~#jwȷUהQy !Yʾ >S]&\f]_-g% NL6Q{ j\w}Bʿc[vz lzT%EJpRNthmtnw)!ǽHK%;qxm5E9{0%ЬZD*e4Dns%kH _|*ed5šmmħINi-~fz~ENLnU; LH:SCa(ZV C*9R^)RLՐ{!_f PʕY},V"$9MZtdS)mKPL-6a@Pz٫ڴY(WewPW2V2u+q m֢AIe6'p7mN/KvI [6]25M8>:Jr`g颮Com^k۴Qm#ܨ:jhL{'Zhpঘ^O⠌c.6}.e/ݬ/qtw#NP{tPبM b3FN?X<KQvJUǪlm]=zʛiI=G۝ּjڨƭ[)ʄGr; B#ԹlM Og3;=bd!7F|*w] pvku f!5#O) >JmMu,u=B tfe4对=@A jXZFƹfmWQj]VPI~2Py=>]ZS[A/֢]QN>e2$}p-fje Eߘ O/5&47 -lk=FەHU5R0<8Zes[߲5ZSPj/DHCo$&gɵoFV9vcbXVU*#h8kq HJPy<w?I39 P$y!]ή>)uZ$W.p={ukS؛ӞvaXmo*܊8Hc, K.AgWjW6ϱlZ]^W[>eNSci $HH*%D@=JzfLVԳ* 9}^:bD8 RQOP]K-"Znjp7SJ㌟U-)H'OsɕOBhZ=8!-QqgC@㍄f,v)̎xZߦ$s▵(~ed>d\Cifo[TꦷPIz}ߟy:qt-0Kr3NJPUǎxHZGO22*jGǟő\IqJQс${z[4IU$@7^mriS.\q)w,$xSHܑJOtAhR;j鷅=ߗ=:?ћ~Tu%oPYUkYm5wq^6ل^>2Ӂ˓CrL~(eDÔ$B'P]/@ꊵb;<ȟ9bZX 'pF*%8JGrcFsA|;~6sf6JʹfP׫uXöXꂆXmvwJJDVP8JOW̓/~6Ө+lb緝lj[3p@#G#G\`c:ڒ(`b@vO.c.Sܗ /R'΂ ϠfɛKoGz֠\tFcJYC=wH mĬt>}qWiqy?GLTՙ}Ie""Fri ^K2S>r}2tN"d穖J00?q+mJKƔŦDSM"ZB&88sQ4!09B(Q8Dn(=ILL(Ke_ #Wݧ4BhB}VhQaG6ڨ!-2@u& BR'@HK 3̘mP#bB q䶜!8>^<Ʒ4اB3 dd1'9#mkG$:i*LJ;=}M?Rc1`7 i]~WK1b;[X >۔RO- @Km>}~=~cQ.p~)OƕI[--4 N?>֩٧45P.b6y*Y>rO3J=BQyD~R N`Orܔd p?:Eu} +%=)σ:)ŗ,z8n;$ J㮴~~SR;|Rfo31JR^({]A}FR<[-Х=qW(ˌ2Tcj *+8MXSD-Sܷ]9W>Nu"]J̇[rB-j UrHl(RIO'b=fU)h['┤\>Ox+'?,mP $ua]p|F9=.2z9}G' ?鱓 ԙ R}=/{SRIRwJ{o쬈7Ro; ҪΔXD7 / B'p/ͬv/+#yj9" r}NSMhnD~$OH' >n4TCǂ=}*AN7*RiVޫcmXS(@~m3NhpZHw"#T +hVWOl\hӊ##RP>5 ĬꉇOlm +u]J|nφztU(w-klIT4=U) zCTS Q34ءlSY#N%8C2N}Ƿ53QjJ'Z%~\{ioPV(N>qKJD9yRǷOMrY$J2Ɇ{|ccl}u"]qZ%  ?N~CNuMw6]l k$|>x颰 t$UжkE~Fq$cϢT AmG?4悐 9^->%AU'? _oeS\OY0^A6ͥ!hhק߃HyŬk-E,}fFؐ#k4co3 ?kXnpͦe X,Iƛ!ъFOOqa%פcwեM[T'Dy-#H X:Pd' 2/`vd>s^bЩZS/<֟t,JS IW~jCCa*)#娝*Jt,30hCԊRpms?lL]oύ@++W^m.B)DsRr9t QfgVB1Y+ᱭl%o#6A2\%A> :U*5^.2Tk@d0WI1Y?vo֜+7/ER~4\! [hAOi ) =sEܻп ZJ C7%4-uojڬWIqQm=Pa_G5=7]2 ˷vĆqfHrVpQP%QSU:i2yn ceOL$ zٶWUتq$>03mjo</]N[rīZUQG}-(d:PJEZLmõKsQM(ߝ T =CIڣ3UʶYv[FmKGcR T9]*  >kOP=w2fUqřժ U!ըlҏN*`Z=S`$];6֩8y%9ӭnM*mYuڍ̩ӣ+' 1䥠$%%*)v ¨7&,zZr;텣( jJJTe4s$r":K +W_]ɰJ.R|ǑVkTHy 5e*әfV:~xސJMڴc6*JϾ|U} ZmbY6߬P(d(v4KMTFDʡ8[TvdFʘKJY%yPm)Hit 'R][ }O۔a@qu=[U]V52:S¾_sAj-O<Ƭ*EplnZ~5()0RHqjin-9V! > =t;jOPH+3#$q%Fs]SVIy6Ӻlap+XQ' "b<[83N85vmruڢV5պܔ! GO{R}34>ed'UG+]bXn+%j<$g\-=;{/ω C[1"݉Qaɩ$|AGv04"eLX*D;-D*:mrʤRi3)$*Ha.P@?2ƙө =:itSU}ni-ݢnqܨӘJfOo#VR0 AuFy(zTuGa(ax rzj-'v/mD ԓ.=[ ,)RNROrn+k3eikenaSlBI$׺: G\$$ `M4eM}~씞TO S7{RЙ.M]3yv:VI0±}u^O&]U 4\{~vmC^9qk![xtj :RId$ U5}[{U6:*u:XS斔Q-?7(3 `_[wX0a.H3 8'0˺d:X[/bP62m: ErU-eInRks Hq LEҒ@| 6kO-海{G/*cl~j92M2ޠ.VMekϠm}B=ڍV6}mm+sNEWmrz8) JO9<鏨6ҴnʍZrbl5Z-,6fʿxRv O6Rćw/ aUK=OIY_&d|wM~A#-nEj]r/^[-h-:6[.CxkY5ר^quBu=pw6FR)=(Qj˘f$3Lv&: ֖R@*;{5|\C}xծkkp,:($èqĩiKR23":>┞WԃA#-GTo3F*МLj ]*"i;\7;Y{{q{B̩Kڕ$(%jˎ08ҰUE2dQJ,~KSQbb[Ŋ~-GJHM) ӥŅ]ԛDf+2жygHKS-4H-+y$41CZy¸{p9jTh霶ZjT G z-j^ JsET߭Tk[q+!<Ԓ= '1$-K[m{cTIiw;Syi[!QDK҂8 p[tQqYAySdMs}~ͶO@R}njfTxіR۸tvA [o]aTV Sjt O{hSMC40}XXR{R8'O?+.6+^T [=+_|istS e R;{^\ ؆乫O)i>'p4++%Ho ucS(BGq:,Km@iǸto>【{ßmKaO{Afpk9\Mb#cNZeqb4 v<pm|S9H?L{iѐ쥼eupO!)o^X4Τ (|1.k'E^*,zREǕZHe}5K> qcϞFIRYATb1,w2}Bh?PN5UiUQe:=#ؑ84&L7b>ӭw@V>*Q1pTt2VwSJeDhJNQA֙4іM2[XKgc?i2Ǩ6"Ą{h9E0r[xpn7ћu°tr}' ͒+A.g鯔) u G2~LSdEw('x C;HI( 9Yxo06I'>2JJ 4F~ԏPeo-*ZPύ~ay2EGkG9DDBLKppHmD1LWOw얠{#gWKQd@HA*?ε%Oz!G}sMSP!3aEC"T[qL{GK[:q $rshPJR5QJ˔%8=^Z@5$%8[aXˉ<}4h.NjR5>5YaHRG )BJ"Rm“0sx ˼f{ZП b,S̔2(#{8P5mEUs{d6ۧQߺXo*;|]|OFL;^yM>ڴڂ28(>8ǾW8cJ\[֯"XuW@l$$x'rhMV>.=26cΖjSJ@N%DV@ (43li)p/9>G@j.*WM=bTK'}l[}sO&ӤU>F1wHUòQNa FSԤHyd,IQ=j6营ɝA`E&wi)MPOjj6P*TQJRSi ]f uZn߲Rd$Sa!ˎ($gVőۯ_d/ja)ƕ/䎄Ҿ#+.sZۇ =TܰZqu^fTmRV|%chuɷ1Ju+zm`%Pۏ0irpZ.Jssqw oڅt_S%jB&I9K,0BII;zUz7;[fMR~ 8G8T0x}񦎖#uW#g4% J{*5UM(xJHʙnz" ZUvWHF_m6lѪ,Xu]EȊZu$$ݏ\ۣ{]!駻2 ZP )dǜt(JI(\|೮>c!hv]Nꪝ]$:TfUJBAqZϝ}1+wz`a(fK3)G#HkS$V ]wM2ͪU#re2S}Gh%xK}"[,SR*x?pfDGeDYj m_t5&խޗ^U>k;TǦ]CLvrqΤ6g^5Cinm϶&*Jݨʧw&GBӟ<*^nlWS rqIL=WcHdIBji9IQK\:0/\۪{ݻ߲Z]n22m)'uW9;6M+9Mě SQؓN q2R~ڬul].{DIYZ[қinT1|jˠݱԋ{7"b-WrVJ t#)8':_BNɩShʰt#u[9GV+=4 n^hPnsG? ZJqgVBVevnJKusIUfR[Vz{tQ[K|h1C*2¢1]ϩWoZ0nŃ\+.b{P22CB2Symk6W-Wm ۩.4Ϧ2LiuR}!ؒHFN0DաZNctةU4L̷)+4&6ڻ['#m!Άy PBUu 홆]l,Gt!*G6S\v spMf[2*.jN:)<)ƦlB};mjF6=Hv]Em!UWZRozJ9 g}Q;K~؋l+[6ƣnGy}Lg_}Zc0޻@kq?`VPգ~εKo nCjjO!9Ұ'3J]7OB2uߞ+;{gϋU.קPY2K-XZrBO#O~t[m9ʵnH3kJTL#T@*N}iPK5T3nMy?r9tEA} 1ʔ<+'NlzeU6߳:4A7 - y^3;ߺ(R\\"OP1Dc^A9d,Fՠ79:](|pV^#9OaJiEDVXu g >_#8\ҡhvṉp颩";oxVdcI[l8PqS(%#6bmeZS'0=4%) 5̎^l )')Hso>:ۃ6Ob"bOLlqgN;&Z&bn$9ϿON7cQ70DZ?8)-:ZVKLG򲡏Nt~cZhRquj_iTd:`#5A*S)ss~Q 科$oBeyC($}t PJ׮[A2Wmsh"NUY[ԠMiR{ 'V%gƽ(ܺ X+M!3\G2P- "bZE5l/o^f׺q4h(Rĺa‚R( +o Ѕ.c˒2yOD0'K'X!w孺ɤJ.{ZW*ÿ(zU 4=Vpi@[raYw-dX ҅j;p\RTPGx!,s.*THlʟtI+ Z߽ 7oQ==GKN/{An_yе3$9v 0 e\uvuM2ٷ O6q]0 {XġJK $d>$k}^^Q۷ui#Tg5MmJ*GAV0qt]inmOj{WgS> 9R8 ?V2IHq9}(m+𷓅sZ_JLV:ҥߘ/iA! pHרker$%T$|;k^).D%_Jڈa%#4 )/8B L.8ȋYZOjʜR|p=3O:ԤA$Z턗K$hiU"%Pα5F#)=ZJY~8:ƜR^S|Z :l}%\HItjeHyM9k}](mn+$>P iO6*GY驫}G*b*@8DZקmڙ y‚;#|?D:54)D(O4LZw[5}Hj 8#9ZCZI% N?&Tj:4]p@ڔs]C ym*nlzjV)*QZu4Rih9)Z禩)Az3Q,I?*L [Դ'*ƍ,%ovUqE0ku2prO5&VԄp`:Ka[Z#HNIJ|iOTu!G۟fTDZ]]Αl8n3tڤFOjkZ[u_~eZ4 M6=)CjQ9ƺ`jRZ1.gi '>ǟ/R݋bu"'Tz?U+Ry #_[K\wAi|hԤ y) rwq6 9%9*:v[I[|DߒGi\*>Z"U]qN!J (9ñzV+J>Fp~]\XLdkc:%kQfiH=w8I$y~]u*_>Kɚ"ՠ5(yqөRΡޖ. #tzݍæ-XzqhhYm'u<==UJmqUCfCjGF~ݼ4šX K#!\KM8bP,6(x0iԤRq.!kJGB|ltF֕A-=OBϬ*AZ*Q$Ou%܁ތp|zJK)d5i’yrnu}*wtեNp+4O,xx1ۛgZ.yTtәmY !6G>0GM:AKc*-DܛVeT*mzָ"kTۭ8Id)$(AY DO1Q3 +tz]Fmz*Gw:vwHE룵a(Qpzj%Uum3mլMڙvwkm.tsUbs\nc_=t9D{hо;in&->Tݑ?wg{ OYH1ΊZδ,:D: nh4hG0FUJqI'tL~!ŕw9Ciy 9=ZZ(ZOմUp>l&U;JT'( &e@C8Z*A89u^yu>?'*M`~^:ܻp[E%UUZ l^o[ -Dqg]%*6}>M@ңX˵jV$S!M-O3qNŘҡK˱22q*PRHӶ%ۛ67k59rۏڤr٦mpHe}D$곊hTR ㏇Smy z֭vvmKghgt<=!NA ʋ붤Rr2m޷= 4MĂ5*3ܴ*)O>ڰ쮜{軅{vZ tOP uq"$T꼧BJ2rRڋHRG;G~ZgD5)uMMo-.A! ѝR([N='ǜ>uK2+kiOtvo- pABRS1hTw\ShH%9dƝ"?k)q! D[n6n.>"Zb#mu>K-iQ!uo)?9C6cu6yvۺ6\5cڎiK2i9 Tl0Dv.E*” z~3(nHQsS ֝uHk+ʦHGͬ9HkN ˭5>!r.^/-VPQkUXH~

`pT_&=ҵqoʻ.n;" aiyP!aHԮ{PmoFPjlg S'f 5O)dipG]f%Wx=~+Zx%֩Q"!Z@uk)yGDXDvICa)R0Ƕ%TdA3g;6)TTM+q:t1ѼC/2k;L=m G9:|`|/GHCC) RBq~:pc̺?HVx?5PW 8*QՄ̺E]79voUtzJcKa=Go-ո T$xe$ ߭Ĭ7sZu۫ȵ!EdN]f*T%rc]N~ܖ\mlN TUZ-զ,۴`ZOj).pf'i2ʥ v]PVkvhzmۋ EL"]w*VTb˚ w)-7)O$unGKjnAgo)sMY^(jLvdh+957'VZ)FlImGPYnܳ R}6O.㻻=帖6oh^$Ovm莱Nfe2_QuJd@ QX77/C.˨ oB8vؒW/JGOuO#?~5BH+V[q"-(h8)p~ZT|>{z`7c]WnMU2U8J*_JR )0bQz表~L߄jܭf) ƪ6i)C*J0I9 ~Ffً"^KRm.&U@/4ڰVr!DN4c;ҶV̸{\͆WoZv:[+\ȭz'!ğ;Vev>nnZ`S簸U֢J((rI:SӘ~YmEn-{^q#hS_LNI}kJWb'@NYwpduT "%%@%eb{|{u) ԫu䥵Wʥc#M>GqrR I-5q zIuX;}t[nɡmmݪ\ )j/]Tʛ 2uKmPZ#®-uqںp!~K[+2e4aGxL?>icjt:Ju6?Lj=d' Φ;pdMKjͫy2ӷ n=VRi7L[m4*]pEI2X)%#$'&ħtӅsګ""r  ڨ]v[Iq--1ԇPHy<TwzJ]?B0yLJ4"Yٛ)ÙOTH[\̠64 P<ΤN[MDhJ{P O9[IMԎ<<{itZJCHm1۔?Ϟ'[fHGlf|py9rr8h2PVS(BYIHƤqLt-4ƚB {~aAu H :xPt. ]i%#0s:eRYe@+?ZSCHCaS8?71! YF )Vj :ʒ6'#Ǒ2*) -I8tҹ.T#CBא)gZq5Z GLt@\8o>7֙`}ʬ$$5]k?kBCg.,%!-9V Arcg?_559!&Φ}ݜxҁY(;G: ϟQy.3b:\yIQ[x%8#q-|9 `-}d.An }u{j]=gvqӐ !ofZSp>϶Zܞ} Iq㑛RRZ{Wtm4'ôI]qm,tl-L{)1)I9P~mp*m)dAƗQJBܡ(c^:܇RKu%K?BeRbŒZϙA-IAJT_i{"['=;UtQ4렧BR-/GV ?>R"cRKN!NO#>.c:)'x#J"dԒp}?)ܞ0u_3%i|z91#ƶjuIIK [@<}|05 Q$Uml"QO[[E 6#JܜQx*]uA)V>S+CRPz+ u6lDjSa7 uMf$Qqk(SM=ҨoUKvn¥+>HLnƮz2 w,Wi ?6&Y@/_&a5b ,,]IF|LBԐCmA1uh;X.PZFiGO}ZI[RIX$qBcTf{k=݉k8>y X=q*8{cK,[^$%;w}ktGP{OӍAyT#qJS2ms]!x'_M9.'H'arR{I*i3̡!$[hWIv!1?3*K20xR1>K)Jr YW,œsϯ!8JPۘo?)$<Ҋ^q?UVBsKKDq i)ypbc бmSԾX>܌t>\K)p d\do#U'Z Z BGVP Ӈ FK*<⃮Դ}x,`nx(wCc9ieB0]3l bW18[}?ΓTh(IaWv3:ܪmdU|)]1t*Ƅ2qW"Zn~˚.!*P菃WjpPSJ$RBQZWܒ<-D_8nꎴ^qg(i* (bTS {zSVҌ*Z~*zD}40Orݥm:$ +|twt=1T7$` BBG9:wj}&$H񏿝Fݝ"%UBatԡqҗ$6P$>wc'Gl= -IGm'UeR4Ai -;Hگ>%JQ yMɸS(%G165*f⒨]wz!ޥKs JSy< ToTxhnS}ĥ}IC@I=7=ݍE. @u]7uaߌiRs;>j!.ӵzќuYR@>JCHz,-IF\[;LcT2%w$2;] ujҦ͛GZa>⨽DSeEƖBsX_m+ A$.e$xP;Z-+R"$1qLYy+@uwe%w<@V|G1ⒸwNxu[Q mBTAQ.BQz3]mh\CJ_Rǟ9և*.<XJy+$x&mBڒ֖@8u E3銨(O=o Vhi,j.Kn'Gi#xǝ;֧&قygN.!4ۭld4w[j:XZ_e9b=Ooi4JTڐ~R3})S=%eG1ٵw!>J[,ZO2N8tDO(mhhu?$e#{_x?Gv;/Mx5U!hfu&( <F}RAD~ B }M/$a}?\}49O!+BKty\9=(yǞ$)( +==Lw9Ƿ5T6c3',9b•iΎh/:j mRU( ?]mD0r\QRA8FAHyJ*.,v lr"%$p`k!O(vT)rTr}.jlJA үm1yO%C|}ow9Fo fuFk P-AiCç-hp|RU5!3ecy?6'\}~OzԼGRPx61#m'-I\yeLSKX2FT Jip*,XyNr~a_)Iˢ ""`Rh(5N ZCJC l8te|8mX @ YW˞Ic}%'L$w*ʣ$}ubKɒ/%Ͽ#oSd2SR'?ԢzD#hXnXb(R!-+JVHY$t *y)* 'jÐRҚh%rs%Z\ )Ey)!)8: UJxV:8N)S) %K#IW}+DxK@}{w5N:CD +9`inԉ/|Hz3FG| >sSia,F4n_Wv.ď׍!*1_ķ`z'lWZLԥ)K*`8>Sα"aZ8I2S;&F 8ODV[K*Sm O׶&GmеpW4+R#)VFhOAI)вJ(cnQ n`Eґ~̦X uܓI*$f㢍SkJ)FU#<`W<}@u%āR xn)jC)iSn$Jlb)2^3 /4R j:D5ڒ꓎AƫHW=ĻC3I[+s~34ȏ%e 13QCy{68Ĵys6FNp'K.NJ*/ R{1݃J1|IHܔ93f)nN!("iNp?BkN㪧@ygWb _e!q ['8g=.nIqANNquixRu7(Å 91Ȩnv898j3QRRqi[1=P{F j)*KϮeF (ЛyLir}~6Kϰ)[m6\Tt[q ii)G>~҅@ʏTjeN䒭 [ީBB^@z+ *!IT>PPQH%d)je{P;xs:Nf[CT;UiVϵV,+ M8@gǝ&(D5-,yju:{T$ߟ馷)Bļ(|$ =LJ, tPfʵgaԷ@Pk5^yA"tAi/ֳY7X xץ fP#x%G ?OZ? k4l !#g׃ ~YZcO=xO*d:fGb7,.֥Lf_UC6_7_:fL_Cԟ*jw>H~YoK_v#?j5|#?gZY~=fC#s0HҴcYg$.~H--Wk5\|Dm _-/:Z Ck5J>S UZQY[ǿdݫďk5"~HjJOfJ」~'?fNecM; i?5& O~a :m? fKd}N=I?ݬh? #W4~ fE;ҟ49쵵f_ _ Ut?oWcis>Q7N5M-}zfDGonionbalance-0.2.2/onionbalance-config.py000077500000000000000000000004401411742520300204100ustar00rootroot00000000000000#! /usr/bin/env python3 # -*- coding: utf-8 -*- """ Convenience wrapper for running onionbalance config generator directly from source tree. """ import onionbalance.config_generator.config_generator if __name__ == '__main__': onionbalance.config_generator.config_generator.main() onionbalance-0.2.2/onionbalance.png000066400000000000000000002126451411742520300173120ustar00rootroot00000000000000PNG  IHDRJvf&bKGD pHYsaa?itIME :"\{ IDATxwTuδ Y)((hb&{!M1DSnow$o1&+VH;su83 K~>`w9g>{\纮cs ["""""*lEDDDDT؊ ["""""*lEDDDDT؊ ["""""*lEDDDDT؊ ["""""*lEDDDDT؊ ["""""*lEDDDDT؊"""""*lEDDDDT؊Ȏ9]kc~ B#"*lED:uwҭ[7~X]}NO" Lr)t?~随⮽Zz!>mƷmTm@="k-/"|ݺudС,^ ;CϞ=c=;w.#G$H0gΝKYYs ݻw/cŬ\>1!Cj*~iW1ϢE4~o={w޼у#<. ,`͚5>w6~̟?{WaF{=w_d7w\y,c̘1ntX-"N3l0֬YSO=E9&]ϸq\eeӧ{睵]qG9-^]{+p7{lwq92d;۽vʕ}?˅IW3n8/\?t뮻hawꩧN;V^s|_ߤ>pᄏ;餓\6¶+q1Ɲs9nѢEc]9_1nڴimΝ7{-?s,hNlܹT{g}dz3e{=FAss3 ,`oڞ={{2o޼..cr衇~mf0a;̚5:hzjNYf1gFI,c̜9>#V\N M_v}w/;w}SҧOMz)5\q...K.a\wu~,]K<=餓k{Xl+WG):rH͛G !nƇ~꫙vu?,] p5p_ᎎ)l 9ϟ󩪪*<~ 7ps嗳tRMV]y œ9s={6:{,~;ogٲe@t+ᦦ& C?ΝKn8SnJnt䏈4Xd."H$WZZZ:+לwyns7bJ1xRvO{ݨ7N X]ݦ+n [omwKg}K_j\ۯ6~-] 6.}{_l7y+q~ݐ!C]"p{챇{=pCcjkp/͟9|t1]DMM .$L2tv? 6s)_vޯ xwӧ={ܬk,_RX{ޛ~m,PDv>*lEd/lxCDD6؊NA="]10dCDDT؊ȮGCDDDDD ["""""*lEDDDD ["""""*lEDDDdSDDw0i槼tsH|~%|HDd -uEDYo~ėRXK 9g 8O;wX3>_JYi'"VDdUͲt9qdqBhØfbak,3Iz-\w*8("VDdze`?FeG&Ex9Kh @d$ZoR0EDT؊l/̮ǻf$)A@;0B1cqƒ1 b!EaKRnOtxQ`ED6VEL?#I[KhĂ,z>1 ^b-}78^:kR }GM?/Q`ED6zlED6nj&hN1!g< `lwY.A y㺎3\7#NK0bOLcnEDL=""& 8c88qg,x$8a!ęsm{]t\kHxq@ٺn~!cI5@ԥ7Au}35 .E9`z>*855qb5ۉ ѷ$px.cL}e-"VDdIgq>tB/ʬSs?f.& t>Ề-WEDT؊l=M!c\p=MspC<97 h !55[D b &6GKh,q uY1)Gu9(Cx&d2 81Xϐ51SQa+"'48?7qlF yv8aI82r H@DdQg,3[n,qQl""[Ϲê1q!XXc|a@=p.29utєjrրM&jZc}J\Ӧ\HKLH0I҄F3 Hi/.z.|9:*7=|M| |\Gma=7am TԊl$ E)/5pѓ%8a3䞵\aD+:fb}A={iZ[ x|F5LL,Y_5|p!X߇ ZρËnx.a@ h Et""[c3j2d#^XL,nyk:*mMEBfgGF6% -J\w}/1lM/UqɔndfpWZr!<>k.kYG?ݓyT;2!``cdi>!}+""*lED3k2;g}k2a3!υ<{N5 @|4yLDd-S^ZESʣ[v0 E9OV=rj""ۀzlEDdq@Κ,3dMЃxÚ^#+""ۀzlEDEz.ZNs shWQa+"mMYŹOxvZBh8/CγQa+"ݻWG+H"";yLDDDDv c+""""*lEDU߾}ѣ!"hR]]M 6[Qa+""""VDDDDD [Qa+""""VDDDDD [Qa+""""VDDDDD [m ٱ9p)""*lEDvla!ZCDDȎidY̛7OQa+"cz<y衇m8 ,taÆ>Z裏(++SpDD>CLW\qUUU >Ad袋4LDDȎo~qz뭔J;x93SDDT؊l07?9C aҤI_.l3n8&MĴi2d'NdAlEc+;L&Ì35kͣV=hIbٲeSRR…^W_?~>Z G}4 V.]ʠA͐oG,Y` [mnݺ)""avJ"JbZCDdsl6` ["""""*lEDDDDl n؈sNQa+ұe˖e˖uɓ'C)X\}\s5tXw;KԞX>}Xf C?+W9ǽ7QG`V~[n_l6Kr- 0~\ve =kd6laJhnnW^X'߹lug}6wƘ2IAN'PD [ ;;yk-%%%,Zи2oc걕&.g^W^yC9D"!Nː/(Ի B6zkUԊSϞzlee?fڪVԞl걕ĉջ Tvر*jE6[ٮ,Ze˖zbp|;\je… I >|*TDVX'ysc =kD"jOEelss'x"6to:(^ɬFXv\Q{*uV0QVVy677Thqg*\rTDl[F*3ϐH$O>e˖!XjE*Q娈"ȶhѢvTVVݝs|< (WE9"Ʋe Ƙ.5 jw9_ό3TQrT9*B J%hʺB#\QQ? rUQQa+mZ98~{ט1c˖-5uQy (WE9"۞s=z9s0a,u=={'T E*Qd4Vv8_yGx׹ K6Z0{XD*Q}: (WE9Naۤ/~a@rUQQa+86⃵1cƨ1(GP$ f͚ѽ#?Caƌ!UQ*GEȖ1xW:}o߾7G3|KJQ*WE9"f|dMիW//_y?{=XO7CU [ԩS km>/Ccc#gȐ! (WE1%O</|sw߭@rUD9*;vgc';ި m/q V]]7CU [M_P{c6{LrUQQa+egumsYZFU [ҳ> rUD9**lED"?%%%}cL0SO=U(GVdݻ&vNdQrT9*;-%LYYYZD"Q͢[f*++ ~mw n> (GVd(--o߾,Y\p?я836k't+UQ*GEgGC-|M7q736>W^ye=ztk6- OD*Q4V)yuyC~gyI&<|W^QE*Q娨l8 ð]6gB%[1-M+Wlx<ʪ&j[(A7xQnR| -`c} UvMLk-MVT11q|+4D>p%O99Ɇ$,%HR*`U門+08'uRg<ZdCZ2Y2l{S^^i[gEmmm-M-!5iQljż\Qk0bvnImp.w[gBYYVv5l֤(MP ͯ5maW(8w޻`1rQclK,**P:a.W}n$AQ2*xmJٵOZŭru\5mOrq2Ʌ*0e!=ijj҉ZinnfEU#=*R1 ֙v DapCyd &QA}: .[2[ JJJ]+ZZZS]ByID,N60`L*G]ut.̝g$Imc ZMHd2'iȴн,BMo&YOyg/ٴv䯂Eŭ6nfGY*)UʮeuqJ&N&"5'Ca\5>.Yp?s &7}o0bfS։:>XBbmFU.% Cj>0K 5Lt;& ¨֬ы\F:[/tMt@[Wsne*:. Ű678p0:VY[O`M.^6Ǐa]˛H={Ts/ҽk(QqgNi7Ufh{^/Əey5|~,Pa+ mH E%AnuL˟qՓ:/kͮ]8p q|RB| UO[JK=23s>Pk XO$qsBZ>w%̐dj*ƴB [U4<l329/pZ\k/ն iӦcmu1\sE]6kS`%tĝf˲e߿zhj.* N6s5a~./jNkwxɧt \aS(\5^TԶރچ K.e|:둵V>E'^ոN9څN&n1 aɯ`Gh-΁<' C tKH]]jOU;Y,.l;sȜ3R5.wfl:(hk9?kܬv+=n#jlמl}^a tdGUUSC&W[2KnO%7 K׺>tlwXw]r;a.?Zd-555 0@ dXUlDQHI׾4}Nr sumJPǶ6qô8}F\O ]+ ,Xk5wA xEczmp6jl=gbeDq,YLuj GH{Dڞ]~7گ0t uK'Ϸ۶b cʃwqhwl}ٵꥶDžA@nZkjhZ\own],2qSZm>:GMO{ ?b7F6w2\ m@&Xk6V\ BG|lgkq!ahsG9ܚ<^:+-%e8Kca}7`vk\O΅Ӆlvml^ >m ]cNE,.<466~|NFwkjG\u Xex:l-ٰ0QEC6FRYk w{?b0*m!tTTZ1cO~GYE7ęhT CY`gszy:reu=p Q9y]>W |ZK>s\HژL rmC6E [5MSېŒB+fјPg ζ .\+*JTTR.*H_S>jsoj[[K}wmvϡm9am·N%3ƖU5jStƒ A0.w`~_\/$\*.mSN>svCy]T\cwY_'ٚ:y>W .拮,:-5Ub:M]%14g ٰmǁ0l6a]scb~,NQqY3n(w6Mkwkkkor/rȏ $NӞڵֿr_Enֆli @'_ EAUTxƧ[EEt+H^nxdOvh3VLqnjJs~CMj̿~C>,\q-9- {_q7$豧ѧ o(^,_"8())pg2+Xh.0 _ 4W3`䗐z麚5$)=~vq<|_WoP%u;7^u{[?d<*,rz1'73EtyxT<_~}:mӧMfGhJݍ?/ᔳ/ŏys+ym˖.s&p9&uz=f5Un-7|n*vUPUW kl  YCO(N,YF,YBb1-I׶= d~Qw\fkRBC}諾1jKiiH7 rM6 Q);Q_9/SV7!V,Wg{+wyopٗr%W\:uuDtK7,rEߠֹ4\53mg&xШ5o,s݀ r. T1ǀ1@~ZTU?_?֥Is{<{xx"E=psUWǞ۱8n)c܁o^C))( r[|k€M'WuÏ/pn]LG#6mb]=^~XtsUWT誁a<c ^h w#b Tc|v4߱7lD19s=j*p0tL8(f8n\E?7=4ޝ˼gS_W7w߁T^Y/QRZN,9 =p8Wl!HddbdnbgkOk~B[r!L&nl\x܍DL "*lU,-ϯUK{tLgZOQ[Z:KcS 5Mdiِ&Rd)?sGF? dh}W^kI&L6ꝋ lਮZC٪df>+;CsƮ΢2lèkc'x8.%F ͚:m뢂9뻹[%_[?';77ڐ 5of/Q լYLغQWS>α_=0:Ks&tTTNg5Uu8},{,̘o<1oSuCfa&7Itp;xh7~Gnh5 ;b-3h0RVWTgum#W7g?+\wYwDN9[2)~qg]=Fx=`atl_,Zj48㨭rā|,ך? k9m% L1#KQX~*Ѧy-['e[Tv֏d.|y7G܏ň$xD"E*YD*9ݔ SHtن&ףc˗Yb;{kA4j͚܌{ZG3Ǹs!G/N/o#*"LH&I$ b1=|ϋbT 7A=0g_cm`L 4<ȝѧ%首hk 7<cs\q04o~0eK{Q}ZIύ=oOs\[qEm1 _q315kc+n/5˚n@6ULs"zsҳ8ΜYt!U+4yk <;p=\v-y` #?SmUna ^}a*yoϝɧ ?f3w&s^}O9/O+g]ƈ炫~u*E>/=w,t>U+?wA^zvS+_ڷmEޮi~Z \tfJw{ݴl)zry?гW?jW3sAL}ooP%o7";\gZ}5ʻ/q'rwk>ˮ&sɩ3h>x9v$|n򃋏΢[OV,]c:iK䧋zvaѶZI=vT IDAT)9wEi+ow}zvoWX֮o{ݶ.6K& LK4`'0ZwH$Sjŧ޳7d1` YjqVU{} m)FQ""F?FcI$&x-xs7ML7A&1%b-""R ai 0sΜ]Yg,jvWRP|67d!bulISWp$P8B{qTnL2B A: vڒf_p:WSiJО!G;L<OڜcүkDe [.2DhHAtF1'J[ksҸh]'55Yybm!KvnyjMMKB, ͍ dXkDCYx,hrrQʦ'#7k DNn!9y{\[K M~7۶{2V]*, $]A'Wh*sK[R& 2bĉ}V5cFgbPq3h j*ôⷼ8.rd{̍Ҙp @<]e) QdwP_P8ܼ]頻dJASC@P8Bsc=ٹ4ՙi=~,3|4F~aIvvUl&C^A>Ć*#a=ܻE5/)`^xs) #JlN8~([x,m4XgVyR҄aBp¡sАaW`&))-KgH~~3sVq lGQtܪ/s  ,qgqK [3P,1 fU]fl$R3{tʻ遗vTU#0v@ >NHʞ}C;كt vf[] 77ӯ,0sz{#JLqUZ<}zy+ K>LyD1`T>˫#TN;Q&K! %&\6dm}/(>s3l6fd{i; ۯ¢N׾J/)Ra˰ l¢rM3L[&EQ(3XQ+Zp3Jn:̄gY{VYq[qN4"l>FN8@,'`-X>TJ3DitQ́lK=RmkrL uP(~M-۵舡cQ,`&.'VpSc3jVԧ`e&ڌW6B[It"A qY.Loˇ]4ҞʌN'%9V*ujՉ /l$X\ljey9DWXjE }8zD”u&nC犪"^G79~44pPJ~{jUuM)or^Zf@hOzhy/v1_ZuÝD/dC"MwPEᲧ ?%m= -&WPXrb\i2_1zI5FӚ6ao:U^(-h$!xE }#::Ɋjp\dc(zeRboŒc-@ppmlޱlH0mRwcZi~Ӌby@ [>nx l e kysl>WDCc=w01$O2b"maM8Y,O8q|~'E)`@yG-E9>>Gˈܥxw6N8cۦ'vr5eNP龭muJ:ioɇϧ8qvegM NZ̭`0߶xgz#@&]\ 7NIXKTzH )˄whoZ] ER.J;lbgM Fqݠma IǕ~Pɩe,ʲb0ssuEfwk٪k*p]ٯvVT/ۡtJn9aΛZΟ!kWr!(+[L!ka8Tz gd{Ska~fMIv lBԧ"lT]L]Z^[E[GIc|~P6T!-NKyT+R %Q&fvh/Em;ymleQ0~|/CY#G BXKY /e𰺴ab*ûW+9/Rn:l۱o%>?D h, c ,`=_Ly~-ە/V2B! :tJu*ŗqclٱחm'hŲBX%6*Vk~.~]΢ll% PHnv/*}֬.R'*cShRh\Z;cjvn4e`PxiC $Rglf#N;ױqG#G(eP|rB8P*IfͭTVճ~n*mDm8ò,"%%%":/skY 5fLˇe;U FofK\Fc4vcO  rQXX(i_RWؽ{7kma:jڃ$zӟ6l_q,\߿ xuc7"[ux2@0Ur| i"0!//bJKK% ݳgko_kaVΧdhr h )Ϗ22ֲ)--`0()Vkb1ٵkuu Զ+cxϿ>|?￷_{E4A&;#hn5LbVV%ǡ*jkh%zϝ7dqݭ3픔|ܐJώ`bKJJ(,,$;;[ :::hlljSoA/yf]=N9Sj/.a9!r)@NNNFK<}q ~ q|6'FOnokuVd P׏""G`6EEE|>(@q2IT[ \"~*I[|B!(..[`^SvPkF-/GYJJJׯxjE "lrrr=z4uuub=%b|>_r*P(D^^B!=cƌZ[[qx" Onnn^lg6Æ * V/Hxdjoy{zJ Yئ [AAD [AAD [AAD "lAAa+Va+ [Aa+ [Aa+ [AlW@A[AAKaۦ  t)lc+AA AA AA VAA VA VA VA VA[AAD [AAz:J)[AAq`Yh   ~a+[A  "lA  "lA  "l  "l        D  VD  VD  VD  Va+ [Aa+ [Aa+ [Aa+ [A  "lA  "lA  "lA  "l  "l        D  VD  VD }͛76tttHa ZknzPY> mv˖-R "l`Osgt. 7-𙡔?1W\q7nkڵkK7fΜɲe۷o窫믗UZK1ٳg3f̠Krg2{l(oӦM>S6l1cظq#HBx 6mL KinnZf̘A~~>.~:> X r'Ja "l`r=p뭷&= J),qN9x )$G0k,?bv믿_RHB{[nɨ[m&/|_] Ia+ B]]WBO"u'@ ͛[+(Z[[2d{խo&'t[בm݆R*SNQ+(8.2|>_^uU"jG$o߫n6mZ [WҕV< BOdƍ=:M8P GqS^^Nuuun7oz++Iڶ)"V葌1"MxkE =P(~#,N8ADЫInZ[[ٴiׯg˖-TTTWX@ @{{;%%%x}{1|pȑ#)))[oeY`\eƍlذ7RQQA4Sϕxc JKK)//gĈ3l EeDQyx ,XݻP8Pc2sL1h UAl'> HRsiq^ Ep0hjjb{y2%Rз ƍCG}umV-{9em|}{;VlGݷfP6b=Vy3eS |;7k]}FcmPTCgҤI{L:U[[oqeQ}XU(; ԗ /öm~__X%*vbŊwyf*Yeg`T#l ۖ/Ck-mo1# V~i.b56x80j5{:\x/H7%*u[]b'ON<F=M^)̻mO~)[o1}tEvp`f ٳg bdzuVӛpCғ ~xʍ1gfΜ)e"V(9M79Bmakg[\}<S{|c3!??;vDLzǶ0xe$8g\lت[l~^Z3:(B}}=<򈔇[a_DQ^?.]3j(۶m'[&ah +#e.|2N@lE ų>k$+oO o~#*Y[x'̛ Iy ϝ^xAC ,0oY~(e.`Q> 7Dd x{j 6a)caSՍ77,=v{?/¶!hnn6] ߣuH~\v$Wo9fy>[ τTMCU'|I& [-˂[>:K`Pxt53>g< l4l`ݺuMvI/^gOa+tɖ-^m7Ǐ][ 2UUs\~t>}nާ{]˫qmwUPʕ+n+8gij݅sWnj08 G}< i&&@lM^[[+ =כ7^q1w4 v\y̧]"jC>xl#n[w٧l5t[sk'|6zoτ w ;~b`---"`za }p8=y=pbhƈ8:L(C̅,6׶CCY6|e4}&Ɖpk`[pxz= Fk`s5o@E0uI{o7ƚضdÞ6F2x}T=Y,{jnn[Z4n4tƍ _zxaYMRm/5&Xկ[Q3b01~;nhboLφ ++Ġl#`ߋ`SMz|A*~`<Ehg[wl?M?o}Wmpq>sLiW LWz'-/oڗӺg7tϏa'KvŠL/ MxNyǗ|߅ ++v$A8d 3q[{9mCs7 }ܾ3޾ĵ}pd!0|/n{SH5mA!FL2]Sa֟=_tfmsS)gG!a%]SlWlWD l? K:xv{ư}|6yyOaznX ;x.iCL< IPky? J9~qS!6`GAdxy=T' 0 V|͈Óv͝oKaXɎx95Ard9 txp:\=ĸ) IDATѿS(d^ Cfb});w`W4#u{ 6Kx'L A/gۧfhL+˽Oz<ɔ»_4n iٮ]&O3_;N0;yMY~#N:F)/3yGȫJP_-K7/(3D }1csgKWcDVS.7+'aQ&S?u*āfa#pfّftn"%G L5]\}\S,U`<`bdp83/g &KyGo6>fm͵\389֤i=) }~˳]-p3& ~:˪XXcy1+wwCԁ^JJ>i15>36Cn8 ~R`fz+u&E]E|Ixw7G bb[Ao3ˁVEf*tE{V__;}Vݚ9KO:U-&MWjtz&"l0ˁ=_;31ՙ9͒N];rYǩmY ,y25Hґ* څ2v*b V%h7eG=hA>Fgb<f!e" 'ABÚ̶sb) h{f]AVAAa+  = EE(Y](}1SZ [gJ+Lj%ˊ# )( 1+fq-fjr+®]XtiҾ^hÆaÆq\pIrPAk c)PSHu"lC V}kB'm@Sd&ܿGaW{0% vlC;V-,6I֩4q*}^lu6kA֮AC, mc#؁ZbA§pC|>BQٞgVy^[˓2t9NR+"1NP{*Wi/4Z&[ᦶu !'bc(VAa+Z,2vtܟNN:eJy@G{lCܦg\R1)E A)Muu5eee"lA!.Z(n Ϝ'clҮ{MoA$~Qj5LJ».hijj<  V8mh{`1o:)Q^84ZjR)Iț޻踋o%m)JAA)Hqmx3:C)/nR'D7G넬B&D MifEvO4t$,E^NhݼAbq(yM?֚=u+:hT"ZFe<=$Zi/X{뵀!!ފsLxlA¡u]xn\Mr.&=A"8W%3Z~\~3Էr()JmYl;S'e-?}ZC y/KPC?9oۛ?_ϥs5QYQ#K-9/步[͏JDmq{nŜqM/JKo(>Yyo]6.1AA)ablN*nB@,˓3hwՅMJN(dtJ8sjG(dV.={ ֤K.5aOZūK1?dWO-g̰sm+xb.tF>S&BMspmUi偿.f]}drZnC5юV֬挩Cyl`$dkkEj_v:C(f7ފ[?&^~evMQQgq\/7oB= on:>߇~țoI4Ŷm~?ƍc„ 3|fΜ)l/_~e6oL$SOeС\˖-c駟N^^^ܹxG7SOQYYIvv6 B)\@,'K/DkڀOEQ0h,?Z`YTڤIDWJq&ҿSW~ OW:q\͹_(?0E̺%恿.cK)) nqyo 1kv$fnGN+qu -ലtu5o[R]a+&'K-۫XNF'yoj~߲rJ3f`|)je„ y睜|\{+L29s0jԨnO'NX i&.2nV O駟 .^x"l%Eгhmmv# UT4z%m_Dwh\ -u#nmо,UQ Xt'Y_H [_}n~iɉ(31qtxx97|Ͻ!>5vpsF}N=)l ksWMLi.8uAuu5;,YGpop/mo۹/C3L׹ug~q2_WFӚ>77ތ5zuh趭кZ6BFZWPm R_]TiZCm>2^H$qw(k|ͽo$>@ @VV>|n&> X˙4i>kŊwD"~,bС\tEK%%%viR2qD>^ϟ߿O{Zn*((m0_җ(++^9묳y]{v kӧ3sLRXŬY2eJz9=*5U#>|?Vk so.6?X>l4 eA3Y'aמ.R%|8{as1+y晜s9}_b…\s5vm,\;wSO`6nc=̙3ZkstMr=M2Ɓ)L@4L1eA0 i+^o^B,rMk]Z!oGG=@%y!~p\M$`> p]Uu ,H4qsJcQUJ$d-͵UU71Nw~mc-(;h78]&ORAcc+?JJ 1q|ZxW5t{#Re?6ځh8N>Ui&B&nH (s%@nn.Ç[ob^|ENʑGɄ |o8=СC91c'Ndsރ̙ٴi> L:_|Ϸm.\ȨQx4i<W^y%sg}6wq't۶mVVVV_UVQ__/˲8xWk:u*r W_}5'x"˖-㬳"QUUŴiX|9c֭p \|:n"w08kh]Ti f̀hK^anÄ[} z⭻j0b(n] k.6z=UyvJYbo,4E~^yg7K.fۖ[в;]N6+/RT^:m[>!n{ꪔ/Җw{9n]c8k4_ބmmmX|>|>o6'x"`B&MĒ%K>ӦMK_QQAuu5IAPQQ]ԍm`0muy]]QVV0?9s& .dRضM$A)E~~~] D/wޡ,OfѢE] ɓ'~7㳷~뮻BF9餓6|ZZZ]} Hmzw t`qWP:m;P]l`8>UP1Bv%;dYuۍV;YE+wxiͿڪ9vŒ-wXq6hg Ɗ[`K zwol Ru&F=y;XQ a& xqA5:+s&:%kXV͕mߴ5_^K[ K*MkY~-uwB{5kUqq5Zbllxִr}3_;A%dZ1V ?q86vQӮxxZN k[~M튢"mK'AtttgϞ {(3uQuVb^]1d=X}ٽ+v]T\zv˕d Qi*dNQ?д>p]gx0w="Of߀::@iٰ7BG*4׆_4-БZ{BlgƱ0؂Z~x!\o573! DAV(s5M i+N  8T3`AͰ~YA~~E p܃I3RۺߴdZaw=bEZ[[io7&ڒ>`ڵ&?꫹뮻Z{n8㌌tttܜ<_, z!n6/^̥^J8utt$+)((`۶m]rTySSMMMb1?Ǝ򫽽=c@}ZgQ]466f͛Yz5#Fq\r%wyl߾r7uݻw'|;Ga4668㩬-9X,e\[{{{dڵ'{,◿%\r |_% h"6lؐ ]u]I;b9[AV (a9C~E8Zv}Y/Oa)6O?"a)26Ź܈ZC)"ҏ(l\{kɣ%N9X8(zR nJCMstԗ,ƍRrssN9Yfi0}f D^^[neذayfFE0dtttG<իb,[M61qDndbUUVbȐ!h=72zh.Zt{u]gy1cƠ&77m۶1h dž >|8L0 ƍ=z4{adggsgK/QYY9眓a; , //,`F1c߿?ϧn[oGM<rlۦ||>GfTTTp 'dxE]իygXr%Æ cƌk1n82dH^O2fgTy9 )T^dYwrf4M #r):JSıHZ&vpv5w5r&]Ojw~:>ꫯ2k,=܌Sn{z  E*ƛ-,1 ބ$km%'a^_trQFu"!!s\$WASf4ZE{V[iS.̘* >3Y]\7mN;ظGƍk,/3Y o /5Ů5^z|M$T)ƌR&c6M Q, "lOKhvͨw]\xm/͓ZF$*htw)¶r?BeF[.*+ dkX:iJ[}" >Ca*,Ge 09a9t:=7H'2[{'[H2Wv&Dyw9>9M|5:]ئM̠WXirYAD "QLSK_q,[y)\?PZ)/4PV驟c/UYʺ[VqHX{YY$kD؉FゎcX:I+VAa+|ae㳍DZu\4`+_ڀI, |mbzG+'1Pfztȴqgl\:hHy "lOzEan.DRPYe3RrUȔښeEA`Z'ت8ePiR [eOo-@ ns²(|f݄@UNZBɱF7-wmbf*I_ܛmJSTpUNJ[h;@4}bkm!O I =,yC&`V.mQpf d"=]yZdvW¶U#keHl]itԴ4cGElv8(|XJ|#f82 "lG:4>/r)Xr H#ykWXlo|K!8xqmϖ J)l絹RVĭV3Ί'[WhY/47ԹI-Qأ{,Z@ii)3.eg'䋠Ԧ ZB:n"%IB4޿0bp)A IDAT ADn׏QŴ'&/EgEdj9c(D$+ ky,ÍR 555qG F/l v/}l|ya\m|5̬wf<6K~M?rvau+;qm@6G9Aa{ׯEEE>6I1zB"9opnjKلv@ CdA0rAR ۶ ͡Hq-8I&"25o5ӇkR Ev~6 VC/%p\/}XKkRYVjB 0ow+`S^v5qKq??O "lA88:N܉XqZ-铅 0@ʫGkq$K z\%Ĉ;Q7u͠[/aBb(e&qL0 "lA8EknH:Rn*J:ЈmdegGڔV{Y22hWĜ(Xuc{SUUEuu5 X D8(q3w}nFy>.Rʒ˘1ckimm~ssϝ;} )Spꩧn\~{q9[p#Ĉ;qkkB\qqݍuZĝh&ǝ?_h #󶻺/u}avˮ];)?Dfn<)sٗfumlbf73O:7w$gL9 Nx:ӎ7]s+UUw^';/1n4i̍8..q7vs6c>ѹꪌuԨQ\y466sEuY'v7`]K/ep L6/d? . /wRa+|v}?!رW^yu1{}3i$マ<ٳYfM.]TTTz[n_:ӧO箻o}^ǜ:+q7jM̉zĜnjIvKj]bI'8Ԭv3TWk~5|]Sq1j87?{ ;7vg嚛W|ys-mGQ&}КHV_f-rmoRԯ/q2=[:Qbn4C&qc;NhX<*i7[o릛nJ^{5v/}3vXx<>ʲe˺t|j*͛ vmo㡇;_=?{EqM'$B (M.EPE*zXÎg=+RiT.Ԅcv7)ycd=?w}WN`+ίoyr-|Wݻw3uTlR6g̘M7D=|oh׮ÇUVJ޽3"!!޽{_ZϡCOyꩧ4h'Ot2k,nϏ? qFmƋ/Hdd$aaa<šf…}t҅:pw|r+$؊믿gرӇ￟Rcdyn&*TaÈgڴi\uUx6H߿e˖ѢE L,s=Galڴ'Ү];}Qf̘%Kڵ+5jԠO>ԭ[ Xp!f̝;w+o߾_-yfVݫ 'N`}72p@bcc9r$\{ :_$_ӢE 2ד/ĉiѢ= ./C$&&ruѨQܹ hSϳ>R~۴i GW<&-[Һuky9"~A<裴k׎u֑BDT/zߏOŵ}:Ltu #8zؚҸFBU\p*FGrQ_HXJXEX5e+ uETw۷6_~6ػs?x,}AzN+h Τot ex^X#^ LV!Ĭ .UjUS}֪U0Se\\~.x&߯ժUСC%^?&L $$O?^x#F0uBHRVRė޽{}z>}LKKcl޼J*aJ^^~K\wu xG=Pj~Va(` L.rY~ZK;&-W!9hm[v k2ӍM NPȳ>SKv JvW'TjR~EPգpT;=:~-|րu ^ۺھu;$P'i~ES_Xu |;܅8\6vW9VqePMZkN&= }7MBTΙL)8azjeϕ'jCߠ]|1XjҿGJq+..Ç~/vlCL8 6ЦMgfL4Yf1m4͛W"H !VW=;wx]v[oq׳h"v;:t8mj=+V8ܸq#۷h0sLZ_˩\2-b޽$%%qȑa͆Z.kvN|CzJYϋ=dԩvpiF"4\ kK[%حFnY`5y9l]oWp>َ`8 X59"t-=>m^/C׏lqO lq LW :IERWmbEtߍMryd;AVۼ&g9'sASc^} :6ʳIYCAPPVGlْ++0d[>Ǐuրe R.˄VԪ+YW>e*yc+ch'V&Bpny9yv04-i wh1.ͮlhBƒP~k6dpxgOOӃBHlt1<潿{HjZ;'L2t؞qgL;'ch'£ɷe =.AĢLz5<F"F =v* kq ;`-5}uft2+gnRapPht[-kp:q8UZmn\.i2#Vqp!ʑdȕ? QطGW׳9 J)>BhNA! IJB3MUPn@) rs1!Vq1;WǾ{8n?B9(Ց|rh0 s0cbb=?qרaZd"E8XE=g,+8(Kj=yhN'μ\(ZG,Vq)v:T8^nw!ΐRWLBB9 MHTM P%x+;<{m98U-n5n# (, #4L~+K% !.TTR pv1Mm_0|!!!TX񜭿B lw!FV5iue!U]4\9Z[A-ʱoقToGKi"VqSJJժUR iJm](--lϥPb0C"&,9*kW{{ĩow?|{fYFY{6>>UfAXXPbq: B8Nlc6 mnn2ղ֔ GPÓ:'OXmm}]4wދuCyBR eBXG%>>^jm% !?0kt8NGX!WTqt9^eevP[꒞ &ӗtc(EFFjՒ`+V!8֘.y5QVơltٙW+?x/E{&/N'i/M$ !Ru80LCYPXieJ8E* . @kpvl% !Bu;V˅2 Ltޚ>5ً3?rقB}= ֥yx6G|?WcPmmΧl[ Otk`u54vJ7x5k2fggӫW/֮]5c N'-[vn:qfΜI6m͛Oln:vX1~x à{СC &&#GseI|ԩSvС<4kwokʕ̝;^zY5M۷#F9ɪUNlN رcK7enF*WLaa!gfʔ),^`Əϒ%KHJJ~9𻘸n\ 575=HkiЦ/ DoZr.wǎ9caalK[O-hԨc&ԯ_p2339p)))\qo޼ytЁd/^ 7Y a͚5l6{9N tQ)((4lؐ}eAdeeQbE222 66|vITT 4(sM4aҤI?sN '22~N'ڵx*UDnn.)))(Mo4lhݢ.((qܹ/fTT%KO`p8(Ğu3h fN[ÐzR EV4_iNVsW|ǒ5kiR7|˪ ٿ`[99df'DK\IfIv^uF$T?leŴJ jOkƱ ‚| ;vүs'<ĸ(,価CXv-_Z'g}/;^֌lg4MHh1C|Zqa!!|]7{t-ݻ)a yy,[;vrE&{ގ~ ]뢋bG45`Ԛ5diRp,l`+.v]vw߿txZ#ӳgOzɲeXt)+W7͛7_~[nZxx8پwOcٳgaaal2,X޽{hܸ1W\qEm͜9ѣG{n̙ jbܸql6>.qQre~7RKLL K,^#..{ƍJbb"ӦM#(_aaa6nXzrrr8x gfѢEdeeŦMxZwвeK v8p jժEzz:?|a8<<\3Kar2*<W#RDG` s`gkxZ((axZgl_Mi/v3OqY$?4_|=GƢ5k{M{F_/CG  7R?>o0xt[6c&k6lKړ9\=.{R1vXv& M[nӹ1ÎqCoa7E}E+ eƒ(x{{.ɥJL%lno=Ya@z_o96ZX=˘s,_|:G)Uf=z4呒„ %66&M믿ꫯj*^|rwm6!h"A6[dX42l6"#+FV(ؿc}py[xNQ7{lڶm˪UڈM={6wuK.aÆ$&&H˖-kp82OL˂y aox^(Wy=6jԨЫ_>ׯgTV 4+ի={e˖l޼ۗB6}aD|U{۞~C@:++i-pm*}oӁQ_333QZ?gea jlWoʚ֡磳A.yŌy%VΫ_}Eb*tN-Jcֺ+AF S Sf^ٞ|θ yBO-D`Cz,O.bX{bO?1ǟ@kXʕ~EHaӞ{5zlɴrdEV㩴B24 oS _׺KsI\{A )YflٲN6;; 6rҜ+Wfo / /믳sNvW_}|[>%%UJլYP6l` dŊg]zuO';;ӠAm3<+뙙QVyRM.rs ntc=l 2Mk Bф3aJUBU#NL4j yGE*!`A!؁<ݴ`ZI PVrG|m;.X@:|6v !;:7zriR&ky^ϵͱZXgC^;۷3KgKZש%68u:'hCѺNBByy҅J|mA^.,2 zc޺,ٲ]c3蜘sc*3{F ̀P9$XC09n p[Gn[U\(/.rbԫW+W2j(i#WKJŪ+1)4.sVݑ{WW4Pv}yv!@ٽ~~6~6@Sc\ \Fc8Ka RHDGGIjjo"!vR )R,5j`/;h_# fl%n8B @x*no?[K[cHi?wUVLUheu\+eeehqp1"P8Pd8K6lnRRk/V0 >}gdN9($88ݻKY宬u0}1.cr,α]`{I0`}ϗ Erѣ*U*eYT͛7g۶m&5$Uۋgy6PJEU+;&G\_w?W^y%UVlEiK\\?3+Vk"~ #r[Vb۶m￧q8+FizD+0*ž{9N*Uh߾=qqqB87ZK k<4i҄%K$ M:g}VZaÆR eU\,euhт`{"""8|0[qVvɬ_^{0 lũj/4i¾}?}Y9(lذp8Xd =z*uY0`SN PUVq뭷ݻR t֭[GΝ),,o"HZZ_=7wWʪ(e5%%f͚ɓ5jqVRSSݻ7Ǐgʔ)L8QSO=%TV-5jٳo(,,CrC҆ IOO{|CUqJ*ѨQ#f͚ʕ+Yfr29pv_~='Vf͚ҥK駟ضm_~91,.~iӦlzY*iڴ)AAA,_ŋOv}8+Wr-pAZlɂ  s˗3tP?Npp0~;ÇQFrpʹ'Oh"^}URSS7n/_ZʪX˽ދVZL0KXl/̲eׯ|yYH-W<ȓO>|mmȵ^KTNq\jՊ)S7RVťTV׮]ƍs\veԬY *I,g\.߿+V.¢4i?W{!⢳}v}]wjժYˣ\?BBBt='|nUyHY=Kn[']v0 9WxԪUK?^gddHHE4M~G֬Yþ}̔RNSvmZlI=*G믿&99Trss$3TVڵkӡC v`+B!. HD!BHB!BB!lB!`+B!$ !B!V!B B!BHB!lB!`+B![!B!$ !B B!BHB!BB!lB![!B!$ !B!V!B B!BHL6>}ȁB!ﱐ`{q;t۶m!Bc!V!BHB!BB!lB!`+B!$ !B!V!B B!BHB!lB!`+B![!B!Ή 94Rr BP|| ׯ5k֔!ElY^ *A) SO=?RSS`!y"mlB![!B!$ !B!V!B B!BB!lB!`+B![!B!V!B B!BHB!) סC̤yr0BB!eB!BB!lB!`+B![!B!V!B B!BHB!BB!`+B![!B!$ !B!V!BHB!BB!lB!`+B!$ !B!V!B B!BHB!BB!`+B![!B!$ !BQ 9BH IDATٷow&994r`_&::$hРUV"`+(p_2c V^ͱc䠈 ZXX͛7g~ABTQno/٠0 1#"F N@^W7#bArDBXznlF4'I;\9ܥt]ZW3?*R9>ǵ v*zz>,O7¿no mg*vщqƍi۶| QH@nn !( j#:ڥvkF ebvc0`Lj=d+`mח}vYz#u1ZI@ QH@rr'Yֺ f۔C7_!dZ?0e]kk;ݡ眿v>a=ʕDl"_BRc+D9{nID?svYž4+>vpp4+@e2:wcf*[l_[OBXX?F6-x-&]ƆU{#kj!},PA*jF>ph ZUբ0Jw'>;;.+z0$f_xCdn 0m \e3Y~]|=hv UօQ0_X"*paBRo>Ilcn EWkd(+`&T1ab8oze׃7 -0-ʺ?Oa!VQoX{Yi=~M1yBmDk쮳]x}ֿᛛ }l.O}fV?n[JV vB־WV~'d8 IGձ.\F/k2qO9׮O`[ OZAsֲ O'`k\B'NxEp?i9V ^`y2-ִF+Մ`Qj7 ti0u3h&`G;.RNQwWy.??BkUvU<.* nZP̞Vr*$\],+:6ҭU[n?Z/WTgDAP[EA6_OwXOZN1ha0_ Z};-8pBHBfoVV!`T|pt NX h!0}UYUp{8Ϫ]ZuWO(jt@x\^ժQ߮wng5_ؑ S:Aʰx?:Cxlb*CX+[sm`jl2{rUۺ&,\ͺ>u~g&\,t}0`1w6֭xNZ+V5ݛeբ 7^uKn۾,6wXM2 ņ>>sgXR*kWjK yRA*`+(O{ ibVZjv Xb5 e7guIH|:s=!=oibJjמ^ ow3@{8So:…4~p`+^ dž9лG[ٮ"\+p~xuqaރ7.Wx9e} s;nװy;`6uge-lXmMWaA+|kdZԿ#~\\^XV1_Y\Is`aX,L_3?9$U1f aep@}&|?,[Z߷oy pJ~!I՛鹥DD$s: "k=_Ld;WW =8ׯiN(؊ȶIlVDd;Ӓ[tDb+"""" """""[ uE^9G3T4=As&3(؊HZTAߺjG5gsc kG%_Z¶!,XKW4dBDlE c蝞usg;RYۈ1|ͅ"[kb) TZV)S=ÌXLMDVDI}}=?;ݟXʞ8(T\Qs]Oz&jaC>9ٟ.U-i@[)C夣Gpؘ=FEe%@X<#T-IWض7úz6fVfQlfJ 'g6QLs'6l"`+"۸nݺqθ1UIij`ql(Y_WuQ .ux.U ԽFDVDEUU}++1$0VSalE6jvX(hTy~ %֕lKBqNZ+,f0ǪUӧ1͒ `+"efpAΏ*hVls3G~ejrm8Hc֒DDVDLk-%6s\X` ,[&؆g[]8=DLywg} PQ06./儒ZXi]]nZsʆID֑[M:d*x{m8qmn dVVDVDʅp~xj6`b1ZnB,E@J#q4 SR]fk7_À>+rgwYc-[CUeO߉?}dqzM}w!]1'*ya?vk{ gx&Չd$<pƲoϣEwtjKKKBY7WuI@׊(؊H$o ֢ xMDn(kKVLQu1P+}m]Vr؝_勇 1!h_>2ـq[ٸ;n柳Ƹ:'I~e|+|3u+o_>ޘ}W|:_ropi{l W; ru&GvnpbɊaxSWr&ل~(؊HHd|G  xxt;x8+yoFK% }xup UKo.wg΂,XéGW\g=-ol}9h37Vv0xrPzWb'IJztKhYVҭ'>34G>=UѾ'^ gP߳Ӿ4YsVo=JmMG~So/n-;qXtcwWule;/nh+6WFS8F߳8ҫ"(؊HH&t JJp:b8cpqQYUKMmm"|̘X[h+tf3[fSTӣK}r;qox7 jf{)?mN;WY^zq1괿rO! ?柿L߆6Z< H-,Y N4Ȏcqⰳ^W f>U^yk7i9.oUqCMUaHDXd$;P؃ԔEl>(xotlreZg ]tnYܜcTlO jYW.y [[)'1AܭД)f)2@ƄU\Ù a{mlT/ 1#Y t׆zD)\iWm8ރo}u0W0)4G WOn3y nbos\w,[3;U᳨[O.Lp箇gQףn8ǫc"ÀU\}xxotP@z}=o%'ޛ Cu>êNx}:vc<5eL+=>*E_8yX|\axuAn mO~vm66ڦYN]s " "RvUsM~yQl:*^>8抖uP=18jo.CpQQ ߗ{1Ea?Z }l;qal;QM修ei EkȐ V/9aEck]*||eϺn1|i`Zl6ZrIU\kW[u0u T:zƙb_~ՅE8gU|`?j;W>#A&gJ[\Ww GnHmguQm6F!&F(^ 'PM C.0N{PL4dKG>T\\xmbpIRKCK -5fHgÞC<ZҚ ^C=H7>g!S58Xz5dX:^;Wcˣ +E 6H/G\B@EP|.Jw: !H;po /6 m PʭdJ&mn'Rؾ|MǮ5՝"`+"ǺN }3W}5hz QMHsc̰B;Vf?U[XJBӲE 41/O&\ӯ, +1EwBs\+g'?W,YK,aYsʄaɒ%̚Vv5gisWcSC o':*Kc}d-=+P}c+8s>#v1mv$q^|GƩoVN SE3#W;)ȊlSz7i$vm:ʞ*koYByH(}i6t_[Cjmp %+;4z3<ӧa؀o ϵZ^8hxD' gZy[-NcwUEC^ݫǵFym)5F]ɿe Ww]b$3Ym-%j=kf<6gnJu1\:ū{s]=DBS(؊HyۊXuJt ʼnキeU\~W2]qu8/iQَ+v%c:Ul]DW !ה\.Ty]QOk봡6" "RƢq%s?lZE6͠@Ad \y.Р\+`+" ,A%r8!&e^0Er' ["h1Vs%u ۣ*"`+"el36pX\6xԱaES40Ukj֞fݍy0ќpΌ`\<b7/`+"ezx&~qxhV/0A@mn)j k;')P<,7q~knv~MO8c{GQbEmF'aƆU[[)`2πIpA q0ᐱrZzk\7!$p S6jk[)R1 zTQS>>b])n}hf]piq{>4ٳc }FO}m+E8}EDVDAt>TjL`]U8Cؘx&FẀ!}^TVV {*\Pଋp(Ulek庌EG.W$y1'`+"?xpX A&ZVhMTs<&[wu''؆S:*+hkkRm[DVDuX  K knY<FzȃO߿cg.3)>Ժ#df3! `+""ALwYJ4W4E}? IDAT0LlR6sٸ}d2)ZV#فoÕ゙sUܱ|;dhYs>/>2]}v2qR+z~6GrIs^k}s~D}9sX|'_yν e]r3A]n6u}AP<'8q{YiGpS3iQrƒ"b~ jh)] [nPQ?\:Eat x8,&WA.](H}Z6O/ӯ7^ㅧ^b q|Dw絗s_8Lm8XLxBÊ#q}G~LeG0roayЇvk/}?|/D |n4xշ}ӎ˿g 7q~0?8CCz&{f= w#; _OT;cstXp&ll;}ms]qWr}Mpʢ XtZ Qr>&ݩK,Le\[grɀB^>Co4Xn{J:|iִኟ_Z3ߙӏ=k/M%\}yWqGիWs^^z7pYsh>,[oty7率~þOȎCw ˻Isg'~cѼ%4-ib1> A ߽|oa0n7feb1,ͮ"ϣNV?+.|:pne\4EX\t!^g ϊb7/[)Ƙ0z;0D8lXQy, `< ݎ\T6ҰJf1X\>H6(\ uq@Mnq|ԇO?^zFLx N<8±'57|uwyIUߢ:ls|ɤ3,_9@:pa| $m#3|+s=HTy?!߹{s GЯ -`n ={G,LlԏMS= l2 пMVY;u =X>>G|8_#tEH%|? ^}%0r|<߀kZ̭}(؊HbmMwIE9u=.XJU|s#־];2maӴZK8R-au؅S00f:hKa8uO{ 5|\S~4g3"iǹrNs.S4 khy -m k HK2݆=݆lu2AGבj-9^cL6YLyunx-\peS~~Ռ<3Z֊WZ}tEݪj}LMtZYr"`+`+"alj7^gwʣ2$ְi϶D>X^Eچn`7Q8mI7RYղǨX/K=[/Oc H$Y hDYZ:N8 =.^A=fV3 X夣ۚn&S[3WL~Ȓ}۸䴫{|J>xCF7vÃ=J۱A\vtYa)4ST=^ײ"`+"el]R6c#Im*F}0bbaut%=g| ?ZFfҏcFPY'֥7|1^A,1r'%iqcF0x|XDDVD0: psx%ųrձh{*EŭӢdf$X}tnυm _\H[)dk.agwTX)q.pYG-Ԕ֫+.m}FخQUYT-ᶎ"`+"-ْ[ 6\Y,?~QExNRLΝ}p-=8-+`+"e+8;B<'fbh}Z C)^a#ןQ|cbΆo:tc{G]Î4\PQeјC8 {a0,e&Zn7w+BъOx&+(wێ j>\r8̙BpipG/`+" ycK{%n;(gKTδGxWLԎ)g. “pQrss^x6¡6ۀLr&ڨKBnA?sig~D,iey;mSbhQmgJVډꨬK䫰貯) Vaj&|CZdlv,$MӿP<1QrIfy9CH]7Z55Xd.+[nE~:M,lw|4.AC8p֒t^6얰AlBcJ_0P&m^LcWVDVDʁs|FD3h1[@0Kqwʥ'[DU~m7vsŗW[[)5550f ~eMIp[XaaΏ'h04 ϣ7 ΛKQSS`+`+"堪**p_YmDa6l@]D6w5|< 9QUY֯/j.[[)8 `j-j\JƗhűbM!B9- .4.` \:TWTL~TQm1L6wzDaLqʖţ-Axm!XCUn1W(V^M>}uDDlEdd} ^ l@̋=ǔՏ`+[*؆'j IN ogdt(؊6f3ada l6S(E3#JelE\[Ӻ(_d#Hf2 " "RNa dp,^PDc V)ZEzܕ`{뺽1v撟 OL~igzVSУg⯝џdžn ޯ|}.´dTJ?DlE\iMH76b8cp^T6V:ǏoZg7b.EPk̈́˻#"Gm-LĿ?<?`%(seZˤW[imis_?Uy~_ǝwN;;iGcM&gO>~= SM`qS3< /_{~Ĩ=vgt_Ώ =zp~SO&n-&mm>dH~ےcKWK+ogW<`EWvGQSt28 (:6:Qp CW蝨뙒Lm oSZ[;*a6 82,3jt ϘsƏ~<ړ'O9DZE#p磏qA>{'@UEe!Fr}ZΜE"簑{q#pYw6v,3?&^pBq _=s}pWK_r)!`&v' ky66䷫Hpm "笣 E4|v9qnlTZͅR r%ںd2Eh6\SլW " "R.<#a-3u\q4AssIoMer3Eߋbք8} ]]ɥN J/|"d`YQo^\ᶾ~ ;稱ߧM#'&1fygl[;@& YgVts4{0<0~3ɰѝẃL2] a`}J3#pۢ/eMt_H%p|+_f]v7̙?1脥SXuytUt]rIHQdGl6"{S.U9)jE9jc.˄A fQcsi !Ӎ3Q^]+uSW4G0َ|䫿Q@5 U]ϋKRl b.</Vrsulؗ8\6%MCUwl n.WMKWXr5QF +|fwëJ˦i^ г;mIt;l\61 Ґ)6(VCﺞ,Z ;G;YGώ௯N{[ ݪk.g[kUYþϬu9ΒsoXl(@x(" "RY<-v%tFYh)r\o7 GhPOy \LSLQbq* a7[ZNaWǮZ.J`-njLG秾 d[[q>ȈrSOS}/Z;:x=hpk Ǥ= (to㦇[~DžGŤSIg;\kk4|l;km1m5Cޞ1xK=+/\|ЃYr%z3tċS`ҫ?F^6{#5kN%SOXA)|&:0hfXWWK糰/נ(n+"`+"elmXt^3 *dBCN8m.w4E_<^XLvQ 46R_Qk`P:wǵǀ=pm;{ځ^|wJCUCz7:p1ܳ{wèw7g| VTCz7ҧ&ϕ}OIn[U:pNA߽Jn$8**һD&M33frOqYK?.i~=ړ_}mtK G%JDOq?c)®3Y: (^׶8rEW 7 ~ 1NCEEޤI]Nws.\Bu+ב<*㟾#I3mw"[dTض?g gdcKڨƋ/51zh~ 1UlE#. +C>Tlհ\gF΄6UQp)#ۜ Om-_s&|?\Nw[[[)[YL`qѥ_bƤE.V: =le n0gf]+&" "RV0x?Pk%zQQ S2)<ȖpUNf c(؊vlG \81\`p>6Er! V<Փ)Ad35˜ܕ<V%3&MEDlE  5/pK.zYέ@dR0­lθm4&^.!l0[)T,clh`@0iX! -ªP %&jsmE[]_[)KZlǘhrΘY^&|5ѵpAWٲ'g|s']^`Æm6ElETVV8ahEx<L,ze3Y<XlO M-e BLOw4E= 6<)s>¯ͯr&" "ׯ3Ril\C/ 9sgLk4'xpْj)ToscrL)ʖ-Ռ1X~ C-c XÅ(ZU'`+"ecn۹OCf=bsrS'ZGQ}+rm`X59p>K&gيeL{yf]MDlEd ihhy سϛOq<B,Ky->7UuE|sQ %u-~E+q/|^m-+`+"<q܉,vJRln\Wr?5 UlElE\wqL0__}IMd( qHv% gJ8~^<,-mbԨQQ__`+`+"cӧ3cvk$TrTIDAT k~"}({/+O>$ "+؊(؊H9d21?s9'hH96m'|2Lo;VVDVDԩS>G*3ApB=X袋uPDlEM8SN9 /+BU-٦[{455/|'|f7vhܸq|b1n3o<d2nVNE+0dF)TWW3h Fرcٳ(؊l)&M殻ٌtFDDDDlEDDDDlEDDDDlEDDDDlEDDDDVDDDDDVDDDDDVDDDDDVDDDDlEDDDDlEDDDDlEDDDDlEDDDDVDDDDDVDDDDDVDDDDDVDDDDlEDDDDlEDDDDlEDDDDlEDDDDlEDDDDVDDDDDVDDDDDVDDDDDVDDDDlEDDDDlEDDDDlEDDDDlEDDDDVDDDDDVDDDDDVDR)ȶkTWW`(؊lfϞ @<Qv=c3gffsNADd u놵 R[[#"b+"\uUd2 ""b+"̘1ޛz/_y<ԩSmtDD6UlED6I&c^n`РAuQL6MIDDVDdƽˈ#8VEF> Xf O6Z]T*E2#xcǎ{wkmd馛馛Xh 9޽{ӳgϭՋ^z1tPƌ;"`+GSS/iiiaÆQWWGuYjYhZ8TTT`ٮMuu5ݺuc]vceo-)S0sLinnoKK WfŊ\r%~Z]MDleә2e '|2T?3|>x'g?1?O6l9#}ծs˗/gʔ)s=L48'+" M>1cpA1a飃"\w~3.R;NC6ɓ'n)F7n/A`l7| =zl2:cƌсvff͚!" q.] At0dYhCՁ6d/^!" qVZEEE:IU]]lzmb QL:JB6d2%DjjjpYo[[QQQQQ[[[[QQQQ[[[[QQQQ[mK[[ncCKDDDleu}qg2gΜ.?11FK6Ȓ%KXd:Oqz!,Q[t>l&O9Yj&L`|+_裏ZK6HYr% _~9MMM8j~:X#"[5s: ۖ_\tEcYk-}a޼yTUU@3gAPUUE*o߾,[ :P#" iR))1x|Avg?k-Ojkk?>:H#"[5uEUUUqWCcc#7uKXkb\| %#" wyӧdUW]Ό?x<^ruڏ(SUU~ ݛ;OF>:߽E6Q[brU[W^yɧNTmQ-"W߿\}ժڏlhXvo9ZݛaͼJz' zI=v:㿘oS/\ڕڏڏ|2t_? 24g5Jz'yJz/hW΂Ԯ~~pElSZ֡ ݩRJv#"vv[ey]]l~ڕ/Q);_w}w/ "*#v%j?Yx1cƌ.c̘1455mEVd DԮDG"sΥVϟI!\26m2z9r++Qѣ9餓xG9w}7`+ۙ4e(eٔS8fk׽֬SiDJJ~$y'NZy~QnTy8{wO7cF|o|UIZlv^Ȫ]]]Hus/ eG~5 :Win^n/gxlϦ\s'mXWeDJJJGGDl71\% {n_g}?ZUlLYn\2R΃4ԮԮ~~DDv8_vF;`n1c8xbϝKxvkfm_ URRRQ8}_;>'ZmpU|1v%uRQO,co>/XqE>SqB+ڕ][[[[QQlp뭷pB8. ෿-ӦMc\x :'sѷonCl0V^e:)勄R( -ۇJzE%A4BC JKEKK)5CSwy?=9~_tsw^'j}6P(`0xxyyYzgϞ]ԹFTTT@P ::b~9W8777tvvСC'pXo$O>A@tt4>xxxՒݻp d-+կ͎>|QQQHMME~~>VZB$(J J^~3whhOF^"[[URRb q ގDBB/=wxx8T*^""66`0felvvjLX.'<<<,.~ c,ǘL&_.)~={~v?1W'W㨪޽{0<=y!o޼AUU$IBQQN< \BHZlڴ E{{;q}TWWC. /_YYY,_hnn۷os<{ $GFHJJRRR`gg<M&TgSF#r9f3,ш+'#dl"bcKDDDD%""""bcKDDDDƖ%""""bcKDDDDƖ%""""bcKDDDDƖ%""""bcKDDDDƖ[keZIENDB`onionbalance-0.2.2/onionbalance.py000077500000000000000000000003271411742520300171510ustar00rootroot00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- """Convenience wrapper for running Onionbalance directly from source tree.""" import onionbalance.manager if __name__ == '__main__': onionbalance.manager.main() onionbalance-0.2.2/onionbalance/000077500000000000000000000000001411742520300165725ustar00rootroot00000000000000onionbalance-0.2.2/onionbalance/__init__.py000066400000000000000000000004431411742520300207040ustar00rootroot00000000000000# -*- coding: utf-8 -*- __author__ = "George Kadianakis, Donncha O'Cearbhaill" __contact__ = "asn@torproject.org" __url__ = "https://github.com/asn-d6/onionbalance" __license__ = "GPL" from onionbalance._version import get_versions __version__ = get_versions()['version'] del get_versions onionbalance-0.2.2/onionbalance/_version.py000066400000000000000000000441301411742520300207720ustar00rootroot00000000000000 # This file helps to compute a version number in source trees obtained from # git-archive tarball (such as those provided by githubs download-from-tag # feature). Distribution tarballs (built by setup.py sdist) and build # directories (produced by setup.py build) will contain a much shorter file # that just contains the computed version number. # This file is released into the public domain. Generated by # versioneer-0.18 (https://github.com/warner/python-versioneer) """Git implementation of _version.py.""" import errno import os import re import subprocess import sys def get_keywords(): """Get the keywords needed to look up the version information.""" # these strings will be replaced by git during git-archive. # setup.py/versioneer.py will grep for the variable names, so they must # each be defined on a line of their own. _version.py will just call # get_keywords(). git_refnames = " (HEAD -> main, tag: 0.2.2)" git_full = "c2b50f7f2de7fe4d1b596cfa61393f27715508ea" git_date = "2021-07-29 20:02:01 +0300" keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} return keywords class VersioneerConfig: """Container for Versioneer configuration parameters.""" def get_config(): """Create, populate and return the VersioneerConfig() object.""" # these strings are filled in when 'setup.py versioneer' creates # _version.py cfg = VersioneerConfig() cfg.VCS = "git" cfg.style = "pep440" cfg.tag_prefix = "" cfg.parentdir_prefix = "onionbalance-" cfg.versionfile_source = "onionbalance/_version.py" cfg.verbose = False return cfg class NotThisMethod(Exception): """Exception raised if a method is not valid for the current scenario.""" LONG_VERSION_PY = {} HANDLERS = {} def register_vcs_handler(vcs, method): # decorator """Decorator to mark a method as the handler for a particular VCS.""" def decorate(f): """Store f in HANDLERS[vcs][method].""" if vcs not in HANDLERS: HANDLERS[vcs] = {} HANDLERS[vcs][method] = f return f return decorate def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): """Call the given command(s).""" assert isinstance(commands, list) p = None for c in commands: try: dispcmd = str([c] + args) # remember shell=False, so use git.cmd on windows, not just git p = subprocess.Popen([c] + args, cwd=cwd, env=env, stdout=subprocess.PIPE, stderr=(subprocess.PIPE if hide_stderr else None)) break except EnvironmentError: e = sys.exc_info()[1] if e.errno == errno.ENOENT: continue if verbose: print("unable to run %s" % dispcmd) print(e) return None, None else: if verbose: print("unable to find command, tried %s" % (commands,)) return None, None stdout = p.communicate()[0].strip() if sys.version_info[0] >= 3: stdout = stdout.decode() if p.returncode != 0: if verbose: print("unable to run %s (error)" % dispcmd) print("stdout was %s" % stdout) return None, p.returncode return stdout, p.returncode def versions_from_parentdir(parentdir_prefix, root, verbose): """Try to determine the version from the parent directory name. Source tarballs conventionally unpack into a directory that includes both the project name and a version string. We will also support searching up two directory levels for an appropriately named parent directory """ rootdirs = [] for i in range(3): dirname = os.path.basename(root) if dirname.startswith(parentdir_prefix): return {"version": dirname[len(parentdir_prefix):], "full-revisionid": None, "dirty": False, "error": None, "date": None} else: rootdirs.append(root) root = os.path.dirname(root) # up a level if verbose: print("Tried directories %s but none started with prefix %s" % (str(rootdirs), parentdir_prefix)) raise NotThisMethod("rootdir doesn't start with parentdir_prefix") @register_vcs_handler("git", "get_keywords") def git_get_keywords(versionfile_abs): """Extract version information from the given file.""" # the code embedded in _version.py can just fetch the value of these # keywords. When used from setup.py, we don't want to import _version.py, # so we do it with a regexp instead. This function is not used from # _version.py. keywords = {} try: f = open(versionfile_abs, "r") for line in f.readlines(): if line.strip().startswith("git_refnames ="): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["refnames"] = mo.group(1) if line.strip().startswith("git_full ="): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["full"] = mo.group(1) if line.strip().startswith("git_date ="): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["date"] = mo.group(1) f.close() except EnvironmentError: pass return keywords @register_vcs_handler("git", "keywords") def git_versions_from_keywords(keywords, tag_prefix, verbose): """Get version information from git keywords.""" if not keywords: raise NotThisMethod("no keywords at all, weird") date = keywords.get("date") if date is not None: # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 # -like" string, which we must then edit to make compliant), because # it's been around since git-1.5.3, and it's too difficult to # discover which version we're using, or to work around using an # older one. date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) refnames = keywords["refnames"].strip() if refnames.startswith("$Format"): if verbose: print("keywords are unexpanded, not using") raise NotThisMethod("unexpanded keywords, not a git-archive tarball") refs = set([r.strip() for r in refnames.strip("()").split(",")]) # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # just "foo-1.0". If we see a "tag: " prefix, prefer those. TAG = "tag: " tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) if not tags: # Either we're using git < 1.8.3, or there really are no tags. We use # a heuristic: assume all version tags have a digit. The old git %d # expansion behaves like git log --decorate=short and strips out the # refs/heads/ and refs/tags/ prefixes that would let us distinguish # between branches and tags. By ignoring refnames without digits, we # filter out many common branch names like "release" and # "stabilization", as well as "HEAD" and "master". tags = set([r for r in refs if re.search(r'\d', r)]) if verbose: print("discarding '%s', no digits" % ",".join(refs - tags)) if verbose: print("likely tags: %s" % ",".join(sorted(tags))) for ref in sorted(tags): # sorting will prefer e.g. "2.0" over "2.0rc1" if ref.startswith(tag_prefix): r = ref[len(tag_prefix):] if verbose: print("picking %s" % r) return {"version": r, "full-revisionid": keywords["full"].strip(), "dirty": False, "error": None, "date": date} # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") return {"version": "0+unknown", "full-revisionid": keywords["full"].strip(), "dirty": False, "error": "no suitable tags", "date": None} @register_vcs_handler("git", "pieces_from_vcs") def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): """Get version from 'git describe' in the root of the source tree. This only gets called if the git-archive 'subst' keywords were *not* expanded, and _version.py hasn't already been rewritten with a short version string, meaning we're inside a checked out source tree. """ GITS = ["git"] if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) if rc != 0: if verbose: print("Directory %s not under git control" % root) raise NotThisMethod("'git rev-parse --git-dir' returned error") # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there isn't one, this yields HEX[-dirty] (no NUM) describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", "--always", "--long", "--match", "%s*" % tag_prefix], cwd=root) # --long was added in git-1.5.5 if describe_out is None: raise NotThisMethod("'git describe' failed") describe_out = describe_out.strip() full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) if full_out is None: raise NotThisMethod("'git rev-parse' failed") full_out = full_out.strip() pieces = {} pieces["long"] = full_out pieces["short"] = full_out[:7] # maybe improved later pieces["error"] = None # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] # TAG might have hyphens. git_describe = describe_out # look for -dirty suffix dirty = git_describe.endswith("-dirty") pieces["dirty"] = dirty if dirty: git_describe = git_describe[:git_describe.rindex("-dirty")] # now we have TAG-NUM-gHEX or HEX if "-" in git_describe: # TAG-NUM-gHEX mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) if not mo: # unparseable. Maybe git-describe is misbehaving? pieces["error"] = ("unable to parse git-describe output: '%s'" % describe_out) return pieces # tag full_tag = mo.group(1) if not full_tag.startswith(tag_prefix): if verbose: fmt = "tag '%s' doesn't start with prefix '%s'" print(fmt % (full_tag, tag_prefix)) pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" % (full_tag, tag_prefix)) return pieces pieces["closest-tag"] = full_tag[len(tag_prefix):] # distance: number of commits since tag pieces["distance"] = int(mo.group(2)) # commit: short hex revision ID pieces["short"] = mo.group(3) else: # HEX: no tags pieces["closest-tag"] = None count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], cwd=root) pieces["distance"] = int(count_out) # total number of commits # commit date: see ISO-8601 comment in git_versions_from_keywords() date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) return pieces def plus_or_dot(pieces): """Return a + if we don't already have one, else return a .""" if "+" in pieces.get("closest-tag", ""): return "." return "+" def render_pep440(pieces): """Build up version string, with post-release "local version identifier". Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty Exceptions: 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: rendered += plus_or_dot(pieces) rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" else: # exception #1 rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered def render_pep440_pre(pieces): """TAG[.post.devDISTANCE] -- No -dirty. Exceptions: 1: no tags. 0.post.devDISTANCE """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"]: rendered += ".post.dev%d" % pieces["distance"] else: # exception #1 rendered = "0.post.dev%d" % pieces["distance"] return rendered def render_pep440_post(pieces): """TAG[.postDISTANCE[.dev0]+gHEX] . The ".dev0" means dirty. Note that .dev0 sorts backwards (a dirty tree will appear "older" than the corresponding clean one), but you shouldn't be releasing software with -dirty anyways. Exceptions: 1: no tags. 0.postDISTANCE[.dev0] """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: rendered += ".post%d" % pieces["distance"] if pieces["dirty"]: rendered += ".dev0" rendered += plus_or_dot(pieces) rendered += "g%s" % pieces["short"] else: # exception #1 rendered = "0.post%d" % pieces["distance"] if pieces["dirty"]: rendered += ".dev0" rendered += "+g%s" % pieces["short"] return rendered def render_pep440_old(pieces): """TAG[.postDISTANCE[.dev0]] . The ".dev0" means dirty. Eexceptions: 1: no tags. 0.postDISTANCE[.dev0] """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: rendered += ".post%d" % pieces["distance"] if pieces["dirty"]: rendered += ".dev0" else: # exception #1 rendered = "0.post%d" % pieces["distance"] if pieces["dirty"]: rendered += ".dev0" return rendered def render_git_describe(pieces): """TAG[-DISTANCE-gHEX][-dirty]. Like 'git describe --tags --dirty --always'. Exceptions: 1: no tags. HEX[-dirty] (note: no 'g' prefix) """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"]: rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) else: # exception #1 rendered = pieces["short"] if pieces["dirty"]: rendered += "-dirty" return rendered def render_git_describe_long(pieces): """TAG-DISTANCE-gHEX[-dirty]. Like 'git describe --tags --dirty --always -long'. The distance/hash is unconditional. Exceptions: 1: no tags. HEX[-dirty] (note: no 'g' prefix) """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) else: # exception #1 rendered = pieces["short"] if pieces["dirty"]: rendered += "-dirty" return rendered def render(pieces, style): """Render the given version pieces into the requested style.""" if pieces["error"]: return {"version": "unknown", "full-revisionid": pieces.get("long"), "dirty": None, "error": pieces["error"], "date": None} if not style or style == "default": style = "pep440" # the default if style == "pep440": rendered = render_pep440(pieces) elif style == "pep440-pre": rendered = render_pep440_pre(pieces) elif style == "pep440-post": rendered = render_pep440_post(pieces) elif style == "pep440-old": rendered = render_pep440_old(pieces) elif style == "git-describe": rendered = render_git_describe(pieces) elif style == "git-describe-long": rendered = render_git_describe_long(pieces) else: raise ValueError("unknown style '%s'" % style) return {"version": rendered, "full-revisionid": pieces["long"], "dirty": pieces["dirty"], "error": None, "date": pieces.get("date")} def get_versions(): """Get version information or return default if unable to do so.""" # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have # __file__, we can work backwards from there to the root. Some # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which # case we can only use expanded keywords. cfg = get_config() verbose = cfg.verbose try: return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose) except NotThisMethod: pass try: root = os.path.realpath(__file__) # versionfile_source is the relative path from the top of the source # tree (where the .git directory might live) to this file. Invert # this to find the root from __file__. for i in cfg.versionfile_source.split('/'): root = os.path.dirname(root) except NameError: return {"version": "0+unknown", "full-revisionid": None, "dirty": None, "error": "unable to find root of source tree", "date": None} try: pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) return render(pieces, cfg.style) except NotThisMethod: pass try: if cfg.parentdir_prefix: return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) except NotThisMethod: pass return {"version": "0+unknown", "full-revisionid": None, "dirty": None, "error": "unable to compute version", "date": None} onionbalance-0.2.2/onionbalance/common/000077500000000000000000000000001411742520300200625ustar00rootroot00000000000000onionbalance-0.2.2/onionbalance/common/__init__.py000066400000000000000000000000001411742520300221610ustar00rootroot00000000000000onionbalance-0.2.2/onionbalance/common/argparser.py000066400000000000000000000034711411742520300224270ustar00rootroot00000000000000import argparse import os import onionbalance TOR_CONTROL_SOCKET = os.environ.get('ONIONBALANCE_TOR_CONTROL_SOCKET', '/var/run/tor/control') def get_common_argparser(): """ Parses and returns command line arguments. """ parser = argparse.ArgumentParser( description="onionbalance distributes the requests for a Tor hidden " "services across multiple Tor instances.") parser.add_argument("--hs-version", type=str, default='v3', choices=('v3', 'v2'), help="Onion service version (default: v3)") parser.add_argument("-i", "--ip", type=str, default='127.0.0.1', help="Tor controller IP address") parser.add_argument("-p", "--port", type=int, default=9051, help="Tor controller port") parser.add_argument("-s", "--socket", type=str, default=TOR_CONTROL_SOCKET, help="Tor unix domain control socket location") parser.add_argument("-c", "--config", type=str, default=os.environ.get('ONIONBALANCE_CONFIG', "config.yaml"), help="Config file location") parser.add_argument("-v", "--verbosity", type=str, default=None, help="Minimum verbosity level for logging. Available " "in ascending order: debug, info, warning, " "error, critical). The default is info.") parser.add_argument('--version', action='version', version='onionbalance %s' % onionbalance.__version__) parser.add_argument("--is-testnet", action='store_true', help="Is this onionbalance on a test net? (Default: no)") return parser onionbalance-0.2.2/onionbalance/common/descriptor.py000066400000000000000000000026701411742520300226170ustar00rootroot00000000000000import stem from onionbalance.common import log logger = log.get_logger() def upload_descriptor(controller, signed_descriptor, hsdirs=None, v3_onion_address=None): """ Upload descriptor via the Tor control port If no HSDirs are specified, Tor will upload to what it thinks are the responsible directories If 'v3_onion_address' is set, this is a v3 HSPOST request, and the address needs to be embedded in the request. """ logger.debug("Beginning service descriptor upload.") server_args = "" # Provide server fingerprints to control command if HSDirs are specified. if hsdirs: server_args = ' '.join([("SERVER={}".format(hsdir)) for hsdir in hsdirs]) if v3_onion_address: server_args += " HSADDRESS=%s" % v3_onion_address.replace(".onion", "") # Stem will insert the leading + and trailing '\r\n.\r\n' response = controller.msg("HSPOST %s\n%s" % (server_args, signed_descriptor)) (response_code, divider, response_content) = response.content()[0] if not response.is_ok(): if response_code == "552": raise stem.InvalidRequest(response_code, response_content) else: raise stem.ProtocolError("HSPOST returned unexpected response " "code: %s\n%s" % (response_code, response_content)) onionbalance-0.2.2/onionbalance/common/instance.py000066400000000000000000000070061411742520300222430ustar00rootroot00000000000000import time import stem.control from onionbalance.common import log import onionbalance.common.util logger = log.get_logger() def helper_fetch_all_instance_descriptors(controller, instances, control_password=None): """ Try fetch fresh descriptors for all HS instances """ logger.info("Initiating fetch of descriptors for all service instances.") # pylint: disable=no-member while True: try: # Clear Tor descriptor cache before making fetches by sending # the NEWNYM singal controller.signal(stem.control.Signal.NEWNYM) time.sleep(5) # Sleep to allow Tor time to build new circuits pass except stem.SocketClosed: logger.error("Failed to send NEWNYM signal, socket is closed.") onionbalance.common.util.reauthenticate(controller, logger, control_password) else: break unique_instances = set(instances) # Only try to retrieve the descriptor once for each unique instance # address. An instance may be configured under multiple master # addressed. We do not want to request the same instance descriptor # multiple times. # Onionbalance will update all of the matching instances when a # descriptor is received. for instance in unique_instances: while True: try: instance.fetch_descriptor() except stem.SocketClosed: logger.error("Failed to fetch descriptor, socket is closed") onionbalance.common.util.reauthenticate(controller, logger, control_password) else: break class Instance(object): """ Instance represents a back-end load balancing hidden service. """ def __init__(self, controller, onion_address, authentication_cookie=None): """ Initialise an Instance object. """ self.controller = controller # Onion address for the service instance. if onion_address: onion_address = onion_address.replace('.onion', '') self.onion_address = onion_address # Flag this instance with its introduction points change. A new # master descriptor will then be published as the introduction # points have changed. self.intro_set_changed_since_published = False def fetch_descriptor(self): """ Try fetch a fresh descriptor for this service instance from the HSDirs """ logger.debug("Trying to fetch a descriptor for instance %s.onion.", self.onion_address) try: self.controller.get_hidden_service_descriptor(self.onion_address, await_result=False) except stem.SocketClosed: # Tor maybe restarting. raise except stem.DescriptorUnavailable: # Could not find the descriptor on the HSDir self.received = None logger.warning("No descriptor received for instance %s.onion, " "the instance may be offline.", self.onion_address) def __eq__(self, other): """ Instance objects are equal if they have the same onion address. """ if isinstance(other, Instance): return self.onion_address == other.onion_address else: return False def __hash__(self): """ Define __hash__ method allowing for set comparison between instances. """ return hash(self.onion_address) onionbalance-0.2.2/onionbalance/common/intro_point_set.py000066400000000000000000000055321411742520300236600ustar00rootroot00000000000000from future.moves.itertools import zip_longest import random import itertools class IntroductionPointSet(object): """ A set of introduction points to included in a HS descriptor. Provided with a list of available introduction points for each backend instance for an onionbalance service. This object will store the set of available introduction points and allow IPs to be selected from the available set. This class tracks which introduction points have already been provided and tries to provide the most diverse set of IPs. """ def __init__(self, intro_points): """ 'intro_points' is a list of lists that looks like this: [ [], [], [], ... ] """ # Shuffle the introduction point order before selecting IPs. # Randomizing now allows later calls to .choose() to be # deterministic. for instance_intro_points in intro_points: random.shuffle(instance_intro_points) random.shuffle(intro_points) self.intro_points = intro_points self._intro_point_generator = self._get_intro_point() def __len__(self): """Provide the total number of available introduction points""" return sum(len(ips) for ips in self.intro_points) def _get_intro_point(self): """ [Private function] Generator function which yields an introduction point Iterates through all available introduction points and try to pick IPs breath first across all backend instances. The intro point set is wrapped in `itertools.cycle` and will provided an infinite series of introduction points. """ # Combine intro points from across the backend instances and flatten intro_points = zip_longest(*self.intro_points) flat_intro_points = itertools.chain.from_iterable(intro_points) for intro_point in itertools.cycle(flat_intro_points): if intro_point: yield intro_point def choose(self, count=10, shuffle=True): """ [Public API] Retrieve N introduction points from the set of IPs Where more than `count` IPs are available, introduction points are selected to try and achieve the greatest distribution of introduction points across all of the available backend instances. Return a list of IntroductionPoints. """ # Limit `count` to the available number of IPs to avoid repeats. count = min(len(self), count) choosen_ips = list(itertools.islice(self._intro_point_generator, count)) if shuffle: random.shuffle(choosen_ips) return choosen_ips onionbalance-0.2.2/onionbalance/common/log.py000066400000000000000000000023131411742520300212140ustar00rootroot00000000000000# -*- coding: utf-8 -*- import logging import logging.handlers handler = logging.StreamHandler() handler.setFormatter(logging.Formatter(fmt="%(asctime)s [%(levelname)s]: " "%(message)s")) logger = logging.getLogger("onionbalance") logger.addHandler(handler) logger.setLevel(logging.DEBUG) def get_logger(): """ Returns a logger. """ return logger def setup_file_logger(log_file): """ Add log file handler to the existing logger """ handler = logging.handlers.RotatingFileHandler( log_file, maxBytes=10485760, backupCount=3) handler.setFormatter(logging.Formatter(fmt="%(asctime)s [%(levelname)s]: " "%(message)s")) logging.getLogger('onionbalance').addHandler(handler) def get_config_generator_logger(): """ Simplified logger for interactive config generator CLI """ handler = logging.StreamHandler() handler.setFormatter(logging.Formatter(fmt="[%(levelname)s]: " "%(message)s")) logger = logging.getLogger("onionbalance-config") logger.addHandler(handler) logger.setLevel(logging.INFO) return logger onionbalance-0.2.2/onionbalance/common/scheduler.py000066400000000000000000000052361411742520300224200ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Simple scheduler for running jobs at regular intervals """ import functools import time from onionbalance.common import log logger = log.get_logger() jobs = [] class Job(object): """ Object to represent a scheduled job task """ def __init__(self, interval, job_func, *job_args, **job_kwargs): self.interval = interval self.planned_run_time = time.time() # Configure the job function and calling arguments self.job_func = functools.partial(job_func, *job_args, **job_kwargs) functools.update_wrapper(self.job_func, job_func) def __lt__(self, other): """ Jobs are sorted based on their next scheduled run time """ return self.planned_run_time < other.planned_run_time @property def should_run(self): """ Check if the job should be run now """ return self.planned_run_time <= time.time() def run(self, override_run_time=None): """ Run job then reschedule it in the job list """ ret = self.job_func() # Pretend the job was scheduled now, if we ran it early with run_all() if override_run_time: self.planned_run_time = time.time() self.planned_run_time += self.interval return ret def __repr__(self): """ Return human readable representation of the Job and arguments """ args = [repr(x) for x in self.job_func.args] kwargs = ["{}={}".format(k, repr(v)) for k, v in self.job_func.keywords.items()] return "{}({})".format(self.job_func.__name__, ', '.join(args + kwargs)) def add_job(interval, function, *job_args, **job_kwargs): """ Add a job to be executed at regular intervals The `interval` value is in seconds, starting from now. """ job = Job(interval, function, *job_args, **job_kwargs) jobs.append(job) def _run_job(job, override_run_time=False): """ Run a job and put it back in the job queue """ return job.run(override_run_time) def run_all(delay_seconds=0): """ Run all jobs at `delay_seconds` regardless of their schedule """ for job in jobs: _run_job(job, override_run_time=True) time.sleep(delay_seconds) def run_forever(check_interval=1): """ Run jobs forever """ while True: if not jobs: logger.error("No scheduled jobs found, scheduler exiting.") return None jobs_to_run = (job for job in jobs if job.should_run) for job in sorted(jobs_to_run): _run_job(job) time.sleep(check_interval) onionbalance-0.2.2/onionbalance/common/signalhandler.py000066400000000000000000000026361411742520300232560ustar00rootroot00000000000000import sys import signal import logging from onionbalance.common import log from onionbalance.hs_v3 import onionbalance as onionbalance_v3 logger = log.get_logger() class SignalHandler(object): """ Handle signals sent to the Onionbalance daemon process """ def __init__(self, version, controller, status_socket=None): """ Setup signal handler """ self._version = version self._tor_controller = controller self._status_socket = status_socket # Register signal handlers signal.signal(signal.SIGTERM, self._handle_sigint_sigterm) signal.signal(signal.SIGINT, self._handle_sigint_sigterm) if self._version == 'v3': signal.signal(signal.SIGHUP, self._handle_sighup) def _handle_sigint_sigterm(self, signum, frame): """ Handle SIGINT (Ctrl-C) and SIGTERM Disconnect from control port and cleanup the status socket """ logger.info("Signal %d received, exiting", signum) self._tor_controller.close() if self._status_socket: self._status_socket.close() logging.shutdown() sys.exit(0) def _handle_sighup(self, signum, frame): """ Handle SIGHUP (v3 only) Reload configuration """ logger.info("Signal SIGHUP received, reloading configuration") onionbalance_v3.my_onionbalance.reload_config() onionbalance-0.2.2/onionbalance/common/status.py000066400000000000000000000022331411742520300217570ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Base class to provide status over Unix socket Default path: /var/run/onionbalance/control """ import errno import os from onionbalance.common import log logger = log.get_logger() class BaseStatusSocket(object): """ For creating a Unix domain socket which emits a summary of the Onionbalance status when a client connects. """ def __init__(self, unix_socket_filename): self.unix_socket_filename = unix_socket_filename def cleanup_socket_file(self): """ Try to remove the socket file if it exists already """ try: os.unlink(self.unix_socket_filename) except OSError as e: # Reraise if its not a FileNotFound exception if e.errno != errno.ENOENT: raise def close(self): """ Close the unix domain socket and remove its file """ try: self.server.shutdown() self.server.server_close() self.cleanup_socket_file() except AttributeError: pass except OSError: logger.exception("Error when removing the status socket") onionbalance-0.2.2/onionbalance/common/util.py000066400000000000000000000045511411742520300214160ustar00rootroot00000000000000import sys import time import os import yaml import stem from stem.control import Controller from onionbalance.common import log logger = log.get_logger() def read_config_data_from_file(config_path): if os.path.exists(config_path): with open(config_path, 'r') as handle: config_data = yaml.safe_load(handle.read()) logger.info("Loaded the config file '%s'.", config_path) else: logger.error("The specified config file '%s' does not exist. The " "onionbalance-config tool can generate the required " "keys and config files.", config_path) sys.exit(1) return config_data def connect_to_control_port(tor_socket=None, tor_address=None, tor_port=0, control_password=None): controller = None # Try first with a connection to the Tor unix domain control socket if tor_socket: try: controller = Controller.from_socket_file(path=tor_socket) logger.debug("Successfully connected to the Tor control socket " "%s.", tor_socket) except stem.SocketError: logger.debug("Unable to connect to the Tor control socket %s.", tor_socket) # If we didn't manage to connect to control socket, try IP:PORT if not controller: try: controller = Controller.from_port(address=tor_address, port=tor_port) logger.debug("Successfully connected to the Tor control port.") except stem.SocketError as exc: logger.error("Unable to connect to Tor control port: %s", exc) sys.exit(1) try: controller.authenticate(password=control_password) except stem.connection.AuthenticationFailure as exc: logger.error("Unable to authenticate on the Tor control connection: " "%s", exc) sys.exit(1) else: logger.debug("Successfully authenticated on the Tor control " "connection.") return controller def reauthenticate(controller, logger, control_password=None): """ Tries to authenticate to the controller """ time.sleep(10) try: controller.authenticate(password=control_password) except stem.connection.AuthenticationFailure: logger.error("Failed to re-authenticate controller.") onionbalance-0.2.2/onionbalance/config_generator/000077500000000000000000000000001411742520300221055ustar00rootroot00000000000000onionbalance-0.2.2/onionbalance/config_generator/__init__.py000066400000000000000000000000001411742520300242040ustar00rootroot00000000000000onionbalance-0.2.2/onionbalance/config_generator/config_generator.py000066400000000000000000000567201411742520300260040ustar00rootroot00000000000000import argparse import logging import shutil import os import sys import getpass import yaml import pkg_resources import Cryptodome.PublicKey.RSA from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from cryptography.hazmat.primitives import serialization from stem.descriptor.hidden_service import HiddenServiceDescriptorV3 import onionbalance from onionbalance.common import log from onionbalance.hs_v2 import util from onionbalance.hs_v3 import tor_ed25519 # Simplify the logging output for the command line tool logger = log.get_config_generator_logger() class ConfigGenerator(object): def __init__(self, args, interactive): self.args = args self.interactive = interactive self.hs_version = None self.output_path = None # A dictionary that maps services to their keys and instances: # { : (, instances) , ... } self.services = {} self.num_instances = None self.tag = None self.torrc_port_line = None self.instances = None self.master_dir = None self.config_file_path = None self.master_key_path = None # If this is set, it means that we read our private key from a tor # instance and hence we should just copy the private key over instead # of recreating the file (so that the Tor private key semantics are # maintained (see self.is_priv_key_in_tor_format in service.py) self.v3_loaded_key_from_file = False # Gather information required to create config file! self.gather_information() # Create config file! self.generate_config() def gather_information(self): self.hs_version = self.get_hs_version() assert(self.hs_version in ['v2', 'v3']) # Check if output directory exists, if not try create it self.output_path = self.get_output_path() if self.hs_version == "v2": self.master_dir = os.path.join(self.output_path, 'master') else: self.master_dir = self.output_path # Allow the creation of multiple services for v3 if self.hs_version == "v3": n_services = self.get_num_services() else: n_services = 1 # for v2 # Gather information for each service for i, _ in enumerate(range(n_services), start=1): # Load or generate the master key master_key, master_onion_address = self.load_master_key(i) # Generate keys for each instance self.num_instances, self.tag = self.get_num_instances(i) # v2 only: Create HiddenServicePort line for instance torrc file if self.hs_version == 'v2': self.torrc_port_line = self.get_torrc_port_line() instances = self.create_instances() self.services[master_onion_address] = (master_key, instances) def generate_config(self): # Write master key for each service for onion_address, (master_key, _) in self.services.items(): self.write_master_key_to_disk(onion_address, master_key) # Create the onionbalance config file self.create_yaml_config_file() if self.hs_version == 'v2': # Generate config files for each instance self.write_v2_instance_files() logger.info("Done! Successfully generated an Onionbalance config and %d " "instance keys for service %s.onion.", self.num_instances, onion_address) if self.hs_version == "v3": logger.info("Done! Successfully generated Onionbalance config.") logger.info("Now please edit '%s' with a text editor to add/remove/edit your backend instances.", self.config_file_path) def get_output_path(self): """ Get path to output directory and create if needed """ output_path = None if self.interactive: output_path = input("Enter path to store generated config " "[{}]: ".format(os.path.abspath(self.args.output))) output_path = output_path or self.args.output try: util.try_make_dir(output_path) except OSError: logger.exception("Problem encountered when trying to create the " "output directory %s.", os.path.abspath(output_path)) sys.exit(1) else: logger.debug("Created the output directory '%s'.", os.path.abspath(output_path)) # Do some directory validation if self.hs_version == 'v2' and not util.is_directory_empty(output_path): # The output directory should be empty to avoid having conflict keys # or config files. logger.error("The specified output directory '%s' is not empty. Please " "delete any files and folders or specify another output " "directory.", output_path) sys.exit(1) elif self.hs_version == 'v3': config_path = os.path.join(output_path, 'config.yaml') if os.path.isfile(config_path): logger.error("The specified output directory '%s' already contains a 'config.yaml' " "file. Please clean the directory before starting config_generator.", output_path) sys.exit(1) return output_path def get_hs_version(self): # Get the HS version hs_version = None if self.interactive: hs_version = input('Enter HS version ("v2" or "v3") (Leave empty for "v3"): ') hs_version = hs_version or self.args.hs_version if hs_version not in ["v2", "v3"]: logger.error('Only accepting "v2" and "v3" as HS versions') sys.exit(1) logger.info("Rolling with HS %s!", hs_version) return hs_version def load_master_key(self, i): """ Return the key and onion address of the frontend service. """ self.master_key_path = self.get_master_key_path(i) # master_key_path is now either None (if no key path is specified) or # set to the actual path if self.hs_version == 'v2': return self.load_v2_master_key(self.master_key_path) else: return self.load_v3_master_key(self.master_key_path) def get_master_key_path(self, i): # Load master key if specified master_key_path = None helper = " (i.e. path to 'hs_ed25519_secret_key')" if self.hs_version == 'v3' else "" if self.interactive: # Read key path from user master_key_path = input("Service #%d: Enter path to master service private key%s " "(Leave empty to generate a key): " % (i, helper)) master_key_path = self.args.key or master_key_path # If a key path was specified make sure it exists if master_key_path: if not os.path.isfile(master_key_path): logger.error("The specified master service private key '%s' " "could not be found. Please confirm the path and " "file permissions are correct.", master_key_path) sys.exit(1) return master_key_path def _load_v3_master_key_from_file(self, master_key_path): """ Load a private key straight from a Tor instance (no OBv3 keys supported) and return the private key and onion address. """ try: with open(master_key_path, 'rb') as handle: pem_key_bytes = handle.read() except EnvironmentError as e: logger.error("Unable to read service private key file ('%s')", e) sys.exit(1) try: master_private_key = tor_ed25519.load_tor_key_from_disk(pem_key_bytes) except ValueError: logger.error("Please provide path to a valid Tor master key") sys.exit(1) identity_pub_key = master_private_key.public_key() identity_pub_key_bytes = identity_pub_key.public_bytes(encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw) master_onion_address = HiddenServiceDescriptorV3.address_from_identity_key(identity_pub_key_bytes) # remove the trailing .onion master_onion_address = master_onion_address.replace(".onion", "") self.v3_loaded_key_from_file = True return master_private_key, master_onion_address def load_v3_master_key(self, master_key_path): if master_key_path: # load key from file # here we need to make many of these return self._load_v3_master_key_from_file(master_key_path) else: # generate new v3 key master_private_key = Ed25519PrivateKey.generate() master_public_key = master_private_key.public_key() master_pub_key_bytes = master_public_key.public_bytes(encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw) master_onion_address = HiddenServiceDescriptorV3.address_from_identity_key(master_pub_key_bytes) # cut out the onion since that's what the rest of the code expects master_onion_address = master_onion_address.replace(".onion", "") return master_private_key, master_onion_address def load_v2_master_key(self, master_key_path): if master_key_path: # Try load the specified private key file master_key = util.key_decrypt_prompt(master_key_path) if not master_key: logger.error("The specified master private key %s could not " "be loaded.", os.path.abspath(master_key)) sys.exit(1) master_onion_address = util.calc_onion_address(master_key) logger.info("Successfully loaded a master key for service " "%s.onion.", master_onion_address) else: # No key specified, begin generating a new one. master_key = Cryptodome.PublicKey.RSA.generate(1024) master_onion_address = util.calc_onion_address(master_key) logger.debug("Created a new master key for service %s.onion.", master_onion_address) return master_key, master_onion_address def get_num_services(self): """ Get the number of services this OnionBalance should support """ num_services = None if self.interactive: num_services = input("Number of services (frontends) to create (default: %d): " % self.args.num_services) # Cast to int if a number was specified try: num_services = int(num_services) except ValueError: num_services = None num_services = num_services or self.args.num_services logger.debug("Creating %d services", num_services) return num_services def get_num_instances(self, i): """ Get the number of instances and a tag name for them. """ num_instances = None if self.interactive: limits = " (min: 1, max: 8)" if self.hs_version == "v3" else "" num_instances = input("Service #%d: Number of instance services to create (default: %d)%s: " % (i, self.args.num_instances, limits)) # Cast to int if a number was specified try: num_instances = int(num_instances) except ValueError: num_instances = None num_instances = num_instances or self.args.num_instances logger.debug("Creating %d service instances.", num_instances) tag = None if self.interactive: tag = input("Service #%d: Provide a tag name to group these instances [%s]:" % (i, self.args.tag)) tag = tag or self.args.tag return num_instances, tag def get_torrc_port_line(self): """ Get the HiddenServicePort line for the instance torrc file """ service_virtual_port = None if self.interactive: service_virtual_port = input("Specify the service virtual port (for " "client connections) [{}]: ".format( self.args.service_virtual_port)) service_virtual_port = service_virtual_port or self.args.service_virtual_port service_target = None if self.interactive: # In interactive mode, change default target to match the specified # virtual port default_service_target = u'127.0.0.1:{}'.format(service_virtual_port) service_target = input("Specify the service target IP and port (where " "your service is listening) [{}]: ".format( default_service_target)) service_target = service_target or default_service_target service_target = service_target or self.args.service_target torrc_port_line = u'HiddenServicePort {} {}'.format(service_virtual_port, service_target) return torrc_port_line def create_instances(self): if self.hs_version == 'v2': return self.create_v2_instances() else: return self.create_v3_instances() def create_v2_instances(self): instances = [] for i in range(0, self.num_instances): instance_key = Cryptodome.PublicKey.RSA.generate(1024) instance_address = util.calc_onion_address(instance_key) logger.debug("Created a key for instance %s.onion.", instance_address) instances.append((instance_address, instance_key)) return instances def create_v3_instances(self): instances = [] for i in range(0, self.num_instances): instances.append(("", None)) return instances def get_master_key_passphrase(self): # Get optional passphrase for master key # [TODO: Implement for v3] master_passphrase = None if self.interactive: master_passphrase = getpass.getpass( "Provide an optional password to encrypt the master private " "key (Not encrypted if no password is specified): ") return master_passphrase or self.args.password def write_master_key_to_disk(self, onion_address, master_key): # Finished reading input, starting to write config files. util.try_make_dir(self.master_dir) master_key_file = os.path.join(self.master_dir, '{}.key'.format(onion_address)) with open(master_key_file, "wb") as key_file: os.chmod(master_key_file, 384) # chmod 0600 in decimal if self.hs_version == 'v2': master_passphrase = self.get_master_key_passphrase() key_file.write(master_key.exportKey(passphrase=master_passphrase)) elif self.hs_version == 'v3' and self.v3_loaded_key_from_file: # If we loaded a v3 key from a file, copy the file directly # (see loaded_key_from_file comments). shutil.copyfile(self.master_key_path, master_key_file) logger.info("Copied v3 master key from %s to %s.", self.master_key_path, master_key_file) else: # If we generated our own v3 master key, write it to file. If # 'master_key' does not exist, it means that we are loading it # from a file, so we dont need to write it to disk. master_key_formatted = master_key.private_bytes(encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption()) key_file.write(master_key_formatted) logger.debug("Successfully wrote master key to file %s.", os.path.abspath(master_key_file)) def write_v2_instance_files(self): master_key, instances = list(self.services.values())[0] for i, (instance_address, instance_key) in enumerate(instances): # Create a numbered directory for instance instance_dir = os.path.join(self.output_path, '{}{}'.format(self.tag, i + 1)) instance_key_dir = os.path.join(instance_dir, instance_address) util.try_make_dir(instance_key_dir) os.chmod(instance_key_dir, 1472) # chmod 2700 in decimal instance_key_file = os.path.join(instance_key_dir, 'private_key') with open(instance_key_file, "wb") as key_file: os.chmod(instance_key_file, 384) # chmod 0600 in decimal key_file.write(instance_key.exportKey()) logger.debug("Successfully wrote key for instance %s.onion to " "file.", instance_address) # Write torrc file for each instance instance_torrc = os.path.join(instance_dir, 'instance_torrc') instance_torrc_template = pkg_resources.resource_string( __name__, 'data/torrc-instance-v2') with open(instance_torrc, "w") as torrc_file: torrc_file.write(instance_torrc_template.decode('utf-8')) # The ./ relative path prevents Tor from raising relative # path warnings. The relative path may need to be edited manual # to work on Windows systems. torrc_file.write(u"HiddenServiceDir {}\n".format( instance_address)) torrc_file.write(u"{}\n".format(self.torrc_port_line)) def create_yaml_config_file(self): services_data = [] # Create an entry for each service for onion_address, (_, instances) in self.services.items(): # Create YAML Onionbalance settings file for these instances service_data = {'key': '{}.key'.format(onion_address)} service_data['instances'] = [{'address': address, 'name': '{}{}'.format(self.tag, i + 1)} for i, (address, _) in enumerate(instances)] services_data.append(service_data) # Yamlify the config settings_data = {'services': services_data} config_yaml = yaml.safe_dump(settings_data, default_flow_style=False) self.config_file_path = os.path.join(self.master_dir, 'config.yaml') with open(self.config_file_path, "w") as config_file: config_file.write(u"# Onionbalance Config File\n") config_file.write(config_yaml) logger.info("Wrote master service config file '%s'.", os.path.abspath(self.config_file_path)) if self.hs_version == 'v2': # Write frontend service torrc master_torrc_path = os.path.join(self.master_dir, 'torrc-server') master_torrc_template = pkg_resources.resource_string(__name__, 'data/torrc-server') with open(master_torrc_path, "w") as master_torrc_file: master_torrc_file.write(master_torrc_template.decode('utf-8')) def parse_cmd_args(): """ Parses and returns command line arguments for config generator """ parser = argparse.ArgumentParser( description="onionbalance-config generates config files and keys for " "Onionbalance instances and management servers. Calling without any " "options will initiate an interactive mode.") parser.add_argument("--hs-version", type=str, default="v3", help="Onion service version (default: %(default)s).") parser.add_argument("--key", type=str, default=None, help="RSA private key for the master onion service.") parser.add_argument("-p", "--password", type=str, default=None, help="Optional password which can be used to encrypt " "the master service private key.") parser.add_argument("-n", type=int, default=2, dest="num_instances", help="Number of instances to generate (default: " "%(default)s).") parser.add_argument("-s", type=int, default=1, dest="num_services", help="Number of services to generate (default: " "%(default)s).") parser.add_argument("-t", "--tag", type=str, default='node', help="Prefix name for the service instances " "(default: %(default)s).") parser.add_argument("--output", type=str, default='config/', help="Directory to store generate config files. " "The directory will be created if it does not " "already exist.") parser.add_argument("--no-interactive", action='store_true', help="Try to run automatically without prompting for " "user input.") parser.add_argument("-v", type=str, default="info", dest='verbosity', help="Minimum verbosity level for logging. Available " "in ascending order: debug, info, warning, error, " "critical). The default is info.") parser.add_argument("--service-virtual-port", type=str, default="80", help="Onion service port for external client " "connections (default: %(default)s).") # TODO: Add validator to check if the target host:port line makes sense. parser.add_argument("--service-target", type=str, default="127.0.0.1:80", help="Target IP and port where your service is " "listening (default: %(default)s).") # .. todo:: Add option to specify HS host and port for instance torrc parser.add_argument('--version', action='version', version='onionbalance %s' % onionbalance.__version__) return parser def main(): """ Entry point for interactive config file generation. """ # Parse initial command line options args = parse_cmd_args().parse_args() logger.info("Beginning Onionbalance config generation.") # If CLI options have been provided, don't enter interactive mode # Crude check to see if any options beside --verbosity are set. verbose = True if '-v' in sys.argv else False if ((len(sys.argv) > 1 and not verbose) or len(sys.argv) > 3 or args.no_interactive): interactive = False logger.info("Entering non-interactive mode.") else: interactive = True logger.info("No command line arguments found, entering interactive " "mode.") logger.setLevel(logging.__dict__[args.verbosity.upper()]) # Start the config generator! try: ConfigGenerator(args, interactive) except KeyboardInterrupt: logger.warning("\nConfig generator got interrupted! There might be temporary configuration files left over... Bye!") sys.exit(0) onionbalance-0.2.2/onionbalance/config_generator/data/000077500000000000000000000000001411742520300230165ustar00rootroot00000000000000onionbalance-0.2.2/onionbalance/config_generator/data/config.example.yaml000066400000000000000000000011411411742520300265760ustar00rootroot00000000000000# Onion Load Balancer Config File # --- # Each hidden service key line should be followed be followed by a list of 0 # or more instances which contain the onion address of the load balancing # hidden service REFRESH_INTERVAL: 600 # How often to poll for updated descriptors services: - key: /path/to/private_key # 7s4hxwwifcslrus2.onion instances: - address: o6ff73vmigi4oxka # web1 - address: nkz23ai6qesuwqhc # web2 - key: /path/to/private_key.enc # dpkdeys3apjtqydk.onion instances: - address: htbzowpp5cn7wj2u # irc1 - address: huey7aiod8dja8a3 # irc2 onionbalance-0.2.2/onionbalance/config_generator/data/torrc-instance-v2000066400000000000000000000006731411742520300262270ustar00rootroot00000000000000# Tor config for the onion service instance servers # --- # The instance servers run standard onion services. In Basic mode the # control port does not need to be enabled. DataDirectory tor-data # ControlPort 9051 # CookieAuthentication 1 SocksPort 0 RunAsDaemon 1 HiddenServiceVersion 2 # Configure each onion service instance with a unique permanent key. # HiddenServiceDir tor-data/hidden_service/ # HiddenServicePort 80 127.0.0.1:80 onionbalance-0.2.2/onionbalance/config_generator/data/torrc-instance-v3000066400000000000000000000006431411742520300262250ustar00rootroot00000000000000# Tor config for the onion service instance servers # --- # The instance servers run standard onion services. In Basic mode the # control port does not need to be enabled. DataDirectory tor-data # ControlPort 9051 # CookieAuthentication 1 SocksPort 0 RunAsDaemon 1 # Configure each onion service instance with a unique permanent key. # HiddenServiceDir tor-data/hidden_service/ # HiddenServicePort 80 127.0.0.1:80 onionbalance-0.2.2/onionbalance/config_generator/data/torrc-server000066400000000000000000000004311411742520300253740ustar00rootroot00000000000000# Tor config for the management server # --- # The management server must be able to access the Tor control port. # Alternatively the control port can be enabled on the system Tor process. DataDirectory tor-data ControlPort 9051 CookieAuthentication 1 SocksPort 0 RunAsDaemon 1 onionbalance-0.2.2/onionbalance/hs_v2/000077500000000000000000000000001411742520300176135ustar00rootroot00000000000000onionbalance-0.2.2/onionbalance/hs_v2/__init__.py000066400000000000000000000000001411742520300217120ustar00rootroot00000000000000onionbalance-0.2.2/onionbalance/hs_v2/config.py000066400000000000000000000020151411742520300214300ustar00rootroot00000000000000# -*- coding: utf-8 -*- import os """ Define default config options for the management server """ # Set default configuration options for the management server REPLICAS = 2 HSDIR_SET = 3 # Publish each descriptor to 3 consecutive HSDirs MAX_INTRO_POINTS = 10 DESCRIPTOR_OVERLAP_PERIOD = 60 * 60 DESCRIPTOR_UPLOAD_PERIOD = 60 * 60 # Re-upload descriptor every hour REFRESH_INTERVAL = 10 * 60 PUBLISH_CHECK_INTERVAL = 5 * 60 INITIAL_DELAY = 45 # Wait for instance descriptors before publishing LOG_LOCATION = os.environ.get('ONIONBALANCE_LOG_LOCATION') LOG_LEVEL = os.environ.get('ONIONBALANCE_LOG_LEVEL', 'info') STATUS_SOCKET_LOCATION = os.environ.get('ONIONBALANCE_STATUS_SOCKET_LOCATION', '/var/run/onionbalance/control') TOR_CONTROL_PASSWORD = None # Upload multiple distinct descriptors containing different subsets of # the available introduction points DISTINCT_DESCRIPTORS = True # Store global data about onion services and their instance nodes. services = [] controller = None onionbalance-0.2.2/onionbalance/hs_v2/consensus.py000066400000000000000000000044141411742520300222100ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ This module provides a set of functions for reading Tor consensus documents. """ from bisect import bisect_left import base64 import binascii import stem import stem.descriptor import onionbalance.common.log as log import onionbalance.hs_v2.config as config logger = log.get_logger() HSDIR_LIST = [] def refresh_consensus(): """ Update consensus state when Tor receives a new network status Retrieve the current set of hidden service directories """ global HSDIR_LIST if not config.controller: logger.warning("Controller connection not found in the configuration. " "Cannot update the Tor state.") return None else: controller = config.controller # pylint: disable=no-member # Retrieve the current set of hidden service directories hsdirs = [] try: for desc in controller.get_network_statuses(): if stem.Flag.HSDIR in desc.flags: hsdirs.append(desc.fingerprint) except IOError as err: logger.error("Could not load consensus from Tor: %s" % err) else: HSDIR_LIST = hsdirs logger.debug("Updated the list of Tor hidden service directories.") def get_hsdirs(descriptor_id): """ Get the responsible HSDirs for a given descriptor ID. """ # Try fetch a consensus if we haven't loaded one already if not HSDIR_LIST: refresh_consensus() if not HSDIR_LIST: raise ValueError('Could not determine the responsible HSDirs.') desc_id_bytes = base64.b32decode(descriptor_id, 1) descriptor_id_hex = (binascii.hexlify(desc_id_bytes). decode('utf-8').upper()) responsible_hsdirs = [] # Find postion of descriptor ID in the HSDir list index = descriptor_position = bisect_left(HSDIR_LIST, descriptor_id_hex) # Pick HSDirs until we have enough while len(responsible_hsdirs) < config.HSDIR_SET: try: responsible_hsdirs.append(HSDIR_LIST[index]) index += 1 except IndexError: # Wrap around when we reach the end of the HSDir list index = 0 # Do not choose a HSDir more than once if index == descriptor_position: break return responsible_hsdirs onionbalance-0.2.2/onionbalance/hs_v2/descriptor.py000066400000000000000000000175751411742520300223620ustar00rootroot00000000000000# -*- coding: utf-8 -*- import base64 import textwrap import datetime import Cryptodome.Signature.pkcs1_15 import Cryptodome.Hash.SHA import stem.descriptor.hidden_service_descriptor from onionbalance.hs_v2 import util from onionbalance.common import log from onionbalance.hs_v2 import config logger = log.get_logger() def generate_service_descriptor(permanent_key, introduction_point_list=None, replica=0, timestamp=None, deviation=0): """ High-level interface for generating a signed HS descriptor """ if not timestamp: timestamp = datetime.datetime.utcnow() unix_timestamp = int(timestamp.strftime("%s")) permanent_key_block = make_public_key_block(permanent_key) permanent_id = util.calc_permanent_id(permanent_key) # Calculate the current secret-id-part for this hidden service # Deviation allows the generation of a descriptor for a different time # period. time_period = (util.get_time_period(unix_timestamp, permanent_id) + int(deviation)) secret_id_part = util.calc_secret_id_part(time_period, None, replica) descriptor_id = util.calc_descriptor_id(permanent_id, secret_id_part) if not introduction_point_list: onion_address = util.calc_onion_address(permanent_key) raise ValueError("No introduction points for service %s.onion." % onion_address) # Generate the introduction point section of the descriptor intro_section = make_introduction_points_part( introduction_point_list ) unsigned_descriptor = generate_hs_descriptor_raw( desc_id_base32=util.base32_encode_str(descriptor_id), permanent_key_block=permanent_key_block, secret_id_part_base32=util.base32_encode_str(secret_id_part), publication_time=util.rounded_timestamp(timestamp), introduction_points_part=intro_section ) signed_descriptor = sign_descriptor(unsigned_descriptor, permanent_key) return signed_descriptor def generate_hs_descriptor_raw(desc_id_base32, permanent_key_block, secret_id_part_base32, publication_time, introduction_points_part): """ Generate hidden service descriptor string """ doc = [ "rendezvous-service-descriptor {}".format(desc_id_base32), "version 2", "permanent-key", permanent_key_block, "secret-id-part {}".format(secret_id_part_base32), "publication-time {}".format(publication_time), "protocol-versions 2,3", "introduction-points", introduction_points_part, "signature\n", ] unsigned_descriptor = '\n'.join(doc) return unsigned_descriptor def make_introduction_points_part(introduction_point_list=None): """ Make introduction point block from list of IntroductionPoint objects """ # If no intro points were specified, we should create an empty list if not introduction_point_list: introduction_point_list = [] intro = [] for intro_point in introduction_point_list: intro.append("introduction-point {}".format(intro_point.identifier)) intro.append("ip-address {}".format(intro_point.address)) intro.append("onion-port {}".format(intro_point.port)) intro.append("onion-key") intro.append(intro_point.onion_key) intro.append("service-key") intro.append(intro_point.service_key) intro_section = '\n'.join(intro).encode('utf-8') intro_section_base64 = base64.b64encode(intro_section).decode('utf-8') intro_section_base64 = textwrap.fill(intro_section_base64, 64) # Add the header and footer: intro_points_with_headers = '\n'.join([ '-----BEGIN MESSAGE-----', intro_section_base64, '-----END MESSAGE-----']) return intro_points_with_headers def make_public_key_block(key): """ Get ASN.1 representation of public key, base64 and add headers """ asn1_pub = util.get_asn1_sequence(key) pub_base64 = base64.b64encode(asn1_pub).decode('utf-8') pub_base64 = textwrap.fill(pub_base64, 64) # Add the header and footer: pub_with_headers = '\n'.join([ '-----BEGIN RSA PUBLIC KEY-----', pub_base64, '-----END RSA PUBLIC KEY-----']) return pub_with_headers def pad_msg_with_tor_pkcs(msg_hash, emLen, with_hash_parameters=True): """ Tor requires PKCS#1 1.5 padding for descriptor signatures but does not include the algorithmIdentifier as specified in RFC3447. Unfortunately, most crypto libraries add that algorithmIdentifier by force and hence we need this function for monkey patching. """ digestInfo = msg_hash.digest() PS = b'\xFF' * (emLen - len(digestInfo) - 3) return b'\x00\x01' + PS + b'\x00' + digestInfo def sign(body, private_key): """ Sign, base64 encode, wrap and add Tor signature headers The message digest is PKCS1 padded without the optional algorithmIdentifier section. """ # First monkey-patch cryptodome to do the Tor PKCS#1 padding Cryptodome.Signature.pkcs1_15._EMSA_PKCS1_V1_5_ENCODE = pad_msg_with_tor_pkcs # The RSA signing API requires a hasher as input hasher = Cryptodome.Hash.SHA1.new(body) # Now do the signing signer = Cryptodome.Signature.pkcs1_15.new(private_key) signature_bytes = signer.sign(hasher) # Convert the signature to the base64 format that our descriptors like signature_base64 = base64.b64encode(signature_bytes).decode('utf-8') signature_base64 = textwrap.fill(signature_base64, 64) # Add the header and footer: signature_with_headers = '\n'.join([ '-----BEGIN SIGNATURE-----', signature_base64, '-----END SIGNATURE-----']) return signature_with_headers def sign_descriptor(descriptor, service_privkey): """ Sign or resign a provided hidden service descriptor """ token_descriptor_signature = '\nsignature\n' # Remove signature block if it exists if token_descriptor_signature in descriptor: descriptor = descriptor[:descriptor.find(token_descriptor_signature) + len(token_descriptor_signature)] else: descriptor = descriptor.strip() + token_descriptor_signature signature_with_headers = sign(descriptor.encode('utf-8'), service_privkey) return descriptor + signature_with_headers def descriptor_received(descriptor_content): """ Process onion service descriptors retrieved from the HSDir system or received directly over the metadata channel. """ try: parsed_descriptor = stem.descriptor.hidden_service_descriptor.\ HiddenServiceDescriptor(descriptor_content, validate=True) except ValueError: logger.exception("Received an invalid service descriptor.") return None # Ensure the received descriptor matches the requested descriptor permanent_key = Cryptodome.PublicKey.RSA.importKey( parsed_descriptor.permanent_key) descriptor_onion_address = util.calc_onion_address(permanent_key) known_descriptor, instance_changed = False, False for instance in [instance for service in config.services for instance in service.instances]: if instance.onion_address == descriptor_onion_address: instance_changed |= instance.update_descriptor(parsed_descriptor) known_descriptor = True if instance_changed: logger.info("The introduction point set has changed for instance " "%s.onion.", descriptor_onion_address) if not known_descriptor: # No matching service instance was found for the descriptor logger.debug("Received a descriptor for an unknown service:\n%s", descriptor_content.decode('utf-8')) logger.warning("Received a descriptor with address %s.onion that " "did not match any configured service instances.", descriptor_onion_address) return None onionbalance-0.2.2/onionbalance/hs_v2/eventhandler.py000066400000000000000000000043671411742520300226560ustar00rootroot00000000000000# -*- coding: utf-8 -*- from builtins import str, object from onionbalance.common import log from onionbalance.hs_v2 import descriptor from onionbalance.hs_v2 import consensus logger = log.get_logger() class EventHandler(object): """ Handles asynchronous Tor events. """ @staticmethod def new_status(status_event): """ Parse Tor status events such as "STATUS_CLIENT" """ # pylint: disable=no-member if status_event.action == "CONSENSUS_ARRIVED": # Update the local view of the consensus in Onionbalance try: consensus.refresh_consensus() except Exception: logger.exception("An unexpected exception occured in the " "when processing the consensus update " "callback.") @staticmethod def new_desc(desc_event): """ Parse HS_DESC response events """ logger.debug("Received new HS_DESC event: %s", str(desc_event)) @staticmethod def new_desc_content(desc_content_event): """ Parse HS_DESC_CONTENT response events for descriptor content Update the HS instance object with the data from the new descriptor. """ logger.debug("Received new HS_DESC_CONTENT event for %s.onion", desc_content_event.address) # Check that the HSDir returned a descriptor that is not empty descriptor_text = str(desc_content_event.descriptor).encode('utf-8') # HSDirs provide a HS_DESC_CONTENT response with either one or two # CRLF lines when they do not have a matching descriptor. Using # len() < 5 should ensure all empty HS_DESC_CONTENT events are matched. if len(descriptor_text) < 5: logger.debug("Empty descriptor received for %s.onion", desc_content_event.address) return None # Send content to callback function which will process the descriptor try: descriptor.descriptor_received(descriptor_text) except Exception: logger.exception("An unexpected exception occured in the " "new descriptor callback.") return None onionbalance-0.2.2/onionbalance/hs_v2/instance.py000066400000000000000000000063451411742520300220010ustar00rootroot00000000000000# -*- coding: utf-8 -*- import datetime from onionbalance.common import log from onionbalance.hs_v2 import config import onionbalance.common.instance logger = log.get_logger() def fetch_instance_descriptors(controller): all_instances = [instance for service in config.services for instance in service.instances] onionbalance.common.instance.helper_fetch_all_instance_descriptors(controller, all_instances, control_password=config.TOR_CONTROL_PASSWORD) class InstanceV2(onionbalance.common.instance.Instance): """ This is a V2 onionbalance instance """ def __init__(self, controller, onion_address, authentication_cookie): """ Constructor for V2 instance """ # Initialize the common instance class super().__init__(controller, onion_address) self.authentication_cookie = authentication_cookie # Store the latest set of introduction points for this instance self.introduction_points = [] # Timestamp when last received a descriptor for this instance self.received = None # Timestamp of the currently loaded descriptor self.timestamp = None def update_descriptor(self, parsed_descriptor): """ Update introduction points when a new HS descriptor is received Parse the descriptor content and update the set of introduction points for this HS instance. Returns True if the introduction point set has changed, False otherwise.` """ self.received = datetime.datetime.utcnow() logger.debug("Received a descriptor for instance %s.onion.", self.onion_address) # Reject descriptor if its timestamp is older than the current # descriptor. Prevents HSDirs from replaying old, expired # descriptors. if self.timestamp and parsed_descriptor.published < self.timestamp: logger.error("Received descriptor for instance %s.onion with " "publication timestamp (%s) older than the latest " "descriptor (%s). Ignoring the descriptor.", self.onion_address, parsed_descriptor.published, self.timestamp) return False else: self.timestamp = parsed_descriptor.published # Parse the introduction point list, decrypting if necessary introduction_points = parsed_descriptor.introduction_points( authentication_cookie=self.authentication_cookie ) # If the new introduction points are different, flag this instance # as modified. Compare the set of introduction point identifiers # (fingerprint of the per IP circuit service key). if (set(ip.identifier for ip in introduction_points) != set(ip.identifier for ip in self.introduction_points)): self.intro_set_changed_since_published = True self.introduction_points = introduction_points return True else: logger.debug("Introduction points for instance %s.onion matched " "the cached set.", self.onion_address) return False onionbalance-0.2.2/onionbalance/hs_v2/manager.py000066400000000000000000000065451411742520300216110ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Load balance a hidden service across multiple (remote) Tor instances by create a hidden service descriptor containing introduction points from each instance. """ import sys import logging # import Cryptodome.PublicKey import stem from stem.control import EventType import onionbalance.common.util from onionbalance.common import scheduler from onionbalance.common import log from onionbalance.common import signalhandler from onionbalance.hs_v2 import config from onionbalance.hs_v2 import eventhandler from onionbalance.hs_v2 import settings from onionbalance.hs_v2 import status from onionbalance.hs_v2.service import publish_all_descriptors from onionbalance.hs_v2.instance import fetch_instance_descriptors logger = log.get_logger() def main(args): """ Entry point when invoked over the command line. """ config_file_options = settings.parse_config_file(args.config) # Update global configuration with options specified in the config file for setting in dir(config): if setting.isupper() and config_file_options.get(setting): setattr(config, setting, config_file_options.get(setting)) # Override the log level if specified on the command line. if args.verbosity: config.LOG_LEVEL = args.verbosity.upper() # Write log file if configured in environment variable or config file if config.LOG_LOCATION: log.setup_file_logger(config.LOG_LOCATION) logger.setLevel(logging.__dict__[config.LOG_LEVEL.upper()]) controller = onionbalance.common.util.connect_to_control_port(args.socket, args.ip, args.port, config.TOR_CONTROL_PASSWORD) status_socket = status.StatusSocket(config.STATUS_SOCKET_LOCATION) signalhandler.SignalHandler('v2', controller, status_socket) # Disable no-member due to bug with "Instance of 'Enum' has no * member" # pylint: disable=no-member # Check that the Tor client supports the HSPOST control port command if not controller.get_version() >= stem.version.Requirement.HSPOST: logger.error("A Tor version >= %s is required. You may need to " "compile Tor from source or install a package from " "the experimental Tor repository.", stem.version.Requirement.HSPOST) sys.exit(1) # Load the keys and config for each onion service settings.initialize_services(controller, config_file_options.get('services')) # Finished parsing all the config file. handler = eventhandler.EventHandler() controller.add_event_listener(handler.new_status, EventType.STATUS_CLIENT) controller.add_event_listener(handler.new_desc, EventType.HS_DESC) controller.add_event_listener(handler.new_desc_content, EventType.HS_DESC_CONTENT) # Schedule descriptor fetch and upload events scheduler.add_job(config.REFRESH_INTERVAL, fetch_instance_descriptors, controller) scheduler.add_job(config.PUBLISH_CHECK_INTERVAL, publish_all_descriptors) # Run initial fetch of HS instance descriptors scheduler.run_all(delay_seconds=config.INITIAL_DELAY) # Begin main loop to poll for HS descriptors scheduler.run_forever() return 0 onionbalance-0.2.2/onionbalance/hs_v2/service.py000066400000000000000000000266331411742520300216370ustar00rootroot00000000000000# -*- coding: utf-8 -*- import datetime import time import base64 import Cryptodome.PublicKey.RSA import stem from onionbalance.hs_v2 import descriptor from onionbalance.hs_v2 import util from onionbalance.hs_v2 import consensus import onionbalance.common.descriptor import onionbalance.common.util from onionbalance.common import log from onionbalance.hs_v2 import config from onionbalance.common import intro_point_set logger = log.get_logger() def publish_all_descriptors(): """ Called periodically to upload new super-descriptors if needed .. todo:: Publishing descriptors for different services at the same time will leak that they are related. Descriptors should be published individually at a random interval to avoid correlation. """ logger.debug("Checking if any master descriptors should be published.") for service in config.services: service.descriptor_publish() class Service(object): """ Service represents a front-facing hidden service which should be load-balanced. """ def __init__(self, controller, service_key=None, instances=None): """ Initialise a HiddenService object. """ self.controller = controller # Service key must be a valid PyCrypto RSA key object if isinstance(service_key, Cryptodome.PublicKey.RSA.RsaKey): self.service_key = service_key else: raise ValueError("Service key is not a valid RSA object.") # List of instances for this onion service if not instances: instances = [] self.instances = instances # Calculate the onion address for this service self.onion_address = util.calc_onion_address(self.service_key) # Timestamp when this descriptor was last attempted self.uploaded = None def _intro_points_modified(self): """ Check if the introduction point set has changed since last publish. """ return any(instance.intro_set_changed_since_published for instance in self.instances) def _descriptor_not_uploaded_recently(self): """ Check if the master descriptor hasn't been uploaded recently """ if not self.uploaded: # Descriptor never uploaded return True descriptor_age = (datetime.datetime.utcnow() - self.uploaded) if (descriptor_age.total_seconds() > config.DESCRIPTOR_UPLOAD_PERIOD): return True else: return False def _descriptor_id_changing_soon(self): """ If the descriptor ID will change soon, upload under both descriptor IDs """ permanent_id = base64.b32decode(self.onion_address, 1) seconds_valid = util.get_seconds_valid(time.time(), permanent_id) # Check if descriptor ID will be changing within the overlap period. if seconds_valid < config.DESCRIPTOR_OVERLAP_PERIOD: return True else: return False def _select_introduction_points(self): """ Choose set of introduction points from all fresh descriptors Returns an intro_point_set.IntroductionPointSet() which can be used to choose introduction points. """ available_intro_points = [] # Loop through each instance and determine fresh intro points for instance in self.instances: if not instance.received: logger.info("No descriptor received for instance %s.onion " "yet.", instance.onion_address) continue # The instance may be offline if no descriptor has been received # for it recently or if the received descriptor's timestamp is # too old received_age = datetime.datetime.utcnow() - instance.received timestamp_age = datetime.datetime.utcnow() - instance.timestamp received_age = received_age.total_seconds() timestamp_age = timestamp_age.total_seconds() if received_age > config.DESCRIPTOR_UPLOAD_PERIOD: logger.info("Our descriptor for instance %s.onion " "was received too long ago (%d). " "The instance may be offline. Its introduction " "points will not be included in the master " "descriptor.", instance.onion_address, received_age) continue elif timestamp_age > (4 * 60 * 60): logger.info("Our descriptor for instance %s.onion " "has an old timestamp (%d). " "The instance may be offline. Its introduction " "points will not be included in the master " "descriptor.", instance.onion_address, timestamp_age) continue else: # Include this instance's introduction points instance.intro_set_changed_since_published = False available_intro_points.append(instance.introduction_points) return intro_point_set.IntroductionPointSet(available_intro_points) def _publish_descriptor(self, deviation=0): """ Create, sign and upload master descriptors for this service """ # Retrieve the set of available introduction points intro_point_set = self._select_introduction_points() max_intro_points = config.MAX_INTRO_POINTS # Upload multiple unique descriptors which contain different # subsets of the available introduction points. # (https://github.com/DonnchaC/onionbalance/issues/16) distinct_descriptors = config.DISTINCT_DESCRIPTORS # If we have <= MAX_INTRO_POINTS we should choose the introduction # points now and use the same set in every descriptor. Using the # same set of introduction points will look more like a standard # Tor client. num_intro_points = len(intro_point_set) if num_intro_points <= max_intro_points: intro_points = intro_point_set.choose(num_intro_points) logger.debug("We have %d IPs, not using distinct descriptors.", len(intro_point_set)) distinct_descriptors = False for replica in range(0, config.REPLICAS): # Using distinct descriptors, choose a new set of intro points # for each descriptor and upload it to individual HSDirs. if distinct_descriptors: descriptor_id = util.calc_descriptor_id_b32( self.onion_address, time=time.time(), replica=replica, deviation=deviation, ) responsible_hsdirs = consensus.get_hsdirs(descriptor_id) for hsdir in responsible_hsdirs: intro_points = intro_point_set.choose(max_intro_points) try: signed_descriptor = ( descriptor.generate_service_descriptor( self.service_key, introduction_point_list=intro_points, replica=replica, deviation=deviation )) except ValueError as exc: logger.warning("Error generating descriptor: %s", exc) continue # Signed descriptor was generated successfully, upload it # to the respective HSDir self._upload_descriptor(signed_descriptor, replica, hsdirs=hsdir) logger.info("Published distinct master descriptors for " "service %s.onion under replica %d.", self.onion_address, replica) else: # Not using distinct descriptors, upload one descriptor # under each replica and let Tor pick the HSDirs. try: signed_descriptor = descriptor.generate_service_descriptor( self.service_key, introduction_point_list=intro_points, replica=replica, deviation=deviation ) except ValueError as exc: logger.warning("Error generating descriptor: %s", exc) continue # Signed descriptor was generated successfully, upload it self._upload_descriptor(signed_descriptor, replica) logger.info("Published a descriptor for service %s.onion " "under replica %d.", self.onion_address, replica) # It would be better to set last_uploaded when an upload succeeds and # not when an upload is just attempted. Unfortunately the HS_DESC # # UPLOADED event does not provide information about the service and # so it can't be used to determine when descriptor upload succeeds self.uploaded = datetime.datetime.utcnow() def _upload_descriptor(self, signed_descriptor, replica, hsdirs=None): """ Convenience method to upload a descriptor Handle some error checking and logging inside the Service class """ if hsdirs and not isinstance(hsdirs, list): hsdirs = [hsdirs] while True: try: onionbalance.common.descriptor.upload_descriptor(self.controller, signed_descriptor, hsdirs=hsdirs) break except stem.SocketClosed: logger.error("Error uploading descriptor for service " "%s.onion, Socket is closed.", self.onion_address) onionbalance.common.util.reauthenticate(self.controller, logger) except stem.ControllerError: logger.exception("Error uploading descriptor for service " "%s.onion.", self.onion_address) break def descriptor_publish(self, force_publish=False): """ Publish descriptor if have new IPs or if descriptor has expired """ # A descriptor should be published if any of the following conditions # are True if any([self._intro_points_modified(), # If any IPs have changed self._descriptor_not_uploaded_recently(), force_publish]): logger.debug("Publishing a descriptor for service %s.onion.", self.onion_address) self._publish_descriptor() # If the descriptor ID will change soon, need to upload under # the new ID too. if self._descriptor_id_changing_soon(): logger.info("Publishing a descriptor for service %s.onion " "under next descriptor ID.", self.onion_address) self._publish_descriptor(deviation=1) else: logger.debug("Not publishing a new descriptor for service " "%s.onion.", self.onion_address) onionbalance-0.2.2/onionbalance/hs_v2/settings.py000066400000000000000000000072711411742520300220340ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Implements the generation and loading of configuration files. """ import os import sys import errno from onionbalance.hs_v2 import config from onionbalance.common import log import onionbalance.hs_v2.util import onionbalance.common.util import onionbalance.hs_v2.service import onionbalance.hs_v2.instance logger = log.get_logger() def parse_config_file(config_file): """ Parse config file containing service information """ config_path = os.path.abspath(config_file) config_data = onionbalance.common.util.read_config_data_from_file(config_path) # Rewrite relative paths in the config to be relative to the config # file directory config_directory = os.path.dirname(config_path) for service in config_data.get('services'): if not os.path.isabs(service.get('key')): service['key'] = os.path.join(config_directory, service['key']) return config_data def initialize_services(controller, services_config): """ Load keys for services listed in the config """ # Load the keys and config for each onion service for service in services_config: try: service_key = onionbalance.hs_v2.util.key_decrypt_prompt(service.get("key")) except ValueError as e: logger.error("Got exception '%s'. Perhaps you are trying to load a v3 onion service " "but with --hs-version=v2 enabled?", e) sys.exit(1) except (IOError, OSError) as e: if e.errno == errno.ENOENT: logger.error("Private key file %s could not be found. " "Relative paths in the config file are loaded " "relative to the config file directory.", service.get("key")) sys.exit(1) elif e.errno == errno.EACCES: logger.error("Permission denied to private key %s.", service.get("key")) sys.exit(1) else: raise # Key file was read but a valid private key was not found. if not service_key: logger.error("Private key %s could not be loaded. It is a not " "valid 1024 bit PEM encoded RSA private key", service.get("key")) sys.exit(1) else: # Successfully imported the private key onion_address = onionbalance.hs_v2.util.calc_onion_address(service_key) logger.debug("Loaded private key for service %s.onion.", onion_address) # Load all instances for the current onion service instance_config = service.get("instances", []) if not instance_config: logger.error("Could not load any instances for service " "%s.onion.", onion_address) sys.exit(1) else: instances = [] for instance in instance_config: instances.append(onionbalance.hs_v2.instance.InstanceV2( controller=controller, onion_address=instance.get("address"), authentication_cookie=instance.get("auth") )) logger.info("Loaded %d instances for service %s.onion.", len(instances), onion_address) # Store service configuration in config.services global config.services.append(onionbalance.hs_v2.service.Service( controller=controller, service_key=service_key, instances=instances )) # Store a global reference to current controller connection config.controller = controller onionbalance-0.2.2/onionbalance/hs_v2/status.py000066400000000000000000000060261411742520300215140ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Provide status over Unix socket Default path: /var/run/onionbalance/control """ import threading import socket from socketserver import BaseRequestHandler, ThreadingMixIn, UnixStreamServer from onionbalance.common import log from onionbalance.common.status import BaseStatusSocket from onionbalance.hs_v2 import config logger = log.get_logger() class StatusSocketHandler(BaseRequestHandler): """ Handler for new domain socket connections """ def handle(self): """ Prepare and output the status summary when a connection is received """ time_format = "%Y-%m-%d %H:%M:%S" response = [] for service in config.services: if service.uploaded: service_timestamp = service.uploaded.strftime(time_format) else: service_timestamp = "[not uploaded]" response.append("{}.onion {}".format(service.onion_address, service_timestamp)) for instance in service.instances: if not instance.timestamp: response.append(" {}.onion [offline]".format( instance.onion_address)) else: response.append(" {}.onion {} {} IPs".format( instance.onion_address, instance.timestamp.strftime(time_format), len(instance.introduction_points))) response.append("") self.request.sendall('\n'.join(response).encode('utf-8')) class ThreadingSocketServer(ThreadingMixIn, UnixStreamServer): """ Unix socket server with threading """ pass class StatusSocket(BaseStatusSocket): """ Create a Unix domain socket which emits a summary of the Onionbalance status when a client connects. """ def __init__(self, status_socket_location): """ Create the Unix domain socket status server and start in a thread Example:: socat - unix-connect:/var/run/onionbalance/control uweyln7jhkyaokka.onion 2016-05-01 11:08:56 r523s7jx65ckitf4.onion [offline] v2q7ujuleky7odph.onion 2016-05-01 11:00:00 3 IPs """ super().__init__(status_socket_location) self.cleanup_socket_file() logger.debug("Creating status socket at %s", self.unix_socket_filename) try: self.server = ThreadingSocketServer(self.unix_socket_filename, StatusSocketHandler) # Start running the socket server in a another thread server_thread = threading.Thread(target=self.server.serve_forever) server_thread.daemon = True # Exit daemon when main thread stops server_thread.start() except (OSError, socket.error): logger.error("Could not start status socket at %s. Does the path " "exist? Do you have permission?", status_socket_location) onionbalance-0.2.2/onionbalance/hs_v2/util.py000066400000000000000000000106611411742520300211460ustar00rootroot00000000000000# -*- coding: utf-8 -*- import hashlib import struct import datetime import getpass import base64 import binascii import os # import Cryptodome.Util import Cryptodome.PublicKey def get_asn1_sequence(rsa_key): seq = Cryptodome.Util.asn1.DerSequence() seq.append(rsa_key.n) seq.append(rsa_key.e) asn1_seq = seq.encode() return asn1_seq def calc_key_digest(rsa_key): """Calculate the SHA1 digest of an RSA key""" return hashlib.sha1(get_asn1_sequence(rsa_key)).digest() def calc_permanent_id(rsa_key): return calc_key_digest(rsa_key)[:10] def calc_onion_address(rsa_key): return base64.b32encode(calc_permanent_id(rsa_key)).decode().lower() def calc_descriptor_id(permanent_id, secret_id_part): return hashlib.sha1(permanent_id + secret_id_part).digest() def get_time_period(time, permanent_id): """ time-period = (current-time + permanent-id-byte * 86400 / 256) / 86400 """ permanent_id_byte = int(struct.unpack('B', permanent_id[0:1])[0]) return int((int(time) + permanent_id_byte * 86400 / 256) / 86400) def get_seconds_valid(time, permanent_id): """ Calculate seconds until the descriptor ID changes """ permanent_id_byte = int(struct.unpack('B', permanent_id[0:1])[0]) return 86400 - int((int(time) + permanent_id_byte * 86400 / 256) % 86400) def calc_secret_id_part(time_period, descriptor_cookie, replica): """ secret-id-part = H(time-period | descriptor-cookie | replica) """ secret_id_part = hashlib.sha1() secret_id_part.update(struct.pack('>I', time_period)[:4]) if descriptor_cookie: secret_id_part.update(descriptor_cookie) secret_id_part.update(binascii.unhexlify('{0:02X}'.format(replica))) return secret_id_part.digest() def calc_descriptor_id_b32(onion_address, time, replica, deviation=0, descriptor_cookie=None): """ High level function to calculate the descriptor ID for a hidden service. The onion address and returned descriptor ID are both base32 encoded. """ permanent_id = base64.b32decode(onion_address, 1) time_period = get_time_period(time, permanent_id) + int(deviation) secret_id_part = calc_secret_id_part(time_period, descriptor_cookie, replica) descriptor_id = calc_descriptor_id(permanent_id, secret_id_part) return base64.b32encode(descriptor_id).decode('utf-8').lower() def rounded_timestamp(timestamp=None): """ Create timestamp rounded down to the nearest hour """ if not timestamp: timestamp = datetime.datetime.utcnow() timestamp = timestamp.replace(minute=0, second=0, microsecond=0) return timestamp.strftime('%Y-%m-%d %H:%M:%S') def base32_encode_str(byte_str): """ Encode bytes as lowercase base32 string """ return base64.b32encode(byte_str).lower().decode('utf-8') def key_decrypt_prompt(key_file, retries=3): """ Try open an PEM encrypted private key, prompting the user for a passphrase if required. """ key_passphrase = None with open(key_file, 'rt') as handle: pem_key = handle.read() for retries in range(0, retries): if "Proc-Type: 4,ENCRYPTED" in pem_key: # Key looks encrypted key_passphrase = getpass.getpass( "Enter the password for the private key (%s): " % key_file) try: rsa_key = Cryptodome.PublicKey.RSA.importKey( pem_key, passphrase=key_passphrase) except ValueError: # Key not decrypted correctly, prompt for passphrase again continue else: # .. todo:: Check the loaded key size in a more reasonable way. if rsa_key.has_private() and rsa_key.size_in_bits() == 1024: return rsa_key else: raise ValueError("The specified key was not a 1024 bit " "private key.") # No private key was imported raise ValueError("Could not import RSA key.") def try_make_dir(path): """ Try to create a directory (including any parent directories) """ try: os.makedirs(path) except OSError: if not os.path.isdir(path): raise def is_directory_empty(path): """ Check if a directory contains any files or directories. """ if os.listdir(path): return False else: return True onionbalance-0.2.2/onionbalance/hs_v3/000077500000000000000000000000001411742520300176145ustar00rootroot00000000000000onionbalance-0.2.2/onionbalance/hs_v3/__init__.py000066400000000000000000000000001411742520300217130ustar00rootroot00000000000000onionbalance-0.2.2/onionbalance/hs_v3/consensus.py000066400000000000000000000255551411742520300222220ustar00rootroot00000000000000import datetime import base64 import hashlib import stem import stem.util import stem.descriptor.remote from stem.descriptor.networkstatus import NetworkStatusDocumentV3 from onionbalance.common import log from onionbalance.hs_v3 import tor_node logger = log.get_logger() class Consensus(object): """ This represents a consensus object. It's initialized once in startup and refreshed during the runtime using the refresh() method to get the latest consensus. """ def __init__(self, do_refresh_consensus=True): # A list of tor_node:Node objects contained in the current consensus self.nodes = None # A stem NetworkStatusDocumentV3 object representing the current consensus self.consensus = None if not do_refresh_consensus: return # Set self.consensus to a NetworkStatusDocumentV3 object # and initialize the nodelist self.refresh() def refresh(self): """ Attempt to refresh the consensus with the latest one available. """ from onionbalance.hs_v3.onionbalance import my_onionbalance # Fetch the current md consensus from the control port md_consensus_str = my_onionbalance.controller.get_md_consensus().encode() try: self.consensus = NetworkStatusDocumentV3(md_consensus_str) except ValueError: logger.warning("No valid consensus received. Waiting for one...") return # Check if it's live if not self.is_live(): logger.info("Loaded consensus is not live. Waiting for a live one.") return self.nodes = self._initialize_nodes() def get_routerstatuses(self): """Give access to the routerstatus entries in this consensus""" # We should never be asked for routerstatuses with a non-live consensus # so make sure this is the case. assert(self.is_live()) return self.consensus.routers def is_live(self): """ Return True if the consensus is live. This function replicates the behavior of the little-t-tor networkstatus_get_reasonably_live_consensus() function. """ if not self.consensus: return False REASONABLY_LIVE_TIME = 24 * 60 * 60 now = datetime.datetime.utcnow() return now >= self.consensus.valid_after - datetime.timedelta(seconds=REASONABLY_LIVE_TIME) and \ now <= self.consensus.valid_until + datetime.timedelta(seconds=REASONABLY_LIVE_TIME) def _initialize_nodes(self): """ Initialize self.nodes with the list of current nodes. """ from onionbalance.hs_v3.onionbalance import my_onionbalance nodes = [] try: microdescriptors_list = list(my_onionbalance.controller.controller.get_microdescriptors()) except stem.DescriptorUnavailable: logger.warning("Can't get microdescriptors from Tor. Delaying...") return # Turn the mds into a dictionary indexed by the digest as an # optimization while matching them with routerstatuses. microdescriptors_dict = {} for md in microdescriptors_list: microdescriptors_dict[md.digest()] = md # Go through the routerstatuses and match them up with # microdescriptors, and create a Node object for each match. If there # is no match we don't register it as a node. for relay_fpr, relay_routerstatus in self.get_routerstatuses().items(): logger.debug("Checking routerstatus with md digest %s", relay_routerstatus.microdescriptor_digest) # Skip routerstatuses for which we cannot find a microdescriptor if relay_routerstatus.microdescriptor_digest not in microdescriptors_dict: logger.debug("Could not find microdesc for rs with fpr %s", relay_fpr) continue node_microdescriptor = microdescriptors_dict[relay_routerstatus.microdescriptor_digest] node = tor_node.Node(node_microdescriptor, relay_routerstatus) nodes.append(node) logger.debug("Initialized %d nodes (%d routerstatuses / %d microdescriptors)", len(nodes), len(self.get_routerstatuses()), len(microdescriptors_list)) return nodes def _get_disaster_srv(self, time_period_num): """ Return disaster SRV for 'time_period_num'. """ time_period_length = self.get_time_period_length() disaster_body = b"shared-random-disaster" + time_period_length.to_bytes(8, 'big') + time_period_num.to_bytes(8, 'big') return hashlib.sha3_256(disaster_body).digest() def get_current_srv(self, time_period_num): if self.consensus.shared_randomness_current_value: return base64.b64decode(self.consensus.shared_randomness_current_value) elif time_period_num: logger.info("SRV not found so falling back to disaster mode") return self._get_disaster_srv(time_period_num) else: return None def get_previous_srv(self, time_period_num): if self.consensus.shared_randomness_previous_value: return base64.b64decode(self.consensus.shared_randomness_previous_value) elif time_period_num: logger.info("SRV not found so falling back to disaster mode") return self._get_disaster_srv(time_period_num) else: return None def _get_srv_phase_duration(self): """ Return the length of the phase of a shared random protocol run in minutes. """ # Each SRV phase takes 12 rounds. But the duration of the round depends # on how big the voting rounds are which differs between live and # testing network: from onionbalance.hs_v3.onionbalance import my_onionbalance if my_onionbalance.is_testnet: return (12 * 20) // 60 else: return 12 * 60 def get_time_period_length(self): """ Get the HSv3 time period length in minutes """ from onionbalance.hs_v3.onionbalance import my_onionbalance if my_onionbalance.is_testnet: # This is a chutney network! Use hs_common.c:get_time_period_length() # logic to calculate time period length return (24 * 20) // 60 else: # This is not a chutney network, so time period length is 1440 minutes (24 hours) return 24 * 60 def get_blinding_param(self, identity_pubkey, time_period_number): """ Calculate the HSv3 blinding parameter as specified in rend-spec-v3.txt section A.2: h = H(BLIND_STRING | A | s | B | N) BLIND_STRING = "Derive temporary signing key" | INT_1(0) N = "key-blind" | INT_8(period-number) | INT_8(period_length) B = "(1511[...]2202, 4631[...]5960)" Use the time period number in 'time_period_number'. """ ED25519_BASEPOINT = b"(15112221349535400772501151409588531511" \ b"454012693041857206046113283949847762202, " \ b"463168356949264781694283940034751631413" \ b"07993866256225615783033603165251855960)" BLIND_STRING = b"Derive temporary signing key" + bytes([0]) period_length = self.get_time_period_length() N = b"key-blind" + time_period_number.to_bytes(8, 'big') + period_length.to_bytes(8, 'big') return hashlib.sha3_256(BLIND_STRING + identity_pubkey + ED25519_BASEPOINT + N).digest() def get_next_time_period_num(self, valid_after=None): return self.get_time_period_num(valid_after) + 1 def get_time_period_num(self, valid_after=None): """ Get time period number for this 'valid_after'. valid_after is a datetime (if not set, we get it ourselves) time_period_length set to default value of 1440 minutes == 1 day """ if not valid_after: assert(self.is_live()) valid_after = self.consensus.valid_after valid_after = stem.util.datetime_to_unix(valid_after) time_period_length = self.get_time_period_length() seconds_since_epoch = valid_after minutes_since_epoch = seconds_since_epoch // 60 # Calculate offset as specified in rend-spec-v3.txt [TIME-PERIODS] time_period_rotation_offset = self._get_srv_phase_duration() assert(minutes_since_epoch > time_period_rotation_offset) minutes_since_epoch -= time_period_rotation_offset time_period_num = minutes_since_epoch // time_period_length return int(time_period_num) def get_start_time_of_current_srv_run(self): """ Return the start time of the current SR protocol run using the times from the current consensus. For example, if the latest consensus valid-after is 23/06/2017 23:00:00 and a full SR protocol run is 24 hours, this function returns 23/06/2017 00:00:00. TODO: unittest """ from onionbalance.hs_v3.onionbalance import my_onionbalance assert(self.is_live()) beginning_of_current_round = stem.util.datetime_to_unix(self.consensus.valid_after) # Voting interval is 20 secs in chutney and one hour in real network if my_onionbalance.is_testnet: voting_interval_secs = 20 else: voting_interval_secs = 60 * 60 # Get current SR protocol round (an SR protocol run has 24 rounds) curr_round_slot = (beginning_of_current_round // voting_interval_secs) % 24 time_elapsed_since_start_of_run = curr_round_slot * voting_interval_secs logger.debug("Current SRV proto run: Start of current round: %s. " "Time elapsed: %s (%s)", beginning_of_current_round, time_elapsed_since_start_of_run, voting_interval_secs) return int(beginning_of_current_round - time_elapsed_since_start_of_run) def get_start_time_of_previous_srv_run(self): from onionbalance.hs_v3.onionbalance import my_onionbalance start_time_of_current_run = self.get_start_time_of_current_srv_run() if my_onionbalance.is_testnet: return start_time_of_current_run - 24 * 20 else: return start_time_of_current_run - 24 * 3600 def get_start_time_of_next_time_period(self, valid_after=None): """ Return the start time of the upcoming time period """ assert(self.is_live()) # Get start time of next time period time_period_length = self.get_time_period_length() next_time_period_num = self.get_next_time_period_num(valid_after) start_of_next_tp_in_mins = next_time_period_num * time_period_length # Apply rotation offset as specified by prop224 section [TIME-PERIODS] time_period_rotation_offset = self._get_srv_phase_duration() return (start_of_next_tp_in_mins + time_period_rotation_offset) * 60 class NoLiveConsensus(Exception): pass onionbalance-0.2.2/onionbalance/hs_v3/descriptor.py000066400000000000000000000274271411742520300223600ustar00rootroot00000000000000import datetime import hashlib import itertools import stem.util from stem.descriptor.hidden_service import HiddenServiceDescriptorV3, InnerLayer from stem.descriptor.certificate import Ed25519CertificateV1, Ed25519Extension, ExtensionType from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives import serialization from cryptography.hazmat.backends import default_backend from onionbalance.common import log from onionbalance.common import intro_point_set from onionbalance.hs_v3 import params logger = log.get_logger() backend = default_backend() class IntroductionPointSetV3(intro_point_set.IntroductionPointSet): """ This class represents a set of introduction points (which are actually stem.descriptor.hidden_service.IntroductionPointV3 objects) It gives us a nice way to compare sets of introduction poinst between them, to see if they are different. It also preserves all the functionality of onionbalance.common.intro_point_set.IntroductionPointSet which allows you to sample introduction points out of the set. """ def __init__(self, introduction_points): """ 'introduction_points' is a list of lists where each internal list contains the introduction points of an instance """ for instance_ips in introduction_points: for ip in instance_ips: if ip.legacy_key_raw: logger.info("Ignoring introduction point with legacy key.") instance_ips.remove(ip) super().__init__(introduction_points) def get_intro_points_flat(self): """ Flatten the .intro_points list of list into a single list and return it """ return list(itertools.chain(*self.intro_points)) def __eq__(self, other): """ Compares two IntroductionPointSetV3 objects and returns True if they have the same introduction points in them. """ # XXX we are currently using onion_key_raw as the identifier for the # intro point. is there a better thing to use? intro_set_1 = set(ip.onion_key_raw for ip in other.get_intro_points_flat()) intro_set_2 = set(ip.onion_key_raw for ip in self.get_intro_points_flat()) # TODO: unittests return intro_set_1 == intro_set_2 class V3Descriptor(object): """ A generic v3 descriptor. Serves as the base class for OBDescriptor and ReceivedDescriptor which implement more specific functionalities. """ def __init__(self, onion_address, v3_desc): self.onion_address = onion_address self.v3_desc = v3_desc # An IntroductionPointSetV3 object with the intros of this descriptor self.intro_set = IntroductionPointSetV3([self.v3_desc._inner_layer.introduction_points]) def get_intro_points(self): """ Get the raw intro points for this descriptor. """ return self.intro_set.get_intro_points_flat() def get_blinded_key(self): """ Extract and return the blinded key from the descriptor """ # The descriptor signing cert, signs the descriptor signing key using # the blinded key. So the signing key should be the one we want here. return self.v3_desc.signing_cert.signing_key() def get_size(self): """ Return size of v3 descriptor in bytes """ return len(str(self.v3_desc)) class OBDescriptor(V3Descriptor): """ A v3 descriptor created by Onionbalance and meant to be published to the network. This class supports generating descriptors. Can raise BadDescriptor if we can't or should not generate a valid descriptor. """ def __init__(self, onion_address, identity_priv_key, blinding_param, intro_points, is_first_desc): # Timestamp of the last attempt to assemble this descriptor self.last_publish_attempt_ts = None # Timestamp we last uploaded this descriptor self.last_upload_ts = None # Set of responsible HSDirs for last time we uploaded this descriptor self.responsible_hsdirs = None # Start generating descriptor desc_signing_key = Ed25519PrivateKey.generate() # Get the intro points for this descriptor and recertify them! recertified_intro_points = [] for ip in intro_points: recertified_intro_points.append(self._recertify_intro_point(ip, desc_signing_key)) rev_counter = self._get_revision_counter(identity_priv_key, is_first_desc) v3_desc_inner_layer = InnerLayer.create(introduction_points=recertified_intro_points) v3_desc = HiddenServiceDescriptorV3.create( blinding_nonce=blinding_param, identity_key=identity_priv_key, signing_key=desc_signing_key, inner_layer=v3_desc_inner_layer, revision_counter=int(rev_counter), ) # TODO stem should probably initialize it itself so that it has balance # between descriptor creation (where this is not inted) and descriptor # parsing (where this is inited) v3_desc._inner_layer = v3_desc_inner_layer # Check max size is within range if len(str(v3_desc)) > params.MAX_DESCRIPTOR_SIZE: logger.error("Created descriptor is too big (%d intros). Consider " "relaxing number of instances or intro points per instance " "(see N_INTROS_PER_INSTANCE)") raise BadDescriptor super().__init__(onion_address, v3_desc) def set_last_publish_attempt_ts(self, last_publish_attempt_ts): self.last_publish_attempt_ts = last_publish_attempt_ts def set_last_upload_ts(self, last_upload_ts): self.last_upload_ts = last_upload_ts def set_responsible_hsdirs(self, responsible_hsdirs): self.responsible_hsdirs = responsible_hsdirs def _recertify_intro_point(self, intro_point, descriptor_signing_key): """ Given an IntroductionPointV3 object, re-certify it using the 'descriptor_signing_key' for this new descriptor. Return the recertified intro point. """ original_auth_key_cert = intro_point.auth_key_cert original_enc_key_cert = intro_point.enc_key_cert # We have already removed all the intros with legacy keys. Make sure that # no legacy intros sneaks up on us, becausey they would result in # unparseable descriptors if we don't recertify them (and we won't). assert(not intro_point.legacy_key_cert) # Get all the certs we need to recertify # [we need to use the _replace method of namedtuples because there is no # setter for those attributes due to the way stem sets those fields. If we # attempt to normally replace the attributes we get the following # exception: AttributeError: can't set attribute] recertified_intro_point = intro_point._replace(auth_key_cert=self._recertify_ed_certificate(original_auth_key_cert, descriptor_signing_key), enc_key_cert=self._recertify_ed_certificate(original_enc_key_cert, descriptor_signing_key)) return recertified_intro_point def _recertify_ed_certificate(self, ed_cert, descriptor_signing_key): """ Recertify an HSv3 intro point certificate using the new descriptor signing key so that it can be accepted as part of a new descriptor. "Recertifying" means taking the certified key and signing it with a new key. Return the new certificate. """ # pylint: disable=no-member extensions = [Ed25519Extension(ExtensionType.HAS_SIGNING_KEY, None, stem.util._pubkey_bytes(descriptor_signing_key))] new_cert = Ed25519CertificateV1(cert_type=ed_cert.type, expiration=ed_cert.expiration, key_type=ed_cert.key_type, key=ed_cert.key, extensions=extensions, signing_key=descriptor_signing_key) return new_cert def _get_revision_counter(self, identity_priv_key, is_first_desc): """ Get the revision counter using the order-preserving-encryption scheme from rend-spec-v3.txt section F.2. """ from onionbalance.hs_v3.onionbalance import my_onionbalance now = int(stem.util.datetime_to_unix(datetime.datetime.utcnow())) # TODO: Mention that this is done with the private key instead of the blinded priv key # this means that this won't cooperate with normal tor privkey_bytes = identity_priv_key.private_bytes(encoding=serialization.Encoding.Raw, format=serialization.PrivateFormat.Raw, encryption_algorithm=serialization.NoEncryption()) cipher_key = hashlib.sha3_256(b"rev-counter-generation" + privkey_bytes).digest() if is_first_desc: srv_start = my_onionbalance.consensus.get_start_time_of_previous_srv_run() else: srv_start = my_onionbalance.consensus.get_start_time_of_current_srv_run() srv_start = int(srv_start) seconds_since_srv_start = now - srv_start # This must be strictly positive seconds_since_srv_start += 1 ope_result = sum(w for w, _ in zip(self._get_ope_scheme_words(cipher_key), range(seconds_since_srv_start))) logger.debug("Rev counter for %s descriptor (SRV secs %s, OPE %s)", "first" if is_first_desc else "second", seconds_since_srv_start, ope_result) return ope_result def _get_ope_scheme_words(self, cipher_key): IV = b'\x00' * 16 cipher = Cipher(algorithms.AES(cipher_key), modes.CTR(IV), backend=backend) e = cipher.encryptor() while True: v = e.update(b'\x00\x00') yield v[0] + 256 * v[1] + 1 class ReceivedDescriptor(V3Descriptor): """ An instance v3 descriptor received from the network. This class supports parsing descriptors. """ def __init__(self, desc_text, onion_address): """ Parse a descriptor in 'desc_text' and return an ReceivedDescriptor object. Raises BadDescriptor if the descriptor cannot be used. """ try: v3_desc = HiddenServiceDescriptorV3.from_str(desc_text) v3_desc.decrypt(onion_address) except ValueError as err: logger.warning("Descriptor is corrupted (%s).", err) raise BadDescriptor self.received_ts = datetime.datetime.utcnow() logger.debug("Successfuly decrypted descriptor for %s!", onion_address) super().__init__(onion_address, v3_desc) def is_old(self): """ Return True if this received descriptor is old and we should consider the instance as offline. """ from onionbalance.hs_v3.onionbalance import my_onionbalance received_age = datetime.datetime.utcnow() - self.received_ts received_age = received_age.total_seconds() if my_onionbalance.is_testnet: too_old_threshold = params.INSTANCE_DESCRIPTOR_TOO_OLD_TESTNET else: too_old_threshold = params.INSTANCE_DESCRIPTOR_TOO_OLD if received_age > too_old_threshold: return True return False class BadDescriptor(Exception): pass onionbalance-0.2.2/onionbalance/hs_v3/ext/000077500000000000000000000000001411742520300204145ustar00rootroot00000000000000onionbalance-0.2.2/onionbalance/hs_v3/ext/__init__.py000066400000000000000000000000001411742520300225130ustar00rootroot00000000000000onionbalance-0.2.2/onionbalance/hs_v3/ext/ed25519_exts_ref.py000066400000000000000000000174631411742520300236760ustar00rootroot00000000000000#!/usr/bin/python # Copyright 2014-2019, The Tor Project, Inc # See LICENSE for licensing information """ Reference implementations for the ed25519 tweaks that Tor uses. Includes self-tester and test vector generator. """ from . import slow_ed25519 from .slow_ed25519 import * import os import random import unittest import binascii import textwrap #define a synonym that doesn't look like 1 ell = l # This replaces expmod above and makes it go a lot faster. slow_ed25519.expmod = pow def blindESK(esk, param): mult = 2**(b-2) + sum(2**i * bit(param,i) for i in range(3,b-2)) s = decodeint(esk[:32]) s_prime = (s * mult) % ell k = esk[32:] assert(len(k) == 32) k_prime = H(b"Derive temporary signing key hash input" + k)[:32] return encodeint(s_prime) + k_prime def blindPK(pk, param): mult = 2**(b-2) + sum(2**i * bit(param,i) for i in range(3,b-2)) P = decodepoint(pk) return encodepoint(scalarmult(P, mult)) def expandSK(sk): h = H(sk) a = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2)) k = b''.join([bytes([h[i]]) for i in range(b//8,b//4)]) assert len(k) == 32 return encodeint(a)+k def publickeyFromESK(h): a = decodeint(h[:32]) A = scalarmult(B,a) return encodepoint(A) def signatureWithESK(m,h,pk): a = decodeint(h[:32]) r = Hint(b''.join([bytes([h[i]]) for i in range(b//8,b//4)]) + m) R = scalarmult(B,r) S = (r + Hint(encodepoint(R) + pk + m) * a) % l return encodepoint(R) + encodeint(S) def newSK(): return os.urandom(32) def random_scalar(entropy_f): # 0..L-1 inclusive # reduce the bias to a safe level by generating 256 extra bits oversized = int(binascii.hexlify(entropy_f(32+32)), 16) return oversized % ell # ------------------------------------------------------------ MSG = b"This is extremely silly. But it is also incredibly serious business!" class SelfTest(unittest.TestCase): def _testSignatures(self, esk, pk): sig = signatureWithESK(MSG, esk, pk) checkvalid(sig, MSG, pk) bad = False try: checkvalid(sig, MSG*2, pk) bad = True except Exception: pass self.assertFalse(bad) def testExpand(self): sk = newSK() pk = publickey(sk) esk = expandSK(sk) sig1 = signature(MSG, sk, pk) sig2 = signatureWithESK(MSG, esk, pk) self.assertEqual(sig1, sig2) def testSignatures(self): sk = newSK() esk = expandSK(sk) pk = publickeyFromESK(esk) pk2 = publickey(sk) self.assertEqual(pk, pk2) self._testSignatures(esk, pk) def testBlinding(self): sk = newSK() esk = expandSK(sk) pk = publickeyFromESK(esk) param = os.urandom(32) besk = blindESK(esk, param) bpk = blindPK(pk, param) bpk2 = publickeyFromESK(besk) self.assertEqual(bpk, bpk2) self._testSignatures(besk, bpk) def testIdentity(self): # Base point: # B is the unique point (x, 4/5) \in E for which x is positive By = 4 * inv(5) Bx = xrecover(By) B = [Bx % q,By % q] # Get identity E by doing: E = l*B, where l is the group order identity = scalarmult(B, ell) # Get identity E by doing: E = l*A, where A is a random point sk = newSK() pk = decodepoint(publickey(sk)) identity2 = scalarmult(pk, ell) # Check that identities match assert(identity == identity2) # Check that identity is the point (0,1) assert(identity == [0,1]) # Check identity element: a*E = E, where a is a random scalar scalar = random_scalar(os.urandom) result = scalarmult(identity, scalar) assert(result == identity == identity2) # ------------------------------------------------------------ # From pprint.pprint([ binascii.b2a_hex(os.urandom(32)) for _ in xrange(8) ]) RAND_INPUTS = [ '26c76712d89d906e6672dafa614c42e5cb1caac8c6568e4d2493087db51f0d36', 'fba7a5366b5cb98c2667a18783f5cf8f4f8d1a2ce939ad22a6e685edde85128d', '67e3aa7a14fac8445d15e45e38a523481a69ae35513c9e4143eb1c2196729a0e', 'd51385942033a76dc17f089a59e6a5a7fe80d9c526ae8ddd8c3a506b99d3d0a6', '5c8eac469bb3f1b85bc7cd893f52dc42a9ab66f1b02b5ce6a68e9b175d3bb433', 'eda433d483059b6d1ff8b7cfbd0fe406bfb23722c8f3c8252629284573b61b86', '4377c40431c30883c5fbd9bc92ae48d1ed8a47b81d13806beac5351739b5533d', 'c6bbcce615839756aed2cc78b1de13884dd3618f48367a17597a16c1cd7a290b'] # From pprint.pprint([ binascii.b2a_hex(os.urandom(32)) for _ in xrange(8) ]) BLINDING_PARAMS = [ '54a513898b471d1d448a2f3c55c1de2c0ef718c447b04497eeb999ed32027823', '831e9b5325b5d31b7ae6197e9c7a7baf2ec361e08248bce055908971047a2347', 'ac78a1d46faf3bfbbdc5af5f053dc6dc9023ed78236bec1760dadfd0b2603760', 'f9c84dc0ac31571507993df94da1b3d28684a12ad14e67d0a068aba5c53019fc', 'b1fe79d1dec9bc108df69f6612c72812755751f21ecc5af99663b30be8b9081f', '81f1512b63ab5fb5c1711a4ec83d379c420574aedffa8c3368e1c3989a3a0084', '97f45142597c473a4b0e9a12d64561133ad9e1155fe5a9807fe6af8a93557818', '3f44f6a5a92cde816635dfc12ade70539871078d2ff097278be2a555c9859cd0'] PREFIX = "ED25519_" def writeArray(name, array): print("static const char *{prefix}{name}[] = {{".format( prefix=PREFIX,name=name)) for a in array: h = a.hex() if len(h) > 70: h1 = h[:70] h2 = h[70:] print(' "{0}"\n "{1}",'.format(h1,h2)) else: print(' "{0}",'.format(h)) print("};\n") def comment(text, initial="/**"): print(initial) print(textwrap.fill(text,initial_indent=" * ",subsequent_indent=" * ")) print(" */") def makeTestVectors(): comment("""Test vectors for our ed25519 implementation and related functions. These were automatically generated by the ed25519_exts_ref.py script.""", initial="/*") comment("""Secret key seeds used as inputs for the ed25519 test vectors. Randomly generated. """) secretKeys = [ bytes.fromhex(r) for r in RAND_INPUTS ] writeArray("SECRET_KEYS", secretKeys) comment("""Secret ed25519 keys after expansion from seeds. This is how Tor represents them internally.""") expandedSecretKeys = [ expandSK(sk) for sk in secretKeys ] writeArray("EXPANDED_SECRET_KEYS", expandedSecretKeys) comment("""Public keys derived from the above secret keys""") publicKeys = [ publickey(sk) for sk in secretKeys ] writeArray("PUBLIC_KEYS", publicKeys) comment("""Parameters used for key blinding tests. Randomly generated.""") blindingParams = [ binascii.a2b_hex(r) for r in BLINDING_PARAMS ] writeArray("BLINDING_PARAMS", blindingParams) comment("""Blinded secret keys for testing key blinding. The nth blinded key corresponds to the nth secret key blidned with the nth blinding parameter.""") writeArray("BLINDED_SECRET_KEYS", (blindESK(expandSK(sk), bp) for sk,bp in zip(secretKeys,blindingParams))) comment("""Blinded public keys for testing key blinding. The nth blinded key corresponds to the nth public key blidned with the nth blinding parameter.""") writeArray("BLINDED_PUBLIC_KEYS", (blindPK(pk, bp) for pk,bp in zip(publicKeys,blindingParams))) comment("""Signatures of the public keys, made with their corresponding secret keys.""") writeArray("SELF_SIGNATURES", (signature(pk, sk, pk) for pk,sk in zip(publicKeys,secretKeys))) if __name__ == '__main__': import sys if len(sys.argv) == 1 or sys.argv[1] not in ("SelfTest", "MakeVectors"): print ("You should specify one of 'SelfTest' or 'MakeVectors'") sys.exit(1) if sys.argv[1] == 'SelfTest': unittest.main() else: makeTestVectors() onionbalance-0.2.2/onionbalance/hs_v3/ext/slow_ed25519.py000066400000000000000000000054541411742520300230400ustar00rootroot00000000000000# This is the ed25519 implementation from # http://ed25519.cr.yp.to/python/ed25519.py . # It is in the public domain. # # It isn't constant-time. Don't use it except for testing. Also, see # warnings about how very slow it is. Only use this for generating # test vectors, I'd suggest. # # Don't edit this file. Mess with ed25519_ref.py import hashlib b = 256 q = 2**255 - 19 l = 2**252 + 27742317777372353535851937790883648493 def H(m): return hashlib.sha512(m).digest() def expmod(b,e,m): if e == 0: return 1 t = expmod(b,e//2,m)**2 % m if e & 1: t = (t*b) % m return t def inv(x): return expmod(x,q-2,q) d = -121665 * inv(121666) I = expmod(2,(q-1)//4,q) def xrecover(y): xx = (y*y-1) * inv(d*y*y+1) x = expmod(xx,(q+3)//8,q) if (x*x - xx) % q != 0: x = (x*I) % q if x % 2 != 0: x = q-x return x By = 4 * inv(5) Bx = xrecover(By) B = [Bx % q,By % q] def edwards(P,Q): x1 = P[0] y1 = P[1] x2 = Q[0] y2 = Q[1] x3 = (x1*y2+x2*y1) * inv(1+d*x1*x2*y1*y2) y3 = (y1*y2+x1*x2) * inv(1-d*x1*x2*y1*y2) return [x3 % q,y3 % q] def scalarmult(P,e): if e == 0: return [0,1] Q = scalarmult(P,e//2) Q = edwards(Q,Q) if e & 1: Q = edwards(Q,P) return Q def encodeint(y): bits = [(y >> i) & 1 for i in range(b)] return b''.join([bytes([sum([bits[i * 8 + j] << j for j in range(8)])]) for i in range(b//8)]) def encodepoint(P): x = P[0] y = P[1] bits = [(y >> i) & 1 for i in range(b - 1)] + [x & 1] return b''.join([bytes([sum([bits[i * 8 + j] << j for j in range(8)])]) for i in range(b//8)]) def bit(h,i): return (h[i//8] >> (i%8)) & 1 def publickey(sk): h = H(sk) a = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2)) A = scalarmult(B,a) return encodepoint(A) def Hint(m): h = H(m) return sum(2**i * bit(h,i) for i in range(2*b)) def signature(m,sk,pk): h = H(sk) a = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2)) r = Hint(b''.join([bytes([h[i]]) for i in range(b//8,b//4)]) + m) R = scalarmult(B,r) S = (r + Hint(encodepoint(R) + pk + m) * a) % l return encodepoint(R) + encodeint(S) def isoncurve(P): x = P[0] y = P[1] return (-x*x + y*y - 1 - d*x*x*y*y) % q == 0 def decodeint(s): return sum(2**i * bit(s,i) for i in range(0,b)) def decodepoint(s): y = sum(2**i * bit(s,i) for i in range(0,b-1)) x = xrecover(y) if x & 1 != bit(s,b-1): x = q-x P = [x,y] if not isoncurve(P): raise Exception("decoding point that is not on curve") return P def checkvalid(s,m,pk): if len(s) != b//4: raise Exception("signature length is wrong") if len(pk) != b//8: raise Exception("public-key length is wrong") R = decodepoint(s[0:b//8]) A = decodepoint(pk) S = decodeint(s[b//8:b//4]) h = Hint(encodepoint(R) + pk + m) if scalarmult(B,S) != edwards(R,scalarmult(A,h)): raise Exception("signature does not pass verification") onionbalance-0.2.2/onionbalance/hs_v3/hashring.py000066400000000000000000000211131411742520300217670ustar00rootroot00000000000000import base64 import bisect import hashlib import stem.util from onionbalance.common import log from onionbalance.hs_v3 import tor_node from onionbalance.hs_v3 import params logger = log.get_logger() def _time_between_tp_and_srv(valid_after): """ Return True if we are currently in the time segment between a new time period and a new SRV (in the real network that happens between 12:00 and 00:00 UTC). Here is a diagram showing exactly when this returns true: +------------------------------------------------------------------+ | | | 00:00 12:00 00:00 12:00 00:00 12:00 | | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 | | | | $==========|-----------$===========|-----------$===========| | | ^^^^^^^^^^^^ ^^^^^^^^^^^^ | | | +------------------------------------------------------------------+ """ from onionbalance.hs_v3.onionbalance import my_onionbalance srv_start_time = my_onionbalance.consensus.get_start_time_of_current_srv_run() tp_start_time = my_onionbalance.consensus.get_start_time_of_next_time_period(srv_start_time) valid_after = stem.util.datetime_to_unix(valid_after) if valid_after >= srv_start_time and valid_after < tp_start_time: logger.debug("We are between SRV and TP") return False logger.debug("We are between TP and SRV (valid_after: %s, srv_start_time: %s -> tp_start_time: %s)", valid_after, srv_start_time, tp_start_time) return True def get_srv_and_time_period(is_first_descriptor): """ Return SRV and time period based on current consensus time """ from onionbalance.hs_v3.onionbalance import my_onionbalance valid_after = my_onionbalance.consensus.consensus.valid_after current_tp = my_onionbalance.consensus.get_time_period_num() previous_tp = current_tp - 1 next_tp = current_tp + 1 assert(previous_tp > 0) # Get the right TP/SRV if is_first_descriptor: if _time_between_tp_and_srv(valid_after): srv = my_onionbalance.consensus.get_previous_srv(previous_tp) tp = previous_tp _case = 1 # just for debugging else: srv = my_onionbalance.consensus.get_previous_srv(current_tp) tp = current_tp _case = 2 # just for debugging else: if _time_between_tp_and_srv(valid_after): srv = my_onionbalance.consensus.get_current_srv(current_tp) tp = current_tp _case = 3 # just for debugging else: srv = my_onionbalance.consensus.get_current_srv(next_tp) tp = next_tp _case = 4 # just for debugging srv_b64 = base64.b64encode(srv) if srv else None logger.debug("For valid_after %s we got SRV %s and TP %s (case: #%d)", valid_after, srv_b64, tp, _case) return srv, tp def _get_hash_ring_for_descriptor(is_first_descriptor): """ Return a dictionary { : Node , .... } """ from onionbalance.hs_v3.onionbalance import my_onionbalance node_hash_ring = {} srv, time_period_num = get_srv_and_time_period(is_first_descriptor) logger.info("Using srv %s and TP#%s (%s descriptor)", srv.hex(), time_period_num, "first" if is_first_descriptor else "second") for node in my_onionbalance.consensus.nodes: try: hsdir_index = node.get_hsdir_index(srv, time_period_num) except (tor_node.NoEd25519Identity, tor_node.NoHSDir) as e: logger.debug("Could not find ed25519 for node %s (%s)", node.routerstatus.fingerprint, e) continue logger.debug("%s: Node: %s, index: %s", is_first_descriptor, node.get_hex_fingerprint(), hsdir_index.hex()) node_hash_ring[hsdir_index] = node return node_hash_ring def _get_hidden_service_index(blinded_pubkey, replica_num, is_first_descriptor): """ hs_index(replicanum) = H("store-at-idx" | blinded_public_key | INT_8(replicanum) | INT_8(period_length) | INT_8(period_num) ) """ from onionbalance.hs_v3.onionbalance import my_onionbalance period_length = my_onionbalance.consensus.get_time_period_length() replica_num_int_8 = replica_num.to_bytes(8, 'big') period_length_int_8 = (period_length).to_bytes(8, 'big') _, time_period_num = get_srv_and_time_period(is_first_descriptor) logger.info("Getting HS index with TP#%s for %s descriptor (%d replica) ", time_period_num, "first" if is_first_descriptor else "second", replica_num) period_num_int_8 = time_period_num.to_bytes(8, 'big') hash_body = b"%s%s%s%s%s" % (b"store-at-idx", blinded_pubkey, replica_num_int_8, period_length_int_8, period_num_int_8) hs_index = hashlib.sha3_256(hash_body).digest() return hs_index def get_responsible_hsdirs(blinded_pubkey, is_first_descriptor): """ Return a list with the responsible HSDirs for a service with 'blinded_pubkey'. The returned list is a list of fingerprints. """ from onionbalance.hs_v3.onionbalance import my_onionbalance # Always use a live consensus when calculating responsible HSDirs assert(my_onionbalance.consensus.is_live()) responsible_hsdirs = [] # TODO: Improve representation of hash ring here... No need to go # between list and dictionary... # dictionary { : Node , .... } node_hash_ring = _get_hash_ring_for_descriptor(is_first_descriptor) if not node_hash_ring: raise EmptyHashRing sorted_hash_ring_list = sorted(list(node_hash_ring.keys())) logger.info("Initialized hash ring of size %d (blinded key: %s)", len(node_hash_ring), base64.b64encode(blinded_pubkey)) for replica_num in range(1, params.HSDIR_N_REPLICAS + 1): # The HSDirs that we are gonna store this replica in replica_store_hsdirs = [] hidden_service_index = _get_hidden_service_index(blinded_pubkey, replica_num, is_first_descriptor) # Find position of descriptor ID in the HSDir list index = bisect.bisect_left(sorted_hash_ring_list, hidden_service_index) logger.info("\t Tried with HS index %s got position %d", hidden_service_index.hex(), index) while len(replica_store_hsdirs) < params.HSDIR_SPREAD_STORE: try: hsdir_key = sorted_hash_ring_list[index] index += 1 except IndexError: # Wrap around when we reach the end of the HSDir list index = 0 hsdir_key = sorted_hash_ring_list[index] hsdir_node = node_hash_ring[hsdir_key] # Check if we have already added this node to this # replica. This should never happen on the real network but # might happen in small testnets like chutney! if hsdir_node.get_hex_fingerprint() in replica_store_hsdirs: logger.debug("Ignoring already added HSDir to this replica!") break # Check if we have already added this node to the responsible # HSDirs. This can happen in the second replica and we should # skip the node if hsdir_node.get_hex_fingerprint() in responsible_hsdirs: logger.debug("Ignoring already added HSDir!") continue logger.debug("%d: %s: %s", index, hsdir_node.get_hex_fingerprint(), hsdir_key.hex()) replica_store_hsdirs.append(hsdir_node.get_hex_fingerprint()) responsible_hsdirs.extend(replica_store_hsdirs) # Do a sanity check if my_onionbalance.is_testnet: # If we are on chutney it's normal to not have enough nodes to populate the hashring assert(len(responsible_hsdirs) <= params.HSDIR_N_REPLICAS * params.HSDIR_SPREAD_STORE) else: if (len(responsible_hsdirs) != params.HSDIR_N_REPLICAS * params.HSDIR_SPREAD_STORE): logger.critical("Got the wrong number of responsible HSDirs: %d. Aborting", len(responsible_hsdirs)) raise EmptyHashRing return responsible_hsdirs class EmptyHashRing(Exception): pass onionbalance-0.2.2/onionbalance/hs_v3/instance.py000066400000000000000000000073261411742520300220020ustar00rootroot00000000000000import datetime import onionbalance.common.instance from onionbalance.common import log from onionbalance.hs_v3 import descriptor as ob_descriptor logger = log.get_logger() class InstanceV3(onionbalance.common.instance.Instance): """ This is a V3 onionbalance instance """ def __init__(self, onion_address): # Get the controller from onionbalance.hs_v3.onionbalance import my_onionbalance controller = my_onionbalance.controller.controller # Initialize the common Instance class. super().__init__(controller, onion_address) # Onion address does not contain the '.onion'. logger.warning("Loaded instance %s", onion_address) self.descriptor = None # When was the intro set of this instance last modified? self.intro_set_modified_timestamp = None def has_onion_address(self, onion_address): """ Return True if this instance has this onion address """ # Strip the ".onion" part of the address if it exists since some # subsystems don't use it (e.g. Tor sometimes omits it from control # port responses) my_onion_address = self.onion_address.replace(".onion", "") their_onion_address = onion_address.replace(".onion", "") return my_onion_address == their_onion_address def register_descriptor(self, descriptor_text, onion_address): """ We received a descriptor (with 'descriptor_text') for 'onion_address'. Register it to this instance. """ logger.info("Found instance %s for this new descriptor!", self.onion_address) assert(onion_address == self.onion_address) # Parse descriptor. If it parsed correctly, we know that this # descriptor is truly for this instance (since the onion address # matches) try: new_descriptor = ob_descriptor.ReceivedDescriptor(descriptor_text, onion_address) except ob_descriptor.BadDescriptor: logger.warning("Received bad descriptor for %s. Ignoring.", self.onion_address) return # Before replacing the current descriptor with this one, check if the # introduction point set changed: # If this is the first descriptor for this instance, the intro point set changed if not self.descriptor: logger.info("This is the first time we see a descriptor for instance %s!", self.onion_address) self.intro_set_modified_timestamp = datetime.datetime.utcnow() self.descriptor = new_descriptor return assert(self.descriptor) assert(new_descriptor.intro_set) # We already have a descriptor but this is a new one. Check the intro points! if new_descriptor.intro_set != self.descriptor.intro_set: logger.info("We got a new descriptor for instance %s and the intro set changed!", self.onion_address) self.intro_set_modified_timestamp = datetime.datetime.utcnow() else: logger.info("We got a new descriptor for instance %s but the intro set did not change.", self.onion_address) self.descriptor = new_descriptor def get_intros_for_publish(self): """ Get a list of stem.descriptor.IntroductionPointV3 objects for this descriptor Raise :InstanceHasNoDescriptor: if there is no descriptor for this instance Raise :InstanceIsOffline: if the instance is offline. """ if not self.descriptor: raise InstanceHasNoDescriptor if self.descriptor.is_old(): raise InstanceIsOffline return self.descriptor.get_intro_points() class InstanceHasNoDescriptor(Exception): pass class InstanceIsOffline(Exception): pass onionbalance-0.2.2/onionbalance/hs_v3/manager.py000066400000000000000000000044251411742520300216050ustar00rootroot00000000000000import logging import os import sys from onionbalance.common import scheduler from onionbalance.common import log from onionbalance.common import signalhandler from onionbalance.hs_v3 import params from onionbalance.hs_v3 import onionbalance logger = log.get_logger() def status_socket_location(config_data): location = os.environ.get('ONIONBALANCE_STATUS_SOCKET_LOCATION', '') if location == '': location = config_data.get('status-socket-location') return location def main(args): """ This is the entry point of v3 functionality. Initialize onionbalance, schedule future jobs and let the scheduler do its thing. """ # Override the log level if specified on the command line. if args.verbosity: params.DEFAULT_LOG_LEVEL = args.verbosity.upper() logger.setLevel(logging.__dict__[params.DEFAULT_LOG_LEVEL.upper()]) # Get the global onionbalance singleton my_onionbalance = onionbalance.my_onionbalance try: my_onionbalance.init_subsystems(args) except onionbalance.ConfigError as err: logger.error("%s", err) sys.exit(1) from onionbalance.hs_v3 import status status_socket = None if status_socket_location(my_onionbalance.config_data) is not None: status_socket = status.StatusSocket(status_socket_location(my_onionbalance.config_data), my_onionbalance) signalhandler.SignalHandler('v3', my_onionbalance.controller.controller, status_socket) # Schedule descriptor fetch and upload events init_scheduler(my_onionbalance) # Begin main loop to poll for HS descriptors scheduler.run_forever() return 0 def init_scheduler(my_onionbalance): scheduler.jobs = [] if my_onionbalance.is_testnet: scheduler.add_job(params.FETCH_DESCRIPTOR_FREQUENCY_TESTNET, my_onionbalance.fetch_instance_descriptors) scheduler.add_job(params.PUBLISH_DESCRIPTOR_CHECK_FREQUENCY_TESTNET, my_onionbalance.publish_all_descriptors) else: scheduler.add_job(params.FETCH_DESCRIPTOR_FREQUENCY, my_onionbalance.fetch_instance_descriptors) scheduler.add_job(params.PUBLISH_DESCRIPTOR_CHECK_FREQUENCY, my_onionbalance.publish_all_descriptors) # Run initial fetch of HS instance descriptors scheduler.run_all(delay_seconds=params.INITIAL_CALLBACK_DELAY) onionbalance-0.2.2/onionbalance/hs_v3/onionbalance.py000066400000000000000000000222361411742520300226230ustar00rootroot00000000000000import os import sys import stem from stem.descriptor.hidden_service import HiddenServiceDescriptorV3 import onionbalance.common.instance from onionbalance.common import log from onionbalance.common import util from onionbalance.hs_v3 import manager from onionbalance.hs_v3 import stem_controller from onionbalance.hs_v3 import service as ob_service from onionbalance.hs_v3 import consensus as ob_consensus logger = log.get_logger() class Onionbalance(object): """ Onionbalance singleton that represents this onionbalance runtime. Contains various objects that are useful to other onionbalance modules so this is imported from all over the codebase. """ def __init__(self): # This is kept minimal so that it's quick (it's executed at program # launch because of the onionbalance singleton). The actual init work # happens in init_subsystems() # True if this onionbalance operates in a testnet (e.g. chutney) self.is_testnet = False def init_subsystems(self, args): """ Initialize subsystems (this is resource intensive) """ self.args = args self.config_path = os.path.abspath(self.args.config) self.config_data = self.load_config_file() self.is_testnet = args.is_testnet if self.is_testnet: logger.warning("Onionbalance configured on a testnet!") # Create stem controller and connect to the Tor network self.controller = stem_controller.StemController(address=args.ip, port=args.port, socket=args.socket) self.consensus = ob_consensus.Consensus() # Initialize our service self.services = self.initialize_services_from_config_data() # Catch interesting events (like receiving descriptors etc.) self.controller.add_event_listeners() logger.warning("Onionbalance initialized (stem version: %s) (tor version: %s)!", stem.__version__, self.controller.controller.get_version()) logger.warning("=" * 80) def initialize_services_from_config_data(self): services = [] try: for service in self.config_data['services']: services.append(ob_service.OnionbalanceService(service, self.config_path)) except ob_service.BadServiceInit: sys.exit(1) return services def load_config_file(self): config_data = util.read_config_data_from_file(self.config_path) logger.debug("Onionbalance config data: %s", config_data) # Do some basic validation if "services" not in config_data: raise ConfigError("Config file is bad. 'services' is missing. Did you make it with onionbalance-config?") # More validation for service in config_data["services"]: if "key" not in service: raise ConfigError("Config file is bad. 'key' is missing. Did you make it with onionbalance-config?") if "instances" not in service: raise ConfigError("Config file is bad. 'instances' is missing. Did you make it with " "onionbalance-config?") if not service["instances"]: raise ConfigError("Config file is bad. No backend instances are set. Onionbalance needs at least 1.") for instance in service["instances"]: if "address" not in instance: raise ConfigError("Config file is wrong. 'address' missing from instance.") if not instance["address"]: raise ConfigError("Config file is bad. Address field is not set.") # Validate that the onion address is legit try: _ = HiddenServiceDescriptorV3.identity_key_from_address(instance["address"]) except ValueError: raise ConfigError("Cannot load instance with address: '{}'. If you are trying to run onionbalance " "for v2 onions, please use the --hs-version=v2 switch".format(instance["address"])) return config_data def fetch_instance_descriptors(self): logger.info("[*] fetch_instance_descriptors() called [*]") # TODO: Don't do this here. Instead do it on a specialized function self.controller.mark_tor_as_active() if not self.consensus.is_live(): logger.warning("No live consensus. Waiting before fetching descriptors...") return all_instances = self._get_all_instances() onionbalance.common.instance.helper_fetch_all_instance_descriptors(self.controller.controller, all_instances) def handle_new_desc_content_event(self, desc_content_event): """ Parse HS_DESC_CONTENT response events for descriptor content Update the HS instance object with the data from the new descriptor. """ onion_address = desc_content_event.address logger.debug("Received descriptor for %s.onion from %s", onion_address, desc_content_event.directory) # Check that the HSDir returned a descriptor that is not empty descriptor_text = str(desc_content_event.descriptor).encode('utf-8') # HSDirs provide a HS_DESC_CONTENT response with either one or two # CRLF lines when they do not have a matching descriptor. Using # len() < 5 should ensure all empty HS_DESC_CONTENT events are matched. if len(descriptor_text) < 5: logger.debug("Empty descriptor received for %s.onion", onion_address) return None # OK this descriptor seems plausible: Find the instances that this # descriptor belongs to: for instance in self._get_all_instances(): if instance.onion_address == onion_address: instance.register_descriptor(descriptor_text, onion_address) def publish_all_descriptors(self): """ For each service attempt to publish all descriptors """ logger.info("[*] publish_all_descriptors() called [*]") if not self.consensus.is_live(): logger.info("No live consensus. Waiting before publishing descriptors...") return for service in self.services: service.publish_descriptors() def _get_all_instances(self): """ Get all instances for all services """ instances = [] for service in self.services: instances.extend(service.instances) return instances def handle_new_status_event(self, status_event): """ Parse Tor status events such as "STATUS_GENERAL" """ # pylint: disable=no-member if status_event.action == "CONSENSUS_ARRIVED": logger.info("Received new consensus!") self.consensus.refresh() # Call all callbacks in case we just got a live consensus my_onionbalance.publish_all_descriptors() my_onionbalance.fetch_instance_descriptors() def _address_is_instance(self, onion_address): """ Return True if 'onion_address' is one of our instances. """ for service in self.services: for instance in service.instances: if instance.has_onion_address(onion_address): return True return False def _address_is_frontend(self, onion_address): for service in self.services: if service.has_onion_address(onion_address): return True return False def handle_new_desc_event(self, desc_event): """ Parse HS_DESC response events """ action = desc_event.action if action == "RECEIVED": pass # We already log in HS_DESC_CONTENT so no need to do it here too elif action == "UPLOADED": logger.debug("Successfully uploaded descriptor for %s to %s", desc_event.address, desc_event.directory) elif action == "FAILED": if self._address_is_instance(desc_event.address): logger.info("Descriptor fetch failed for instance %s from %s (%s)", desc_event.address, desc_event.directory, desc_event.reason) elif self._address_is_frontend(desc_event.address): logger.warning("Descriptor upload failed for frontend %s to %s (%s)", desc_event.address, desc_event.directory, desc_event.reason) else: logger.warning("Descriptor action failed for unknown service %s to %s (%s)", desc_event.address, desc_event.directory, desc_event.reason) elif action == "REQUESTED": logger.debug("Requested descriptor for %s from %s...", desc_event.address, desc_event.directory) else: pass def reload_config(self): """ Reload configuration and reset job scheduler """ try: self.init_subsystems(self.args) manager.init_scheduler(self) except ConfigError as err: logger.error("%s", err) sys.exit(1) class ConfigError(Exception): pass my_onionbalance = Onionbalance() onionbalance-0.2.2/onionbalance/hs_v3/params.py000066400000000000000000000033511411742520300214530ustar00rootroot00000000000000import os # Parameters definining Onionbalance behavior # How long to wait for onionbalance to bootstrap before starting periodic # events (in seconds) INITIAL_CALLBACK_DELAY = 45 # Every how often we should be fetching instance descriptors (in seconds) FETCH_DESCRIPTOR_FREQUENCY = 10 * 60 FETCH_DESCRIPTOR_FREQUENCY_TESTNET = 20 # Every how often we should be checking whether we should publish our frontend # descriptor (in seconds). Triggering this callback doesn't mean we will # actually upload a descriptor. We only upload a descriptor if it has expired, # the intro points have changed, etc. PUBLISH_DESCRIPTOR_CHECK_FREQUENCY = 5 * 60 PUBLISH_DESCRIPTOR_CHECK_FREQUENCY_TESTNET = 10 # How long should we keep a frontend descriptor before we expire it (in # seconds)? FRONTEND_DESCRIPTOR_LIFETIME = 60 * 60 FRONTEND_DESCRIPTOR_LIFETIME_TESTNET = 20 # How many intros should we use from each instance in the final frontend # descriptor? # [TODO: This makes no attempt to hide the use of onionbalance. In the future we # should be smarter and sneakier here.] N_INTROS_PER_INSTANCE = 2 # If we last received a descriptor for this instance more than # INSTANCE_DESCRIPTOR_TOO_OLD seconds ago, consider the instance to be down. INSTANCE_DESCRIPTOR_TOO_OLD = 60 * 60 INSTANCE_DESCRIPTOR_TOO_OLD_TESTNET = 20 # Parameters defined by HSv3 spec and little-t-tor implementation # Number of replicas per descriptor HSDIR_N_REPLICAS = 2 # How many uploads per replica # [TODO: Get these from the consensus instead of hardcoded] HSDIR_SPREAD_STORE = 4 # Max descriptor size (in bytes) (see hs_cache_get_max_descriptor_size() in # little-t-tor) MAX_DESCRIPTOR_SIZE = 50000 # Misc parameters DEFAULT_LOG_LEVEL = os.environ.get('ONIONBALANCE_LOG_LEVEL', 'warning') onionbalance-0.2.2/onionbalance/hs_v3/service.py000066400000000000000000000403361411742520300216340ustar00rootroot00000000000000import datetime import os from cryptography.hazmat.primitives import serialization from cryptography.hazmat.backends import default_backend import stem import stem.descriptor.hidden_service from stem.descriptor.hidden_service import HiddenServiceDescriptorV3 import onionbalance.common.descriptor from onionbalance.common import log import onionbalance.common.util import onionbalance.hs_v3.instance from onionbalance.hs_v3 import params from onionbalance.hs_v3 import hashring from onionbalance.hs_v3 import descriptor from onionbalance.hs_v3 import tor_ed25519 logger = log.get_logger() class OnionbalanceService(object): """ Service represents a front-facing hidden service which should be load-balanced. """ def __init__(self, service_config_data, config_path): """ With 'config_data' straight out of the config file, create the service and its instances. 'config_path' is the full path to the config file. Raise ValueError if the config file is not well formatted """ # Is our private key in Tor's extended key format? self.is_priv_key_in_tor_format = False # Load private key and onion address from config # (the onion_address also includes the ".onion") self.identity_priv_key, self.onion_address = self._load_service_keys(service_config_data, config_path) # XXX This is an epic hack! If we are using keys in tor's extended # format, we basically override stem's function for signing with # blinded keys because it assumes that its keys are in standard # non-extended format. To avoid a double key extension we use our own # function... This will prove to be a problem if we ever move to # multiple services per onionbalance, or if stem changes its code # behind our backs. if self.is_priv_key_in_tor_format: stem.descriptor.hidden_service._blinded_sign = tor_ed25519._blinded_sign_with_tor_key # Now load up the instances self.instances = self._load_instances(service_config_data) # First descriptor for this service (the one we uploaded last) self.first_descriptor = None # Second descriptor for this service (the one we uploaded last) self.second_descriptor = None def has_onion_address(self, onion_address): """ Return True if this service has this onion address """ # Strip the ".onion" part of the address if it exists since some # subsystems don't use it (e.g. Tor sometimes omits it from control # port responses) my_onion_address = self.onion_address.replace(".onion", "") their_onion_address = onion_address.replace(".onion", "") return my_onion_address == their_onion_address def _load_instances(self, service_config_data): instances = [] for config_instance in service_config_data['instances']: new_instance = onionbalance.hs_v3.instance.InstanceV3(config_instance['address']) instances.append(new_instance) # Some basic validation for instance in instances: if self.has_onion_address(instance.onion_address): logger.error("Config file error. Did you configure your frontend (%s) as an instance?", self.onion_address) raise BadServiceInit return instances def _load_service_keys(self, service_config_data, config_path): # First of all let's load up the private key key_fname = service_config_data['key'] config_directory = os.path.dirname(config_path) if not os.path.isabs(key_fname): key_fname = os.path.join(config_directory, key_fname) try: with open(key_fname, 'rb') as handle: pem_key_bytes = handle.read() except EnvironmentError as e: logger.critical("Unable to read service private key file ('%s')", e) raise BadServiceInit # Get the service private key # First try with the OBv3 PEM format identity_priv_key = None try: identity_priv_key = serialization.load_pem_private_key(pem_key_bytes, password=None, backend=default_backend()) except ValueError as e: logger.warning("Service private key not in OBv3 format ('%s'). Trying tor's format...", e) # If the key was not in OBv3 PEM format, try the Tor binary format if not identity_priv_key: try: identity_priv_key = tor_ed25519.load_tor_key_from_disk(pem_key_bytes) self.is_priv_key_in_tor_format = True except ValueError as e: logger.warning("Service private key not in Tor format either ('%s'). Aborting.", e) raise BadServiceInit # Get onion address identity_pub_key = identity_priv_key.public_key() identity_pub_key_bytes = identity_pub_key.public_bytes(encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw) onion_address = HiddenServiceDescriptorV3.address_from_identity_key(identity_pub_key_bytes) logger.warning("Loaded onion %s from %s", onion_address, key_fname) return identity_priv_key, onion_address def _intro_set_modified(self, is_first_desc): """ Check if the introduction point set has changed since last publish. """ if is_first_desc: last_upload_ts = self.first_descriptor.last_upload_ts else: last_upload_ts = self.second_descriptor.last_upload_ts if not last_upload_ts: logger.info("\t Descriptor never published before. Do it now!") return True for instance in self.instances: if not instance.intro_set_modified_timestamp: logger.info("\t Still dont have a descriptor for this instance") continue if instance.intro_set_modified_timestamp > last_upload_ts: logger.info("\t Intro set modified") return True logger.info("\t Intro set not modified") return False def _get_descriptor_lifetime(self): from onionbalance.hs_v3.onionbalance import my_onionbalance if my_onionbalance.is_testnet: return params.FRONTEND_DESCRIPTOR_LIFETIME_TESTNET else: return params.FRONTEND_DESCRIPTOR_LIFETIME def _descriptor_has_expired(self, is_first_desc): """ Check if the descriptor has expired (hasn't been uploaded recently). If 'is_first_desc' is set then check the first descriptor of the service, otherwise the second. """ if is_first_desc: last_upload_ts = self.first_descriptor.last_upload_ts else: last_upload_ts = self.second_descriptor.last_upload_ts descriptor_age = (datetime.datetime.utcnow() - last_upload_ts) descriptor_age = int(descriptor_age.total_seconds()) if (descriptor_age > self._get_descriptor_lifetime()): logger.info("\t Our %s descriptor has expired (%s seconds old). Uploading new one.", "first" if is_first_desc else "second", descriptor_age) return True else: logger.info("\t Our %s descriptor is still fresh (%s seconds old).", "first" if is_first_desc else "second", descriptor_age) return False def _hsdir_set_changed(self, is_first_desc): """ Return True if the HSDir has changed between the last upload of this descriptor and the current state of things """ from onionbalance.hs_v3.onionbalance import my_onionbalance # Derive blinding parameter _, time_period_number = hashring.get_srv_and_time_period(is_first_desc) blinded_param = my_onionbalance.consensus.get_blinding_param(self._get_identity_pubkey_bytes(), time_period_number) # Get blinded key # TODO: hoho! this is dirty we are poking into internal stem API. We # should ask atagar to make it public for us! :) blinded_key = stem.descriptor.hidden_service._blinded_pubkey(self._get_identity_pubkey_bytes(), blinded_param) # Calculate current responsible HSDirs try: responsible_hsdirs = hashring.get_responsible_hsdirs(blinded_key, is_first_desc) except hashring.EmptyHashRing: return False if is_first_desc: previous_responsible_hsdirs = self.first_descriptor.responsible_hsdirs else: previous_responsible_hsdirs = self.second_descriptor.responsible_hsdirs if set(responsible_hsdirs) != set(previous_responsible_hsdirs): logger.info("\t HSDir set changed (%s vs %s)", set(responsible_hsdirs), set(previous_responsible_hsdirs)) return True else: logger.info("\t HSDir set remained the same") return False def _should_publish_descriptor_now(self, is_first_desc, force_publish=False): """ Return True if we should publish a descriptor right now """ # If descriptor not yet uploaded, do it now! if is_first_desc and not self.first_descriptor: return True if not is_first_desc and not self.second_descriptor: return True # OK this is not the first time we publish a descriptor. Check various # parameters to see if we should try to publish again: return any([self._intro_set_modified(is_first_desc), self._descriptor_has_expired(is_first_desc), self._hsdir_set_changed(is_first_desc), force_publish]) def get_all_intros_for_publish(self): """ Return an IntroductionPointSetV3 with all the intros of all the instances of this service. """ all_intros = [] for instance in self.instances: try: instance_intros = instance.get_intros_for_publish() except onionbalance.hs_v3.instance.InstanceHasNoDescriptor: logger.info("Entirely missing a descriptor for instance %s. Continuing anyway if possible", instance.onion_address) continue except onionbalance.hs_v3.instance.InstanceIsOffline: logger.info("Instance %s is offline. Ignoring its intro points...", instance.onion_address) continue all_intros.append(instance_intros) return descriptor.IntroductionPointSetV3(all_intros) def publish_descriptors(self): self._publish_descriptor(is_first_desc=True) self._publish_descriptor(is_first_desc=False) def _get_intros_for_desc(self): """ Get the intros that should be included in a descriptor for this service. """ all_intros = self.get_all_intros_for_publish() # Get number of instances that contributed to final intro point list n_instances = len(all_intros.intro_points) n_intros_wanted = n_instances * params.N_INTROS_PER_INSTANCE final_intros = all_intros.choose(n_intros_wanted) if (len(final_intros) == 0): logger.info("Got no usable intro points from our instances. Delaying descriptor push...") raise NotEnoughIntros logger.info("We got %d intros from %d instances. We want %d intros ourselves (got: %d)", len(all_intros.get_intro_points_flat()), n_instances, n_intros_wanted, len(final_intros)) return final_intros def _publish_descriptor(self, is_first_desc): """ Attempt to publish descriptor if needed. If 'is_first_desc' is set then attempt to upload the first descriptor of the service, otherwise the second. """ from onionbalance.hs_v3.onionbalance import my_onionbalance if not self._should_publish_descriptor_now(is_first_desc): logger.info("No reason to publish %s descriptor for %s", "first" if is_first_desc else "second", self.onion_address) return try: intro_points = self._get_intros_for_desc() except NotEnoughIntros: return # Derive blinding parameter _, time_period_number = hashring.get_srv_and_time_period(is_first_desc) blinding_param = my_onionbalance.consensus.get_blinding_param(self._get_identity_pubkey_bytes(), time_period_number) try: desc = descriptor.OBDescriptor(self.onion_address, self.identity_priv_key, blinding_param, intro_points, is_first_desc) except descriptor.BadDescriptor: return logger.info("Service %s created %s descriptor (%s intro points) (blinding param: %s) (size: %s bytes). About to publish:", self.onion_address, "first" if is_first_desc else "second", len(desc.intro_set), blinding_param.hex(), len(str(desc.v3_desc))) # When we do a v3 HSPOST on the control port, Tor decodes the # descriptor and extracts the blinded pubkey to be used when uploading # the descriptor. So let's do the same to compute the responsible # HSDirs: blinded_key = desc.get_blinded_key() # Calculate responsible HSDirs for our service try: responsible_hsdirs = hashring.get_responsible_hsdirs(blinded_key, is_first_desc) except hashring.EmptyHashRing: logger.warning("Can't publish desc with no hash ring. Delaying...") return desc.set_last_publish_attempt_ts(datetime.datetime.utcnow()) logger.info("Uploading %s descriptor for %s to %s", "first" if is_first_desc else "second", self.onion_address, responsible_hsdirs) # Upload descriptor self._upload_descriptor(my_onionbalance.controller.controller, desc, responsible_hsdirs) # It would be better to set last_upload_ts when an upload succeeds and # not when an upload is just attempted. Unfortunately the HS_DESC # # UPLOADED event does not provide information about the service and # so it can't be used to determine when descriptor upload succeeds desc.set_last_upload_ts(datetime.datetime.utcnow()) desc.set_responsible_hsdirs(responsible_hsdirs) # Set the descriptor if is_first_desc: self.first_descriptor = desc else: self.second_descriptor = desc def _upload_descriptor(self, controller, ob_desc, hsdirs): """ Convenience method to upload a descriptor Handle some error checking and logging inside the Service class """ if hsdirs and not isinstance(hsdirs, list): hsdirs = [hsdirs] while True: try: onionbalance.common.descriptor.upload_descriptor(controller, ob_desc.v3_desc, hsdirs=hsdirs, v3_onion_address=ob_desc.onion_address) break except stem.SocketClosed: logger.error("Error uploading descriptor for service " "%s.onion. Control port socket is closed.", self.onion_address) onionbalance.common.util.reauthenticate(controller, logger) except stem.ControllerError: logger.exception("Error uploading descriptor for service " "%s.onion.", self.onion_address) break def _get_identity_pubkey_bytes(self): identity_pub_key = self.identity_priv_key.public_key() return identity_pub_key.public_bytes(encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw) class NotEnoughIntros(Exception): pass class BadServiceInit(Exception): pass onionbalance-0.2.2/onionbalance/hs_v3/status.py000066400000000000000000000113551411742520300215160ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Provide status over Unix socket Default path: /var/run/onionbalance/control """ import json import threading import socket from socketserver import BaseRequestHandler, ThreadingMixIn, UnixStreamServer from onionbalance.common import log from onionbalance.common.status import BaseStatusSocket from onionbalance.hs_v3.onionbalance import Onionbalance logger = log.get_logger() class StatusSocketHandlerMixin(object): def __init__(self, balance: Onionbalance): self.balance = balance def _outputString(self): time_format = "%Y-%m-%d %H:%M:%S" response = {} services_data = [] for service in self.balance.services: instances_data = [] for instance in service.instances: received = instance.descriptor.received_ts.strftime(time_format) \ if instance.descriptor else None if not instance.intro_set_modified_timestamp: instances_data.append({'onionAddress': instance.onion_address + '.onion', 'received': received}) else: instances_data.append({'onionAddress': instance.onion_address + '.onion', 'introSetModified': instance.intro_set_modified_timestamp.strftime(time_format), 'introPointsNum': len(instance.descriptor.intro_set), 'descriptorReceived': received}) if service.first_descriptor: last_attempt_first = service.first_descriptor.last_publish_attempt_ts.strftime(time_format) else: last_attempt_first = None if service.second_descriptor: last_attempt_second = service.second_descriptor.last_publish_attempt_ts.strftime(time_format) else: last_attempt_second = None services_data.append({'onionAddress': service.onion_address, 'publishAttemptFirstDescriptor': last_attempt_first, 'publishAttemptSecondDescriptor': last_attempt_second, 'instances': instances_data}) response['services'] = services_data return json.dumps(response, sort_keys=True) class StatusSocketHandlerImpl(BaseRequestHandler, StatusSocketHandlerMixin): """ Handler for new domain socket connections """ def __init__(self, balance: Onionbalance, *args, **kwargs): StatusSocketHandlerMixin.__init__(self, balance) BaseRequestHandler.__init__(self, *args, **kwargs) def handle(self): """ Prepare and output the status summary when a connection is received """ self.request.sendall(self._outputString().encode('utf-8')) def create_status_socket_handler(balance: Onionbalance): def StatusSocketHandler(*args, **kwargs): """ Fake constructor for StatusSocketHandlerImpl """ return StatusSocketHandlerImpl(balance, *args, **kwargs) return StatusSocketHandler class ThreadingSocketServer(ThreadingMixIn, UnixStreamServer): """ Unix socket server with threading """ pass class StatusSocket(BaseStatusSocket): """ Create a Unix domain socket which emits a summary of the Onionbalance status when a client connects. """ def __init__(self, status_socket_location, balance: Onionbalance): """ Create the Unix domain socket status server and start in a thread Example:: socat - unix-connect:/var/run/onionbalance/control {"services": [{"instances": [{"introModified": "2020-06-16 19:35:17", "ipsNum": 3, "onionAddress": "vkmiy6biqcyphtx5exswxl5sjus2vn2b6pzir7lz5akudhwbqk5muead.onion"}], "onionAddress": "bvy46sg2b5dokczabwv2pabqlrps3lppweyrebhat6gjieo2avojdvad.onion.onion", "timestamp": "2020-06-16 19:36:01"}]} """ super().__init__(status_socket_location) self.cleanup_socket_file() logger.debug("Creating status socket at %s", self.unix_socket_filename) try: self.server = ThreadingSocketServer(self.unix_socket_filename, create_status_socket_handler(balance)) # Start running the socket server in a another thread server_thread = threading.Thread(target=self.server.serve_forever) server_thread.daemon = True # Exit daemon when main thread stops server_thread.start() except (OSError, socket.error): logger.error("Could not start status socket at %s. Does the path " "exist? Do you have permission?", status_socket_location) onionbalance-0.2.2/onionbalance/hs_v3/stem_controller.py000066400000000000000000000052461411742520300234100ustar00rootroot00000000000000import traceback import stem from stem.control import EventType from stem import Signal import onionbalance.common.util from onionbalance.common import log logger = log.get_logger() def handle_new_status_event_wrapper(status_event): """ A wrapper for this control port event. We need this so that we print tracebacks on the listener thread (also see https://stem.torproject.org/tutorials/tortoise_and_the_hare.html#advanced-listeners) """ from onionbalance.hs_v3.onionbalance import my_onionbalance try: my_onionbalance.handle_new_status_event(status_event) except BaseException: print(traceback.format_exc()) def handle_new_desc_event_wrapper(desc_event): """ A wrapper for this control port event (see above) """ from onionbalance.hs_v3.onionbalance import my_onionbalance try: my_onionbalance.handle_new_desc_event(desc_event) except BaseException: print(traceback.format_exc()) def handle_new_desc_content_event_wrapper(desc_content_event): """ A wrapper for this control port event (see above) """ from onionbalance.hs_v3.onionbalance import my_onionbalance try: my_onionbalance.handle_new_desc_content_event(desc_content_event) except BaseException: print(traceback.format_exc()) class StemController(object): """This class is our interface to the control port""" def __init__(self, address=None, port=None, socket=None): self.controller = onionbalance.common.util.connect_to_control_port(tor_socket=socket, tor_address=address, tor_port=port) assert(self.controller.is_authenticated()) def mark_tor_as_active(self): """ Send the ACTIVE signal to the control port so that Tor does not become dormant. """ # pylint: disable=no-member try: self.controller.signal(Signal.ACTIVE) except stem.SocketClosed: logger.warning("Can't connect to the control port to send ACTIVE signal. Moving on...") def get_md_consensus(self): return self.controller.get_info("dir/status-vote/current/consensus-microdesc") def add_event_listeners(self): # pylint: disable=no-member self.controller.add_event_listener(handle_new_status_event_wrapper, EventType.STATUS_CLIENT) self.controller.add_event_listener(handle_new_desc_event_wrapper, EventType.HS_DESC) self.controller.add_event_listener(handle_new_desc_content_event_wrapper, EventType.HS_DESC_CONTENT) def shutdown(self): self.controller.close() onionbalance-0.2.2/onionbalance/hs_v3/tor_ed25519.py000066400000000000000000000111031411742520300220440ustar00rootroot00000000000000from stem.util import ed25519 from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey, Ed25519PrivateKey from cryptography.hazmat.primitives import serialization from onionbalance.hs_v3.ext import ed25519_exts_ref from onionbalance.hs_v3.ext import slow_ed25519 def load_tor_key_from_disk(key_bytes): """ Load a private identity key from little-t-tor. """ # Verify header if (key_bytes[:29] != b'== ed25519v1-secret: type0 =='): raise ValueError("Tor key does not start with Tor header") expanded_sk = key_bytes[32:] # The rest should be 64 bytes (a,h): # 32 bytes for secret scalar 'a' # 32 bytes for PRF key 'h' if (len(expanded_sk) != 64): raise ValueError("Tor private key has the wrong length") return TorEd25519PrivateKey(expanded_sk) def _blinded_sign_with_tor_key(msg, identity_key, blinded_key, blinding_nonce): """ This is identical to stem's hidden_service.py:_blinded_sign() but takes an extended private key (i.e. in tor format) as its argument, instead of the standard format that hazmat does. It basically omits the "extended the key" step and does everything else the same. """ identity_key_bytes = identity_key.private_bytes( encoding = serialization.Encoding.Raw, format = serialization.PrivateFormat.Raw, encryption_algorithm = serialization.NoEncryption(), ) # blind the ESK with this nonce esk = identity_key_bytes mult = 2 ** (ed25519.b - 2) + sum(2 ** i * ed25519.bit(blinding_nonce, i) for i in range(3, ed25519.b - 2)) s = ed25519.decodeint(esk[:32]) s_prime = (s * mult) % ed25519.l k = esk[32:] k_prime = ed25519.H(b'Derive temporary signing key hash input' + k)[:32] blinded_esk = ed25519.encodeint(s_prime) + k_prime # finally, sign the message a = ed25519.decodeint(blinded_esk[:32]) r = ed25519.Hint(b''.join([blinded_esk[i:i + 1] for i in range(ed25519.b // 8, ed25519.b // 4)]) + msg) R = ed25519.scalarmult(ed25519.B, r) S = (r + ed25519.Hint(ed25519.encodepoint(R) + blinded_key + msg) * a) % ed25519.l return ed25519.encodepoint(R) + ed25519.encodeint(S) """ Tor ed25519 keys Expose classes for ed25519 keys in Tor's extended key format which can mimic the hazmat ed25519 public/private key classes, so that we can use them interchangeably in stem. Tor uses the "extended" (a,h) format for its private keys, whereas hazmat uses the "standard" (seed,A) format: https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/ Since you can't go from the "extended" format to the "standard" format, we created these wrappers that act exactly like hazmat keys when it comes to their interface. """ class TorEd25519PrivateKey(object): """ Represents the private part of a blinded ed25519 key of an onion service and should expose a public_key() method and a sign() method. """ def __init__(self, expanded_sk): self.priv_key = expanded_sk self.pub_key_bytes = ed25519_exts_ref.publickeyFromESK(self.priv_key) self.pub_key = TorEd25519PublicKey(self.pub_key_bytes) def public_key(self): return self.pub_key def private_bytes(self, encoding=None, format=None, encryption_algorithm=None): return self.priv_key def sign(self, msg): return ed25519_exts_ref.signatureWithESK(msg, self.priv_key, self.pub_key_bytes) @property def __class__(self): """ This is an epic hack to make this class look like a hazmat ed25519 public key in the eyes of stem: https://github.com/asn-d6/onionbalance/issues/10#issuecomment-610425916 The __class_ attribute is what's being used by unittest.mock and the C API to trick isinstance() checks, so as long as stem uses isinstance() this is gonna work: https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.__class__ https://docs.python.org/3/c-api/object.html#c.PyObject_IsInstance """ return Ed25519PrivateKey class TorEd25519PublicKey(object): """ Represents the public blinded ed25519 key of an onion service and should expose a public_bytes() method and a verify() method. """ def __init__(self, public_key): self.public_key = public_key def public_bytes(self, encoding=None, format=None): return self.public_key def verify(self, signature, message): """ raises exception if sig not valid """ slow_ed25519.checkvalid(signature, message, self.public_key) @property def __class__(self): return Ed25519PublicKey onionbalance-0.2.2/onionbalance/hs_v3/tor_node.py000066400000000000000000000057661411742520300220150ustar00rootroot00000000000000import base64 import hashlib from onionbalance.common import log logger = log.get_logger() class Node(object): """ Represents a Tor node. A Node instance gets created for each node of a consensus. When we fetch a new consensus, we create new Node instances for the routers found inside. The 'microdescriptor' and 'routerstatus' fields of this object are immutable: They are set once when we receive the consensus based on the state of the network at that point, and they stay like that until we get a new consensus. """ def __init__(self, microdescriptor, routerstatus): assert(microdescriptor and routerstatus) logger.debug("Initializing node with fpr %s", routerstatus.fingerprint) # The microdescriptor of this node self.microdescriptor = microdescriptor # The consensus routerstatus for this node self.routerstatus = routerstatus def get_hex_fingerprint(self): return self.routerstatus.fingerprint def get_hsdir_index(self, srv, period_num): """ Get the HSDir index for this node: hsdir_index(node) = H("node-idx" | node_identity | shared_random_value | INT_8(period_num) | INT_8(period_length) ) Raises NoHSDir or NoEd25519Identity in case of errors. """ from onionbalance.hs_v3.onionbalance import my_onionbalance # See if this node can be an HSDir (it needs to be supported both in # protover and in flags) if 'HSDir' not in self.routerstatus.protocols or \ 2 not in self.routerstatus.protocols['HSDir'] or \ 'HSDir' not in self.routerstatus.flags: raise NoHSDir # See if ed25519 identity is supported for this node if 'ed25519' not in self.microdescriptor.identifiers: raise NoEd25519Identity # In stem the ed25519 identity is a base64 string and we need to add # the missing padding so that the python base64 module can successfuly # decode it. # TODO: Abstract this into its own function... ed25519_node_identity_b64 = self.microdescriptor.identifiers['ed25519'] missing_padding = len(ed25519_node_identity_b64) % 4 ed25519_node_identity_b64 += '=' * missing_padding ed25519_node_identity = base64.b64decode(ed25519_node_identity_b64) period_num_int_8 = period_num.to_bytes(8, 'big') period_length = my_onionbalance.consensus.get_time_period_length() period_length_int_8 = period_length.to_bytes(8, 'big') hash_body = b"%s%s%s%s%s" % (b"node-idx", ed25519_node_identity, srv, period_num_int_8, period_length_int_8) hsdir_index = hashlib.sha3_256(hash_body).digest() return hsdir_index class NoEd25519Identity(Exception): pass class NoHSDir(Exception): pass onionbalance-0.2.2/onionbalance/manager.py000066400000000000000000000014071411742520300205600ustar00rootroot00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- """ Global entry point to decide whether we are doing v2 or v3 so that we use the right codebase """ from onionbalance.common import argparser from onionbalance.common import log from setproctitle import setproctitle # pylint: disable=no-name-in-module import onionbalance.hs_v2.manager import onionbalance.hs_v3.manager from onionbalance import __version__ logger = log.get_logger() def main(): setproctitle('onionbalance') parser = argparser.get_common_argparser() args = parser.parse_args() logger.warning("Initializing onionbalance (version: %s)...", __version__) if args.hs_version == 'v2': onionbalance.hs_v2.manager.main(args) else: onionbalance.hs_v3.manager.main(args) onionbalance-0.2.2/requirements.txt000066400000000000000000000001351411742520300174250ustar00rootroot00000000000000setproctitle>=1.1.9 stem>=1.8 pyyaml>=4.2b1 cryptography>=3.2 pycryptodomex future>=0.14.3 onionbalance-0.2.2/scripts/000077500000000000000000000000001411742520300156315ustar00rootroot00000000000000onionbalance-0.2.2/scripts/rend-connection-stats.py000077500000000000000000000113171411742520300224320ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Log information about the number and rate of rendezvous connections to a HS. """ import sys import time import argparse import logging import logging.handlers import threading import stem from stem.control import Controller import schedule handler = logging.StreamHandler() formatter = logging.Formatter(fmt="%(asctime)s [%(levelname)s]: %(message)s") handler.setFormatter(formatter) logger = logging.getLogger("onionbalance") logger.addHandler(handler) logger.setLevel(logging.DEBUG) lock = threading.RLock() # Track circuits established in current time period. new_rend_circuits_established = 0 rend_circuits_closed = 0 def circ_event_handler(event): """ Handle the event received when Tor emits an event related the a rendezvous circuit """ global new_rend_circuits_established, rend_circuits_closed if event.purpose == "HS_SERVICE_REND" and event.hs_state == "HSSR_JOINED": if event.type == "CIRC_MINOR": # Log when a new rendezvous circuit is successfully established. # A CIRC_MINOR event is emitted when the rendezvous circuit moves # from HS_STATE=HSSR_CONNECTING to HS_STATE=HSSR_JOINED logger.debug("New rendezvous circuit established (CircID: %s)", event.id) new_rend_circuits_established += 1 elif event.type == "CIRC" and event.status == "CLOSED": logger.debug("Rendezvous circuit closed (CircID: %s)", event.id) rend_circuits_closed += 1 return def output_status(controller): """ Output the current counts every tick period. """ global new_rend_circuits_established, rend_circuits_closed # Count number of currently established rendezvous circuits for this HS. rend_circ_count = len([circ for circ in controller.get_circuits() if circ.purpose == "HS_SERVICE_REND" and circ.hs_state == "HSSR_JOINED"]) with lock: logger.info("New rend circuits: %d - Closed rend circuits: %d - " "Established rend circuits: %d", new_rend_circuits_established, rend_circuits_closed, rend_circ_count) new_rend_circuits_established = 0 rend_circuits_closed = 0 return None def parse_cmd_args(): """ Parses and returns command line arguments. """ parser = argparse.ArgumentParser( description="%s logs stats about Tor rendezvous circuits" % sys.argv[0]) parser.add_argument("-i", "--ip", type=str, default="127.0.0.1", help="Tor controller IP address") parser.add_argument("-p", "--port", type=int, default=9051, help="Tor controller port") parser.add_argument("-t", "--tick", type=int, default=60, help="Output total every tick seconds " "(default: %(default)s)") parser.add_argument("--log-file", type=str, default="rendezvous.log", help="Location to log the rendezvous connection" "data.") parser.add_argument("-v", "--verbosity", type=str, default="info", help="Minimum verbosity level for logging. Available " "in ascending order: debug, info, warning, " "error, critical). The default is info.") return parser.parse_args() def main(): args = parse_cmd_args() logger.setLevel(logging.__dict__[args.verbosity.upper()]) if args.log_file: file_handler = logging.handlers.TimedRotatingFileHandler( args.log_file, when='D') file_handler.setFormatter(formatter) logger.addHandler(file_handler) logger.info("Beginning rendezvous circuit monitoring." "Status output every %d seconds", args.tick) with Controller.from_port(port=args.port) as controller: # Create a connection to the Tor control port controller.authenticate() # Add event listeners for HS_DESC and HS_DESC_CONTENT controller.add_event_listener(circ_event_handler, stem.control.EventType.CIRC) controller.add_event_listener(circ_event_handler, stem.control.EventType.CIRC_MINOR) # Schedule rendezvous status output. schedule.every(args.tick).seconds.do(output_status, controller) schedule.run_all() try: while True: schedule.run_pending() time.sleep(1) except KeyboardInterrupt: logger.info("Stopping rendezvous circuit monitoring.") sys.exit(0) if __name__ == '__main__': main() onionbalance-0.2.2/setup.cfg000066400000000000000000000005171411742520300157660ustar00rootroot00000000000000[tool:pytest] norecursedirs = _build tor chutney [bdist_wheel] universal=1 [versioneer] VCS = git style = pep440 versionfile_source = onionbalance/_version.py versionfile_build = onionbalance/_version.py tag_prefix ='' parentdir_prefix = onionbalance- [flake8] ignore = E501, E302, E251, E305, E261 exclude = onionbalance/hs_v3/ext/onionbalance-0.2.2/setup.py000066400000000000000000000043011411742520300156520ustar00rootroot00000000000000# -*- coding: utf-8 -*- """setup.py: setuptools control.""" import io import versioneer import os from setuptools import setup # Read version and other info from package's __init.py file module_info = {} init_path = os.path.join(os.path.dirname(__file__), 'onionbalance', '__init__.py') with open(init_path) as init_file: exec(init_file.read(), module_info) def read(*names, **kwargs): return io.open( os.path.join(os.path.dirname(__file__), *names), encoding=kwargs.get("encoding", "utf8") ).read() setup( name="Onionbalance", packages=["onionbalance", "onionbalance.hs_v2", "onionbalance.hs_v3", "onionbalance.hs_v3.ext", "onionbalance.common", "onionbalance.config_generator"], entry_points={ "console_scripts": [ 'onionbalance = onionbalance.manager:main', 'onionbalance-config = onionbalance.config_generator.config_generator:main', ]}, description="Onionbalance provides load-balancing and redundancy for Tor " "hidden services by distributing requests to multiple backend " "Tor instances.", long_description=read('README.rst'), author=module_info.get('__author__'), author_email=module_info.get('__contact__'), url=module_info.get('__url__'), license=module_info.get('__license__'), keywords='tor', python_requires='>=3.6', install_requires=[ 'setuptools', 'stem>=1.8', 'PyYAML>=4.2b1', 'pycryptodomex', 'future>=0.14.0', 'setproctitle', 'cryptography>=2.5', ], tests_require=['pytest-mock', 'pytest', 'mock', 'pexpect', 'pylint', 'flake8', 'coveralls'], package_data={'onionbalance.config_generator': ['data/*']}, include_package_data=True, classifiers=[ 'Development Status :: 3 - Alpha', 'License :: OSI Approved :: GNU General Public License (GPL)', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', ], version=versioneer.get_version(), cmdclass=versioneer.get_cmdclass(), ) onionbalance-0.2.2/test-requirements.txt000066400000000000000000000001441411742520300204020ustar00rootroot00000000000000pytest mock pytest-mock pexpect coveralls>=1.1 pytest-cov>=2.4 sphinx pylint flake8 coverage>=5.3.1 onionbalance-0.2.2/test/000077500000000000000000000000001411742520300151215ustar00rootroot00000000000000onionbalance-0.2.2/test/__init__.py000066400000000000000000000000001411742520300172200ustar00rootroot00000000000000onionbalance-0.2.2/test/functional/000077500000000000000000000000001411742520300172635ustar00rootroot00000000000000onionbalance-0.2.2/test/functional/__init__.py000066400000000000000000000000001411742520300213620ustar00rootroot00000000000000onionbalance-0.2.2/test/functional/util.py000066400000000000000000000104641411742520300206170ustar00rootroot00000000000000# -*- coding: utf-8 -*- import base64 import os import socket import Cryptodome.PublicKey.RSA import pytest import yaml from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from cryptography.hazmat.primitives.serialization.base import Encoding, PublicFormat from onionbalance.config_generator.config_generator import ConfigGenerator, parse_cmd_args # Skip functional tests if Chutney environment is not running. pytestmark = pytest.mark.skipif( "os.environ.get('CHUTNEY_ONION_ADDRESS') is None", reason="Skipping functional test, no Chutney environment detected") def parse_chutney_enviroment(): """ Read environment variables and determine chutney instance and client addresses. """ tor_client = os.environ.get('CHUTNEY_CLIENT_PORT') assert tor_client # Calculate the address and port of clients control port client_address, client_socks_port = tor_client.split(':') client_ip = socket.gethostbyname(client_address) tor_client_number = int(client_socks_port) - 9000 # Control port in the 8000-8999 range, offset by Tor client number control_port = 8000 + tor_client_number assert control_port # Retrieve instance onion address exported during chutney setup instance_address = os.environ.get('CHUTNEY_ONION_ADDRESS') assert instance_address # Need at least 1 instance address for test return { 'client_ip': client_ip, 'control_port': control_port, 'instances': [instance_address], } def create_test_config_file_v2(tmppath, private_key=None, instances=None): """ Setup function to create a temp directory with master key and config file. Returns a path to the temporary config file. .. todo:: Refactor settings.py config creation to avoid code duplication in integration tests. """ if not private_key: private_key = Cryptodome.PublicKey.RSA.generate(1024) # Write private key file key_path = tmppath.join('private_key') key_path.write(private_key.exportKey()) assert key_path.check() # Create YAML Onionbalance settings file for these instances service_data = {'key': str(key_path)} service_data['instances'] = [{'address': addr[:16]} for addr in instances] settings_data = { 'services': [service_data], 'STATUS_SOCKET_LOCATION': str(tmppath.join('control')), } config_yaml = yaml.dump(settings_data, default_flow_style=False) config_path = tmppath.join('config.yaml') config_path.write_binary(config_yaml.encode('utf-8')) assert config_path.check() return str(config_path) def create_test_config_file_v3(tmppath, instance_address): args = parse_cmd_args().parse_args(['--hs-version', 'v3', '-n', '1', '--output', str(tmppath)]) ConfigGenerator(args, False) config_path = tmppath.join('config.yaml') assert config_path.check() with open(config_path) as f: config = yaml.safe_load(f) config['services'][0]['instances'][0]['address'] = instance_address with open(config_path, "w") as f: yaml.dump(config, f) return str(config_path) def update_test_config_file_v3(tmppath, instance_address): config_path = tmppath.join('config.yaml') assert config_path.check() with open(config_path) as f: config = yaml.safe_load(f) config['services'][0]['instances'][0]['address'] = instance_address with open(config_path, "w") as f: yaml.dump(config, f) return str(config_path) def random_onionv3_address(): private_key = Ed25519PrivateKey.generate() public_key = private_key.public_key() public_bytes = public_key.public_bytes(encoding=Encoding.Raw, format=PublicFormat.Raw) # checksum = H(".onion checksum" || pubkey || version) checksumBytes = b''.join([b'.onion checksum', public_bytes, bytes([0x03])]) digest = hashes.Hash(hashes.SHA3_256(), backend=default_backend()) digest.update(checksumBytes) checksum = digest.finalize()[:2] # onion_address = base32(pubkey || checksum || version) onionAddressBytes = b''.join([public_bytes, checksum, bytes([0x03])]) print(onionAddressBytes) onionAddress = base64.b32encode(onionAddressBytes).lower().decode('utf-8') return onionAddress + '.onion' onionbalance-0.2.2/test/functional/v2/000077500000000000000000000000001411742520300176125ustar00rootroot00000000000000onionbalance-0.2.2/test/functional/v2/__init__.py000066400000000000000000000000001411742520300217110ustar00rootroot00000000000000onionbalance-0.2.2/test/functional/v2/test_onionbalance_config.py000066400000000000000000000132271411742520300252050ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Functional tests which run the onionbalance-config tool and check the created output files. """ import sys import pexpect import Cryptodome.PublicKey.RSA import onionbalance.hs_v2.util def onionbalance_config_interact(cli, cli_input): """ Send each input line to the onionbalance-config CLI interface """ cli.expect(u"Enter HS version") cli.send("v2\n") cli.expect(u"store generated config") cli.send("{}\n".format(cli_input.get('config_dir', u''))) cli.expect(u"path to master service private key") cli.send(u"{}\n".format(cli_input.get('private_key_path', u''))) cli.expect(u"Number of instance services") cli.send(u"{}\n".format(cli_input.get('num_instances', u''))) cli.expect(u"Provide a tag name") cli.send(u"{}\n".format(cli_input.get('tag_name', u''))) cli.expect(u"service virtual port") cli.send(u"{}\n".format(cli_input.get('virtual_port', u''))) cli.expect(u"service target IP and port") cli.send(u"{}\n".format(cli_input.get('target_ip', u''))) cli.expect(u"optional password") cli.send(u"{}\n".format(cli_input.get('password', u''))) return None def check_basic_config_output(config_dir): """ Run basic tests on the generated config files and keys to check that they look reasonable. """ assert len(config_dir.listdir()) == 1 + 2 # Find generated instance addresses instance_addresses = [] for directory in config_dir.listdir(): if directory.basename != 'master': instance_addresses.extend( [str(name.basename) for name in directory.listdir() if 'torrc' not in name.basename]) # Correct number of directories created assert len(config_dir.listdir()) == 1 + 2 assert config_dir.join('master', 'torrc-server').check() assert config_dir.join('master', 'config.yaml').check() config_file = config_dir.join('master', 'config.yaml').read_text('utf-8') assert all(address in config_file for address in instance_addresses) # Test that all addresses are encoded as bytes and not unicode assert "!!python/unicode" not in config_file return True def test_onionbalance_config_interactive(tmpdir): """ Functional test to run onion-balance config in interactive mode. """ # Start onionbalance-config in interactive mode (no command line arguments) cli = pexpect.spawnu("onionbalance-config", logfile=sys.stdout) cli.expect(u"entering interactive mode") # Interact with the running onionbalance-config process onionbalance_config_interact( cli, cli_input={'config_dir': str(tmpdir.join(u"configdir"))}) cli.expect(u"Done! Successfully generated") check_basic_config_output(tmpdir.join(u"configdir")) def test_onionbalance_config_automatic(tmpdir): """ Functional test to run onion-balance config in automatic mode. """ # Start onionbalance-config in automatic mode cli = pexpect.spawnu("onionbalance-config", logfile=sys.stdout, args=[ '--hs-version', 'v2', '--output', str(tmpdir.join(u"configdir")), ]) cli.expect(u"Done! Successfully generated") check_basic_config_output(tmpdir.join(u"configdir")) def test_onionbalance_config_automatic_custom_ports(tmpdir): """ Run onionbalance-config in interactive mode, providing a custom port line. """ cli = pexpect.spawnu("onionbalance-config", logfile=sys.stdout, args=[ '--hs-version', 'v2', '--output', str(tmpdir.join(u"configdir")), '--service-virtual-port', u'443', '--service-target', u'127.0.0.1:8443', ]) cli.expect(u"Done! Successfully generated") # Read one of the generated torrc files for directory in tmpdir.join(u"configdir").listdir(): if directory.basename != 'master': torrc_file = [name for name in directory.listdir() if name.basename == 'instance_torrc'][0] break assert torrc_file.check() # Check torrc line contains the correct HiddenServicePort line torrc_contents = torrc_file.read_text('utf-8') assert u'HiddenServicePort 443 127.0.0.1:8443' in torrc_contents def test_onionbalance_config_automatic_key_with_password(tmpdir, mocker): """ Run onionbalance-config with an existing key, export as password protected key. """ # Create input private_key private_key = Cryptodome.PublicKey.RSA.generate(1024) key_path = tmpdir.join('private_key') key_path.write(private_key.exportKey()) # Start onionbalance-config in automatic mode cli = pexpect.spawnu("onionbalance-config", logfile=sys.stdout, args=[ '--hs-version', 'v2', '--output', str(tmpdir.join(u"configdir")), '--key', str(key_path), '--password', 'testpassword', ]) cli.expect(u"Done! Successfully generated") # Check master config was generated with password protected key. master_dir = tmpdir.join('configdir', 'master') output_key_path = [fpath for fpath in master_dir.listdir() if fpath.ext == '.key'][0] assert output_key_path.check() # Check key decrypts and is valid mocker.patch('getpass.getpass', lambda *_: 'testpassword') output_key = onionbalance.hs_v2.util.key_decrypt_prompt(str(output_key_path)) assert isinstance(output_key, Cryptodome.PublicKey.RSA.RsaKey) onionbalance-0.2.2/test/functional/v2/test_publish_master_descriptor.py000066400000000000000000000073521411742520300265110ustar00rootroot00000000000000# -*- coding: utf-8 -*- import socket import sys import time import Cryptodome.PublicKey.RSA import pexpect import stem.control import onionbalance.hs_v2.util from test.functional.util import * def test_master_descriptor_publication(tmpdir): """ Functional test to run Onionbalance, publish a master descriptor and check that it can be retrieved from the DHT. """ chutney_config = parse_chutney_enviroment() print(chutney_config) private_key = Cryptodome.PublicKey.RSA.generate(1024) master_onion_address = onionbalance.hs_v2.util.calc_onion_address(private_key) config_file_path = create_test_config_file_v2( tmppath=tmpdir, private_key=private_key, instances=chutney_config.get('instances', []), ) assert config_file_path # Start an Onionbalance server and monitor for correct output with pexpect server = pexpect.spawnu("onionbalance", args=[ '--hs-version', 'v2', '-i', chutney_config.get('client_ip'), '-p', str(chutney_config.get('control_port')), '-c', config_file_path, '-v', 'debug', '--is-testnet' ], logfile=sys.stdout, timeout=15) # Check for expected output from Onionbalance server.expect(u"Loaded the config file") server.expect(u"introduction point set has changed") server.expect(u"Published a descriptor", timeout=60) # Check Tor control port gave an uploaded event. server.expect(u"HS_DESC UPLOADED") # Eek, sleep to wait for descriptor upload to all replicas to finish time.sleep(10) # .. todo:: Also need to check and raise for any warnings or errors # that are emitted # Try fetch and validate the descriptor with stem with stem.control.Controller.from_port( address=chutney_config.get('client_ip'), port=chutney_config.get('control_port') ) as controller: controller.authenticate() # get_hidden_service_descriptor() will raise exceptions if it # cannot find the descriptors master_descriptor = controller.get_hidden_service_descriptor( master_onion_address) master_ips = master_descriptor.introduction_points() # Try retrieve a descriptor for each instance for instance_address in chutney_config.get('instances'): instance_descriptor = controller.get_hidden_service_descriptor( instance_address.split(':')[0]) instance_ips = instance_descriptor.introduction_points() # Check if all instance IPs were included in the master descriptor assert (set(ip.identifier for ip in instance_ips) == set(ip.identifier for ip in master_ips)) # Check that the control socket was created socket_path = tmpdir.join('control') assert socket_path.check() # Connect to the control socket and check the output sock_client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock_client.connect(str(socket_path)) # Read the data from the status socket result = [] while True: data = sock_client.recv(1024) if not data: break result.append(data.decode('utf-8')) result_data = ''.join(result) # Check each instance is in the output for instance_address in chutney_config.get('instances'): assert instance_address.split(':')[0] in result_data # Check all instances were online and all master descriptors uploaded assert master_onion_address in result_data assert '[offline]' not in result_data assert '[not uploaded]' not in result_data onionbalance-0.2.2/test/functional/v3/000077500000000000000000000000001411742520300176135ustar00rootroot00000000000000onionbalance-0.2.2/test/functional/v3/__init__.py000066400000000000000000000000001411742520300217120ustar00rootroot00000000000000onionbalance-0.2.2/test/functional/v3/test_sighup_reload_config.py000066400000000000000000000031731411742520300254020ustar00rootroot00000000000000# -*- coding: utf-8 -*- import signal import sys import time import pexpect from test.functional.util import * def test_sighup_reload_config(tmpdir): """ Functional test to run Onionbalance, send SIGHUP then check if config is reloaded """ chutney_config = parse_chutney_enviroment() original_instance_address = random_onionv3_address() config_file_path = create_test_config_file_v3(tmppath=tmpdir, instance_address=original_instance_address) assert config_file_path # Start an Onionbalance server and monitor for correct output with pexpect server = pexpect.spawnu("onionbalance", args=[ '--hs-version', 'v3', '-i', chutney_config.get('client_ip'), '-p', str(chutney_config.get('control_port')), '-c', config_file_path, '-v', 'debug', '--is-testnet' ], logfile=sys.stdout, timeout=5) time.sleep(1) # Check for expected output from Onionbalance server.expect(u"Loaded the config file") server.expect(original_instance_address) # Update config file and send SIGHUP updated_instance_address = random_onionv3_address() config_file_path = update_test_config_file_v3(tmppath=tmpdir, instance_address=updated_instance_address) assert config_file_path server.kill(signal.SIGHUP) server.expect(u"Signal SIGHUP received, reloading configuration") server.expect(u"Loaded the config file") server.expect(updated_instance_address) onionbalance-0.2.2/test/scripts/000077500000000000000000000000001411742520300166105ustar00rootroot00000000000000onionbalance-0.2.2/test/scripts/run-functional-tests.sh000077500000000000000000000003331411742520300232520ustar00rootroot00000000000000#!/bin/bash set -ex [[ $TEST =~ functional_(.*) ]] version="${BASH_REMATCH[1]}" pwd pytest --cov-report=term-missing --cov=onionbalance test/functional/$version/ pylint --disable=R,C,W onionbalance flake8 onionbalance onionbalance-0.2.2/test/scripts/run-unit-tests.sh000077500000000000000000000002641411742520300220720ustar00rootroot00000000000000#!/bin/bash set -ex pytest --cov-report=term-missing --cov=onionbalance --ignore=test/functional/ pylint onionbalance --disable=R,C,W --ignore=test/functional/ flake8 onionbalanceonionbalance-0.2.2/test/v2/000077500000000000000000000000001411742520300154505ustar00rootroot00000000000000onionbalance-0.2.2/test/v2/__init__.py000066400000000000000000000000001411742520300175470ustar00rootroot00000000000000onionbalance-0.2.2/test/v2/test_consensus.py000066400000000000000000000047151411742520300211100ustar00rootroot00000000000000# -*- coding: utf-8 -*- import pytest from onionbalance.hs_v2 import consensus from onionbalance.hs_v2 import config # Mock hex-encoded HSDir fingerprint list MOCK_HSDIR_LIST = [ "1111111111111111111111111111111111111111", "2222222222222222222222222222222222222222", "3333333333333333333333333333333333333333", "4444444444444444444444444444444444444444", "5555555555555555555555555555555555555555", "6666666666666666666666666666666666666666", ] config.HSDIR_SET = 3 # Always select 3 responsible HSDirs def test_get_hsdirs_no_consensus(): """ `get_hsdirs` should raise an exception when we don't have a valid HSDir list from the consensus. """ with pytest.raises(ValueError): consensus.get_hsdirs('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') def test_get_hsdirs(monkeypatch): """Test for normal responsible HSDir selection""" monkeypatch.setattr(consensus, 'HSDIR_LIST', MOCK_HSDIR_LIST) # Descriptor ID before '222....'' descriptor_id_base32 = "eiqaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" responsible_hsdirs = consensus.get_hsdirs(descriptor_id_base32) assert (responsible_hsdirs == [ "2222222222222222222222222222222222222222", "3333333333333333333333333333333333333333", "4444444444444444444444444444444444444444", ]) def test_get_hsdirs_edge_of_ring(monkeypatch): """Test that selection wraps around the edge of the HSDir ring""" monkeypatch.setattr(consensus, 'HSDIR_LIST', MOCK_HSDIR_LIST) # Descriptor ID before '666....'' descriptor_id_base32 = "mzqaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" responsible_hsdirs = consensus.get_hsdirs(descriptor_id_base32) assert (responsible_hsdirs == [ "6666666666666666666666666666666666666666", "1111111111111111111111111111111111111111", "2222222222222222222222222222222222222222", ]) def test_get_hsdirs_no_repeat(monkeypatch): """Test that selection wraps around the edge of the HSDir ring""" SHORT_HSDIR_LIST = [ "1111111111111111111111111111111111111111", "2222222222222222222222222222222222222222", ] monkeypatch.setattr(consensus, 'HSDIR_LIST', SHORT_HSDIR_LIST) # Descriptor ID before '111....'' descriptor_id_base32 = "ceiaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" responsible_hsdirs = consensus.get_hsdirs(descriptor_id_base32) assert (responsible_hsdirs == [ "1111111111111111111111111111111111111111", "2222222222222222222222222222222222222222", ]) onionbalance-0.2.2/test/v2/test_descriptor.py000066400000000000000000000410361411742520300212430ustar00rootroot00000000000000# -*- coding: utf-8 -*- import datetime import string import pytest import Cryptodome.PublicKey.RSA import stem.descriptor import stem.descriptor.hidden_service_descriptor import hashlib from binascii import unhexlify from onionbalance.common import intro_point_set from onionbalance.hs_v2 import descriptor PEM_PRIVATE_KEY = u'\n'.join([ '-----BEGIN RSA PRIVATE KEY-----', 'MIICWwIBAAKBgQDXzP6HGtjPSy7uF9OlY7ZmefTVKcFLsq0mSEzQrW5wSiNuYc+d', 'oSV2OWxPg+1fVe19ES43AUkq/bS/gjAMLOunP6u9FbPDojyh1Vs/6TVqftS3sPkl', 'Q0ItrrZwAwhtHC0WaEyrwYJNOSCBq3wpupdQhpRyWJFqMwm9+iBCG1QcJQIDAQAB', 'AoGAegc2Sqm4vgdyozof+R8Ybnw6ISu6XRbNaJ9rqHjZwW9695khsK4GJAM2pwQf', '/0/0ukszyfDVMhVC1yREDS59lgzNecItd6nQZWbwr9TFxIoa9ouTqk8PcAoNixTb', 'wafjPcMmWGakizXeAHiOfazPBH4x2keDQCulxfYxXZxTpyECQQDqZu61kd1S3U7T', 'BT2NQBd3tHX0Hvonx+IkOKXwpHFY0Mo4d32Bi+MxRuEnd3tO44AaMvlkl13QMTF2', 'kHFSC70dAkEA669LZavGjW67+rO+f+xyDVby9pD5GJQBb78xRCf93Zcu2KW4NSp3', 'XC4p4eWfLgff1VuXL7g0VdFm4wUUHqYUqQJAZLmqpjdyBeO3tZIw6vu5meTgMvEE', 'ygdos+vr0sa3NlUyMKWYNwznqgstQYpkYHf+WkPBS2qIE6iv+qUDLSCCOQJAESSk', 'CFYxUBJQ7BBs9+Mb/Kppa9Ppuobxf85ZaAq8pYScrLeJKZzYJ8VX2I2aQX/jISLT', 'YW41qFRd9n9lEkGkWQJAcxPmNI+2r5zJG+K148LLmWCIDTVZ4nxOcxffHka/3tCJ', 'lDGUw4p2wU6pVRDpNfKrF5Nc9ZKO8NAtC17ZvDyVkQ==', '-----END RSA PRIVATE KEY-----', ]) INTRODUCTION_POINT_PART = u'\n'.join([ '-----BEGIN MESSAGE-----', 'AgEdbps604RR6lqeyoZBzOb6+HvlL2cDt63w8vBtyRaLirq5ZD5GDnr+R0ePj71C', 'nC7qmRWuwBmzSdSd0lOTaSApBvIifbJksHUeT/rq03dpnnRHdHSVqSvig6bukcWJ', 'LgJmrRd3ES13LXVHenD3C6AZMHuL9TG+MjLO2PIHu0mFO18aAHVnWY32Dmt144IY', 'c2eTVZbsKobjjwCYvDf0PBZI+B6H0PZWkDX/ykYjArpLDwydeZyp+Zwj4+k0+nRr', 'RPlzbHYoBY9pFYDUXDXWdL+vTsgFTG0EngLGlgUWSY5U1T1Db5HfOqc7hbqklgs/', 'ULG8NUY1k41Wb+dleJI28/+ZOM9zOpHcegNx4Cn8UGbw/Yv3Tj+yki+TMeOtJyhK', 'PQP8NWq8zThiVhBrfpmVjMYkNeVNyVNoxRwS6rxCQjoLWSJit2Mpf57zY1AOvT1S', 'EqqFbsX+slD2Uk67imALh4pMtjX29VLIujpum3drLhoTHDszBRhIH61A2eAZqdJy', '7JkJd1x/8x7U0l8xNWhnj/bhUHdt3OrCvlN+n8x6BwmMNoLF8JIsskTuGHOaAKSQ', 'WK3z0rHjgIrEjkQeuQtfmptiIgRB9LnNr+YahRnRR6XIOJGaIoVLVM2Uo2RG4MS1', '2KC3DRJ87WdMv2yNWha3w+lWt/mOALahYrvuNMU8wEuNXSi5yCo1OKirv+d5viGe', 'hAgVZjRymBQF+vd30zMdOG9qXNoQFUN49JfS8z5FjWmdHRt2MHlqD2isxoeabERY', 'T4Q50fFH8XHkRRomKBEbCwy/4t2DiqcTOSLGOSbTtf7qlUACp2bRth/g0ySAW8X/', 'CaWVm53z1vdgF2+t6j1CnuIqf0dUygZ07HEAHgu3rMW0YTk04QkvR3jiKAKijvGH', '3YcMJz1aJ7psWSsgiwn8a8Cs4fAcLNJcdTrnyxhQI4PMST/QLfp8nPYrhKEeifTc', 'vYkC4CtGuEFkWyRifIGbeD7FcjkL1zqVNu31vgo3EIVbHzylERgpgTIYBRv7aV7W', 'X7XAbrrgXL0zgpI0orOyPkr2KRs6CcoEqcc2MLyB6gJ5fYAm69Ige+6gWtRT6qvZ', 'tJXagfKZivLj73dRD6sUqTCX4tmgo7Q8WFSeNscDAVm/p4dVsw6SOoFcRgaH20yX', 'MBa3oLNTUNAaGbScUPx2Ja3MQS0UITwk0TFTF7hL++NhTvTp6IdgQW4DG+/bVJ3M', 'BRR+hsvSz5BSQQj2FUIAsJ+WoVK9ImbgsBbYxSH60jCvxTIdeh2IeUzS2T1bU9AU', 'jOLzcJZmNh95Nj2Qdrc8/0gin9KpgPmuPQ6CyH3TPFy88lf19v9jHUMO4SKEr7am', 'DAjbX3D7APKgHyZ61CkuoB3gylIRb8rRJD2ote38M6A1+04yJL/jG+PCL1UnMWdL', 'yJ4f4LzI9c4ksnGyl9neq0IHnA0Nlky6dmgmE+vLi6OCbEEs2v132wc5PIxRY+TW', '8JWu+3wUA4tj5uQvQRqU9/lmoHG/Jxubx/HwdD9Ri17G+qX8re5sySmmq7rcZEGJ', 'LVrlFuvA0NdoTM4AZY23iR6trJ/Ba2Q4pQk4SfOEMSoZJmf0UbxIP0Ez6Fb+Dxzk', 'WKXfI+D0ScuVjzV0bs8iXTrCcynztRKndNbtpd39hGAR0rNqvnHyQGYV75bWm5dS', '0S0PQ6DOzicLxjNXZFicQvwfieg9VyJikWLFLu4zAbzHnuoRk6b2KbSU4UCG/BCz', 'mHqz4y6GfsncsNkmFmsD5Gn9UrloWcEWgIDL05yIikL+L9DPLnNlSYtehDfxlhvh', 'xHzY/Rad4Nzxe62yXhSxhROLTXIolllyOFJgqZ4hBlXybBqJH7sZUll6PUpDwZdu', 'BK14pzMIpfxq2eYp8jI7fh4lU9YrkuSUM0Ewa7HfrltAgxMhHyaFjfINt61P9OlO', 's3nuBY17+KokaSWjACkCimVLH13H5DRhfX8OBRT4LeRMUspX3cyKbccwpOmoBf4y', 'WPM9QXw7nQy2hwnuX6NiK5QfeCGfY64M06J2tBGcCDmjPSIcJgMcyY7jfH9yPlDt', 'SKyyXpZnFOJplS2v28A/1csPSGy9kk/uGN0hfFULH4VvyAgNDYzmeOd8FvrbfHH2', '8BUTI/Tq2pckxwCYBWHcjSdXRAj5moCNSxCUMtK3kWFdxLFYzoiKuiZwq171qb5L', 'yCHMwNDIWEMeC75XSMswHaBsK6ON0UUg5oedQkOK+II9L/DVyTs3UYJOsWDfM67E', '312O9/bmsoHvr+rofF7HEc74dtUAcaDGJNyNiB+O4UmWbtEpCfuLmq2vaZa9J7Y0', 'hXlD2pcibC9CWpKR58cRL+dyYHZGJ4VKg6OHlJlF+JBPeLzObNDz/zQuEt9aL9Ae', 'QByamqGDGcaVMVZ/A80fRoUUgHbh3bLoAmxLCvMbJ0YMtRujdtGm8ZD0WvLXQA/U', 'dNmQ6tsP6pyVorWVa/Ma5CR7Em5q7M6639T8WPcu7ETTO19MnWud2lPJ5A==', '-----END MESSAGE-----', ]) UNSIGNED_DESCRIPTOR = u'\n'.join([ 'rendezvous-service-descriptor 6wgohrr64y2od75psnrfdkbc74ddqx2v', 'version 2', 'permanent-key', '-----BEGIN RSA PUBLIC KEY-----', 'MIGJAoGBANfM/oca2M9LLu4X06VjtmZ59NUpwUuyrSZITNCtbnBKI25hz52hJXY5', 'bE+D7V9V7X0RLjcBSSr9tL+CMAws66c/q70Vs8OiPKHVWz/pNWp+1Lew+SVDQi2u', 'tnADCG0cLRZoTKvBgk05IIGrfCm6l1CGlHJYkWozCb36IEIbVBwlAgMBAAE=', '-----END RSA PUBLIC KEY-----', 'secret-id-part udmoj3e2ykfp73kpvauoq4t4p7kkwsjq', 'publication-time 2015-06-25 11:00:00', 'protocol-versions 2,3', 'introduction-points', '-----BEGIN MESSAGE-----', 'AgEdbps604RR6lqeyoZBzOb6+HvlL2cDt63w8vBtyRaLirq5ZD5GDnr+R0ePj71C', 'nC7qmRWuwBmzSdSd0lOTaSApBvIifbJksHUeT/rq03dpnnRHdHSVqSvig6bukcWJ', 'LgJmrRd3ES13LXVHenD3C6AZMHuL9TG+MjLO2PIHu0mFO18aAHVnWY32Dmt144IY', 'c2eTVZbsKobjjwCYvDf0PBZI+B6H0PZWkDX/ykYjArpLDwydeZyp+Zwj4+k0+nRr', 'RPlzbHYoBY9pFYDUXDXWdL+vTsgFTG0EngLGlgUWSY5U1T1Db5HfOqc7hbqklgs/', 'ULG8NUY1k41Wb+dleJI28/+ZOM9zOpHcegNx4Cn8UGbw/Yv3Tj+yki+TMeOtJyhK', 'PQP8NWq8zThiVhBrfpmVjMYkNeVNyVNoxRwS6rxCQjoLWSJit2Mpf57zY1AOvT1S', 'EqqFbsX+slD2Uk67imALh4pMtjX29VLIujpum3drLhoTHDszBRhIH61A2eAZqdJy', '7JkJd1x/8x7U0l8xNWhnj/bhUHdt3OrCvlN+n8x6BwmMNoLF8JIsskTuGHOaAKSQ', 'WK3z0rHjgIrEjkQeuQtfmptiIgRB9LnNr+YahRnRR6XIOJGaIoVLVM2Uo2RG4MS1', '2KC3DRJ87WdMv2yNWha3w+lWt/mOALahYrvuNMU8wEuNXSi5yCo1OKirv+d5viGe', 'hAgVZjRymBQF+vd30zMdOG9qXNoQFUN49JfS8z5FjWmdHRt2MHlqD2isxoeabERY', 'T4Q50fFH8XHkRRomKBEbCwy/4t2DiqcTOSLGOSbTtf7qlUACp2bRth/g0ySAW8X/', 'CaWVm53z1vdgF2+t6j1CnuIqf0dUygZ07HEAHgu3rMW0YTk04QkvR3jiKAKijvGH', '3YcMJz1aJ7psWSsgiwn8a8Cs4fAcLNJcdTrnyxhQI4PMST/QLfp8nPYrhKEeifTc', 'vYkC4CtGuEFkWyRifIGbeD7FcjkL1zqVNu31vgo3EIVbHzylERgpgTIYBRv7aV7W', 'X7XAbrrgXL0zgpI0orOyPkr2KRs6CcoEqcc2MLyB6gJ5fYAm69Ige+6gWtRT6qvZ', 'tJXagfKZivLj73dRD6sUqTCX4tmgo7Q8WFSeNscDAVm/p4dVsw6SOoFcRgaH20yX', 'MBa3oLNTUNAaGbScUPx2Ja3MQS0UITwk0TFTF7hL++NhTvTp6IdgQW4DG+/bVJ3M', 'BRR+hsvSz5BSQQj2FUIAsJ+WoVK9ImbgsBbYxSH60jCvxTIdeh2IeUzS2T1bU9AU', 'jOLzcJZmNh95Nj2Qdrc8/0gin9KpgPmuPQ6CyH3TPFy88lf19v9jHUMO4SKEr7am', 'DAjbX3D7APKgHyZ61CkuoB3gylIRb8rRJD2ote38M6A1+04yJL/jG+PCL1UnMWdL', 'yJ4f4LzI9c4ksnGyl9neq0IHnA0Nlky6dmgmE+vLi6OCbEEs2v132wc5PIxRY+TW', '8JWu+3wUA4tj5uQvQRqU9/lmoHG/Jxubx/HwdD9Ri17G+qX8re5sySmmq7rcZEGJ', 'LVrlFuvA0NdoTM4AZY23iR6trJ/Ba2Q4pQk4SfOEMSoZJmf0UbxIP0Ez6Fb+Dxzk', 'WKXfI+D0ScuVjzV0bs8iXTrCcynztRKndNbtpd39hGAR0rNqvnHyQGYV75bWm5dS', '0S0PQ6DOzicLxjNXZFicQvwfieg9VyJikWLFLu4zAbzHnuoRk6b2KbSU4UCG/BCz', 'mHqz4y6GfsncsNkmFmsD5Gn9UrloWcEWgIDL05yIikL+L9DPLnNlSYtehDfxlhvh', 'xHzY/Rad4Nzxe62yXhSxhROLTXIolllyOFJgqZ4hBlXybBqJH7sZUll6PUpDwZdu', 'BK14pzMIpfxq2eYp8jI7fh4lU9YrkuSUM0Ewa7HfrltAgxMhHyaFjfINt61P9OlO', 's3nuBY17+KokaSWjACkCimVLH13H5DRhfX8OBRT4LeRMUspX3cyKbccwpOmoBf4y', 'WPM9QXw7nQy2hwnuX6NiK5QfeCGfY64M06J2tBGcCDmjPSIcJgMcyY7jfH9yPlDt', 'SKyyXpZnFOJplS2v28A/1csPSGy9kk/uGN0hfFULH4VvyAgNDYzmeOd8FvrbfHH2', '8BUTI/Tq2pckxwCYBWHcjSdXRAj5moCNSxCUMtK3kWFdxLFYzoiKuiZwq171qb5L', 'yCHMwNDIWEMeC75XSMswHaBsK6ON0UUg5oedQkOK+II9L/DVyTs3UYJOsWDfM67E', '312O9/bmsoHvr+rofF7HEc74dtUAcaDGJNyNiB+O4UmWbtEpCfuLmq2vaZa9J7Y0', 'hXlD2pcibC9CWpKR58cRL+dyYHZGJ4VKg6OHlJlF+JBPeLzObNDz/zQuEt9aL9Ae', 'QByamqGDGcaVMVZ/A80fRoUUgHbh3bLoAmxLCvMbJ0YMtRujdtGm8ZD0WvLXQA/U', 'dNmQ6tsP6pyVorWVa/Ma5CR7Em5q7M6639T8WPcu7ETTO19MnWud2lPJ5A==', '-----END MESSAGE-----', 'signature', '-----BEGIN SIGNATURE-----', 'VX4GC6s6zmY84mKsh+YdAqyZqDevJwGYr9yJntBNms4XRQHlgiW/JCspJzCqvrQG', 'N4Fh8XNTodQFnxz/kz8K3SBFlLnJHzKxSBTSZTLd8hRp84F/XxDcPaIPda8UJZuF', 'pOT8V0hfhgo8WxLpOyUzxrYugPB2GRkWYLhHaKhxkJY=', '-----END SIGNATURE-----', ]) SIGNED_DESCRIPTOR = u'\n'.join([ 'rendezvous-service-descriptor 6wgohrr64y2od75psnrfdkbc74ddqx2v', 'version 2', 'permanent-key', '-----BEGIN RSA PUBLIC KEY-----', 'MIGJAoGBANfM/oca2M9LLu4X06VjtmZ59NUpwUuyrSZITNCtbnBKI25hz52hJXY5', 'bE+D7V9V7X0RLjcBSSr9tL+CMAws66c/q70Vs8OiPKHVWz/pNWp+1Lew+SVDQi2u', 'tnADCG0cLRZoTKvBgk05IIGrfCm6l1CGlHJYkWozCb36IEIbVBwlAgMBAAE=', '-----END RSA PUBLIC KEY-----', 'secret-id-part udmoj3e2ykfp73kpvauoq4t4p7kkwsjq', 'publication-time 2015-06-25 11:00:00', 'protocol-versions 2,3', 'introduction-points', '-----BEGIN MESSAGE-----', 'AgEdbps604RR6lqeyoZBzOb6+HvlL2cDt63w8vBtyRaLirq5ZD5GDnr+R0ePj71C', 'nC7qmRWuwBmzSdSd0lOTaSApBvIifbJksHUeT/rq03dpnnRHdHSVqSvig6bukcWJ', 'LgJmrRd3ES13LXVHenD3C6AZMHuL9TG+MjLO2PIHu0mFO18aAHVnWY32Dmt144IY', 'c2eTVZbsKobjjwCYvDf0PBZI+B6H0PZWkDX/ykYjArpLDwydeZyp+Zwj4+k0+nRr', 'RPlzbHYoBY9pFYDUXDXWdL+vTsgFTG0EngLGlgUWSY5U1T1Db5HfOqc7hbqklgs/', 'ULG8NUY1k41Wb+dleJI28/+ZOM9zOpHcegNx4Cn8UGbw/Yv3Tj+yki+TMeOtJyhK', 'PQP8NWq8zThiVhBrfpmVjMYkNeVNyVNoxRwS6rxCQjoLWSJit2Mpf57zY1AOvT1S', 'EqqFbsX+slD2Uk67imALh4pMtjX29VLIujpum3drLhoTHDszBRhIH61A2eAZqdJy', '7JkJd1x/8x7U0l8xNWhnj/bhUHdt3OrCvlN+n8x6BwmMNoLF8JIsskTuGHOaAKSQ', 'WK3z0rHjgIrEjkQeuQtfmptiIgRB9LnNr+YahRnRR6XIOJGaIoVLVM2Uo2RG4MS1', '2KC3DRJ87WdMv2yNWha3w+lWt/mOALahYrvuNMU8wEuNXSi5yCo1OKirv+d5viGe', 'hAgVZjRymBQF+vd30zMdOG9qXNoQFUN49JfS8z5FjWmdHRt2MHlqD2isxoeabERY', 'T4Q50fFH8XHkRRomKBEbCwy/4t2DiqcTOSLGOSbTtf7qlUACp2bRth/g0ySAW8X/', 'CaWVm53z1vdgF2+t6j1CnuIqf0dUygZ07HEAHgu3rMW0YTk04QkvR3jiKAKijvGH', '3YcMJz1aJ7psWSsgiwn8a8Cs4fAcLNJcdTrnyxhQI4PMST/QLfp8nPYrhKEeifTc', 'vYkC4CtGuEFkWyRifIGbeD7FcjkL1zqVNu31vgo3EIVbHzylERgpgTIYBRv7aV7W', 'X7XAbrrgXL0zgpI0orOyPkr2KRs6CcoEqcc2MLyB6gJ5fYAm69Ige+6gWtRT6qvZ', 'tJXagfKZivLj73dRD6sUqTCX4tmgo7Q8WFSeNscDAVm/p4dVsw6SOoFcRgaH20yX', 'MBa3oLNTUNAaGbScUPx2Ja3MQS0UITwk0TFTF7hL++NhTvTp6IdgQW4DG+/bVJ3M', 'BRR+hsvSz5BSQQj2FUIAsJ+WoVK9ImbgsBbYxSH60jCvxTIdeh2IeUzS2T1bU9AU', 'jOLzcJZmNh95Nj2Qdrc8/0gin9KpgPmuPQ6CyH3TPFy88lf19v9jHUMO4SKEr7am', 'DAjbX3D7APKgHyZ61CkuoB3gylIRb8rRJD2ote38M6A1+04yJL/jG+PCL1UnMWdL', 'yJ4f4LzI9c4ksnGyl9neq0IHnA0Nlky6dmgmE+vLi6OCbEEs2v132wc5PIxRY+TW', '8JWu+3wUA4tj5uQvQRqU9/lmoHG/Jxubx/HwdD9Ri17G+qX8re5sySmmq7rcZEGJ', 'LVrlFuvA0NdoTM4AZY23iR6trJ/Ba2Q4pQk4SfOEMSoZJmf0UbxIP0Ez6Fb+Dxzk', 'WKXfI+D0ScuVjzV0bs8iXTrCcynztRKndNbtpd39hGAR0rNqvnHyQGYV75bWm5dS', '0S0PQ6DOzicLxjNXZFicQvwfieg9VyJikWLFLu4zAbzHnuoRk6b2KbSU4UCG/BCz', 'mHqz4y6GfsncsNkmFmsD5Gn9UrloWcEWgIDL05yIikL+L9DPLnNlSYtehDfxlhvh', 'xHzY/Rad4Nzxe62yXhSxhROLTXIolllyOFJgqZ4hBlXybBqJH7sZUll6PUpDwZdu', 'BK14pzMIpfxq2eYp8jI7fh4lU9YrkuSUM0Ewa7HfrltAgxMhHyaFjfINt61P9OlO', 's3nuBY17+KokaSWjACkCimVLH13H5DRhfX8OBRT4LeRMUspX3cyKbccwpOmoBf4y', 'WPM9QXw7nQy2hwnuX6NiK5QfeCGfY64M06J2tBGcCDmjPSIcJgMcyY7jfH9yPlDt', 'SKyyXpZnFOJplS2v28A/1csPSGy9kk/uGN0hfFULH4VvyAgNDYzmeOd8FvrbfHH2', '8BUTI/Tq2pckxwCYBWHcjSdXRAj5moCNSxCUMtK3kWFdxLFYzoiKuiZwq171qb5L', 'yCHMwNDIWEMeC75XSMswHaBsK6ON0UUg5oedQkOK+II9L/DVyTs3UYJOsWDfM67E', '312O9/bmsoHvr+rofF7HEc74dtUAcaDGJNyNiB+O4UmWbtEpCfuLmq2vaZa9J7Y0', 'hXlD2pcibC9CWpKR58cRL+dyYHZGJ4VKg6OHlJlF+JBPeLzObNDz/zQuEt9aL9Ae', 'QByamqGDGcaVMVZ/A80fRoUUgHbh3bLoAmxLCvMbJ0YMtRujdtGm8ZD0WvLXQA/U', 'dNmQ6tsP6pyVorWVa/Ma5CR7Em5q7M6639T8WPcu7ETTO19MnWud2lPJ5A==', '-----END MESSAGE-----', 'signature', '-----BEGIN SIGNATURE-----', 'VX4GC6s6zmY84mKsh+YdAqyZqDevJwGYr9yJntBNms4XRQHlgiW/JCspJzCqvrQG', 'N4Fh8XNTodQFnxz/kz8K3SBFlLnJHzKxSBTSZTLd8hRp84F/XxDcPaIPda8UJZuF', 'pOT8V0hfhgo8WxLpOyUzxrYugPB2GRkWYLhHaKhxkJY=', '-----END SIGNATURE-----', ]) PRIVATE_KEY = Cryptodome.PublicKey.RSA.importKey(PEM_PRIVATE_KEY) UNIX_TIMESTAMP = 1435233021 """ TODO: Reenable test that fails with Pytest3 @pytest.mark.parametrize('intro_point_distribution, selected_ip_count', [ ([3], 3), ([3, 3], 6), ([0], 0), ([10, 10], 10), ([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], 10), ([10, 10, 10, 10, 10, 10], 10), pytest.mark.xfail(([0, 3, 3], 10)), pytest.mark.xfail(([6, 3, 3], 12)), ]) def test_introduction_point_selection(intro_point_distribution, selected_ip_count): # Basic test case to check that the correct number of IPs are selected. # Create Mock list of instances (index by letter) and their respective # introduction points. available_intro_points = [[index] * count for index, count in zip(string.ascii_lowercase, intro_point_distribution)] intro_set = intro_point_set.IntroductionPointSet(available_intro_points) # Check that we can fetch the same number for each descriptor for i in range(0, 2): # Max 10 introduction points per descriptor choosen_intro_points = intro_set.choose(10) assert len(choosen_intro_points) == selected_ip_count """ def test_generate_service_descriptor(monkeypatch, mocker): """ Test creation of a fully signed hidden service descriptor """ # Mock the datetime function to return a constant timestamp class frozen_datetime(datetime.datetime): @classmethod def utcnow(cls): return datetime.datetime.utcfromtimestamp(UNIX_TIMESTAMP) monkeypatch.setattr(datetime, 'datetime', frozen_datetime) # Patch make_introduction_points_part to return the test introduction # point section mocker.patch('onionbalance.hs_v2.descriptor.make_introduction_points_part', lambda *_: INTRODUCTION_POINT_PART) # Test basic descriptor generation. signed_descriptor = descriptor.generate_service_descriptor( PRIVATE_KEY, introduction_point_list=['mocked-ip-list'], ).encode('utf-8') stem.descriptor.hidden_service_descriptor.\ HiddenServiceDescriptor(signed_descriptor, validate=True) assert (hashlib.sha1(signed_descriptor).hexdigest() == 'df4f4a7a15492205f073c32cbcfc4eb9511e4ad8') # Test descriptor generation with specified timestamp signed_descriptor = descriptor.generate_service_descriptor( PRIVATE_KEY, introduction_point_list=['mocked-ip-list'], timestamp=datetime.datetime.utcfromtimestamp(UNIX_TIMESTAMP), ).encode('utf-8') stem.descriptor.hidden_service_descriptor.\ HiddenServiceDescriptor(signed_descriptor, validate=True) assert (hashlib.sha1(signed_descriptor).hexdigest() == 'df4f4a7a15492205f073c32cbcfc4eb9511e4ad8') # Test descriptor for deviation and replica 1 signed_descriptor = descriptor.generate_service_descriptor( PRIVATE_KEY, introduction_point_list=['mocked-ip-list'], replica=1, deviation=24*60*60, ).encode('utf-8') stem.descriptor.hidden_service_descriptor.\ HiddenServiceDescriptor(signed_descriptor, validate=True) assert (hashlib.sha1(signed_descriptor).hexdigest() == 'd828140cdccb1165dbc5a4b39622fcb45e6438fb') def test_generate_service_descriptor_no_intros(): with pytest.raises(ValueError): descriptor.generate_service_descriptor( PRIVATE_KEY, introduction_point_list=[], ) def test_make_public_key_block(): """ Test generation of ASN.1 representation of public key """ public_key_block = descriptor.make_public_key_block(PRIVATE_KEY) assert (hashlib.sha1(public_key_block.encode('utf-8')).hexdigest() == '2cf75da5e1a198ca7cb3db7b0baa6708feaf26e8') def test_sign_descriptor(): """ Test signing a descriptor """ # Test signing an unsigned descriptor signed_descriptor = descriptor.sign_descriptor( UNSIGNED_DESCRIPTOR, PRIVATE_KEY).encode('utf-8') stem.descriptor.hidden_service_descriptor.\ HiddenServiceDescriptor(signed_descriptor, validate=True) assert (hashlib.sha1(signed_descriptor).hexdigest() == 'df4f4a7a15492205f073c32cbcfc4eb9511e4ad8') # Test resigning a previously signed descriptor signed_descriptor = descriptor.sign_descriptor( SIGNED_DESCRIPTOR, PRIVATE_KEY).encode('utf-8') stem.descriptor.hidden_service_descriptor.\ HiddenServiceDescriptor(signed_descriptor, validate=True) assert (hashlib.sha1(signed_descriptor).hexdigest() == 'df4f4a7a15492205f073c32cbcfc4eb9511e4ad8') def test_descriptor_received_invalid_descriptor(mocker): """ Test invalid descriptor content received from the HSDir """ mocker.patch("onionbalance.hs_v2.descriptor.logger.exception", side_effect=ValueError('InvalidDescriptorException')) # Check that the invalid descriptor error is logged. with pytest.raises(ValueError): descriptor.descriptor_received(u'not-a-valid-descriptor-input') assert descriptor.logger.exception.call_count == 1 onionbalance-0.2.2/test/v2/test_settings.py000066400000000000000000000027201411742520300207220ustar00rootroot00000000000000# -*- coding: utf-8 -*- import io import os import pytest from onionbalance.hs_v2 import settings from .util import builtin CONFIG_FILE_VALID = u'\n'.join([ "services:", " - key: private.key", " instances:", " - address: fqyw6ojo2voercr7", " - address: facebookcorewwwi", ]) CONFIG_FILE_ABSOLUTE = u'\n'.join([ "services:", " - key: /absdir/private.key", " instances:", " - address: fqyw6ojo2voercr7", " - address: facebookcorewwwi", ]) def test_parse_config_file_valid(mocker): # Patch config file read mocker.patch('os.path.exists', return_value=True) mocker.patch(builtin('open'), lambda *_: io.StringIO(CONFIG_FILE_VALID)) parsed_config = settings.parse_config_file('/configdir/config_rel.yaml') assert len(parsed_config['services']) == 1 assert len(parsed_config['services'][0]['instances']) == 2 # Test key with absolute path assert os.path.dirname(parsed_config['services'][0]['key']) == '/configdir' # Test key with absolute path mocker.patch(builtin('open'), lambda *_: io.StringIO(CONFIG_FILE_ABSOLUTE)) parsed_config = settings.parse_config_file('/configdir/config_abs.yaml') assert os.path.dirname(parsed_config['services'][0]['key']) == '/absdir' def test_parse_config_file_does_not_exist(mocker): with pytest.raises(SystemExit): settings.parse_config_file('doesnotexist/config.yaml') onionbalance-0.2.2/test/v2/test_util.py000066400000000000000000000252031411742520300200400ustar00rootroot00000000000000# -*- coding: utf-8 -*- from binascii import hexlify, unhexlify import base64 import datetime import io import sys import Cryptodome.PublicKey.RSA import pytest from .util import builtin from onionbalance.hs_v2.util import * from onionbalance.hs_v2.descriptor import pad_msg_with_tor_pkcs PEM_PRIVATE_KEY = u'\n'.join([ "-----BEGIN RSA PRIVATE KEY-----", "MIICWwIBAAKBgQDXzP6HGtjPSy7uF9OlY7ZmefTVKcFLsq0mSEzQrW5wSiNuYc+d", "oSV2OWxPg+1fVe19ES43AUkq/bS/gjAMLOunP6u9FbPDojyh1Vs/6TVqftS3sPkl", "Q0ItrrZwAwhtHC0WaEyrwYJNOSCBq3wpupdQhpRyWJFqMwm9+iBCG1QcJQIDAQAB", "AoGAegc2Sqm4vgdyozof+R8Ybnw6ISu6XRbNaJ9rqHjZwW9695khsK4GJAM2pwQf", "/0/0ukszyfDVMhVC1yREDS59lgzNecItd6nQZWbwr9TFxIoa9ouTqk8PcAoNixTb", "wafjPcMmWGakizXeAHiOfazPBH4x2keDQCulxfYxXZxTpyECQQDqZu61kd1S3U7T", "BT2NQBd3tHX0Hvonx+IkOKXwpHFY0Mo4d32Bi+MxRuEnd3tO44AaMvlkl13QMTF2", "kHFSC70dAkEA669LZavGjW67+rO+f+xyDVby9pD5GJQBb78xRCf93Zcu2KW4NSp3", "XC4p4eWfLgff1VuXL7g0VdFm4wUUHqYUqQJAZLmqpjdyBeO3tZIw6vu5meTgMvEE", "ygdos+vr0sa3NlUyMKWYNwznqgstQYpkYHf+WkPBS2qIE6iv+qUDLSCCOQJAESSk", "CFYxUBJQ7BBs9+Mb/Kppa9Ppuobxf85ZaAq8pYScrLeJKZzYJ8VX2I2aQX/jISLT", "YW41qFRd9n9lEkGkWQJAcxPmNI+2r5zJG+K148LLmWCIDTVZ4nxOcxffHka/3tCJ", "lDGUw4p2wU6pVRDpNfKrF5Nc9ZKO8NAtC17ZvDyVkQ==", "-----END RSA PRIVATE KEY-----", ]) PEM_INVALID_KEY = u'\n'.join([ "-----BEGIN RSA PRIVATE KEY-----", "MIICWwIBAAKBgQDXzP6HGtjPSy7uF9OlY7ZmefTVKcFLsq0mSEzQrW5wSiNuYc+d", "oSV2OWxPg+1fVe19ES43AUkq/bS/gjAMLOunP6u9FbPDojyh1Vs/6TVqftS3sPkl", "Q0ItrrZwAwhtHC0WaEyrwYJNOSCBq3wpupdQhpRyWJFqMwm9+iBCG1QcJQIDAQAB", "AoGAegc2Sqm4vgdyozof+R8Ybnw6ISu6XRbNaJ9rqHjZwW9695khsK4GJAM2pwQf", "/0/0ukszyfDVMhVC1yREDS59lgzNecItd6nQZWbwr9TFxIoa9ouTqk8PcAoNixTb", "wafjPcMmWGakizXeAHiOfazPBH4x2keDQCulxfYxXZxTpyECQQDqZu61kd1S3U7T", "BT2NQBd3t This is an invalid key lkl13QMTF2", "kHFSC70dAkEA669LZavGjW67+rO+f+xyDVby9pD5GJQBb78xRCf93Zcu2KW4NSp3", "XC4p4eWfLgff1VuXL7g0VdFm4wUUHqYUqQJAZLmqpjdyBeO3tZIw6vu5meTgMvEE", "ygdos+vr0sa3NlUyMKWYNwznqgstQYpkYHf+WkPBS2qIE6iv+qUDLSCCOQJAESSk", "CFYxUBJQ7BBs9+Mb/Kppa9Ppuobxf85ZaAq8pYScrLeJKZzYJ8VX2I2aQX/jISLT", "YW41qFRd9n9lEkGkWQJAcxPmNI+2r5zJG+K148LLmWCIDTVZ4nxOcxffHka/3tCJ", "lDGUw4p2wU6pVRDpNfKrF5Nc9ZKO8NAtC17ZvDyVkQ==", "-----END RSA PRIVATE KEY-----", ]) # Private key encrypted with the password 'password' PEM_ENCRYPTED = u'\n'.join([ "-----BEGIN RSA PRIVATE KEY-----", "Proc-Type: 4,ENCRYPTED", "DEK-Info: DES-EDE3-CBC,7CB7069233655F1A", "", "EpKWFhHefxQLlKS1M6fPXLUVW0gcrHwYNd2q/0J4emhrHmO50KTC6/nVGTvYS1VC", "XQwzlla04Ed7kAuP7nkbvT+/6fS72iZmIO/kuhihjaMmRV+peznjEroErndRzWko", "LCpe70/yMrHhULGR1lLINe+dZddESfYRoGEM1IYhPEEchXZBdqThvaThgeyVmoAV", "A5qhBOP4QFPSV4J0Jd28wTy+uPmGgCjvfvXjx4JZ2LAfPnLXOoKotRqb/cOtMapp", "9EmsvjRZH3OLreeQm1BmVzcXGgHLIZWmybGNAW/M0seqeD+NRPXEACOBahXZsSwd", "krnWALTkcfLw4NXgaHKdsogDV7gWlwkXr05CrSim0+zvg+hQpVp6Phg9qrT3Jh8g", "988v4Fx/rlVdEpfEeXAmLUpXH3jjeyU1ZOyi8c91Vobxe1dJ9G9P8YBBqnZo1xDa", "q89FR852v2DKR3xv+GRpzFM43NlWLck9DcNcqIUpbrGd0qRA1k87ZwYSiUPhBvtJ", "dix6XfeqbqVMYiH0K4sEyuXxJ98UqFzNY3bBi9oqvoQWpo0qrRYAzHrmDg/hJbO6", "aw8yhe922zw8W9+IQIy2j+ZKkaHSMKqjkIwFxmig5EA+mHNDIP4HwlCxA2e2w6HG", "ykLE01aHMeS72qRdLVwjib4q2iTEXZVnuyFg/wVprLmLY512iWr03kbj2CVN836b", "vEpVIvSj5W6oNXjm+hkKA1AMcHVK96y8Ms3BtarDe4tQDh7GjipkoSXrv+2lIl0o", "XjumCv4Gs63Fv3kUr+jo9N3P0SGe1GggX6MOYIcZF0I=", "-----END RSA PRIVATE KEY-----", ]) PRIVATE_KEY = Cryptodome.PublicKey.RSA.importKey(PEM_PRIVATE_KEY) UNIX_TIMESTAMP = 1435229421 def test_add_pkcs1_padding(): hasher = Cryptodome.Hash.SHA1.new(b"got me thinking") padded_message = pad_msg_with_tor_pkcs(hasher, 128) """ $ echo -n "got me thinking"| sha1sum 65a4a767b502df42594acf2de0a0cee109ba338e - """ assert len(padded_message) == 128 assert (padded_message == unhexlify( b'0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' b'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' b'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' b'ffffffffffffffffffffff0065a4a767b502df42594acf2de0a0cee109ba338e' )) def test_get_asn1_sequence(): asn1_sequence = get_asn1_sequence(PRIVATE_KEY) assert (asn1_sequence == unhexlify( b'30818902818100d7ccfe871ad8cf4b2eee17d3a563b66679f4d529c14bb2ad26' b'484cd0ad6e704a236e61cf9da12576396c4f83ed5f55ed7d112e3701492afdb4' b'bf82300c2ceba73fabbd15b3c3a23ca1d55b3fe9356a7ed4b7b0f92543422dae' b'b67003086d1c2d16684cabc1824d392081ab7c29ba975086947258916a3309bd' b'fa20421b541c250203010001' )) def test_calc_key_digest(): key_digest = calc_key_digest(PRIVATE_KEY) assert hexlify(key_digest) == b'4e2a58768ccb6aa06f95e11646e187879d07fb66' def test_calc_public_key_digest(): public_key = PRIVATE_KEY.publickey() key_digest = calc_key_digest(public_key) assert hexlify(key_digest) == b'4e2a58768ccb6aa06f95e11646e187879d07fb66' def test_calc_permanent_id(): assert hexlify(calc_permanent_id(PRIVATE_KEY)) == b'4e2a58768ccb6aa06f95' def test_calc_onion_address(): assert calc_onion_address(PRIVATE_KEY) == u'jyvfq5umznvka34v' def test_get_time_period(): time_period = get_time_period( time=UNIX_TIMESTAMP, permanent_id=unhexlify(b'4e2a58768ccb6aa06f95'), ) assert time_period == 16611 def test_get_seconds_valid(): seconds_valid = get_seconds_valid( time=UNIX_TIMESTAMP, permanent_id=unhexlify(b'4e2a58768ccb6aa06f95'), ) assert seconds_valid == 21054 def test_calc_secret_id_part(): secret_id_part = calc_secret_id_part( time_period=16611, descriptor_cookie=None, replica=0, ) assert (hexlify(secret_id_part) == b'a0d8e4ec9ac28affed4fa828e8727c7fd4ab4930') def test_calc_secret_id_part_descriptor_cookie(): secret_id_part = calc_secret_id_part( time_period=16611, descriptor_cookie=base64.b64decode('dCmx3qIvArbil8A0KM4KgQ=='), replica=0, ) assert (hexlify(secret_id_part) == b'ea4e24b1a832f1da687f874b40fa9ecfe5221dd9') def test_calc_descriptor_id(): descriptor_id = calc_descriptor_id( permanent_id=b'N*Xv\x8c\xcbj\xa0o\x95', secret_id_part=unhexlify(b'a0d8e4ec9ac28affed4fa828e8727c7fd4ab4930'), ) assert (hexlify(descriptor_id) == b'f58ce3c63ee634e1ffaf936251a822ff06385f55') def test_calc_descriptor_id_full(): descriptor_id = calc_descriptor_id_b32( onion_address='jyvfq5umznvka34v', time=UNIX_TIMESTAMP, replica=0) assert descriptor_id == '6wgohrr64y2od75psnrfdkbc74ddqx2v' def test_calc_descriptor_id_full_replica(): descriptor_id = calc_descriptor_id_b32( onion_address='jyvfq5umznvka34v', time=UNIX_TIMESTAMP, replica=1) assert descriptor_id == 'he35m4nouhkz6thymvhdvc3y5htqs422' def test_calc_descriptor_id_full_with_deviation(): descriptor_id = calc_descriptor_id_b32( onion_address='jyvfq5umznvka34v', time=UNIX_TIMESTAMP, replica=0, deviation=1) assert descriptor_id == 'esnnz2q6dnfwprvc4qhsgsfzz6r6ksrt' def test_rounded_timestamp(): timestamp = datetime.datetime(2015, 6, 25, 13, 13, 25) assert rounded_timestamp(timestamp) == u'2015-06-25 13:00:00' def test_rounded_timestamp_none_specified(monkeypatch): # Freeze datetime returned from datetime.datetime.utcnow() class frozen_datetime(datetime.datetime): @classmethod def utcnow(cls): return datetime.datetime(2015, 6, 25, 13, 13, 25) monkeypatch.setattr(datetime, 'datetime', frozen_datetime) assert rounded_timestamp(timestamp=None) == u'2015-06-25 13:00:00' def test_base32_encode_str(): assert base32_encode_str(byte_str=b'byte input') == u'mj4xizjanfxha5lu' @pytest.mark.skipif(sys.version_info < (3, 0), reason="python3 only") def test_base32_encode_str_not_byte_string(): with pytest.raises(TypeError): base32_encode_str(byte_str=u'not a byte string') def test_key_decrypt_prompt(mocker): # Valid private PEM key mocker.patch(builtin('open'), lambda *_: io.StringIO(PEM_PRIVATE_KEY)) key = key_decrypt_prompt('private.key') assert isinstance(key, Cryptodome.PublicKey.RSA.RsaKey) assert key.has_private() def test_key_decrypt_prompt_public_key(mocker): # Valid public PEM key private_key = Cryptodome.PublicKey.RSA.importKey(PEM_PRIVATE_KEY) pem_public_key = private_key.publickey().exportKey().decode('utf-8') mocker.patch(builtin('open'), lambda *_: io.StringIO(pem_public_key)) with pytest.raises(ValueError): key_decrypt_prompt('public.key') def test_key_decrypt_prompt_malformed_key(mocker): mocker.patch(builtin('open'), lambda *_: io.StringIO(PEM_INVALID_KEY)) with pytest.raises(ValueError): key_decrypt_prompt('private.key') def test_key_decrypt_prompt_incorrect_size(mocker): # Key which is not 1024 bits private_key_1280 = Cryptodome.PublicKey.RSA.generate(1280) pem_key_1280 = private_key_1280.exportKey().decode('utf-8') mocker.patch(builtin('open'), lambda *_: io.StringIO(pem_key_1280)) with pytest.raises(ValueError): key_decrypt_prompt('512-bit-private.key') def test_key_decrypt_prompt_encrypted(mocker): mocker.patch(builtin('open'), lambda *_: io.StringIO(PEM_ENCRYPTED)) # Load with correct password mocker.patch('getpass.getpass', lambda *_: u'password') key = key_decrypt_prompt('encrypted_private.key') assert isinstance(key, Cryptodome.PublicKey.RSA.RsaKey) # Load with incorrect password mocker.patch('getpass.getpass', lambda *_: u'incorrect password') with pytest.raises(ValueError): key_decrypt_prompt('encrypted_private.key') def test_try_make_dir_makedirs(mocker): mocker.patch('os.makedirs') try_make_dir('dir') os.makedirs.assert_called_once_with('dir') def test_try_make_dir_makedirs_dir_already_exists(mocker): mocker.patch('os.makedirs', side_effect=OSError) mocker.patch('os.path.isdir', return_value=True) try_make_dir('dir') os.path.isdir.assert_called_once_with('dir') def test_try_make_dir_makedirs_dir_other_error(mocker): mocker.patch('os.makedirs', side_effect=OSError) mocker.patch('os.path.isdir', return_value=False) with pytest.raises(OSError): try_make_dir('dir') def test_is_directory_empty_empty(mocker): # Directory is empty mocker.patch('os.listdir', return_value=[]) assert is_directory_empty('dir_empty/') def test_is_directory_empty_not_empty(mocker): # Directory is empty mocker.patch('os.listdir', return_value=['filename']) assert not is_directory_empty('dir_not_empty/') onionbalance-0.2.2/test/v2/util.py000066400000000000000000000004231411742520300167760ustar00rootroot00000000000000# -*- coding: utf-8 -*- import sys def builtin(name): """ Provide the correct import name for builtins on Python 2 or Python 3 """ if sys.version_info >= (3,): return 'builtins.{}'.format(name) else: return '__builtin__.{}'.format(name) onionbalance-0.2.2/test/v3/000077500000000000000000000000001411742520300154515ustar00rootroot00000000000000onionbalance-0.2.2/test/v3/test_v3_descriptor.py000066400000000000000000000017261411742520300216560ustar00rootroot00000000000000from onionbalance.hs_v3 import consensus def test_disaster_srv(): """ Test that disaster SRV creation is correct based on little-t-tor's unittests as test vectors (in particular see test_disaster_srv()) """ my_consensus = consensus.Consensus(do_refresh_consensus=False) # Correct disaster SRV for TPs: 1, 2, 3, 4, 5 correct_srvs = [ "F8A4948707653837FA44ABB5BBC75A12F6F101E7F8FAF699B9715F4965D3507D", "C17966DF8B4834638E1B7BF38944C4995B92E89749D1623E417A44938D08FD67", "A3BAB4327F9C2F8B30A126DAD3ABCCE12DD813169A5D924244B57987AEE413C2", "C79ABE7116FCF7810F7A5C76198B97339990C738B456EFDBC1399189927BADEC", "941D5FE4289FBF2F853766EAC4B948B51C81A4137A44516342ABC80518E0183D"] for i in range(1,6): disaster_srv = my_consensus._get_disaster_srv(i) assert(disaster_srv.hex().upper() == correct_srvs[i-1]) if __name__ == '__main__': unittest.main() onionbalance-0.2.2/test/v3/test_v3_hashring.py000066400000000000000000000073371411742520300213070ustar00rootroot00000000000000import unittest import mock import datetime import base64 from cryptography.hazmat.primitives.asymmetric import ed25519 from onionbalance.hs_v3 import tor_node from onionbalance.hs_v3 import hashring from onionbalance.hs_v3 import consensus CORRECT_HSDIR_FPRS_FIRST_DESCRIPTOR = [ "D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1", "2F2F2F2F2F2F2F2F2F2F2F2F2F2F2F2F2F2F2F2F", "B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0", "3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A", "5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A", "DFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDF", "F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7", "3434343434343434343434343434343434343434" ] CORRECT_HSDIR_FPRS_SECOND_DESCRIPTOR = [ "5D5D5D5D5D5D5D5D5D5D5D5D5D5D5D5D5D5D5D5D", "9A9A9A9A9A9A9A9A9A9A9A9A9A9A9A9A9A9A9A9A", "D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1", "7A7A7A7A7A7A7A7A7A7A7A7A7A7A7A7A7A7A7A7A", "C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3", "C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6", "E9E9E9E9E9E9E9E9E9E9E9E9E9E9E9E9E9E9E9E9", "8686868686868686868686868686868686868686" ] class DummyConsensus(consensus.Consensus): def __init__(self): self.consensus = None class TestHashRing(unittest.TestCase): def test_hashring(self): current_time = datetime.datetime.fromtimestamp(10101010101) current_srv = bytes([41])*32 previous_srv = bytes([42])*32 # Create 255 fake Tor nodes that will be used as part of the unittest network_nodes = [] for i in range(1,256): microdescriptor = mock.Mock() routerstatus = mock.Mock() routerstatus.fingerprint = (bytes([i])*20).hex() routerstatus.protocols = {'HSDir' : [2]} routerstatus.flags = ['HSDir'] node_ed25519_id_b64 = base64.b64encode(bytes([i])*32).decode('utf-8') microdescriptor.identifiers = {'ed25519' : node_ed25519_id_b64} node = tor_node.Node(microdescriptor, routerstatus) network_nodes.append(node) # Mock a fake consensus consensus = DummyConsensus() consensus.consensus = mock.Mock() consensus.consensus.valid_after = current_time consensus.get_current_srv = mock.Mock() consensus.get_current_srv.return_value = current_srv consensus.get_previous_srv = mock.Mock() consensus.get_previous_srv.return_value = previous_srv consensus.is_live = mock.Mock() consensus.is_live.return_value = True consensus.nodes = network_nodes # Mock a fake Tor network from onionbalance.hs_v3.onionbalance import my_onionbalance my_onionbalance.consensus = consensus previous_blinded_pubkey_hex = "063AEC5E1FD3025098F2DF71EF570B28D94B463FFCCB5EC6A9C061E38F551C6A" previous_blinded_pubkey_bytes = base64.b16decode(previous_blinded_pubkey_hex) responsible_hsdirs = hashring.get_responsible_hsdirs(previous_blinded_pubkey_bytes, True) i = 0 for responsible_hsdir in responsible_hsdirs: self.assertEqual(responsible_hsdir.upper(), CORRECT_HSDIR_FPRS_FIRST_DESCRIPTOR[i]) i+=1 print("===") # we need to use the new blinded key since this uses a new time period......... current_blinded_pubkey_hex = "5DB624F2D74F103E6E8C6FBCCD074586EF5A5572F90673C00B77DEF94EC11499" current_blinded_pubkey_bytes = base64.b16decode(current_blinded_pubkey_hex) responsible_hsdirs = hashring.get_responsible_hsdirs(current_blinded_pubkey_bytes, False) i = 0 for responsible_hsdir in responsible_hsdirs: self.assertEqual(responsible_hsdir.upper(), CORRECT_HSDIR_FPRS_SECOND_DESCRIPTOR[i]) i+=1 if __name__ == '__main__': unittest.main() onionbalance-0.2.2/test/v3/test_v3_keys.py000066400000000000000000000043221411742520300204460ustar00rootroot00000000000000import unittest import binascii from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey import stem.util from stem.descriptor.hidden_service import HiddenServiceDescriptorV3 from onionbalance.hs_v3 import tor_ed25519 ONION_ADDR = "4a2zwxetkje5lwvahfv75arzrctn6bznkwmosvfyobmyv2fc3idbpwyd.onion" PRIVKEY_FILE_HEX = "3d3d206564323535313976312d7365637265743a207479706530203d3d000000a0c632db831865c9562c5eca9446c50d1c6a88d9985e372986c9ae800a7f347a93bd4a97b9b18adf2ad9bceee4d167bf3d61362440066c22719d3cb5d1f97011" class TestKeys(unittest.TestCase): def test_load_tor_privkey(self): privkey_bytes = binascii.unhexlify(PRIVKEY_FILE_HEX) privkey = tor_ed25519.load_tor_key_from_disk(privkey_bytes) pubkey = privkey.public_key() # Make sure that the fake instances are right self.assertTrue(isinstance(privkey, Ed25519PrivateKey)) self.assertTrue(isinstance(pubkey, Ed25519PublicKey)) # Make sure that the public key matches the onion address onion_addr_pubkey_bytes = HiddenServiceDescriptorV3.identity_key_from_address(ONION_ADDR) self.assertEqual(onion_addr_pubkey_bytes, pubkey.public_bytes()) # Check that signature verification works msg = b"07-04-2020 weird days" msg_sig = privkey.sign(msg) onion_addr_pubkey = tor_ed25519.TorEd25519PublicKey(onion_addr_pubkey_bytes) onion_addr_pubkey.verify(msg_sig, msg) # Now check that it won't just verify any message self.assertRaises(Exception, onion_addr_pubkey.verify, msg_sig, b"another message another day") # Now check that stem will accept this self.assertEqual(stem.util._pubkey_bytes(privkey), pubkey.public_bytes()) self.assertEqual(stem.util._pubkey_bytes(pubkey), pubkey.public_bytes()) # Now check that blinding can work blinded_key_bytes = stem.descriptor.hidden_service._blinded_pubkey(privkey, b"a"*32) blinded_key = tor_ed25519.TorEd25519PublicKey(blinded_key_bytes) signature = tor_ed25519._blinded_sign_with_tor_key(b"haha", privkey, blinded_key_bytes, b"a"*32) blinded_key.verify(signature, b"haha") if __name__ == '__main__': unittest.main() onionbalance-0.2.2/test/v3/test_v3_onionbalance.py000066400000000000000000000061531411742520300221270ustar00rootroot00000000000000import datetime import unittest import mock from types import SimpleNamespace from onionbalance.hs_v3 import consensus from onionbalance.hs_v3.onionbalance import Onionbalance class DummyConsensus(consensus.Consensus): def __init__(self): self.consensus = None class OutdatedConsensus(unittest.TestCase): def test_outdated_consensus(self): current_time = datetime.datetime.fromtimestamp(10101010101) consensus = DummyConsensus() consensus.consensus = mock.Mock() with mock.patch("datetime.datetime") as mock_datetime: consensus.consensus.valid_after = current_time # valid_until is 3 hours in the future consensus.consensus.valid_until = current_time + datetime.timedelta(seconds=3600 * 3) # Test some legitimate cases mock_datetime.utcnow.return_value = current_time self.assertTrue(consensus.is_live()) mock_datetime.utcnow.return_value = current_time + datetime.timedelta(seconds=3600 * 11) self.assertTrue(consensus.is_live()) mock_datetime.utcnow.return_value = current_time + datetime.timedelta(seconds=3600 * 12) self.assertTrue(consensus.is_live()) mock_datetime.utcnow.return_value = current_time + datetime.timedelta(seconds=3600 * 24) self.assertTrue(consensus.is_live()) mock_datetime.utcnow.return_value = current_time - datetime.timedelta(seconds=3600 * 24) self.assertTrue(consensus.is_live()) # Now test some bad cases. The is_live() function is lenient up to 24 # hours after the valid_until, or 24 hours before the valid_after mock_datetime.utcnow.return_value = consensus.consensus.valid_until + datetime.timedelta(seconds=3600 * 24 + 1) self.assertFalse(consensus.is_live()) mock_datetime.utcnow.return_value = consensus.consensus.valid_after - datetime.timedelta(seconds=3600 * 24 + 1) self.assertFalse(consensus.is_live()) class TestReloadConfig(unittest.TestCase): @mock.patch('onionbalance.hs_v3.service.OnionbalanceService') @mock.patch('onionbalance.hs_v3.consensus.Consensus') @mock.patch('onionbalance.hs_v3.stem_controller.StemController') @mock.patch('onionbalance.hs_v3.onionbalance.Onionbalance.load_config_file') @mock.patch('onionbalance.hs_v3.manager.init_scheduler') def test_reload_config(self, mock_init_scheduler, mock_load_config_file, mock_StemController, mock_Consensus, mock_OnionbalanceService): # setup onionbalance instance test_onionbalance = Onionbalance() test_onionbalance.args = self.create_dummy_args() # checks if Onionbalance.load_config_file and manager.init_scheduler have been called by reload_config test_onionbalance.reload_config() mock_load_config_file.assert_called_once() mock_init_scheduler.assert_called_once() @staticmethod def create_dummy_args(): return SimpleNamespace(config='config/config.yaml', hs_version='v3', ip='127.0.0.1', is_testnet=False, port=6666, socket='/var/run/tor/control', verbosity='info') onionbalance-0.2.2/test/v3/test_v3_status.py000066400000000000000000000101701411742520300210140ustar00rootroot00000000000000import os import unittest # import mock from datetime import datetime import yaml from onionbalance.hs_v3.manager import status_socket_location from onionbalance.hs_v3.status import StatusSocketHandlerMixin class DummyDescriptor(object): pass class DummyInstance(object): pass class DummyService(object): def __init__(self): self.instances = [] class TestStatus(unittest.TestCase): def test_status(self): # Mock a fake Tor network from onionbalance.hs_v3.onionbalance import my_onionbalance time_format = "%Y-%m-%d %H:%M:%S" service = DummyService() service.onion_address = 'bvy46sg2b5dokczabwv2pabqlrps3lppweyrebhat6gjieo2avojdvad.onion' service.first_descriptor = None service.second_descriptor = None my_onionbalance.services = [service] status = StatusSocketHandlerMixin(my_onionbalance) self.assertEqual(status._outputString(), '{"services": [{"instances": [], "onionAddress": "bvy46sg2b5dokczabwv2pabqlrps3lppweyrebhat6gjieo2avojdvad.onion", "publishAttemptFirstDescriptor": null, "publishAttemptSecondDescriptor": null}]}') service.first_descriptor = DummyDescriptor() service.first_descriptor.last_publish_attempt_ts = datetime.strptime('2020-06-16 20:00:12', time_format) service.first_descriptor.last_upload_ts = datetime.strptime('2020-06-16 20:00:13', time_format) service.second_descriptor = DummyDescriptor() service.second_descriptor.last_publish_attempt_ts = datetime.strptime('2020-06-16 20:00:14', time_format) service.second_descriptor.last_upload_ts = datetime.strptime('2020-06-16 20:00:15', time_format) self.assertEqual(status._outputString(), '{"services": [{"instances": [], "onionAddress": "bvy46sg2b5dokczabwv2pabqlrps3lppweyrebhat6gjieo2avojdvad.onion", "publishAttemptFirstDescriptor": "2020-06-16 20:00:12", "publishAttemptSecondDescriptor": "2020-06-16 20:00:14"}]}') service.first_descriptor = None service.second_descriptor = None instance1 = DummyInstance() instance1.onion_address = 'vkmiy6biqcyphtx5exswxl5sjus2vn2b6pzir7lz5akudhwbqk5muead' instance1.intro_set_modified_timestamp = datetime.strptime('2020-06-16 21:00:12', time_format) instance1.descriptor = DummyDescriptor() instance1.descriptor.received_ts = datetime.strptime('2020-06-11 20:00:10', time_format) instance1.descriptor.intro_set = ['127.0.0.1', '127.0.0.2'] instance2 = DummyInstance() instance2.onion_address = 'jhkhjkhdgjkhdfjkhgfjkhgkjdhgjkfdhgjkfdhhdfgjfhdgkjfhkd88' instance2.intro_set_modified_timestamp = None instance2.descriptor = DummyDescriptor() instance2.descriptor.received_ts = datetime.strptime('2020-07-11 20:00:10', time_format) instance2.descriptor.intro_set = ['127.0.0.1', '127.0.0.2'] service.instances = [instance1, instance2] self.assertEqual(status._outputString(), '{"services": [{"instances": [{"descriptorReceived": "2020-06-11 20:00:10", "introPointsNum": 2, "introSetModified": "2020-06-16 21:00:12", "onionAddress": "vkmiy6biqcyphtx5exswxl5sjus2vn2b6pzir7lz5akudhwbqk5muead.onion"}, {"onionAddress": "jhkhjkhdgjkhdfjkhgfjkhgkjdhgjkfdhgjkfdhhdfgjfhdgkjfhkd88.onion", "received": "2020-07-11 20:00:10"}], "onionAddress": "bvy46sg2b5dokczabwv2pabqlrps3lppweyrebhat6gjieo2avojdvad.onion", "publishAttemptFirstDescriptor": null, "publishAttemptSecondDescriptor": null}]}') class TestStatusSocketLocation(unittest.TestCase): def test_location(self): try: del os.environ['ONIONBALANCE_STATUS_SOCKET_LOCATION'] except KeyError: pass config_data = yaml.safe_load("dummy: x") self.assertEqual(status_socket_location(config_data), None) config_data = yaml.safe_load("status-socket-location: /home/user/test.sock") self.assertEqual(status_socket_location(config_data), '/home/user/test.sock') os.environ['ONIONBALANCE_STATUS_SOCKET_LOCATION'] = '/home/user/test2.sock' self.assertEqual(status_socket_location(config_data), '/home/user/test2.sock') if __name__ == '__main__': unittest.main() onionbalance-0.2.2/versioneer.py000066400000000000000000002060031411742520300166760ustar00rootroot00000000000000 # Version: 0.18 """The Versioneer - like a rocketeer, but for versions. The Versioneer ============== * like a rocketeer, but for versions! * https://github.com/warner/python-versioneer * Brian Warner * License: Public Domain * Compatible With: python2.6, 2.7, 3.2, 3.3, 3.4, 3.5, 3.6, and pypy * [![Latest Version] (https://pypip.in/version/versioneer/badge.svg?style=flat) ](https://pypi.python.org/pypi/versioneer/) * [![Build Status] (https://travis-ci.org/warner/python-versioneer.png?branch=master) ](https://travis-ci.org/warner/python-versioneer) This is a tool for managing a recorded version number in distutils-based python projects. The goal is to remove the tedious and error-prone "update the embedded version string" step from your release process. Making a new release should be as easy as recording a new tag in your version-control system, and maybe making new tarballs. ## Quick Install * `pip install versioneer` to somewhere to your $PATH * add a `[versioneer]` section to your setup.cfg (see below) * run `versioneer install` in your source tree, commit the results ## Version Identifiers Source trees come from a variety of places: * a version-control system checkout (mostly used by developers) * a nightly tarball, produced by build automation * a snapshot tarball, produced by a web-based VCS browser, like github's "tarball from tag" feature * a release tarball, produced by "setup.py sdist", distributed through PyPI Within each source tree, the version identifier (either a string or a number, this tool is format-agnostic) can come from a variety of places: * ask the VCS tool itself, e.g. "git describe" (for checkouts), which knows about recent "tags" and an absolute revision-id * the name of the directory into which the tarball was unpacked * an expanded VCS keyword ($Id$, etc) * a `_version.py` created by some earlier build step For released software, the version identifier is closely related to a VCS tag. Some projects use tag names that include more than just the version string (e.g. "myproject-1.2" instead of just "1.2"), in which case the tool needs to strip the tag prefix to extract the version identifier. For unreleased software (between tags), the version identifier should provide enough information to help developers recreate the same tree, while also giving them an idea of roughly how old the tree is (after version 1.2, before version 1.3). Many VCS systems can report a description that captures this, for example `git describe --tags --dirty --always` reports things like "0.7-1-g574ab98-dirty" to indicate that the checkout is one revision past the 0.7 tag, has a unique revision id of "574ab98", and is "dirty" (it has uncommitted changes. The version identifier is used for multiple purposes: * to allow the module to self-identify its version: `myproject.__version__` * to choose a name and prefix for a 'setup.py sdist' tarball ## Theory of Operation Versioneer works by adding a special `_version.py` file into your source tree, where your `__init__.py` can import it. This `_version.py` knows how to dynamically ask the VCS tool for version information at import time. `_version.py` also contains `$Revision$` markers, and the installation process marks `_version.py` to have this marker rewritten with a tag name during the `git archive` command. As a result, generated tarballs will contain enough information to get the proper version. To allow `setup.py` to compute a version too, a `versioneer.py` is added to the top level of your source tree, next to `setup.py` and the `setup.cfg` that configures it. This overrides several distutils/setuptools commands to compute the version when invoked, and changes `setup.py build` and `setup.py sdist` to replace `_version.py` with a small static file that contains just the generated version data. ## Installation See [INSTALL.md](./INSTALL.md) for detailed installation instructions. ## Version-String Flavors Code which uses Versioneer can learn about its version string at runtime by importing `_version` from your main `__init__.py` file and running the `get_versions()` function. From the "outside" (e.g. in `setup.py`), you can import the top-level `versioneer.py` and run `get_versions()`. Both functions return a dictionary with different flavors of version information: * `['version']`: A condensed version string, rendered using the selected style. This is the most commonly used value for the project's version string. The default "pep440" style yields strings like `0.11`, `0.11+2.g1076c97`, or `0.11+2.g1076c97.dirty`. See the "Styles" section below for alternative styles. * `['full-revisionid']`: detailed revision identifier. For Git, this is the full SHA1 commit id, e.g. "1076c978a8d3cfc70f408fe5974aa6c092c949ac". * `['date']`: Date and time of the latest `HEAD` commit. For Git, it is the commit date in ISO 8601 format. This will be None if the date is not available. * `['dirty']`: a boolean, True if the tree has uncommitted changes. Note that this is only accurate if run in a VCS checkout, otherwise it is likely to be False or None * `['error']`: if the version string could not be computed, this will be set to a string describing the problem, otherwise it will be None. It may be useful to throw an exception in setup.py if this is set, to avoid e.g. creating tarballs with a version string of "unknown". Some variants are more useful than others. Including `full-revisionid` in a bug report should allow developers to reconstruct the exact code being tested (or indicate the presence of local changes that should be shared with the developers). `version` is suitable for display in an "about" box or a CLI `--version` output: it can be easily compared against release notes and lists of bugs fixed in various releases. The installer adds the following text to your `__init__.py` to place a basic version in `YOURPROJECT.__version__`: from ._version import get_versions __version__ = get_versions()['version'] del get_versions ## Styles The setup.cfg `style=` configuration controls how the VCS information is rendered into a version string. The default style, "pep440", produces a PEP440-compliant string, equal to the un-prefixed tag name for actual releases, and containing an additional "local version" section with more detail for in-between builds. For Git, this is TAG[+DISTANCE.gHEX[.dirty]] , using information from `git describe --tags --dirty --always`. For example "0.11+2.g1076c97.dirty" indicates that the tree is like the "1076c97" commit but has uncommitted changes (".dirty"), and that this commit is two revisions ("+2") beyond the "0.11" tag. For released software (exactly equal to a known tag), the identifier will only contain the stripped tag, e.g. "0.11". Other styles are available. See [details.md](details.md) in the Versioneer source tree for descriptions. ## Debugging Versioneer tries to avoid fatal errors: if something goes wrong, it will tend to return a version of "0+unknown". To investigate the problem, run `setup.py version`, which will run the version-lookup code in a verbose mode, and will display the full contents of `get_versions()` (including the `error` string, which may help identify what went wrong). ## Known Limitations Some situations are known to cause problems for Versioneer. This details the most significant ones. More can be found on Github [issues page](https://github.com/warner/python-versioneer/issues). ### Subprojects Versioneer has limited support for source trees in which `setup.py` is not in the root directory (e.g. `setup.py` and `.git/` are *not* siblings). The are two common reasons why `setup.py` might not be in the root: * Source trees which contain multiple subprojects, such as [Buildbot](https://github.com/buildbot/buildbot), which contains both "master" and "slave" subprojects, each with their own `setup.py`, `setup.cfg`, and `tox.ini`. Projects like these produce multiple PyPI distributions (and upload multiple independently-installable tarballs). * Source trees whose main purpose is to contain a C library, but which also provide bindings to Python (and perhaps other langauges) in subdirectories. Versioneer will look for `.git` in parent directories, and most operations should get the right version string. However `pip` and `setuptools` have bugs and implementation details which frequently cause `pip install .` from a subproject directory to fail to find a correct version string (so it usually defaults to `0+unknown`). `pip install --editable .` should work correctly. `setup.py install` might work too. Pip-8.1.1 is known to have this problem, but hopefully it will get fixed in some later version. [Bug #38](https://github.com/warner/python-versioneer/issues/38) is tracking this issue. The discussion in [PR #61](https://github.com/warner/python-versioneer/pull/61) describes the issue from the Versioneer side in more detail. [pip PR#3176](https://github.com/pypa/pip/pull/3176) and [pip PR#3615](https://github.com/pypa/pip/pull/3615) contain work to improve pip to let Versioneer work correctly. Versioneer-0.16 and earlier only looked for a `.git` directory next to the `setup.cfg`, so subprojects were completely unsupported with those releases. ### Editable installs with setuptools <= 18.5 `setup.py develop` and `pip install --editable .` allow you to install a project into a virtualenv once, then continue editing the source code (and test) without re-installing after every change. "Entry-point scripts" (`setup(entry_points={"console_scripts": ..})`) are a convenient way to specify executable scripts that should be installed along with the python package. These both work as expected when using modern setuptools. When using setuptools-18.5 or earlier, however, certain operations will cause `pkg_resources.DistributionNotFound` errors when running the entrypoint script, which must be resolved by re-installing the package. This happens when the install happens with one version, then the egg_info data is regenerated while a different version is checked out. Many setup.py commands cause egg_info to be rebuilt (including `sdist`, `wheel`, and installing into a different virtualenv), so this can be surprising. [Bug #83](https://github.com/warner/python-versioneer/issues/83) describes this one, but upgrading to a newer version of setuptools should probably resolve it. ### Unicode version strings While Versioneer works (and is continually tested) with both Python 2 and Python 3, it is not entirely consistent with bytes-vs-unicode distinctions. Newer releases probably generate unicode version strings on py2. It's not clear that this is wrong, but it may be surprising for applications when then write these strings to a network connection or include them in bytes-oriented APIs like cryptographic checksums. [Bug #71](https://github.com/warner/python-versioneer/issues/71) investigates this question. ## Updating Versioneer To upgrade your project to a new release of Versioneer, do the following: * install the new Versioneer (`pip install -U versioneer` or equivalent) * edit `setup.cfg`, if necessary, to include any new configuration settings indicated by the release notes. See [UPGRADING](./UPGRADING.md) for details. * re-run `versioneer install` in your source tree, to replace `SRC/_version.py` * commit any changed files ## Future Directions This tool is designed to make it easily extended to other version-control systems: all VCS-specific components are in separate directories like src/git/ . The top-level `versioneer.py` script is assembled from these components by running make-versioneer.py . In the future, make-versioneer.py will take a VCS name as an argument, and will construct a version of `versioneer.py` that is specific to the given VCS. It might also take the configuration arguments that are currently provided manually during installation by editing setup.py . Alternatively, it might go the other direction and include code from all supported VCS systems, reducing the number of intermediate scripts. ## License To make Versioneer easier to embed, all its code is dedicated to the public domain. The `_version.py` that it creates is also in the public domain. Specifically, both are released under the Creative Commons "Public Domain Dedication" license (CC0-1.0), as described in https://creativecommons.org/publicdomain/zero/1.0/ . """ from __future__ import print_function try: import configparser except ImportError: import ConfigParser as configparser import errno import json import os import re import subprocess import sys class VersioneerConfig: """Container for Versioneer configuration parameters.""" def get_root(): """Get the project root directory. We require that all commands are run from the project root, i.e. the directory that contains setup.py, setup.cfg, and versioneer.py . """ root = os.path.realpath(os.path.abspath(os.getcwd())) setup_py = os.path.join(root, "setup.py") versioneer_py = os.path.join(root, "versioneer.py") if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)): # allow 'python path/to/setup.py COMMAND' root = os.path.dirname(os.path.realpath(os.path.abspath(sys.argv[0]))) setup_py = os.path.join(root, "setup.py") versioneer_py = os.path.join(root, "versioneer.py") if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)): err = ("Versioneer was unable to run the project root directory. " "Versioneer requires setup.py to be executed from " "its immediate directory (like 'python setup.py COMMAND'), " "or in a way that lets it use sys.argv[0] to find the root " "(like 'python path/to/setup.py COMMAND').") raise VersioneerBadRootError(err) try: # Certain runtime workflows (setup.py install/develop in a setuptools # tree) execute all dependencies in a single python process, so # "versioneer" may be imported multiple times, and python's shared # module-import table will cache the first one. So we can't use # os.path.dirname(__file__), as that will find whichever # versioneer.py was first imported, even in later projects. me = os.path.realpath(os.path.abspath(__file__)) me_dir = os.path.normcase(os.path.splitext(me)[0]) vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0]) if me_dir != vsr_dir: print("Warning: build in %s is using versioneer.py from %s" % (os.path.dirname(me), versioneer_py)) except NameError: pass return root def get_config_from_root(root): """Read the project setup.cfg file to determine Versioneer config.""" # This might raise EnvironmentError (if setup.cfg is missing), or # configparser.NoSectionError (if it lacks a [versioneer] section), or # configparser.NoOptionError (if it lacks "VCS="). See the docstring at # the top of versioneer.py for instructions on writing your setup.cfg . setup_cfg = os.path.join(root, "setup.cfg") parser = configparser.SafeConfigParser() with open(setup_cfg, "r") as f: parser.readfp(f) VCS = parser.get("versioneer", "VCS") # mandatory def get(parser, name): if parser.has_option("versioneer", name): return parser.get("versioneer", name) return None cfg = VersioneerConfig() cfg.VCS = VCS cfg.style = get(parser, "style") or "" cfg.versionfile_source = get(parser, "versionfile_source") cfg.versionfile_build = get(parser, "versionfile_build") cfg.tag_prefix = get(parser, "tag_prefix") if cfg.tag_prefix in ("''", '""'): cfg.tag_prefix = "" cfg.parentdir_prefix = get(parser, "parentdir_prefix") cfg.verbose = get(parser, "verbose") return cfg class NotThisMethod(Exception): """Exception raised if a method is not valid for the current scenario.""" # these dictionaries contain VCS-specific tools LONG_VERSION_PY = {} HANDLERS = {} def register_vcs_handler(vcs, method): # decorator """Decorator to mark a method as the handler for a particular VCS.""" def decorate(f): """Store f in HANDLERS[vcs][method].""" if vcs not in HANDLERS: HANDLERS[vcs] = {} HANDLERS[vcs][method] = f return f return decorate def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): """Call the given command(s).""" assert isinstance(commands, list) p = None for c in commands: try: dispcmd = str([c] + args) # remember shell=False, so use git.cmd on windows, not just git p = subprocess.Popen([c] + args, cwd=cwd, env=env, stdout=subprocess.PIPE, stderr=(subprocess.PIPE if hide_stderr else None)) break except EnvironmentError: e = sys.exc_info()[1] if e.errno == errno.ENOENT: continue if verbose: print("unable to run %s" % dispcmd) print(e) return None, None else: if verbose: print("unable to find command, tried %s" % (commands,)) return None, None stdout = p.communicate()[0].strip() if sys.version_info[0] >= 3: stdout = stdout.decode() if p.returncode != 0: if verbose: print("unable to run %s (error)" % dispcmd) print("stdout was %s" % stdout) return None, p.returncode return stdout, p.returncode LONG_VERSION_PY['git'] = ''' # This file helps to compute a version number in source trees obtained from # git-archive tarball (such as those provided by githubs download-from-tag # feature). Distribution tarballs (built by setup.py sdist) and build # directories (produced by setup.py build) will contain a much shorter file # that just contains the computed version number. # This file is released into the public domain. Generated by # versioneer-0.18 (https://github.com/warner/python-versioneer) """Git implementation of _version.py.""" import errno import os import re import subprocess import sys def get_keywords(): """Get the keywords needed to look up the version information.""" # these strings will be replaced by git during git-archive. # setup.py/versioneer.py will grep for the variable names, so they must # each be defined on a line of their own. _version.py will just call # get_keywords(). git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s" git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s" git_date = "%(DOLLAR)sFormat:%%ci%(DOLLAR)s" keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} return keywords class VersioneerConfig: """Container for Versioneer configuration parameters.""" def get_config(): """Create, populate and return the VersioneerConfig() object.""" # these strings are filled in when 'setup.py versioneer' creates # _version.py cfg = VersioneerConfig() cfg.VCS = "git" cfg.style = "%(STYLE)s" cfg.tag_prefix = "%(TAG_PREFIX)s" cfg.parentdir_prefix = "%(PARENTDIR_PREFIX)s" cfg.versionfile_source = "%(VERSIONFILE_SOURCE)s" cfg.verbose = False return cfg class NotThisMethod(Exception): """Exception raised if a method is not valid for the current scenario.""" LONG_VERSION_PY = {} HANDLERS = {} def register_vcs_handler(vcs, method): # decorator """Decorator to mark a method as the handler for a particular VCS.""" def decorate(f): """Store f in HANDLERS[vcs][method].""" if vcs not in HANDLERS: HANDLERS[vcs] = {} HANDLERS[vcs][method] = f return f return decorate def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): """Call the given command(s).""" assert isinstance(commands, list) p = None for c in commands: try: dispcmd = str([c] + args) # remember shell=False, so use git.cmd on windows, not just git p = subprocess.Popen([c] + args, cwd=cwd, env=env, stdout=subprocess.PIPE, stderr=(subprocess.PIPE if hide_stderr else None)) break except EnvironmentError: e = sys.exc_info()[1] if e.errno == errno.ENOENT: continue if verbose: print("unable to run %%s" %% dispcmd) print(e) return None, None else: if verbose: print("unable to find command, tried %%s" %% (commands,)) return None, None stdout = p.communicate()[0].strip() if sys.version_info[0] >= 3: stdout = stdout.decode() if p.returncode != 0: if verbose: print("unable to run %%s (error)" %% dispcmd) print("stdout was %%s" %% stdout) return None, p.returncode return stdout, p.returncode def versions_from_parentdir(parentdir_prefix, root, verbose): """Try to determine the version from the parent directory name. Source tarballs conventionally unpack into a directory that includes both the project name and a version string. We will also support searching up two directory levels for an appropriately named parent directory """ rootdirs = [] for i in range(3): dirname = os.path.basename(root) if dirname.startswith(parentdir_prefix): return {"version": dirname[len(parentdir_prefix):], "full-revisionid": None, "dirty": False, "error": None, "date": None} else: rootdirs.append(root) root = os.path.dirname(root) # up a level if verbose: print("Tried directories %%s but none started with prefix %%s" %% (str(rootdirs), parentdir_prefix)) raise NotThisMethod("rootdir doesn't start with parentdir_prefix") @register_vcs_handler("git", "get_keywords") def git_get_keywords(versionfile_abs): """Extract version information from the given file.""" # the code embedded in _version.py can just fetch the value of these # keywords. When used from setup.py, we don't want to import _version.py, # so we do it with a regexp instead. This function is not used from # _version.py. keywords = {} try: f = open(versionfile_abs, "r") for line in f.readlines(): if line.strip().startswith("git_refnames ="): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["refnames"] = mo.group(1) if line.strip().startswith("git_full ="): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["full"] = mo.group(1) if line.strip().startswith("git_date ="): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["date"] = mo.group(1) f.close() except EnvironmentError: pass return keywords @register_vcs_handler("git", "keywords") def git_versions_from_keywords(keywords, tag_prefix, verbose): """Get version information from git keywords.""" if not keywords: raise NotThisMethod("no keywords at all, weird") date = keywords.get("date") if date is not None: # git-2.2.0 added "%%cI", which expands to an ISO-8601 -compliant # datestamp. However we prefer "%%ci" (which expands to an "ISO-8601 # -like" string, which we must then edit to make compliant), because # it's been around since git-1.5.3, and it's too difficult to # discover which version we're using, or to work around using an # older one. date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) refnames = keywords["refnames"].strip() if refnames.startswith("$Format"): if verbose: print("keywords are unexpanded, not using") raise NotThisMethod("unexpanded keywords, not a git-archive tarball") refs = set([r.strip() for r in refnames.strip("()").split(",")]) # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # just "foo-1.0". If we see a "tag: " prefix, prefer those. TAG = "tag: " tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) if not tags: # Either we're using git < 1.8.3, or there really are no tags. We use # a heuristic: assume all version tags have a digit. The old git %%d # expansion behaves like git log --decorate=short and strips out the # refs/heads/ and refs/tags/ prefixes that would let us distinguish # between branches and tags. By ignoring refnames without digits, we # filter out many common branch names like "release" and # "stabilization", as well as "HEAD" and "master". tags = set([r for r in refs if re.search(r'\d', r)]) if verbose: print("discarding '%%s', no digits" %% ",".join(refs - tags)) if verbose: print("likely tags: %%s" %% ",".join(sorted(tags))) for ref in sorted(tags): # sorting will prefer e.g. "2.0" over "2.0rc1" if ref.startswith(tag_prefix): r = ref[len(tag_prefix):] if verbose: print("picking %%s" %% r) return {"version": r, "full-revisionid": keywords["full"].strip(), "dirty": False, "error": None, "date": date} # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") return {"version": "0+unknown", "full-revisionid": keywords["full"].strip(), "dirty": False, "error": "no suitable tags", "date": None} @register_vcs_handler("git", "pieces_from_vcs") def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): """Get version from 'git describe' in the root of the source tree. This only gets called if the git-archive 'subst' keywords were *not* expanded, and _version.py hasn't already been rewritten with a short version string, meaning we're inside a checked out source tree. """ GITS = ["git"] if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) if rc != 0: if verbose: print("Directory %%s not under git control" %% root) raise NotThisMethod("'git rev-parse --git-dir' returned error") # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there isn't one, this yields HEX[-dirty] (no NUM) describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", "--always", "--long", "--match", "%%s*" %% tag_prefix], cwd=root) # --long was added in git-1.5.5 if describe_out is None: raise NotThisMethod("'git describe' failed") describe_out = describe_out.strip() full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) if full_out is None: raise NotThisMethod("'git rev-parse' failed") full_out = full_out.strip() pieces = {} pieces["long"] = full_out pieces["short"] = full_out[:7] # maybe improved later pieces["error"] = None # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] # TAG might have hyphens. git_describe = describe_out # look for -dirty suffix dirty = git_describe.endswith("-dirty") pieces["dirty"] = dirty if dirty: git_describe = git_describe[:git_describe.rindex("-dirty")] # now we have TAG-NUM-gHEX or HEX if "-" in git_describe: # TAG-NUM-gHEX mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) if not mo: # unparseable. Maybe git-describe is misbehaving? pieces["error"] = ("unable to parse git-describe output: '%%s'" %% describe_out) return pieces # tag full_tag = mo.group(1) if not full_tag.startswith(tag_prefix): if verbose: fmt = "tag '%%s' doesn't start with prefix '%%s'" print(fmt %% (full_tag, tag_prefix)) pieces["error"] = ("tag '%%s' doesn't start with prefix '%%s'" %% (full_tag, tag_prefix)) return pieces pieces["closest-tag"] = full_tag[len(tag_prefix):] # distance: number of commits since tag pieces["distance"] = int(mo.group(2)) # commit: short hex revision ID pieces["short"] = mo.group(3) else: # HEX: no tags pieces["closest-tag"] = None count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], cwd=root) pieces["distance"] = int(count_out) # total number of commits # commit date: see ISO-8601 comment in git_versions_from_keywords() date = run_command(GITS, ["show", "-s", "--format=%%ci", "HEAD"], cwd=root)[0].strip() pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) return pieces def plus_or_dot(pieces): """Return a + if we don't already have one, else return a .""" if "+" in pieces.get("closest-tag", ""): return "." return "+" def render_pep440(pieces): """Build up version string, with post-release "local version identifier". Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty Exceptions: 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: rendered += plus_or_dot(pieces) rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" else: # exception #1 rendered = "0+untagged.%%d.g%%s" %% (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered def render_pep440_pre(pieces): """TAG[.post.devDISTANCE] -- No -dirty. Exceptions: 1: no tags. 0.post.devDISTANCE """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"]: rendered += ".post.dev%%d" %% pieces["distance"] else: # exception #1 rendered = "0.post.dev%%d" %% pieces["distance"] return rendered def render_pep440_post(pieces): """TAG[.postDISTANCE[.dev0]+gHEX] . The ".dev0" means dirty. Note that .dev0 sorts backwards (a dirty tree will appear "older" than the corresponding clean one), but you shouldn't be releasing software with -dirty anyways. Exceptions: 1: no tags. 0.postDISTANCE[.dev0] """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: rendered += ".post%%d" %% pieces["distance"] if pieces["dirty"]: rendered += ".dev0" rendered += plus_or_dot(pieces) rendered += "g%%s" %% pieces["short"] else: # exception #1 rendered = "0.post%%d" %% pieces["distance"] if pieces["dirty"]: rendered += ".dev0" rendered += "+g%%s" %% pieces["short"] return rendered def render_pep440_old(pieces): """TAG[.postDISTANCE[.dev0]] . The ".dev0" means dirty. Eexceptions: 1: no tags. 0.postDISTANCE[.dev0] """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: rendered += ".post%%d" %% pieces["distance"] if pieces["dirty"]: rendered += ".dev0" else: # exception #1 rendered = "0.post%%d" %% pieces["distance"] if pieces["dirty"]: rendered += ".dev0" return rendered def render_git_describe(pieces): """TAG[-DISTANCE-gHEX][-dirty]. Like 'git describe --tags --dirty --always'. Exceptions: 1: no tags. HEX[-dirty] (note: no 'g' prefix) """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"]: rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) else: # exception #1 rendered = pieces["short"] if pieces["dirty"]: rendered += "-dirty" return rendered def render_git_describe_long(pieces): """TAG-DISTANCE-gHEX[-dirty]. Like 'git describe --tags --dirty --always -long'. The distance/hash is unconditional. Exceptions: 1: no tags. HEX[-dirty] (note: no 'g' prefix) """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) else: # exception #1 rendered = pieces["short"] if pieces["dirty"]: rendered += "-dirty" return rendered def render(pieces, style): """Render the given version pieces into the requested style.""" if pieces["error"]: return {"version": "unknown", "full-revisionid": pieces.get("long"), "dirty": None, "error": pieces["error"], "date": None} if not style or style == "default": style = "pep440" # the default if style == "pep440": rendered = render_pep440(pieces) elif style == "pep440-pre": rendered = render_pep440_pre(pieces) elif style == "pep440-post": rendered = render_pep440_post(pieces) elif style == "pep440-old": rendered = render_pep440_old(pieces) elif style == "git-describe": rendered = render_git_describe(pieces) elif style == "git-describe-long": rendered = render_git_describe_long(pieces) else: raise ValueError("unknown style '%%s'" %% style) return {"version": rendered, "full-revisionid": pieces["long"], "dirty": pieces["dirty"], "error": None, "date": pieces.get("date")} def get_versions(): """Get version information or return default if unable to do so.""" # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have # __file__, we can work backwards from there to the root. Some # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which # case we can only use expanded keywords. cfg = get_config() verbose = cfg.verbose try: return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose) except NotThisMethod: pass try: root = os.path.realpath(__file__) # versionfile_source is the relative path from the top of the source # tree (where the .git directory might live) to this file. Invert # this to find the root from __file__. for i in cfg.versionfile_source.split('/'): root = os.path.dirname(root) except NameError: return {"version": "0+unknown", "full-revisionid": None, "dirty": None, "error": "unable to find root of source tree", "date": None} try: pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) return render(pieces, cfg.style) except NotThisMethod: pass try: if cfg.parentdir_prefix: return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) except NotThisMethod: pass return {"version": "0+unknown", "full-revisionid": None, "dirty": None, "error": "unable to compute version", "date": None} ''' @register_vcs_handler("git", "get_keywords") def git_get_keywords(versionfile_abs): """Extract version information from the given file.""" # the code embedded in _version.py can just fetch the value of these # keywords. When used from setup.py, we don't want to import _version.py, # so we do it with a regexp instead. This function is not used from # _version.py. keywords = {} try: f = open(versionfile_abs, "r") for line in f.readlines(): if line.strip().startswith("git_refnames ="): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["refnames"] = mo.group(1) if line.strip().startswith("git_full ="): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["full"] = mo.group(1) if line.strip().startswith("git_date ="): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["date"] = mo.group(1) f.close() except EnvironmentError: pass return keywords @register_vcs_handler("git", "keywords") def git_versions_from_keywords(keywords, tag_prefix, verbose): """Get version information from git keywords.""" if not keywords: raise NotThisMethod("no keywords at all, weird") date = keywords.get("date") if date is not None: # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 # -like" string, which we must then edit to make compliant), because # it's been around since git-1.5.3, and it's too difficult to # discover which version we're using, or to work around using an # older one. date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) refnames = keywords["refnames"].strip() if refnames.startswith("$Format"): if verbose: print("keywords are unexpanded, not using") raise NotThisMethod("unexpanded keywords, not a git-archive tarball") refs = set([r.strip() for r in refnames.strip("()").split(",")]) # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # just "foo-1.0". If we see a "tag: " prefix, prefer those. TAG = "tag: " tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) if not tags: # Either we're using git < 1.8.3, or there really are no tags. We use # a heuristic: assume all version tags have a digit. The old git %d # expansion behaves like git log --decorate=short and strips out the # refs/heads/ and refs/tags/ prefixes that would let us distinguish # between branches and tags. By ignoring refnames without digits, we # filter out many common branch names like "release" and # "stabilization", as well as "HEAD" and "master". tags = set([r for r in refs if re.search(r'\d', r)]) if verbose: print("discarding '%s', no digits" % ",".join(refs - tags)) if verbose: print("likely tags: %s" % ",".join(sorted(tags))) for ref in sorted(tags): # sorting will prefer e.g. "2.0" over "2.0rc1" if ref.startswith(tag_prefix): r = ref[len(tag_prefix):] if verbose: print("picking %s" % r) return {"version": r, "full-revisionid": keywords["full"].strip(), "dirty": False, "error": None, "date": date} # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") return {"version": "0+unknown", "full-revisionid": keywords["full"].strip(), "dirty": False, "error": "no suitable tags", "date": None} @register_vcs_handler("git", "pieces_from_vcs") def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): """Get version from 'git describe' in the root of the source tree. This only gets called if the git-archive 'subst' keywords were *not* expanded, and _version.py hasn't already been rewritten with a short version string, meaning we're inside a checked out source tree. """ GITS = ["git"] if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) if rc != 0: if verbose: print("Directory %s not under git control" % root) raise NotThisMethod("'git rev-parse --git-dir' returned error") # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there isn't one, this yields HEX[-dirty] (no NUM) describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", "--always", "--long", "--match", "%s*" % tag_prefix], cwd=root) # --long was added in git-1.5.5 if describe_out is None: raise NotThisMethod("'git describe' failed") describe_out = describe_out.strip() full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) if full_out is None: raise NotThisMethod("'git rev-parse' failed") full_out = full_out.strip() pieces = {} pieces["long"] = full_out pieces["short"] = full_out[:7] # maybe improved later pieces["error"] = None # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] # TAG might have hyphens. git_describe = describe_out # look for -dirty suffix dirty = git_describe.endswith("-dirty") pieces["dirty"] = dirty if dirty: git_describe = git_describe[:git_describe.rindex("-dirty")] # now we have TAG-NUM-gHEX or HEX if "-" in git_describe: # TAG-NUM-gHEX mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) if not mo: # unparseable. Maybe git-describe is misbehaving? pieces["error"] = ("unable to parse git-describe output: '%s'" % describe_out) return pieces # tag full_tag = mo.group(1) if not full_tag.startswith(tag_prefix): if verbose: fmt = "tag '%s' doesn't start with prefix '%s'" print(fmt % (full_tag, tag_prefix)) pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" % (full_tag, tag_prefix)) return pieces pieces["closest-tag"] = full_tag[len(tag_prefix):] # distance: number of commits since tag pieces["distance"] = int(mo.group(2)) # commit: short hex revision ID pieces["short"] = mo.group(3) else: # HEX: no tags pieces["closest-tag"] = None count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], cwd=root) pieces["distance"] = int(count_out) # total number of commits # commit date: see ISO-8601 comment in git_versions_from_keywords() date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) return pieces def do_vcs_install(manifest_in, versionfile_source, ipy): """Git-specific installation logic for Versioneer. For Git, this means creating/changing .gitattributes to mark _version.py for export-subst keyword substitution. """ GITS = ["git"] if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] files = [manifest_in, versionfile_source] if ipy: files.append(ipy) try: me = __file__ if me.endswith(".pyc") or me.endswith(".pyo"): me = os.path.splitext(me)[0] + ".py" versioneer_file = os.path.relpath(me) except NameError: versioneer_file = "versioneer.py" files.append(versioneer_file) present = False try: f = open(".gitattributes", "r") for line in f.readlines(): if line.strip().startswith(versionfile_source): if "export-subst" in line.strip().split()[1:]: present = True f.close() except EnvironmentError: pass if not present: f = open(".gitattributes", "a+") f.write("%s export-subst\n" % versionfile_source) f.close() files.append(".gitattributes") run_command(GITS, ["add", "--"] + files) def versions_from_parentdir(parentdir_prefix, root, verbose): """Try to determine the version from the parent directory name. Source tarballs conventionally unpack into a directory that includes both the project name and a version string. We will also support searching up two directory levels for an appropriately named parent directory """ rootdirs = [] for i in range(3): dirname = os.path.basename(root) if dirname.startswith(parentdir_prefix): return {"version": dirname[len(parentdir_prefix):], "full-revisionid": None, "dirty": False, "error": None, "date": None} else: rootdirs.append(root) root = os.path.dirname(root) # up a level if verbose: print("Tried directories %s but none started with prefix %s" % (str(rootdirs), parentdir_prefix)) raise NotThisMethod("rootdir doesn't start with parentdir_prefix") SHORT_VERSION_PY = """ # This file was generated by 'versioneer.py' (0.18) from # revision-control system data, or from the parent directory name of an # unpacked source archive. Distribution tarballs contain a pre-generated copy # of this file. import json version_json = ''' %s ''' # END VERSION_JSON def get_versions(): return json.loads(version_json) """ def versions_from_file(filename): """Try to determine the version from _version.py if present.""" try: with open(filename) as f: contents = f.read() except EnvironmentError: raise NotThisMethod("unable to read _version.py") mo = re.search(r"version_json = '''\n(.*)''' # END VERSION_JSON", contents, re.M | re.S) if not mo: mo = re.search(r"version_json = '''\r\n(.*)''' # END VERSION_JSON", contents, re.M | re.S) if not mo: raise NotThisMethod("no version_json in _version.py") return json.loads(mo.group(1)) def write_to_version_file(filename, versions): """Write the given version number to the given _version.py file.""" os.unlink(filename) contents = json.dumps(versions, sort_keys=True, indent=1, separators=(",", ": ")) with open(filename, "w") as f: f.write(SHORT_VERSION_PY % contents) print("set %s to '%s'" % (filename, versions["version"])) def plus_or_dot(pieces): """Return a + if we don't already have one, else return a .""" if "+" in pieces.get("closest-tag", ""): return "." return "+" def render_pep440(pieces): """Build up version string, with post-release "local version identifier". Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty Exceptions: 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: rendered += plus_or_dot(pieces) rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" else: # exception #1 rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered def render_pep440_pre(pieces): """TAG[.post.devDISTANCE] -- No -dirty. Exceptions: 1: no tags. 0.post.devDISTANCE """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"]: rendered += ".post.dev%d" % pieces["distance"] else: # exception #1 rendered = "0.post.dev%d" % pieces["distance"] return rendered def render_pep440_post(pieces): """TAG[.postDISTANCE[.dev0]+gHEX] . The ".dev0" means dirty. Note that .dev0 sorts backwards (a dirty tree will appear "older" than the corresponding clean one), but you shouldn't be releasing software with -dirty anyways. Exceptions: 1: no tags. 0.postDISTANCE[.dev0] """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: rendered += ".post%d" % pieces["distance"] if pieces["dirty"]: rendered += ".dev0" rendered += plus_or_dot(pieces) rendered += "g%s" % pieces["short"] else: # exception #1 rendered = "0.post%d" % pieces["distance"] if pieces["dirty"]: rendered += ".dev0" rendered += "+g%s" % pieces["short"] return rendered def render_pep440_old(pieces): """TAG[.postDISTANCE[.dev0]] . The ".dev0" means dirty. Eexceptions: 1: no tags. 0.postDISTANCE[.dev0] """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: rendered += ".post%d" % pieces["distance"] if pieces["dirty"]: rendered += ".dev0" else: # exception #1 rendered = "0.post%d" % pieces["distance"] if pieces["dirty"]: rendered += ".dev0" return rendered def render_git_describe(pieces): """TAG[-DISTANCE-gHEX][-dirty]. Like 'git describe --tags --dirty --always'. Exceptions: 1: no tags. HEX[-dirty] (note: no 'g' prefix) """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"]: rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) else: # exception #1 rendered = pieces["short"] if pieces["dirty"]: rendered += "-dirty" return rendered def render_git_describe_long(pieces): """TAG-DISTANCE-gHEX[-dirty]. Like 'git describe --tags --dirty --always -long'. The distance/hash is unconditional. Exceptions: 1: no tags. HEX[-dirty] (note: no 'g' prefix) """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) else: # exception #1 rendered = pieces["short"] if pieces["dirty"]: rendered += "-dirty" return rendered def render(pieces, style): """Render the given version pieces into the requested style.""" if pieces["error"]: return {"version": "unknown", "full-revisionid": pieces.get("long"), "dirty": None, "error": pieces["error"], "date": None} if not style or style == "default": style = "pep440" # the default if style == "pep440": rendered = render_pep440(pieces) elif style == "pep440-pre": rendered = render_pep440_pre(pieces) elif style == "pep440-post": rendered = render_pep440_post(pieces) elif style == "pep440-old": rendered = render_pep440_old(pieces) elif style == "git-describe": rendered = render_git_describe(pieces) elif style == "git-describe-long": rendered = render_git_describe_long(pieces) else: raise ValueError("unknown style '%s'" % style) return {"version": rendered, "full-revisionid": pieces["long"], "dirty": pieces["dirty"], "error": None, "date": pieces.get("date")} class VersioneerBadRootError(Exception): """The project root directory is unknown or missing key files.""" def get_versions(verbose=False): """Get the project version from whatever source is available. Returns dict with two keys: 'version' and 'full'. """ if "versioneer" in sys.modules: # see the discussion in cmdclass.py:get_cmdclass() del sys.modules["versioneer"] root = get_root() cfg = get_config_from_root(root) assert cfg.VCS is not None, "please set [versioneer]VCS= in setup.cfg" handlers = HANDLERS.get(cfg.VCS) assert handlers, "unrecognized VCS '%s'" % cfg.VCS verbose = verbose or cfg.verbose assert cfg.versionfile_source is not None, \ "please set versioneer.versionfile_source" assert cfg.tag_prefix is not None, "please set versioneer.tag_prefix" versionfile_abs = os.path.join(root, cfg.versionfile_source) # extract version from first of: _version.py, VCS command (e.g. 'git # describe'), parentdir. This is meant to work for developers using a # source checkout, for users of a tarball created by 'setup.py sdist', # and for users of a tarball/zipball created by 'git archive' or github's # download-from-tag feature or the equivalent in other VCSes. get_keywords_f = handlers.get("get_keywords") from_keywords_f = handlers.get("keywords") if get_keywords_f and from_keywords_f: try: keywords = get_keywords_f(versionfile_abs) ver = from_keywords_f(keywords, cfg.tag_prefix, verbose) if verbose: print("got version from expanded keyword %s" % ver) return ver except NotThisMethod: pass try: ver = versions_from_file(versionfile_abs) if verbose: print("got version from file %s %s" % (versionfile_abs, ver)) return ver except NotThisMethod: pass from_vcs_f = handlers.get("pieces_from_vcs") if from_vcs_f: try: pieces = from_vcs_f(cfg.tag_prefix, root, verbose) ver = render(pieces, cfg.style) if verbose: print("got version from VCS %s" % ver) return ver except NotThisMethod: pass try: if cfg.parentdir_prefix: ver = versions_from_parentdir(cfg.parentdir_prefix, root, verbose) if verbose: print("got version from parentdir %s" % ver) return ver except NotThisMethod: pass if verbose: print("unable to compute version") return {"version": "0+unknown", "full-revisionid": None, "dirty": None, "error": "unable to compute version", "date": None} def get_version(): """Get the short version string for this project.""" return get_versions()["version"] def get_cmdclass(): """Get the custom setuptools/distutils subclasses used by Versioneer.""" if "versioneer" in sys.modules: del sys.modules["versioneer"] # this fixes the "python setup.py develop" case (also 'install' and # 'easy_install .'), in which subdependencies of the main project are # built (using setup.py bdist_egg) in the same python process. Assume # a main project A and a dependency B, which use different versions # of Versioneer. A's setup.py imports A's Versioneer, leaving it in # sys.modules by the time B's setup.py is executed, causing B to run # with the wrong versioneer. Setuptools wraps the sub-dep builds in a # sandbox that restores sys.modules to it's pre-build state, so the # parent is protected against the child's "import versioneer". By # removing ourselves from sys.modules here, before the child build # happens, we protect the child from the parent's versioneer too. # Also see https://github.com/warner/python-versioneer/issues/52 cmds = {} # we add "version" to both distutils and setuptools from distutils.core import Command class cmd_version(Command): description = "report generated version string" user_options = [] boolean_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): vers = get_versions(verbose=True) print("Version: %s" % vers["version"]) print(" full-revisionid: %s" % vers.get("full-revisionid")) print(" dirty: %s" % vers.get("dirty")) print(" date: %s" % vers.get("date")) if vers["error"]: print(" error: %s" % vers["error"]) cmds["version"] = cmd_version # we override "build_py" in both distutils and setuptools # # most invocation pathways end up running build_py: # distutils/build -> build_py # distutils/install -> distutils/build ->.. # setuptools/bdist_wheel -> distutils/install ->.. # setuptools/bdist_egg -> distutils/install_lib -> build_py # setuptools/install -> bdist_egg ->.. # setuptools/develop -> ? # pip install: # copies source tree to a tempdir before running egg_info/etc # if .git isn't copied too, 'git describe' will fail # then does setup.py bdist_wheel, or sometimes setup.py install # setup.py egg_info -> ? # we override different "build_py" commands for both environments if "setuptools" in sys.modules: from setuptools.command.build_py import build_py as _build_py else: from distutils.command.build_py import build_py as _build_py class cmd_build_py(_build_py): def run(self): root = get_root() cfg = get_config_from_root(root) versions = get_versions() _build_py.run(self) # now locate _version.py in the new build/ directory and replace # it with an updated value if cfg.versionfile_build: target_versionfile = os.path.join(self.build_lib, cfg.versionfile_build) print("UPDATING %s" % target_versionfile) write_to_version_file(target_versionfile, versions) cmds["build_py"] = cmd_build_py if "cx_Freeze" in sys.modules: # cx_freeze enabled? from cx_Freeze.dist import build_exe as _build_exe # nczeczulin reports that py2exe won't like the pep440-style string # as FILEVERSION, but it can be used for PRODUCTVERSION, e.g. # setup(console=[{ # "version": versioneer.get_version().split("+", 1)[0], # FILEVERSION # "product_version": versioneer.get_version(), # ... class cmd_build_exe(_build_exe): def run(self): root = get_root() cfg = get_config_from_root(root) versions = get_versions() target_versionfile = cfg.versionfile_source print("UPDATING %s" % target_versionfile) write_to_version_file(target_versionfile, versions) _build_exe.run(self) os.unlink(target_versionfile) with open(cfg.versionfile_source, "w") as f: LONG = LONG_VERSION_PY[cfg.VCS] f.write(LONG % {"DOLLAR": "$", "STYLE": cfg.style, "TAG_PREFIX": cfg.tag_prefix, "PARENTDIR_PREFIX": cfg.parentdir_prefix, "VERSIONFILE_SOURCE": cfg.versionfile_source, }) cmds["build_exe"] = cmd_build_exe del cmds["build_py"] if 'py2exe' in sys.modules: # py2exe enabled? try: from py2exe.distutils_buildexe import py2exe as _py2exe # py3 except ImportError: from py2exe.build_exe import py2exe as _py2exe # py2 class cmd_py2exe(_py2exe): def run(self): root = get_root() cfg = get_config_from_root(root) versions = get_versions() target_versionfile = cfg.versionfile_source print("UPDATING %s" % target_versionfile) write_to_version_file(target_versionfile, versions) _py2exe.run(self) os.unlink(target_versionfile) with open(cfg.versionfile_source, "w") as f: LONG = LONG_VERSION_PY[cfg.VCS] f.write(LONG % {"DOLLAR": "$", "STYLE": cfg.style, "TAG_PREFIX": cfg.tag_prefix, "PARENTDIR_PREFIX": cfg.parentdir_prefix, "VERSIONFILE_SOURCE": cfg.versionfile_source, }) cmds["py2exe"] = cmd_py2exe # we override different "sdist" commands for both environments if "setuptools" in sys.modules: from setuptools.command.sdist import sdist as _sdist else: from distutils.command.sdist import sdist as _sdist class cmd_sdist(_sdist): def run(self): versions = get_versions() self._versioneer_generated_versions = versions # unless we update this, the command will keep using the old # version self.distribution.metadata.version = versions["version"] return _sdist.run(self) def make_release_tree(self, base_dir, files): root = get_root() cfg = get_config_from_root(root) _sdist.make_release_tree(self, base_dir, files) # now locate _version.py in the new base_dir directory # (remembering that it may be a hardlink) and replace it with an # updated value target_versionfile = os.path.join(base_dir, cfg.versionfile_source) print("UPDATING %s" % target_versionfile) write_to_version_file(target_versionfile, self._versioneer_generated_versions) cmds["sdist"] = cmd_sdist return cmds CONFIG_ERROR = """ setup.cfg is missing the necessary Versioneer configuration. You need a section like: [versioneer] VCS = git style = pep440 versionfile_source = src/myproject/_version.py versionfile_build = myproject/_version.py tag_prefix = parentdir_prefix = myproject- You will also need to edit your setup.py to use the results: import versioneer setup(version=versioneer.get_version(), cmdclass=versioneer.get_cmdclass(), ...) Please read the docstring in ./versioneer.py for configuration instructions, edit setup.cfg, and re-run the installer or 'python versioneer.py setup'. """ SAMPLE_CONFIG = """ # See the docstring in versioneer.py for instructions. Note that you must # re-run 'versioneer.py setup' after changing this section, and commit the # resulting files. [versioneer] #VCS = git #style = pep440 #versionfile_source = #versionfile_build = #tag_prefix = #parentdir_prefix = """ INIT_PY_SNIPPET = """ from ._version import get_versions __version__ = get_versions()['version'] del get_versions """ def do_setup(): """Main VCS-independent setup function for installing Versioneer.""" root = get_root() try: cfg = get_config_from_root(root) except (EnvironmentError, configparser.NoSectionError, configparser.NoOptionError) as e: if isinstance(e, (EnvironmentError, configparser.NoSectionError)): print("Adding sample versioneer config to setup.cfg", file=sys.stderr) with open(os.path.join(root, "setup.cfg"), "a") as f: f.write(SAMPLE_CONFIG) print(CONFIG_ERROR, file=sys.stderr) return 1 print(" creating %s" % cfg.versionfile_source) with open(cfg.versionfile_source, "w") as f: LONG = LONG_VERSION_PY[cfg.VCS] f.write(LONG % {"DOLLAR": "$", "STYLE": cfg.style, "TAG_PREFIX": cfg.tag_prefix, "PARENTDIR_PREFIX": cfg.parentdir_prefix, "VERSIONFILE_SOURCE": cfg.versionfile_source, }) ipy = os.path.join(os.path.dirname(cfg.versionfile_source), "__init__.py") if os.path.exists(ipy): try: with open(ipy, "r") as f: old = f.read() except EnvironmentError: old = "" if INIT_PY_SNIPPET not in old: print(" appending to %s" % ipy) with open(ipy, "a") as f: f.write(INIT_PY_SNIPPET) else: print(" %s unmodified" % ipy) else: print(" %s doesn't exist, ok" % ipy) ipy = None # Make sure both the top-level "versioneer.py" and versionfile_source # (PKG/_version.py, used by runtime code) are in MANIFEST.in, so # they'll be copied into source distributions. Pip won't be able to # install the package without this. manifest_in = os.path.join(root, "MANIFEST.in") simple_includes = set() try: with open(manifest_in, "r") as f: for line in f: if line.startswith("include "): for include in line.split()[1:]: simple_includes.add(include) except EnvironmentError: pass # That doesn't cover everything MANIFEST.in can do # (http://docs.python.org/2/distutils/sourcedist.html#commands), so # it might give some false negatives. Appending redundant 'include' # lines is safe, though. if "versioneer.py" not in simple_includes: print(" appending 'versioneer.py' to MANIFEST.in") with open(manifest_in, "a") as f: f.write("include versioneer.py\n") else: print(" 'versioneer.py' already in MANIFEST.in") if cfg.versionfile_source not in simple_includes: print(" appending versionfile_source ('%s') to MANIFEST.in" % cfg.versionfile_source) with open(manifest_in, "a") as f: f.write("include %s\n" % cfg.versionfile_source) else: print(" versionfile_source already in MANIFEST.in") # Make VCS-specific changes. For git, this means creating/changing # .gitattributes to mark _version.py for export-subst keyword # substitution. do_vcs_install(manifest_in, cfg.versionfile_source, ipy) return 0 def scan_setup_py(): """Validate the contents of setup.py against Versioneer's expectations.""" found = set() setters = False errors = 0 with open("setup.py", "r") as f: for line in f.readlines(): if "import versioneer" in line: found.add("import") if "versioneer.get_cmdclass()" in line: found.add("cmdclass") if "versioneer.get_version()" in line: found.add("get_version") if "versioneer.VCS" in line: setters = True if "versioneer.versionfile_source" in line: setters = True if len(found) != 3: print("") print("Your setup.py appears to be missing some important items") print("(but I might be wrong). Please make sure it has something") print("roughly like the following:") print("") print(" import versioneer") print(" setup( version=versioneer.get_version(),") print(" cmdclass=versioneer.get_cmdclass(), ...)") print("") errors += 1 if setters: print("You should remove lines like 'versioneer.VCS = ' and") print("'versioneer.versionfile_source = ' . This configuration") print("now lives in setup.cfg, and should be removed from setup.py") print("") errors += 1 return errors if __name__ == "__main__": cmd = sys.argv[1] if cmd == "setup": errors = do_setup() errors += scan_setup_py() if errors: sys.exit(1)