pax_global_header00006660000000000000000000000064135765016160014524gustar00rootroot0000000000000052 comment=1ca1244c2028c47aabcbd836436a7bbb53ef6015 zip4j-2.2.8/000077500000000000000000000000001357650161600125755ustar00rootroot00000000000000zip4j-2.2.8/.gitignore000066400000000000000000000000371357650161600145650ustar00rootroot00000000000000.idea/ target/ *.iml .DS_Store zip4j-2.2.8/.travis.yml000066400000000000000000000000741357650161600147070ustar00rootroot00000000000000language: java script: - travis_wait 30 mvn clean verify zip4j-2.2.8/LICENSE000066400000000000000000000261351357650161600136110ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. zip4j-2.2.8/README.md000066400000000000000000000512521357650161600140610ustar00rootroot00000000000000 [![Build Status](https://travis-ci.org/srikanth-lingala/zip4j.svg?branch=master)](https://travis-ci.org/srikanth-lingala/zip4j) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/net.lingala.zip4j/zip4j/badge.svg)](https://maven-badges.herokuapp.com/maven-central/net.lingala.zip4j/zip4j) [![Known Vulnerabilities](https://snyk.io//test/github/srikanth-lingala/zip4j/badge.svg?targetFile=pom.xml)](https://snyk.io//test/github/srikanth-lingala/zip4j?targetFile=pom.xml) Zip4j - A java library for zip files / streams ========================= ## Thank you for rating Zip4j as the best java library for zip files [[1][1], [2][2], [3][3], [4][4]]. It has encouraged me to bring this project to life again after a gap of several years. I tried to add some of the important features that were requested over this time, and also made api much more neater. The newer version (> 2.0.0) now supports streams, which was understandably, one of the most requested feature. If you have any feedback, bugs to report, feature requests, etc, please open an issue here on github. I will try to address them as soon as I can. I also monitor the tag `zip4j` on [stackoverflow][10]. ## About Zip4j is the most comprehensive java library for Zip files or streams. As of this writing, it is the only java library which has support for zip encryption, apart from several other features. It tries to make handling zip files/streams a lot more easier. No more clunky boiler plate code with input streams and output streams. As you can see in the usage section below, working with zip files can now even be a single line of code, compared to [this][5]. I mean no offense to the Java's in-built zip support. In fact, this library depends on Java's in-built zip code and it would have been significantly more ~~complicated~~ challenging if I had to write compression logic as well. But lets be honest, working with zip files or streams can be a lot of boiler plate code. The main goal of this library is to provide a simple api for all usual actions of a zip file or streams by doing the heavy lifting within the library and not have developers worry about having to deal with streams, etc. Apart from usability, other important goal of this library is to provide support for as many zip features as possible, which brings me to: ## Features ~~~ * Create, Add, Extract, Update, Remove files from a Zip file * Support for streams (ZipInputStream and ZipOutputStream) * Read/Write password protected Zip files and streams * Support for both AES and Zip-Standard encryption methods * Support for Zip64 format * Store (No Compression) and Deflate compression method * Create or extract files from Split Zip files (Ex: z01, z02,...zip) * Support for Unicode file names and comments in zip * Progress Monitor - for integration into apps and user facing applications ~~~ ## Background Zip4j was started by me (Srikanth Reddy Lingala) back in 2008/2009, when I realized the lack of support for majority of zip format features in Java. And also working with zip files was, as mentioned several times above, a lot of boiler plate code, having to deal with streams (worse still, it was back in the days when there was no try-with-resources in java). There was also no comprehensive library which supports zip features. So, I decided to write one, and approximately after a year, the first version was out. The response was truly overwhelming, and I got a lot of support right from the next day of release. It was not put on github as git/github was not as popular as it is now. Code was hosted on my website, as, guess what, a zip file :). And unfortunately, after a year or two after the initial release, life got busy and I was not able to support Zip4j as much as I wanted to. But the overwhelming encouragement I got over the years made me start working on Zip4j once again, and makes me support Zip4j as much as I can. ## Maven ~~~~ net.lingala.zip4j zip4j 2.2.8 ~~~~ Please check the latest version number on [Zip4j's Maven repository][6] ## Usage ### Creating a zip file with single file in it / Adding single file to an existing zip ~~~~ new ZipFile("filename.zip").addFile("filename.ext"); ~~~~    Or ~~~~ new ZipFile("filename.zip").addFile(new File("filename.ext")); ~~~~ ### Creating a zip file with multiple files / Adding multiple files to an existing zip ~~~~ new ZipFile("filename.zip").addFiles(Arrays.asList(new File("first_file"), new File("second_file"))); ~~~~ ### Creating a zip file by adding a folder to it / Adding a folder to an existing zip ~~~~ new ZipFile("filename.zip").addFolder(new File("/user/myuser/folder_to_add")); ~~~~ ### Creating a zip file from stream / Adding a stream to an existing zip ~~~~ new ZipFile("filename.zip").addStream(inputStream, new ZipParameters()); ~~~~ Passing in `new ZipParameters()`, as in the above example, will make Zip4j use default zip parameters. Please look at [ZipParameters][7] to see the default configuration. ### Creating a zip file of compression method store / Adding entries to zip file of compression method store By default Zip4j uses Deflate compression algorithm to compress files. However, if you would like to not use any compression (called STORE compression), you can do so as shown in the example below: ~~~~ ZipParameters zipParameters = new ZipParameters(); zipParameters.setCompressionMethod(CompressionMethod.STORE); new ZipFile("filename.zip").addFile("fileToAdd", zipParameters); ~~~~ You can similarly pass in zip parameters to all the other examples to create a zip file of STORE compression. ### Creating a password protected zip file / Adding files to an existing zip with password protection ##### AES encryption ~~~~ ZipParameters zipParameters = new ZipParameters(); zipParameters.setEncryptFiles(true); zipParameters.setEncryptionMethod(EncryptionMethod.AES); // Below line is optional. AES 256 is used by default. You can override it to use AES 128. AES 192 is supported only for extracting. zipParameters.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_256); List filesToAdd = Arrays.asList( new File("somefile"), new File("someotherfile") ); ZipFile zipFile = new ZipFile("filename.zip", "password".toCharArray()); zipFile.addFiles(filesToAdd, zipParameters); ~~~~ ##### Zip Standard encryption: Instead of AES, replace `zipParameters.setEncryptionMethod(EncryptionMethod.AES);` with `zipParameters.setEncryptionMethod(EncryptionMethod.ZIP_STANDARD);`. You can omit the line to set Aes Key strength. As the name suggests, this is only applicable for AES encryption. In all the above examples, you can similarly pass in zip parameters with appropriate password configuration to create a password protected zip file ### Creating a split zip file If you want to split the zip file over several files when the size exceeds a particular limit, you can do so like this: ~~~~ List filesToAdd = Arrays.asList( new File("somefile"), new File("someotherfile") ); ZipFile zipFile = new ZipFile("filename.zip"); zipFile.createSplitZipFile(filesToAdd, new ZipParameters(), true, 10485760); // using 10MB in this example ~~~~ Passing in `new ZipParameters()`, as in the above example, will make Zip4j use default zip parameters. Please look at [ZipParameters][7] to see the default configuration. Zip file format specifies a minimum of 65536 bytes (64kb) as a minimum length for split files. Zip4j will throw an exception if anything less than this value is specified. To create a split zip with password protection, pass in appropriate ZipParameters as shown in the example below: ~~~~ ZipParameters zipParameters = new ZipParameters(); zipParameters.setEncryptFiles(true); zipParameters.setEncryptionMethod(EncryptionMethod.AES); List filesToAdd = Arrays.asList( new File("somefile"), new File("someotherfile") ); ZipFile zipFile = new ZipFile("filename.zip", "password".toCharArray()); zipFile.createSplitZipFile(filesToAdd, zipParameters, true, 10485760); // using 10MB in this example ~~~~ ### Zip64 format Zip64 is a zip feature which allows support for zip files when the size of the zip file exceeds the maximum that can be stored in 4 bytes (i.e., greater than 4,294,967,295 bytes). Traditionally, zip headers have a provision of 4 bytes to store for file sizes. But with growing file sizes compared to a few decades back, zip file format extended support of file sizes which extends 4 bytes by adding additional headers which uses 8 bytes for file sizes (compressed and uncompressed file sizes). This feature is known as Zip64. Zip4j will automatically make a zip file a Zip64 format and add appropriate headers, when it detects the zip file to be crossing this file size limit. You do not have to explicitly specify any flag for Zip4j to use this feature. ### Extracting All files in a zip ~~~~ new ZipFile("filename.zip").extractAll("/destination_directory"); ~~~~ ### Extracting All files in a password protected zip ~~~~ new ZipFile("filename.zip", "password".toCharArray()).extractAll("/destination_directory"); ~~~~ ### Extracting a single file from zip ~~~~ new ZipFile("filename.zip").extractFile("fileNameInZip.txt", "/destination_directory"); ~~~~ ### Extracting a single file from zip which is password protected ~~~~ new ZipFile("filename.zip", "password".toCharArray()).extractFile("fileNameInZip.txt", "/destination_directory"); ~~~~ ### Extracting a single file from zip and giving it a new file name Below example will extract the file `fileNameInZip.txt` from the zip file to the output directory `/destination_directory` and will give the file a name `newfileName.txt`. Without the third parameter of the new file name, the same name as the file in the zip will be used, which in this case is `fileNameInZip.txt` ~~~~ new ZipFile("filename.zip", "password".toCharArray()).extractFile("fileNameInZip.txt", "/destination_directory", "newfileName.txt"); ~~~~ ### Get an input stream for an entry in a zip file ~~~~ ZipFile zipFile = new ZipFile("filename.zip"); FileHeader fileHeader = zipFile.getFileHeader("entry_name_in_zip.txt"); InputStream inputStream = zipFile.getInputStream(fileHeader); ~~~~ You can now use this input stream to read content from it/write content to an output stream. Please note that the entry/file name is relative to the directory it is in. If `entry_name_in_zip.txt` is in a folder called "root_folder" in the zip, then you can use `zipFile.getFileHeader("root_folder/entry_name_in_zip.txt");` ### Remove a file/entry from a zip file ~~~~ new ZipFile("filename.zip").removeFile("fileNameInZipToRemove"); ~~~~ Please note that the file name is relative the root folder in zip. That is, if the file you want to remove exists in a folder called "folder1", which in-turn exists in a folder called "root-folder", removing this file from zip can be done as below: ~~~~ new ZipFile("filename.zip").removeFile("root-folder/folder1/fileNameInZipToRemove"); ~~~~ If you want to be sure that the file you want to remove exists in zip file or if you don't want to deal with file names as string when dealing `removeFile` api, you can use the other overloaded method which takes in a `FileHeader`: ~~~~ ZipFile zipFile = new ZipFile("someZip.zip"); FileHeader fileHeader = zipFile.getFileHeader("fileNameInZipToRemove"); if (fileHeader == null) { // file does not exist } zipFile.removeFile(fileHeader); ~~~~ ### Merging split zip files into a single zip This is the reverse of creating a split zip file, that is, this feature will merge a zip file which is split across several files into a single zip file ~~~~ new ZipFile("split_zip_file.zip").mergeZipFile("merged_zip_file.zip"); ~~~~ This method will throw an exception if the split zip file (in this case `split_zip_file.zip`) is not a split zip file ### List all files in a zip ~~~~ List fileHeaders = new ZipFile("zipfile.zip").getFileHeaders(); fileHeaders.stream().forEach(fileHeader -> System.out.println(fileHeader.getFileName())); ~~~~ You can get all other information from the `FileHeader` object corresponding to each file/entry in the zip. ### Check if a zip file is password protected ~~~~ new ZipFile("encrypted_zip_file.zip").isEncrypted(); ~~~~ ### Check if a zip file is a split zip file ~~~~ new ZipFile("split_zip_file.zip").isSplitArchive(); ~~~~ ### Set comment for a zip file ~~~~ new ZipFile("some_zip_file.zip").setComment("Some comment"); ~~~~ ### Remove comment of a zip file ~~~~ new ZipFile("some_zip_file.zip").setComment(""); ~~~~ ### Get comment of a zip file ~~~~ new ZipFile("some_zip_file.zip").getComment(); ~~~~ ### Check if a zip file is valid Note: This will only check for the validity of the headers and not the validity of each entry in the zip file. ~~~~ new ZipFile("valid_zip_file.zip").isValidZipFile(); ~~~~ ## Working with streams ### Adding entries with ZipOutputStream ~~~~ import net.lingala.zip4j.io.outputstream.ZipOutputStream; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.AesKeyStrength; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.model.enums.EncryptionMethod; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.List; public class ZipOutputStreamExample { public void zipOutputStreamExample(File outputZipFile, List filesToAdd, char[] password, CompressionMethod compressionMethod, boolean encrypt, EncryptionMethod encryptionMethod, AesKeyStrength aesKeyStrength) throws IOException { ZipParameters zipParameters = buildZipParameters(compressionMethod, encrypt, encryptionMethod, aesKeyStrength); byte[] buff = new byte[4096]; int readLen; try(ZipOutputStream zos = initializeZipOutputStream(outputZipFile, encrypt, password)) { for (File fileToAdd : filesToAdd) { // Entry size has to be set if you want to add entries of STORE compression method (no compression) // This is not required for deflate compression if (zipParameters.getCompressionMethod() == CompressionMethod.STORE) { zipParameters.setEntrySize(fileToAdd.length()); } zipParameters.setFileNameInZip(fileToAdd.getName()); zos.putNextEntry(zipParameters); try(InputStream inputStream = new FileInputStream(fileToAdd)) { while ((readLen = inputStream.read(buff)) != -1) { zos.write(buff, 0, readLen); } } zos.closeEntry(); } } } private ZipOutputStream initializeZipOutputStream(File outputZipFile, boolean encrypt, char[] password) throws IOException { FileOutputStream fos = new FileOutputStream(outputZipFile); if (encrypt) { return new ZipOutputStream(fos, password); } return new ZipOutputStream(fos); } private ZipParameters buildZipParameters(CompressionMethod compressionMethod, boolean encrypt, EncryptionMethod encryptionMethod, AesKeyStrength aesKeyStrength) { ZipParameters zipParameters = new ZipParameters(); zipParameters.setCompressionMethod(compressionMethod); zipParameters.setEncryptionMethod(encryptionMethod); zipParameters.setAesKeyStrength(aesKeyStrength); zipParameters.setEncryptFiles(encrypt); return zipParameters; } } ~~~~ ### Extract files with ZipInputStream ~~~~ import net.lingala.zip4j.io.inputstream.ZipInputStream; import net.lingala.zip4j.model.LocalFileHeader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; public class ZipInputStreamExample { public void extractWithZipInputStream(File zipFile, char[] password) throws IOException { LocalFileHeader localFileHeader; int readLen; byte[] readBuffer = new byte[4096]; try (FileInputStream fileInputStream = new FileInputStream(zipFile); ZipInputStream zipInputStream = new ZipInputStream(fileInputStream, password)) { while ((localFileHeader = zipInputStream.getNextEntry()) != null) { File extractedFile = new File(localFileHeader.getFileName()); try (OutputStream outputStream = new FileOutputStream(extractedFile)) { while ((readLen = zipInputStream.read(readBuffer)) != -1) { outputStream.write(readBuffer, 0, readLen); } } } } } } ~~~~ ## Working with Progress Monitor ProgressMonitor makes it easier for applications (especially user facing) to integrate Zip4j. It is useful to show progress (example: updating a progress bar, displaying the current action, show file name being worked on, etc). To use ProgressMonitor, you have to set `ZipFile.setRunInThread(true)`. This will make any actions being done on the zip file to run in a background thread. You can then access ProgressMonitor `Zipfile.getProgressMonitor()` and get details of the current action being done along with the percentage work done, etc. Below is an example: ~~~ ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); ProgressMonitor progressMonitor = zipFile.getProgressMonitor(); zipFile.setRunInThread(true); zipFile.addFolder("/some/folder"); while (!progressMonitor.getState().equals(ProgressMonitor.State.READY)) { System.out.println("Percentage done: " + progressMonitor.getPercentDone()); System.out.println("Current file: " + progressMonitor.getFileName()); System.out.println("Current task: " + progressMonitor.getCurrentTask()); Thread.sleep(100); } ~~~ Note that in the above example, `addFolder()` will almost immediately return back the control to the caller. The client code can then perform a loop until the state gets back to "Ready" as shown in the above example. Similarly, ProgressMonitor can be used with other actions like, `addFiles`, `removeFiles` and `extractFiles`. ## Contribution It is hard to find as much free time as I used to have when I first started Zip4j 10 years back in 2009. I would highly appreciate any support I can get for this project. You can fork this project, and send me pull requests for any bug fixes, issues mentioned here or new features. If you need any support in understanding the code or zip specification, just drop me a mail and I will help you as best as I can. (See FAQ for my email id.) ## FAQ 1. **Why do I have to pass in password as char array and not as a string?** [That's why][8] 2. **How can I contact you?** srikanth.mailbox@gmail.com 3. **Are unicode file names supported?** Yes, unicode file names (UTF-8) are supported as specified by the zip format specification. Zip4j will use utf-8 file name and file comment encoding when creating a zip file. When extracting a zip file, Zip4j will only use utf-8 encoding, only if the appropriate header flag is set as specified by zip file format specification. If this flag is not set, Zip4j will use Cp437 encoding which only supports English alphabetical characters. 4. **Where can I find Zip file format specification?** [Here][9] 5. **Why are there so many changes in version 2.x compared to 1.x?** Because 1.x was written about 10 years back, Zip4j was badly in need of a face-lift and code modernization. Also, my coding standards have also improved over the years (or at least that's what I like to think). Although I am proud of the work I did with Zip4j 10 years back, some parts of the code make me feel like hiding my face in shame. One such example is the usage of `ArrayList` instead of `List`. Api and code should look much neater now. And also, Zip4j now supports a minimum of JRE 8, as compared to JRE 5 with 1.x, which obviously will bring some nice features that I can make use of. (For example: no more explicitly closing the streams all over the code). If you still feel like something can be improved (and I am pretty sure that there are things to be improved), please let me know by opening an issue here or writing to me (My email id is in point #2 above). [1]: https://stackoverflow.com/questions/9324933/what-is-a-good-java-library-to-zip-unzip-files [2]: https://stackoverflow.com/questions/5362364/java-library-to-work-with-zip-files [3]: https://stackoverflow.com/questions/166340/recommendations-on-a-free-library-to-be-used-for-zipping-files [4]: https://stackoverflow.com/questions/18201279/file-compression-library-for-java/18201553 [5]: https://www.baeldung.com/java-compress-and-uncompress [6]: https://mvnrepository.com/artifact/net.lingala.zip4j/zip4j [7]: https://github.com/srikanth-lingala/zip4j/blob/master/src/main/java/net/lingala/zip4j/model/ZipParameters.java [8]: https://www.baeldung.com/java-storing-passwords [9]: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT [10]: https://stackoverflow.com/questions/tagged/zip4j zip4j-2.2.8/pom.xml000066400000000000000000000145721357650161600141230ustar00rootroot00000000000000 4.0.0 net.lingala.zip4j zip4j 2.2.9-SNAPSHOT Zip4j Zip4j - A Java library for zip files and streams https://github.com/srikanth-lingala/zip4j git@github.com:srikanth-lingala/zip4j.git Srikanth Reddy Lingala srikanth.mailbox@gmail.com Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0.txt 1.8 1.8 4.12 1.18.8 3.12.2 2.28.2 2.0.2 UTF-8 ossrh https://oss.sonatype.org/content/repositories/snapshots ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ junit junit ${junit.version} test org.assertj assertj-core ${assertj.version} test org.mockito mockito-core ${mockito.version} test org.powermock powermock-module-junit4 ${powermock.version} test org.powermock powermock-api-mockito2 ${powermock.version} test src/main/java org.apache.maven.plugins maven-failsafe-plugin 3.0.0-M3 integration-test verify org.apache.maven.plugins maven-source-plugin 2.2.1 attach-sources deploy jar-no-fork org.apache.maven.plugins maven-javadoc-plugin 2.9.1 attach-javadocs deploy jar org.apache.maven.plugins maven-gpg-plugin 1.5 sign-artifacts deploy sign org.sonatype.plugins nexus-staging-maven-plugin 1.6.8 true ossrh https://oss.sonatype.org/ true org.apache.felix maven-bundle-plugin true net.lingala.zip4j.* bundle-manifest package bundle zip4j-2.2.8/src/000077500000000000000000000000001357650161600133645ustar00rootroot00000000000000zip4j-2.2.8/src/main/000077500000000000000000000000001357650161600143105ustar00rootroot00000000000000zip4j-2.2.8/src/main/java/000077500000000000000000000000001357650161600152315ustar00rootroot00000000000000zip4j-2.2.8/src/main/java/net/000077500000000000000000000000001357650161600160175ustar00rootroot00000000000000zip4j-2.2.8/src/main/java/net/lingala/000077500000000000000000000000001357650161600174265ustar00rootroot00000000000000zip4j-2.2.8/src/main/java/net/lingala/zip4j/000077500000000000000000000000001357650161600204665ustar00rootroot00000000000000zip4j-2.2.8/src/main/java/net/lingala/zip4j/ZipFile.java000077500000000000000000000722271357650161600227100ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.headers.HeaderReader; import net.lingala.zip4j.headers.HeaderUtil; import net.lingala.zip4j.headers.HeaderWriter; import net.lingala.zip4j.io.inputstream.ZipInputStream; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.RandomAccessFileMode; import net.lingala.zip4j.progress.ProgressMonitor; import net.lingala.zip4j.tasks.AddFilesToZipTask; import net.lingala.zip4j.tasks.AddFilesToZipTask.AddFilesToZipTaskParameters; import net.lingala.zip4j.tasks.AddFolderToZipTask; import net.lingala.zip4j.tasks.AddFolderToZipTask.AddFolderToZipTaskParameters; import net.lingala.zip4j.tasks.AddStreamToZipTask; import net.lingala.zip4j.tasks.AddStreamToZipTask.AddStreamToZipTaskParameters; import net.lingala.zip4j.tasks.ExtractAllFilesTask; import net.lingala.zip4j.tasks.ExtractAllFilesTask.ExtractAllFilesTaskParameters; import net.lingala.zip4j.tasks.ExtractFileTask; import net.lingala.zip4j.tasks.ExtractFileTask.ExtractFileTaskParameters; import net.lingala.zip4j.tasks.MergeSplitZipFileTask; import net.lingala.zip4j.tasks.MergeSplitZipFileTask.MergeSplitZipFileTaskParameters; import net.lingala.zip4j.tasks.RemoveEntryFromZipFileTask; import net.lingala.zip4j.tasks.RemoveEntryFromZipFileTask.RemoveEntryFromZipFileTaskParameters; import net.lingala.zip4j.tasks.SetCommentTask; import net.lingala.zip4j.tasks.SetCommentTask.SetCommentTaskTaskParameters; import net.lingala.zip4j.util.FileUtils; import net.lingala.zip4j.util.Zip4jUtil; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.nio.charset.Charset; import java.util.Collections; import java.util.List; import static net.lingala.zip4j.util.FileUtils.assertFilesExist; import static net.lingala.zip4j.util.InternalZipConstants.CHARSET_UTF_8; import static net.lingala.zip4j.util.UnzipUtil.createZipInputStream; import static net.lingala.zip4j.util.Zip4jUtil.isStringNotNullAndNotEmpty; /** * Base class to handle zip files. Some of the operations supported * in this class are:
*
    *
  • Create Zip File
  • *
  • Add files to zip file
  • *
  • Add folder to zip file
  • *
  • Extract files from zip files
  • *
  • Remove files from zip file
  • *
*/ public class ZipFile { private File zipFile; private ZipModel zipModel; private boolean isEncrypted; private ProgressMonitor progressMonitor; private boolean runInThread; private char[] password; private HeaderWriter headerWriter = new HeaderWriter(); private Charset charset = CHARSET_UTF_8; /** * Creates a new ZipFile instance with the zip file at the location specified in zipFile. * This constructor does not yet create a zip file if it does not exist. Creation happens when adding files * to this ZipFile instance * @param zipFile */ public ZipFile(String zipFile) { this(new File(zipFile), null); } /** * Creates a new ZipFile instance with the zip file at the location specified in zipFile. * Input password will be used for any zip operations like adding files or extracting files. * This constructor does not yet create a zip file if it does not exist. Creation happens when adding files * to this ZipFile instance * @param zipFile */ public ZipFile(String zipFile, char[] password) { this(new File(zipFile), password); } /** * Creates a new Zip File Object with the input file. * If the zip file does not exist, it is not created at this point. * * @param zipFile */ public ZipFile(File zipFile) { this(zipFile, null); } public ZipFile(File zipFile, char[] password) { this.zipFile = zipFile; this.password = password; this.runInThread = false; this.progressMonitor = new ProgressMonitor(); } /** * Creates a zip file and adds the list of source file(s) to the zip file. If the zip file * exists then this method throws an exception. Parameters such as compression type, etc * can be set in the input parameters. While the method addFile/addFiles also creates the * zip file if it does not exist, the main functionality of this method is to create a split * zip file. To create a split zip file, set the splitArchive parameter to true with a valid * splitLength. Split Length has to be more than 65536 bytes * * @param filesToAdd - File to be added to the zip file * @param parameters - zip parameters for this file list * @param splitArchive - if archive has to be split or not * @param splitLength - if archive has to be split, then length in bytes at which it has to be split * @throws ZipException */ public void createSplitZipFile(List filesToAdd, ZipParameters parameters, boolean splitArchive, long splitLength) throws ZipException { if (zipFile.exists()) { throw new ZipException("zip file: " + zipFile + " already exists. To add files to existing zip file use addFile method"); } if (filesToAdd == null || filesToAdd.size() == 0) { throw new ZipException("input file List is null, cannot create zip file"); } createNewZipModel(); zipModel.setSplitArchive(splitArchive); zipModel.setSplitLength(splitLength); new AddFilesToZipTask(progressMonitor, runInThread, zipModel, password, headerWriter).execute( new AddFilesToZipTaskParameters(filesToAdd, parameters, charset)); } /** * Creates a zip file and adds the files/folders from the specified folder to the zip file. * This method does the same functionality as in addFolder method except that this method * can also create split zip files when adding a folder. To create a split zip file, set the * splitArchive parameter to true and specify the splitLength. Split length has to be more than * or equal to 65536 bytes. Note that this method throws an exception if the zip file already * exists. * * @param folderToAdd * @param parameters * @param splitArchive * @param splitLength * @throws ZipException */ public void createSplitZipFileFromFolder(File folderToAdd, ZipParameters parameters, boolean splitArchive, long splitLength) throws ZipException { if (folderToAdd == null) { throw new ZipException("folderToAdd is null, cannot create zip file from folder"); } if (parameters == null) { throw new ZipException("input parameters are null, cannot create zip file from folder"); } if (zipFile.exists()) { throw new ZipException("zip file: " + zipFile + " already exists. To add files to existing zip file use addFolder method"); } createNewZipModel(); zipModel.setSplitArchive(splitArchive); if (splitArchive) { zipModel.setSplitLength(splitLength); } addFolder(folderToAdd, parameters, false); } /** * Adds input source file to the zip file with default zip parameters. If zip file does not exist, * this method creates a new zip file. * * @param fileToAdd - File with path to be added to the zip file * @throws ZipException */ public void addFile(String fileToAdd) throws ZipException { addFile(fileToAdd, new ZipParameters()); } /** * Adds input source file to the zip file with provided zip parameters. If zip file does not exist, * this method creates a new zip file. * * @param fileToAdd - File with path to be added to the zip file * @param zipParameters - parameters for the entry to be added to zip * @throws ZipException */ public void addFile(String fileToAdd, ZipParameters zipParameters) throws ZipException { if (!isStringNotNullAndNotEmpty(fileToAdd)) { throw new ZipException("file to add is null or empty"); } addFiles(Collections.singletonList(new File(fileToAdd)), zipParameters); } /** * Adds input source file to the zip file with default zip parameters. If zip file does not exist, * this method creates a new zip file. * * @param fileToAdd - File to be added to the zip file * @throws ZipException */ public void addFile(File fileToAdd) throws ZipException { addFiles(Collections.singletonList(fileToAdd), new ZipParameters()); } /** * Adds input source file to the zip file. If zip file does not exist, * this method creates a new zip file. Parameters such as compression type, etc * can be set in the input parameters. * * @param fileToAdd - File to be added to the zip file * @param parameters - zip parameters for this file * @throws ZipException */ public void addFile(File fileToAdd, ZipParameters parameters) throws ZipException { addFiles(Collections.singletonList(fileToAdd), parameters); } /** * Adds the list of input files to the zip file with default zip parameters. If zip file does not exist, * this method creates a new zip file. * * @param filesToAdd * @throws ZipException */ public void addFiles(List filesToAdd) throws ZipException { addFiles(filesToAdd, new ZipParameters()); } /** * Adds the list of input files to the zip file. If zip file does not exist, then * this method creates a new zip file. Parameters such as compression type, etc * can be set in the input parameters. * * @param filesToAdd * @param parameters * @throws ZipException */ public void addFiles(List filesToAdd, ZipParameters parameters) throws ZipException { if (filesToAdd == null || filesToAdd.size() == 0) { throw new ZipException("input file List is null or empty"); } if (parameters == null) { throw new ZipException("input parameters are null"); } if (progressMonitor.getState() == ProgressMonitor.State.BUSY) { throw new ZipException("invalid operation - Zip4j is in busy state"); } assertFilesExist(filesToAdd); readZipInfo(); if (zipModel == null) { throw new ZipException("internal error: zip model is null"); } if (zipFile.exists() && zipModel.isSplitArchive()) { throw new ZipException("Zip file already exists. Zip file format does not allow updating split/spanned files"); } new AddFilesToZipTask(progressMonitor, runInThread, zipModel, password, headerWriter).execute( new AddFilesToZipTaskParameters(filesToAdd, parameters, charset)); } /** * Adds the folder in the given file object to the zip file with default zip parameters. If zip file does not exist, * then a new zip file is created. If input folder is invalid then an exception * is thrown. * * @param folderToAdd * @throws ZipException */ public void addFolder(File folderToAdd) throws ZipException { addFolder(folderToAdd, new ZipParameters()); } /** * Adds the folder in the given file object to the zip file. If zip file does not exist, * then a new zip file is created. If input folder is invalid then an exception * is thrown. Zip parameters for the files in the folder to be added can be set in * the input parameters * * @param folderToAdd * @param zipParameters * @throws ZipException */ public void addFolder(File folderToAdd, ZipParameters zipParameters) throws ZipException { if (folderToAdd == null) { throw new ZipException("input path is null, cannot add folder to zip file"); } if (!folderToAdd.exists()) { throw new ZipException("folder does not exist"); } if (!folderToAdd.isDirectory()) { throw new ZipException("input folder is not a directory"); } if (!folderToAdd.canRead()) { throw new ZipException("cannot read input folder"); } if (zipParameters == null) { throw new ZipException("input parameters are null, cannot add folder to zip file"); } addFolder(folderToAdd, zipParameters, true); } /** * Internal method to add a folder to the zip file. * * @param folderToAdd * @param zipParameters * @param checkSplitArchive * @throws ZipException */ private void addFolder(File folderToAdd, ZipParameters zipParameters, boolean checkSplitArchive) throws ZipException { readZipInfo(); if (zipModel == null) { throw new ZipException("internal error: zip model is null"); } if (checkSplitArchive) { if (zipModel.isSplitArchive()) { throw new ZipException("This is a split archive. Zip file format does not allow updating split/spanned files"); } } new AddFolderToZipTask(progressMonitor, runInThread, zipModel, password, headerWriter).execute( new AddFolderToZipTaskParameters(folderToAdd, zipParameters, charset)); } /** * Creates a new entry in the zip file and adds the content of the input stream to the * zip file. ZipParameters.isSourceExternalStream and ZipParameters.fileNameInZip have to be * set before in the input parameters. If the file name ends with / or \, this method treats the * content as a directory. Setting the flag ProgressMonitor.setRunInThread to true will have * no effect for this method and hence this method cannot be used to add content to zip in * thread mode * * @param inputStream * @param parameters * @throws ZipException */ public void addStream(InputStream inputStream, ZipParameters parameters) throws ZipException { if (inputStream == null) { throw new ZipException("inputstream is null, cannot add file to zip"); } if (parameters == null) { throw new ZipException("zip parameters are null"); } this.setRunInThread(false); readZipInfo(); if (zipModel == null) { throw new ZipException("internal error: zip model is null"); } if (zipFile.exists() && zipModel.isSplitArchive()) { throw new ZipException("Zip file already exists. Zip file format does not allow updating split/spanned files"); } new AddStreamToZipTask(progressMonitor, runInThread, zipModel, password, headerWriter).execute( new AddStreamToZipTaskParameters(inputStream, parameters, charset)); } /** * Extracts all the files in the given zip file to the input destination path. * If zip file does not exist or destination path is invalid then an * exception is thrown. * * @param destinationPath * @throws ZipException */ public void extractAll(String destinationPath) throws ZipException { if (!isStringNotNullAndNotEmpty(destinationPath)) { throw new ZipException("output path is null or invalid"); } if (!Zip4jUtil.createDirectoryIfNotExists(new File(destinationPath))) { throw new ZipException("invalid output path"); } if (zipModel == null) { readZipInfo(); } // Throw an exception if zipModel is still null if (zipModel == null) { throw new ZipException("Internal error occurred when extracting zip file"); } if (progressMonitor.getState() == ProgressMonitor.State.BUSY) { throw new ZipException("invalid operation - Zip4j is in busy state"); } new ExtractAllFilesTask(progressMonitor, runInThread, zipModel, password).execute( new ExtractAllFilesTaskParameters(destinationPath, charset)); } /** * Extracts a specific file from the zip file to the destination path. * If destination path is invalid, then this method throws an exception. *

* If newFileName is not null or empty, newly created file name will be replaced by * the value in newFileName. If this value is null, then the file name will be the * value in FileHeader.getFileName * * @param fileHeader * @param destinationPath * @throws ZipException */ public void extractFile(FileHeader fileHeader, String destinationPath) throws ZipException { extractFile(fileHeader, destinationPath, null); } /** * Extracts a specific file from the zip file to the destination path. * If destination path is invalid, then this method throws an exception. * * @param fileHeader * @param destinationPath * @param newFileName * @throws ZipException */ public void extractFile(FileHeader fileHeader, String destinationPath, String newFileName) throws ZipException { if (fileHeader == null) { throw new ZipException("input file header is null, cannot extract file"); } if (!isStringNotNullAndNotEmpty(destinationPath)) { throw new ZipException("destination path is empty or null, cannot extract file"); } if (progressMonitor.getState() == ProgressMonitor.State.BUSY) { throw new ZipException("invalid operation - Zip4j is in busy state"); } readZipInfo(); new ExtractFileTask(progressMonitor, runInThread, zipModel, password).execute( new ExtractFileTaskParameters(destinationPath, fileHeader, newFileName, charset)); } /** * Extracts a specific file from the zip file to the destination path. * This method first finds the necessary file header from the input file name. *

* File name is relative file name in the zip file. For example if a zip file contains * a file "a.txt", then to extract this file, input file name has to be "a.txt". Another * example is if there is a file "b.txt" in a folder "abc" in the zip file, then the * input file name has to be abc/b.txt *

* Throws an exception if file header could not be found for the given file name or if * the destination path is invalid * * @param fileName * @param destinationPath * @throws ZipException */ public void extractFile(String fileName, String destinationPath) throws ZipException { extractFile(fileName, destinationPath, null); } /** * Extracts a specific file from the zip file to the destination path. * This method first finds the necessary file header from the input file name. *

* File name is relative file name in the zip file. For example if a zip file contains * a file "a.txt", then to extract this file, input file name has to be "a.txt". Another * example is if there is a file "b.txt" in a folder "abc" in the zip file, then the * input file name has to be abc/b.txt *

* If newFileName is not null or empty, newly created file name will be replaced by * the value in newFileName. If this value is null, then the file name will be the * value in FileHeader.getFileName *

* Throws an exception if file header could not be found for the given file name or if * the destination path is invalid * * @param fileName * @param destinationPath * @param newFileName * @throws ZipException */ public void extractFile(String fileName, String destinationPath, String newFileName) throws ZipException { if (!isStringNotNullAndNotEmpty(fileName)) { throw new ZipException("file to extract is null or empty, cannot extract file"); } readZipInfo(); FileHeader fileHeader = HeaderUtil.getFileHeader(zipModel, fileName); if (fileHeader == null) { throw new ZipException("No file found with name " + fileName + " in zip file"); } extractFile(fileHeader, destinationPath, newFileName); } /** * Returns the list of file headers in the zip file. Returns an empty list if the zip file does not exist. * * @return list of file headers * @throws ZipException */ public List getFileHeaders() throws ZipException { readZipInfo(); if (zipModel == null || zipModel.getCentralDirectory() == null) { return Collections.emptyList(); } return zipModel.getCentralDirectory().getFileHeaders(); } /** * Returns FileHeader if a file header with the given fileHeader * string exists in the zip model: If not returns null * * @param fileName * @return FileHeader * @throws ZipException */ public FileHeader getFileHeader(String fileName) throws ZipException { if (!isStringNotNullAndNotEmpty(fileName)) { throw new ZipException("input file name is emtpy or null, cannot get FileHeader"); } readZipInfo(); if (zipModel == null || zipModel.getCentralDirectory() == null) { return null; } return HeaderUtil.getFileHeader(zipModel, fileName); } /** * Checks to see if the zip file is encrypted * * @return true if encrypted, false if not * @throws ZipException */ public boolean isEncrypted() throws ZipException { if (zipModel == null) { readZipInfo(); if (zipModel == null) { throw new ZipException("Zip Model is null"); } } if (zipModel.getCentralDirectory() == null || zipModel.getCentralDirectory().getFileHeaders() == null) { throw new ZipException("invalid zip file"); } for (FileHeader fileHeader : zipModel.getCentralDirectory().getFileHeaders()) { if (fileHeader != null) { if (fileHeader.isEncrypted()) { isEncrypted = true; break; } } } return isEncrypted; } /** * Checks if the zip file is a split archive * * @return true if split archive, false if not * @throws ZipException */ public boolean isSplitArchive() throws ZipException { if (zipModel == null) { readZipInfo(); if (zipModel == null) { throw new ZipException("Zip Model is null"); } } return zipModel.isSplitArchive(); } /** * Removes the file provided in the input parameters from the zip file. * This method first finds the file header and then removes the file. * If file does not exist, then this method throws an exception. * If zip file is a split zip file, then this method throws an exception as * zip specification does not allow for updating split zip archives. * * @param fileName * @throws ZipException */ public void removeFile(String fileName) throws ZipException { if (!isStringNotNullAndNotEmpty(fileName)) { throw new ZipException("file name is empty or null, cannot remove file"); } if (zipModel == null) { readZipInfo(); } if (zipModel.isSplitArchive()) { throw new ZipException("Zip file format does not allow updating split/spanned files"); } FileHeader fileHeader = HeaderUtil.getFileHeader(zipModel, fileName); if (fileHeader == null) { throw new ZipException("could not find file header for file: " + fileName); } removeFile(fileHeader); } /** * Removes the file provided in the input file header from the zip file. * If zip file is a split zip file, then this method throws an exception as * zip specification does not allow for updating split zip archives. * * @param fileHeader * @throws ZipException */ public void removeFile(FileHeader fileHeader) throws ZipException { if (fileHeader == null) { throw new ZipException("input file header is null, cannot remove file"); } if (zipModel == null) { readZipInfo(); } if (zipModel.isSplitArchive()) { throw new ZipException("Zip file format does not allow updating split/spanned files"); } new RemoveEntryFromZipFileTask(progressMonitor, runInThread, zipModel).execute( new RemoveEntryFromZipFileTaskParameters(fileHeader, charset)); } /** * Merges split zip files into a single zip file without the need to extract the * files in the archive * * @param outputZipFile * @throws ZipException */ public void mergeSplitFiles(File outputZipFile) throws ZipException { if (outputZipFile == null) { throw new ZipException("outputZipFile is null, cannot merge split files"); } if (outputZipFile.exists()) { throw new ZipException("output Zip File already exists"); } readZipInfo(); if (this.zipModel == null) { throw new ZipException("zip model is null, corrupt zip file?"); } new MergeSplitZipFileTask(progressMonitor, runInThread, zipModel).execute( new MergeSplitZipFileTaskParameters(outputZipFile, charset)); } /** * Sets comment for the Zip file * * @param comment * @throws ZipException */ public void setComment(String comment) throws ZipException { if (comment == null) { throw new ZipException("input comment is null, cannot update zip file"); } if (!zipFile.exists()) { throw new ZipException("zip file does not exist, cannot set comment for zip file"); } readZipInfo(); if (zipModel == null) { throw new ZipException("zipModel is null, cannot update zip file"); } if (zipModel.getEndOfCentralDirectoryRecord() == null) { throw new ZipException("end of central directory is null, cannot set comment"); } new SetCommentTask(progressMonitor, runInThread, zipModel).execute( new SetCommentTaskTaskParameters(comment, charset)); } /** * Returns the comment set for the Zip file * * @return String * @throws ZipException */ public String getComment() throws ZipException { if (!zipFile.exists()) { throw new ZipException("zip file does not exist, cannot read comment"); } readZipInfo(); if (zipModel == null) { throw new ZipException("zip model is null, cannot read comment"); } if (zipModel.getEndOfCentralDirectoryRecord() == null) { throw new ZipException("end of central directory record is null, cannot read comment"); } return zipModel.getEndOfCentralDirectoryRecord().getComment(); } /** * Returns an input stream for reading the contents of the Zip file corresponding * to the input FileHeader. Throws an exception if the FileHeader does not exist * in the ZipFile * * @param fileHeader * @return ZipInputStream * @throws ZipException */ public ZipInputStream getInputStream(FileHeader fileHeader) throws IOException { if (fileHeader == null) { throw new ZipException("FileHeader is null, cannot get InputStream"); } readZipInfo(); if (zipModel == null) { throw new ZipException("zip model is null, cannot get inputstream"); } return createZipInputStream(zipModel, fileHeader, password); } /** * Checks to see if the input zip file is a valid zip file. This method * will try to read zip headers. If headers are read successfully, this * method returns true else false * * @return boolean * @since 1.2.3 */ public boolean isValidZipFile() { if (!zipFile.exists()) { return false; } try { readZipInfo(); return true; } catch (Exception e) { return false; } } /** * Returns the full file path+names of all split zip files * in an ArrayList. For example: If a split zip file(abc.zip) has a 10 split parts * this method returns an array list with path + "abc.z01", path + "abc.z02", etc. * Returns null if the zip file does not exist * * @return List of Split zip Files * @throws ZipException */ public List getSplitZipFiles() throws ZipException { readZipInfo(); return FileUtils.getSplitZipFiles(zipModel); } /** * Sets a password to be used for the zip file. Will override if a password supplied via ZipFile constructor * @param password - char array of the password to be used */ public void setPassword(char[] password) { this.password = password; } /** * Reads the zip header information for this zip file. If the zip file * does not exist, it creates an empty zip model.

* Note: This method does not read local file header information * * @throws ZipException */ private void readZipInfo() throws ZipException { if (zipModel != null) { return; } if (!zipFile.exists()) { createNewZipModel(); return; } if (!zipFile.canRead()) { throw new ZipException("no read access for the input zip file"); } try (RandomAccessFile randomAccessFile = new RandomAccessFile(zipFile, RandomAccessFileMode.READ.getValue())) { HeaderReader headerReader = new HeaderReader(); zipModel = headerReader.readAllHeaders(randomAccessFile, charset); zipModel.setZipFile(zipFile); } catch (ZipException e) { throw e; } catch (IOException e) { throw new ZipException(e); } } /** * Creates a new instance of zip model * * @throws ZipException */ private void createNewZipModel() { zipModel = new ZipModel(); zipModel.setZipFile(zipFile); } public ProgressMonitor getProgressMonitor() { return progressMonitor; } public boolean isRunInThread() { return runInThread; } public void setRunInThread(boolean runInThread) { this.runInThread = runInThread; } public File getFile() { return zipFile; } public Charset getCharset() { return charset; } public void setCharset(Charset charset) throws IllegalArgumentException { if(charset == null) { throw new IllegalArgumentException("charset cannot be null"); } this.charset = charset; } @Override public String toString() { return zipFile.toString(); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/crypto/000077500000000000000000000000001357650161600220065ustar00rootroot00000000000000zip4j-2.2.8/src/main/java/net/lingala/zip4j/crypto/AESDecrypter.java000077500000000000000000000103471357650161600251530ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.crypto; import net.lingala.zip4j.crypto.PBKDF2.MacBasedPRF; import net.lingala.zip4j.crypto.PBKDF2.PBKDF2Engine; import net.lingala.zip4j.crypto.PBKDF2.PBKDF2Parameters; import net.lingala.zip4j.crypto.engine.AESEngine; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.AESExtraDataRecord; import net.lingala.zip4j.model.enums.AesKeyStrength; import java.util.Arrays; import static net.lingala.zip4j.crypto.AesCipherUtil.prepareBuffAESIVBytes; import static net.lingala.zip4j.util.InternalZipConstants.AES_BLOCK_SIZE; public class AESDecrypter implements Decrypter { public static final int PASSWORD_VERIFIER_LENGTH = 2; private AESExtraDataRecord aesExtraDataRecord; private char[] password; private AESEngine aesEngine; private MacBasedPRF mac; private int nonce = 1; private byte[] iv; private byte[] counterBlock; public AESDecrypter(AESExtraDataRecord aesExtraDataRecord, char[] password, byte[] salt, byte[] passwordVerifier) throws ZipException { this.aesExtraDataRecord = aesExtraDataRecord; this.password = password; iv = new byte[AES_BLOCK_SIZE]; counterBlock = new byte[AES_BLOCK_SIZE]; init(salt, passwordVerifier); } private void init(byte[] salt, byte[] passwordVerifier) throws ZipException { AesKeyStrength aesKeyStrength = aesExtraDataRecord.getAesKeyStrength(); if (password == null || password.length <= 0) { throw new ZipException("empty or null password provided for AES Decryptor"); } byte[] derivedKey = deriveKey(salt, password, aesKeyStrength.getKeyLength(), aesKeyStrength.getMacLength()); if (derivedKey == null || derivedKey.length != (aesKeyStrength.getKeyLength() + aesKeyStrength.getMacLength() + PASSWORD_VERIFIER_LENGTH)) { throw new ZipException("invalid derived key"); } byte[] aesKey = new byte[aesKeyStrength.getKeyLength()]; byte[] macKey = new byte[aesKeyStrength.getMacLength()]; byte[] derivedPasswordVerifier = new byte[PASSWORD_VERIFIER_LENGTH]; System.arraycopy(derivedKey, 0, aesKey, 0, aesKeyStrength.getKeyLength()); System.arraycopy(derivedKey, aesKeyStrength.getKeyLength(), macKey, 0, aesKeyStrength.getMacLength()); System.arraycopy(derivedKey, aesKeyStrength.getKeyLength() + aesKeyStrength.getMacLength(), derivedPasswordVerifier, 0, PASSWORD_VERIFIER_LENGTH); if (!Arrays.equals(passwordVerifier, derivedPasswordVerifier)) { throw new ZipException("Wrong Password", ZipException.Type.WRONG_PASSWORD); } aesEngine = new AESEngine(aesKey); mac = new MacBasedPRF("HmacSHA1"); mac.init(macKey); } @Override public int decryptData(byte[] buff, int start, int len) throws ZipException { for (int j = start; j < (start + len); j += AES_BLOCK_SIZE) { int loopCount = (j + AES_BLOCK_SIZE <= (start + len)) ? AES_BLOCK_SIZE : ((start + len) - j); mac.update(buff, j, loopCount); prepareBuffAESIVBytes(iv, nonce); aesEngine.processBlock(iv, counterBlock); for (int k = 0; k < loopCount; k++) { buff[j + k] = (byte) (buff[j + k] ^ counterBlock[k]); } nonce++; } return len; } private byte[] deriveKey(byte[] salt, char[] password, int keyLength, int macLength) { PBKDF2Parameters p = new PBKDF2Parameters("HmacSHA1", "ISO-8859-1", salt, 1000); PBKDF2Engine e = new PBKDF2Engine(p); return e.deriveKey(password, keyLength + macLength + PASSWORD_VERIFIER_LENGTH); } public byte[] getCalculatedAuthenticationBytes() { return mac.doFinal(); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/crypto/AESEncrpyter.java000077500000000000000000000134611357650161600251650ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.crypto; import net.lingala.zip4j.crypto.PBKDF2.MacBasedPRF; import net.lingala.zip4j.crypto.PBKDF2.PBKDF2Engine; import net.lingala.zip4j.crypto.PBKDF2.PBKDF2Parameters; import net.lingala.zip4j.crypto.engine.AESEngine; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.enums.AesKeyStrength; import java.util.Random; import static net.lingala.zip4j.crypto.AesCipherUtil.prepareBuffAESIVBytes; import static net.lingala.zip4j.util.InternalZipConstants.AES_BLOCK_SIZE; public class AESEncrpyter implements Encrypter { private static final int PASSWORD_VERIFIER_LENGTH = 2; private char[] password; private AesKeyStrength aesKeyStrength; private AESEngine aesEngine; private MacBasedPRF mac; private boolean finished; private int nonce = 1; private int loopCount = 0; private byte[] iv; private byte[] counterBlock; private byte[] derivedPasswordVerifier; private byte[] saltBytes; public AESEncrpyter(char[] password, AesKeyStrength aesKeyStrength) throws ZipException { if (password == null || password.length == 0) { throw new ZipException("input password is empty or null"); } if (aesKeyStrength != AesKeyStrength.KEY_STRENGTH_128 && aesKeyStrength != AesKeyStrength.KEY_STRENGTH_256) { throw new ZipException("Invalid AES key strength"); } this.password = password; this.aesKeyStrength = aesKeyStrength; this.finished = false; counterBlock = new byte[AES_BLOCK_SIZE]; iv = new byte[AES_BLOCK_SIZE]; init(); } private void init() throws ZipException { int keyLength = aesKeyStrength.getKeyLength(); int macLength = aesKeyStrength.getMacLength(); int saltLength = aesKeyStrength.getSaltLength(); saltBytes = generateSalt(saltLength); byte[] keyBytes = deriveKey(saltBytes, password, keyLength, macLength); if (keyBytes == null || keyBytes.length != (keyLength + macLength + PASSWORD_VERIFIER_LENGTH)) { throw new ZipException("invalid key generated, cannot decrypt file"); } byte[] aesKey = new byte[keyLength]; byte[] macKey = new byte[macLength]; derivedPasswordVerifier = new byte[PASSWORD_VERIFIER_LENGTH]; System.arraycopy(keyBytes, 0, aesKey, 0, keyLength); System.arraycopy(keyBytes, keyLength, macKey, 0, macLength); System.arraycopy(keyBytes, keyLength + macLength, derivedPasswordVerifier, 0, PASSWORD_VERIFIER_LENGTH); aesEngine = new AESEngine(aesKey); mac = new MacBasedPRF("HmacSHA1"); mac.init(macKey); } private byte[] deriveKey(byte[] salt, char[] password, int keyLength, int macLength) throws ZipException { try { PBKDF2Parameters p = new PBKDF2Parameters("HmacSHA1", "ISO-8859-1", salt, 1000); PBKDF2Engine e = new PBKDF2Engine(p); byte[] derivedKey = e.deriveKey(password, keyLength + macLength + PASSWORD_VERIFIER_LENGTH); return derivedKey; } catch (Exception e) { throw new ZipException(e); } } public int encryptData(byte[] buff) throws ZipException { if (buff == null) { throw new ZipException("input bytes are null, cannot perform AES encrpytion"); } return encryptData(buff, 0, buff.length); } public int encryptData(byte[] buff, int start, int len) throws ZipException { if (finished) { // A non 16 byte block has already been passed to encrypter // non 16 byte block should be the last block of compressed data in AES encryption // any more encryption will lead to corruption of data throw new ZipException("AES Encrypter is in finished state (A non 16 byte block has already been passed to encrypter)"); } if (len % 16 != 0) { this.finished = true; } for (int j = start; j < (start + len); j += AES_BLOCK_SIZE) { loopCount = (j + AES_BLOCK_SIZE <= (start + len)) ? AES_BLOCK_SIZE : ((start + len) - j); prepareBuffAESIVBytes(iv, nonce); aesEngine.processBlock(iv, counterBlock); for (int k = 0; k < loopCount; k++) { buff[j + k] = (byte) (buff[j + k] ^ counterBlock[k]); } mac.update(buff, j, loopCount); nonce++; } return len; } private static byte[] generateSalt(int size) throws ZipException { if (size != 8 && size != 16) { throw new ZipException("invalid salt size, cannot generate salt"); } int rounds = 0; if (size == 8) rounds = 2; if (size == 16) rounds = 4; byte[] salt = new byte[size]; for (int j = 0; j < rounds; j++) { Random rand = new Random(); int i = rand.nextInt(); salt[0 + j * 4] = (byte) (i >> 24); salt[1 + j * 4] = (byte) (i >> 16); salt[2 + j * 4] = (byte) (i >> 8); salt[3 + j * 4] = (byte) i; } return salt; } public byte[] getFinalMac() { byte[] rawMacBytes = mac.doFinal(); byte[] macBytes = new byte[10]; System.arraycopy(rawMacBytes, 0, macBytes, 0, 10); return macBytes; } public byte[] getDerivedPasswordVerifier() { return derivedPasswordVerifier; } public byte[] getSaltBytes() { return saltBytes; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/crypto/AesCipherUtil.java000066400000000000000000000005221357650161600253510ustar00rootroot00000000000000package net.lingala.zip4j.crypto; public class AesCipherUtil { public static void prepareBuffAESIVBytes(byte[] buff, int nonce) { buff[0] = (byte) nonce; buff[1] = (byte) (nonce >> 8); buff[2] = (byte) (nonce >> 16); buff[3] = (byte) (nonce >> 24); for (int i = 4; i <= 15; i++) { buff[i] = 0; } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/crypto/Decrypter.java000077500000000000000000000014621357650161600246200ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.crypto; import net.lingala.zip4j.exception.ZipException; public interface Decrypter { int decryptData(byte[] buff, int start, int len) throws ZipException; } zip4j-2.2.8/src/main/java/net/lingala/zip4j/crypto/Encrypter.java000077500000000000000000000015511357650161600246310ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.crypto; import net.lingala.zip4j.exception.ZipException; public interface Encrypter { int encryptData(byte[] buff) throws ZipException; int encryptData(byte[] buff, int start, int len) throws ZipException; } zip4j-2.2.8/src/main/java/net/lingala/zip4j/crypto/PBKDF2/000077500000000000000000000000001357650161600227165ustar00rootroot00000000000000zip4j-2.2.8/src/main/java/net/lingala/zip4j/crypto/PBKDF2/BinTools.java000077500000000000000000000040401357650161600253130ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.crypto.PBKDF2; /* * Source referred from Matthias Gartner's PKCS#5 implementation - * see http://rtner.de/software/PBKDF2.html */ class BinTools { public static final String hex = "0123456789ABCDEF"; public static String bin2hex(final byte[] b) { if (b == null) { return ""; } StringBuffer sb = new StringBuffer(2 * b.length); for (int i = 0; i < b.length; i++) { int v = (256 + b[i]) % 256; sb.append(hex.charAt((v / 16) & 15)); sb.append(hex.charAt((v % 16) & 15)); } return sb.toString(); } public static byte[] hex2bin(final String s) { String m = s; if (s == null) { // Allow empty input string. m = ""; } else if (s.length() % 2 != 0) { // Assume leading zero for odd string length m = "0" + s; } byte r[] = new byte[m.length() / 2]; for (int i = 0, n = 0; i < m.length(); n++) { char h = m.charAt(i++); char l = m.charAt(i++); r[n] = (byte) (hex2bin(h) * 16 + hex2bin(l)); } return r; } public static int hex2bin(char c) { if (c >= '0' && c <= '9') { return (c - '0'); } if (c >= 'A' && c <= 'F') { return (c - 'A' + 10); } if (c >= 'a' && c <= 'f') { return (c - 'a' + 10); } throw new IllegalArgumentException("Input string may only contain hex digits, but found '" + c + "'"); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/crypto/PBKDF2/MacBasedPRF.java000077500000000000000000000040141357650161600255720ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.crypto.PBKDF2; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; /* * Source referred from Matthias Gartner's PKCS#5 implementation - * see http://rtner.de/software/PBKDF2.html */ public class MacBasedPRF implements PRF { private Mac mac; private int hLen; private String macAlgorithm; public MacBasedPRF(String macAlgorithm) { this.macAlgorithm = macAlgorithm; try { mac = Mac.getInstance(macAlgorithm); hLen = mac.getMacLength(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } public byte[] doFinal(byte[] M) { return mac.doFinal(M); } public byte[] doFinal() { return mac.doFinal(); } public int getHLen() { return hLen; } public void init(byte[] P) { try { mac.init(new SecretKeySpec(P, macAlgorithm)); } catch (InvalidKeyException e) { throw new RuntimeException(e); } } public void update(byte[] U) { try { mac.update(U); } catch (IllegalStateException e) { throw new RuntimeException(e); } } public void update(byte[] U, int start, int len) { try { mac.update(U, start, len); } catch (IllegalStateException e) { throw new RuntimeException(e); } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/crypto/PBKDF2/PBKDF2Engine.java000077500000000000000000000101741357650161600256250ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.crypto.PBKDF2; import static net.lingala.zip4j.util.Zip4jUtil.convertCharArrayToByteArray; /* * Source referred from Matthias Gartner's PKCS#5 implementation - * see http://rtner.de/software/PBKDF2.html */ public class PBKDF2Engine { private PBKDF2Parameters parameters; private PRF prf; public PBKDF2Engine(PBKDF2Parameters parameters) { this(parameters, null); } public PBKDF2Engine(PBKDF2Parameters parameters, PRF prf) { this.parameters = parameters; this.prf = prf; } public byte[] deriveKey(char[] inputPassword) { return deriveKey(inputPassword, 0); } public byte[] deriveKey(char[] inputPassword, int dkLen) { byte p[]; if (inputPassword == null) { throw new NullPointerException(); } p = convertCharArrayToByteArray(inputPassword); assertPRF(p); if (dkLen == 0) { dkLen = prf.getHLen(); } return PBKDF2(prf, parameters.getSalt(), parameters.getIterationCount(), dkLen); } public boolean verifyKey(char[] inputPassword) { byte[] referenceKey = getParameters().getDerivedKey(); if (referenceKey == null || referenceKey.length == 0) { return false; } byte[] inputKey = deriveKey(inputPassword, referenceKey.length); if (inputKey == null || inputKey.length != referenceKey.length) { return false; } for (int i = 0; i < inputKey.length; i++) { if (inputKey[i] != referenceKey[i]) { return false; } } return true; } private void assertPRF(byte[] P) { if (prf == null) { prf = new MacBasedPRF(parameters.getHashAlgorithm()); } prf.init(P); } public PRF getPseudoRandomFunction() { return prf; } private byte[] PBKDF2(PRF prf, byte[] S, int c, int dkLen) { if (S == null) { S = new byte[0]; } int hLen = prf.getHLen(); int l = ceil(dkLen, hLen); int r = dkLen - (l - 1) * hLen; byte T[] = new byte[l * hLen]; int ti_offset = 0; for (int i = 1; i <= l; i++) { _F(T, ti_offset, prf, S, c, i); ti_offset += hLen; } if (r < hLen) { // Incomplete last block byte DK[] = new byte[dkLen]; System.arraycopy(T, 0, DK, 0, dkLen); return DK; } return T; } private int ceil(int a, int b) { int m = 0; if (a % b > 0) { m = 1; } return a / b + m; } private void _F(byte[] dest, int offset, PRF prf, byte[] S, int c, int blockIndex) { int hLen = prf.getHLen(); byte U_r[] = new byte[hLen]; // U0 = S || INT (i); byte U_i[] = new byte[S.length + 4]; System.arraycopy(S, 0, U_i, 0, S.length); INT(U_i, S.length, blockIndex); for (int i = 0; i < c; i++) { U_i = prf.doFinal(U_i); xor(U_r, U_i); } System.arraycopy(U_r, 0, dest, offset, hLen); } private void xor(byte[] dest, byte[] src) { for (int i = 0; i < dest.length; i++) { dest[i] ^= src[i]; } } protected void INT(byte[] dest, int offset, int i) { dest[offset] = (byte) (i / (256 * 256 * 256)); dest[offset + 1] = (byte) (i / (256 * 256)); dest[offset + 2] = (byte) (i / (256)); dest[offset + 3] = (byte) (i); } public PBKDF2Parameters getParameters() { return parameters; } public void setParameters(PBKDF2Parameters parameters) { this.parameters = parameters; } public void setPseudoRandomFunction(PRF prf) { this.prf = prf; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/crypto/PBKDF2/PBKDF2HexFormatter.java000077500000000000000000000027651357650161600270370ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.crypto.PBKDF2; /* * Source referred from Matthias Gartner's PKCS#5 implementation - * see http://rtner.de/software/PBKDF2.html */ class PBKDF2HexFormatter { public boolean fromString(PBKDF2Parameters p, String s) { if (p == null || s == null) { return true; } String[] pSplit = s.split(":"); if (pSplit.length != 3) { return true; } byte salt[] = BinTools.hex2bin(pSplit[0]); int iterationCount = Integer.parseInt(pSplit[1]); byte bDK[] = BinTools.hex2bin(pSplit[2]); p.setSalt(salt); p.setIterationCount(iterationCount); p.setDerivedKey(bDK); return false; } public String toString(PBKDF2Parameters p) { String s = BinTools.bin2hex(p.getSalt()) + ":" + String.valueOf(p.getIterationCount()) + ":" + BinTools.bin2hex(p.getDerivedKey()); return s; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/crypto/PBKDF2/PBKDF2Parameters.java000077500000000000000000000046601357650161600265260ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.crypto.PBKDF2; /* * Source referred from Matthias Gartner's PKCS#5 implementation - * see http://rtner.de/software/PBKDF2.html */ public class PBKDF2Parameters { protected byte[] salt; protected int iterationCount; protected String hashAlgorithm; protected String hashCharset; protected byte[] derivedKey; public PBKDF2Parameters() { this.hashAlgorithm = null; this.hashCharset = "UTF-8"; this.salt = null; this.iterationCount = 1000; this.derivedKey = null; } public PBKDF2Parameters(String hashAlgorithm, String hashCharset, byte[] salt, int iterationCount) { this(hashAlgorithm, hashCharset, salt, iterationCount, null); } public PBKDF2Parameters(String hashAlgorithm, String hashCharset, byte[] salt, int iterationCount, byte[] derivedKey) { this.hashAlgorithm = hashAlgorithm; this.hashCharset = hashCharset; this.salt = salt; this.iterationCount = iterationCount; this.derivedKey = derivedKey; } public int getIterationCount() { return iterationCount; } public void setIterationCount(int iterationCount) { this.iterationCount = iterationCount; } public byte[] getSalt() { return salt; } public void setSalt(byte[] salt) { this.salt = salt; } public byte[] getDerivedKey() { return derivedKey; } public void setDerivedKey(byte[] derivedKey) { this.derivedKey = derivedKey; } public String getHashAlgorithm() { return hashAlgorithm; } public void setHashAlgorithm(String hashAlgorithm) { this.hashAlgorithm = hashAlgorithm; } public String getHashCharset() { return hashCharset; } public void setHashCharset(String hashCharset) { this.hashCharset = hashCharset; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/crypto/PBKDF2/PRF.java000077500000000000000000000015641357650161600242210ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.crypto.PBKDF2; /* * Source referred from Matthias Gartner's PKCS#5 implementation - * see http://rtner.de/software/PBKDF2.html */ interface PRF { void init(byte[] P); byte[] doFinal(byte[] M); int getHLen(); } zip4j-2.2.8/src/main/java/net/lingala/zip4j/crypto/StandardDecrypter.java000077500000000000000000000057321357650161600263050ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.crypto; import net.lingala.zip4j.crypto.engine.ZipCryptoEngine; import net.lingala.zip4j.exception.ZipException; import static net.lingala.zip4j.util.InternalZipConstants.STD_DEC_HDR_SIZE; public class StandardDecrypter implements Decrypter { private char[] password; private byte[] crcBytes; private byte[] crc = new byte[4]; private ZipCryptoEngine zipCryptoEngine; public StandardDecrypter(char[] password, byte[] crcBytes , byte[] headerBytes) throws ZipException { this.password = password; this.crcBytes = crcBytes; this.zipCryptoEngine = new ZipCryptoEngine(); init(headerBytes); } public int decryptData(byte[] buff, int start, int len) throws ZipException { if (start < 0 || len < 0) { throw new ZipException("one of the input parameters were null in standard decrypt data"); } for (int i = start; i < start + len; i++) { int val = buff[i] & 0xff; val = (val ^ zipCryptoEngine.decryptByte()) & 0xff; zipCryptoEngine.updateKeys((byte) val); buff[i] = (byte) val; } return len; } private void init(byte[] headerBytes) throws ZipException { crc[3] = (byte) (crcBytes[3] & 0xFF); crc[2] = (byte) ((crcBytes[3] >> 8) & 0xFF); crc[1] = (byte) ((crcBytes[3] >> 16) & 0xFF); crc[0] = (byte) ((crcBytes[3] >> 24) & 0xFF); if (crc[2] > 0 || crc[1] > 0 || crc[0] > 0) throw new IllegalStateException("Invalid CRC in File Header"); if (password == null || password.length <= 0) { throw new ZipException("Wrong password!", ZipException.Type.WRONG_PASSWORD); } zipCryptoEngine.initKeys(password); int result = headerBytes[0]; for (int i = 0; i < STD_DEC_HDR_SIZE; i++) { // Commented this as this check cannot always be trusted // New functionality: If there is an error in extracting a password protected file, // "Wrong Password?" text is appended to the exception message // if(i+1 == InternalZipConstants.STD_DEC_HDR_SIZE && ((byte)(result ^ zipCryptoEngine.decryptByte()) != crc[3]) && !isSplit) // throw new ZipException("Wrong password!", ZipExceptionConstants.WRONG_PASSWORD); zipCryptoEngine.updateKeys((byte) (result ^ zipCryptoEngine.decryptByte())); if (i + 1 != STD_DEC_HDR_SIZE) result = headerBytes[i + 1]; } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/crypto/StandardEncrypter.java000077500000000000000000000062561357650161600263210ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.crypto; import net.lingala.zip4j.crypto.engine.ZipCryptoEngine; import net.lingala.zip4j.exception.ZipException; import java.util.Random; import static net.lingala.zip4j.util.InternalZipConstants.STD_DEC_HDR_SIZE; public class StandardEncrypter implements Encrypter { private ZipCryptoEngine zipCryptoEngine; private byte[] headerBytes; public StandardEncrypter(char[] password, long key) throws ZipException { this.zipCryptoEngine = new ZipCryptoEngine(); this.headerBytes = new byte[STD_DEC_HDR_SIZE]; init(password, key); } private void init(char[] password, long key) throws ZipException { if (password == null || password.length <= 0) { throw new ZipException("input password is null or empty, cannot initialize standard encrypter"); } zipCryptoEngine.initKeys(password); headerBytes = generateRandomBytes(STD_DEC_HDR_SIZE); // Initialize again since the generated bytes were encrypted. zipCryptoEngine.initKeys(password); headerBytes[STD_DEC_HDR_SIZE - 1] = (byte) ((key >>> 24)); headerBytes[STD_DEC_HDR_SIZE - 2] = (byte) ((key >>> 16)); if (headerBytes.length < STD_DEC_HDR_SIZE) { throw new ZipException("invalid header bytes generated, cannot perform standard encryption"); } encryptData(headerBytes); } public int encryptData(byte[] buff) throws ZipException { if (buff == null) { throw new NullPointerException(); } return encryptData(buff, 0, buff.length); } public int encryptData(byte[] buff, int start, int len) throws ZipException { if (len < 0) { throw new ZipException("invalid length specified to decrpyt data"); } for (int i = start; i < start + len; i++) { buff[i] = encryptByte(buff[i]); } return len; } protected byte encryptByte(byte val) { byte temp_val = (byte) (val ^ zipCryptoEngine.decryptByte() & 0xff); zipCryptoEngine.updateKeys(val); return temp_val; } protected byte[] generateRandomBytes(int size) throws ZipException { if (size <= 0) { throw new ZipException("size is either 0 or less than 0, cannot generate header for standard encryptor"); } byte[] buff = new byte[size]; Random rand = new Random(); for (int i = 0; i < buff.length; i++) { // Encrypted to get less predictability for poorly implemented rand functions. buff[i] = encryptByte((byte) rand.nextInt(256)); } return buff; } public byte[] getHeaderBytes() { return headerBytes; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/crypto/engine/000077500000000000000000000000001357650161600232535ustar00rootroot00000000000000zip4j-2.2.8/src/main/java/net/lingala/zip4j/crypto/engine/AESEngine.java000077500000000000000000000333561357650161600256710ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.crypto.engine; import net.lingala.zip4j.exception.ZipException; import static net.lingala.zip4j.util.InternalZipConstants.AES_BLOCK_SIZE; /** * Core Engine for AES Encryption * * @author Srikanth Reddy Lingala */ public class AESEngine { private int rounds; private int[][] workingKey = null; private int C0, C1, C2, C3; public AESEngine(byte[] key) throws ZipException { init(key); } private void init(byte[] key) throws ZipException { workingKey = generateWorkingKey(key); } private int[][] generateWorkingKey(byte[] key) throws ZipException { int kc = key.length / 4; int t; if (((kc != 4) && (kc != 6) && (kc != 8)) || ((kc * 4) != key.length)) { throw new ZipException("invalid key length (not 128/192/256)"); } rounds = kc + 6; int[][] W = new int[rounds + 1][4]; t = 0; int i = 0; while (i < key.length) { W[t >> 2][t & 3] = (key[i] & 0xff) | ((key[i + 1] & 0xff) << 8) | ((key[i + 2] & 0xff) << 16) | (key[i + 3] << 24); i += 4; t++; } int k = (rounds + 1) << 2; for (i = kc; (i < k); i++) { int temp = W[(i - 1) >> 2][(i - 1) & 3]; if ((i % kc) == 0) { temp = subWord(shift(temp, 8)) ^ rcon[(i / kc) - 1]; } else if ((kc > 6) && ((i % kc) == 4)) { temp = subWord(temp); } W[i >> 2][i & 3] = W[(i - kc) >> 2][(i - kc) & 3] ^ temp; } return W; } public int processBlock(byte[] in, byte[] out) throws ZipException { return processBlock(in, 0, out, 0); } public int processBlock(byte[] in, int inOff, byte[] out, int outOff) throws ZipException { if (workingKey == null) { throw new ZipException("AES engine not initialised"); } if ((inOff + (32 / 2)) > in.length) { throw new ZipException("input buffer too short"); } if ((outOff + (32 / 2)) > out.length) { throw new ZipException("output buffer too short"); } stateIn(in, inOff); encryptBlock(workingKey); stateOut(out, outOff); return AES_BLOCK_SIZE; } private void stateIn(byte[] bytes, int off) { int index = off; C0 = (bytes[index++] & 0xff); C0 |= (bytes[index++] & 0xff) << 8; C0 |= (bytes[index++] & 0xff) << 16; C0 |= bytes[index++] << 24; C1 = (bytes[index++] & 0xff); C1 |= (bytes[index++] & 0xff) << 8; C1 |= (bytes[index++] & 0xff) << 16; C1 |= bytes[index++] << 24; C2 = (bytes[index++] & 0xff); C2 |= (bytes[index++] & 0xff) << 8; C2 |= (bytes[index++] & 0xff) << 16; C2 |= bytes[index++] << 24; C3 = (bytes[index++] & 0xff); C3 |= (bytes[index++] & 0xff) << 8; C3 |= (bytes[index++] & 0xff) << 16; C3 |= bytes[index++] << 24; } private void stateOut(byte[] bytes, int off) { int index = off; bytes[index++] = (byte) C0; bytes[index++] = (byte) (C0 >> 8); bytes[index++] = (byte) (C0 >> 16); bytes[index++] = (byte) (C0 >> 24); bytes[index++] = (byte) C1; bytes[index++] = (byte) (C1 >> 8); bytes[index++] = (byte) (C1 >> 16); bytes[index++] = (byte) (C1 >> 24); bytes[index++] = (byte) C2; bytes[index++] = (byte) (C2 >> 8); bytes[index++] = (byte) (C2 >> 16); bytes[index++] = (byte) (C2 >> 24); bytes[index++] = (byte) C3; bytes[index++] = (byte) (C3 >> 8); bytes[index++] = (byte) (C3 >> 16); bytes[index++] = (byte) (C3 >> 24); } private void encryptBlock(int[][] KW) { int r, r0, r1, r2, r3; C0 ^= KW[0][0]; C1 ^= KW[0][1]; C2 ^= KW[0][2]; C3 ^= KW[0][3]; r = 1; while (r < rounds - 1) { r0 = T0[C0 & 255] ^ shift(T0[(C1 >> 8) & 255], 24) ^ shift(T0[(C2 >> 16) & 255], 16) ^ shift(T0[(C3 >> 24) & 255], 8) ^ KW[r][0]; r1 = T0[C1 & 255] ^ shift(T0[(C2 >> 8) & 255], 24) ^ shift(T0[(C3 >> 16) & 255], 16) ^ shift(T0[(C0 >> 24) & 255], 8) ^ KW[r][1]; r2 = T0[C2 & 255] ^ shift(T0[(C3 >> 8) & 255], 24) ^ shift(T0[(C0 >> 16) & 255], 16) ^ shift(T0[(C1 >> 24) & 255], 8) ^ KW[r][2]; r3 = T0[C3 & 255] ^ shift(T0[(C0 >> 8) & 255], 24) ^ shift(T0[(C1 >> 16) & 255], 16) ^ shift(T0[(C2 >> 24) & 255], 8) ^ KW[r++][3]; C0 = T0[r0 & 255] ^ shift(T0[(r1 >> 8) & 255], 24) ^ shift(T0[(r2 >> 16) & 255], 16) ^ shift(T0[(r3 >> 24) & 255], 8) ^ KW[r][0]; C1 = T0[r1 & 255] ^ shift(T0[(r2 >> 8) & 255], 24) ^ shift(T0[(r3 >> 16) & 255], 16) ^ shift(T0[(r0 >> 24) & 255], 8) ^ KW[r][1]; C2 = T0[r2 & 255] ^ shift(T0[(r3 >> 8) & 255], 24) ^ shift(T0[(r0 >> 16) & 255], 16) ^ shift(T0[(r1 >> 24) & 255], 8) ^ KW[r][2]; C3 = T0[r3 & 255] ^ shift(T0[(r0 >> 8) & 255], 24) ^ shift(T0[(r1 >> 16) & 255], 16) ^ shift(T0[(r2 >> 24) & 255], 8) ^ KW[r++][3]; } r0 = T0[C0 & 255] ^ shift(T0[(C1 >> 8) & 255], 24) ^ shift(T0[(C2 >> 16) & 255], 16) ^ shift(T0[(C3 >> 24) & 255], 8) ^ KW[r][0]; r1 = T0[C1 & 255] ^ shift(T0[(C2 >> 8) & 255], 24) ^ shift(T0[(C3 >> 16) & 255], 16) ^ shift(T0[(C0 >> 24) & 255], 8) ^ KW[r][1]; r2 = T0[C2 & 255] ^ shift(T0[(C3 >> 8) & 255], 24) ^ shift(T0[(C0 >> 16) & 255], 16) ^ shift(T0[(C1 >> 24) & 255], 8) ^ KW[r][2]; r3 = T0[C3 & 255] ^ shift(T0[(C0 >> 8) & 255], 24) ^ shift(T0[(C1 >> 16) & 255], 16) ^ shift(T0[(C2 >> 24) & 255], 8) ^ KW[r++][3]; C0 = (S[r0 & 255] & 255) ^ ((S[(r1 >> 8) & 255] & 255) << 8) ^ ((S[(r2 >> 16) & 255] & 255) << 16) ^ (S[(r3 >> 24) & 255] << 24) ^ KW[r][0]; C1 = (S[r1 & 255] & 255) ^ ((S[(r2 >> 8) & 255] & 255) << 8) ^ ((S[(r3 >> 16) & 255] & 255) << 16) ^ (S[(r0 >> 24) & 255] << 24) ^ KW[r][1]; C2 = (S[r2 & 255] & 255) ^ ((S[(r3 >> 8) & 255] & 255) << 8) ^ ((S[(r0 >> 16) & 255] & 255) << 16) ^ (S[(r1 >> 24) & 255] << 24) ^ KW[r][2]; C3 = (S[r3 & 255] & 255) ^ ((S[(r0 >> 8) & 255] & 255) << 8) ^ ((S[(r1 >> 16) & 255] & 255) << 16) ^ (S[(r2 >> 24) & 255] << 24) ^ KW[r][3]; } private int shift(int r, int shift) { return (r >>> shift) | (r << -shift); } private int subWord(int x) { return (S[x & 255] & 255 | ((S[(x >> 8) & 255] & 255) << 8) | ((S[(x >> 16) & 255] & 255) << 16) | S[(x >> 24) & 255] << 24); } private static final byte[] S = { (byte) 99, (byte) 124, (byte) 119, (byte) 123, (byte) 242, (byte) 107, (byte) 111, (byte) 197, (byte) 48, (byte) 1, (byte) 103, (byte) 43, (byte) 254, (byte) 215, (byte) 171, (byte) 118, (byte) 202, (byte) 130, (byte) 201, (byte) 125, (byte) 250, (byte) 89, (byte) 71, (byte) 240, (byte) 173, (byte) 212, (byte) 162, (byte) 175, (byte) 156, (byte) 164, (byte) 114, (byte) 192, (byte) 183, (byte) 253, (byte) 147, (byte) 38, (byte) 54, (byte) 63, (byte) 247, (byte) 204, (byte) 52, (byte) 165, (byte) 229, (byte) 241, (byte) 113, (byte) 216, (byte) 49, (byte) 21, (byte) 4, (byte) 199, (byte) 35, (byte) 195, (byte) 24, (byte) 150, (byte) 5, (byte) 154, (byte) 7, (byte) 18, (byte) 128, (byte) 226, (byte) 235, (byte) 39, (byte) 178, (byte) 117, (byte) 9, (byte) 131, (byte) 44, (byte) 26, (byte) 27, (byte) 110, (byte) 90, (byte) 160, (byte) 82, (byte) 59, (byte) 214, (byte) 179, (byte) 41, (byte) 227, (byte) 47, (byte) 132, (byte) 83, (byte) 209, (byte) 0, (byte) 237, (byte) 32, (byte) 252, (byte) 177, (byte) 91, (byte) 106, (byte) 203, (byte) 190, (byte) 57, (byte) 74, (byte) 76, (byte) 88, (byte) 207, (byte) 208, (byte) 239, (byte) 170, (byte) 251, (byte) 67, (byte) 77, (byte) 51, (byte) 133, (byte) 69, (byte) 249, (byte) 2, (byte) 127, (byte) 80, (byte) 60, (byte) 159, (byte) 168, (byte) 81, (byte) 163, (byte) 64, (byte) 143, (byte) 146, (byte) 157, (byte) 56, (byte) 245, (byte) 188, (byte) 182, (byte) 218, (byte) 33, (byte) 16, (byte) 255, (byte) 243, (byte) 210, (byte) 205, (byte) 12, (byte) 19, (byte) 236, (byte) 95, (byte) 151, (byte) 68, (byte) 23, (byte) 196, (byte) 167, (byte) 126, (byte) 61, (byte) 100, (byte) 93, (byte) 25, (byte) 115, (byte) 96, (byte) 129, (byte) 79, (byte) 220, (byte) 34, (byte) 42, (byte) 144, (byte) 136, (byte) 70, (byte) 238, (byte) 184, (byte) 20, (byte) 222, (byte) 94, (byte) 11, (byte) 219, (byte) 224, (byte) 50, (byte) 58, (byte) 10, (byte) 73, (byte) 6, (byte) 36, (byte) 92, (byte) 194, (byte) 211, (byte) 172, (byte) 98, (byte) 145, (byte) 149, (byte) 228, (byte) 121, (byte) 231, (byte) 200, (byte) 55, (byte) 109, (byte) 141, (byte) 213, (byte) 78, (byte) 169, (byte) 108, (byte) 86, (byte) 244, (byte) 234, (byte) 101, (byte) 122, (byte) 174, (byte) 8, (byte) 186, (byte) 120, (byte) 37, (byte) 46, (byte) 28, (byte) 166, (byte) 180, (byte) 198, (byte) 232, (byte) 221, (byte) 116, (byte) 31, (byte) 75, (byte) 189, (byte) 139, (byte) 138, (byte) 112, (byte) 62, (byte) 181, (byte) 102, (byte) 72, (byte) 3, (byte) 246, (byte) 14, (byte) 97, (byte) 53, (byte) 87, (byte) 185, (byte) 134, (byte) 193, (byte) 29, (byte) 158, (byte) 225, (byte) 248, (byte) 152, (byte) 17, (byte) 105, (byte) 217, (byte) 142, (byte) 148, (byte) 155, (byte) 30, (byte) 135, (byte) 233, (byte) 206, (byte) 85, (byte) 40, (byte) 223, (byte) 140, (byte) 161, (byte) 137, (byte) 13, (byte) 191, (byte) 230, (byte) 66, (byte) 104, (byte) 65, (byte) 153, (byte) 45, (byte) 15, (byte) 176, (byte) 84, (byte) 187, (byte) 22, }; private static final int[] rcon = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91}; private static final int[] T0 = { 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c}; } zip4j-2.2.8/src/main/java/net/lingala/zip4j/crypto/engine/ZipCryptoEngine.java000077500000000000000000000033301357650161600272110ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.crypto.engine; public class ZipCryptoEngine { private final int keys[] = new int[3]; private static final int[] CRC_TABLE = new int[256]; static { for (int i = 0; i < 256; i++) { int r = i; for (int j = 0; j < 8; j++) { if ((r & 1) == 1) { r = (r >>> 1) ^ 0xedb88320; } else { r >>>= 1; } } CRC_TABLE[i] = r; } } public void initKeys(char[] password) { keys[0] = 305419896; keys[1] = 591751049; keys[2] = 878082192; for (int i = 0; i < password.length; i++) { updateKeys((byte) (password[i] & 0xff)); } } public void updateKeys(byte charAt) { keys[0] = crc32(keys[0], charAt); keys[1] += keys[0] & 0xff; keys[1] = keys[1] * 134775813 + 1; keys[2] = crc32(keys[2], (byte) (keys[1] >> 24)); } private int crc32(int oldCrc, byte charAt) { return ((oldCrc >>> 8) ^ CRC_TABLE[(oldCrc ^ charAt) & 0xff]); } public byte decryptByte() { int temp = keys[2] | 2; return (byte) ((temp * (temp ^ 1)) >>> 8); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/exception/000077500000000000000000000000001357650161600224645ustar00rootroot00000000000000zip4j-2.2.8/src/main/java/net/lingala/zip4j/exception/ZipException.java000077500000000000000000000030021357650161600257460ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.exception; import java.io.IOException; public class ZipException extends IOException { private static final long serialVersionUID = 1L; private Type type = Type.UNKNOWN; public ZipException(String message) { super(message); } public ZipException(Exception rootException) { super(rootException); } public ZipException(String message, Exception rootException) { super(message, rootException); } public ZipException(String message, Type type) { super(message); this.type = type; } public ZipException(String message, Throwable throwable, Type type) { super(message, throwable); this.type = type; } public Type getType() { return type; } public enum Type { WRONG_PASSWORD, TASK_CANCELLED_EXCEPTION, CHECKSUM_MISMATCH, UNKNOWN_COMPRESSION_METHOD, UNKNOWN } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/headers/000077500000000000000000000000001357650161600221015ustar00rootroot00000000000000zip4j-2.2.8/src/main/java/net/lingala/zip4j/headers/FileHeaderFactory.java000066400000000000000000000164301357650161600262700ustar00rootroot00000000000000package net.lingala.zip4j.headers; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.AESExtraDataRecord; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.LocalFileHeader; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.AesKeyStrength; import net.lingala.zip4j.model.enums.CompressionLevel; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.model.enums.EncryptionMethod; import net.lingala.zip4j.util.InternalZipConstants; import net.lingala.zip4j.util.Zip4jUtil; import java.nio.charset.Charset; import static net.lingala.zip4j.util.BitUtils.setBit; import static net.lingala.zip4j.util.BitUtils.unsetBit; import static net.lingala.zip4j.util.FileUtils.isZipEntryDirectory; public class FileHeaderFactory { public FileHeader generateFileHeader(ZipParameters zipParameters, boolean isSplitZip, int currentDiskNumberStart, Charset charset) throws ZipException { FileHeader fileHeader = new FileHeader(); fileHeader.setSignature(HeaderSignature.CENTRAL_DIRECTORY); fileHeader.setVersionMadeBy(20); fileHeader.setVersionNeededToExtract(20); if (zipParameters.isEncryptFiles() && zipParameters.getEncryptionMethod() == EncryptionMethod.AES) { fileHeader.setCompressionMethod(CompressionMethod.AES_INTERNAL_ONLY); fileHeader.setAesExtraDataRecord(generateAESExtraDataRecord(zipParameters)); } else { fileHeader.setCompressionMethod(zipParameters.getCompressionMethod()); } if (zipParameters.isEncryptFiles()) { if (zipParameters.getEncryptionMethod() == null || zipParameters.getEncryptionMethod() == EncryptionMethod.NONE) { throw new ZipException("Encryption method has to be set when encryptFiles flag is set in zip parameters"); } fileHeader.setEncrypted(true); fileHeader.setEncryptionMethod(zipParameters.getEncryptionMethod()); } String fileName = validateAndGetFileName(zipParameters.getFileNameInZip()); fileHeader.setFileName(fileName); fileHeader.setFileNameLength(determineFileNameLength(fileName, charset)); fileHeader.setDiskNumberStart(isSplitZip ? currentDiskNumberStart : 0); if (zipParameters.getLastModifiedFileTime() > 0) { fileHeader.setLastModifiedTime(Zip4jUtil.javaToDosTime(zipParameters.getLastModifiedFileTime())); } else { fileHeader.setLastModifiedTime(Zip4jUtil.javaToDosTime(System.currentTimeMillis())); } //For files added by this library, this attribute will be set after closeEntry is done fileHeader.setExternalFileAttributes(new byte[4]); fileHeader.setDirectory(isZipEntryDirectory(fileName)); if (zipParameters.isWriteExtendedLocalFileHeader() && zipParameters.getEntrySize() == -1) { fileHeader.setUncompressedSize(0); } else { fileHeader.setUncompressedSize(zipParameters.getEntrySize()); } if (zipParameters.isEncryptFiles() && zipParameters.getEncryptionMethod() == EncryptionMethod.ZIP_STANDARD) { fileHeader.setCrc(zipParameters.getEntryCRC()); } fileHeader.setGeneralPurposeFlag(determineGeneralPurposeBitFlag(fileHeader.isEncrypted(), zipParameters, charset)); fileHeader.setDataDescriptorExists(zipParameters.isWriteExtendedLocalFileHeader()); return fileHeader; } public LocalFileHeader generateLocalFileHeader(FileHeader fileHeader) { LocalFileHeader localFileHeader = new LocalFileHeader(); localFileHeader.setSignature(HeaderSignature.LOCAL_FILE_HEADER); localFileHeader.setVersionNeededToExtract(fileHeader.getVersionNeededToExtract()); localFileHeader.setCompressionMethod(fileHeader.getCompressionMethod()); localFileHeader.setLastModifiedTime(fileHeader.getLastModifiedTime()); localFileHeader.setUncompressedSize(fileHeader.getUncompressedSize()); localFileHeader.setFileNameLength(fileHeader.getFileNameLength()); localFileHeader.setFileName(fileHeader.getFileName()); localFileHeader.setEncrypted(fileHeader.isEncrypted()); localFileHeader.setEncryptionMethod(fileHeader.getEncryptionMethod()); localFileHeader.setAesExtraDataRecord(fileHeader.getAesExtraDataRecord()); localFileHeader.setCrc(fileHeader.getCrc()); localFileHeader.setCompressedSize(fileHeader.getCompressedSize()); localFileHeader.setGeneralPurposeFlag(fileHeader.getGeneralPurposeFlag().clone()); localFileHeader.setDataDescriptorExists(fileHeader.isDataDescriptorExists()); return localFileHeader; } private byte[] determineGeneralPurposeBitFlag(boolean isEncrypted, ZipParameters zipParameters, Charset charset) { byte[] generalPurposeBitFlag = new byte[2]; generalPurposeBitFlag[0] = generateFirstGeneralPurposeByte(isEncrypted, zipParameters); if(charset.equals(InternalZipConstants.CHARSET_UTF_8)) { generalPurposeBitFlag[1] = setBit(generalPurposeBitFlag[1], 3); // set 3rd bit which corresponds to utf-8 file name charset } return generalPurposeBitFlag; } private byte generateFirstGeneralPurposeByte(boolean isEncrypted, ZipParameters zipParameters) { byte firstByte = 0; if (isEncrypted) { firstByte = setBit(firstByte, 0); } if (zipParameters.getCompressionMethod() == CompressionMethod.DEFLATE) { if (zipParameters.getCompressionLevel() == CompressionLevel.NORMAL) { firstByte = unsetBit(firstByte, 1); firstByte = unsetBit(firstByte, 2); } else if (zipParameters.getCompressionLevel() == CompressionLevel.MAXIMUM) { firstByte = setBit(firstByte, 1); firstByte = unsetBit(firstByte, 2); } else if (zipParameters.getCompressionLevel() == CompressionLevel.FAST) { firstByte = unsetBit(firstByte, 1); firstByte = setBit(firstByte, 2); } else if (zipParameters.getCompressionLevel() == CompressionLevel.FASTEST) { firstByte = setBit(firstByte, 1); firstByte = setBit(firstByte, 2); } } if (zipParameters.isWriteExtendedLocalFileHeader()) { firstByte = setBit(firstByte, 3); } return firstByte; } private String validateAndGetFileName(String fileNameInZip) throws ZipException { if (!Zip4jUtil.isStringNotNullAndNotEmpty(fileNameInZip)) { throw new ZipException("fileNameInZip is null or empty"); } return fileNameInZip; } private AESExtraDataRecord generateAESExtraDataRecord(ZipParameters parameters) throws ZipException { AESExtraDataRecord aesExtraDataRecord = new AESExtraDataRecord(); if (parameters.getAesVersion() != null) { aesExtraDataRecord.setAesVersion(parameters.getAesVersion()); } if (parameters.getAesKeyStrength() == AesKeyStrength.KEY_STRENGTH_128) { aesExtraDataRecord.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_128); } else if (parameters.getAesKeyStrength() == AesKeyStrength.KEY_STRENGTH_192) { aesExtraDataRecord.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_192); } else if (parameters.getAesKeyStrength() == AesKeyStrength.KEY_STRENGTH_256) { aesExtraDataRecord.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_256); } else { throw new ZipException("invalid AES key strength"); } aesExtraDataRecord.setCompressionMethod(parameters.getCompressionMethod()); return aesExtraDataRecord; } private int determineFileNameLength(String fileName, Charset charset) { return fileName.getBytes(charset).length; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/headers/HeaderReader.java000077500000000000000000000713701357650161600252720ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.headers; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.AESExtraDataRecord; import net.lingala.zip4j.model.CentralDirectory; import net.lingala.zip4j.model.DataDescriptor; import net.lingala.zip4j.model.DigitalSignature; import net.lingala.zip4j.model.EndOfCentralDirectoryRecord; import net.lingala.zip4j.model.ExtraDataRecord; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.LocalFileHeader; import net.lingala.zip4j.model.Zip64EndOfCentralDirectoryLocator; import net.lingala.zip4j.model.Zip64EndOfCentralDirectoryRecord; import net.lingala.zip4j.model.Zip64ExtendedInfo; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.model.enums.AesKeyStrength; import net.lingala.zip4j.model.enums.AesVersion; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.model.enums.EncryptionMethod; import net.lingala.zip4j.util.RawIO; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.math.BigInteger; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.List; import static net.lingala.zip4j.headers.HeaderUtil.decodeStringWithCharset; import static net.lingala.zip4j.util.BitUtils.isBitSet; import static net.lingala.zip4j.util.InternalZipConstants.ENDHDR; import static net.lingala.zip4j.util.InternalZipConstants.ZIP_64_SIZE_LIMIT; import static net.lingala.zip4j.util.Zip4jUtil.readFully; /** * Helper class to read header information for the zip file */ public class HeaderReader { private ZipModel zipModel; private RawIO rawIO = new RawIO(); private byte[] intBuff = new byte[4]; public ZipModel readAllHeaders(RandomAccessFile zip4jRaf, Charset charset) throws IOException { if (zip4jRaf.length() < ENDHDR) { throw new ZipException("Zip file size less than minimum expected zip file size. " + "Probably not a zip file or a corrupted zip file"); } zipModel = new ZipModel(); try { zipModel.setEndOfCentralDirectoryRecord(readEndOfCentralDirectoryRecord(zip4jRaf, rawIO, charset)); } catch (ZipException e){ throw e; } catch (IOException e) { throw new ZipException("Zip headers not found. Probably not a zip file or a corrupted zip file", e); } // If file is Zip64 format, Zip64 headers have to be read before reading central directory zipModel.setZip64EndOfCentralDirectoryLocator(readZip64EndOfCentralDirectoryLocator(zip4jRaf, rawIO)); if (zipModel.isZip64Format()) { zipModel.setZip64EndOfCentralDirectoryRecord(readZip64EndCentralDirRec(zip4jRaf, rawIO)); if (zipModel.getZip64EndOfCentralDirectoryRecord() != null && zipModel.getZip64EndOfCentralDirectoryRecord().getNumberOfThisDisk() > 0) { zipModel.setSplitArchive(true); } else { zipModel.setSplitArchive(false); } } zipModel.setCentralDirectory(readCentralDirectory(zip4jRaf, rawIO, charset)); return zipModel; } private EndOfCentralDirectoryRecord readEndOfCentralDirectoryRecord(RandomAccessFile zip4jRaf, RawIO rawIO, Charset charset) throws IOException { long pos = zip4jRaf.length() - ENDHDR; EndOfCentralDirectoryRecord endOfCentralDirectoryRecord = new EndOfCentralDirectoryRecord(); int counter = 0; int headerSignature; do { zip4jRaf.seek(pos--); counter++; } while (((headerSignature = rawIO.readIntLittleEndian(zip4jRaf)) != HeaderSignature.END_OF_CENTRAL_DIRECTORY.getValue()) && counter <= 3000); if (headerSignature != HeaderSignature.END_OF_CENTRAL_DIRECTORY.getValue()) { throw new ZipException("Zip headers not found. Probably not a zip file"); } endOfCentralDirectoryRecord.setSignature(HeaderSignature.END_OF_CENTRAL_DIRECTORY); endOfCentralDirectoryRecord.setNumberOfThisDisk(rawIO.readShortLittleEndian(zip4jRaf)); endOfCentralDirectoryRecord.setNumberOfThisDiskStartOfCentralDir(rawIO.readShortLittleEndian(zip4jRaf)); endOfCentralDirectoryRecord.setTotalNumberOfEntriesInCentralDirectoryOnThisDisk( rawIO.readShortLittleEndian(zip4jRaf)); endOfCentralDirectoryRecord.setTotalNumberOfEntriesInCentralDirectory(rawIO.readShortLittleEndian(zip4jRaf)); endOfCentralDirectoryRecord.setSizeOfCentralDirectory(rawIO.readIntLittleEndian(zip4jRaf)); zip4jRaf.readFully(intBuff); endOfCentralDirectoryRecord.setOffsetOfStartOfCentralDirectory(rawIO.readLongLittleEndian(intBuff, 0)); int commentLength = rawIO.readShortLittleEndian(zip4jRaf); if (commentLength > 0) { byte[] commentBuf = new byte[commentLength]; zip4jRaf.readFully(commentBuf); endOfCentralDirectoryRecord.setComment(new String(commentBuf, charset)); } else { endOfCentralDirectoryRecord.setComment(null); } zipModel.setSplitArchive(endOfCentralDirectoryRecord.getNumberOfThisDisk() > 0); return endOfCentralDirectoryRecord; } private CentralDirectory readCentralDirectory(RandomAccessFile zip4jRaf, RawIO rawIO, Charset charset) throws IOException { CentralDirectory centralDirectory = new CentralDirectory(); List fileHeaders = new ArrayList<>(); long offSetStartCentralDir = getOffsetCentralDirectory(zipModel); long centralDirEntryCount = getNumberOfEntriesInCentralDirectory(zipModel); if (zipModel.isZip64Format()) { offSetStartCentralDir = zipModel.getZip64EndOfCentralDirectoryRecord() .getOffsetStartCentralDirectoryWRTStartDiskNumber(); centralDirEntryCount = (int) zipModel.getZip64EndOfCentralDirectoryRecord() .getTotalNumberOfEntriesInCentralDirectory(); } zip4jRaf.seek(offSetStartCentralDir); byte[] shortBuff = new byte[2]; byte[] intBuff = new byte[4]; for (int i = 0; i < centralDirEntryCount; i++) { FileHeader fileHeader = new FileHeader(); if (rawIO.readIntLittleEndian(zip4jRaf) != HeaderSignature.CENTRAL_DIRECTORY.getValue()) { throw new ZipException("Expected central directory entry not found (#" + (i + 1) + ")"); } fileHeader.setSignature(HeaderSignature.CENTRAL_DIRECTORY); fileHeader.setVersionMadeBy(rawIO.readShortLittleEndian(zip4jRaf)); fileHeader.setVersionNeededToExtract(rawIO.readShortLittleEndian(zip4jRaf)); byte[] generalPurposeFlags = new byte[2]; zip4jRaf.readFully(generalPurposeFlags); fileHeader.setEncrypted(isBitSet(generalPurposeFlags[0], 0)); fileHeader.setDataDescriptorExists(isBitSet(generalPurposeFlags[0], 3)); fileHeader.setFileNameUTF8Encoded(isBitSet(generalPurposeFlags[1], 3)); fileHeader.setGeneralPurposeFlag(generalPurposeFlags.clone()); fileHeader.setCompressionMethod(CompressionMethod.getCompressionMethodFromCode(rawIO.readShortLittleEndian( zip4jRaf))); fileHeader.setLastModifiedTime(rawIO.readIntLittleEndian(zip4jRaf)); zip4jRaf.readFully(intBuff); fileHeader.setCrc(rawIO.readLongLittleEndian(intBuff, 0)); fileHeader.setCrcRawData(intBuff); fileHeader.setCompressedSize(rawIO.readLongLittleEndian(zip4jRaf, 4)); fileHeader.setUncompressedSize(rawIO.readLongLittleEndian(zip4jRaf, 4)); int fileNameLength = rawIO.readShortLittleEndian(zip4jRaf); fileHeader.setFileNameLength(fileNameLength); fileHeader.setExtraFieldLength(rawIO.readShortLittleEndian(zip4jRaf)); int fileCommentLength = rawIO.readShortLittleEndian(zip4jRaf); fileHeader.setFileCommentLength(fileCommentLength); fileHeader.setDiskNumberStart(rawIO.readShortLittleEndian(zip4jRaf)); zip4jRaf.readFully(shortBuff); fileHeader.setInternalFileAttributes(shortBuff.clone()); zip4jRaf.readFully(intBuff); fileHeader.setExternalFileAttributes(intBuff.clone()); zip4jRaf.readFully(intBuff); fileHeader.setOffsetLocalHeader(rawIO.readLongLittleEndian(intBuff, 0)); if (fileNameLength > 0) { byte[] fileNameBuff = new byte[fileNameLength]; zip4jRaf.readFully(fileNameBuff); String fileName = decodeStringWithCharset(fileNameBuff, fileHeader.isFileNameUTF8Encoded(), charset); if (fileName.contains(":\\")) { fileName = fileName.substring(fileName.indexOf(":\\") + 2); } fileHeader.setFileName(fileName); fileHeader.setDirectory(fileName.endsWith("/") || fileName.endsWith("\\")); } else { fileHeader.setFileName(null); } readExtraDataRecords(zip4jRaf, fileHeader); readZip64ExtendedInfo(fileHeader, rawIO); readAesExtraDataRecord(fileHeader, rawIO); if (fileCommentLength > 0) { byte[] fileCommentBuff = new byte[fileCommentLength]; zip4jRaf.readFully(fileCommentBuff); fileHeader.setFileComment(decodeStringWithCharset(fileCommentBuff, fileHeader.isFileNameUTF8Encoded(), charset)); } if (fileHeader.isEncrypted()) { if (fileHeader.getAesExtraDataRecord() != null) { fileHeader.setEncryptionMethod(EncryptionMethod.AES); } else { fileHeader.setEncryptionMethod(EncryptionMethod.ZIP_STANDARD); } } fileHeaders.add(fileHeader); } centralDirectory.setFileHeaders(fileHeaders); DigitalSignature digitalSignature = new DigitalSignature(); if (rawIO.readIntLittleEndian(zip4jRaf) == HeaderSignature.DIGITAL_SIGNATURE.getValue()) { digitalSignature.setSignature(HeaderSignature.DIGITAL_SIGNATURE); digitalSignature.setSizeOfData(rawIO.readShortLittleEndian(zip4jRaf)); if (digitalSignature.getSizeOfData() > 0) { byte[] signatureDataBuff = new byte[digitalSignature.getSizeOfData()]; zip4jRaf.readFully(signatureDataBuff); digitalSignature.setSignatureData(new String(signatureDataBuff)); } } return centralDirectory; } private void readExtraDataRecords(RandomAccessFile zip4jRaf, FileHeader fileHeader) throws IOException { int extraFieldLength = fileHeader.getExtraFieldLength(); if (extraFieldLength <= 0) { return; } fileHeader.setExtraDataRecords(readExtraDataRecords(zip4jRaf, extraFieldLength)); } private void readExtraDataRecords(InputStream inputStream, LocalFileHeader localFileHeader) throws IOException { int extraFieldLength = localFileHeader.getExtraFieldLength(); if (extraFieldLength <= 0) { return; } localFileHeader.setExtraDataRecords(readExtraDataRecords(inputStream, extraFieldLength)); } private List readExtraDataRecords(RandomAccessFile zip4jRaf, int extraFieldLength) throws IOException { if (extraFieldLength < 4) { if (extraFieldLength > 0) { zip4jRaf.skipBytes(extraFieldLength); } return null; } byte[] extraFieldBuf = new byte[extraFieldLength]; zip4jRaf.read(extraFieldBuf); try { return parseExtraDataRecords(extraFieldBuf, extraFieldLength); } catch (Exception e) { // Ignore any errors when parsing extra data records return Collections.emptyList(); } } private List readExtraDataRecords(InputStream inputStream, int extraFieldLength) throws IOException { if (extraFieldLength < 4) { if (extraFieldLength > 0) { inputStream.skip(extraFieldLength); } return null; } byte[] extraFieldBuf = new byte[extraFieldLength]; readFully(inputStream, extraFieldBuf); try { return parseExtraDataRecords(extraFieldBuf, extraFieldLength); } catch (Exception e) { // Ignore any errors when parsing extra data records return Collections.emptyList(); } } private List parseExtraDataRecords(byte[] extraFieldBuf, int extraFieldLength) { int counter = 0; List extraDataRecords = new ArrayList<>(); while (counter < extraFieldLength) { ExtraDataRecord extraDataRecord = new ExtraDataRecord(); int header = rawIO.readShortLittleEndian(extraFieldBuf, counter); extraDataRecord.setHeader(header); counter += 2; int sizeOfRec = rawIO.readShortLittleEndian(extraFieldBuf, counter); extraDataRecord.setSizeOfData(sizeOfRec); counter += 2; if (sizeOfRec > 0) { byte[] data = new byte[sizeOfRec]; System.arraycopy(extraFieldBuf, counter, data, 0, sizeOfRec); extraDataRecord.setData(data); } counter += sizeOfRec; extraDataRecords.add(extraDataRecord); } return extraDataRecords.size() > 0 ? extraDataRecords : null; } private Zip64EndOfCentralDirectoryLocator readZip64EndOfCentralDirectoryLocator(RandomAccessFile zip4jRaf, RawIO rawIO) throws IOException { Zip64EndOfCentralDirectoryLocator zip64EndOfCentralDirectoryLocator = new Zip64EndOfCentralDirectoryLocator(); setFilePointerToReadZip64EndCentralDirLoc(zip4jRaf, rawIO); int signature = rawIO.readIntLittleEndian(zip4jRaf); if (signature == HeaderSignature.ZIP64_END_CENTRAL_DIRECTORY_LOCATOR.getValue()) { zipModel.setZip64Format(true); zip64EndOfCentralDirectoryLocator.setSignature(HeaderSignature.ZIP64_END_CENTRAL_DIRECTORY_LOCATOR); } else { zipModel.setZip64Format(false); return null; } zip64EndOfCentralDirectoryLocator.setNumberOfDiskStartOfZip64EndOfCentralDirectoryRecord( rawIO.readIntLittleEndian(zip4jRaf)); zip64EndOfCentralDirectoryLocator.setOffsetZip64EndOfCentralDirectoryRecord( rawIO.readLongLittleEndian(zip4jRaf)); zip64EndOfCentralDirectoryLocator.setTotalNumberOfDiscs(rawIO.readIntLittleEndian(zip4jRaf)); return zip64EndOfCentralDirectoryLocator; } private Zip64EndOfCentralDirectoryRecord readZip64EndCentralDirRec(RandomAccessFile zip4jRaf, RawIO rawIO) throws IOException { if (zipModel.getZip64EndOfCentralDirectoryLocator() == null) { throw new ZipException("invalid zip64 end of central directory locator"); } long offSetStartOfZip64CentralDir = zipModel.getZip64EndOfCentralDirectoryLocator() .getOffsetZip64EndOfCentralDirectoryRecord(); if (offSetStartOfZip64CentralDir < 0) { throw new ZipException("invalid offset for start of end of central directory record"); } zip4jRaf.seek(offSetStartOfZip64CentralDir); Zip64EndOfCentralDirectoryRecord zip64EndOfCentralDirectoryRecord = new Zip64EndOfCentralDirectoryRecord(); int signature = rawIO.readIntLittleEndian(zip4jRaf); if (signature != HeaderSignature.ZIP64_END_CENTRAL_DIRECTORY_RECORD.getValue()) { throw new ZipException("invalid signature for zip64 end of central directory record"); } zip64EndOfCentralDirectoryRecord.setSignature(HeaderSignature.ZIP64_END_CENTRAL_DIRECTORY_RECORD); zip64EndOfCentralDirectoryRecord.setSizeOfZip64EndCentralDirectoryRecord(rawIO.readLongLittleEndian(zip4jRaf)); zip64EndOfCentralDirectoryRecord.setVersionMadeBy(rawIO.readShortLittleEndian(zip4jRaf)); zip64EndOfCentralDirectoryRecord.setVersionNeededToExtract(rawIO.readShortLittleEndian(zip4jRaf)); zip64EndOfCentralDirectoryRecord.setNumberOfThisDisk(rawIO.readIntLittleEndian(zip4jRaf)); zip64EndOfCentralDirectoryRecord.setNumberOfThisDiskStartOfCentralDirectory(rawIO.readIntLittleEndian(zip4jRaf)); zip64EndOfCentralDirectoryRecord.setTotalNumberOfEntriesInCentralDirectoryOnThisDisk( rawIO.readLongLittleEndian(zip4jRaf)); zip64EndOfCentralDirectoryRecord.setTotalNumberOfEntriesInCentralDirectory(rawIO.readLongLittleEndian(zip4jRaf)); zip64EndOfCentralDirectoryRecord.setSizeOfCentralDirectory(rawIO.readLongLittleEndian(zip4jRaf)); zip64EndOfCentralDirectoryRecord.setOffsetStartCentralDirectoryWRTStartDiskNumber( rawIO.readLongLittleEndian(zip4jRaf)); //zip64 extensible data sector //44 is the size of fixed variables in this record long extDataSecSize = zip64EndOfCentralDirectoryRecord.getSizeOfZip64EndCentralDirectoryRecord() - 44; if (extDataSecSize > 0) { byte[] extDataSecRecBuf = new byte[(int) extDataSecSize]; zip4jRaf.readFully(extDataSecRecBuf); zip64EndOfCentralDirectoryRecord.setExtensibleDataSector(extDataSecRecBuf); } return zip64EndOfCentralDirectoryRecord; } private void readZip64ExtendedInfo(FileHeader fileHeader, RawIO rawIO) throws ZipException { if (fileHeader.getExtraDataRecords() == null || fileHeader.getExtraDataRecords().size() <= 0) { return; } Zip64ExtendedInfo zip64ExtendedInfo = readZip64ExtendedInfo(fileHeader.getExtraDataRecords(), rawIO, fileHeader.getUncompressedSize(), fileHeader.getCompressedSize(), fileHeader.getOffsetLocalHeader()); if (zip64ExtendedInfo == null) { return; } fileHeader.setZip64ExtendedInfo(zip64ExtendedInfo); if (zip64ExtendedInfo.getUncompressedSize() != -1) { fileHeader.setUncompressedSize(zip64ExtendedInfo.getUncompressedSize()); } if (zip64ExtendedInfo.getCompressedSize() != -1) { fileHeader.setCompressedSize(zip64ExtendedInfo.getCompressedSize()); } if (zip64ExtendedInfo.getOffsetLocalHeader() != -1) { fileHeader.setOffsetLocalHeader(zip64ExtendedInfo.getOffsetLocalHeader()); } if (zip64ExtendedInfo.getDiskNumberStart() != -1) { fileHeader.setDiskNumberStart(zip64ExtendedInfo.getDiskNumberStart()); } } private void readZip64ExtendedInfo(LocalFileHeader localFileHeader, RawIO rawIO) throws ZipException { if (localFileHeader == null) { throw new ZipException("file header is null in reading Zip64 Extended Info"); } if (localFileHeader.getExtraDataRecords() == null || localFileHeader.getExtraDataRecords().size() <= 0) { return; } Zip64ExtendedInfo zip64ExtendedInfo = readZip64ExtendedInfo(localFileHeader.getExtraDataRecords(), rawIO, localFileHeader.getUncompressedSize(), localFileHeader.getCompressedSize(), 0); if (zip64ExtendedInfo == null) { return; } localFileHeader.setZip64ExtendedInfo(zip64ExtendedInfo); if (zip64ExtendedInfo.getUncompressedSize() != -1) { localFileHeader.setUncompressedSize(zip64ExtendedInfo.getUncompressedSize()); } if (zip64ExtendedInfo.getCompressedSize() != -1) { localFileHeader.setCompressedSize(zip64ExtendedInfo.getCompressedSize()); } } private Zip64ExtendedInfo readZip64ExtendedInfo(List extraDataRecords, RawIO rawIO, long uncompressedSize, long compressedSize, long offsetLocalHeader) throws ZipException { for (ExtraDataRecord extraDataRecord : extraDataRecords) { if (extraDataRecord == null) { continue; } if (HeaderSignature.ZIP64_EXTRA_FIELD_SIGNATURE.getValue() == extraDataRecord.getHeader()) { Zip64ExtendedInfo zip64ExtendedInfo = new Zip64ExtendedInfo(); byte[] extraData = extraDataRecord.getData(); if (extraDataRecord.getSizeOfData() <= 0) { throw new ZipException("No data present for Zip64Extended info"); } int counter = 0; if (counter < extraDataRecord.getSizeOfData() && uncompressedSize == ZIP_64_SIZE_LIMIT) { zip64ExtendedInfo.setUncompressedSize(rawIO.readLongLittleEndian(extraData, counter)); counter += 8; } if ( counter < extraDataRecord.getSizeOfData() && compressedSize == ZIP_64_SIZE_LIMIT) { zip64ExtendedInfo.setCompressedSize(rawIO.readLongLittleEndian(extraData, counter)); counter += 8; } if (counter < extraDataRecord.getSizeOfData() && offsetLocalHeader == ZIP_64_SIZE_LIMIT) { zip64ExtendedInfo.setOffsetLocalHeader(rawIO.readLongLittleEndian(extraData, counter)); counter += 8; } if (counter < extraDataRecord.getSizeOfData()) { zip64ExtendedInfo.setDiskNumberStart(rawIO.readIntLittleEndian(extraData, counter)); } return zip64ExtendedInfo; } } return null; } private void setFilePointerToReadZip64EndCentralDirLoc(RandomAccessFile zip4jRaf, RawIO rawIO) throws IOException { long pos = zip4jRaf.length() - ENDHDR; do { zip4jRaf.seek(pos--); } while (rawIO.readIntLittleEndian(zip4jRaf) != HeaderSignature.END_OF_CENTRAL_DIRECTORY.getValue()); // Now the file pointer is at the end of signature of Central Dir Rec // Seek back with the following values // 4 -> end of central dir signature // 4 -> total number of disks // 8 -> relative offset of the zip64 end of central directory record // 4 -> number of the disk with the start of the zip64 end of central directory // 4 -> zip64 end of central dir locator signature // Refer to Appnote for more information zip4jRaf.seek(zip4jRaf.getFilePointer() - 4 - 4 - 8 - 4 - 4); } public LocalFileHeader readLocalFileHeader(InputStream inputStream, Charset charset) throws IOException { LocalFileHeader localFileHeader = new LocalFileHeader(); byte[] intBuff = new byte[4]; //signature int sig = rawIO.readIntLittleEndian(inputStream); if (sig != HeaderSignature.LOCAL_FILE_HEADER.getValue()) { return null; } localFileHeader.setSignature(HeaderSignature.LOCAL_FILE_HEADER); localFileHeader.setVersionNeededToExtract(rawIO.readShortLittleEndian(inputStream)); byte[] generalPurposeFlags = new byte[2]; if (readFully(inputStream, generalPurposeFlags) != 2) { throw new ZipException("Could not read enough bytes for generalPurposeFlags"); } localFileHeader.setEncrypted(isBitSet(generalPurposeFlags[0], 0)); localFileHeader.setDataDescriptorExists(isBitSet(generalPurposeFlags[0], 3)); localFileHeader.setFileNameUTF8Encoded(isBitSet(generalPurposeFlags[1], 3)); localFileHeader.setGeneralPurposeFlag(generalPurposeFlags.clone()); localFileHeader.setCompressionMethod(CompressionMethod.getCompressionMethodFromCode( rawIO.readShortLittleEndian(inputStream))); localFileHeader.setLastModifiedTime(rawIO.readIntLittleEndian(inputStream)); readFully(inputStream, intBuff); localFileHeader.setCrc(rawIO.readLongLittleEndian(intBuff, 0)); localFileHeader.setCrcRawData(intBuff.clone()); localFileHeader.setCompressedSize(rawIO.readLongLittleEndian(inputStream, 4)); localFileHeader.setUncompressedSize(rawIO.readLongLittleEndian(inputStream, 4)); int fileNameLength = rawIO.readShortLittleEndian(inputStream); localFileHeader.setFileNameLength(fileNameLength); localFileHeader.setExtraFieldLength(rawIO.readShortLittleEndian(inputStream)); if (fileNameLength > 0) { byte[] fileNameBuf = new byte[fileNameLength]; readFully(inputStream, fileNameBuf); // Modified after user reported an issue http://www.lingala.net/zip4j/forum/index.php?topic=2.0 // String fileName = new String(fileNameBuf, "Cp850"); // String fileName = Zip4jUtil.getCp850EncodedString(fileNameBuf); String fileName = decodeStringWithCharset(fileNameBuf, localFileHeader.isFileNameUTF8Encoded(), charset); if (fileName == null) { throw new ZipException("file name is null, cannot assign file name to local file header"); } if (fileName.contains(":" + System.getProperty("file.separator"))) { fileName = fileName.substring(fileName.indexOf(":" + System.getProperty("file.separator")) + 2); } localFileHeader.setFileName(fileName); localFileHeader.setDirectory(fileName.endsWith("/") || fileName.endsWith("\\")); } else { localFileHeader.setFileName(null); } readExtraDataRecords(inputStream, localFileHeader); readZip64ExtendedInfo(localFileHeader, rawIO); readAesExtraDataRecord(localFileHeader, rawIO); if (localFileHeader.isEncrypted()) { if (localFileHeader.getEncryptionMethod() == EncryptionMethod.AES) { //Do nothing } else { if (BigInteger.valueOf(localFileHeader.getGeneralPurposeFlag()[0]).testBit(6)) { localFileHeader.setEncryptionMethod(EncryptionMethod.ZIP_STANDARD_VARIANT_STRONG); } else { localFileHeader.setEncryptionMethod(EncryptionMethod.ZIP_STANDARD); } } } return localFileHeader; } public DataDescriptor readDataDescriptor(InputStream inputStream, boolean isZip64Format) throws IOException { DataDescriptor dataDescriptor = new DataDescriptor(); byte[] intBuff = new byte[4]; readFully(inputStream, intBuff); long sigOrCrc = rawIO.readLongLittleEndian(intBuff, 0); //According to zip specification, presence of extra data record header signature is optional. //If this signature is present, read it and read the next 4 bytes for crc //If signature not present, assign the read 4 bytes for crc if (sigOrCrc == HeaderSignature.EXTRA_DATA_RECORD.getValue()) { dataDescriptor.setSignature(HeaderSignature.EXTRA_DATA_RECORD); readFully(inputStream, intBuff); dataDescriptor.setCrc(rawIO.readLongLittleEndian(intBuff, 0)); } else { dataDescriptor.setCrc(sigOrCrc); } if (isZip64Format) { dataDescriptor.setCompressedSize(rawIO.readLongLittleEndian(inputStream)); dataDescriptor.setUncompressedSize(rawIO.readLongLittleEndian(inputStream)); } else { dataDescriptor.setCompressedSize(rawIO.readIntLittleEndian(inputStream)); dataDescriptor.setUncompressedSize(rawIO.readIntLittleEndian(inputStream)); } return dataDescriptor; } private void readAesExtraDataRecord(FileHeader fileHeader, RawIO rawIO) throws ZipException { if (fileHeader.getExtraDataRecords() == null || fileHeader.getExtraDataRecords().size() <= 0) { return; } AESExtraDataRecord aesExtraDataRecord = readAesExtraDataRecord(fileHeader.getExtraDataRecords(), rawIO); if (aesExtraDataRecord != null) { fileHeader.setAesExtraDataRecord(aesExtraDataRecord); fileHeader.setEncryptionMethod(EncryptionMethod.AES); } } private void readAesExtraDataRecord(LocalFileHeader localFileHeader, RawIO rawIO) throws ZipException { if (localFileHeader.getExtraDataRecords() == null || localFileHeader.getExtraDataRecords().size() <= 0) { return; } AESExtraDataRecord aesExtraDataRecord = readAesExtraDataRecord(localFileHeader.getExtraDataRecords(), rawIO); if (aesExtraDataRecord != null) { localFileHeader.setAesExtraDataRecord(aesExtraDataRecord); localFileHeader.setEncryptionMethod(EncryptionMethod.AES); } } private AESExtraDataRecord readAesExtraDataRecord(List extraDataRecords, RawIO rawIO) throws ZipException { if (extraDataRecords == null) { return null; } for (ExtraDataRecord extraDataRecord : extraDataRecords) { if (extraDataRecord == null) { continue; } if (extraDataRecord.getHeader() == HeaderSignature.AES_EXTRA_DATA_RECORD.getValue()) { if (extraDataRecord.getData() == null) { throw new ZipException("corrupt AES extra data records"); } AESExtraDataRecord aesExtraDataRecord = new AESExtraDataRecord(); aesExtraDataRecord.setSignature(HeaderSignature.AES_EXTRA_DATA_RECORD); aesExtraDataRecord.setDataSize(extraDataRecord.getSizeOfData()); byte[] aesData = extraDataRecord.getData(); aesExtraDataRecord.setAesVersion(AesVersion.getFromVersionNumber(rawIO.readShortLittleEndian(aesData, 0))); byte[] vendorIDBytes = new byte[2]; System.arraycopy(aesData, 2, vendorIDBytes, 0, 2); aesExtraDataRecord.setVendorID(new String(vendorIDBytes)); aesExtraDataRecord.setAesKeyStrength(AesKeyStrength.getAesKeyStrengthFromRawCode(aesData[4] & 0xFF)); aesExtraDataRecord.setCompressionMethod( CompressionMethod.getCompressionMethodFromCode(rawIO.readShortLittleEndian(aesData, 5))); return aesExtraDataRecord; } } return null; } private long getOffsetCentralDirectory(ZipModel zipModel) { if (zipModel.isZip64Format()) { return zipModel.getZip64EndOfCentralDirectoryRecord().getOffsetStartCentralDirectoryWRTStartDiskNumber(); } return zipModel.getEndOfCentralDirectoryRecord().getOffsetOfStartOfCentralDirectory(); } private long getNumberOfEntriesInCentralDirectory(ZipModel zipModel) { if (zipModel.isZip64Format()) { return zipModel.getZip64EndOfCentralDirectoryRecord().getTotalNumberOfEntriesInCentralDirectory(); } return zipModel.getEndOfCentralDirectoryRecord().getTotalNumberOfEntriesInCentralDirectory(); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/headers/HeaderSignature.java000066400000000000000000000012451357650161600260200ustar00rootroot00000000000000package net.lingala.zip4j.headers; public enum HeaderSignature { LOCAL_FILE_HEADER(0x04034b50L), // "PK\003\004" EXTRA_DATA_RECORD(0x08074b50L), // "PK\007\008" CENTRAL_DIRECTORY(0x02014b50L), // "PK\001\002" END_OF_CENTRAL_DIRECTORY(0x06054b50L), // "PK\005\006" DIGITAL_SIGNATURE(0x05054b50L), ARCEXTDATREC(0x08064b50L), SPLIT_ZIP(0x08074b50L), ZIP64_END_CENTRAL_DIRECTORY_LOCATOR(0x07064b50L), ZIP64_END_CENTRAL_DIRECTORY_RECORD(0x06064b50), ZIP64_EXTRA_FIELD_SIGNATURE(0x0001), AES_EXTRA_DATA_RECORD(0x9901); private long value; HeaderSignature(long value) { this.value = value; } public long getValue() { return value; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/headers/HeaderUtil.java000066400000000000000000000076101357650161600247760ustar00rootroot00000000000000package net.lingala.zip4j.headers; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.util.InternalZipConstants; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.List; import static net.lingala.zip4j.util.InternalZipConstants.ZIP_STANDARD_CHARSET; import static net.lingala.zip4j.util.Zip4jUtil.isStringNotNullAndNotEmpty; public class HeaderUtil { public static FileHeader getFileHeader(ZipModel zipModel, String fileName) throws ZipException { FileHeader fileHeader = getFileHeaderWithExactMatch(zipModel, fileName); if (fileHeader == null) { fileName = fileName.replaceAll("\\\\", "/"); fileHeader = getFileHeaderWithExactMatch(zipModel, fileName); if (fileHeader == null) { fileName = fileName.replaceAll("/", "\\\\"); fileHeader = getFileHeaderWithExactMatch(zipModel, fileName); } } return fileHeader; } public static int getIndexOfFileHeader(ZipModel zipModel, FileHeader fileHeader) throws ZipException { if (zipModel == null || fileHeader == null) { throw new ZipException("input parameters is null, cannot determine index of file header"); } if (zipModel.getCentralDirectory() == null || zipModel.getCentralDirectory().getFileHeaders() == null || zipModel.getCentralDirectory().getFileHeaders().size() <= 0) { return -1; } String fileName = fileHeader.getFileName(); if (!isStringNotNullAndNotEmpty(fileName)) { throw new ZipException("file name in file header is empty or null, cannot determine index of file header"); } List fileHeadersFromCentralDir = zipModel.getCentralDirectory().getFileHeaders(); for (int i = 0; i < fileHeadersFromCentralDir.size(); i++) { FileHeader fileHeaderFromCentralDir = fileHeadersFromCentralDir.get(i); String fileNameForHdr = fileHeaderFromCentralDir.getFileName(); if (!isStringNotNullAndNotEmpty(fileNameForHdr)) { continue; } if (fileName.equalsIgnoreCase(fileNameForHdr)) { return i; } } return -1; } public static String decodeStringWithCharset(byte[] data, boolean isUtf8Encoded, Charset charset) { if(charset != null) { return new String(data, charset); } if (isUtf8Encoded) { return new String(data, InternalZipConstants.CHARSET_UTF_8); } try { return new String(data, ZIP_STANDARD_CHARSET); } catch (UnsupportedEncodingException e) { return new String(data); } } private static FileHeader getFileHeaderWithExactMatch(ZipModel zipModel, String fileName) throws ZipException { if (zipModel == null) { throw new ZipException("zip model is null, cannot determine file header with exact match for fileName: " + fileName); } if (!isStringNotNullAndNotEmpty(fileName)) { throw new ZipException("file name is null, cannot determine file header with exact match for fileName: " + fileName); } if (zipModel.getCentralDirectory() == null) { throw new ZipException("central directory is null, cannot determine file header with exact match for fileName: " + fileName); } if (zipModel.getCentralDirectory().getFileHeaders() == null) { throw new ZipException("file Headers are null, cannot determine file header with exact match for fileName: " + fileName); } if (zipModel.getCentralDirectory().getFileHeaders().size() == 0) { return null; } for (FileHeader fileHeader : zipModel.getCentralDirectory().getFileHeaders()) { String fileNameForHdr = fileHeader.getFileName(); if (!isStringNotNullAndNotEmpty(fileNameForHdr)) { continue; } if (fileName.equalsIgnoreCase(fileNameForHdr)) { return fileHeader; } } return null; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/headers/HeaderWriter.java000077500000000000000000000740141357650161600253420ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.headers; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.io.outputstream.CountingOutputStream; import net.lingala.zip4j.io.outputstream.SplitOutputStream; import net.lingala.zip4j.model.AESExtraDataRecord; import net.lingala.zip4j.model.ExtraDataRecord; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.LocalFileHeader; import net.lingala.zip4j.model.Zip64EndOfCentralDirectoryLocator; import net.lingala.zip4j.model.Zip64EndOfCentralDirectoryRecord; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.util.InternalZipConstants; import net.lingala.zip4j.util.RawIO; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; import java.util.List; import static net.lingala.zip4j.util.FileUtils.getZipFileNameWithoutExtension; import static net.lingala.zip4j.util.InternalZipConstants.ZIP_64_SIZE_LIMIT; import static net.lingala.zip4j.util.Zip4jUtil.isStringNotNullAndNotEmpty; public class HeaderWriter { private static final short ZIP64_EXTRA_DATA_RECORD_SIZE_LFH = 16; private static final short ZIP64_EXTRA_DATA_RECORD_SIZE_FH = 28; private static final short AES_EXTRA_DATA_RECORD_SIZE = 11; private RawIO rawIO = new RawIO(); private byte[] longBuff = new byte[8]; public void writeLocalFileHeader(ZipModel zipModel, LocalFileHeader localFileHeader, OutputStream outputStream, Charset charset) throws IOException { try(ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { rawIO.writeIntLittleEndian(byteArrayOutputStream, (int) localFileHeader.getSignature().getValue()); rawIO.writeShortLittleEndian(byteArrayOutputStream, localFileHeader.getVersionNeededToExtract()); byteArrayOutputStream.write(localFileHeader.getGeneralPurposeFlag()); rawIO.writeShortLittleEndian(byteArrayOutputStream, localFileHeader.getCompressionMethod().getCode()); rawIO.writeLongLittleEndian(longBuff, 0, localFileHeader.getLastModifiedTime()); byteArrayOutputStream.write(longBuff, 0, 4); rawIO.writeLongLittleEndian(longBuff, 0, localFileHeader.getCrc()); byteArrayOutputStream.write(longBuff, 0, 4); boolean writeZip64Header = localFileHeader.getCompressedSize() >= ZIP_64_SIZE_LIMIT || localFileHeader.getUncompressedSize() >= ZIP_64_SIZE_LIMIT; if (writeZip64Header) { rawIO.writeLongLittleEndian(longBuff, 0, ZIP_64_SIZE_LIMIT); //Set the uncompressed size to ZipConstants.ZIP_64_SIZE_LIMIT as //these values will be stored in Zip64 extra record byteArrayOutputStream.write(longBuff, 0, 4); byteArrayOutputStream.write(longBuff, 0, 4); zipModel.setZip64Format(true); localFileHeader.setWriteCompressedSizeInZip64ExtraRecord(true); } else { rawIO.writeLongLittleEndian(longBuff, 0, localFileHeader.getCompressedSize()); byteArrayOutputStream.write(longBuff, 0, 4); rawIO.writeLongLittleEndian(longBuff, 0, localFileHeader.getUncompressedSize()); byteArrayOutputStream.write(longBuff, 0, 4); localFileHeader.setWriteCompressedSizeInZip64ExtraRecord(false); } byte[] fileNameBytes = new byte[0]; if (isStringNotNullAndNotEmpty(localFileHeader.getFileName())) { fileNameBytes = localFileHeader.getFileName().getBytes(charset); } rawIO.writeShortLittleEndian(byteArrayOutputStream, fileNameBytes.length); int extraFieldLength = 0; if (writeZip64Header) { extraFieldLength += ZIP64_EXTRA_DATA_RECORD_SIZE_LFH + 4; // 4 for signature + size of record } if (localFileHeader.getAesExtraDataRecord() != null) { extraFieldLength += AES_EXTRA_DATA_RECORD_SIZE; } rawIO.writeShortLittleEndian(byteArrayOutputStream, extraFieldLength); if (fileNameBytes.length > 0) { byteArrayOutputStream.write(fileNameBytes); } //Zip64 should be the first extra data record that should be written //This is NOT according to any specification but if this is changed //corresponding logic for updateLocalFileHeader for compressed size //has to be modified as well if (writeZip64Header) { rawIO.writeShortLittleEndian(byteArrayOutputStream, (int) HeaderSignature.ZIP64_EXTRA_FIELD_SIGNATURE.getValue()); rawIO.writeShortLittleEndian(byteArrayOutputStream, ZIP64_EXTRA_DATA_RECORD_SIZE_LFH); rawIO.writeLongLittleEndian(byteArrayOutputStream, localFileHeader.getUncompressedSize()); rawIO.writeLongLittleEndian(byteArrayOutputStream, localFileHeader.getCompressedSize()); } if (localFileHeader.getAesExtraDataRecord() != null) { AESExtraDataRecord aesExtraDataRecord = localFileHeader.getAesExtraDataRecord(); rawIO.writeShortLittleEndian(byteArrayOutputStream, (int) aesExtraDataRecord.getSignature().getValue()); rawIO.writeShortLittleEndian(byteArrayOutputStream, aesExtraDataRecord.getDataSize()); rawIO.writeShortLittleEndian(byteArrayOutputStream, aesExtraDataRecord.getAesVersion().getVersionNumber()); byteArrayOutputStream.write(aesExtraDataRecord.getVendorID().getBytes()); byte[] aesStrengthBytes = new byte[1]; aesStrengthBytes[0] = (byte) aesExtraDataRecord.getAesKeyStrength().getRawCode(); byteArrayOutputStream.write(aesStrengthBytes); rawIO.writeShortLittleEndian(byteArrayOutputStream, aesExtraDataRecord.getCompressionMethod().getCode()); } outputStream.write(byteArrayOutputStream.toByteArray()); } } public void writeExtendedLocalHeader(LocalFileHeader localFileHeader, OutputStream outputStream) throws IOException { if (localFileHeader == null || outputStream == null) { throw new ZipException("input parameters is null, cannot write extended local header"); } try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { rawIO.writeIntLittleEndian(byteArrayOutputStream, (int) HeaderSignature.EXTRA_DATA_RECORD.getValue()); rawIO.writeLongLittleEndian(longBuff, 0, localFileHeader.getCrc()); byteArrayOutputStream.write(longBuff, 0, 4); if (localFileHeader.isWriteCompressedSizeInZip64ExtraRecord()) { rawIO.writeLongLittleEndian(byteArrayOutputStream, localFileHeader.getCompressedSize()); rawIO.writeLongLittleEndian(byteArrayOutputStream, localFileHeader.getUncompressedSize()); } else { rawIO.writeLongLittleEndian(longBuff, 0, localFileHeader.getCompressedSize()); byteArrayOutputStream.write(longBuff, 0, 4); rawIO.writeLongLittleEndian(longBuff, 0, localFileHeader.getUncompressedSize()); byteArrayOutputStream.write(longBuff, 0, 4); } outputStream.write(byteArrayOutputStream.toByteArray()); } } public void finalizeZipFile(ZipModel zipModel, OutputStream outputStream, Charset charset) throws IOException { if (zipModel == null || outputStream == null) { throw new ZipException("input parameters is null, cannot finalize zip file"); } try(ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { processHeaderData(zipModel, outputStream); long offsetCentralDir = zipModel.getEndOfCentralDirectoryRecord().getOffsetOfStartOfCentralDirectory(); writeCentralDirectory(zipModel, byteArrayOutputStream, rawIO, charset); int sizeOfCentralDir = byteArrayOutputStream.size(); if (zipModel.isZip64Format() || offsetCentralDir >= InternalZipConstants.ZIP_64_SIZE_LIMIT || zipModel.getCentralDirectory().getFileHeaders().size() >= InternalZipConstants.ZIP_64_NUMBER_OF_ENTRIES_LIMIT) { if (zipModel.getZip64EndOfCentralDirectoryRecord() == null) { zipModel.setZip64EndOfCentralDirectoryRecord(new Zip64EndOfCentralDirectoryRecord()); } if (zipModel.getZip64EndOfCentralDirectoryLocator() == null) { zipModel.setZip64EndOfCentralDirectoryLocator(new Zip64EndOfCentralDirectoryLocator()); } zipModel.getZip64EndOfCentralDirectoryLocator().setOffsetZip64EndOfCentralDirectoryRecord(offsetCentralDir + sizeOfCentralDir); if (isSplitZipFile(outputStream)) { int currentSplitFileCounter = getCurrentSplitFileCounter(outputStream); zipModel.getZip64EndOfCentralDirectoryLocator().setNumberOfDiskStartOfZip64EndOfCentralDirectoryRecord( currentSplitFileCounter); zipModel.getZip64EndOfCentralDirectoryLocator().setTotalNumberOfDiscs(currentSplitFileCounter + 1); } else { zipModel.getZip64EndOfCentralDirectoryLocator().setNumberOfDiskStartOfZip64EndOfCentralDirectoryRecord(0); zipModel.getZip64EndOfCentralDirectoryLocator().setTotalNumberOfDiscs(1); } writeZip64EndOfCentralDirectoryRecord(zipModel, sizeOfCentralDir, offsetCentralDir, byteArrayOutputStream, rawIO); writeZip64EndOfCentralDirectoryLocator(zipModel, byteArrayOutputStream, rawIO); } writeEndOfCentralDirectoryRecord(zipModel, sizeOfCentralDir, offsetCentralDir, byteArrayOutputStream, rawIO, charset); writeZipHeaderBytes(zipModel, outputStream, byteArrayOutputStream.toByteArray(), charset); } } public void finalizeZipFileWithoutValidations(ZipModel zipModel, OutputStream outputStream, Charset charset) throws IOException { if (zipModel == null || outputStream == null) { throw new ZipException("input parameters is null, cannot finalize zip file without validations"); } try(ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { long offsetCentralDir = zipModel.getEndOfCentralDirectoryRecord().getOffsetOfStartOfCentralDirectory(); writeCentralDirectory(zipModel, byteArrayOutputStream, rawIO, charset); int sizeOfCentralDir = byteArrayOutputStream.size(); if (zipModel.isZip64Format() || offsetCentralDir >= InternalZipConstants.ZIP_64_SIZE_LIMIT || zipModel.getCentralDirectory().getFileHeaders().size() >= InternalZipConstants.ZIP_64_NUMBER_OF_ENTRIES_LIMIT) { if (zipModel.getZip64EndOfCentralDirectoryRecord() == null) { zipModel.setZip64EndOfCentralDirectoryRecord(new Zip64EndOfCentralDirectoryRecord()); } if (zipModel.getZip64EndOfCentralDirectoryLocator() == null) { zipModel.setZip64EndOfCentralDirectoryLocator(new Zip64EndOfCentralDirectoryLocator()); } zipModel.getZip64EndOfCentralDirectoryLocator().setOffsetZip64EndOfCentralDirectoryRecord(offsetCentralDir + sizeOfCentralDir); writeZip64EndOfCentralDirectoryRecord(zipModel, sizeOfCentralDir, offsetCentralDir, byteArrayOutputStream, rawIO); writeZip64EndOfCentralDirectoryLocator(zipModel, byteArrayOutputStream, rawIO); } writeEndOfCentralDirectoryRecord(zipModel, sizeOfCentralDir, offsetCentralDir, byteArrayOutputStream, rawIO, charset); writeZipHeaderBytes(zipModel, outputStream, byteArrayOutputStream.toByteArray(), charset); } } public void updateLocalFileHeader(FileHeader fileHeader, ZipModel zipModel, SplitOutputStream outputStream) throws IOException { if (fileHeader == null || zipModel == null) { throw new ZipException("invalid input parameters, cannot update local file header"); } boolean closeFlag = false; SplitOutputStream currOutputStream; if (fileHeader.getDiskNumberStart() != outputStream.getCurrentSplitFileCounter()) { String parentFile = zipModel.getZipFile().getParent(); String fileNameWithoutExt = getZipFileNameWithoutExtension(zipModel.getZipFile().getName()); String fileName = parentFile + System.getProperty("file.separator"); if (fileHeader.getDiskNumberStart() < 9) { fileName += fileNameWithoutExt + ".z0" + (fileHeader.getDiskNumberStart() + 1); } else { fileName += fileNameWithoutExt + ".z" + (fileHeader.getDiskNumberStart() + 1); } currOutputStream = new SplitOutputStream(new File(fileName)); closeFlag = true; } else { currOutputStream = outputStream; } long currOffset = currOutputStream.getFilePointer(); currOutputStream.seek(fileHeader.getOffsetLocalHeader() + InternalZipConstants.UPDATE_LFH_CRC); rawIO.writeLongLittleEndian(longBuff, 0, fileHeader.getCrc()); currOutputStream.write(longBuff, 0, 4); updateFileSizesInLocalFileHeader(currOutputStream, fileHeader); if (closeFlag) { currOutputStream.close(); } else { outputStream.seek(currOffset); } } private void updateFileSizesInLocalFileHeader(SplitOutputStream outputStream, FileHeader fileHeader) throws IOException { if (fileHeader.getUncompressedSize() >= ZIP_64_SIZE_LIMIT) { rawIO.writeLongLittleEndian(longBuff, 0, ZIP_64_SIZE_LIMIT); outputStream.write(longBuff, 0, 4); outputStream.write(longBuff, 0, 4); //2 - file name length //2 - extra field length //variable - file name which can be determined by fileNameLength //2 - Zip64 signature //2 - size of zip64 data //8 - uncompressed size //8 - compressed size int zip64CompressedSizeOffset = 2 + 2 + fileHeader.getFileNameLength() + 2 + 2; if (outputStream.skipBytes(zip64CompressedSizeOffset) != zip64CompressedSizeOffset) { throw new ZipException("Unable to skip " + zip64CompressedSizeOffset + " bytes to update LFH"); } rawIO.writeLongLittleEndian(outputStream, fileHeader.getUncompressedSize()); rawIO.writeLongLittleEndian(outputStream, fileHeader.getCompressedSize()); } else { rawIO.writeLongLittleEndian(longBuff, 0, fileHeader.getCompressedSize()); outputStream.write(longBuff, 0, 4); rawIO.writeLongLittleEndian(longBuff, 0, fileHeader.getUncompressedSize()); outputStream.write(longBuff, 0, 4); } } private boolean isSplitZipFile(OutputStream outputStream) { if (outputStream instanceof SplitOutputStream) { return ((SplitOutputStream) outputStream).isSplitZipFile(); } else if (outputStream instanceof CountingOutputStream) { return ((CountingOutputStream) outputStream).isSplitZipFile(); } return false; } private int getCurrentSplitFileCounter(OutputStream outputStream) { if (outputStream instanceof SplitOutputStream) { return ((SplitOutputStream) outputStream).getCurrentSplitFileCounter(); } return ((CountingOutputStream) outputStream).getCurrentSplitFileCounter(); } private void writeZipHeaderBytes(ZipModel zipModel, OutputStream outputStream, byte[] buff, Charset charset) throws IOException { if (buff == null) { throw new ZipException("invalid buff to write as zip headers"); } if (outputStream instanceof CountingOutputStream) { if (((CountingOutputStream) outputStream).checkBuffSizeAndStartNextSplitFile(buff.length)) { finalizeZipFile(zipModel, outputStream, charset); return; } } outputStream.write(buff); } private void processHeaderData(ZipModel zipModel, OutputStream outputStream) throws IOException { int currentSplitFileCounter = 0; if (outputStream instanceof CountingOutputStream) { zipModel.getEndOfCentralDirectoryRecord().setOffsetOfStartOfCentralDirectory( ((CountingOutputStream) outputStream).getFilePointer()); currentSplitFileCounter = ((CountingOutputStream) outputStream).getCurrentSplitFileCounter(); } if (zipModel.isZip64Format()) { if (zipModel.getZip64EndOfCentralDirectoryRecord() == null) { zipModel.setZip64EndOfCentralDirectoryRecord(new Zip64EndOfCentralDirectoryRecord()); } if (zipModel.getZip64EndOfCentralDirectoryLocator() == null) { zipModel.setZip64EndOfCentralDirectoryLocator(new Zip64EndOfCentralDirectoryLocator()); } zipModel.getZip64EndOfCentralDirectoryLocator().setNumberOfDiskStartOfZip64EndOfCentralDirectoryRecord( currentSplitFileCounter); zipModel.getZip64EndOfCentralDirectoryLocator().setTotalNumberOfDiscs(currentSplitFileCounter + 1); } zipModel.getEndOfCentralDirectoryRecord().setNumberOfThisDisk(currentSplitFileCounter); zipModel.getEndOfCentralDirectoryRecord().setNumberOfThisDiskStartOfCentralDir(currentSplitFileCounter); } private void writeCentralDirectory(ZipModel zipModel, ByteArrayOutputStream byteArrayOutputStream, RawIO rawIO, Charset charset) throws ZipException { if (zipModel.getCentralDirectory() == null || zipModel.getCentralDirectory().getFileHeaders() == null || zipModel.getCentralDirectory().getFileHeaders().size() <= 0) { return; } for (FileHeader fileHeader: zipModel.getCentralDirectory().getFileHeaders()) { writeFileHeader(zipModel, fileHeader, byteArrayOutputStream, rawIO, charset); } } private void writeFileHeader(ZipModel zipModel, FileHeader fileHeader, ByteArrayOutputStream byteArrayOutputStream, RawIO rawIO, Charset charset) throws ZipException { if (fileHeader == null) { throw new ZipException("input parameters is null, cannot write local file header"); } try { final byte[] emptyShortByte = {0, 0}; boolean writeZip64ExtendedInfo = false; if (isZip64Entry(fileHeader)) { writeZip64ExtendedInfo = true; } rawIO.writeIntLittleEndian(byteArrayOutputStream, (int) fileHeader.getSignature().getValue()); rawIO.writeShortLittleEndian(byteArrayOutputStream, fileHeader.getVersionMadeBy()); rawIO.writeShortLittleEndian(byteArrayOutputStream, fileHeader.getVersionNeededToExtract()); byteArrayOutputStream.write(fileHeader.getGeneralPurposeFlag()); rawIO.writeShortLittleEndian(byteArrayOutputStream, fileHeader.getCompressionMethod().getCode()); rawIO.writeLongLittleEndian(longBuff, 0, fileHeader.getLastModifiedTime()); byteArrayOutputStream.write(longBuff, 0, 4); rawIO.writeLongLittleEndian(longBuff, 0, fileHeader.getCrc()); byteArrayOutputStream.write(longBuff, 0, 4); if (writeZip64ExtendedInfo) { rawIO.writeLongLittleEndian(longBuff, 0, ZIP_64_SIZE_LIMIT); byteArrayOutputStream.write(longBuff, 0, 4); byteArrayOutputStream.write(longBuff, 0, 4); zipModel.setZip64Format(true); } else { rawIO.writeLongLittleEndian(longBuff, 0, fileHeader.getCompressedSize()); byteArrayOutputStream.write(longBuff, 0, 4); rawIO.writeLongLittleEndian(longBuff, 0, fileHeader.getUncompressedSize()); byteArrayOutputStream.write(longBuff, 0, 4); } byte[] fileNameBytes = new byte[0]; if (isStringNotNullAndNotEmpty(fileHeader.getFileName())) { fileNameBytes = fileHeader.getFileName().getBytes(charset); } rawIO.writeShortLittleEndian(byteArrayOutputStream, fileNameBytes.length); //Compute offset bytes before extra field is written for Zip64 compatibility //NOTE: this data is not written now, but written at a later point byte[] offsetLocalHeaderBytes = new byte[4]; if (writeZip64ExtendedInfo) { rawIO.writeLongLittleEndian(longBuff, 0, ZIP_64_SIZE_LIMIT); System.arraycopy(longBuff, 0, offsetLocalHeaderBytes, 0, 4); } else { rawIO.writeLongLittleEndian(longBuff, 0, fileHeader.getOffsetLocalHeader()); System.arraycopy(longBuff, 0, offsetLocalHeaderBytes, 0, 4); } int extraFieldLength = calculateExtraDataRecordsSize(fileHeader, writeZip64ExtendedInfo); rawIO.writeShortLittleEndian(byteArrayOutputStream, extraFieldLength); String fileComment = fileHeader.getFileComment(); byte[] fileCommentBytes = new byte[0]; if (isStringNotNullAndNotEmpty(fileComment)) { fileCommentBytes = fileComment.getBytes(charset); } rawIO.writeShortLittleEndian(byteArrayOutputStream, fileCommentBytes.length); //Skip disk number start for now rawIO.writeShortLittleEndian(byteArrayOutputStream, fileHeader.getDiskNumberStart()); //Skip internal file attributes for now byteArrayOutputStream.write(emptyShortByte); //External file attributes byteArrayOutputStream.write(fileHeader.getExternalFileAttributes()); //offset local header - this data is computed above byteArrayOutputStream.write(offsetLocalHeaderBytes); if (fileNameBytes.length > 0) { byteArrayOutputStream.write(fileNameBytes); } if (writeZip64ExtendedInfo) { zipModel.setZip64Format(true); //Zip64 header rawIO.writeShortLittleEndian(byteArrayOutputStream, (int) HeaderSignature.ZIP64_EXTRA_FIELD_SIGNATURE.getValue()); //size of data rawIO.writeShortLittleEndian(byteArrayOutputStream, ZIP64_EXTRA_DATA_RECORD_SIZE_FH); rawIO.writeLongLittleEndian(byteArrayOutputStream, fileHeader.getUncompressedSize()); rawIO.writeLongLittleEndian(byteArrayOutputStream, fileHeader.getCompressedSize()); rawIO.writeLongLittleEndian(byteArrayOutputStream, fileHeader.getOffsetLocalHeader()); rawIO.writeIntLittleEndian(byteArrayOutputStream, fileHeader.getDiskNumberStart()); } if (fileHeader.getAesExtraDataRecord() != null) { AESExtraDataRecord aesExtraDataRecord = fileHeader.getAesExtraDataRecord(); rawIO.writeShortLittleEndian(byteArrayOutputStream, (int) aesExtraDataRecord.getSignature().getValue()); rawIO.writeShortLittleEndian(byteArrayOutputStream, aesExtraDataRecord.getDataSize()); rawIO.writeShortLittleEndian(byteArrayOutputStream, aesExtraDataRecord.getAesVersion().getVersionNumber()); byteArrayOutputStream.write(aesExtraDataRecord.getVendorID().getBytes()); byte[] aesStrengthBytes = new byte[1]; aesStrengthBytes[0] = (byte) aesExtraDataRecord.getAesKeyStrength().getRawCode(); byteArrayOutputStream.write(aesStrengthBytes); rawIO.writeShortLittleEndian(byteArrayOutputStream, aesExtraDataRecord.getCompressionMethod().getCode()); } writeRemainingExtraDataRecordsIfPresent(fileHeader, byteArrayOutputStream); if (fileCommentBytes.length > 0) { byteArrayOutputStream.write(fileCommentBytes); } } catch (Exception e) { throw new ZipException(e); } } private int calculateExtraDataRecordsSize(FileHeader fileHeader, boolean writeZip64ExtendedInfo) throws IOException { int extraFieldLength = 0; if (writeZip64ExtendedInfo) { extraFieldLength += ZIP64_EXTRA_DATA_RECORD_SIZE_FH + 4; // 4 for signature + size of record } if (fileHeader.getAesExtraDataRecord() != null) { extraFieldLength += AES_EXTRA_DATA_RECORD_SIZE; } if (fileHeader.getExtraDataRecords() != null) { for (ExtraDataRecord extraDataRecord : fileHeader.getExtraDataRecords()) { if (extraDataRecord.getHeader() == HeaderSignature.AES_EXTRA_DATA_RECORD.getValue() || extraDataRecord.getHeader() == HeaderSignature.ZIP64_EXTRA_FIELD_SIGNATURE.getValue()) { continue; } extraFieldLength += 4 + extraDataRecord.getSizeOfData(); // 4 = 2 for header + 2 for size of data } } return extraFieldLength; } private void writeRemainingExtraDataRecordsIfPresent(FileHeader fileHeader, OutputStream outputStream) throws IOException { if (fileHeader.getExtraDataRecords() == null || fileHeader.getExtraDataRecords().size() == 0) { return; } for (ExtraDataRecord extraDataRecord : fileHeader.getExtraDataRecords()) { if (extraDataRecord.getHeader() == HeaderSignature.AES_EXTRA_DATA_RECORD.getValue() || extraDataRecord.getHeader() == HeaderSignature.ZIP64_EXTRA_FIELD_SIGNATURE.getValue()) { continue; } rawIO.writeShortLittleEndian(outputStream, (int) extraDataRecord.getHeader()); rawIO.writeShortLittleEndian(outputStream, extraDataRecord.getSizeOfData()); outputStream.write(extraDataRecord.getData()); } } private void writeZip64EndOfCentralDirectoryRecord(ZipModel zipModel, int sizeOfCentralDir, long offsetCentralDir, ByteArrayOutputStream byteArrayOutputStream, RawIO rawIO) throws IOException { byte[] emptyShortByte = {0, 0}; rawIO.writeIntLittleEndian(byteArrayOutputStream, (int) HeaderSignature.ZIP64_END_CENTRAL_DIRECTORY_RECORD.getValue()); rawIO.writeLongLittleEndian(byteArrayOutputStream, (long) 44); if (zipModel.getCentralDirectory() != null && zipModel.getCentralDirectory().getFileHeaders() != null && zipModel.getCentralDirectory().getFileHeaders().size() > 0) { rawIO.writeShortLittleEndian(byteArrayOutputStream, zipModel.getCentralDirectory().getFileHeaders().get(0).getVersionMadeBy()); rawIO.writeShortLittleEndian(byteArrayOutputStream, zipModel.getCentralDirectory().getFileHeaders().get(0).getVersionNeededToExtract()); } else { byteArrayOutputStream.write(emptyShortByte); byteArrayOutputStream.write(emptyShortByte); } rawIO.writeIntLittleEndian(byteArrayOutputStream, zipModel.getEndOfCentralDirectoryRecord().getNumberOfThisDisk()); rawIO.writeIntLittleEndian(byteArrayOutputStream, zipModel.getEndOfCentralDirectoryRecord() .getNumberOfThisDiskStartOfCentralDir()); long numEntries = zipModel.getCentralDirectory().getFileHeaders().size(); long numEntriesOnThisDisk = numEntries; if (zipModel.isSplitArchive()) { numEntriesOnThisDisk = countNumberOfFileHeaderEntriesOnDisk(zipModel.getCentralDirectory().getFileHeaders(), zipModel.getEndOfCentralDirectoryRecord().getNumberOfThisDisk()); } rawIO.writeLongLittleEndian(byteArrayOutputStream, numEntriesOnThisDisk); rawIO.writeLongLittleEndian(byteArrayOutputStream, numEntries); rawIO.writeLongLittleEndian(byteArrayOutputStream, sizeOfCentralDir); rawIO.writeLongLittleEndian(byteArrayOutputStream, offsetCentralDir); } private void writeZip64EndOfCentralDirectoryLocator(ZipModel zipModel, ByteArrayOutputStream byteArrayOutputStream, RawIO rawIO) throws IOException { rawIO.writeIntLittleEndian(byteArrayOutputStream, (int) HeaderSignature.ZIP64_END_CENTRAL_DIRECTORY_LOCATOR.getValue()); rawIO.writeIntLittleEndian(byteArrayOutputStream, zipModel.getZip64EndOfCentralDirectoryLocator().getNumberOfDiskStartOfZip64EndOfCentralDirectoryRecord()); rawIO.writeLongLittleEndian(byteArrayOutputStream, zipModel.getZip64EndOfCentralDirectoryLocator().getOffsetZip64EndOfCentralDirectoryRecord()); rawIO.writeIntLittleEndian(byteArrayOutputStream, zipModel.getZip64EndOfCentralDirectoryLocator().getTotalNumberOfDiscs()); } private void writeEndOfCentralDirectoryRecord(ZipModel zipModel, int sizeOfCentralDir, long offsetCentralDir, ByteArrayOutputStream byteArrayOutputStream, RawIO rawIO, Charset charset) throws IOException { byte[] longByte = new byte[8]; rawIO.writeIntLittleEndian(byteArrayOutputStream, (int) HeaderSignature.END_OF_CENTRAL_DIRECTORY.getValue()); rawIO.writeShortLittleEndian(byteArrayOutputStream, zipModel.getEndOfCentralDirectoryRecord().getNumberOfThisDisk()); rawIO.writeShortLittleEndian(byteArrayOutputStream, zipModel.getEndOfCentralDirectoryRecord().getNumberOfThisDiskStartOfCentralDir()); long numEntries = zipModel.getCentralDirectory().getFileHeaders().size(); long numEntriesOnThisDisk = numEntries; if (zipModel.isSplitArchive()) { numEntriesOnThisDisk = countNumberOfFileHeaderEntriesOnDisk(zipModel.getCentralDirectory().getFileHeaders(), zipModel.getEndOfCentralDirectoryRecord().getNumberOfThisDisk()); } if (numEntriesOnThisDisk > InternalZipConstants.ZIP_64_NUMBER_OF_ENTRIES_LIMIT) { numEntriesOnThisDisk = InternalZipConstants.ZIP_64_NUMBER_OF_ENTRIES_LIMIT; } rawIO.writeShortLittleEndian(byteArrayOutputStream, (int) numEntriesOnThisDisk); if (numEntries > InternalZipConstants.ZIP_64_NUMBER_OF_ENTRIES_LIMIT) { numEntries = InternalZipConstants.ZIP_64_NUMBER_OF_ENTRIES_LIMIT; } rawIO.writeShortLittleEndian(byteArrayOutputStream, (int) numEntries); rawIO.writeIntLittleEndian(byteArrayOutputStream, sizeOfCentralDir); if (offsetCentralDir > ZIP_64_SIZE_LIMIT) { rawIO.writeLongLittleEndian(longByte, 0, ZIP_64_SIZE_LIMIT); byteArrayOutputStream.write(longByte, 0, 4); } else { rawIO.writeLongLittleEndian(longByte, 0, offsetCentralDir); byteArrayOutputStream.write(longByte, 0, 4); } String comment = zipModel.getEndOfCentralDirectoryRecord().getComment(); if (isStringNotNullAndNotEmpty(comment)) { byte[] commentBytes = comment.getBytes(charset); rawIO.writeShortLittleEndian(byteArrayOutputStream, commentBytes.length); byteArrayOutputStream.write(commentBytes); } else { rawIO.writeShortLittleEndian(byteArrayOutputStream, 0); } } private long countNumberOfFileHeaderEntriesOnDisk(List fileHeaders, int numOfDisk) throws ZipException { if (fileHeaders == null) { throw new ZipException("file headers are null, cannot calculate number of entries on this disk"); } int noEntries = 0; for (FileHeader fileHeader : fileHeaders) { if (fileHeader.getDiskNumberStart() == numOfDisk) { noEntries++; } } return noEntries; } private boolean isZip64Entry(FileHeader fileHeader) { return fileHeader.getCompressedSize() >= ZIP_64_SIZE_LIMIT || fileHeader.getUncompressedSize() >= ZIP_64_SIZE_LIMIT || fileHeader.getOffsetLocalHeader() >= ZIP_64_SIZE_LIMIT; } }zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/000077500000000000000000000000001357650161600210755ustar00rootroot00000000000000zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/inputstream/000077500000000000000000000000001357650161600234505ustar00rootroot00000000000000zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/inputstream/AesCipherInputStream.java000066400000000000000000000142701357650161600303560ustar00rootroot00000000000000package net.lingala.zip4j.io.inputstream; import net.lingala.zip4j.crypto.AESDecrypter; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.AESExtraDataRecord; import net.lingala.zip4j.model.LocalFileHeader; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.util.InternalZipConstants; import net.lingala.zip4j.util.Zip4jUtil; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import static net.lingala.zip4j.util.InternalZipConstants.AES_AUTH_LENGTH; import static net.lingala.zip4j.util.Zip4jUtil.readFully; class AesCipherInputStream extends CipherInputStream { private byte[] singleByteBuffer = new byte[1]; private byte[] aes16ByteBlock = new byte[16]; private int aes16ByteBlockPointer = 0; private int remainingAes16ByteBlockLength = 0; private int lengthToRead = 0; private int offsetWithAesBlock = 0; private int bytesCopiedInThisIteration = 0; private int lengthToCopyInThisIteration = 0; private int aes16ByteBlockReadLength = 0; public AesCipherInputStream(ZipEntryInputStream zipEntryInputStream, LocalFileHeader localFileHeader, char[] password) throws IOException { super(zipEntryInputStream, localFileHeader, password); } @Override protected AESDecrypter initializeDecrypter(LocalFileHeader localFileHeader, char[] password) throws IOException { return new AESDecrypter(localFileHeader.getAesExtraDataRecord(), password, getSalt(localFileHeader), getPasswordVerifier()); } @Override public int read() throws IOException { int readLen = read(singleByteBuffer); if (readLen == -1) { return -1; } return singleByteBuffer[0]; } @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @Override public int read(byte[] b, int off, int len) throws IOException { lengthToRead = len; offsetWithAesBlock = off; bytesCopiedInThisIteration = 0; if (remainingAes16ByteBlockLength != 0) { copyBytesFromBuffer(b, offsetWithAesBlock); if (bytesCopiedInThisIteration == len) { return bytesCopiedInThisIteration; } } if (lengthToRead < 16) { aes16ByteBlockReadLength = super.read(aes16ByteBlock, 0, aes16ByteBlock.length); aes16ByteBlockPointer = 0; if (aes16ByteBlockReadLength == -1) { remainingAes16ByteBlockLength = 0; if (bytesCopiedInThisIteration > 0) { return bytesCopiedInThisIteration; } return -1; } remainingAes16ByteBlockLength = aes16ByteBlockReadLength; copyBytesFromBuffer(b, offsetWithAesBlock); if (bytesCopiedInThisIteration == len) { return bytesCopiedInThisIteration; } } int readLen = super.read(b, offsetWithAesBlock, (lengthToRead - lengthToRead %16)); if (readLen == -1) { if (bytesCopiedInThisIteration > 0) { return bytesCopiedInThisIteration; } else { return -1; } } else { return readLen + bytesCopiedInThisIteration; } } private void copyBytesFromBuffer(byte[] b, int off) { lengthToCopyInThisIteration = lengthToRead < remainingAes16ByteBlockLength ? lengthToRead : remainingAes16ByteBlockLength; System.arraycopy(aes16ByteBlock, aes16ByteBlockPointer, b, off, lengthToCopyInThisIteration); incrementAesByteBlockPointer(lengthToCopyInThisIteration); decrementRemainingAesBytesLength(lengthToCopyInThisIteration); bytesCopiedInThisIteration += lengthToCopyInThisIteration; lengthToRead -= lengthToCopyInThisIteration; offsetWithAesBlock += lengthToCopyInThisIteration; } @Override protected void endOfEntryReached(InputStream inputStream) throws IOException { verifyContent(readStoredMac(inputStream)); } private void verifyContent(byte[] storedMac) throws IOException { if (getLocalFileHeader().isDataDescriptorExists() && CompressionMethod.DEFLATE.equals(Zip4jUtil.getCompressionMethod(getLocalFileHeader()))) { // Skip content verification in case of Deflate compression and if data descriptor exists. // In this case, we do not know the exact size of compressed data before hand and it is possible that we read // and pass more than required data into inflater, thereby corrupting the aes mac bytes. // See usage of PushBackInputStream in the project for how this push back of data is done // Unfortunately, in this case we cannot perform a content verification and have to skip return; } byte[] calculatedMac = getDecrypter().getCalculatedAuthenticationBytes(); byte[] first10BytesOfCalculatedMac = new byte[AES_AUTH_LENGTH]; System.arraycopy(calculatedMac, 0, first10BytesOfCalculatedMac, 0, InternalZipConstants.AES_AUTH_LENGTH); if (!Arrays.equals(storedMac, first10BytesOfCalculatedMac)) { throw new IOException("Reached end of data for this entry, but aes verification failed"); } } protected byte[] readStoredMac(InputStream inputStream) throws IOException { byte[] storedMac = new byte[AES_AUTH_LENGTH]; int readLen = readFully(inputStream, storedMac); if (readLen != AES_AUTH_LENGTH) { throw new ZipException("Invalid AES Mac bytes. Could not read sufficient data"); } return storedMac; } private byte[] getSalt(LocalFileHeader localFileHeader) throws IOException { if (localFileHeader.getAesExtraDataRecord() == null) { throw new IOException("invalid aes extra data record"); } AESExtraDataRecord aesExtraDataRecord = localFileHeader.getAesExtraDataRecord(); byte[] saltBytes = new byte[aesExtraDataRecord.getAesKeyStrength().getSaltLength()]; readRaw(saltBytes); return saltBytes; } private byte[] getPasswordVerifier() throws IOException { byte[] pvBytes = new byte[2]; readRaw(pvBytes); return pvBytes; } private void incrementAesByteBlockPointer(int incrementBy) { aes16ByteBlockPointer += incrementBy; if (aes16ByteBlockPointer >= 15) { aes16ByteBlockPointer = 15; } } private void decrementRemainingAesBytesLength(int decrementBy) { remainingAes16ByteBlockLength -= decrementBy; if (remainingAes16ByteBlockLength <= 0) { remainingAes16ByteBlockLength = 0; } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/inputstream/CipherInputStream.java000066400000000000000000000061621357650161600277260ustar00rootroot00000000000000package net.lingala.zip4j.io.inputstream; import net.lingala.zip4j.crypto.Decrypter; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.LocalFileHeader; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.util.InternalZipConstants; import java.io.IOException; import java.io.InputStream; import static net.lingala.zip4j.util.Zip4jUtil.readFully; abstract class CipherInputStream extends InputStream { private ZipEntryInputStream zipEntryInputStream; private T decrypter; private byte[] lastReadRawDataCache; private byte[] singleByteBuffer = new byte[1]; private LocalFileHeader localFileHeader; public CipherInputStream(ZipEntryInputStream zipEntryInputStream, LocalFileHeader localFileHeader, char[] password) throws IOException, ZipException { this.zipEntryInputStream = zipEntryInputStream; this.decrypter = initializeDecrypter(localFileHeader, password); this.localFileHeader = localFileHeader; if (getCompressionMethod(localFileHeader) == CompressionMethod.DEFLATE) { lastReadRawDataCache = new byte[InternalZipConstants.BUFF_SIZE]; } } @Override public int read() throws IOException { int readLen = read(singleByteBuffer); if (readLen == -1) { return -1; } return singleByteBuffer[0] & 0xff; } @Override public int read(byte[] b) throws IOException { return this.read(b, 0, b.length); } @Override public int read(byte[] b, int off, int len) throws IOException { int readLen = readFully(zipEntryInputStream, b, off, len); if (readLen > 0) { cacheRawData(b, readLen); decrypter.decryptData(b, off, readLen); } return readLen; } @Override public void close() throws IOException { zipEntryInputStream.close(); } public byte[] getLastReadRawDataCache() { return lastReadRawDataCache; } protected int readRaw(byte[] b) throws IOException { return zipEntryInputStream.readRawFully(b); } private void cacheRawData(byte[] b, int len) { if (lastReadRawDataCache != null) { System.arraycopy(b, 0, lastReadRawDataCache, 0, len); } } private CompressionMethod getCompressionMethod(LocalFileHeader localFileHeader) throws ZipException { if (localFileHeader.getCompressionMethod() != CompressionMethod.AES_INTERNAL_ONLY) { return localFileHeader.getCompressionMethod(); } if (localFileHeader.getAesExtraDataRecord() == null) { throw new ZipException("AesExtraDataRecord not present in localheader for aes encrypted data"); } return localFileHeader.getAesExtraDataRecord().getCompressionMethod(); } public T getDecrypter() { return decrypter; } protected void endOfEntryReached(InputStream inputStream) throws IOException { // is optional but useful for AES } protected long getNumberOfBytesReadForThisEntry() { return zipEntryInputStream.getNumberOfBytesRead(); } public LocalFileHeader getLocalFileHeader() { return localFileHeader; } protected abstract T initializeDecrypter(LocalFileHeader localFileHeader, char[] password) throws IOException, ZipException; } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/inputstream/DecompressedInputStream.java000066400000000000000000000024301357650161600311230ustar00rootroot00000000000000package net.lingala.zip4j.io.inputstream; import java.io.IOException; import java.io.InputStream; import java.io.PushbackInputStream; abstract class DecompressedInputStream extends InputStream { private CipherInputStream cipherInputStream; protected byte[] oneByteBuffer = new byte[1]; public DecompressedInputStream(CipherInputStream cipherInputStream) { this.cipherInputStream = cipherInputStream; } @Override public int read() throws IOException { int readLen = read(oneByteBuffer); if (readLen == -1) { return -1; } return oneByteBuffer[0]; } @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @Override public int read(byte[] b, int off, int len) throws IOException { return cipherInputStream.read(b, off, len); } @Override public void close() throws IOException { cipherInputStream.close(); } public void endOfEntryReached(InputStream inputStream) throws IOException { cipherInputStream.endOfEntryReached(inputStream); } public void pushBackInputStreamIfNecessary(PushbackInputStream pushbackInputStream) throws IOException { // Do nothing by default } protected byte[] getLastReadRawDataCache() { return cipherInputStream.getLastReadRawDataCache(); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/inputstream/InflaterInputStream.java000066400000000000000000000035221357650161600302550ustar00rootroot00000000000000package net.lingala.zip4j.io.inputstream; import net.lingala.zip4j.util.InternalZipConstants; import java.io.EOFException; import java.io.IOException; import java.io.PushbackInputStream; import java.util.zip.DataFormatException; import java.util.zip.Inflater; public class InflaterInputStream extends DecompressedInputStream { private Inflater inflater; private byte[] buff; private byte[] singleByteBuffer = new byte[1]; private int len; public InflaterInputStream(CipherInputStream cipherInputStream) { super(cipherInputStream); this.inflater = new Inflater(true); buff = new byte[InternalZipConstants.BUFF_SIZE]; } @Override public int read() throws IOException { int readLen = read(singleByteBuffer); if (readLen == -1) { return -1; } return singleByteBuffer[0]; } @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @Override public int read(byte[] b, int off, int len) throws IOException { try { int n; while ((n = inflater.inflate(b, off, len)) == 0) { if (inflater.finished() || inflater.needsDictionary()) { return -1; } if (inflater.needsInput()) { fill(); } } return n; } catch (DataFormatException e) { throw new IOException(e); } } @Override public void pushBackInputStreamIfNecessary(PushbackInputStream pushbackInputStream) throws IOException { int n = inflater.getRemaining(); if (n > 0) { byte[] rawDataCache = getLastReadRawDataCache(); pushbackInputStream.unread(rawDataCache, len - n, n); } } private void fill() throws IOException { len = super.read(buff, 0, buff.length); if (len == -1) { throw new EOFException("Unexpected end of input stream"); } inflater.setInput(buff, 0, len); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/inputstream/NoCipherInputStream.java000066400000000000000000000014211357650161600302140ustar00rootroot00000000000000package net.lingala.zip4j.io.inputstream; import net.lingala.zip4j.crypto.Decrypter; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.LocalFileHeader; import java.io.IOException; class NoCipherInputStream extends CipherInputStream { public NoCipherInputStream(ZipEntryInputStream zipEntryInputStream, LocalFileHeader localFileHeader, char[] password) throws IOException, ZipException { super(zipEntryInputStream, localFileHeader, password); } @Override protected Decrypter initializeDecrypter(LocalFileHeader localFileHeader, char[] password) { return new NoDecrypter(); } static class NoDecrypter implements Decrypter { @Override public int decryptData(byte[] buff, int start, int len) { return len; } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/inputstream/SplitInputStream.java000066400000000000000000000062231357650161600276050ustar00rootroot00000000000000package net.lingala.zip4j.io.inputstream; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.enums.RandomAccessFileMode; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; public class SplitInputStream extends InputStream { private RandomAccessFile randomAccessFile; private File zipFile; private int lastSplitZipFileNumber; private boolean isSplitZipArchive; private int currentSplitFileCounter = 0; private byte[] singleByteArray = new byte[1]; public SplitInputStream(File zipFile, boolean isSplitZipArchive, int lastSplitZipFileNumber) throws FileNotFoundException { this.randomAccessFile = new RandomAccessFile(zipFile, RandomAccessFileMode.READ.getValue()); this.zipFile = zipFile; this.isSplitZipArchive = isSplitZipArchive; this.lastSplitZipFileNumber = lastSplitZipFileNumber; if (isSplitZipArchive) { currentSplitFileCounter = lastSplitZipFileNumber; } } @Override public int read() throws IOException { int readLen = read(singleByteArray); if (readLen == -1) { return -1; } return singleByteArray[0]; } @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @Override public int read(byte[] b, int off, int len) throws IOException { int readLen = randomAccessFile.read(b, off, len); if ((readLen != len || readLen == -1) && isSplitZipArchive) { openRandomAccessFileForIndex(currentSplitFileCounter + 1); currentSplitFileCounter++; if (readLen < 0) readLen = 0; int newlyRead = randomAccessFile.read(b, readLen, len - readLen); if (newlyRead > 0) readLen += newlyRead; } return readLen; } public void prepareExtractionForFileHeader(FileHeader fileHeader) throws IOException { if (isSplitZipArchive && (currentSplitFileCounter != fileHeader.getDiskNumberStart())) { openRandomAccessFileForIndex(fileHeader.getDiskNumberStart()); currentSplitFileCounter = fileHeader.getDiskNumberStart(); } randomAccessFile.seek(fileHeader.getOffsetLocalHeader()); } private void openRandomAccessFileForIndex(int zipFileIndex) throws IOException { File nextSplitFile = getNextSplitFileName(zipFileIndex); if (!nextSplitFile.exists()) { throw new FileNotFoundException("zip split file does not exist: " + nextSplitFile); } randomAccessFile.close(); randomAccessFile = new RandomAccessFile(nextSplitFile, RandomAccessFileMode.READ.getValue()); } private File getNextSplitFileName(int zipFileIndex) throws IOException { if (zipFileIndex == lastSplitZipFileNumber) { return zipFile; } String currZipFileNameWithPath = zipFile.getCanonicalPath(); String extensionSubString = ".z0"; if (zipFileIndex >= 9) { extensionSubString = ".z"; } return new File(currZipFileNameWithPath.substring(0, currZipFileNameWithPath.lastIndexOf(".")) + extensionSubString + (zipFileIndex + 1)); } @Override public void close() throws IOException { if (randomAccessFile != null) { randomAccessFile.close(); } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/inputstream/StoreInputStream.java000066400000000000000000000003121357650161600275770ustar00rootroot00000000000000package net.lingala.zip4j.io.inputstream; class StoreInputStream extends DecompressedInputStream { public StoreInputStream(CipherInputStream cipherInputStream) { super(cipherInputStream); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/inputstream/ZipEntryInputStream.java000066400000000000000000000043761357650161600303050ustar00rootroot00000000000000package net.lingala.zip4j.io.inputstream; import java.io.IOException; import java.io.InputStream; class ZipEntryInputStream extends InputStream { private static final int MAX_RAW_READ_FULLY_RETRY_ATTEMPTS = 15; private InputStream inputStream; private long numberOfBytesRead = 0; private byte[] singleByteArray = new byte[1]; private long compressedSize; public ZipEntryInputStream(InputStream inputStream, long compressedSize) { this.inputStream = inputStream; this.compressedSize = compressedSize; } @Override public int read() throws IOException { int readLen = read(singleByteArray); if (readLen == -1) { return -1; } return singleByteArray[0]; } @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @Override public int read(byte[] b, int off, int len) throws IOException { if (compressedSize != -1) { if (numberOfBytesRead >= compressedSize) { return -1; } if (len > compressedSize - numberOfBytesRead) { len = (int) (compressedSize - numberOfBytesRead); } } int readLen = inputStream.read(b, off, len); if (readLen > 0) { numberOfBytesRead += readLen; } return readLen; } public int readRawFully(byte[] b) throws IOException { int readLen = inputStream.read(b); if (readLen != b.length) { readLen = readUntilBufferIsFull(b, readLen); if (readLen != b.length) { throw new IOException("Cannot read fully into byte buffer"); } } return readLen; } private int readUntilBufferIsFull(byte[] b, int readLength) throws IOException { int remainingLength = b.length - readLength; int loopReadLength = 0; int retryAttempt = 0; while (readLength < b.length && loopReadLength != -1 && retryAttempt < MAX_RAW_READ_FULLY_RETRY_ATTEMPTS) { loopReadLength += inputStream.read(b, readLength, remainingLength); if (loopReadLength > 0) { readLength += loopReadLength; remainingLength -= loopReadLength; } retryAttempt++; } return readLength; } @Override public void close() throws IOException { inputStream.close(); } public long getNumberOfBytesRead() { return numberOfBytesRead; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/inputstream/ZipInputStream.java000077500000000000000000000263701357650161600272640ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.io.inputstream; import net.lingala.zip4j.crypto.AESDecrypter; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.headers.HeaderReader; import net.lingala.zip4j.headers.HeaderSignature; import net.lingala.zip4j.model.DataDescriptor; import net.lingala.zip4j.model.ExtraDataRecord; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.LocalFileHeader; import net.lingala.zip4j.model.enums.AesVersion; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.model.enums.EncryptionMethod; import net.lingala.zip4j.util.InternalZipConstants; import java.io.IOException; import java.io.InputStream; import java.io.PushbackInputStream; import java.nio.charset.Charset; import java.util.List; import java.util.zip.CRC32; import java.util.zip.DataFormatException; import static net.lingala.zip4j.util.FileUtils.isZipEntryDirectory; import static net.lingala.zip4j.util.Zip4jUtil.getCompressionMethod; public class ZipInputStream extends InputStream { private PushbackInputStream inputStream; private DecompressedInputStream decompressedInputStream; private HeaderReader headerReader = new HeaderReader(); private char[] password; private LocalFileHeader localFileHeader; private CRC32 crc32 = new CRC32(); private byte[] endOfEntryBuffer; private boolean extraDataRecordReadForThisEntry = false; private boolean canSkipExtendedLocalFileHeader = false; private Charset charset; public ZipInputStream(InputStream inputStream) { this(inputStream, null, InternalZipConstants.CHARSET_UTF_8); } public ZipInputStream(InputStream inputStream, Charset charset) { this(inputStream, null, charset); } public ZipInputStream(InputStream inputStream, char[] password) { this(inputStream, password, InternalZipConstants.CHARSET_UTF_8); } public ZipInputStream(InputStream inputStream, char[] password, Charset charset) { if(charset == null) { charset = InternalZipConstants.CHARSET_UTF_8; } this.inputStream = new PushbackInputStream(inputStream, InternalZipConstants.BUFF_SIZE); this.password = password; this.charset = charset; } public LocalFileHeader getNextEntry() throws IOException { return getNextEntry(null); } public LocalFileHeader getNextEntry(FileHeader fileHeader) throws IOException { if (localFileHeader != null) { readUntilEndOfEntry(); } localFileHeader = headerReader.readLocalFileHeader(inputStream, charset); if (localFileHeader == null) { return null; } verifyLocalFileHeader(localFileHeader); crc32.reset(); if (fileHeader != null) { localFileHeader.setCrc(fileHeader.getCrc()); localFileHeader.setCompressedSize(fileHeader.getCompressedSize()); localFileHeader.setUncompressedSize(fileHeader.getUncompressedSize()); canSkipExtendedLocalFileHeader = true; } else { canSkipExtendedLocalFileHeader = false; } if (!isZipEntryDirectory(localFileHeader.getFileName())) { this.decompressedInputStream = initializeEntryInputStream(localFileHeader); } this.extraDataRecordReadForThisEntry = false; return localFileHeader; } @Override public int read() throws IOException { byte[] b = new byte[1]; int readLen = read(b); if (readLen == -1) { return -1; } return b[0]; } @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @Override public int read(byte[] b, int off, int len) throws IOException { if (len < 0) { throw new IllegalArgumentException("Negative read length"); } if (len == 0) { return 0; } if (localFileHeader == null) { // localfileheader can be null when end of compressed data is reached. If null check is missing, read method will // throw a NPE when end of compressed data is reached and read is called again. return -1; } if (localFileHeader.isDirectory()) { if (!extraDataRecordReadForThisEntry) { readExtendedLocalFileHeaderIfPresent(); extraDataRecordReadForThisEntry = true; } return -1; } try { int readLen = decompressedInputStream.read(b, off, len); if (readLen == -1) { endOfCompressedDataReached(); } else { crc32.update(b, off, readLen); } return readLen; } catch (IOException e) { if (e.getCause() != null && e.getCause() instanceof DataFormatException && isEncryptionMethodZipStandard(localFileHeader)) { throw new ZipException(e.getMessage(), e.getCause(), ZipException.Type.WRONG_PASSWORD); } throw e; } } @Override public void close() throws IOException { if (decompressedInputStream != null) { decompressedInputStream.close(); } } public int getAvailableBytesInPushBackInputStream() throws IOException { return inputStream.available(); } private void endOfCompressedDataReached() throws IOException { //With inflater, without knowing the compressed or uncompressed size, we over read necessary data //In such cases, we have to push back the inputstream to the end of data decompressedInputStream.pushBackInputStreamIfNecessary(inputStream); //First signal the end of data for this entry so that ciphers can read any header data if applicable decompressedInputStream.endOfEntryReached(inputStream); readExtendedLocalFileHeaderIfPresent(); verifyCrc(); resetFields(); } private DecompressedInputStream initializeEntryInputStream(LocalFileHeader localFileHeader) throws IOException { ZipEntryInputStream zipEntryInputStream = new ZipEntryInputStream(inputStream, getCompressedSize(localFileHeader)); CipherInputStream cipherInputStream = initializeCipherInputStream(zipEntryInputStream, localFileHeader); return initializeDecompressorForThisEntry(cipherInputStream, localFileHeader); } private CipherInputStream initializeCipherInputStream(ZipEntryInputStream zipEntryInputStream, LocalFileHeader localFileHeader) throws IOException { if (!localFileHeader.isEncrypted()) { return new NoCipherInputStream(zipEntryInputStream, localFileHeader, password); } if (localFileHeader.getEncryptionMethod() == EncryptionMethod.AES) { return new AesCipherInputStream(zipEntryInputStream, localFileHeader, password); } else { return new ZipStandardCipherInputStream(zipEntryInputStream, localFileHeader, password); } } private DecompressedInputStream initializeDecompressorForThisEntry(CipherInputStream cipherInputStream, LocalFileHeader localFileHeader) { CompressionMethod compressionMethod = getCompressionMethod(localFileHeader); if (compressionMethod == CompressionMethod.DEFLATE) { return new InflaterInputStream(cipherInputStream); } return new StoreInputStream(cipherInputStream); } private void readExtendedLocalFileHeaderIfPresent() throws IOException { if (!localFileHeader.isDataDescriptorExists() || canSkipExtendedLocalFileHeader) { return; } DataDescriptor dataDescriptor = headerReader.readDataDescriptor(inputStream, checkIfZip64ExtraDataRecordPresentInLFH(localFileHeader.getExtraDataRecords())); localFileHeader.setCompressedSize(dataDescriptor.getCompressedSize()); localFileHeader.setUncompressedSize(dataDescriptor.getUncompressedSize()); localFileHeader.setCrc(dataDescriptor.getCrc()); } private void verifyLocalFileHeader(LocalFileHeader localFileHeader) throws IOException { if (!isEntryDirectory(localFileHeader.getFileName()) && localFileHeader.getCompressionMethod() == CompressionMethod.STORE && localFileHeader.getUncompressedSize() < 0) { throw new IOException("Invalid local file header for: " + localFileHeader.getFileName() + ". Uncompressed size has to be set for entry of compression type store which is not a directory"); } } private boolean checkIfZip64ExtraDataRecordPresentInLFH(List extraDataRecords) { if (extraDataRecords == null) { return false; } for (ExtraDataRecord extraDataRecord : extraDataRecords) { if (extraDataRecord.getHeader() == HeaderSignature.ZIP64_EXTRA_FIELD_SIGNATURE.getValue()) { return true; } } return false; } private void verifyCrc() throws IOException { if (localFileHeader.getEncryptionMethod() == EncryptionMethod.AES && localFileHeader.getAesExtraDataRecord().getAesVersion().equals(AesVersion.TWO)) { // Verification will be done in this case by AesCipherInputStream return; } if (localFileHeader.getCrc() != crc32.getValue()) { ZipException.Type exceptionType = ZipException.Type.CHECKSUM_MISMATCH; if (isEncryptionMethodZipStandard(localFileHeader)) { exceptionType = ZipException.Type.WRONG_PASSWORD; } throw new ZipException("Reached end of entry, but crc verification failed for " + localFileHeader.getFileName(), exceptionType); } } private void resetFields() { localFileHeader = null; crc32.reset(); } private boolean isEntryDirectory(String entryName) { return entryName.endsWith("/") || entryName.endsWith("\\"); } private long getCompressedSize(LocalFileHeader localFileHeader) { if (getCompressionMethod(localFileHeader).equals(CompressionMethod.STORE)) { return localFileHeader.getUncompressedSize(); } if (localFileHeader.isDataDescriptorExists() && !canSkipExtendedLocalFileHeader) { return -1; } long compressedSize = localFileHeader.getCompressedSize(); if (localFileHeader.getZip64ExtendedInfo() != null) { compressedSize = localFileHeader.getZip64ExtendedInfo().getCompressedSize(); } return compressedSize - getEncryptionHeaderSize(localFileHeader); } private int getEncryptionHeaderSize(LocalFileHeader localFileHeader) { if (!localFileHeader.isEncrypted()) { return 0; } if (localFileHeader.getEncryptionMethod().equals(EncryptionMethod.AES)) { return InternalZipConstants.AES_AUTH_LENGTH + AESDecrypter.PASSWORD_VERIFIER_LENGTH + localFileHeader.getAesExtraDataRecord().getAesKeyStrength().getSaltLength(); } else if (localFileHeader.getEncryptionMethod().equals(EncryptionMethod.ZIP_STANDARD)) { return InternalZipConstants.STD_DEC_HDR_SIZE; } else { return 0; } } private void readUntilEndOfEntry() throws IOException { if (endOfEntryBuffer == null) { endOfEntryBuffer = new byte[512]; } while (read(endOfEntryBuffer) != -1); } private boolean isEncryptionMethodZipStandard(LocalFileHeader localFileHeader) { return localFileHeader.isEncrypted() && EncryptionMethod.ZIP_STANDARD.equals(localFileHeader.getEncryptionMethod()); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/inputstream/ZipStandardCipherInputStream.java000066400000000000000000000020361357650161600320660ustar00rootroot00000000000000package net.lingala.zip4j.io.inputstream; import net.lingala.zip4j.crypto.StandardDecrypter; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.LocalFileHeader; import java.io.IOException; import static net.lingala.zip4j.util.InternalZipConstants.STD_DEC_HDR_SIZE; class ZipStandardCipherInputStream extends CipherInputStream { public ZipStandardCipherInputStream(ZipEntryInputStream zipEntryInputStream, LocalFileHeader localFileHeader, char[] password) throws IOException, ZipException { super(zipEntryInputStream, localFileHeader, password); } @Override protected StandardDecrypter initializeDecrypter(LocalFileHeader localFileHeader, char[] password) throws ZipException, IOException { return new StandardDecrypter(password, localFileHeader.getCrcRawData(), getStandardDecrypterHeaderBytes()); } private byte[] getStandardDecrypterHeaderBytes() throws IOException { byte[] headerBytes = new byte[STD_DEC_HDR_SIZE]; readRaw(headerBytes); return headerBytes; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/outputstream/000077500000000000000000000000001357650161600236515ustar00rootroot00000000000000zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/outputstream/AesCipherOutputStream.java000066400000000000000000000046531357650161600307640ustar00rootroot00000000000000package net.lingala.zip4j.io.outputstream; import net.lingala.zip4j.crypto.AESEncrpyter; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.ZipParameters; import java.io.IOException; import java.io.OutputStream; import static net.lingala.zip4j.util.InternalZipConstants.AES_BLOCK_SIZE; class AesCipherOutputStream extends CipherOutputStream { private byte[] pendingBuffer = new byte[AES_BLOCK_SIZE]; private int pendingBufferLength = 0; public AesCipherOutputStream(ZipEntryOutputStream outputStream, ZipParameters zipParameters, char[] password) throws IOException, ZipException { super(outputStream, zipParameters, password); } @Override protected AESEncrpyter initializeEncrypter(OutputStream outputStream, ZipParameters zipParameters, char[] password) throws IOException, ZipException { AESEncrpyter encrypter = new AESEncrpyter(password, zipParameters.getAesKeyStrength()); writeAesEncryptionHeaderData(encrypter); return encrypter; } private void writeAesEncryptionHeaderData(AESEncrpyter encrypter) throws IOException { writeHeaders(encrypter.getSaltBytes()); writeHeaders(encrypter.getDerivedPasswordVerifier()); } @Override public void write(int b) throws IOException { write(new byte[] {(byte) b}); } @Override public void write(byte[] b) throws IOException { write(b, 0, b.length); } @Override public void write(byte[] b, int off, int len) throws IOException { if (len >= (AES_BLOCK_SIZE - pendingBufferLength)) { System.arraycopy(b, off, pendingBuffer, pendingBufferLength, (AES_BLOCK_SIZE - pendingBufferLength)); super.write(pendingBuffer, 0, pendingBuffer.length); off = (AES_BLOCK_SIZE - pendingBufferLength); len = len - off; pendingBufferLength = 0; } else { System.arraycopy(b, off, pendingBuffer, pendingBufferLength, len); pendingBufferLength += len; return; } if (len != 0 && len % 16 != 0) { System.arraycopy(b, (len + off) - (len % 16), pendingBuffer, 0, len % 16); pendingBufferLength = len % 16; len = len - pendingBufferLength; } super.write(b, off, len); } @Override public void closeEntry() throws IOException { if (this.pendingBufferLength != 0) { super.write(pendingBuffer, 0, pendingBufferLength); pendingBufferLength = 0; } writeHeaders(getEncrypter().getFinalMac()); super.closeEntry(); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/outputstream/CipherOutputStream.java000077500000000000000000000044061357650161600303320ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.io.outputstream; import net.lingala.zip4j.crypto.Encrypter; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.ZipParameters; import java.io.IOException; import java.io.OutputStream; abstract class CipherOutputStream extends OutputStream { private ZipEntryOutputStream zipEntryOutputStream; private T encrypter; public CipherOutputStream(ZipEntryOutputStream zipEntryOutputStream, ZipParameters zipParameters, char[] password) throws IOException, ZipException { this.zipEntryOutputStream = zipEntryOutputStream; this.encrypter = initializeEncrypter(zipEntryOutputStream, zipParameters, password); } @Override public void write(int b) throws IOException { zipEntryOutputStream.write(b); } @Override public void write(byte[] b) throws IOException { zipEntryOutputStream.write(b); } @Override public void write(byte[] b, int off, int len) throws IOException { encrypter.encryptData(b, off, len); zipEntryOutputStream.write(b, off, len); } public void writeHeaders(byte[] b) throws IOException { zipEntryOutputStream.write(b); } public void closeEntry() throws IOException { zipEntryOutputStream.closeEntry(); } @Override public void close() throws IOException { zipEntryOutputStream.close(); } public long getNumberOfBytesWrittenForThisEntry() { return zipEntryOutputStream.getNumberOfBytesWrittenForThisEntry(); } protected T getEncrypter() { return encrypter; } protected abstract T initializeEncrypter(OutputStream outputStream, ZipParameters zipParameters, char[] password) throws IOException, ZipException; } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/outputstream/CompressedOutputStream.java000066400000000000000000000017011357650161600312140ustar00rootroot00000000000000package net.lingala.zip4j.io.outputstream; import java.io.IOException; import java.io.OutputStream; abstract class CompressedOutputStream extends OutputStream { private CipherOutputStream cipherOutputStream; public CompressedOutputStream(CipherOutputStream cipherOutputStream) { this.cipherOutputStream = cipherOutputStream; } @Override public void write(int b) throws IOException { write(new byte[] {(byte) b}); } @Override public void write(byte[] b) throws IOException { write(b, 0, b.length); } @Override public void write(byte[] b, int off, int len) throws IOException { cipherOutputStream.write(b, off, len); } protected void closeEntry() throws IOException { cipherOutputStream.closeEntry(); } @Override public void close() throws IOException { cipherOutputStream.close(); } public long getCompressedSize() { return cipherOutputStream.getNumberOfBytesWrittenForThisEntry(); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/outputstream/CountingOutputStream.java000066400000000000000000000042601357650161600307010ustar00rootroot00000000000000package net.lingala.zip4j.io.outputstream; import net.lingala.zip4j.exception.ZipException; import java.io.IOException; import java.io.OutputStream; public class CountingOutputStream extends OutputStream { private OutputStream outputStream; private long numberOfBytesWritten = 0; public CountingOutputStream(OutputStream outputStream) { this.outputStream = outputStream; } @Override public void write(int b) throws IOException { write(new byte[] {(byte) b}); } @Override public void write(byte[] b) throws IOException { write(b, 0, b.length); } @Override public void write(byte[] b, int off, int len) throws IOException { outputStream.write(b, off, len); numberOfBytesWritten += len; } public int getCurrentSplitFileCounter() { if (isSplitZipFile()) { return ((SplitOutputStream) outputStream).getCurrentSplitFileCounter(); } return 0; } public long getOffsetForNextEntry() throws IOException { if (outputStream instanceof SplitOutputStream) { return ((SplitOutputStream) outputStream).getFilePointer(); } return numberOfBytesWritten; } public long getSplitLength() { if (isSplitZipFile()) { return ((SplitOutputStream) outputStream).getSplitLength(); } return 0; } public boolean isSplitZipFile() { return outputStream instanceof SplitOutputStream && ((SplitOutputStream)outputStream).isSplitZipFile(); } public long getNumberOfBytesWritten() throws IOException { if (outputStream instanceof SplitOutputStream) { return ((SplitOutputStream) outputStream).getFilePointer(); } return numberOfBytesWritten; } public boolean checkBuffSizeAndStartNextSplitFile(int bufferSize) throws ZipException { if (!isSplitZipFile()) { return false; } return ((SplitOutputStream)outputStream).checkBufferSizeAndStartNextSplitFile(bufferSize); } public long getFilePointer() throws IOException { if (outputStream instanceof SplitOutputStream) { return ((SplitOutputStream) outputStream).getFilePointer(); } return numberOfBytesWritten; } @Override public void close() throws IOException { outputStream.close(); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/outputstream/DeflaterOutputStream.java000077500000000000000000000036361357650161600306520ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.io.outputstream; import net.lingala.zip4j.model.enums.CompressionLevel; import java.io.IOException; import java.util.zip.Deflater; import static net.lingala.zip4j.util.InternalZipConstants.BUFF_SIZE; class DeflaterOutputStream extends CompressedOutputStream { private byte[] buff = new byte[BUFF_SIZE]; protected Deflater deflater; public DeflaterOutputStream(CipherOutputStream cipherOutputStream, CompressionLevel compressionLevel) { super(cipherOutputStream); deflater = new Deflater(compressionLevel.getLevel(), true); } public void write(byte[] b) throws IOException { write(b, 0, b.length); } public void write(int bval) throws IOException { byte[] b = new byte[1]; b[0] = (byte) bval; write(b, 0, 1); } public void write(byte[] buf, int off, int len) throws IOException { deflater.setInput(buf, off, len); while (!deflater.needsInput()) { deflate(); } } private void deflate() throws IOException { int len = deflater.deflate(buff, 0, buff.length); if (len > 0) { super.write(buff, 0, len); } } public void closeEntry() throws IOException { if (!deflater.finished()) { deflater.finish(); while (!deflater.finished()) { deflate(); } } deflater.end(); super.closeEntry(); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/outputstream/NoCipherOutputStream.java000066400000000000000000000017271357650161600306270ustar00rootroot00000000000000package net.lingala.zip4j.io.outputstream; import net.lingala.zip4j.crypto.Encrypter; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.ZipParameters; import java.io.IOException; import java.io.OutputStream; class NoCipherOutputStream extends CipherOutputStream { public NoCipherOutputStream(ZipEntryOutputStream zipEntryOutputStream, ZipParameters zipParameters, char[] password) throws IOException, ZipException { super(zipEntryOutputStream, zipParameters, password); } @Override protected NoEncrypter initializeEncrypter(OutputStream outputStream, ZipParameters zipParameters, char[] password) { return new NoEncrypter(); } static class NoEncrypter implements Encrypter { @Override public int encryptData(byte[] buff) { return encryptData(buff, 0, buff.length); } @Override public int encryptData(byte[] buff, int start, int len) { return len; } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/outputstream/SplitOutputStream.java000077500000000000000000000144251357650161600302150ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.io.outputstream; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.headers.HeaderSignature; import net.lingala.zip4j.model.enums.RandomAccessFileMode; import net.lingala.zip4j.util.RawIO; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; import java.io.RandomAccessFile; import static net.lingala.zip4j.util.FileUtils.getZipFileNameWithoutExtension; import static net.lingala.zip4j.util.InternalZipConstants.MIN_SPLIT_LENGTH; public class SplitOutputStream extends OutputStream { private RandomAccessFile raf; private long splitLength; private File zipFile; private int currSplitFileCounter; private long bytesWrittenForThisPart; private RawIO rawIO = new RawIO(); public SplitOutputStream(File file) throws FileNotFoundException, ZipException { this(file, -1); } public SplitOutputStream(File file, long splitLength) throws FileNotFoundException, ZipException { if (splitLength >= 0 && splitLength < MIN_SPLIT_LENGTH) { throw new ZipException("split length less than minimum allowed split length of " + MIN_SPLIT_LENGTH + " Bytes"); } this.raf = new RandomAccessFile(file, RandomAccessFileMode.WRITE.getValue()); this.splitLength = splitLength; this.zipFile = file; this.currSplitFileCounter = 0; this.bytesWrittenForThisPart = 0; } public void write(int b) throws IOException { write(new byte[] {(byte) b}); } public void write(byte[] b) throws IOException { write(b, 0, b.length); } public void write(byte[] b, int off, int len) throws IOException { if (len <= 0) { return; } if (splitLength == -1) { raf.write(b, off, len); bytesWrittenForThisPart += len; return; } if (bytesWrittenForThisPart >= splitLength) { startNextSplitFile(); raf.write(b, off, len); bytesWrittenForThisPart = len; } else if (bytesWrittenForThisPart + len > splitLength) { if (isHeaderData(b)) { startNextSplitFile(); raf.write(b, off, len); bytesWrittenForThisPart = len; } else { raf.write(b, off, (int) (splitLength - bytesWrittenForThisPart)); startNextSplitFile(); raf.write(b, off + (int) (splitLength - bytesWrittenForThisPart), (int) (len - (splitLength - bytesWrittenForThisPart))); bytesWrittenForThisPart = len - (splitLength - bytesWrittenForThisPart); } } else { raf.write(b, off, len); bytesWrittenForThisPart += len; } } private void startNextSplitFile() throws IOException { String zipFileWithoutExt = getZipFileNameWithoutExtension(zipFile.getName()); String zipFileName = zipFile.getAbsolutePath(); String parentPath = (zipFile.getParent() == null) ? "" : zipFile.getParent() + System.getProperty("file.separator"); String fileExtension = ".z0" + (currSplitFileCounter + 1); if (currSplitFileCounter >= 9) { fileExtension = ".z" + (currSplitFileCounter + 1); } File currSplitFile = new File(parentPath + zipFileWithoutExt + fileExtension); raf.close(); if (currSplitFile.exists()) { throw new IOException("split file: " + currSplitFile.getName() + " already exists in the current directory, cannot rename this file"); } if (!zipFile.renameTo(currSplitFile)) { throw new IOException("cannot rename newly created split file"); } zipFile = new File(zipFileName); raf = new RandomAccessFile(zipFile, RandomAccessFileMode.WRITE.getValue()); currSplitFileCounter++; } private boolean isHeaderData(byte[] buff) { int signature = rawIO.readIntLittleEndian(buff); for (HeaderSignature headerSignature : HeaderSignature.values()) { //Ignore split signature if (headerSignature != HeaderSignature.SPLIT_ZIP && headerSignature.getValue() == signature) { return true; } } return false; } /** * Checks if the buffer size is sufficient for the current split file. If not * a new split file will be started. * * @param bufferSize * @return true if a new split file was started else false * @throws ZipException */ public boolean checkBufferSizeAndStartNextSplitFile(int bufferSize) throws ZipException { if (bufferSize < 0) { throw new ZipException("negative buffersize for checkBufferSizeAndStartNextSplitFile"); } if (!isBufferSizeFitForCurrSplitFile(bufferSize)) { try { startNextSplitFile(); bytesWrittenForThisPart = 0; return true; } catch (IOException e) { throw new ZipException(e); } } return false; } /** * Checks if the given buffer size will be fit in the current split file. * If this output stream is a non-split file, then this method always returns true * * @param bufferSize * @return true if the buffer size is fit in the current split file or else false. */ private boolean isBufferSizeFitForCurrSplitFile(int bufferSize) { if (splitLength >= MIN_SPLIT_LENGTH) { return (bytesWrittenForThisPart + bufferSize <= splitLength); } else { //Non split zip -- return true return true; } } public void seek(long pos) throws IOException { raf.seek(pos); } public int skipBytes(int n) throws IOException { return raf.skipBytes(n); } public void close() throws IOException { raf.close(); } public long getFilePointer() throws IOException { return raf.getFilePointer(); } public boolean isSplitZipFile() { return splitLength != -1; } public long getSplitLength() { return splitLength; } public int getCurrentSplitFileCounter() { return currSplitFileCounter; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/outputstream/StoreOutputStream.java000066400000000000000000000003201357650161600302000ustar00rootroot00000000000000package net.lingala.zip4j.io.outputstream; class StoreOutputStream extends CompressedOutputStream { public StoreOutputStream(CipherOutputStream cipherOutputStream) { super(cipherOutputStream); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/outputstream/ZipEntryOutputStream.java000066400000000000000000000022751357650161600307030ustar00rootroot00000000000000package net.lingala.zip4j.io.outputstream; import java.io.IOException; import java.io.OutputStream; class ZipEntryOutputStream extends OutputStream { private long numberOfBytesWrittenForThisEntry = 0; private OutputStream outputStream; private boolean entryClosed; public ZipEntryOutputStream(OutputStream outputStream) { this.outputStream = outputStream; entryClosed = false; } @Override public void write(int b) throws IOException { write(new byte[] {(byte) b}); } @Override public void write(byte[] b) throws IOException { write(b, 0, b.length); } @Override public void write(byte[] b, int off, int len) throws IOException { if (entryClosed) { throw new IllegalStateException("ZipEntryOutputStream is closed"); } outputStream.write(b, off, len); numberOfBytesWrittenForThisEntry += len; } public void closeEntry() throws IOException { entryClosed = true; } public long getNumberOfBytesWrittenForThisEntry() { return numberOfBytesWrittenForThisEntry; } @Override public void close() throws IOException { // Do nothing // Do not close the outputstream yet. This will be closed by countingOutputStream } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/outputstream/ZipOutputStream.java000077500000000000000000000202121357650161600276530ustar00rootroot00000000000000package net.lingala.zip4j.io.outputstream; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.headers.FileHeaderFactory; import net.lingala.zip4j.headers.HeaderSignature; import net.lingala.zip4j.headers.HeaderWriter; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.LocalFileHeader; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.AesVersion; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.model.enums.EncryptionMethod; import net.lingala.zip4j.util.InternalZipConstants; import net.lingala.zip4j.util.RawIO; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; import java.util.zip.CRC32; public class ZipOutputStream extends OutputStream { private CountingOutputStream countingOutputStream; private char[] password; private ZipModel zipModel; private CompressedOutputStream compressedOutputStream; private FileHeader fileHeader; private LocalFileHeader localFileHeader; private FileHeaderFactory fileHeaderFactory = new FileHeaderFactory(); private HeaderWriter headerWriter = new HeaderWriter(); private CRC32 crc32 = new CRC32(); private RawIO rawIO = new RawIO(); private long uncompressedSizeForThisEntry = 0; private Charset charset; public ZipOutputStream(OutputStream outputStream) throws IOException { this(outputStream, null, InternalZipConstants.CHARSET_UTF_8); } public ZipOutputStream(OutputStream outputStream, Charset charset) throws IOException { this(outputStream, null, charset); } public ZipOutputStream(OutputStream outputStream, char[] password) throws IOException { this(outputStream, password, InternalZipConstants.CHARSET_UTF_8); } public ZipOutputStream(OutputStream outputStream, char[] password, Charset charset) throws IOException { this(outputStream, password, charset, new ZipModel()); } public ZipOutputStream(OutputStream outputStream, char[] password, Charset charset, ZipModel zipModel) throws IOException { if(charset == null) { charset = InternalZipConstants.CHARSET_UTF_8; } this.countingOutputStream = new CountingOutputStream(outputStream); this.password = password; this.charset = charset; this.zipModel = initializeZipModel(zipModel, countingOutputStream); writeSplitZipHeaderIfApplicable(); } public void putNextEntry(ZipParameters zipParameters) throws IOException { verifyZipParameters(zipParameters); initializeAndWriteFileHeader(zipParameters); //Initialisation of below compressedOutputStream should happen after writing local file header //because local header data should be written first and then the encryption header data //and below initialisation writes encryption header data compressedOutputStream = initializeCompressedOutputStream(zipParameters); } public void write(int b) throws IOException { write(new byte[] {(byte)b}); } public void write(byte[] b) throws IOException { write(b, 0, b.length); } public void write(byte[] b, int off, int len) throws IOException { crc32.update(b, off, len); compressedOutputStream.write(b, off, len); uncompressedSizeForThisEntry += len; } public FileHeader closeEntry() throws IOException { compressedOutputStream.closeEntry(); long compressedSize = compressedOutputStream.getCompressedSize(); fileHeader.setCompressedSize(compressedSize); localFileHeader.setCompressedSize(compressedSize); fileHeader.setUncompressedSize(uncompressedSizeForThisEntry); localFileHeader.setUncompressedSize(uncompressedSizeForThisEntry); if (writeCrc(fileHeader)) { fileHeader.setCrc(crc32.getValue()); localFileHeader.setCrc(crc32.getValue()); } zipModel.getLocalFileHeaders().add(localFileHeader); zipModel.getCentralDirectory().getFileHeaders().add(fileHeader); if (localFileHeader.isDataDescriptorExists()) { headerWriter.writeExtendedLocalHeader(localFileHeader, countingOutputStream); } reset(); return fileHeader; } @Override public void close() throws IOException { zipModel.getEndOfCentralDirectoryRecord().setOffsetOfStartOfCentralDirectory(countingOutputStream.getNumberOfBytesWritten()); headerWriter.finalizeZipFile(zipModel, countingOutputStream, charset); countingOutputStream.close(); } private ZipModel initializeZipModel(ZipModel zipModel, CountingOutputStream countingOutputStream) { if (zipModel == null) { zipModel = new ZipModel(); } if (countingOutputStream.isSplitZipFile()) { zipModel.setSplitArchive(true); zipModel.setSplitLength(countingOutputStream.getSplitLength()); } return zipModel; } private void initializeAndWriteFileHeader(ZipParameters zipParameters) throws IOException { fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, countingOutputStream.isSplitZipFile(), countingOutputStream.getCurrentSplitFileCounter(), charset); fileHeader.setOffsetLocalHeader(countingOutputStream.getOffsetForNextEntry()); localFileHeader = fileHeaderFactory.generateLocalFileHeader(fileHeader); headerWriter.writeLocalFileHeader(zipModel, localFileHeader, countingOutputStream, charset); } private void reset() throws IOException { uncompressedSizeForThisEntry = 0; crc32.reset(); compressedOutputStream.close(); } private void writeSplitZipHeaderIfApplicable() throws IOException { if (!countingOutputStream.isSplitZipFile()) { return; } rawIO.writeIntLittleEndian(countingOutputStream, (int) HeaderSignature.SPLIT_ZIP.getValue()); } private CompressedOutputStream initializeCompressedOutputStream(ZipParameters zipParameters) throws IOException { ZipEntryOutputStream zipEntryOutputStream = new ZipEntryOutputStream(countingOutputStream); CipherOutputStream cipherOutputStream = initializeCipherOutputStream(zipEntryOutputStream, zipParameters); return initializeCompressedOutputStream(cipherOutputStream, zipParameters); } private CipherOutputStream initializeCipherOutputStream(ZipEntryOutputStream zipEntryOutputStream, ZipParameters zipParameters) throws IOException { if (!zipParameters.isEncryptFiles()) { return new NoCipherOutputStream(zipEntryOutputStream, zipParameters, null); } if (password == null || password.length == 0) { throw new ZipException("password not set"); } if (zipParameters.getEncryptionMethod() == EncryptionMethod.AES) { return new AesCipherOutputStream(zipEntryOutputStream, zipParameters, password); } else if (zipParameters.getEncryptionMethod() == EncryptionMethod.ZIP_STANDARD) { return new ZipStandardCipherOutputStream(zipEntryOutputStream, zipParameters, password); } else { throw new ZipException("Invalid encryption method"); } } private CompressedOutputStream initializeCompressedOutputStream(CipherOutputStream cipherOutputStream, ZipParameters zipParameters) { if (zipParameters.getCompressionMethod() == CompressionMethod.DEFLATE) { return new DeflaterOutputStream(cipherOutputStream, zipParameters.getCompressionLevel()); } return new StoreOutputStream(cipherOutputStream); } private void verifyZipParameters(ZipParameters zipParameters) { if (zipParameters.getCompressionMethod() == CompressionMethod.STORE && zipParameters.getEntrySize() < 0 && !isEntryDirectory(zipParameters.getFileNameInZip()) && zipParameters.isWriteExtendedLocalFileHeader()) { throw new IllegalArgumentException("uncompressed size should be set for zip entries of compression type store"); } } private boolean writeCrc(FileHeader fileHeader) { boolean isAesEncrypted = fileHeader.isEncrypted() && fileHeader.getEncryptionMethod().equals(EncryptionMethod.AES); if (!isAesEncrypted) { return true; } return fileHeader.getAesExtraDataRecord().getAesVersion().equals(AesVersion.ONE); } private boolean isEntryDirectory(String entryName) { return entryName.endsWith("/") || entryName.endsWith("\\"); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/io/outputstream/ZipStandardCipherOutputStream.java000066400000000000000000000030321357650161600324650ustar00rootroot00000000000000package net.lingala.zip4j.io.outputstream; import net.lingala.zip4j.crypto.StandardEncrypter; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.util.Zip4jUtil; import java.io.IOException; import java.io.OutputStream; class ZipStandardCipherOutputStream extends CipherOutputStream { public ZipStandardCipherOutputStream(ZipEntryOutputStream outputStream, ZipParameters zipParameters, char[] password) throws IOException, ZipException { super(outputStream, zipParameters, password); } @Override protected StandardEncrypter initializeEncrypter(OutputStream outputStream, ZipParameters zipParameters, char[] password) throws IOException, ZipException { long key = getEncryptionKey(zipParameters); StandardEncrypter encrypter = new StandardEncrypter(password, key); writeHeaders(encrypter.getHeaderBytes()); return encrypter; } @Override public void write(int b) throws IOException { write(new byte[] {(byte) b}); } @Override public void write(byte[] b) throws IOException { write(b, 0, b.length); } @Override public void write(byte[] b, int off, int len) throws IOException { super.write(b, off, len); } private long getEncryptionKey(ZipParameters zipParameters) { if (zipParameters.isWriteExtendedLocalFileHeader()) { long dosTime = Zip4jUtil.javaToDosTime(zipParameters.getLastModifiedFileTime()); return (dosTime & 0x0000ffff) << 16; } return zipParameters.getEntryCRC(); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/000077500000000000000000000000001357650161600215665ustar00rootroot00000000000000zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/AESExtraDataRecord.java000077500000000000000000000042001357650161600257750ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.model; import net.lingala.zip4j.headers.HeaderSignature; import net.lingala.zip4j.model.enums.AesKeyStrength; import net.lingala.zip4j.model.enums.AesVersion; import net.lingala.zip4j.model.enums.CompressionMethod; public class AESExtraDataRecord extends ZipHeader { private int dataSize; private AesVersion aesVersion; private String vendorID; private AesKeyStrength aesKeyStrength; private CompressionMethod compressionMethod; public AESExtraDataRecord() { setSignature(HeaderSignature.AES_EXTRA_DATA_RECORD); dataSize = 7; aesVersion = AesVersion.TWO; vendorID = "AE"; aesKeyStrength = AesKeyStrength.KEY_STRENGTH_256; compressionMethod = CompressionMethod.DEFLATE; } public int getDataSize() { return dataSize; } public void setDataSize(int dataSize) { this.dataSize = dataSize; } public AesVersion getAesVersion() { return aesVersion; } public void setAesVersion(AesVersion aesVersion) { this.aesVersion = aesVersion; } public String getVendorID() { return vendorID; } public void setVendorID(String vendorID) { this.vendorID = vendorID; } public AesKeyStrength getAesKeyStrength() { return aesKeyStrength; } public void setAesKeyStrength(AesKeyStrength aesKeyStrength) { this.aesKeyStrength = aesKeyStrength; } public CompressionMethod getCompressionMethod() { return compressionMethod; } public void setCompressionMethod(CompressionMethod compressionMethod) { this.compressionMethod = compressionMethod; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/AbstractFileHeader.java000066400000000000000000000105071357650161600261100ustar00rootroot00000000000000package net.lingala.zip4j.model; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.model.enums.EncryptionMethod; import java.util.List; public abstract class AbstractFileHeader extends ZipHeader { private int versionNeededToExtract; private byte[] generalPurposeFlag; private CompressionMethod compressionMethod; private long lastModifiedTime; private long crc = 0; private byte[] crcRawData; private long compressedSize = 0; private long uncompressedSize = 0; private int fileNameLength; private int extraFieldLength; private String fileName; private boolean isEncrypted; private EncryptionMethod encryptionMethod = EncryptionMethod.NONE; private boolean dataDescriptorExists; private Zip64ExtendedInfo zip64ExtendedInfo; private AESExtraDataRecord aesExtraDataRecord; private boolean fileNameUTF8Encoded; private List extraDataRecords; private boolean isDirectory; public int getVersionNeededToExtract() { return versionNeededToExtract; } public void setVersionNeededToExtract(int versionNeededToExtract) { this.versionNeededToExtract = versionNeededToExtract; } public byte[] getGeneralPurposeFlag() { return generalPurposeFlag; } public void setGeneralPurposeFlag(byte[] generalPurposeFlag) { this.generalPurposeFlag = generalPurposeFlag; } public CompressionMethod getCompressionMethod() { return compressionMethod; } public void setCompressionMethod(CompressionMethod compressionMethod) { this.compressionMethod = compressionMethod; } public long getLastModifiedTime() { return lastModifiedTime; } public void setLastModifiedTime(long lastModifiedTime) { this.lastModifiedTime = lastModifiedTime; } public long getCrc() { return crc; } public void setCrc(long crc) { this.crc = crc; } public byte[] getCrcRawData() { return crcRawData; } public void setCrcRawData(byte[] crcRawData) { this.crcRawData = crcRawData; } public long getCompressedSize() { return compressedSize; } public void setCompressedSize(long compressedSize) { this.compressedSize = compressedSize; } public long getUncompressedSize() { return uncompressedSize; } public void setUncompressedSize(long uncompressedSize) { this.uncompressedSize = uncompressedSize; } public int getFileNameLength() { return fileNameLength; } public void setFileNameLength(int fileNameLength) { this.fileNameLength = fileNameLength; } public int getExtraFieldLength() { return extraFieldLength; } public void setExtraFieldLength(int extraFieldLength) { this.extraFieldLength = extraFieldLength; } public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } public boolean isEncrypted() { return isEncrypted; } public void setEncrypted(boolean encrypted) { isEncrypted = encrypted; } public EncryptionMethod getEncryptionMethod() { return encryptionMethod; } public void setEncryptionMethod(EncryptionMethod encryptionMethod) { this.encryptionMethod = encryptionMethod; } public boolean isDataDescriptorExists() { return dataDescriptorExists; } public void setDataDescriptorExists(boolean dataDescriptorExists) { this.dataDescriptorExists = dataDescriptorExists; } public Zip64ExtendedInfo getZip64ExtendedInfo() { return zip64ExtendedInfo; } public void setZip64ExtendedInfo(Zip64ExtendedInfo zip64ExtendedInfo) { this.zip64ExtendedInfo = zip64ExtendedInfo; } public AESExtraDataRecord getAesExtraDataRecord() { return aesExtraDataRecord; } public void setAesExtraDataRecord(AESExtraDataRecord aesExtraDataRecord) { this.aesExtraDataRecord = aesExtraDataRecord; } public boolean isFileNameUTF8Encoded() { return fileNameUTF8Encoded; } public void setFileNameUTF8Encoded(boolean fileNameUTF8Encoded) { this.fileNameUTF8Encoded = fileNameUTF8Encoded; } public List getExtraDataRecords() { return extraDataRecords; } public void setExtraDataRecords(List extraDataRecords) { this.extraDataRecords = extraDataRecords; } public boolean isDirectory() { return isDirectory; } public void setDirectory(boolean directory) { isDirectory = directory; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/ArchiveExtraDataRecord.java000077500000000000000000000022041357650161600267500ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.model; public class ArchiveExtraDataRecord extends ZipHeader { private int extraFieldLength; private String extraFieldData; public int getExtraFieldLength() { return extraFieldLength; } public void setExtraFieldLength(int extraFieldLength) { this.extraFieldLength = extraFieldLength; } public String getExtraFieldData() { return extraFieldData; } public void setExtraFieldData(String extraFieldData) { this.extraFieldData = extraFieldData; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/CentralDirectory.java000077500000000000000000000024021357650161600257070ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.model; import java.util.ArrayList; import java.util.List; public class CentralDirectory { private List fileHeaders = new ArrayList<>(); private DigitalSignature digitalSignature = new DigitalSignature(); public List getFileHeaders() { return fileHeaders; } public void setFileHeaders(List fileHeaders) { this.fileHeaders = fileHeaders; } public DigitalSignature getDigitalSignature() { return digitalSignature; } public void setDigitalSignature(DigitalSignature digitalSignature) { this.digitalSignature = digitalSignature; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/DataDescriptor.java000077500000000000000000000023151357650161600253450ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.model; public class DataDescriptor extends ZipHeader { private long crc; private long compressedSize; private long uncompressedSize; public long getCrc() { return crc; } public void setCrc(long crc) { this.crc = crc; } public long getCompressedSize() { return compressedSize; } public void setCompressedSize(long compressedSize) { this.compressedSize = compressedSize; } public long getUncompressedSize() { return uncompressedSize; } public void setUncompressedSize(long uncompressedSize) { this.uncompressedSize = uncompressedSize; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/DigitalSignature.java000077500000000000000000000021161357650161600256730ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.model; public class DigitalSignature extends ZipHeader { private int sizeOfData; private String signatureData; public int getSizeOfData() { return sizeOfData; } public void setSizeOfData(int sizeOfData) { this.sizeOfData = sizeOfData; } public String getSignatureData() { return signatureData; } public void setSignatureData(String signatureData) { this.signatureData = signatureData; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/EndOfCentralDirectoryRecord.java000077500000000000000000000056011357650161600277660ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.model; import net.lingala.zip4j.headers.HeaderSignature; public class EndOfCentralDirectoryRecord extends ZipHeader { private int numberOfThisDisk; private int numberOfThisDiskStartOfCentralDir; private int totalNumberOfEntriesInCentralDirectoryOnThisDisk; private int totalNumberOfEntriesInCentralDirectory; private int sizeOfCentralDirectory; private long offsetOfStartOfCentralDirectory; private String comment = ""; public EndOfCentralDirectoryRecord() { setSignature(HeaderSignature.END_OF_CENTRAL_DIRECTORY); } public int getNumberOfThisDisk() { return numberOfThisDisk; } public void setNumberOfThisDisk(int numberOfThisDisk) { this.numberOfThisDisk = numberOfThisDisk; } public int getNumberOfThisDiskStartOfCentralDir() { return numberOfThisDiskStartOfCentralDir; } public void setNumberOfThisDiskStartOfCentralDir(int numberOfThisDiskStartOfCentralDir) { this.numberOfThisDiskStartOfCentralDir = numberOfThisDiskStartOfCentralDir; } public int getTotalNumberOfEntriesInCentralDirectoryOnThisDisk() { return totalNumberOfEntriesInCentralDirectoryOnThisDisk; } public void setTotalNumberOfEntriesInCentralDirectoryOnThisDisk( int totalNumberOfEntriesInCentralDirectoryOnThisDisk) { this.totalNumberOfEntriesInCentralDirectoryOnThisDisk = totalNumberOfEntriesInCentralDirectoryOnThisDisk; } public int getTotalNumberOfEntriesInCentralDirectory() { return totalNumberOfEntriesInCentralDirectory; } public void setTotalNumberOfEntriesInCentralDirectory(int totNoOfEntrisInCentralDir) { this.totalNumberOfEntriesInCentralDirectory = totNoOfEntrisInCentralDir; } public int getSizeOfCentralDirectory() { return sizeOfCentralDirectory; } public void setSizeOfCentralDirectory(int sizeOfCentralDirectory) { this.sizeOfCentralDirectory = sizeOfCentralDirectory; } public long getOffsetOfStartOfCentralDirectory() { return offsetOfStartOfCentralDirectory; } public void setOffsetOfStartOfCentralDirectory(long offSetOfStartOfCentralDir) { this.offsetOfStartOfCentralDirectory = offSetOfStartOfCentralDir; } public String getComment() { return comment; } public void setComment(String comment) { if (comment != null) { this.comment = comment; } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/ExtraDataRecord.java000077500000000000000000000022471357650161600254550ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.model; public class ExtraDataRecord extends ZipHeader { private long header; private int sizeOfData; private byte[] data; public long getHeader() { return header; } public void setHeader(long header) { this.header = header; } public int getSizeOfData() { return sizeOfData; } public void setSizeOfData(int sizeOfData) { this.sizeOfData = sizeOfData; } public byte[] getData() { return data; } public void setData(byte[] data) { this.data = data; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/FileHeader.java000077500000000000000000000045131357650161600244270ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.model; import net.lingala.zip4j.headers.HeaderSignature; public class FileHeader extends AbstractFileHeader { private int versionMadeBy; private int fileCommentLength = 0; private int diskNumberStart; private byte[] internalFileAttributes; private byte[] externalFileAttributes; private long offsetLocalHeader; private String fileComment; public FileHeader() { setSignature(HeaderSignature.CENTRAL_DIRECTORY); } public int getVersionMadeBy() { return versionMadeBy; } public void setVersionMadeBy(int versionMadeBy) { this.versionMadeBy = versionMadeBy; } public int getFileCommentLength() { return fileCommentLength; } public void setFileCommentLength(int fileCommentLength) { this.fileCommentLength = fileCommentLength; } public int getDiskNumberStart() { return diskNumberStart; } public void setDiskNumberStart(int diskNumberStart) { this.diskNumberStart = diskNumberStart; } public byte[] getInternalFileAttributes() { return internalFileAttributes; } public void setInternalFileAttributes(byte[] internalFileAttributes) { this.internalFileAttributes = internalFileAttributes; } public byte[] getExternalFileAttributes() { return externalFileAttributes; } public void setExternalFileAttributes(byte[] externalFileAttributes) { this.externalFileAttributes = externalFileAttributes; } public long getOffsetLocalHeader() { return offsetLocalHeader; } public void setOffsetLocalHeader(long offsetLocalHeader) { this.offsetLocalHeader = offsetLocalHeader; } public String getFileComment() { return fileComment; } public void setFileComment(String fileComment) { this.fileComment = fileComment; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/LocalFileHeader.java000077500000000000000000000031071357650161600254000ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.model; import net.lingala.zip4j.headers.HeaderSignature; public class LocalFileHeader extends AbstractFileHeader { private byte[] extraField; private long offsetStartOfData; private boolean writeCompressedSizeInZip64ExtraRecord; public LocalFileHeader() { setSignature(HeaderSignature.LOCAL_FILE_HEADER); } public byte[] getExtraField() { return extraField; } public void setExtraField(byte[] extraField) { this.extraField = extraField; } public long getOffsetStartOfData() { return offsetStartOfData; } public void setOffsetStartOfData(long offsetStartOfData) { this.offsetStartOfData = offsetStartOfData; } public boolean isWriteCompressedSizeInZip64ExtraRecord() { return writeCompressedSizeInZip64ExtraRecord; } public void setWriteCompressedSizeInZip64ExtraRecord(boolean writeCompressedSizeInZip64ExtraRecord) { this.writeCompressedSizeInZip64ExtraRecord = writeCompressedSizeInZip64ExtraRecord; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/Zip64EndOfCentralDirectoryLocator.java000077500000000000000000000033121357650161600310050ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.model; public class Zip64EndOfCentralDirectoryLocator extends ZipHeader { private int numberOfDiskStartOfZip64EndOfCentralDirectoryRecord; private long offsetZip64EndOfCentralDirectoryRecord; private int totalNumberOfDiscs; public int getNumberOfDiskStartOfZip64EndOfCentralDirectoryRecord() { return numberOfDiskStartOfZip64EndOfCentralDirectoryRecord; } public void setNumberOfDiskStartOfZip64EndOfCentralDirectoryRecord( int noOfDiskStartOfZip64EndOfCentralDirRec) { this.numberOfDiskStartOfZip64EndOfCentralDirectoryRecord = noOfDiskStartOfZip64EndOfCentralDirRec; } public long getOffsetZip64EndOfCentralDirectoryRecord() { return offsetZip64EndOfCentralDirectoryRecord; } public void setOffsetZip64EndOfCentralDirectoryRecord(long offsetZip64EndOfCentralDirectoryRecord) { this.offsetZip64EndOfCentralDirectoryRecord = offsetZip64EndOfCentralDirectoryRecord; } public int getTotalNumberOfDiscs() { return totalNumberOfDiscs; } public void setTotalNumberOfDiscs(int totNumberOfDiscs) { this.totalNumberOfDiscs = totNumberOfDiscs; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/Zip64EndOfCentralDirectoryRecord.java000077500000000000000000000073771357650161600306370ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.model; public class Zip64EndOfCentralDirectoryRecord extends ZipHeader { private long sizeOfZip64EndCentralDirectoryRecord; private int versionMadeBy; private int versionNeededToExtract; private int numberOfThisDisk; private int numberOfThisDiskStartOfCentralDirectory; private long totalNumberOfEntriesInCentralDirectoryOnThisDisk; private long totalNumberOfEntriesInCentralDirectory; private long sizeOfCentralDirectory; private long offsetStartCentralDirectoryWRTStartDiskNumber; private byte[] extensibleDataSector; public long getSizeOfZip64EndCentralDirectoryRecord() { return sizeOfZip64EndCentralDirectoryRecord; } public void setSizeOfZip64EndCentralDirectoryRecord(long sizeOfZip64EndCentralDirectoryRecord) { this.sizeOfZip64EndCentralDirectoryRecord = sizeOfZip64EndCentralDirectoryRecord; } public int getVersionMadeBy() { return versionMadeBy; } public void setVersionMadeBy(int versionMadeBy) { this.versionMadeBy = versionMadeBy; } public int getVersionNeededToExtract() { return versionNeededToExtract; } public void setVersionNeededToExtract(int versionNeededToExtract) { this.versionNeededToExtract = versionNeededToExtract; } public int getNumberOfThisDisk() { return numberOfThisDisk; } public void setNumberOfThisDisk(int numberOfThisDisk) { this.numberOfThisDisk = numberOfThisDisk; } public int getNumberOfThisDiskStartOfCentralDirectory() { return numberOfThisDiskStartOfCentralDirectory; } public void setNumberOfThisDiskStartOfCentralDirectory(int numberOfThisDiskStartOfCentralDirectory) { this.numberOfThisDiskStartOfCentralDirectory = numberOfThisDiskStartOfCentralDirectory; } public long getTotalNumberOfEntriesInCentralDirectoryOnThisDisk() { return totalNumberOfEntriesInCentralDirectoryOnThisDisk; } public void setTotalNumberOfEntriesInCentralDirectoryOnThisDisk( long totalNumberOfEntriesInCentralDirectoryOnThisDisk) { this.totalNumberOfEntriesInCentralDirectoryOnThisDisk = totalNumberOfEntriesInCentralDirectoryOnThisDisk; } public long getTotalNumberOfEntriesInCentralDirectory() { return totalNumberOfEntriesInCentralDirectory; } public void setTotalNumberOfEntriesInCentralDirectory(long totalNumberOfEntriesInCentralDirectory) { this.totalNumberOfEntriesInCentralDirectory = totalNumberOfEntriesInCentralDirectory; } public long getSizeOfCentralDirectory() { return sizeOfCentralDirectory; } public void setSizeOfCentralDirectory(long sizeOfCentralDirectory) { this.sizeOfCentralDirectory = sizeOfCentralDirectory; } public long getOffsetStartCentralDirectoryWRTStartDiskNumber() { return offsetStartCentralDirectoryWRTStartDiskNumber; } public void setOffsetStartCentralDirectoryWRTStartDiskNumber( long offsetStartCentralDirectoryWRTStartDiskNumber) { this.offsetStartCentralDirectoryWRTStartDiskNumber = offsetStartCentralDirectoryWRTStartDiskNumber; } public byte[] getExtensibleDataSector() { return extensibleDataSector; } public void setExtensibleDataSector(byte[] extensibleDataSector) { this.extensibleDataSector = extensibleDataSector; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/Zip64ExtendedInfo.java000077500000000000000000000035311357650161600256470ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.model; public class Zip64ExtendedInfo extends ZipHeader { private int size; private long compressedSize; private long uncompressedSize; private long offsetLocalHeader; private int diskNumberStart; public Zip64ExtendedInfo() { compressedSize = -1; uncompressedSize = -1; offsetLocalHeader = -1; diskNumberStart = -1; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public long getCompressedSize() { return compressedSize; } public void setCompressedSize(long compressedSize) { this.compressedSize = compressedSize; } public long getUncompressedSize() { return uncompressedSize; } public void setUncompressedSize(long uncompressedSize) { this.uncompressedSize = uncompressedSize; } public long getOffsetLocalHeader() { return offsetLocalHeader; } public void setOffsetLocalHeader(long offsetLocalHeader) { this.offsetLocalHeader = offsetLocalHeader; } public int getDiskNumberStart() { return diskNumberStart; } public void setDiskNumberStart(int diskNumberStart) { this.diskNumberStart = diskNumberStart; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/ZipHeader.java000066400000000000000000000005011357650161600243000ustar00rootroot00000000000000package net.lingala.zip4j.model; import net.lingala.zip4j.headers.HeaderSignature; public abstract class ZipHeader { private HeaderSignature signature; public HeaderSignature getSignature() { return signature; } public void setSignature(HeaderSignature signature) { this.signature = signature; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/ZipModel.java000077500000000000000000000112641357650161600241630ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.model; import java.io.File; import java.util.ArrayList; import java.util.List; public class ZipModel implements Cloneable { private List localFileHeaders = new ArrayList<>(); private List dataDescriptors = new ArrayList<>(); private ArchiveExtraDataRecord archiveExtraDataRecord = new ArchiveExtraDataRecord(); private CentralDirectory centralDirectory = new CentralDirectory(); private EndOfCentralDirectoryRecord endOfCentralDirectoryRecord = new EndOfCentralDirectoryRecord(); private Zip64EndOfCentralDirectoryLocator zip64EndOfCentralDirectoryLocator = new Zip64EndOfCentralDirectoryLocator(); private Zip64EndOfCentralDirectoryRecord zip64EndOfCentralDirectoryRecord = new Zip64EndOfCentralDirectoryRecord(); private boolean splitArchive; private long splitLength; private File zipFile; private boolean isZip64Format = false; private boolean isNestedZipFile; private long start; private long end; public ZipModel() { splitLength = -1; } public List getLocalFileHeaders() { return localFileHeaders; } public void setLocalFileHeaders(List localFileHeaderList) { this.localFileHeaders = localFileHeaderList; } public List getDataDescriptors() { return dataDescriptors; } public void setDataDescriptors(List dataDescriptors) { this.dataDescriptors = dataDescriptors; } public CentralDirectory getCentralDirectory() { return centralDirectory; } public void setCentralDirectory(CentralDirectory centralDirectory) { this.centralDirectory = centralDirectory; } public EndOfCentralDirectoryRecord getEndOfCentralDirectoryRecord() { return endOfCentralDirectoryRecord; } public void setEndOfCentralDirectoryRecord(EndOfCentralDirectoryRecord endOfCentralDirectoryRecord) { this.endOfCentralDirectoryRecord = endOfCentralDirectoryRecord; } public ArchiveExtraDataRecord getArchiveExtraDataRecord() { return archiveExtraDataRecord; } public void setArchiveExtraDataRecord( ArchiveExtraDataRecord archiveExtraDataRecord) { this.archiveExtraDataRecord = archiveExtraDataRecord; } public boolean isSplitArchive() { return splitArchive; } public void setSplitArchive(boolean splitArchive) { this.splitArchive = splitArchive; } public File getZipFile() { return zipFile; } public void setZipFile(File zipFile) { this.zipFile = zipFile; } public Zip64EndOfCentralDirectoryLocator getZip64EndOfCentralDirectoryLocator() { return zip64EndOfCentralDirectoryLocator; } public void setZip64EndOfCentralDirectoryLocator( Zip64EndOfCentralDirectoryLocator zip64EndOfCentralDirectoryLocator) { this.zip64EndOfCentralDirectoryLocator = zip64EndOfCentralDirectoryLocator; } public Zip64EndOfCentralDirectoryRecord getZip64EndOfCentralDirectoryRecord() { return zip64EndOfCentralDirectoryRecord; } public void setZip64EndOfCentralDirectoryRecord( Zip64EndOfCentralDirectoryRecord zip64EndOfCentralDirectoryRecord) { this.zip64EndOfCentralDirectoryRecord = zip64EndOfCentralDirectoryRecord; } public boolean isZip64Format() { return isZip64Format; } public void setZip64Format(boolean isZip64Format) { this.isZip64Format = isZip64Format; } public boolean isNestedZipFile() { return isNestedZipFile; } public void setNestedZipFile(boolean isNestedZipFile) { this.isNestedZipFile = isNestedZipFile; } public long getStart() { return start; } public void setStart(long start) { this.start = start; } public long getEnd() { return end; } public void setEnd(long end) { this.end = end; } public long getSplitLength() { return splitLength; } public void setSplitLength(long splitLength) { this.splitLength = splitLength; } public Object clone() throws CloneNotSupportedException { return super.clone(); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/ZipParameters.java000077500000000000000000000143551357650161600252320ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.model; import net.lingala.zip4j.model.enums.AesKeyStrength; import net.lingala.zip4j.model.enums.AesVersion; import net.lingala.zip4j.model.enums.CompressionLevel; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.model.enums.EncryptionMethod; public class ZipParameters { private CompressionMethod compressionMethod = CompressionMethod.DEFLATE; private CompressionLevel compressionLevel = CompressionLevel.NORMAL; private boolean encryptFiles = false; private EncryptionMethod encryptionMethod = EncryptionMethod.NONE; private boolean readHiddenFiles = true; private boolean readHiddenFolders = true; private AesKeyStrength aesKeyStrength = AesKeyStrength.KEY_STRENGTH_256; private AesVersion aesVersion = AesVersion.TWO; private boolean includeRootFolder = true; private long entryCRC; private String defaultFolderPath; private String fileNameInZip; private long lastModifiedFileTime = System.currentTimeMillis(); private long entrySize = -1; private boolean writeExtendedLocalFileHeader = true; private boolean overrideExistingFilesInZip = true; private String rootFolderNameInZip; public ZipParameters() { } public ZipParameters(ZipParameters zipParameters) { this.compressionMethod = zipParameters.getCompressionMethod(); this.compressionLevel = zipParameters.getCompressionLevel(); this.encryptFiles = zipParameters.isEncryptFiles(); this.encryptionMethod = zipParameters.getEncryptionMethod(); this.readHiddenFiles = zipParameters.isReadHiddenFiles(); this.readHiddenFolders = zipParameters.isReadHiddenFolders(); this.aesKeyStrength = zipParameters.getAesKeyStrength(); this.aesVersion = zipParameters.getAesVersion(); this.includeRootFolder = zipParameters.isIncludeRootFolder(); this.entryCRC = zipParameters.getEntryCRC(); this.defaultFolderPath = zipParameters.getDefaultFolderPath(); this.fileNameInZip = zipParameters.getFileNameInZip(); this.lastModifiedFileTime = zipParameters.getLastModifiedFileTime(); this.entrySize = zipParameters.getEntrySize(); this.writeExtendedLocalFileHeader = zipParameters.isWriteExtendedLocalFileHeader(); this.overrideExistingFilesInZip = zipParameters.isOverrideExistingFilesInZip(); this.rootFolderNameInZip = zipParameters.getRootFolderNameInZip(); } public CompressionMethod getCompressionMethod() { return compressionMethod; } public void setCompressionMethod(CompressionMethod compressionMethod) { this.compressionMethod = compressionMethod; } public boolean isEncryptFiles() { return encryptFiles; } public void setEncryptFiles(boolean encryptFiles) { this.encryptFiles = encryptFiles; } public EncryptionMethod getEncryptionMethod() { return encryptionMethod; } public void setEncryptionMethod(EncryptionMethod encryptionMethod) { this.encryptionMethod = encryptionMethod; } public CompressionLevel getCompressionLevel() { return compressionLevel; } public void setCompressionLevel(CompressionLevel compressionLevel) { this.compressionLevel = compressionLevel; } public boolean isReadHiddenFiles() { return readHiddenFiles; } public void setReadHiddenFiles(boolean readHiddenFiles) { this.readHiddenFiles = readHiddenFiles; } public boolean isReadHiddenFolders() { return readHiddenFolders; } public void setReadHiddenFolders(boolean readHiddenFolders) { this.readHiddenFolders = readHiddenFolders; } public Object clone() throws CloneNotSupportedException { return super.clone(); } public AesKeyStrength getAesKeyStrength() { return aesKeyStrength; } public void setAesKeyStrength(AesKeyStrength aesKeyStrength) { this.aesKeyStrength = aesKeyStrength; } public AesVersion getAesVersion() { return aesVersion; } public void setAesVersion(AesVersion aesVersion) { this.aesVersion = aesVersion; } public boolean isIncludeRootFolder() { return includeRootFolder; } public void setIncludeRootFolder(boolean includeRootFolder) { this.includeRootFolder = includeRootFolder; } public long getEntryCRC() { return entryCRC; } public void setEntryCRC(long entryCRC) { this.entryCRC = entryCRC; } public String getDefaultFolderPath() { return defaultFolderPath; } public void setDefaultFolderPath(String defaultFolderPath) { this.defaultFolderPath = defaultFolderPath; } public String getFileNameInZip() { return fileNameInZip; } public void setFileNameInZip(String fileNameInZip) { this.fileNameInZip = fileNameInZip; } public long getLastModifiedFileTime() { return lastModifiedFileTime; } public void setLastModifiedFileTime(long lastModifiedFileTime) { if (lastModifiedFileTime <= 0) { return; } this.lastModifiedFileTime = lastModifiedFileTime; } public long getEntrySize() { return entrySize; } public void setEntrySize(long entrySize) { this.entrySize = entrySize; } public boolean isWriteExtendedLocalFileHeader() { return writeExtendedLocalFileHeader; } public void setWriteExtendedLocalFileHeader(boolean writeExtendedLocalFileHeader) { this.writeExtendedLocalFileHeader = writeExtendedLocalFileHeader; } public boolean isOverrideExistingFilesInZip() { return overrideExistingFilesInZip; } public void setOverrideExistingFilesInZip(boolean overrideExistingFilesInZip) { this.overrideExistingFilesInZip = overrideExistingFilesInZip; } public String getRootFolderNameInZip() { return rootFolderNameInZip; } public void setRootFolderNameInZip(String rootFolderNameInZip) { this.rootFolderNameInZip = rootFolderNameInZip; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/enums/000077500000000000000000000000001357650161600227155ustar00rootroot00000000000000zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/enums/AesKeyStrength.java000066400000000000000000000016711357650161600264650ustar00rootroot00000000000000package net.lingala.zip4j.model.enums; public enum AesKeyStrength { KEY_STRENGTH_128(1, 8, 16, 16), KEY_STRENGTH_192(2, 12, 24, 24), KEY_STRENGTH_256(3, 16, 32, 32); private int rawCode; private int saltLength; private int macLength; private int keyLength; AesKeyStrength(int rawCode, int saltLength, int macLength, int keyLength) { this.rawCode = rawCode; this.saltLength = saltLength; this.macLength = macLength; this.keyLength = keyLength; } public int getRawCode() { return rawCode; } public int getSaltLength() { return saltLength; } public int getMacLength() { return macLength; } public int getKeyLength() { return keyLength; } public static AesKeyStrength getAesKeyStrengthFromRawCode(int code) { for (AesKeyStrength aesKeyStrength : values()) { if (aesKeyStrength.getRawCode() == code) { return aesKeyStrength; } } return null; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/enums/AesVersion.java000066400000000000000000000010411357650161600256320ustar00rootroot00000000000000package net.lingala.zip4j.model.enums; public enum AesVersion { ONE(1), TWO(2); private int versionNumber; AesVersion(int versionNumber) { this.versionNumber = versionNumber; } public int getVersionNumber() { return versionNumber; } public static AesVersion getFromVersionNumber(int versionNumber) { for (AesVersion aesVersion : values()) { if (aesVersion.versionNumber == versionNumber) { return aesVersion; } } throw new IllegalArgumentException("Unsupported Aes version"); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/enums/CompressionLevel.java000066400000000000000000000004021357650161600270450ustar00rootroot00000000000000package net.lingala.zip4j.model.enums; public enum CompressionLevel { FASTEST(1), FAST(3), NORMAL(5), MAXIMUM(7); private int level; CompressionLevel(int level) { this.level = level; } public int getLevel() { return level; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/enums/CompressionMethod.java000066400000000000000000000012301357650161600272160ustar00rootroot00000000000000package net.lingala.zip4j.model.enums; import net.lingala.zip4j.exception.ZipException; public enum CompressionMethod { STORE(0), DEFLATE(8), AES_INTERNAL_ONLY(99); private int code; CompressionMethod(int code) { this.code = code; } public int getCode() { return code; } public static CompressionMethod getCompressionMethodFromCode(int code) throws ZipException { for (CompressionMethod compressionMethod : values()) { if (compressionMethod.getCode() == code) { return compressionMethod; } } throw new ZipException("Unknown compression method", ZipException.Type.UNKNOWN_COMPRESSION_METHOD); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/enums/EncryptionMethod.java000066400000000000000000000002101357650161600270440ustar00rootroot00000000000000package net.lingala.zip4j.model.enums; public enum EncryptionMethod { NONE, ZIP_STANDARD, ZIP_STANDARD_VARIANT_STRONG, AES } zip4j-2.2.8/src/main/java/net/lingala/zip4j/model/enums/RandomAccessFileMode.java000066400000000000000000000003731357650161600275320ustar00rootroot00000000000000package net.lingala.zip4j.model.enums; public enum RandomAccessFileMode { READ("r"), WRITE("rw"); private String value; RandomAccessFileMode(String value) { this.value = value; } public String getValue() { return value; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/progress/000077500000000000000000000000001357650161600223325ustar00rootroot00000000000000zip4j-2.2.8/src/main/java/net/lingala/zip4j/progress/ProgressMonitor.java000077500000000000000000000070731357650161600263630ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.progress; /** * If Zip4j is set to run in thread mode, this class helps retrieve current progress */ public class ProgressMonitor { public enum State { READY, BUSY } public enum Result { SUCCESS, WORK_IN_PROGRESS, ERROR, CANCELLED } public enum Task { NONE, ADD_ENTRY, REMOVE_ENTRY, CALCULATE_CRC, EXTRACT_ENTRY, MERGE_ZIP_FILES, SET_COMMENT} private State state; private long totalWork; private long workCompleted; private int percentDone; private Task currentTask; private String fileName; private Result result; private Exception exception; private boolean cancelAllTasks; private boolean pause; public ProgressMonitor() { reset(); } public void updateWorkCompleted(long workCompleted) { this.workCompleted += workCompleted; if (totalWork > 0) { percentDone = (int) ((this.workCompleted * 100 / totalWork)); if (percentDone > 100) { percentDone = 100; } } while (pause) { try { Thread.sleep(150); } catch (InterruptedException e) { //Do nothing } } } public void endProgressMonitor() { result = Result.SUCCESS; percentDone = 100; reset(); } public void endProgressMonitor(Exception e) { result = Result.ERROR; exception = e; reset(); } public void fullReset() { reset(); fileName = null; totalWork = 0; workCompleted = 0; percentDone = 0; } private void reset() { currentTask = Task.NONE; state = State.READY; } public State getState() { return state; } public void setState(State state) { this.state = state; } public long getTotalWork() { return totalWork; } public void setTotalWork(long totalWork) { this.totalWork = totalWork; } public long getWorkCompleted() { return workCompleted; } public int getPercentDone() { return percentDone; } public void setPercentDone(int percentDone) { this.percentDone = percentDone; } public Task getCurrentTask() { return currentTask; } public void setCurrentTask(Task currentTask) { this.currentTask = currentTask; } public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } public Result getResult() { return result; } public void setResult(Result result) { this.result = result; } public Exception getException() { return exception; } public void setException(Exception exception) { this.exception = exception; } public boolean isCancelAllTasks() { return cancelAllTasks; } public void setCancelAllTasks(boolean cancelAllTasks) { this.cancelAllTasks = cancelAllTasks; } public boolean isPause() { return pause; } public void setPause(boolean pause) { this.pause = pause; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/tasks/000077500000000000000000000000001357650161600216135ustar00rootroot00000000000000zip4j-2.2.8/src/main/java/net/lingala/zip4j/tasks/AbstractAddFileToZipTask.java000066400000000000000000000223431357650161600272470ustar00rootroot00000000000000package net.lingala.zip4j.tasks; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.headers.HeaderWriter; import net.lingala.zip4j.io.outputstream.SplitOutputStream; import net.lingala.zip4j.io.outputstream.ZipOutputStream; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.model.enums.EncryptionMethod; import net.lingala.zip4j.progress.ProgressMonitor; import net.lingala.zip4j.tasks.RemoveEntryFromZipFileTask.RemoveEntryFromZipFileTaskParameters; import net.lingala.zip4j.util.FileUtils; import net.lingala.zip4j.util.Zip4jUtil; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import static net.lingala.zip4j.headers.HeaderUtil.getFileHeader; import static net.lingala.zip4j.model.enums.CompressionMethod.DEFLATE; import static net.lingala.zip4j.model.enums.CompressionMethod.STORE; import static net.lingala.zip4j.model.enums.EncryptionMethod.NONE; import static net.lingala.zip4j.model.enums.EncryptionMethod.ZIP_STANDARD; import static net.lingala.zip4j.progress.ProgressMonitor.Task.ADD_ENTRY; import static net.lingala.zip4j.progress.ProgressMonitor.Task.CALCULATE_CRC; import static net.lingala.zip4j.progress.ProgressMonitor.Task.REMOVE_ENTRY; import static net.lingala.zip4j.util.CrcUtil.computeFileCrc; import static net.lingala.zip4j.util.FileUtils.getRelativeFileName; import static net.lingala.zip4j.util.InternalZipConstants.BUFF_SIZE; import static net.lingala.zip4j.util.Zip4jUtil.javaToDosTime; public abstract class AbstractAddFileToZipTask extends AsyncZipTask { private ZipModel zipModel; private char[] password; private HeaderWriter headerWriter; AbstractAddFileToZipTask(ProgressMonitor progressMonitor, boolean runInThread, ZipModel zipModel, char[] password, HeaderWriter headerWriter) { super(progressMonitor, runInThread); this.zipModel = zipModel; this.password = password; this.headerWriter = headerWriter; } void addFilesToZip(List filesToAdd, ProgressMonitor progressMonitor, ZipParameters zipParameters, Charset charset) throws IOException { List updatedFilesToAdd = removeFilesIfExists(filesToAdd, zipParameters, progressMonitor, charset); try (SplitOutputStream splitOutputStream = new SplitOutputStream(zipModel.getZipFile(), zipModel.getSplitLength()); ZipOutputStream zipOutputStream = initializeOutputStream(splitOutputStream, charset)) { byte[] readBuff = new byte[BUFF_SIZE]; int readLen = -1; for (File fileToAdd : updatedFilesToAdd) { verifyIfTaskIsCancelled(); ZipParameters clonedZipParameters = cloneAndAdjustZipParameters(zipParameters, fileToAdd, progressMonitor); progressMonitor.setFileName(fileToAdd.getAbsolutePath()); zipOutputStream.putNextEntry(clonedZipParameters); if (fileToAdd.isDirectory()) { zipOutputStream.closeEntry(); continue; } try (InputStream inputStream = new FileInputStream(fileToAdd)) { while ((readLen = inputStream.read(readBuff)) != -1) { zipOutputStream.write(readBuff, 0, readLen); progressMonitor.updateWorkCompleted(readLen); verifyIfTaskIsCancelled(); } } FileHeader fileHeader = zipOutputStream.closeEntry(); fileHeader.setExternalFileAttributes(FileUtils.getFileAttributes(fileToAdd)); updateLocalFileHeader(fileHeader, splitOutputStream); } } } long calculateWorkForFiles(List filesToAdd, ZipParameters zipParameters) throws ZipException { long totalWork = 0; for (File fileToAdd : filesToAdd) { if (!fileToAdd.exists()) { continue; } if (zipParameters.isEncryptFiles() && zipParameters.getEncryptionMethod() == EncryptionMethod.ZIP_STANDARD) { totalWork += (fileToAdd.length() * 2); // for CRC calculation } else { totalWork += fileToAdd.length(); } //If an entry already exists, we have to remove that entry first and then add content again. //In this case, add corresponding work String relativeFileName = getRelativeFileName(fileToAdd.getAbsolutePath(), zipParameters.getDefaultFolderPath(), zipParameters.getRootFolderNameInZip()); FileHeader fileHeader = getFileHeader(getZipModel(), relativeFileName); if (fileHeader != null) { totalWork += (getZipModel().getZipFile().length() - fileHeader.getCompressedSize()); } } return totalWork; } ZipOutputStream initializeOutputStream(SplitOutputStream splitOutputStream, Charset charset) throws IOException { if (zipModel.getZipFile().exists()) { if (zipModel.getEndOfCentralDirectoryRecord() == null) { throw new ZipException("invalid end of central directory record"); } splitOutputStream.seek(zipModel.getEndOfCentralDirectoryRecord().getOffsetOfStartOfCentralDirectory()); } return new ZipOutputStream(splitOutputStream, password, charset, zipModel); } void verifyZipParameters(ZipParameters parameters) throws ZipException { if (parameters == null) { throw new ZipException("cannot validate zip parameters"); } if (parameters.getCompressionMethod() != STORE && parameters.getCompressionMethod() != DEFLATE) { throw new ZipException("unsupported compression type"); } if (parameters.isEncryptFiles()) { if (parameters.getEncryptionMethod() == NONE) { throw new ZipException("Encryption method has to be set, when encrypt files flag is set"); } if (password == null || password.length <= 0) { throw new ZipException("input password is empty or null"); } } else { parameters.setEncryptionMethod(NONE); } } void updateLocalFileHeader(FileHeader fileHeader, SplitOutputStream splitOutputStream) throws IOException { headerWriter.updateLocalFileHeader(fileHeader, getZipModel(), splitOutputStream); } private ZipParameters cloneAndAdjustZipParameters(ZipParameters zipParameters, File fileToAdd, ProgressMonitor progressMonitor) throws IOException { ZipParameters clonedZipParameters = new ZipParameters(zipParameters); clonedZipParameters.setLastModifiedFileTime(javaToDosTime((fileToAdd.lastModified()))); if (fileToAdd.isDirectory()) { clonedZipParameters.setEntrySize(0); } else { clonedZipParameters.setEntrySize(fileToAdd.length()); } clonedZipParameters.setWriteExtendedLocalFileHeader(false); clonedZipParameters.setLastModifiedFileTime(fileToAdd.lastModified()); if (!Zip4jUtil.isStringNotNullAndNotEmpty(zipParameters.getFileNameInZip())) { String relativeFileName = getRelativeFileName(fileToAdd.getAbsolutePath(), zipParameters.getDefaultFolderPath(), zipParameters.getRootFolderNameInZip()); clonedZipParameters.setFileNameInZip(relativeFileName); } if (fileToAdd.isDirectory()) { clonedZipParameters.setCompressionMethod(CompressionMethod.STORE); clonedZipParameters.setEncryptionMethod(EncryptionMethod.NONE); clonedZipParameters.setEncryptFiles(false); } else { if (clonedZipParameters.isEncryptFiles() && clonedZipParameters.getEncryptionMethod() == ZIP_STANDARD) { progressMonitor.setCurrentTask(CALCULATE_CRC); clonedZipParameters.setEntryCRC(computeFileCrc(fileToAdd, progressMonitor)); progressMonitor.setCurrentTask(ADD_ENTRY); } if (fileToAdd.length() == 0) { clonedZipParameters.setCompressionMethod(CompressionMethod.STORE); } } return clonedZipParameters; } private List removeFilesIfExists(List files, ZipParameters zipParameters, ProgressMonitor progressMonitor, Charset charset) throws ZipException { List filesToAdd = new ArrayList<>(files); if (!zipModel.getZipFile().exists()) { return filesToAdd; } for (File file : files) { String fileName = getRelativeFileName(file.getAbsolutePath(), zipParameters.getDefaultFolderPath(), zipParameters.getRootFolderNameInZip()); FileHeader fileHeader = getFileHeader(zipModel, fileName); if (fileHeader != null) { if (zipParameters.isOverrideExistingFilesInZip()) { progressMonitor.setCurrentTask(REMOVE_ENTRY); removeFile(fileHeader, progressMonitor, charset); verifyIfTaskIsCancelled(); progressMonitor.setCurrentTask(ADD_ENTRY); } else { filesToAdd.remove(file); } } } return filesToAdd; } private void removeFile(FileHeader fileHeader, ProgressMonitor progressMonitor, Charset charset) throws ZipException { RemoveEntryFromZipFileTask removeEntryFromZipFileTask = new RemoveEntryFromZipFileTask(progressMonitor, false, zipModel); removeEntryFromZipFileTask.execute(new RemoveEntryFromZipFileTaskParameters(fileHeader, charset)); } @Override protected ProgressMonitor.Task getTask() { return ProgressMonitor.Task.ADD_ENTRY; } protected ZipModel getZipModel() { return zipModel; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/tasks/AbstractExtractFileTask.java000066400000000000000000000105401357650161600271770ustar00rootroot00000000000000package net.lingala.zip4j.tasks; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.io.inputstream.ZipInputStream; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.LocalFileHeader; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.progress.ProgressMonitor; import net.lingala.zip4j.util.UnzipUtil; import net.lingala.zip4j.util.Zip4jUtil; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.regex.Matcher; import static net.lingala.zip4j.util.InternalZipConstants.BUFF_SIZE; import static net.lingala.zip4j.util.InternalZipConstants.FILE_SEPARATOR; public abstract class AbstractExtractFileTask extends AsyncZipTask { private ZipModel zipModel; private byte[] buff = new byte[BUFF_SIZE]; public AbstractExtractFileTask(ProgressMonitor progressMonitor, boolean runInThread, ZipModel zipModel) { super(progressMonitor, runInThread); this.zipModel = zipModel; } protected void extractFile(ZipInputStream zipInputStream, FileHeader fileHeader, String outputPath, String newFileName, ProgressMonitor progressMonitor) throws IOException { if (!outputPath.endsWith(FILE_SEPARATOR)) { outputPath += FILE_SEPARATOR; } File outputFile = determineOutputFile(fileHeader, outputPath, newFileName); progressMonitor.setFileName(outputFile.getAbsolutePath()); // make sure no file is extracted outside of the target directory (a.k.a zip slip) if (!outputFile.getCanonicalPath().startsWith(new File(outputPath).getCanonicalPath())) { throw new ZipException("illegal file name that breaks out of the target directory: " + fileHeader.getFileName()); } verifyNextEntry(zipInputStream, fileHeader); if (fileHeader.isDirectory()) { if (!outputFile.exists()) { if (!outputFile.mkdirs()) { throw new ZipException("Could not create directory: " + outputFile); } } } else { checkOutputDirectoryStructure(outputFile); unzipFile(zipInputStream, fileHeader, outputFile, progressMonitor); } } private void unzipFile(ZipInputStream inputStream, FileHeader fileHeader, File outputFile, ProgressMonitor progressMonitor) throws IOException { int readLength; try (OutputStream outputStream = new FileOutputStream(outputFile)) { while ((readLength = inputStream.read(buff)) != -1) { outputStream.write(buff, 0, readLength); progressMonitor.updateWorkCompleted(readLength); verifyIfTaskIsCancelled(); } } catch (Exception e) { if (outputFile.exists()) { outputFile.delete(); } throw e; } UnzipUtil.applyFileAttributes(fileHeader, outputFile); } private void verifyNextEntry(ZipInputStream zipInputStream, FileHeader fileHeader) throws IOException { LocalFileHeader localFileHeader = zipInputStream.getNextEntry(fileHeader); if (localFileHeader == null) { throw new ZipException("Could not read corresponding local file header for file header: " + fileHeader.getFileName()); } if (!fileHeader.getFileName().equals(localFileHeader.getFileName())) { throw new ZipException("File header and local file header mismatch"); } } private void checkOutputDirectoryStructure(File outputFile) throws ZipException { if (!outputFile.getParentFile().exists() && !outputFile.getParentFile().mkdirs()) { throw new ZipException("Unable to create parent directories: " + outputFile.getParentFile()); } } private File determineOutputFile(FileHeader fileHeader, String outputPath, String newFileName) { String outputFileName; if (Zip4jUtil.isStringNotNullAndNotEmpty(newFileName)) { outputFileName = newFileName; } else { outputFileName = getFileNameWithSystemFileSeparators(fileHeader.getFileName()); // replace all slashes with file separator } return new File(outputPath + FILE_SEPARATOR + outputFileName); } private String getFileNameWithSystemFileSeparators(String fileNameToReplace) { return fileNameToReplace.replaceAll("[/\\\\]", Matcher.quoteReplacement(FILE_SEPARATOR)); } @Override protected ProgressMonitor.Task getTask() { return ProgressMonitor.Task.EXTRACT_ENTRY; } public ZipModel getZipModel() { return zipModel; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/tasks/AbstractZipTaskParameters.java000066400000000000000000000003601357650161600275520ustar00rootroot00000000000000package net.lingala.zip4j.tasks; import java.nio.charset.Charset; public abstract class AbstractZipTaskParameters { protected Charset charset; protected AbstractZipTaskParameters(Charset charset) { this.charset = charset; } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/tasks/AddFilesToZipTask.java000066400000000000000000000034221357650161600257430ustar00rootroot00000000000000package net.lingala.zip4j.tasks; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.headers.HeaderWriter; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.progress.ProgressMonitor; import net.lingala.zip4j.tasks.AddFilesToZipTask.AddFilesToZipTaskParameters; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.util.List; public class AddFilesToZipTask extends AbstractAddFileToZipTask { public AddFilesToZipTask(ProgressMonitor progressMonitor, boolean runInThread, ZipModel zipModel, char[] password, HeaderWriter headerWriter) { super(progressMonitor, runInThread, zipModel, password, headerWriter); } @Override protected void executeTask(AddFilesToZipTaskParameters taskParameters, ProgressMonitor progressMonitor) throws IOException { verifyZipParameters(taskParameters.zipParameters); addFilesToZip(taskParameters.filesToAdd, progressMonitor, taskParameters.zipParameters, taskParameters.charset); } @Override protected long calculateTotalWork(AddFilesToZipTaskParameters taskParameters) throws ZipException { return calculateWorkForFiles(taskParameters.filesToAdd, taskParameters.zipParameters); } @Override protected ProgressMonitor.Task getTask() { return super.getTask(); } public static class AddFilesToZipTaskParameters extends AbstractZipTaskParameters { private List filesToAdd; private ZipParameters zipParameters; public AddFilesToZipTaskParameters(List filesToAdd, ZipParameters zipParameters, Charset charset) { super(charset); this.filesToAdd = filesToAdd; this.zipParameters = zipParameters; } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/tasks/AddFolderToZipTask.java000066400000000000000000000062771357650161600261270ustar00rootroot00000000000000package net.lingala.zip4j.tasks; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.headers.HeaderWriter; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.progress.ProgressMonitor; import net.lingala.zip4j.tasks.AddFolderToZipTask.AddFolderToZipTaskParameters; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.util.List; import static net.lingala.zip4j.util.FileUtils.getFilesInDirectoryRecursive; public class AddFolderToZipTask extends AbstractAddFileToZipTask { public AddFolderToZipTask(ProgressMonitor progressMonitor, boolean runInThread, ZipModel zipModel, char[] password, HeaderWriter headerWriter) { super(progressMonitor, runInThread, zipModel, password, headerWriter); } @Override protected void executeTask(AddFolderToZipTaskParameters taskParameters, ProgressMonitor progressMonitor) throws IOException { List filesToAdd = getFilesToAdd(taskParameters); setDefaultFolderPath(taskParameters); addFilesToZip(filesToAdd, progressMonitor, taskParameters.zipParameters, taskParameters.charset); } @Override protected long calculateTotalWork(AddFolderToZipTaskParameters taskParameters) throws ZipException { List filesToAdd = getFilesInDirectoryRecursive(taskParameters.folderToAdd, taskParameters.zipParameters.isReadHiddenFiles(), taskParameters.zipParameters.isReadHiddenFolders()); if (taskParameters.zipParameters.isIncludeRootFolder()) { filesToAdd.add(taskParameters.folderToAdd); } return calculateWorkForFiles(filesToAdd, taskParameters.zipParameters); } private void setDefaultFolderPath(AddFolderToZipTaskParameters taskParameters) throws IOException { String rootFolderPath; File folderToAdd = taskParameters.folderToAdd; if (taskParameters.zipParameters.isIncludeRootFolder()) { File parentFile = folderToAdd.getCanonicalFile().getParentFile(); if (parentFile == null) { rootFolderPath = folderToAdd.getCanonicalPath(); } else { rootFolderPath = folderToAdd.getCanonicalFile().getParentFile().getCanonicalPath(); } } else { rootFolderPath = folderToAdd.getCanonicalPath(); } taskParameters.zipParameters.setDefaultFolderPath(rootFolderPath); } private List getFilesToAdd(AddFolderToZipTaskParameters taskParameters) throws ZipException { List filesToAdd = getFilesInDirectoryRecursive(taskParameters.folderToAdd, taskParameters.zipParameters.isReadHiddenFiles(), taskParameters.zipParameters.isReadHiddenFolders()); if (taskParameters.zipParameters.isIncludeRootFolder()) { filesToAdd.add(taskParameters.folderToAdd); } return filesToAdd; } public static class AddFolderToZipTaskParameters extends AbstractZipTaskParameters { private File folderToAdd; private ZipParameters zipParameters; public AddFolderToZipTaskParameters(File folderToAdd, ZipParameters zipParameters, Charset charset) { super(charset); this.folderToAdd = folderToAdd; this.zipParameters = zipParameters; } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/tasks/AddStreamToZipTask.java000066400000000000000000000105531357650161600261370ustar00rootroot00000000000000package net.lingala.zip4j.tasks; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.headers.HeaderUtil; import net.lingala.zip4j.headers.HeaderWriter; import net.lingala.zip4j.io.outputstream.SplitOutputStream; import net.lingala.zip4j.io.outputstream.ZipOutputStream; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.progress.ProgressMonitor; import net.lingala.zip4j.tasks.AddStreamToZipTask.AddStreamToZipTaskParameters; import net.lingala.zip4j.tasks.RemoveEntryFromZipFileTask.RemoveEntryFromZipFileTaskParameters; import net.lingala.zip4j.util.Zip4jUtil; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import static net.lingala.zip4j.util.InternalZipConstants.BUFF_SIZE; public class AddStreamToZipTask extends AbstractAddFileToZipTask { public AddStreamToZipTask(ProgressMonitor progressMonitor, boolean runInThread, ZipModel zipModel, char[] password, HeaderWriter headerWriter) { super(progressMonitor, runInThread, zipModel, password, headerWriter); } @Override protected void executeTask(AddStreamToZipTaskParameters taskParameters, ProgressMonitor progressMonitor) throws IOException { verifyZipParameters(taskParameters.zipParameters); if (!Zip4jUtil.isStringNotNullAndNotEmpty(taskParameters.zipParameters.getFileNameInZip())) { throw new ZipException("fileNameInZip has to be set in zipParameters when adding stream"); } removeFileIfExists(getZipModel(), taskParameters.charset, taskParameters.zipParameters.getFileNameInZip(), progressMonitor); // For streams, it is necessary to write extended local file header because of Zip standard encryption. // If we do not write extended local file header, zip standard encryption needs a crc upfront for key, // which cannot be calculated until we read the complete stream. If we use extended local file header, // last modified file time is used, or current system time if not available. taskParameters.zipParameters.setWriteExtendedLocalFileHeader(true); if (taskParameters.zipParameters.getCompressionMethod().equals(CompressionMethod.STORE)) { // Set some random value here. This will be updated again when closing entry taskParameters.zipParameters.setEntrySize(0); } try(SplitOutputStream splitOutputStream = new SplitOutputStream(getZipModel().getZipFile(), getZipModel().getSplitLength()); ZipOutputStream zipOutputStream = initializeOutputStream(splitOutputStream, taskParameters.charset)) { byte[] readBuff = new byte[BUFF_SIZE]; int readLen = -1; ZipParameters zipParameters = taskParameters.zipParameters; zipOutputStream.putNextEntry(zipParameters); if (!zipParameters.getFileNameInZip().endsWith("/") && !zipParameters.getFileNameInZip().endsWith("\\")) { while ((readLen = taskParameters.inputStream.read(readBuff)) != -1) { zipOutputStream.write(readBuff, 0, readLen); } } FileHeader fileHeader = zipOutputStream.closeEntry(); if (fileHeader.getCompressionMethod().equals(CompressionMethod.STORE)) { updateLocalFileHeader(fileHeader, splitOutputStream); } } } @Override protected long calculateTotalWork(AddStreamToZipTaskParameters taskParameters) { return 0; } private void removeFileIfExists(ZipModel zipModel, Charset charset, String fileNameInZip, ProgressMonitor progressMonitor) throws ZipException { FileHeader fileHeader = HeaderUtil.getFileHeader(zipModel, fileNameInZip); if (fileHeader != null) { RemoveEntryFromZipFileTask removeEntryFromZipFileTask = new RemoveEntryFromZipFileTask(progressMonitor, false, zipModel); removeEntryFromZipFileTask.execute(new RemoveEntryFromZipFileTaskParameters(fileHeader, charset)); } } public static class AddStreamToZipTaskParameters extends AbstractZipTaskParameters { private InputStream inputStream; private ZipParameters zipParameters; public AddStreamToZipTaskParameters(InputStream inputStream, ZipParameters zipParameters, Charset charset) { super(charset); this.inputStream = inputStream; this.zipParameters = zipParameters; } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/tasks/AsyncZipTask.java000066400000000000000000000042651357650161600250500ustar00rootroot00000000000000package net.lingala.zip4j.tasks; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.progress.ProgressMonitor; import java.io.IOException; import java.util.concurrent.Executors; public abstract class AsyncZipTask { private ProgressMonitor progressMonitor; private boolean runInThread; public AsyncZipTask(ProgressMonitor progressMonitor, boolean runInThread) { this.progressMonitor = progressMonitor; this.runInThread = runInThread; } public void execute(T taskParameters) throws ZipException { progressMonitor.fullReset(); progressMonitor.setState(ProgressMonitor.State.BUSY); progressMonitor.setCurrentTask(getTask()); if (runInThread) { long totalWorkToBeDone = calculateTotalWork(taskParameters); progressMonitor.setTotalWork(totalWorkToBeDone); Executors.newSingleThreadExecutor().execute(() -> { try { performTaskWithErrorHandling(taskParameters, progressMonitor); } catch (ZipException e) { //Do nothing. Exception will be passed through progress monitor } }); } else { performTaskWithErrorHandling(taskParameters, progressMonitor); } } private void performTaskWithErrorHandling(T taskParameters, ProgressMonitor progressMonitor) throws ZipException { try { executeTask(taskParameters, progressMonitor); progressMonitor.endProgressMonitor(); } catch (ZipException e) { progressMonitor.endProgressMonitor(e); throw e; } catch (Exception e) { progressMonitor.endProgressMonitor(e); throw new ZipException(e); } } protected void verifyIfTaskIsCancelled() throws ZipException { if (!progressMonitor.isCancelAllTasks()) { return; } progressMonitor.setResult(ProgressMonitor.Result.CANCELLED); progressMonitor.setState(ProgressMonitor.State.READY); throw new ZipException("Task cancelled", ZipException.Type.TASK_CANCELLED_EXCEPTION); } protected abstract void executeTask(T taskParameters, ProgressMonitor progressMonitor) throws IOException; protected abstract long calculateTotalWork(T taskParameters) throws ZipException; protected abstract ProgressMonitor.Task getTask(); } zip4j-2.2.8/src/main/java/net/lingala/zip4j/tasks/ExtractAllFilesTask.java000066400000000000000000000063231357650161600263330ustar00rootroot00000000000000package net.lingala.zip4j.tasks; import net.lingala.zip4j.io.inputstream.SplitInputStream; import net.lingala.zip4j.io.inputstream.ZipInputStream; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.progress.ProgressMonitor; import net.lingala.zip4j.tasks.ExtractAllFilesTask.ExtractAllFilesTaskParameters; import java.io.IOException; import java.nio.charset.Charset; public class ExtractAllFilesTask extends AbstractExtractFileTask { private char[] password; private SplitInputStream splitInputStream; public ExtractAllFilesTask(ProgressMonitor progressMonitor, boolean runInThread, ZipModel zipModel, char[] password) { super(progressMonitor, runInThread, zipModel); this.password = password; } @Override protected void executeTask(ExtractAllFilesTaskParameters taskParameters, ProgressMonitor progressMonitor) throws IOException { try (ZipInputStream zipInputStream = prepareZipInputStream(taskParameters.charset)) { for (FileHeader fileHeader : getZipModel().getCentralDirectory().getFileHeaders()) { if (fileHeader.getFileName().startsWith("__MACOSX")) { progressMonitor.updateWorkCompleted(fileHeader.getUncompressedSize()); continue; } splitInputStream.prepareExtractionForFileHeader(fileHeader); extractFile(zipInputStream, fileHeader, taskParameters.outputPath, null, progressMonitor); verifyIfTaskIsCancelled(); } } finally { if (splitInputStream != null) { splitInputStream.close(); } } } @Override protected long calculateTotalWork(ExtractAllFilesTaskParameters taskParameters) { long totalWork = 0; for (FileHeader fileHeader : getZipModel().getCentralDirectory().getFileHeaders()) { if (fileHeader.getZip64ExtendedInfo() != null && fileHeader.getZip64ExtendedInfo().getUncompressedSize() > 0) { totalWork += fileHeader.getZip64ExtendedInfo().getUncompressedSize(); } else { totalWork += fileHeader.getUncompressedSize(); } } return totalWork; } private ZipInputStream prepareZipInputStream(Charset charset) throws IOException { splitInputStream = new SplitInputStream(getZipModel().getZipFile(), getZipModel().isSplitArchive(), getZipModel().getEndOfCentralDirectoryRecord().getNumberOfThisDisk()); FileHeader fileHeader = getFirstFileHeader(getZipModel()); if (fileHeader != null) { splitInputStream.prepareExtractionForFileHeader(fileHeader); } return new ZipInputStream(splitInputStream, password, charset); } private FileHeader getFirstFileHeader(ZipModel zipModel) { if (zipModel.getCentralDirectory() == null || zipModel.getCentralDirectory().getFileHeaders() == null || zipModel.getCentralDirectory().getFileHeaders().size() == 0) { return null; } return zipModel.getCentralDirectory().getFileHeaders().get(0); } public static class ExtractAllFilesTaskParameters extends AbstractZipTaskParameters { private String outputPath; public ExtractAllFilesTaskParameters(String outputPath, Charset charset) { super(charset); this.outputPath = outputPath; } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/tasks/ExtractFileTask.java000066400000000000000000000044301357650161600255140ustar00rootroot00000000000000package net.lingala.zip4j.tasks; import net.lingala.zip4j.io.inputstream.SplitInputStream; import net.lingala.zip4j.io.inputstream.ZipInputStream; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.progress.ProgressMonitor; import net.lingala.zip4j.tasks.ExtractFileTask.ExtractFileTaskParameters; import java.io.IOException; import java.nio.charset.Charset; public class ExtractFileTask extends AbstractExtractFileTask { private char[] password; private SplitInputStream splitInputStream; public ExtractFileTask(ProgressMonitor progressMonitor, boolean runInThread, ZipModel zipModel, char[] password) { super(progressMonitor, runInThread, zipModel); this.password = password; } @Override protected void executeTask(ExtractFileTaskParameters taskParameters, ProgressMonitor progressMonitor) throws IOException { try(ZipInputStream zipInputStream = createZipInputStream(taskParameters.fileHeader, taskParameters.charset)) { extractFile(zipInputStream, taskParameters.fileHeader, taskParameters.outputPath, taskParameters.newFileName, progressMonitor); } finally { if (splitInputStream != null) { splitInputStream.close(); } } } @Override protected long calculateTotalWork(ExtractFileTaskParameters taskParameters) { return taskParameters.fileHeader.getUncompressedSize(); } protected ZipInputStream createZipInputStream(FileHeader fileHeader, Charset charset) throws IOException { splitInputStream = new SplitInputStream(getZipModel().getZipFile(), getZipModel().isSplitArchive(), getZipModel().getEndOfCentralDirectoryRecord().getNumberOfThisDisk()); splitInputStream.prepareExtractionForFileHeader(fileHeader); return new ZipInputStream(splitInputStream, password, charset); } public static class ExtractFileTaskParameters extends AbstractZipTaskParameters { private String outputPath; private FileHeader fileHeader; private String newFileName; public ExtractFileTaskParameters(String outputPath, FileHeader fileHeader, String newFileName, Charset charset) { super(charset); this.outputPath = outputPath; this.fileHeader = fileHeader; this.newFileName = newFileName; } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/tasks/MergeSplitZipFileTask.java000066400000000000000000000203071357650161600266410ustar00rootroot00000000000000package net.lingala.zip4j.tasks; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.headers.HeaderSignature; import net.lingala.zip4j.headers.HeaderWriter; import net.lingala.zip4j.model.EndOfCentralDirectoryRecord; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.Zip64EndOfCentralDirectoryLocator; import net.lingala.zip4j.model.Zip64EndOfCentralDirectoryRecord; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.model.enums.RandomAccessFileMode; import net.lingala.zip4j.progress.ProgressMonitor; import net.lingala.zip4j.tasks.MergeSplitZipFileTask.MergeSplitZipFileTaskParameters; import net.lingala.zip4j.util.RawIO; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.RandomAccessFile; import java.nio.charset.Charset; import java.util.List; import static net.lingala.zip4j.util.FileUtils.copyFile; public class MergeSplitZipFileTask extends AsyncZipTask { private ZipModel zipModel; private RawIO rawIO = new RawIO(); public MergeSplitZipFileTask(ProgressMonitor progressMonitor, boolean runInThread, ZipModel zipModel) { super(progressMonitor, runInThread); this.zipModel = zipModel; } @Override protected void executeTask(MergeSplitZipFileTaskParameters taskParameters, ProgressMonitor progressMonitor) throws IOException { if (!zipModel.isSplitArchive()) { ZipException e = new ZipException("archive not a split zip file"); progressMonitor.endProgressMonitor(e); throw e; } try (OutputStream outputStream = new FileOutputStream(taskParameters.outputZipFile)) { long totalBytesWritten = 0; int totalNumberOfSplitFiles = zipModel.getEndOfCentralDirectoryRecord().getNumberOfThisDisk(); if (totalNumberOfSplitFiles <= 0) { throw new ZipException("zip archive not a split zip file"); } int splitSignatureOverhead = 0; for (int i = 0; i <= totalNumberOfSplitFiles; i++) { try (RandomAccessFile randomAccessFile = createSplitZipFileStream(zipModel, i)) { int start = 0; long end = randomAccessFile.length(); if (i == 0) { if (rawIO.readIntLittleEndian(randomAccessFile) == HeaderSignature.SPLIT_ZIP.getValue()) { splitSignatureOverhead = 4; start = 4; } else { randomAccessFile.seek(0); } } if (i == totalNumberOfSplitFiles) { end = zipModel.getEndOfCentralDirectoryRecord().getOffsetOfStartOfCentralDirectory(); } copyFile(randomAccessFile, outputStream, start, end, progressMonitor); totalBytesWritten += (end - start); updateFileHeaderOffsetsForIndex(zipModel.getCentralDirectory().getFileHeaders(), i == 0 ? 0 : totalBytesWritten, i, splitSignatureOverhead); verifyIfTaskIsCancelled(); } } updateHeadersForMergeSplitFileAction(zipModel, totalBytesWritten, outputStream, taskParameters.charset); progressMonitor.endProgressMonitor(); } catch (CloneNotSupportedException e) { throw new ZipException(e); } } @Override protected long calculateTotalWork(MergeSplitZipFileTaskParameters taskParameters) { if (!zipModel.isSplitArchive()) { return 0; } long totalSize = 0; for (int i = 0; i <= zipModel.getEndOfCentralDirectoryRecord().getNumberOfThisDisk(); i++) { totalSize += getNextSplitZipFile(zipModel, i).length(); } return totalSize; } private void updateFileHeaderOffsetsForIndex(List fileHeaders, long offsetToAdd, int index, int splitSignatureOverhead) { for (FileHeader fileHeader : fileHeaders) { if (fileHeader.getDiskNumberStart() == index) { fileHeader.setOffsetLocalHeader(fileHeader.getOffsetLocalHeader() + offsetToAdd - splitSignatureOverhead); fileHeader.setDiskNumberStart(0); } } } private File getNextSplitZipFile(ZipModel zipModel, int partNumber) { if (partNumber == zipModel.getEndOfCentralDirectoryRecord().getNumberOfThisDisk()) { return zipModel.getZipFile(); } String splitZipExtension = ".z0"; if (partNumber >= 9) { splitZipExtension = ".z"; } String rootZipFile = zipModel.getZipFile().getPath(); String nextSplitZipFileName = zipModel.getZipFile().getPath().substring(0, rootZipFile.lastIndexOf(".")) + splitZipExtension + (partNumber + 1); return new File(nextSplitZipFileName); } private RandomAccessFile createSplitZipFileStream(ZipModel zipModel, int partNumber) throws FileNotFoundException { File splitFile = getNextSplitZipFile(zipModel, partNumber); return new RandomAccessFile(splitFile, RandomAccessFileMode.READ.getValue()); } private void updateHeadersForMergeSplitFileAction(ZipModel zipModel, long totalBytesWritten, OutputStream outputStream, Charset charset) throws IOException, CloneNotSupportedException { ZipModel newZipModel = (ZipModel) zipModel.clone(); newZipModel.getEndOfCentralDirectoryRecord().setOffsetOfStartOfCentralDirectory(totalBytesWritten); updateSplitZipModel(newZipModel, totalBytesWritten); HeaderWriter headerWriter = new HeaderWriter(); headerWriter.finalizeZipFileWithoutValidations(newZipModel, outputStream, charset); } private void updateSplitZipModel(ZipModel zipModel, long totalFileSize) { zipModel.setSplitArchive(false); updateSplitEndCentralDirectory(zipModel); if (zipModel.isZip64Format()) { updateSplitZip64EndCentralDirLocator(zipModel, totalFileSize); updateSplitZip64EndCentralDirRec(zipModel, totalFileSize); } } private void updateSplitEndCentralDirectory(ZipModel zipModel) { int numberOfFileHeaders = zipModel.getCentralDirectory().getFileHeaders().size(); EndOfCentralDirectoryRecord endOfCentralDirectoryRecord = zipModel.getEndOfCentralDirectoryRecord(); endOfCentralDirectoryRecord.setNumberOfThisDisk(0); endOfCentralDirectoryRecord.setNumberOfThisDiskStartOfCentralDir(0); endOfCentralDirectoryRecord.setTotalNumberOfEntriesInCentralDirectory(numberOfFileHeaders); endOfCentralDirectoryRecord.setTotalNumberOfEntriesInCentralDirectoryOnThisDisk(numberOfFileHeaders); } private void updateSplitZip64EndCentralDirLocator(ZipModel zipModel, long totalFileSize) { if (zipModel.getZip64EndOfCentralDirectoryLocator() == null) { return; } Zip64EndOfCentralDirectoryLocator zip64EndOfCentralDirectoryLocator = zipModel .getZip64EndOfCentralDirectoryLocator(); zip64EndOfCentralDirectoryLocator.setNumberOfDiskStartOfZip64EndOfCentralDirectoryRecord(0); zip64EndOfCentralDirectoryLocator.setOffsetZip64EndOfCentralDirectoryRecord( zip64EndOfCentralDirectoryLocator.getOffsetZip64EndOfCentralDirectoryRecord() + totalFileSize); zip64EndOfCentralDirectoryLocator.setTotalNumberOfDiscs(1); } private void updateSplitZip64EndCentralDirRec(ZipModel zipModel, long totalFileSize) { if (zipModel.getZip64EndOfCentralDirectoryRecord() == null) { return; } Zip64EndOfCentralDirectoryRecord zip64EndOfCentralDirectoryRecord = zipModel.getZip64EndOfCentralDirectoryRecord(); zip64EndOfCentralDirectoryRecord.setNumberOfThisDisk(0); zip64EndOfCentralDirectoryRecord.setNumberOfThisDiskStartOfCentralDirectory(0); zip64EndOfCentralDirectoryRecord.setTotalNumberOfEntriesInCentralDirectoryOnThisDisk( zipModel.getEndOfCentralDirectoryRecord().getTotalNumberOfEntriesInCentralDirectory()); zip64EndOfCentralDirectoryRecord.setOffsetStartCentralDirectoryWRTStartDiskNumber( zip64EndOfCentralDirectoryRecord.getOffsetStartCentralDirectoryWRTStartDiskNumber() + totalFileSize); } @Override protected ProgressMonitor.Task getTask() { return ProgressMonitor.Task.MERGE_ZIP_FILES; } public static class MergeSplitZipFileTaskParameters extends AbstractZipTaskParameters { private File outputZipFile; public MergeSplitZipFileTaskParameters(File outputZipFile, Charset charset) { super(charset); this.outputZipFile = outputZipFile; } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/tasks/RemoveEntryFromZipFileTask.java000066400000000000000000000205361357650161600276750ustar00rootroot00000000000000package net.lingala.zip4j.tasks; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.headers.HeaderWriter; import net.lingala.zip4j.io.outputstream.SplitOutputStream; import net.lingala.zip4j.model.EndOfCentralDirectoryRecord; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.model.enums.RandomAccessFileMode; import net.lingala.zip4j.progress.ProgressMonitor; import net.lingala.zip4j.tasks.RemoveEntryFromZipFileTask.RemoveEntryFromZipFileTaskParameters; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.charset.Charset; import java.util.List; import java.util.Random; import static net.lingala.zip4j.headers.HeaderUtil.getIndexOfFileHeader; import static net.lingala.zip4j.util.FileUtils.copyFile; public class RemoveEntryFromZipFileTask extends AsyncZipTask { private ZipModel zipModel; public RemoveEntryFromZipFileTask(ProgressMonitor progressMonitor, boolean runInThread, ZipModel zipModel) { super(progressMonitor, runInThread); this.zipModel = zipModel; } @Override protected void executeTask(RemoveEntryFromZipFileTaskParameters taskParameters, ProgressMonitor progressMonitor) throws IOException { if (zipModel.isSplitArchive()) { throw new ZipException("This is a split archive. Zip file format does not allow updating split/spanned files"); } File temporaryZipFile = getTemporaryFile(zipModel.getZipFile().getPath()); boolean successFlag = false; try (SplitOutputStream outputStream = new SplitOutputStream(temporaryZipFile); RandomAccessFile inputStream = new RandomAccessFile(zipModel.getZipFile(), RandomAccessFileMode.READ.getValue())){ int indexOfFileHeader = getIndexOfFileHeader(zipModel, taskParameters.fileHeader); long offsetLocalFileHeader = getOffsetLocalFileHeader(taskParameters.fileHeader); long offsetStartOfCentralDirectory = getOffsetOfStartOfCentralDirectory(zipModel); List fileHeaders = zipModel.getCentralDirectory().getFileHeaders(); long offsetEndOfCompressedData = getOffsetEndOfCompressedData(indexOfFileHeader, offsetStartOfCentralDirectory, fileHeaders); if (indexOfFileHeader == 0) { if (zipModel.getCentralDirectory().getFileHeaders().size() > 1) { // if this is the only file and it is deleted then no need to do this copyFile(inputStream, outputStream, offsetEndOfCompressedData + 1, offsetStartOfCentralDirectory, progressMonitor); } } else if (indexOfFileHeader == fileHeaders.size() - 1) { copyFile(inputStream, outputStream, 0, offsetLocalFileHeader, progressMonitor); } else { copyFile(inputStream, outputStream, 0, offsetLocalFileHeader, progressMonitor); copyFile(inputStream, outputStream, offsetEndOfCompressedData + 1, offsetStartOfCentralDirectory, progressMonitor); } verifyIfTaskIsCancelled(); updateHeaders(zipModel, outputStream, indexOfFileHeader, offsetEndOfCompressedData, offsetLocalFileHeader, taskParameters.charset); successFlag = true; } finally { cleanupFile(successFlag, zipModel.getZipFile(), temporaryZipFile); } } private long getOffsetOfStartOfCentralDirectory(ZipModel zipModel) { long offsetStartCentralDir = zipModel.getEndOfCentralDirectoryRecord().getOffsetOfStartOfCentralDirectory(); if (zipModel.isZip64Format() && zipModel.getZip64EndOfCentralDirectoryRecord() != null) { offsetStartCentralDir = zipModel.getZip64EndOfCentralDirectoryRecord() .getOffsetStartCentralDirectoryWRTStartDiskNumber(); } return offsetStartCentralDir; } private long getOffsetEndOfCompressedData(int indexOfFileHeader, long offsetStartOfCentralDirectory, List fileHeaders) { if (indexOfFileHeader == fileHeaders.size() - 1) { return offsetStartOfCentralDirectory - 1; } FileHeader nextFileHeader = fileHeaders.get(indexOfFileHeader + 1); long offsetEndOfCompressedFile = nextFileHeader.getOffsetLocalHeader() - 1; if (nextFileHeader.getZip64ExtendedInfo() != null && nextFileHeader.getZip64ExtendedInfo().getOffsetLocalHeader() != -1) { offsetEndOfCompressedFile = nextFileHeader.getZip64ExtendedInfo().getOffsetLocalHeader() - 1; } return offsetEndOfCompressedFile; } private File getTemporaryFile(String zipPathWithName) { Random random = new Random(); File tmpFile = new File(zipPathWithName + random.nextInt(10000)); while (tmpFile.exists()) { tmpFile = new File(zipPathWithName + random.nextInt(10000)); } return tmpFile; } private long getOffsetLocalFileHeader(FileHeader fileHeader) { long offsetLocalFileHeader = fileHeader.getOffsetLocalHeader(); if (fileHeader.getZip64ExtendedInfo() != null && fileHeader.getZip64ExtendedInfo().getOffsetLocalHeader() != -1) { offsetLocalFileHeader = fileHeader.getZip64ExtendedInfo().getOffsetLocalHeader(); } return offsetLocalFileHeader; } private void updateHeaders(ZipModel zipModel, SplitOutputStream splitOutputStream, int indexOfFileHeader, long offsetEndOfCompressedFile, long offsetLocalFileHeader, Charset charset) throws IOException { updateEndOfCentralDirectoryRecord(zipModel, splitOutputStream); zipModel.getCentralDirectory().getFileHeaders().remove(indexOfFileHeader); updateFileHeadersWithLocalHeaderOffsets(zipModel.getCentralDirectory().getFileHeaders(), offsetEndOfCompressedFile, offsetLocalFileHeader); HeaderWriter headerWriter = new HeaderWriter(); headerWriter.finalizeZipFile(zipModel, splitOutputStream, charset); } private void updateEndOfCentralDirectoryRecord(ZipModel zipModel, SplitOutputStream splitOutputStream) throws IOException { EndOfCentralDirectoryRecord endOfCentralDirectoryRecord = zipModel.getEndOfCentralDirectoryRecord(); endOfCentralDirectoryRecord.setOffsetOfStartOfCentralDirectory(splitOutputStream.getFilePointer()); endOfCentralDirectoryRecord.setTotalNumberOfEntriesInCentralDirectory( endOfCentralDirectoryRecord.getTotalNumberOfEntriesInCentralDirectory() - 1); endOfCentralDirectoryRecord.setTotalNumberOfEntriesInCentralDirectoryOnThisDisk( endOfCentralDirectoryRecord.getTotalNumberOfEntriesInCentralDirectoryOnThisDisk() - 1); zipModel.setEndOfCentralDirectoryRecord(endOfCentralDirectoryRecord); } private void updateFileHeadersWithLocalHeaderOffsets(List fileHeaders, long offsetEndOfCompressedFile, long offsetLocalFileHeader) { for (int i = 0; i < fileHeaders.size(); i ++) { FileHeader fileHeader = fileHeaders.get(i); long offsetLocalHdr = fileHeader.getOffsetLocalHeader(); if (fileHeader.getZip64ExtendedInfo() != null && fileHeader.getZip64ExtendedInfo().getOffsetLocalHeader() != -1) { offsetLocalHdr = fileHeader.getZip64ExtendedInfo().getOffsetLocalHeader(); } if (offsetLocalHdr > offsetLocalFileHeader) { fileHeader.setOffsetLocalHeader(offsetLocalHdr - (offsetEndOfCompressedFile - offsetLocalFileHeader) - 1); } } } private void cleanupFile(boolean successFlag, File zipFile, File temporaryZipFile) throws ZipException { if (successFlag) { restoreFileName(zipFile, temporaryZipFile); } else { temporaryZipFile.delete(); } } private void restoreFileName(File zipFile, File temporaryZipFile) throws ZipException { if (zipFile.delete()) { if (!temporaryZipFile.renameTo(zipFile)) { throw new ZipException("cannot rename modified zip file"); } } else { throw new ZipException("cannot delete old zip file"); } } @Override protected long calculateTotalWork(RemoveEntryFromZipFileTaskParameters taskParameters) { return zipModel.getZipFile().length() - taskParameters.fileHeader.getCompressedSize(); } @Override protected ProgressMonitor.Task getTask() { return ProgressMonitor.Task.REMOVE_ENTRY; } public static class RemoveEntryFromZipFileTaskParameters extends AbstractZipTaskParameters { private FileHeader fileHeader; public RemoveEntryFromZipFileTaskParameters(FileHeader fileHeader, Charset charset) { super(charset); this.fileHeader = fileHeader; } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/tasks/SetCommentTask.java000066400000000000000000000043041357650161600253600ustar00rootroot00000000000000package net.lingala.zip4j.tasks; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.headers.HeaderWriter; import net.lingala.zip4j.io.outputstream.SplitOutputStream; import net.lingala.zip4j.model.EndOfCentralDirectoryRecord; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.progress.ProgressMonitor; import net.lingala.zip4j.tasks.SetCommentTask.SetCommentTaskTaskParameters; import java.io.IOException; import java.nio.charset.Charset; public class SetCommentTask extends AsyncZipTask { private ZipModel zipModel; public SetCommentTask(ProgressMonitor progressMonitor, boolean runInThread, ZipModel zipModel) { super(progressMonitor, runInThread); this.zipModel = zipModel; } @Override protected void executeTask(SetCommentTaskTaskParameters taskParameters, ProgressMonitor progressMonitor) throws IOException { if (taskParameters.comment == null) { throw new ZipException("comment is null, cannot update Zip file with comment"); } EndOfCentralDirectoryRecord endOfCentralDirectoryRecord = zipModel.getEndOfCentralDirectoryRecord(); endOfCentralDirectoryRecord.setComment(taskParameters.comment); try (SplitOutputStream outputStream = new SplitOutputStream(zipModel.getZipFile())) { if (zipModel.isZip64Format()) { outputStream.seek(zipModel.getZip64EndOfCentralDirectoryRecord() .getOffsetStartCentralDirectoryWRTStartDiskNumber()); } else { outputStream.seek(endOfCentralDirectoryRecord.getOffsetOfStartOfCentralDirectory()); } HeaderWriter headerWriter = new HeaderWriter(); headerWriter.finalizeZipFileWithoutValidations(zipModel, outputStream, taskParameters.charset); } } @Override protected long calculateTotalWork(SetCommentTaskTaskParameters taskParameters) { return 0; } @Override protected ProgressMonitor.Task getTask() { return ProgressMonitor.Task.SET_COMMENT; } public static class SetCommentTaskTaskParameters extends AbstractZipTaskParameters { private String comment; public SetCommentTaskTaskParameters(String comment, Charset charset) { super(charset); this.comment = comment; } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/util/000077500000000000000000000000001357650161600214435ustar00rootroot00000000000000zip4j-2.2.8/src/main/java/net/lingala/zip4j/util/BitUtils.java000066400000000000000000000005101357650161600240410ustar00rootroot00000000000000package net.lingala.zip4j.util; public class BitUtils { public static boolean isBitSet(byte b, int pos) { return (b & (1L << pos)) != 0; } public static byte setBit(byte b, int pos) { return (byte) (b | 1 << pos); } public static byte unsetBit(byte b, int pos) { return (byte) (b & ~(1 << pos)); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/util/CrcUtil.java000077500000000000000000000035761357650161600236710ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.util; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.progress.ProgressMonitor; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.zip.CRC32; public class CrcUtil { private static final int BUF_SIZE = 1 << 14; //16384 public static long computeFileCrc(File inputFile, ProgressMonitor progressMonitor) throws IOException { if (inputFile == null || !inputFile.exists() || !inputFile.canRead()) { throw new ZipException("input file is null or does not exist or cannot read. " + "Cannot calculate CRC for the file"); } byte[] buff = new byte[BUF_SIZE]; CRC32 crc32 = new CRC32(); try(InputStream inputStream = new FileInputStream(inputFile)) { int readLen; while ((readLen = inputStream.read(buff)) != -1) { crc32.update(buff, 0, readLen); if (progressMonitor != null) { progressMonitor.updateWorkCompleted(readLen); if (progressMonitor.isCancelAllTasks()) { progressMonitor.setResult(ProgressMonitor.Result.CANCELLED); progressMonitor.setState(ProgressMonitor.State.READY); return 0; } } } return crc32.getValue(); } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/util/FileUtils.java000066400000000000000000000345621357650161600242200ustar00rootroot00000000000000package net.lingala.zip4j.util; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.progress.ProgressMonitor; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.RandomAccessFile; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.DosFileAttributeView; import java.nio.file.attribute.DosFileAttributes; import java.nio.file.attribute.FileTime; import java.nio.file.attribute.PosixFileAttributeView; import java.nio.file.attribute.PosixFilePermission; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import static java.nio.file.attribute.PosixFilePermission.GROUP_EXECUTE; import static java.nio.file.attribute.PosixFilePermission.GROUP_READ; import static java.nio.file.attribute.PosixFilePermission.GROUP_WRITE; import static java.nio.file.attribute.PosixFilePermission.OTHERS_EXECUTE; import static java.nio.file.attribute.PosixFilePermission.OTHERS_READ; import static java.nio.file.attribute.PosixFilePermission.OTHERS_WRITE; import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE; import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; import static net.lingala.zip4j.util.BitUtils.isBitSet; import static net.lingala.zip4j.util.InternalZipConstants.BUFF_SIZE; import static net.lingala.zip4j.util.InternalZipConstants.FILE_SEPARATOR; import static net.lingala.zip4j.util.InternalZipConstants.ZIP_FILE_SEPARATOR; import static net.lingala.zip4j.util.Zip4jUtil.isStringNotNullAndNotEmpty; public class FileUtils { public static void setFileAttributes(Path file, byte[] fileAttributes) { if (fileAttributes == null || fileAttributes.length == 0) { return; } String os = System.getProperty("os.name").toLowerCase(); if (isWindows(os)) { applyWindowsFileAttributes(file, fileAttributes); } else if (isMac(os) || isUnix(os)) { applyPosixFileAttributes(file, fileAttributes); } } public static void setFileLastModifiedTime(Path file, long lastModifiedTime) { if (lastModifiedTime <= 0 || !Files.exists(file)) { return; } try { Files.setLastModifiedTime(file, FileTime.fromMillis(Zip4jUtil.dosToJavaTme(lastModifiedTime))); } catch (Exception e) { // Ignore } } public static void setFileLastModifiedTimeWithoutNio(File file, long lastModifiedTime) { file.setLastModified(Zip4jUtil.dosToJavaTme(lastModifiedTime)); } public static byte[] getFileAttributes(File file) { try { if (file == null || !file.exists()) { return new byte[4]; } Path path = file.toPath(); String os = System.getProperty("os.name").toLowerCase(); if (isWindows(os)) { return getWindowsFileAttributes(path); } else if (isMac(os) || isUnix(os)) { return getPosixFileAttributes(path); } else { return new byte[4]; } } catch (NoSuchMethodError e) { return new byte[4]; } } public static List getFilesInDirectoryRecursive(File path, boolean readHiddenFiles, boolean readHiddenFolders) throws ZipException { if (path == null) { throw new ZipException("input path is null, cannot read files in the directory"); } List result = new ArrayList<>(); File[] filesAndDirs = path.listFiles(); if (!path.isDirectory() || !path.canRead() || filesAndDirs == null) { return result; } for (File file : filesAndDirs) { if (file.isHidden()) { if (file.isDirectory()) { if (!readHiddenFolders) { continue; } } else if (!readHiddenFiles) { continue; } } result.add(file); if (file.isDirectory()) { result.addAll(getFilesInDirectoryRecursive(file, readHiddenFiles, readHiddenFolders)); } } return result; } public static String getZipFileNameWithoutExtension(String zipFile) throws ZipException { if (!isStringNotNullAndNotEmpty(zipFile)) { throw new ZipException("zip file name is empty or null, cannot determine zip file name"); } String tmpFileName = zipFile; if (zipFile.contains(System.getProperty("file.separator"))) { tmpFileName = zipFile.substring(zipFile.lastIndexOf(System.getProperty("file.separator")) + 1); } if (tmpFileName.endsWith(".zip")) { tmpFileName = tmpFileName.substring(0, tmpFileName.lastIndexOf(".")); } return tmpFileName; } public static List getSplitZipFiles(ZipModel zipModel) throws ZipException { if (zipModel == null) { throw new ZipException("cannot get split zip files: zipmodel is null"); } if (zipModel.getEndOfCentralDirectoryRecord() == null) { return null; } if (!zipModel.getZipFile().exists()) { throw new ZipException("zip file does not exist"); } List splitZipFiles = new ArrayList<>(); File currZipFile = zipModel.getZipFile(); String partFile; if (!zipModel.isSplitArchive()) { splitZipFiles.add(currZipFile); return splitZipFiles; } int numberOfThisDisk = zipModel.getEndOfCentralDirectoryRecord().getNumberOfThisDisk(); if (numberOfThisDisk == 0) { splitZipFiles.add(currZipFile); return splitZipFiles; } else { for (int i = 0; i <= numberOfThisDisk; i++) { if (i == numberOfThisDisk) { splitZipFiles.add(zipModel.getZipFile()); } else { String fileExt = ".z0"; if (i >= 9) { fileExt = ".z"; } partFile = (currZipFile.getName().contains(".")) ? currZipFile.getPath().substring(0, currZipFile.getPath().lastIndexOf(".")) : currZipFile.getPath(); partFile = partFile + fileExt + (i + 1); splitZipFiles.add(new File(partFile)); } } } return splitZipFiles; } public static String getRelativeFileName(String file, String rootFolderPath, String rootFolderNameInZip) throws ZipException { String fileName; try { String fileCanonicalPath = new File(file).getCanonicalPath(); if (isStringNotNullAndNotEmpty(rootFolderPath)) { File rootFolderFile = new File(rootFolderPath); String rootFolderFileRef = rootFolderFile.getCanonicalPath(); if (!rootFolderFileRef.endsWith(FILE_SEPARATOR)) { rootFolderFileRef += FILE_SEPARATOR; } String tmpFileName = fileCanonicalPath.substring(rootFolderFileRef.length()); if (tmpFileName.startsWith(System.getProperty("file.separator"))) { tmpFileName = tmpFileName.substring(1); } File tmpFile = new File(fileCanonicalPath); if (tmpFile.isDirectory()) { tmpFileName = tmpFileName.replaceAll("\\\\", "/"); tmpFileName += ZIP_FILE_SEPARATOR; } else { String bkFileName = tmpFileName.substring(0, tmpFileName.lastIndexOf(tmpFile.getName())); bkFileName = bkFileName.replaceAll("\\\\", "/"); tmpFileName = bkFileName + tmpFile.getName(); } fileName = tmpFileName; } else { File relFile = new File(fileCanonicalPath); if (relFile.isDirectory()) { fileName = relFile.getName() + ZIP_FILE_SEPARATOR; } else { fileName = relFile.getName(); } } } catch (IOException e) { throw new ZipException(e); } if (Zip4jUtil.isStringNotNullAndNotEmpty(rootFolderNameInZip)) { if (!rootFolderNameInZip.endsWith("\\") && !rootFolderNameInZip.endsWith("/")) { rootFolderNameInZip = rootFolderNameInZip + InternalZipConstants.FILE_SEPARATOR; } rootFolderNameInZip = rootFolderNameInZip.replaceAll("\\\\", "/"); fileName = rootFolderNameInZip + fileName; } return fileName; } public static boolean isZipEntryDirectory(String fileNameInZip) { return fileNameInZip.endsWith("/") || fileNameInZip.endsWith("\\"); } public static void copyFile(RandomAccessFile randomAccessFile, OutputStream outputStream, long start, long end, ProgressMonitor progressMonitor) throws ZipException { if (start < 0 || end < 0 || start > end) { throw new ZipException("invalid offsets"); } if (start == end) { return; } try { randomAccessFile.seek(start); int readLen; byte[] buff; long bytesRead = 0; long bytesToRead = end - start; if ((end - start) < BUFF_SIZE) { buff = new byte[(int) bytesToRead]; } else { buff = new byte[BUFF_SIZE]; } while ((readLen = randomAccessFile.read(buff)) != -1) { outputStream.write(buff, 0, readLen); progressMonitor.updateWorkCompleted(readLen); if (progressMonitor.isCancelAllTasks()) { progressMonitor.setResult(ProgressMonitor.Result.CANCELLED); return; } bytesRead += readLen; if (bytesRead == bytesToRead) { break; } else if (bytesRead + buff.length > bytesToRead) { buff = new byte[(int) (bytesToRead - bytesRead)]; } } } catch (IOException e) { throw new ZipException(e); } } public static void assertFilesExist(List files) throws ZipException { for (File file : files) { if (!file.exists()) { throw new ZipException("File does not exist: " + file); } } } private static void applyWindowsFileAttributes(Path file, byte[] fileAttributes) { if (fileAttributes[0] == 0) { // No file attributes defined in the archive return; } DosFileAttributeView fileAttributeView = Files.getFileAttributeView(file, DosFileAttributeView.class); try { fileAttributeView.setReadOnly(isBitSet(fileAttributes[0], 0)); fileAttributeView.setHidden(isBitSet(fileAttributes[0], 1)); fileAttributeView.setSystem(isBitSet(fileAttributes[0], 2)); fileAttributeView.setArchive(isBitSet(fileAttributes[0], 5)); } catch (IOException e) { //Ignore } } private static void applyPosixFileAttributes(Path file, byte[] fileAttributes) { if (fileAttributes[2] == 0 && fileAttributes[3] == 0) { // No file attributes defined return; } try { Set posixFilePermissions = new HashSet<>(); addIfBitSet(fileAttributes[3], 0, posixFilePermissions, PosixFilePermission.OWNER_READ); addIfBitSet(fileAttributes[2], 7, posixFilePermissions, PosixFilePermission.OWNER_WRITE); addIfBitSet(fileAttributes[2], 6, posixFilePermissions, PosixFilePermission.OWNER_EXECUTE); addIfBitSet(fileAttributes[2], 5, posixFilePermissions, PosixFilePermission.GROUP_READ); addIfBitSet(fileAttributes[2], 4, posixFilePermissions, PosixFilePermission.GROUP_WRITE); addIfBitSet(fileAttributes[2], 3, posixFilePermissions, PosixFilePermission.GROUP_EXECUTE); addIfBitSet(fileAttributes[2], 2, posixFilePermissions, PosixFilePermission.OTHERS_READ); addIfBitSet(fileAttributes[2], 1, posixFilePermissions, PosixFilePermission.OTHERS_WRITE); addIfBitSet(fileAttributes[2], 0, posixFilePermissions, PosixFilePermission.OTHERS_EXECUTE); Files.setPosixFilePermissions(file, posixFilePermissions); } catch (IOException e) { // Ignore } } private static byte[] getWindowsFileAttributes(Path file) { byte[] fileAttributes = new byte[4]; try { DosFileAttributeView dosFileAttributeView = Files.getFileAttributeView(file, DosFileAttributeView.class); DosFileAttributes dosFileAttributes = dosFileAttributeView.readAttributes(); byte windowsAttribute = 0; windowsAttribute = setBitIfApplicable(dosFileAttributes.isReadOnly(), windowsAttribute, 0); windowsAttribute = setBitIfApplicable(dosFileAttributes.isHidden(), windowsAttribute, 1); windowsAttribute = setBitIfApplicable(dosFileAttributes.isSystem(), windowsAttribute, 2); windowsAttribute = setBitIfApplicable(dosFileAttributes.isArchive(), windowsAttribute, 5); fileAttributes[0] = windowsAttribute; } catch (IOException e) { // ignore } return fileAttributes; } private static byte[] getPosixFileAttributes(Path file) { byte[] fileAttributes = new byte[4]; try { PosixFileAttributeView posixFileAttributeView = Files.getFileAttributeView(file, PosixFileAttributeView.class); Set posixFilePermissions = posixFileAttributeView.readAttributes().permissions(); fileAttributes[3] = setBitIfApplicable(Files.isRegularFile(file), fileAttributes[3], 7); fileAttributes[3] = setBitIfApplicable(Files.isDirectory(file), fileAttributes[3], 6); fileAttributes[3] = setBitIfApplicable(posixFilePermissions.contains(OWNER_READ), fileAttributes[3], 0); fileAttributes[2] = setBitIfApplicable(posixFilePermissions.contains(OWNER_WRITE), fileAttributes[2], 7); fileAttributes[2] = setBitIfApplicable(posixFilePermissions.contains(OWNER_EXECUTE), fileAttributes[2], 6); fileAttributes[2] = setBitIfApplicable(posixFilePermissions.contains(GROUP_READ), fileAttributes[2], 5); fileAttributes[2] = setBitIfApplicable(posixFilePermissions.contains(GROUP_WRITE), fileAttributes[2], 4); fileAttributes[2] = setBitIfApplicable(posixFilePermissions.contains(GROUP_EXECUTE), fileAttributes[2], 3); fileAttributes[2] = setBitIfApplicable(posixFilePermissions.contains(OTHERS_READ), fileAttributes[2], 2); fileAttributes[2] = setBitIfApplicable(posixFilePermissions.contains(OTHERS_WRITE), fileAttributes[2], 1); fileAttributes[2] = setBitIfApplicable(posixFilePermissions.contains(OTHERS_EXECUTE), fileAttributes[2], 0); } catch (IOException e) { // Ignore } return fileAttributes; } private static byte setBitIfApplicable(boolean applicable, byte b, int pos) { if (applicable) { b = BitUtils.setBit(b, pos); } return b; } private static void addIfBitSet(byte b, int pos, Set posixFilePermissions, PosixFilePermission posixFilePermissionToAdd) { if (isBitSet(b, pos)) { posixFilePermissions.add(posixFilePermissionToAdd); } } private static boolean isWindows(String os) { return (os.contains("win")); } private static boolean isMac(String os) { return (os.contains("mac")); } private static boolean isUnix(String os) { return (os.contains("nux")); } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/util/InternalZipConstants.java000077500000000000000000000035551357650161600264550ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.util; import java.nio.charset.Charset; public final class InternalZipConstants { private InternalZipConstants() { } public static final int ENDHDR = 22; // END header size public static final int STD_DEC_HDR_SIZE = 12; //AES Constants public static final int AES_AUTH_LENGTH = 10; public static final int AES_BLOCK_SIZE = 16; public static final int MIN_SPLIT_LENGTH = 65536; public static final long ZIP_64_SIZE_LIMIT = 4294967295L; public static final int ZIP_64_NUMBER_OF_ENTRIES_LIMIT = 65535; public static final int BUFF_SIZE = 1024 * 4; // Update local file header constants // This value holds the number of bytes to skip from // the offset of start of local header public static final int UPDATE_LFH_CRC = 14; public static final int UPDATE_LFH_COMP_SIZE = 18; public static final int UPDATE_LFH_UNCOMP_SIZE = 22; public static final String ZIP_STANDARD_CHARSET = "Cp437"; public static final String FILE_SEPARATOR = System.getProperty("file.separator"); public static final String ZIP_FILE_SEPARATOR = "/"; public static final int MAX_ALLOWED_ZIP_COMMENT_LENGTH = 0xFFFF; public static final Charset CHARSET_UTF_8 = Charset.forName("UTF-8"); } zip4j-2.2.8/src/main/java/net/lingala/zip4j/util/RawIO.java000077500000000000000000000122451357650161600232760ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.util; import net.lingala.zip4j.exception.ZipException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; public class RawIO { private byte[] shortBuff = new byte[2]; private byte[] intBuff = new byte[4]; private byte[] longBuff = new byte[8]; public long readLongLittleEndian(RandomAccessFile randomAccessFile) throws IOException { randomAccessFile.readFully(longBuff); return readLongLittleEndian(longBuff, 0); } public long readLongLittleEndian(RandomAccessFile randomAccessFile, int readLen) throws IOException { resetBytes(longBuff); randomAccessFile.readFully(longBuff, 0, readLen); return readLongLittleEndian(longBuff, 0); } public long readLongLittleEndian(InputStream inputStream) throws IOException { readFully(inputStream, longBuff, longBuff.length); return readLongLittleEndian(longBuff, 0); } public long readLongLittleEndian(InputStream inputStream, int readLen) throws IOException { resetBytes(longBuff); readFully(inputStream, longBuff, readLen); return readLongLittleEndian(longBuff, 0); } public long readLongLittleEndian(byte[] array, int pos) { if (array.length < 8) { resetBytes(longBuff); } System.arraycopy(array, pos, longBuff, 0, array.length < 8 ? array.length : 8); long temp = 0; temp |= longBuff[7] & 0xff; temp <<= 8; temp |= longBuff[6] & 0xff; temp <<= 8; temp |= longBuff[5] & 0xff; temp <<= 8; temp |= longBuff[4] & 0xff; temp <<= 8; temp |= longBuff[3] & 0xff; temp <<= 8; temp |= longBuff[2] & 0xff; temp <<= 8; temp |= longBuff[1] & 0xff; temp <<= 8; temp |= longBuff[0] & 0xff; return temp; } public int readIntLittleEndian(RandomAccessFile randomAccessFile) throws IOException { randomAccessFile.readFully(intBuff); return readIntLittleEndian(intBuff); } public int readIntLittleEndian(InputStream inputStream) throws IOException { readFully(inputStream, intBuff, 4); return readIntLittleEndian(intBuff); } public int readIntLittleEndian(byte[] b) { return readIntLittleEndian(b, 0); } public int readIntLittleEndian(byte[] b, int pos) { return ((b[pos] & 0xff) | (b[1 + pos] & 0xff) << 8) | ((b[2 + pos] & 0xff) | (b[3 + pos] & 0xff) << 8) << 16; } public int readShortLittleEndian(RandomAccessFile randomAccessFile) throws IOException { randomAccessFile.readFully(shortBuff); return readShortLittleEndian(shortBuff, 0); } public int readShortLittleEndian(InputStream inputStream) throws IOException { readFully(inputStream, shortBuff, shortBuff.length); return readShortLittleEndian(shortBuff, 0); } public int readShortLittleEndian(byte[] buff, int position) { return (buff[position] & 0xff) | (buff[1 + position] & 0xff) << 8; } public void writeShortLittleEndian(OutputStream outputStream, int value) throws IOException { writeShortLittleEndian(shortBuff, 0, value); outputStream.write(shortBuff); } public void writeShortLittleEndian(byte[] array, int pos, int value) { array[pos + 1] = (byte) (value >>> 8); array[pos] = (byte) (value & 0xFF); } public void writeIntLittleEndian(OutputStream outputStream, int value) throws IOException { writeIntLittleEndian(intBuff, 0, value); outputStream.write(intBuff); } public void writeIntLittleEndian(byte[] array, int pos, int value) { array[pos + 3] = (byte) (value >>> 24); array[pos + 2] = (byte) (value >>> 16); array[pos + 1] = (byte) (value >>> 8); array[pos] = (byte) (value & 0xFF); } public void writeLongLittleEndian(OutputStream outputStream, long value) throws IOException { writeLongLittleEndian(longBuff, 0, value); outputStream.write(longBuff); } public void writeLongLittleEndian(byte[] array, int pos, long value) { array[pos + 7] = (byte) (value >>> 56); array[pos + 6] = (byte) (value >>> 48); array[pos + 5] = (byte) (value >>> 40); array[pos + 4] = (byte) (value >>> 32); array[pos + 3] = (byte) (value >>> 24); array[pos + 2] = (byte) (value >>> 16); array[pos + 1] = (byte) (value >>> 8); array[pos] = (byte) (value & 0xFF); } private void readFully(InputStream inputStream, byte[] buff, int readLen) throws IOException { int actualReadLength = Zip4jUtil.readFully(inputStream, buff, 0, readLen); if (actualReadLength != readLen) { throw new ZipException("Could not fill buffer"); } } private void resetBytes(byte[] b) { for(int i = 0; i < b.length; i++) { b[i] = 0; } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/util/UnzipUtil.java000077500000000000000000000034761357650161600242660ustar00rootroot00000000000000package net.lingala.zip4j.util; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.io.inputstream.SplitInputStream; import net.lingala.zip4j.io.inputstream.ZipInputStream; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.ZipModel; import java.io.File; import java.io.IOException; import java.nio.file.Path; import static net.lingala.zip4j.util.FileUtils.setFileAttributes; import static net.lingala.zip4j.util.FileUtils.setFileLastModifiedTime; import static net.lingala.zip4j.util.FileUtils.setFileLastModifiedTimeWithoutNio; public class UnzipUtil { public static ZipInputStream createZipInputStream(ZipModel zipModel, FileHeader fileHeader, char[] password) throws IOException { SplitInputStream splitInputStream = null; try { splitInputStream = new SplitInputStream(zipModel.getZipFile(), zipModel.isSplitArchive(), zipModel.getEndOfCentralDirectoryRecord().getNumberOfThisDisk()); splitInputStream.prepareExtractionForFileHeader(fileHeader); ZipInputStream zipInputStream = new ZipInputStream(splitInputStream, password); if (zipInputStream.getNextEntry(fileHeader) == null) { throw new ZipException("Could not locate local file header for corresponding file header"); } return zipInputStream; } catch (IOException e) { if (splitInputStream != null) { splitInputStream.close(); } throw e; } } public static void applyFileAttributes(FileHeader fileHeader, File file) { try { Path path = file.toPath(); setFileAttributes(path, fileHeader.getExternalFileAttributes()); setFileLastModifiedTime(path, fileHeader.getLastModifiedTime()); } catch (NoSuchMethodError e) { setFileLastModifiedTimeWithoutNio(file, fileHeader.getLastModifiedTime()); } } } zip4j-2.2.8/src/main/java/net/lingala/zip4j/util/Zip4jUtil.java000077500000000000000000000122461357650161600241540ustar00rootroot00000000000000/* * Copyright 2010 Srikanth Reddy Lingala * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.lingala.zip4j.util; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.LocalFileHeader; import net.lingala.zip4j.model.enums.CompressionMethod; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Calendar; public class Zip4jUtil { private static final int MAX_RAW_READ_FULLY_RETRY_ATTEMPTS = 15; public static boolean isStringNotNullAndNotEmpty(String str) { return str != null && str.trim().length() > 0; } public static boolean createDirectoryIfNotExists(File file) throws ZipException { if (file == null) { throw new ZipException("output path is null"); } if (file.exists()) { if (!file.isDirectory()) { throw new ZipException("output directory is not valid"); } } else { if (!file.mkdirs()) { throw new ZipException("Cannot create output directories"); } } return true; } public static long javaToDosTime(long time) { Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(time); int year = cal.get(Calendar.YEAR); if (year < 1980) { return (1 << 21) | (1 << 16); } return (year - 1980) << 25 | (cal.get(Calendar.MONTH) + 1) << 21 | cal.get(Calendar.DATE) << 16 | cal.get(Calendar.HOUR_OF_DAY) << 11 | cal.get(Calendar.MINUTE) << 5 | cal.get(Calendar.SECOND) >> 1; } public static long dosToJavaTme(long dosTime) { int sec = (int) (2 * (dosTime & 0x1f)); int min = (int) ((dosTime >> 5) & 0x3f); int hrs = (int) ((dosTime >> 11) & 0x1f); int day = (int) ((dosTime >> 16) & 0x1f); int mon = (int) (((dosTime >> 21) & 0xf) - 1); int year = (int) (((dosTime >> 25) & 0x7f) + 1980); Calendar cal = Calendar.getInstance(); cal.set(year, mon, day, hrs, min, sec); cal.set(Calendar.MILLISECOND, 0); return cal.getTime().getTime(); } public static byte[] convertCharArrayToByteArray(char[] charArray) { byte[] bytes = new byte[charArray.length]; for (int i = 0; i < charArray.length; i++) { bytes[i] = (byte) charArray[i]; } return bytes; } public static CompressionMethod getCompressionMethod(LocalFileHeader localFileHeader) { if (localFileHeader.getCompressionMethod() != CompressionMethod.AES_INTERNAL_ONLY) { return localFileHeader.getCompressionMethod(); } if (localFileHeader.getAesExtraDataRecord() == null) { throw new RuntimeException("AesExtraDataRecord not present in local header for aes encrypted data"); } return localFileHeader.getAesExtraDataRecord().getCompressionMethod(); } public static int readFully(InputStream inputStream, byte[] bufferToReadInto) throws IOException { int readLen = inputStream.read(bufferToReadInto); if (readLen != bufferToReadInto.length) { readLen = readUntilBufferIsFull(inputStream, bufferToReadInto, readLen); if (readLen != bufferToReadInto.length) { throw new IOException("Cannot read fully into byte buffer"); } } return readLen; } public static int readFully(InputStream inputStream, byte[] b, int offset, int length) throws IOException { int numberOfBytesRead = 0; if (offset < 0) { throw new IllegalArgumentException("Negative offset"); } if (length < 0) { throw new IllegalArgumentException("Negative length"); } if (length == 0) { return 0; } if (offset + length > b.length) { throw new IllegalArgumentException("Length greater than buffer size"); } while (numberOfBytesRead != length) { int currentReadLength = inputStream.read(b, offset + numberOfBytesRead, length - numberOfBytesRead); if (currentReadLength == -1) { if (numberOfBytesRead == 0) { return -1; } return numberOfBytesRead; } numberOfBytesRead += currentReadLength; } return numberOfBytesRead; } private static int readUntilBufferIsFull(InputStream inputStream, byte[] bufferToReadInto, int readLength) throws IOException { int remainingLength = bufferToReadInto.length - readLength; int loopReadLength = 0; int retryAttempt = 1; // first attempt is already done before this method is called while (readLength < bufferToReadInto.length && loopReadLength != -1 && retryAttempt < MAX_RAW_READ_FULLY_RETRY_ATTEMPTS) { loopReadLength = inputStream.read(bufferToReadInto, readLength, remainingLength); if (loopReadLength > 0) { readLength += loopReadLength; remainingLength -= loopReadLength; } retryAttempt++; } return readLength; } } zip4j-2.2.8/src/test/000077500000000000000000000000001357650161600143435ustar00rootroot00000000000000zip4j-2.2.8/src/test/java/000077500000000000000000000000001357650161600152645ustar00rootroot00000000000000zip4j-2.2.8/src/test/java/net/000077500000000000000000000000001357650161600160525ustar00rootroot00000000000000zip4j-2.2.8/src/test/java/net/lingala/000077500000000000000000000000001357650161600174615ustar00rootroot00000000000000zip4j-2.2.8/src/test/java/net/lingala/zip4j/000077500000000000000000000000001357650161600205215ustar00rootroot00000000000000zip4j-2.2.8/src/test/java/net/lingala/zip4j/AbstractIT.java000066400000000000000000000050011357650161600233600ustar00rootroot00000000000000package net.lingala.zip4j; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.AesKeyStrength; import net.lingala.zip4j.model.enums.EncryptionMethod; import net.lingala.zip4j.testutils.TestUtils; import org.junit.Before; import org.junit.Rule; import org.junit.rules.TemporaryFolder; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.util.Arrays; import java.util.List; import static net.lingala.zip4j.testutils.TestUtils.getTestFileFromResources; import static org.assertj.core.api.Assertions.assertThat; public abstract class AbstractIT { protected static final char[] PASSWORD = "test123!".toCharArray(); protected static final List FILES_TO_ADD = Arrays.asList( getTestFileFromResources("sample_text1.txt"), getTestFileFromResources("sample_text_large.txt"), getTestFileFromResources("sample.pdf") ); protected static final Charset CHARSET_MS_932 = Charset.forName("Ms932"); protected static final Charset CHARSET_GBK = Charset.forName("GBK"); protected static final Charset CHARSET_CP_949 = Charset.forName("Cp949"); protected File generatedZipFile; protected File outputFolder; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Before public void before() throws IOException { generatedZipFile = temporaryFolder.newFile("output.zip"); outputFolder = temporaryFolder.newFolder("output"); File[] allTempFiles = temporaryFolder.getRoot().listFiles(); Arrays.stream(allTempFiles).forEach(File::delete); } protected ZipParameters createZipParameters(EncryptionMethod encryptionMethod, AesKeyStrength aesKeyStrength) { ZipParameters zipParameters = new ZipParameters(); zipParameters.setEncryptFiles(true); zipParameters.setEncryptionMethod(encryptionMethod); zipParameters.setAesKeyStrength(aesKeyStrength); return zipParameters; } protected void verifyFileHeadersContainsFiles(List fileHeaders, List fileNames) { for (String fileName : fileNames) { boolean fileFound = false; for (FileHeader fileHeader : fileHeaders) { if (fileHeader.getFileName().equals(fileName)) { fileFound = true; break; } } assertThat(fileFound).as("File with name %s not found in zip file", fileName).isTrue(); } } protected File getTestArchiveFromResources(String archiveName) { return TestUtils.getTestArchiveFromResources(archiveName); } } zip4j-2.2.8/src/test/java/net/lingala/zip4j/AddFilesToZipIT.java000066400000000000000000001303071357650161600242660ustar00rootroot00000000000000package net.lingala.zip4j; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.io.inputstream.ZipInputStream; import net.lingala.zip4j.model.AESExtraDataRecord; import net.lingala.zip4j.model.AbstractFileHeader; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.LocalFileHeader; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.AesKeyStrength; import net.lingala.zip4j.model.enums.AesVersion; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.model.enums.EncryptionMethod; import net.lingala.zip4j.progress.ProgressMonitor; import net.lingala.zip4j.testutils.HeaderVerifier; import net.lingala.zip4j.testutils.TestUtils; import net.lingala.zip4j.testutils.ZipFileVerifier; import net.lingala.zip4j.util.BitUtils; import net.lingala.zip4j.util.InternalZipConstants; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; public class AddFilesToZipIT extends AbstractIT { @Rule public ExpectedException expectedException = ExpectedException.none(); private HeaderVerifier headerVerifier = new HeaderVerifier(); @Test public void testAddFileAsStringParameterThrowsExceptionWhenFileDoesNotExist() throws ZipException { expectedException.expectMessage("File does not exist: somefile.txt"); expectedException.expect(ZipException.class); ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFile("somefile.txt"); } @Test public void testAddFileAsStringParameterWithoutZipParameterAddsAsDeflate() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFile(TestUtils.getTestFileFromResources("sample.pdf").getPath()); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, 1); verifyZipFileContainsFiles(generatedZipFile, singletonList("sample.pdf"), CompressionMethod.DEFLATE, null, null); } @Test public void testAddFileAsStringWithZipParametersStoreAndStandardEncryption() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.ZIP_STANDARD, null); zipParameters.setCompressionMethod(CompressionMethod.STORE); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFile(TestUtils.getTestFileFromResources("sample_text_large.txt").getPath(), zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 1); verifyZipFileContainsFiles(generatedZipFile, singletonList("sample_text_large.txt"), CompressionMethod.STORE, EncryptionMethod.ZIP_STANDARD, null); } @Test public void testAddFileAsStringWithZipParametersStoreAndStandardEncryptionAndCharsetCp949() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.ZIP_STANDARD, null); zipParameters.setCompressionMethod(CompressionMethod.STORE); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.setCharset(CHARSET_CP_949); String koreanFileName = "가나다.abc"; zipFile.addFile(TestUtils.getTestFileFromResources(koreanFileName).getPath(), zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 1, true, CHARSET_CP_949); assertThat(zipFile.getFileHeaders().get(0).getFileName()).isEqualTo(koreanFileName); } @Test public void testAddFileThrowsExceptionWhenFileDoesNotExist() throws ZipException { expectedException.expectMessage("File does not exist: somefile.txt"); expectedException.expect(ZipException.class); ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFile(new File("somefile.txt")); } @Test public void testAddFileThrowsExceptionWhenPasswordNotSet() throws ZipException { expectedException.expectMessage("input password is empty or null"); expectedException.expect(ZipException.class); ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFile(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf"), zipParameters); } @Test public void testAddFileWithoutZipParameterAddsAsDeflate() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFile(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, 1); verifyZipFileContainsFiles(generatedZipFile, singletonList("file_PDF_1MB.pdf"), CompressionMethod.DEFLATE, null, null); } @Test public void testAddFileWithZipParametersStoreAndStandardZip() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.ZIP_STANDARD, null); zipParameters.setCompressionMethod(CompressionMethod.STORE); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFile(TestUtils.getTestFileFromResources("sample_text_large.txt"), zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 1); verifyZipFileContainsFiles(generatedZipFile, singletonList("sample_text_large.txt"), CompressionMethod.STORE, EncryptionMethod.ZIP_STANDARD, null); } @Test public void testAddFileWithZipParametersStoreAndAes128Encryption() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128); zipParameters.setCompressionMethod(CompressionMethod.STORE); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFile(TestUtils.getTestFileFromResources("sample_text_large.txt"), zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 1); verifyZipFileContainsFiles(generatedZipFile, singletonList("sample_text_large.txt"), CompressionMethod.STORE, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128); } @Test public void testAddFileWithZipParametersStoreAndAes256Encryption() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); zipParameters.setCompressionMethod(CompressionMethod.STORE); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFile(TestUtils.getTestFileFromResources("sample_text_large.txt"), zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 1); verifyZipFileContainsFiles(generatedZipFile, singletonList("sample_text_large.txt"), CompressionMethod.STORE, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); } @Test public void testAddFileRemovesExistingFileNoEncryption() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); zipFile.addFile(TestUtils.getTestFileFromResources("sample_text_large.txt")); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, 3); verifyZipFileContainsFiles(generatedZipFile, singletonList("sample_text_large.txt"), CompressionMethod.DEFLATE, null, null); } @Test public void testAddFileDoesNotOverrideFileIfFlagIsDisabled() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); ZipParameters zipParameters = new ZipParameters(); zipParameters.setCompressionMethod(CompressionMethod.STORE); zipParameters.setEncryptFiles(true); zipParameters.setEncryptionMethod(EncryptionMethod.AES); zipParameters.setOverrideExistingFilesInZip(false); zipFile.setPassword(PASSWORD); zipFile.addFile(TestUtils.getTestFileFromResources("sample_text_large.txt"), zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, 3); verifyZipFileContainsFiles(generatedZipFile, singletonList("sample_text_large.txt"), CompressionMethod.DEFLATE, null, null); } @Test public void testAddFileRemovesExistingFileNoEncryptionSingleFileInZip() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFile(TestUtils.getTestFileFromResources("sample_text_large.txt")); ZipParameters zipParameters = new ZipParameters(); zipParameters.setCompressionMethod(CompressionMethod.STORE); zipFile.addFile(TestUtils.getTestFileFromResources("sample_text_large.txt"), zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, 1); verifyZipFileContainsFiles(generatedZipFile, singletonList("sample_text_large.txt"), CompressionMethod.STORE, null, null); } @Test public void testAddFileWithDifferentFileNameSetsTheNewFileName() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); ZipParameters zipParameters = new ZipParameters(); zipParameters.setFileNameInZip("/data/newfile.txt"); zipFile.addFile(TestUtils.getTestFileFromResources("sample_text_large.txt"), zipParameters); zipFile = new ZipFile(generatedZipFile); assertThat(zipFile.getFileHeaders()).hasSize(1); assertThat(zipFile.getFileHeader("/data/newfile.txt")).isNotNull(); assertThat(zipFile.getFileHeader("sample_text_large.txt")).isNull(); zipFile.extractAll(outputFolder.getPath()); } @Test public void testAddFileRemovesExistingFileWithAesEncryption() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); List filesToAdd = new ArrayList<>(FILES_TO_ADD); filesToAdd.add(1, TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); zipFile.addFiles(filesToAdd, zipParameters); zipFile.addFile(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf"), zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, FILES_TO_ADD.size() + 1); verifyZipFileContainsFiles(generatedZipFile, singletonList("file_PDF_1MB.pdf"), CompressionMethod.DEFLATE, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); } @Test public void testAddFileWithAfterDeflateRemainingBytesTestFile() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); zipParameters.setAesVersion(AesVersion.TWO); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFile(TestUtils.getTestFileFromResources("after_deflate_remaining_bytes.bin"), zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 1); verifyZipFileContainsFiles(generatedZipFile, singletonList("after_deflate_remaining_bytes.bin"), CompressionMethod.DEFLATE, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); } @Test public void testAddFileProgressMonitorThrowsExceptionWhenPerformingActionInBusyState() throws ZipException { expectedException.expectMessage("invalid operation - Zip4j is in busy state"); expectedException.expect(ZipException.class); ZipFile zipFile = new ZipFile(generatedZipFile); ProgressMonitor progressMonitor = zipFile.getProgressMonitor(); progressMonitor.setState(ProgressMonitor.State.BUSY); zipFile.addFile(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); } @Test public void testAddFileWithProgressMonitor() throws IOException, InterruptedException { ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); ProgressMonitor progressMonitor = zipFile.getProgressMonitor(); boolean percentBetweenZeroAndHundred = false; boolean fileNameSet = false; boolean taskNameSet = false; zipFile.setRunInThread(true); zipFile.addFile(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf"), createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256)); while (!progressMonitor.getState().equals(ProgressMonitor.State.READY)) { int percentDone = progressMonitor.getPercentDone(); String fileName = progressMonitor.getFileName(); if (percentDone > 0 && percentDone < 100) { percentBetweenZeroAndHundred = true; } if (fileName != null) { assertThat(fileName).contains("file_PDF_1MB.pdf"); fileNameSet = true; } Thread.sleep(10); if (!progressMonitor.getCurrentTask().equals(ProgressMonitor.Task.NONE)) { assertThat(progressMonitor.getCurrentTask()).isEqualTo(ProgressMonitor.Task.ADD_ENTRY); taskNameSet = true; } } assertThat(progressMonitor.getResult()).isEqualTo(ProgressMonitor.Result.SUCCESS); assertThat(progressMonitor.getState().equals(ProgressMonitor.State.READY)); assertThat(progressMonitor.getException()).isNull(); assertThat(percentBetweenZeroAndHundred).isTrue(); assertThat(fileNameSet).isTrue(); assertThat(taskNameSet).isTrue(); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 1); } @Test public void testAddFilesWithoutParametersWhenZipFileDoesNotExistCreatesSuccessfully() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(asList( TestUtils.getTestFileFromResources("file_PDF_1MB.pdf"), TestUtils.getTestFileFromResources("zero_byte_file.txt") )); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, 2); verifyZipFileContainsFiles(generatedZipFile, asList("file_PDF_1MB.pdf", "zero_byte_file.txt"), CompressionMethod.DEFLATE, null, null); } @Test public void testAddFilesWhenZipFileDoesNotExistCreatesSuccessfully() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(asList( TestUtils.getTestFileFromResources("file_PDF_1MB.pdf"), TestUtils.getTestFileFromResources("sample_text1.txt") ), new ZipParameters()); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, 2); verifyZipFileContainsFiles(generatedZipFile, asList("file_PDF_1MB.pdf", "sample_text1.txt"), CompressionMethod.DEFLATE, null, null); } @Test public void testAddFilesWithZeroByteFileWithAes128Encryption() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(singletonList(TestUtils.getTestFileFromResources("zero_byte_file.txt")), zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 1); verifyZipFileContainsFiles(generatedZipFile, singletonList("zero_byte_file.txt"), CompressionMethod.DEFLATE, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128); } @Test public void testAddFilesWithAes256EncryptionV1() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); zipParameters.setAesVersion(AesVersion.ONE); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, FILES_TO_ADD.size()); List fileNames = FILES_TO_ADD.stream().map(File::getName).collect(Collectors.toList()); verifyZipFileContainsFiles(generatedZipFile, fileNames, CompressionMethod.DEFLATE, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256, AesVersion.ONE); } @Test public void testAddFilesWithAes256EncryptionV2() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, FILES_TO_ADD.size()); List fileNames = FILES_TO_ADD.stream().map(File::getName).collect(Collectors.toList()); verifyZipFileContainsFiles(generatedZipFile, fileNames, CompressionMethod.DEFLATE, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); } @Test public void testAddFilesWithZipStandardEncryption() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.ZIP_STANDARD, null); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, FILES_TO_ADD.size()); List fileNames = FILES_TO_ADD.stream().map(File::getName).collect(Collectors.toList()); verifyZipFileContainsFiles(generatedZipFile, fileNames, CompressionMethod.DEFLATE, EncryptionMethod.ZIP_STANDARD, null); } @Test public void testAddFilesWhenFilesAlreadyExistsRemovesFiles() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); char[] newPassword = "SOME_OTHER_PASSWORD".toCharArray(); zipFile = new ZipFile(generatedZipFile, newPassword); zipParameters.setEncryptionMethod(EncryptionMethod.ZIP_STANDARD); zipFile.addFiles(FILES_TO_ADD, zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, newPassword, outputFolder, FILES_TO_ADD.size()); List fileNames = FILES_TO_ADD.stream().map(File::getName).collect(Collectors.toList()); verifyZipFileContainsFiles(generatedZipFile, fileNames, CompressionMethod.DEFLATE, EncryptionMethod.ZIP_STANDARD, null); } @Test public void testAddFilesThrowsExceptionForAES192() throws ZipException { expectedException.expect(ZipException.class); expectedException.expectMessage("Invalid AES key strength"); ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_192); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); } @Test public void testAddFilesToSplitZipThrowsException() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.createSplitZipFile(singletonList(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")), new ZipParameters(), true, InternalZipConstants.MIN_SPLIT_LENGTH); expectedException.expect(ZipException.class); expectedException.expectMessage("Zip file already exists. " + "Zip file format does not allow updating split/spanned files"); zipFile.addFiles(singletonList(TestUtils.getTestFileFromResources("sample.pdf"))); } @Test public void testAddFilesWithDifferentEncryptionType() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(singletonList(TestUtils.getTestFileFromResources("sample.pdf")), zipParameters); zipParameters.setEncryptionMethod(EncryptionMethod.ZIP_STANDARD); zipFile.addFiles(singletonList(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")), zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 2); } @Test public void testAddFilesWithUtf8Characters() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(asList( TestUtils.getTestFileFromResources("sample.pdf"), TestUtils.getTestFileFromResources("бореиская.txt"), TestUtils.getTestFileFromResources("zero_byte_file.txt"), TestUtils.getTestFileFromResources("sample_text1.txt"), TestUtils.getTestFileFromResources("가나다.abc") ), zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 5); List fileNamesThatShouldExistInZip = asList( "sample.pdf", "бореиская.txt", "zero_byte_file.txt", "sample_text1.txt", "가나다.abc" ); verifyZipFileContainsFiles(generatedZipFile, fileNamesThatShouldExistInZip, CompressionMethod.DEFLATE, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); } @Test public void testAddFilesWithProgressMonitor() throws IOException, InterruptedException { ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); ProgressMonitor progressMonitor = zipFile.getProgressMonitor(); boolean percentBetweenZeroAndHundred = false; boolean fileNameSet = false; boolean taskNameSet = false; List filesToAdd = new ArrayList<>(FILES_TO_ADD); filesToAdd.add(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); zipFile.setRunInThread(true); zipFile.addFiles(filesToAdd, createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256)); while (!progressMonitor.getState().equals(ProgressMonitor.State.READY)) { int percentDone = progressMonitor.getPercentDone(); String fileName = progressMonitor.getFileName(); if (percentDone > 0 && percentDone < 100) { percentBetweenZeroAndHundred = true; } if (fileName != null) { fileNameSet = true; } Thread.sleep(10); if (!progressMonitor.getCurrentTask().equals(ProgressMonitor.Task.NONE)) { assertThat(progressMonitor.getCurrentTask()).isEqualTo(ProgressMonitor.Task.ADD_ENTRY); taskNameSet = true; } } assertThat(progressMonitor.getResult()).isEqualTo(ProgressMonitor.Result.SUCCESS); assertThat(progressMonitor.getState().equals(ProgressMonitor.State.READY)); assertThat(progressMonitor.getException()).isNull(); assertThat(percentBetweenZeroAndHundred).isTrue(); assertThat(fileNameSet).isTrue(); assertThat(taskNameSet).isTrue(); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 4); } @Test public void testAddFolderWithoutZipParameters() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFolder(TestUtils.getTestFileFromResources("")); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, 13); List fileHeaders = getFileHeaders(generatedZipFile); verifyAllFilesInZipContainsPath(fileHeaders, "test-files/"); verifyFoldersInZip(fileHeaders, generatedZipFile, null); } @Test public void testAddFolderWithStoreAndAes128() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128); zipParameters.setCompressionMethod(CompressionMethod.STORE); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFolder(TestUtils.getTestFileFromResources(""), zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 13); List fileHeaders = getFileHeaders(generatedZipFile); verifyAllFilesInZipContainsPath(fileHeaders, "test-files/"); verifyFoldersInZip(fileHeaders, generatedZipFile, PASSWORD); } @Test public void testAddFolderWithDeflateAndAes256AndWithoutRootFolder() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); zipParameters.setIncludeRootFolder(false); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFolder(TestUtils.getTestFileFromResources(""), zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 12); List fileHeaders = getFileHeaders(generatedZipFile); verifyAllFilesInZipDoesNotContainPath(fileHeaders, "test-files/"); verifyFoldersInZip(fileHeaders, generatedZipFile, PASSWORD); } @Test public void testAddFolderWithRootFolderNameInZipAndWithoutRootFolder() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); zipParameters.setIncludeRootFolder(false); zipParameters.setRootFolderNameInZip("root_folder_name"); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFolder(TestUtils.getTestFileFromResources(""), zipParameters); List fileHeaders = getFileHeaders(generatedZipFile); verifyAllFilesInZipContainsPath(fileHeaders, "root_folder_name/"); verifyAllFilesInZipDoesNotContainPath(fileHeaders, "root_folder_name/test-files/"); verifyFoldersInZip(fileHeaders, generatedZipFile, PASSWORD); } @Test public void testAddFolderWithRootFolderNameInZipAndWithRootFolder() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); zipParameters.setIncludeRootFolder(true); zipParameters.setRootFolderNameInZip("root_folder_name"); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFolder(TestUtils.getTestFileFromResources(""), zipParameters); List fileHeaders = getFileHeaders(generatedZipFile); verifyAllFilesInZipContainsPath(fileHeaders, "root_folder_name/"); verifyAllFilesInZipContainsPath(fileHeaders, "root_folder_name/test-files/"); verifyFoldersInZip(fileHeaders, generatedZipFile, PASSWORD); } @Test public void testAddFolderWithProgressMonitor() throws IOException, InterruptedException { ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); ProgressMonitor progressMonitor = zipFile.getProgressMonitor(); boolean percentBetweenZeroAndHundred = false; boolean fileNameSet = false; zipFile.setRunInThread(true); zipFile.addFolder(TestUtils.getTestFileFromResources(""), createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256)); while (!progressMonitor.getState().equals(ProgressMonitor.State.READY)) { int percentDone = progressMonitor.getPercentDone(); String fileName = progressMonitor.getFileName(); if (percentDone > 0 && percentDone < 100) { percentBetweenZeroAndHundred = true; } if (fileName != null) { fileNameSet = true; } Thread.sleep(10); } assertThat(progressMonitor.getResult()).isEqualTo(ProgressMonitor.Result.SUCCESS); assertThat(progressMonitor.getState().equals(ProgressMonitor.State.READY)); assertThat(progressMonitor.getException()).isNull(); assertThat(percentBetweenZeroAndHundred).isTrue(); assertThat(fileNameSet).isTrue(); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 13); } @Test public void testAddFolderWithNotNormalizedPath() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); ZipParameters parameters = new ZipParameters(); String folderToAddPath = TestUtils.getTestFileFromResources("").getPath() + InternalZipConstants.FILE_SEPARATOR + ".." + InternalZipConstants.FILE_SEPARATOR + TestUtils.getTestFileFromResources("").getName(); File folderToAdd = new File(folderToAddPath); zipFile.addFolder(folderToAdd, parameters); File fileToAdd = TestUtils.getTestFileFromResources("file_PDF_1MB.pdf"); zipFile.addFile(fileToAdd, parameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, 13); } @Test public void testAddStreamToZipThrowsExceptionWhenFileNameIsNull() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); InputStream inputStream = new FileInputStream(TestUtils.getTestFileFromResources("бореиская.txt")); ZipParameters zipParameters = new ZipParameters(); zipParameters.setFileNameInZip(null); expectedException.expectMessage("fileNameInZip has to be set in zipParameters when adding stream"); expectedException.expect(ZipException.class); zipFile.addStream(inputStream, zipParameters); } @Test public void testAddStreamToZipThrowsExceptionWhenFileNameIsEmpty() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); InputStream inputStream = new FileInputStream(TestUtils.getTestFileFromResources("бореиская.txt")); ZipParameters zipParameters = new ZipParameters(); zipParameters.setFileNameInZip(""); expectedException.expectMessage("fileNameInZip has to be set in zipParameters when adding stream"); expectedException.expect(ZipException.class); zipFile.addStream(inputStream, zipParameters); } @Test public void testAddStreamToZipWithoutEncryptionForNewZipAddsSuccessfully() throws IOException { File fileToAdd = TestUtils.getTestFileFromResources("бореиская.txt"); ZipParameters zipParameters = new ZipParameters(); zipParameters.setFileNameInZip(fileToAdd.getName()); ZipFile zipFile = new ZipFile(generatedZipFile); InputStream inputStream = new FileInputStream(fileToAdd); zipFile.addStream(inputStream, zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, 1); verifyZipFileContainsFiles(generatedZipFile, singletonList("бореиская.txt"), CompressionMethod.DEFLATE, null, null); } @Test public void testAddStreamToWithStoreCompressionAndWithoutEncryption() throws IOException { File fileToAdd = TestUtils.getTestFileFromResources("бореиская.txt"); ZipParameters zipParameters = new ZipParameters(); zipParameters.setCompressionMethod(CompressionMethod.STORE); zipParameters.setFileNameInZip(fileToAdd.getName()); ZipFile zipFile = new ZipFile(generatedZipFile); InputStream inputStream = new FileInputStream(fileToAdd); zipFile.addStream(inputStream, zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, 1); verifyZipFileContainsFiles(generatedZipFile, singletonList("бореиская.txt"), CompressionMethod.STORE, null, null); zipFile = new ZipFile(generatedZipFile); byte[] generalPurposeBytes = zipFile.getFileHeaders().get(0).getGeneralPurposeFlag(); // assert that extra data record is not present assertThat(BitUtils.isBitSet(generalPurposeBytes[0], 3)).isTrue(); } @Test public void testAddStreamToWithStoreCompressionAndZipStandardEncryption() throws IOException { File fileToAdd = TestUtils.getTestFileFromResources("sample_text_large.txt"); ZipParameters zipParameters = createZipParameters(EncryptionMethod.ZIP_STANDARD, null); zipParameters.setCompressionMethod(CompressionMethod.STORE); zipParameters.setFileNameInZip(fileToAdd.getName()); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); InputStream inputStream = new FileInputStream(fileToAdd); zipFile.addStream(inputStream, zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 1); verifyZipFileContainsFiles(generatedZipFile, singletonList("sample_text_large.txt"), CompressionMethod.STORE, EncryptionMethod.ZIP_STANDARD, null); zipFile = new ZipFile(generatedZipFile, PASSWORD); byte[] generalPurposeBytes = zipFile.getFileHeaders().get(0).getGeneralPurposeFlag(); // assert that extra data record is not present assertThat(BitUtils.isBitSet(generalPurposeBytes[0], 3)).isTrue(); } @Test public void testAddStreamWithStoreCompressionAndCharset() throws IOException { String koreanFileName = "가나다.abc"; File fileToAdd = TestUtils.getTestFileFromResources(koreanFileName); ZipParameters zipParameters = new ZipParameters(); zipParameters.setCompressionMethod(CompressionMethod.STORE); zipParameters.setFileNameInZip(fileToAdd.getName()); ZipFile zipFile = new ZipFile(generatedZipFile); InputStream inputStream = new FileInputStream(fileToAdd); zipFile.setCharset(CHARSET_CP_949); zipFile.addStream(inputStream, zipParameters); byte[] generalPurposeBytes = zipFile.getFileHeaders().get(0).getGeneralPurposeFlag(); // assert that extra data record is not present assertThat(BitUtils.isBitSet(generalPurposeBytes[1], 3)).isFalse(); assertThat(zipFile.getFileHeaders().get(0).getFileName()).isEqualTo(koreanFileName); } @Test public void testAddStreamWithStoreCompressionAndDefaultCharset() throws IOException { String koreanFileName = "가나다.abc"; File fileToAdd = TestUtils.getTestFileFromResources(koreanFileName); ZipParameters zipParameters = new ZipParameters(); zipParameters.setCompressionMethod(CompressionMethod.STORE); zipParameters.setFileNameInZip(fileToAdd.getName()); ZipFile zipFile = new ZipFile(generatedZipFile); InputStream inputStream = new FileInputStream(fileToAdd); zipFile.addStream(inputStream, zipParameters); byte[] generalPurposeBytes = zipFile.getFileHeaders().get(0).getGeneralPurposeFlag(); // assert that extra data record is not present assertThat(BitUtils.isBitSet(generalPurposeBytes[1], 3)).isTrue(); assertThat(zipFile.getFileHeaders().get(0).getFileName()).isEqualTo(koreanFileName); } @Test public void testAddStreamToZipWithAesEncryptionForNewZipAddsSuccessfully() throws IOException { File fileToAdd = TestUtils.getTestFileFromResources("бореиская.txt"); ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); zipParameters.setFileNameInZip(fileToAdd.getName()); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); InputStream inputStream = new FileInputStream(fileToAdd); zipFile.addStream(inputStream, zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 1); verifyZipFileContainsFiles(generatedZipFile, singletonList("бореиская.txt"), CompressionMethod.DEFLATE, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); headerVerifier.verifyLocalFileHeaderUncompressedSize(generatedZipFile, "бореиская.txt", 0); } @Test public void testAddStreamToZipWithoutEncryptionForExistingZipAddsSuccessfully() throws IOException { File fileToAdd = TestUtils.getTestFileFromResources("가나다.abc"); ZipParameters zipParameters = new ZipParameters(); zipParameters.setFileNameInZip(fileToAdd.getName()); ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); InputStream inputStream = new FileInputStream(fileToAdd); zipFile.addStream(inputStream, zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, 4); verifyFileIsOf(generatedZipFile, "가나다.abc", CompressionMethod.DEFLATE, EncryptionMethod.NONE, null, null); } @Test public void testAddStreamToZipWithAesEncryptionV2ForExistingZipAddsSuccessfully() throws IOException { File fileToAdd = TestUtils.getTestFileFromResources("가나다.abc"); ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128); zipParameters.setFileNameInZip(fileToAdd.getName()); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD); InputStream inputStream = new FileInputStream(fileToAdd); zipFile.addStream(inputStream, zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 4); verifyFileIsOf(generatedZipFile, "가나다.abc", CompressionMethod.DEFLATE, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128, AesVersion.TWO); } @Test public void testAddStreamToZipWithAesEncryptionV1ForExistingZipAddsSuccessfully() throws IOException { File fileToAdd = TestUtils.getTestFileFromResources("가나다.abc"); ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); zipParameters.setAesVersion(AesVersion.ONE); zipParameters.setFileNameInZip(fileToAdd.getName()); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD); InputStream inputStream = new FileInputStream(fileToAdd); zipFile.addStream(inputStream, zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 4); verifyFileIsOf(generatedZipFile, "가나다.abc", CompressionMethod.DEFLATE, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256, AesVersion.ONE); } @Test public void testAddStreamToZipWithCharsetCp949() throws IOException { String koreanFileName = "가나다.abc"; ZipFile zipFile = new ZipFile(generatedZipFile); File fileToAdd = TestUtils.getTestFileFromResources(koreanFileName); InputStream inputStream = new FileInputStream(fileToAdd); ZipParameters zipParameters = new ZipParameters(); zipParameters.setFileNameInZip(fileToAdd.getName()); zipFile.setCharset(CHARSET_CP_949); zipFile.addStream(inputStream, zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, null, outputFolder, 1, true, CHARSET_CP_949); assertThat(zipFile.getFileHeaders().get(0).getFileName()).isEqualTo(koreanFileName); } @Test public void testAddStreamToZipWithSameEntryNameRemovesOldEntry() throws IOException { File fileToAdd = TestUtils.getTestFileFromResources("sample.pdf"); ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFile(fileToAdd); try(InputStream inputStream = new FileInputStream(fileToAdd)) { ZipParameters zipParameters = new ZipParameters(); zipParameters.setFileNameInZip("sample.pdf"); zipFile.addStream(inputStream, zipParameters); } ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, 1); } private void verifyZipFileContainsFiles(File generatedZipFile, List fileNames, CompressionMethod compressionMethod, EncryptionMethod encryptionMethod, AesKeyStrength aesKeyStrength) throws ZipException { verifyZipFileContainsFiles(generatedZipFile, fileNames, compressionMethod, encryptionMethod, aesKeyStrength, AesVersion.TWO); } private void verifyZipFileContainsFiles(File generatedZipFile, List fileNames, CompressionMethod compressionMethod, EncryptionMethod encryptionMethod, AesKeyStrength aesKeyStrength, AesVersion aesVersion) throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); List fileHeaders = zipFile.getFileHeaders(); verifyFileHeadersContainsFiles(fileHeaders, fileNames); verifyAllFilesAreOf(fileHeaders, compressionMethod, encryptionMethod, aesKeyStrength, aesVersion); } private void verifyFoldersInZip(List fileHeaders, File generatedZipFile, char[] password) throws IOException { verifyFoldersInFileHeaders(fileHeaders); verifyFoldersInLocalFileHeaders(generatedZipFile, password); } private void verifyFoldersInFileHeaders(List fileHeaders) { fileHeaders.stream().filter(FileHeader::isDirectory).forEach(this::verifyFolderEntryInZip); } private void verifyFoldersInLocalFileHeaders(File generatedZipFile, char[] password) throws IOException { try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(generatedZipFile), password)) { LocalFileHeader localFileHeader; while ((localFileHeader = zipInputStream.getNextEntry()) != null) { if (localFileHeader.isDirectory()) { verifyFolderEntryInZip(localFileHeader); } } } } private void verifyFolderEntryInZip(AbstractFileHeader fileHeader) { assertThat(fileHeader.getCrc()).isZero(); assertThat(fileHeader.getCompressedSize()).isZero(); assertThat(fileHeader.getUncompressedSize()).isZero(); assertThat(fileHeader.getCompressionMethod()).isEqualTo(CompressionMethod.STORE); assertThat(fileHeader.getEncryptionMethod()).isEqualTo(EncryptionMethod.NONE); } private void verifyAllFilesInZipContainsPath(List fileHeaders, String pathToBeChecked) { fileHeaders.forEach(e -> assertThat(e.getFileName()).startsWith(pathToBeChecked)); } private void verifyAllFilesInZipDoesNotContainPath(List fileHeaders, String pathToBeChecked) { fileHeaders.forEach(e -> assertThat(e.getFileName()).doesNotStartWith(pathToBeChecked)); } private void verifyAllFilesAreOf(List fileHeaders, CompressionMethod compressionMethod, EncryptionMethod encryptionMethod, AesKeyStrength aesKeyStrength, AesVersion aesVersion) { for (FileHeader fileHeader : fileHeaders) { if (fileHeader.isDirectory()) { assertThat(fileHeader.getCompressionMethod()).isEqualTo(CompressionMethod.STORE); assertThat(fileHeader.isEncrypted()).isFalse(); assertThat(fileHeader.getAesExtraDataRecord()).isNull(); assertThat(fileHeader.getCrc()).isZero(); } else { CompressionMethod shouldBeCompressionMethod = getShouldBeCompressionMethod( encryptionMethod == EncryptionMethod.AES, compressionMethod, fileHeader.getUncompressedSize()); assertThat(fileHeader.getCompressionMethod()).isEqualTo(shouldBeCompressionMethod); if (encryptionMethod == null) { assertThat(fileHeader.getEncryptionMethod()).isEqualTo(EncryptionMethod.NONE); } else { assertThat(fileHeader.getEncryptionMethod()).isEqualTo(encryptionMethod); } if (encryptionMethod == EncryptionMethod.AES) { verifyAesExtraDataRecord(fileHeader.getAesExtraDataRecord(), aesKeyStrength, aesVersion); if (fileHeader.getAesExtraDataRecord().getAesVersion().equals(AesVersion.TWO)) { assertThat(fileHeader.getCrc()).isZero(); } else { if (fileHeader.getCompressedSize() != 0) { assertThat(fileHeader.getCrc()).isNotZero(); } } } else { assertThat(fileHeader.getAesExtraDataRecord()).isNull(); } } } } private void verifyFileIsOf(File generatedZipFile, String fileName, CompressionMethod compressionMethod, EncryptionMethod encryptionMethod, AesKeyStrength aesKeyStrength, AesVersion aesVersion) throws ZipException { List fileHeaders = getFileHeaders(generatedZipFile); FileHeader fileHeader = getFileHeaderFrom(fileHeaders, fileName); if (encryptionMethod == null || encryptionMethod == EncryptionMethod.NONE) { assertThat(fileHeader.isEncrypted()).isFalse(); assertThat(fileHeader.getEncryptionMethod()).isIn(null, EncryptionMethod.NONE); } else { verifyAllFilesAreOf(singletonList(fileHeader), compressionMethod, encryptionMethod, aesKeyStrength, aesVersion); } } private FileHeader getFileHeaderFrom(List fileHeaders, String fileName) { Optional fileHeader = fileHeaders.stream().filter(e -> e.getFileName().equals(fileName)).findFirst(); assertThat(fileHeader).isPresent(); return fileHeader.get(); } private List getFileHeaders(File generatedZipFile) throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); List fileHeaders = zipFile.getFileHeaders(); assertThat(fileHeaders.size()).isNotZero(); return fileHeaders; } private void verifyAesExtraDataRecord(AESExtraDataRecord aesExtraDataRecord, AesKeyStrength aesKeyStrength, AesVersion aesVersion) { assertThat(aesExtraDataRecord).isNotNull(); assertThat(aesExtraDataRecord.getAesKeyStrength()).isEqualTo(aesKeyStrength); assertThat(aesExtraDataRecord.getAesVersion()).isEqualTo(aesVersion); } private CompressionMethod getShouldBeCompressionMethod(boolean isAesEncrypted, CompressionMethod compressionMethod, long uncompressedSize) { if (isAesEncrypted) { return CompressionMethod.AES_INTERNAL_ONLY; } if (uncompressedSize == 0) { return CompressionMethod.STORE; } return compressionMethod; } } zip4j-2.2.8/src/test/java/net/lingala/zip4j/CreateZipFileIT.java000066400000000000000000000351311357650161600243120ustar00rootroot00000000000000package net.lingala.zip4j; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.AESExtraDataRecord; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.AesKeyStrength; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.model.enums.EncryptionMethod; import net.lingala.zip4j.testutils.TestUtils; import net.lingala.zip4j.testutils.ZipFileVerifier; import net.lingala.zip4j.util.FileUtils; import net.lingala.zip4j.util.InternalZipConstants; import org.assertj.core.data.Offset; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.util.ArrayList; import java.util.List; import static net.lingala.zip4j.testutils.TestUtils.getTestFileFromResources; import static net.lingala.zip4j.testutils.ZipFileVerifier.verifyZipFileByExtractingAllFiles; import static org.assertj.core.api.Assertions.assertThat; /** * Contains Integration tests for create operations of ZipFile */ public class CreateZipFileIT extends AbstractIT { @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void testCreateSplitZipFileNotSplitArchiveWithZipNameAsString() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile.getPath()); zipFile.createSplitZipFile(FILES_TO_ADD, new ZipParameters(), false, -1); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, FILES_TO_ADD.size()); } @Test public void testCreateSplitZipFileNotSplitArchiveWithZipNameAsStringAndCharsetCp949() throws IOException { String koreanFileName = "가나다.abc"; ZipFile zipFile = new ZipFile(generatedZipFile.getPath()); List filesToAdd = new ArrayList<>(); filesToAdd.add(getTestFileFromResources(koreanFileName)); zipFile.setCharset(CHARSET_CP_949); zipFile.createSplitZipFile(filesToAdd, new ZipParameters(), false, -1); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, null, outputFolder, filesToAdd.size(), true, CHARSET_CP_949); assertThat(zipFile.getFileHeaders().get(0).getFileName()).isEqualTo(koreanFileName); } @Test public void testCreateSplitZipFileNotSplitArchiveWithZipNameAsStringWithAESEncryption256() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile.getPath(), PASSWORD); zipFile.createSplitZipFile(FILES_TO_ADD, zipParameters, false, -1); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, FILES_TO_ADD.size()); verifyFileHeadersEncrypted(zipFile.getFileHeaders(), EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256, CompressionMethod.DEFLATE); } @Test public void testCreateSplitZipFileNotSplitArchiveWithZipNameAsStringWithAESEncryption128() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128); ZipFile zipFile = new ZipFile(generatedZipFile.getPath(), PASSWORD); zipFile.createSplitZipFile(FILES_TO_ADD, zipParameters, false, -1); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, FILES_TO_ADD.size()); verifyFileHeadersEncrypted(zipFile.getFileHeaders(), EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128, CompressionMethod.DEFLATE); } @Test public void testCreateSplitZipFileNotSplitArchiveWithFile() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.createSplitZipFile(FILES_TO_ADD, new ZipParameters(), false, -1); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, FILES_TO_ADD.size()); } @Test public void testCreateSplitZipFileNotSplitArchiveWithFileAndWithAESEncryption256() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile.getPath(), PASSWORD); zipFile.createSplitZipFile(FILES_TO_ADD, zipParameters, false, -1); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, FILES_TO_ADD.size()); verifyFileHeadersEncrypted(zipFile.getFileHeaders(), EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256, CompressionMethod.DEFLATE); } @Test public void testCreateSplitZipFileNotSplitArchiveWithFileAndWithAESEncryption128() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128); ZipFile zipFile = new ZipFile(generatedZipFile.getPath(), PASSWORD); zipFile.createSplitZipFile(FILES_TO_ADD, zipParameters, false, -1); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, FILES_TO_ADD.size()); verifyFileHeadersEncrypted(zipFile.getFileHeaders(), EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128, CompressionMethod.DEFLATE); } @Test public void testCreateSplitZipFileNotSplitArchiveWithFileAndWithStandardEncryption() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.ZIP_STANDARD, AesKeyStrength.KEY_STRENGTH_128); ZipFile zipFile = new ZipFile(generatedZipFile.getPath(), PASSWORD); zipFile.createSplitZipFile(FILES_TO_ADD, zipParameters, false, -1); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, FILES_TO_ADD.size()); verifyFileHeadersEncrypted(zipFile.getFileHeaders(), EncryptionMethod.ZIP_STANDARD, AesKeyStrength.KEY_STRENGTH_128, CompressionMethod.DEFLATE); } @Test public void testCreateSplitZipFileThrowsExceptionWhenSplitSizeLessThanMinimumAllowed() throws ZipException { expectedException.expectMessage("split length less than minimum allowed split length of " + InternalZipConstants.MIN_SPLIT_LENGTH + " Bytes"); expectedException.expect(ZipException.class); ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.createSplitZipFile(FILES_TO_ADD, new ZipParameters(), true, InternalZipConstants.MIN_SPLIT_LENGTH - 1); } @Test public void testCreateSplitZipFileStoreAndWithoutEncryption() throws IOException { ZipParameters zipParameters = new ZipParameters(); zipParameters.setCompressionMethod(CompressionMethod.STORE); ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.createSplitZipFile(FILES_TO_ADD, zipParameters, true, InternalZipConstants.MIN_SPLIT_LENGTH); verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, FILES_TO_ADD.size()); verifySplitZip(generatedZipFile, 2, InternalZipConstants.MIN_SPLIT_LENGTH); } @Test public void testCreateSplitZipFileStoreAndStandardZipEncryption() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.ZIP_STANDARD, null); zipParameters.setCompressionMethod(CompressionMethod.STORE); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.createSplitZipFile(FILES_TO_ADD, zipParameters, true, InternalZipConstants.MIN_SPLIT_LENGTH); verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, FILES_TO_ADD.size()); verifySplitZip(generatedZipFile, 2, InternalZipConstants.MIN_SPLIT_LENGTH); } @Test public void testCreateSplitZipFileStoreAndWithAesEncryptionKeyStrength256() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); zipParameters.setCompressionMethod(CompressionMethod.STORE); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.createSplitZipFile(FILES_TO_ADD, zipParameters, true, InternalZipConstants.MIN_SPLIT_LENGTH); verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, FILES_TO_ADD.size()); verifySplitZip(generatedZipFile, 2, InternalZipConstants.MIN_SPLIT_LENGTH); } @Test public void testCreateSplitZipFileStoreAndWithAesEncryptionKeyStrength128() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128); zipParameters.setCompressionMethod(CompressionMethod.STORE); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); List filesToAdd = new ArrayList<>(FILES_TO_ADD); filesToAdd.add(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); zipFile.createSplitZipFile(filesToAdd, zipParameters, true, InternalZipConstants.MIN_SPLIT_LENGTH); verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, FILES_TO_ADD.size() + 1); verifySplitZip(generatedZipFile, 18, InternalZipConstants.MIN_SPLIT_LENGTH); } @Test public void testCreateSplitZipFileDeflateAndWithoutEncryption() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); List filesToAdd = new ArrayList<>(FILES_TO_ADD); filesToAdd.add(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); zipFile.createSplitZipFile(filesToAdd, new ZipParameters(), true, 716800); verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, FILES_TO_ADD.size() + 1); verifySplitZip(generatedZipFile, 2, InternalZipConstants.MIN_SPLIT_LENGTH); } @Test public void testCreateSplitZipFileDeflateAndStandardZipEncryption() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.ZIP_STANDARD, null); zipParameters.setCompressionMethod(CompressionMethod.DEFLATE); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); List filesToAdd = new ArrayList<>(FILES_TO_ADD); filesToAdd.add(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); zipFile.createSplitZipFile(filesToAdd, zipParameters, true, 512000); verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, FILES_TO_ADD.size() + 1); verifySplitZip(generatedZipFile, 2, InternalZipConstants.MIN_SPLIT_LENGTH); } @Test public void testCreateSplitZipFileDeflateAndWithAesEncryptionKeyStrength256() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); List filesToAdd = new ArrayList<>(FILES_TO_ADD); filesToAdd.add(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); zipFile.createSplitZipFile(filesToAdd, zipParameters, true, InternalZipConstants.MIN_SPLIT_LENGTH); verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, FILES_TO_ADD.size() + 1); verifySplitZip(generatedZipFile, 15, InternalZipConstants.MIN_SPLIT_LENGTH); } @Test public void testCreateSplitZipFileDeflateAndWithAesEncryptionKeyStrength128() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); List filesToAdd = new ArrayList<>(FILES_TO_ADD); filesToAdd.add(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); zipFile.createSplitZipFile(filesToAdd, zipParameters, true, InternalZipConstants.MIN_SPLIT_LENGTH + 2000); verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, FILES_TO_ADD.size() + 1); verifySplitZip(generatedZipFile, 15, InternalZipConstants.MIN_SPLIT_LENGTH + 2000); } @Test public void testCreateZipFileWithSetPasswordSetter() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128); ZipFile zipFile = new ZipFile(generatedZipFile, "WRONG_PASSWORD".toCharArray()); zipFile.setPassword(PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, FILES_TO_ADD.size()); } private void verifySplitZip(File zipFile, int numberOfExpectedSplitFiles, long splitLength) throws ZipException { assertNumberOfSplitFile(zipFile, numberOfExpectedSplitFiles); assertSplitFileSizes(zipFile, numberOfExpectedSplitFiles, splitLength); } private void assertSplitFileSizes(File zipFile, int numberOfExpectedSplitFiles, long splitLength) { for (int i = 0; i < numberOfExpectedSplitFiles - 2; i++) { String zipExtension = ".z0"; if (i >= 9) { zipExtension = ".z"; } String fileNameWithoutExtension = zipFile.getPath().substring(0, zipFile.getPath().lastIndexOf(".zip")); File splitZip = new File(fileNameWithoutExtension + zipExtension + (i + 1)); assertThat(splitZip).exists(); assertThat(splitZip.length()).isCloseTo(splitLength, Offset.offset(1000L)); } } private void assertNumberOfSplitFile(File zipFile, int numberOfExpectedSplitFiles) throws ZipException { File[] allSplitFiles = getAllSplitZipFilesInFolder(zipFile.getParentFile(), FileUtils.getZipFileNameWithoutExtension(zipFile.getName())); assertThat(allSplitFiles.length).as("Number of split files").isEqualTo(numberOfExpectedSplitFiles); } private void verifyFileHeadersEncrypted(List fileHeaders, EncryptionMethod encryptionMethod, AesKeyStrength aesKeyStrength, CompressionMethod compressionMethod) { for (FileHeader fileHeader : fileHeaders) { if (fileHeader.isDirectory()) { assertThat(fileHeader.isEncrypted()).isFalse(); assertThat(fileHeader.getAesExtraDataRecord()).isNull(); } else { assertThat(fileHeader.isEncrypted()).isTrue(); assertThat(fileHeader.getEncryptionMethod()).isEqualTo(encryptionMethod); if (encryptionMethod == EncryptionMethod.AES) { verifyAesExtraDataRecord(fileHeader.getAesExtraDataRecord(), compressionMethod, aesKeyStrength); } else { assertThat(fileHeader.getAesExtraDataRecord()).isNull(); } } } } private void verifyAesExtraDataRecord(AESExtraDataRecord aesExtraDataRecord, CompressionMethod compressionMethod, AesKeyStrength aesKeyStrength) { assertThat(aesExtraDataRecord).isNotNull(); assertThat(aesExtraDataRecord.getCompressionMethod()).isEqualTo(compressionMethod); assertThat(aesExtraDataRecord.getAesKeyStrength()).isEqualTo(aesKeyStrength); } private File[] getAllSplitZipFilesInFolder(File folder, String fileNameWithoutExtension) { FilenameFilter filenameFilter = (dir, name) -> name.contains(fileNameWithoutExtension + "."); return folder.listFiles(filenameFilter); } } zip4j-2.2.8/src/test/java/net/lingala/zip4j/ExtractZipFileIT.java000066400000000000000000000447651357650161600245360ustar00rootroot00000000000000package net.lingala.zip4j; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.io.inputstream.ZipInputStream; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.AesKeyStrength; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.model.enums.EncryptionMethod; import net.lingala.zip4j.testutils.TestUtils; import net.lingala.zip4j.testutils.ZipFileVerifier; import net.lingala.zip4j.util.FileUtils; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; public class ExtractZipFileIT extends AbstractIT { @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void testExtractAllStoreAndNoEncryptionExtractsSuccessfully() throws IOException { ZipParameters zipParameters = new ZipParameters(); zipParameters.setCompressionMethod(CompressionMethod.STORE); ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD, zipParameters); zipFile.extractAll(outputFolder.getPath()); ZipFileVerifier.verifyFolderContentsSameAsSourceFiles(outputFolder); verifyNumberOfFilesInOutputFolder(outputFolder, 3); } @Test public void testExtractAllStoreAndZipStandardEncryptionExtractsSuccessfully() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.ZIP_STANDARD, null); zipParameters.setCompressionMethod(CompressionMethod.STORE); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); zipFile.extractAll(outputFolder.getPath()); ZipFileVerifier.verifyFolderContentsSameAsSourceFiles(outputFolder); verifyNumberOfFilesInOutputFolder(outputFolder, 3); } @Test public void testExtractAllStoreAndAes128EncryptionExtractsSuccessfully() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128); zipParameters.setCompressionMethod(CompressionMethod.STORE); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); zipFile.extractAll(outputFolder.getPath()); ZipFileVerifier.verifyFolderContentsSameAsSourceFiles(outputFolder); verifyNumberOfFilesInOutputFolder(outputFolder, 3); } @Test public void testExtractAllStoreAndAes256EncryptionExtractsSuccessfully() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); zipParameters.setCompressionMethod(CompressionMethod.STORE); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); zipFile.extractAll(outputFolder.getPath()); ZipFileVerifier.verifyFolderContentsSameAsSourceFiles(outputFolder); verifyNumberOfFilesInOutputFolder(outputFolder, 3); } @Test public void testExtractAllDeflateAndNoEncryptionExtractsSuccessfully() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); zipFile.extractAll(outputFolder.getPath()); ZipFileVerifier.verifyFolderContentsSameAsSourceFiles(outputFolder); verifyNumberOfFilesInOutputFolder(outputFolder, 3); } @Test public void testExtractAllDeflateAndZipStandardEncryptionExtractsSuccessfully() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.ZIP_STANDARD, null); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); zipFile.extractAll(outputFolder.getPath()); ZipFileVerifier.verifyFolderContentsSameAsSourceFiles(outputFolder); verifyNumberOfFilesInOutputFolder(outputFolder, 3); } @Test public void testExtractAllDeflateAndAes128EncryptionExtractsSuccessfully() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); zipFile.extractAll(outputFolder.getPath()); ZipFileVerifier.verifyFolderContentsSameAsSourceFiles(outputFolder); verifyNumberOfFilesInOutputFolder(outputFolder, 3); } @Test public void testExtractAllDeflateAndAes256EncryptionExtractsSuccessfully() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); zipFile.extractAll(outputFolder.getPath()); ZipFileVerifier.verifyFolderContentsSameAsSourceFiles(outputFolder); verifyNumberOfFilesInOutputFolder(outputFolder, 3); } @Test public void testExtractFileWithFileHeaderWithAes128() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); FileHeader fileHeader = zipFile.getFileHeader("sample_text_large.txt"); zipFile.extractFile(fileHeader, outputFolder.getPath()); File[] outputFiles = outputFolder.listFiles(); assertThat(outputFiles).hasSize(1); ZipFileVerifier.verifyFileContent(TestUtils.getTestFileFromResources("sample_text_large.txt"), outputFiles[0]); } @Test public void testExtractFileWithFileHeaderWithAes128AndInDirectory() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFolder(TestUtils.getTestFileFromResources(""), zipParameters); FileHeader fileHeader = zipFile.getFileHeader("test-files/öüäöäö/asöäööl"); zipFile.extractFile(fileHeader, outputFolder.getPath()); File outputFile = getFileWithNameFrom(outputFolder, "asöäööl"); ZipFileVerifier.verifyFileContent(TestUtils.getTestFileFromResources("öüäöäö/asöäööl"), outputFile); } @Test public void testExtractFileWithFileHeaderWithAes256AndWithANewFileName() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); String newFileName = "newFileName"; FileHeader fileHeader = zipFile.getFileHeader("sample_text_large.txt"); zipFile.extractFile(fileHeader, outputFolder.getPath(), newFileName); File outputFile = getFileWithNameFrom(outputFolder, newFileName); ZipFileVerifier.verifyFileContent(TestUtils.getTestFileFromResources("sample_text_large.txt"), outputFile); } @Test public void testExtractFileWithFileNameThrowsExceptionWhenFileNotFound() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD); expectedException.expect(ZipException.class); expectedException.expectMessage("No file found with name NOT_EXISTING in zip file"); zipFile.extractFile("NOT_EXISTING", outputFolder.getPath()); } @Test public void testExtractFileWithFileNameWithZipStandardEncryption() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.ZIP_STANDARD, null); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFolder(TestUtils.getTestFileFromResources(""), zipParameters); zipFile.extractFile("test-files/sample_directory/favicon.ico", outputFolder.getPath()); File outputFile = getFileWithNameFrom(outputFolder, "favicon.ico"); ZipFileVerifier.verifyFileContent(TestUtils.getTestFileFromResources("sample_directory/favicon.ico"), outputFile); } @Test public void testExtractFileWithFileNameWithZipStandardEncryptionAndNewFileName() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.ZIP_STANDARD, null); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFolder(TestUtils.getTestFileFromResources(""), zipParameters); String newFileName = "newFileName"; zipFile.extractFile("test-files/sample_directory/favicon.ico", outputFolder.getPath(), newFileName); File outputFile = getFileWithNameFrom(outputFolder, newFileName); ZipFileVerifier.verifyFileContent(TestUtils.getTestFileFromResources("sample_directory/favicon.ico"), outputFile); } @Test public void testExtractFilesThrowsExceptionForWrongPasswordForAes() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); try { zipFile = new ZipFile(generatedZipFile, "WRONG_PASSWORD".toCharArray()); zipFile.extractAll(outputFolder.getPath()); fail("Should throw an exception"); } catch (ZipException e) { assertThat(e).isNotNull(); assertThat(e.getType()).isEqualTo(ZipException.Type.WRONG_PASSWORD); } } @Test public void testExtractFilesThrowsExceptionForWrongPasswordForZipStandardAndDeflate() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.ZIP_STANDARD, null); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); try { zipFile = new ZipFile(generatedZipFile, "WRONG_PASSWORD".toCharArray()); zipFile.extractAll(outputFolder.getPath()); fail("Should throw an exception"); } catch (ZipException e) { assertThat(e).isNotNull(); assertThat(e.getType()).isEqualTo(ZipException.Type.WRONG_PASSWORD); } } @Test public void testExtractFilesThrowsExceptionForWrongPasswordForZipStandardAndStore() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.ZIP_STANDARD, null); zipParameters.setCompressionMethod(CompressionMethod.STORE); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); try { zipFile = new ZipFile(generatedZipFile, "WRONG_PASSWORD".toCharArray()); zipFile.extractAll(outputFolder.getPath()); fail("Should throw an exception"); } catch (ZipException e) { assertThat(e).isNotNull(); assertThat(e.getType()).isEqualTo(ZipException.Type.WRONG_PASSWORD); } } @Test public void testExtractFilesForAZipMadeWithZip4jv1AndStoreCompressionWithAES() throws IOException { File zipArchiveToTest = getTestArchiveFromResources("store_compression_made_with_v1.3.3.zip"); ZipFileVerifier.verifyZipFileByExtractingAllFiles(zipArchiveToTest, "aaaaaaaa".toCharArray(), outputFolder, 5, false); } @Test public void testExtractFilesForZipFileWhileWithCorruptExtraDataRecordLength() throws IOException { ZipFile zipFile = new ZipFile(getTestArchiveFromResources("corrupt_extra_data_record_length.zip")); zipFile.extractAll(outputFolder.getPath()); assertThat(zipFile.getFileHeaders()).hasSize(44); assertThat(Files.walk(outputFolder.toPath()).filter(Files::isRegularFile)).hasSize(44); } @Test public void testExtractFilesWithSetPasswordSetter() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.ZIP_STANDARD, null); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); zipFile = new ZipFile(generatedZipFile, "WRONG_PASSWORD".toCharArray()); zipFile.setPassword(PASSWORD); zipFile.extractAll(outputFolder.getCanonicalPath()); } @Test public void testExtractZipFileWithMissingExtendedLocalFileHeader() throws IOException { ZipFile zipFile = new ZipFile(getTestArchiveFromResources("missing_exended_local_file_header.zip")); zipFile.extractAll(outputFolder.getPath()); assertThat(zipFile.getFileHeaders()).hasSize(3); assertThat(Files.walk(outputFolder.toPath()).filter(Files::isRegularFile)).hasSize(3); } @Test public void testExtractZipFileWithZip64ExtraDataRecordWithOnlyFileSizesInIt() throws IOException { ZipFile zipFile = new ZipFile(getTestArchiveFromResources("zip64_with_only_file_sizes.zip"), "Shu1an@2019GTS".toCharArray()); zipFile.extractAll(outputFolder.getPath()); assertThat(zipFile.getFileHeaders()).hasSize(1); assertThat(Files.walk(outputFolder.toPath()).filter(Files::isRegularFile)).hasSize(1); } @Test public void testExtractZipFileWithChineseCharsetGBK() throws IOException { String expactedFileName = "fff - 副本.txt"; ZipFile zipFile = new ZipFile(getTestArchiveFromResources("testfile_with_chinese_filename_by_7zip.zip")); zipFile.setCharset(CHARSET_GBK); zipFile.extractAll(outputFolder.getPath()); assertThat(zipFile.getFileHeaders()).hasSize(2); Set filenameSet = new HashSet<>(); Files.walk(outputFolder.toPath()).forEach(file -> filenameSet.add(file.getFileName().toString())); assertThat(filenameSet.contains(expactedFileName)).isTrue(); } @Test public void testExtractZipFileWithCheckoutMismatchThrowsExceptionWithType() { try { ZipFile zipFile = new ZipFile(getTestArchiveFromResources("entry_with_checksum_mismatch.zip")); zipFile.extractAll(outputFolder.getPath()); fail("Should throw an exception"); } catch (ZipException e) { assertThat(e.getType()).isEqualTo(ZipException.Type.CHECKSUM_MISMATCH); assertThat(e.getMessage()).isEqualTo("Reached end of entry, but crc verification failed for sample_text1.txt"); } } @Test public void testExtractNestedZipFileWithNoEncryptionOnInnerAndOuter() throws IOException { testExtractNestedZipFileWithEncrpytion(EncryptionMethod.NONE, EncryptionMethod.NONE); } @Test public void testExtractNestedZipFileWithNoEncryptionOnInnerAndZipStandardOuter() throws IOException { testExtractNestedZipFileWithEncrpytion(EncryptionMethod.NONE, EncryptionMethod.ZIP_STANDARD); } @Test public void testExtractNestedZipFileWithNoEncryptionOnInnerAndAesdOuter() throws IOException { testExtractNestedZipFileWithEncrpytion(EncryptionMethod.NONE, EncryptionMethod.AES); } @Test public void testExtractNestedZipFileWithZipStandardEncryptionOnInnerAndNoneOuter() throws IOException { testExtractNestedZipFileWithEncrpytion(EncryptionMethod.ZIP_STANDARD, EncryptionMethod.NONE); } @Test public void testExtractNestedZipFileWitAesOnInnerAndNoneOuter() throws IOException { testExtractNestedZipFileWithEncrpytion(EncryptionMethod.AES, EncryptionMethod.NONE); } @Test public void testExtractNestedZipFileWithZipStandardEncryptionOnInnerAndAesOuter() throws IOException { testExtractNestedZipFileWithEncrpytion(EncryptionMethod.ZIP_STANDARD, EncryptionMethod.AES); } @Test public void testExtractNestedZipFileWithAesOnInnerAndZipStandardOuter() throws IOException { testExtractNestedZipFileWithEncrpytion(EncryptionMethod.AES, EncryptionMethod.ZIP_STANDARD); } @Test public void testExtractZipFileLessThanMinimumExpectedZipFileSizeThrowsException() throws IOException { expectedException.expect(ZipException.class); expectedException.expectMessage("Zip file size less than minimum expected zip file size. " + "Probably not a zip file or a corrupted zip file"); ZipFile zipFile = new ZipFile(getTestArchiveFromResources("invalid_zip_file_size_less_than_22kb.zip")); zipFile.extractAll(temporaryFolder.toString()); } private void testExtractNestedZipFileWithEncrpytion(EncryptionMethod innerZipEncryption, EncryptionMethod outerZipEncryption) throws IOException { File innerZipFile = temporaryFolder.newFile("inner.zip"); File outerZipFile = temporaryFolder.newFile("outer.zip"); innerZipFile.delete(); outerZipFile.delete(); createNestedZip(innerZipFile, outerZipFile, innerZipEncryption, outerZipEncryption); verifyNestedZipFile(outerZipFile, FILES_TO_ADD.size()); } private void createNestedZip(File innerSourceZipFile, File outerSourceZipFile, EncryptionMethod innerEncryption, EncryptionMethod outerEncryption) throws ZipException { ZipFile innerZipFile = new ZipFile(innerSourceZipFile, PASSWORD); ZipParameters innerZipParameters = createZipParametersForNestedZip(innerEncryption); innerZipFile.addFiles(FILES_TO_ADD, innerZipParameters); ZipFile outerZipFile = new ZipFile(outerSourceZipFile, PASSWORD); ZipParameters outerZipParameters = createZipParametersForNestedZip(outerEncryption); outerZipFile.addFile(innerSourceZipFile, outerZipParameters); } private void verifyNestedZipFile(File outerZipFileToVerify, int numberOfFilesInNestedZip) throws IOException { ZipFile zipFile = new ZipFile(outerZipFileToVerify, PASSWORD); FileHeader fileHeader = zipFile.getFileHeader("inner.zip"); assertThat(fileHeader).isNotNull(); int actualNumberOfFilesInNestedZip = 0; try(InputStream inputStream = zipFile.getInputStream(fileHeader)) { try(ZipInputStream zipInputStream = new ZipInputStream(inputStream, PASSWORD)) { while (zipInputStream.getNextEntry() != null) { actualNumberOfFilesInNestedZip++; } } } assertThat(actualNumberOfFilesInNestedZip).isEqualTo(numberOfFilesInNestedZip); } private ZipParameters createZipParametersForNestedZip(EncryptionMethod encryptionMethod) { ZipParameters zipParameters = new ZipParameters(); if (encryptionMethod != null && !encryptionMethod.equals(EncryptionMethod.NONE)) { zipParameters.setEncryptFiles(true); zipParameters.setEncryptionMethod(encryptionMethod); } return zipParameters; } private void verifyNumberOfFilesInOutputFolder(File outputFolder, int numberOfExpectedFiles) { assertThat(outputFolder.listFiles()).hasSize(numberOfExpectedFiles); } private File getFileWithNameFrom(File outputFolder, String fileName) throws ZipException { List filesInFolder = FileUtils.getFilesInDirectoryRecursive(outputFolder, true, true); Optional file = filesInFolder.stream().filter(e -> e.getName().equals(fileName)).findFirst(); assertThat(file).isPresent(); return file.get(); } } zip4j-2.2.8/src/test/java/net/lingala/zip4j/MiscZipFileIT.java000066400000000000000000000450421357650161600240040ustar00rootroot00000000000000package net.lingala.zip4j; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.AesKeyStrength; import net.lingala.zip4j.model.enums.EncryptionMethod; import net.lingala.zip4j.testutils.TestUtils; import net.lingala.zip4j.testutils.ZipFileVerifier; import net.lingala.zip4j.util.FileUtils; import net.lingala.zip4j.util.InternalZipConstants; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; public class MiscZipFileIT extends AbstractIT { @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void testMergeSplitZipFilesMergesSuccessfully() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); List filesToAdd = new ArrayList<>(FILES_TO_ADD); filesToAdd.add(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); zipFile.createSplitZipFile(filesToAdd, new ZipParameters(), true, InternalZipConstants.MIN_SPLIT_LENGTH); File mergedZipFile = new File(temporaryFolder.getRoot().getPath() + InternalZipConstants.FILE_SEPARATOR + "merged_zip_file.zip"); zipFile.mergeSplitFiles(mergedZipFile); ZipFileVerifier.verifyZipFileByExtractingAllFiles(mergedZipFile, outputFolder, 4); } @Test public void testMergeSplitZipFilesWithAesEncryptionMergesSuccessfully() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); List filesToAdd = new ArrayList<>(FILES_TO_ADD); filesToAdd.add(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); zipFile.createSplitZipFile(filesToAdd, zipParameters, true, InternalZipConstants.MIN_SPLIT_LENGTH); File mergedZipFile = new File(temporaryFolder.getRoot().getPath() + InternalZipConstants.FILE_SEPARATOR + "merged_zip_file.zip"); zipFile.mergeSplitFiles(mergedZipFile); ZipFileVerifier.verifyZipFileByExtractingAllFiles(mergedZipFile, PASSWORD, outputFolder, 4); } @Test public void testGetFileHeadersReturnsEmptyListForNewZip() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); assertThat(zipFile.getFileHeaders()).isEmpty(); } @Test public void testGetFileHeadersReturnsAllHeaders() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); List fileHeaders = zipFile.getFileHeaders(); assertThat(fileHeaders).isNotNull(); assertThat(fileHeaders).hasSize(FILES_TO_ADD.size()); List fileNames = FILES_TO_ADD.stream().map(File::getName).collect(Collectors.toList()); verifyFileHeadersContainsFiles(fileHeaders, fileNames); } @Test public void testGetFileHeadersReturnsAllHeadersAfterAddingAnotherFile() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); zipFile.addFile(TestUtils.getTestFileFromResources("бореиская.txt")); List fileHeaders = zipFile.getFileHeaders(); assertThat(fileHeaders).isNotNull(); assertThat(fileHeaders).hasSize(FILES_TO_ADD.size() + 1); List fileNames = FILES_TO_ADD.stream().map(File::getName).collect(Collectors.toList()); fileNames.add("бореиская.txt"); verifyFileHeadersContainsFiles(fileHeaders, fileNames); } @Test public void testGetFileHeaderReturnsNullForNewZip() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); assertThat(zipFile.getFileHeader("SOME_NAME")).isNull(); } @Test public void testGetFileHeaderReturnsNullWhenFileDoesNotExist() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); assertThat(zipFile.getFileHeader("SOME_NAME")).isNull(); } @Test public void testGetFileHeaderReturnsFileHeaderSuccessfully() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); FileHeader fileHeader = zipFile.getFileHeader("sample_text_large.txt"); assertThat(fileHeader).isNotNull(); assertThat(fileHeader.getFileName()).isEqualTo("sample_text_large.txt"); } @Test public void testGetFileHeaderReturnsFileHeaderSuccessfullyAfterAddingNewFile() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); String fileToAdd = "file_PDF_1MB.pdf"; zipFile.addFile(TestUtils.getTestFileFromResources(fileToAdd)); FileHeader fileHeader = zipFile.getFileHeader(fileToAdd); assertThat(fileHeader).isNotNull(); assertThat(fileHeader.getFileName()).isEqualTo(fileToAdd); } @Test public void testIsEncryptedReturnsFalseForNewZip() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); assertThat(zipFile.isEncrypted()).isFalse(); } @Test public void testIsEncryptedReturnsFalseForNonEncryptedZip() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); assertThat(zipFile.isEncrypted()).isFalse(); } @Test public void testIsEncryptedReturnsTrueForStandardZipEncryption() throws ZipException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.ZIP_STANDARD, null); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); assertThat(zipFile.isEncrypted()).isTrue(); } @Test public void testIsEncryptedReturnsTrueForAesEncryption() throws ZipException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); assertThat(zipFile.isEncrypted()).isTrue(); } @Test public void testIsEncryptedReturnsTrueAfterAddingAnEncryptedFile() throws ZipException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD); zipFile.addFile(TestUtils.getTestFileFromResources("sample_text_large.txt"), zipParameters); assertThat(zipFile.isEncrypted()).isTrue(); } @Test public void testIsEncryptedReturnsFalseAfterRemovingAllEncryptedFiles() throws ZipException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD); zipFile.addFile(TestUtils.getTestFileFromResources("sample_text_large.txt"), zipParameters); zipFile.removeFile("sample_text_large.txt"); assertThat(zipFile.isEncrypted()).isFalse(); } @Test public void testIsSplitArchiveReturnsFalseForNewlyCreatedZip() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); assertThat(zipFile.isSplitArchive()).isFalse(); } @Test public void testIsSplitArchiveReturnsFalseForNonSplitZip() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); assertThat(zipFile.isSplitArchive()).isFalse(); } @Test public void testIsSplitArchiveReturnsTrueForSplitZip() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); List filesToAdd = new ArrayList<>(FILES_TO_ADD); filesToAdd.add(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); zipFile.createSplitZipFile(filesToAdd, new ZipParameters(), true, InternalZipConstants.MIN_SPLIT_LENGTH); assertThat(zipFile.isSplitArchive()).isTrue(); } @Test public void testIsSplitArchiveReturnsFalseWhenCreatedAsSplitZipButNotSplit() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.createSplitZipFile(FILES_TO_ADD, new ZipParameters(), true, InternalZipConstants.MIN_SPLIT_LENGTH); zipFile = new ZipFile(generatedZipFile); assertThat(zipFile.isSplitArchive()).isFalse(); } @Test public void testIsSplitArchiveReturnsFalseForMergedZipFile() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); List filesToAdd = new ArrayList<>(FILES_TO_ADD); filesToAdd.add(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); zipFile.createSplitZipFile(filesToAdd, new ZipParameters(), true, InternalZipConstants.MIN_SPLIT_LENGTH); File mergedZipFile = new File(temporaryFolder.getRoot().getPath() + InternalZipConstants.FILE_SEPARATOR + "merged.zip"); zipFile.mergeSplitFiles(mergedZipFile); zipFile = new ZipFile(mergedZipFile); assertThat(zipFile.isSplitArchive()).isFalse(); } @Test public void testSetComment() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); zipFile.setComment("SOME_COMMENT"); zipFile = new ZipFile(generatedZipFile); assertThat(zipFile.getComment()).isEqualTo("SOME_COMMENT"); } @Test public void testSetCommentWithChineseCharacters() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.setCharset(Charset.forName("GBK")); zipFile.addFiles(FILES_TO_ADD); zipFile.setComment("测试中文"); zipFile = new ZipFile(generatedZipFile); zipFile.setCharset(Charset.forName("GBK")); assertThat(zipFile.getComment()).isEqualTo("测试中文"); } @Test public void testSetCommentWithGermanCharacters() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); zipFile.setComment("ÄÜÖÖÜSDSDS"); zipFile = new ZipFile(generatedZipFile); assertThat(zipFile.getComment()).isEqualTo("ÄÜÖÖÜSDSDS"); } @Test public void testSetCommentForMergedZipRetainsComment() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); List filesToAdd = new ArrayList<>(FILES_TO_ADD); filesToAdd.add(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); zipFile.createSplitZipFile(filesToAdd, new ZipParameters(), true, InternalZipConstants.MIN_SPLIT_LENGTH); String comment = "SOME_COMMENT"; zipFile.setComment(comment); assertThat(zipFile.getComment()).isEqualTo(comment); File mergedZipFile = new File(temporaryFolder.getRoot().getPath() + InternalZipConstants.FILE_SEPARATOR + "merged.zip"); zipFile.mergeSplitFiles(mergedZipFile); zipFile = new ZipFile(mergedZipFile); assertThat(zipFile.getComment()).isEqualTo(comment); } @Test public void testSetCommentWithEmptyStringRemovesComment() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); String comment = "SOME_COMMENT"; zipFile.setComment(comment); assertThat(zipFile.getComment()).isEqualTo(comment); zipFile.setComment(""); assertThat(zipFile.getComment()).isEqualTo(""); //Make sure comment is empty and not null also when a new instance is now created zipFile = new ZipFile(generatedZipFile); assertThat(zipFile.getComment()).isEqualTo(""); } @Test public void testGetInputStreamWithoutEncryptionReturnsSuccessfully() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); try (InputStream inputStream = zipFile.getInputStream(zipFile.getFileHeader("sample_text_large.txt"))) { assertThat(inputStream).isNotNull(); verifyInputStream(inputStream, TestUtils.getTestFileFromResources("sample_text_large.txt")); } } @Test public void testGetInputStreamWithAesEncryptionReturnsSuccessfully() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD, zipParameters); try (InputStream inputStream = zipFile.getInputStream(zipFile.getFileHeader("sample_text_large.txt"))) { assertThat(inputStream).isNotNull(); verifyInputStream(inputStream, TestUtils.getTestFileFromResources("sample_text_large.txt")); } } @Test public void testGetInputStreamWithAesEncryptionAndSplitFileReturnsSuccessfully() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); List filesToAdd = new ArrayList<>(FILES_TO_ADD); filesToAdd.add(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); zipFile.createSplitZipFile(filesToAdd, zipParameters, true, InternalZipConstants.MIN_SPLIT_LENGTH); try (InputStream inputStream = zipFile.getInputStream(zipFile.getFileHeader("file_PDF_1MB.pdf"))) { assertThat(inputStream).isNotNull(); verifyInputStream(inputStream, TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); } //Check also with a new instance zipFile = new ZipFile(generatedZipFile, PASSWORD); try (InputStream inputStream = zipFile.getInputStream(zipFile.getFileHeader("file_PDF_1MB.pdf"))) { verifyInputStream(inputStream, TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); } } @Test public void testIsValidZipFileReturnsFalseForNonZipFile() { assertThat(new ZipFile(TestUtils.getTestFileFromResources("sample_text_large.txt")).isValidZipFile()).isFalse(); } @Test public void testIsValidZipFileReturnsFalseForNonExistingZip() { assertThat(new ZipFile("DoesNoExist").isValidZipFile()).isFalse(); } @Test public void testIsValidZipFileReturnsTrueForAValidZip() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); assertThat(zipFile.isValidZipFile()).isTrue(); } @Test public void testGetSplitZipFilesReturnsJustZipFileForNonSplit() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); List splitZipFiles = zipFile.getSplitZipFiles(); assertThat(splitZipFiles).hasSize(1); assertThat(splitZipFiles.get(0)).hasName(generatedZipFile.getName()); } @Test public void testGetSplitZipFilesReturnsAllSplitZipFiles() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); List filesToAdd = new ArrayList<>(FILES_TO_ADD); filesToAdd.add(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); zipFile.createSplitZipFile(filesToAdd, new ZipParameters(), true, InternalZipConstants.MIN_SPLIT_LENGTH); List splitZipFiles = zipFile.getSplitZipFiles(); assertThat(splitZipFiles).hasSize(15); verifySplitZipFileNames(splitZipFiles, 15, FileUtils.getZipFileNameWithoutExtension(generatedZipFile.getName())); } @Test public void testRenameZipFileAfterExtractionWithInputStreamSucceeds() throws IOException { new ZipFile(generatedZipFile).addFiles(FILES_TO_ADD); ZipFile zipFile = new ZipFile(generatedZipFile); FileHeader fileHeader = zipFile.getFileHeader("sample_text1.txt"); assertThat(fileHeader).isNotNull(); try(InputStream inputStream = zipFile.getInputStream(fileHeader)) { inputStream.read(new byte[100]); } File newFile = temporaryFolder.newFile("NEW_FILE_NAME.ZIP"); String oldFile = generatedZipFile.getPath(); if(TestUtils.isWindows()) { newFile.delete(); } assertThat(generatedZipFile.renameTo(newFile)).isTrue(); assertThat(new File(oldFile)).doesNotExist(); } @Test public void testZipSlipFix() throws Exception { ZipParameters zipParameters = new ZipParameters(); zipParameters.setFileNameInZip("../../bad.txt"); ZipFile zip = new ZipFile(generatedZipFile); zip.addFile(TestUtils.getTestFileFromResources("sample_text1.txt"), zipParameters); try { zip.extractAll(outputFolder.getAbsolutePath()); fail("zip4j is vulnerable for slip zip"); } catch (ZipException e) { assertThat(e).hasMessageStartingWith("illegal file name that breaks out of the target directory: "); } } @Test public void testUnzipFileZipSlipWithNotNormalizedTarget() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFiles(FILES_TO_ADD); zipFile.extractAll(new File(outputFolder.getPath(), ".." + InternalZipConstants.FILE_SEPARATOR + outputFolder.getName()).getAbsolutePath()); } @Test public void testExtractFileDeletesOutputFileWhenWrongPassword() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.ZIP_STANDARD, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFile(TestUtils.getTestFileFromResources("sample_text1.txt"), zipParameters); try { zipFile = new ZipFile(generatedZipFile, "WRONG_PASSWORD".toCharArray()); zipFile.extractAll(outputFolder.getCanonicalPath()); fail("Should throw an exception"); } catch (ZipException e) { assertThat(new File(outputFolder.getCanonicalPath() + "sample_text1.txt")).doesNotExist(); assertThat(e.getType()).isEqualTo(ZipException.Type.WRONG_PASSWORD); } } private void verifyInputStream(InputStream inputStream, File fileToCompareAgainst) throws IOException { File outputFile = temporaryFolder.newFile(); try (OutputStream outputStream = new FileOutputStream(outputFile)) { byte[] b = new byte[InternalZipConstants.BUFF_SIZE]; int readLen = -1; while ((readLen = inputStream.read(b)) != -1) { outputStream.write(b, 0, readLen); } } ZipFileVerifier.verifyFileContent(fileToCompareAgainst, outputFile); } private void verifySplitZipFileNames(List files, int expectedNumberOfZipFiles, String fileNameWithoutExtension) { assertThat(files).hasSize(expectedNumberOfZipFiles); for (int i = 0; i < expectedNumberOfZipFiles; i++) { File file = files.get(i); String fileExtensionPrefix = ".z0"; if (i >= 9) { fileExtensionPrefix = ".z"; } String expectedFileName = fileNameWithoutExtension + fileExtensionPrefix + (i + 1); if (i == expectedNumberOfZipFiles - 1) { expectedFileName = fileNameWithoutExtension + ".zip"; } assertThat(file).hasName(expectedFileName); } } } zip4j-2.2.8/src/test/java/net/lingala/zip4j/RemoveFilesFromZipIT.java000066400000000000000000000126621357650161600253570ustar00rootroot00000000000000package net.lingala.zip4j; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.AesKeyStrength; import net.lingala.zip4j.model.enums.EncryptionMethod; import net.lingala.zip4j.testutils.TestUtils; import net.lingala.zip4j.testutils.ZipFileVerifier; import net.lingala.zip4j.util.InternalZipConstants; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; public class RemoveFilesFromZipIT extends AbstractIT { @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void testRemoveFileAsFileNameThrowsExceptionWhenZipFileDoesNotExist() throws ZipException { String fileNameToRemove = "SOME_NAME"; expectedException.expect(ZipException.class); expectedException.expectMessage("could not find file header for file: " + fileNameToRemove); new ZipFile(generatedZipFile).removeFile(fileNameToRemove); } @Test public void testRemoveFileAsFileNameThrowsExceptionWhenFileDoesNotExistInZip() throws ZipException { String fileNameToRemove = "SOME_NAME"; expectedException.expect(ZipException.class); expectedException.expectMessage("could not find file header for file: " + fileNameToRemove); ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); zipFile.removeFile(fileNameToRemove); } @Test public void testRemoveFileAsFileNameThrowsExceptionForSplitArchive() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); List filesToAdd = new ArrayList<>(FILES_TO_ADD); filesToAdd.add(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); zipFile.createSplitZipFile(filesToAdd, new ZipParameters(), true, InternalZipConstants.MIN_SPLIT_LENGTH); expectedException.expect(ZipException.class); expectedException.expectMessage("Zip file format does not allow updating split/spanned files"); zipFile.removeFile("file_PDF_1MB.pdf"); } @Test public void testRemoveFileAsFileNameRemovesSuccessfully() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); zipFile.addFiles(FILES_TO_ADD); zipFile.removeFile("sample_text1.txt"); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, 2); verifyZipFileDoesNotContainFile(generatedZipFile, "sample_text1.txt"); } @Test public void testRemoveFileAsFileNameWithCharsetCp949RemovesSuccessfully() throws IOException { ZipFile zipFile = new ZipFile(generatedZipFile); List filesToAdd = new ArrayList<>(); filesToAdd.add(TestUtils.getTestFileFromResources("가나다.abc")); filesToAdd.add(TestUtils.getTestFileFromResources("sample_text1.txt")); zipFile.setCharset(CHARSET_CP_949); zipFile.addFiles(filesToAdd); zipFile.removeFile("sample_text1.txt"); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, null, outputFolder, 1, true, CHARSET_CP_949); verifyZipFileDoesNotContainFile(generatedZipFile, "sample_text1.txt"); } @Test public void testRemoveFileAsFileNameRemovesSuccessfullyWithFolderNameInPath() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFolder(TestUtils.getTestFileFromResources(""), zipParameters); zipFile.removeFile("test-files/öüäöäö/asöäööl"); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 12); verifyZipFileDoesNotContainFile(generatedZipFile, "test-files/öüäöäö/asöäööl"); } @Test public void testRemoveFileAsFileHeaderThrowsExceptionForSplitArchive() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); List filesToAdd = new ArrayList<>(FILES_TO_ADD); filesToAdd.add(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")); zipFile.createSplitZipFile(filesToAdd, new ZipParameters(), true, InternalZipConstants.MIN_SPLIT_LENGTH); expectedException.expect(ZipException.class); expectedException.expectMessage("Zip file format does not allow updating split/spanned files"); zipFile.removeFile(zipFile.getFileHeader("file_PDF_1MB.pdf")); } @Test public void testRemoveFileAsFileHeaderRemovesSuccessfully() throws IOException { ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256); ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD); zipFile.addFolder(TestUtils.getTestFileFromResources(""), zipParameters); zipFile.removeFile(zipFile.getFileHeader("test-files/sample_directory/favicon.ico")); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 12); verifyZipFileDoesNotContainFile(generatedZipFile, "sample_directory/favicon.ico"); } private void verifyZipFileDoesNotContainFile(File generatedZipFile, String fileNameToCheck) throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); Optional fileHeader = zipFile.getFileHeaders().stream() .filter(e -> e.getFileName().equals(fileNameToCheck)).findFirst(); assertThat(fileHeader).isNotPresent(); } } zip4j-2.2.8/src/test/java/net/lingala/zip4j/ZipFileTest.java000066400000000000000000000435451357650161600236010ustar00rootroot00000000000000package net.lingala.zip4j; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.progress.ProgressMonitor; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.util.Collections; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.when; // Tests only failure scenarios. All other tests are covered in the corresponding Integration test public class ZipFileTest { private File sourceZipFile; private ZipFile zipFile; @Rule public ExpectedException expectedException = ExpectedException.none(); @Before public void setup() { sourceZipFile = mockFile(false); zipFile = new ZipFile(sourceZipFile); } @Test public void testCreateZipFileThrowsExceptionWhenZipFileExists() throws ZipException { reset(sourceZipFile); when(sourceZipFile.exists()).thenReturn(true); expectedException.expect(ZipException.class); expectedException.expectMessage("zip file: " + sourceZipFile + " already exists. " + "To add files to existing zip file use addFile method"); zipFile.createSplitZipFile(Collections.emptyList(), new ZipParameters(), true, 10000); } @Test public void testCreateZipFileThrowsExceptionWhenFileListIsNull() throws ZipException { expectedException.expect(ZipException.class); expectedException.expectMessage("input file List is null, cannot create zip file"); zipFile.createSplitZipFile(null, new ZipParameters(), true, 10000); } @Test public void testCreateZipFileThrowsExceptionWhenFileListIsEmpty() throws ZipException { expectedException.expect(ZipException.class); expectedException.expectMessage("input file List is null, cannot create zip file"); zipFile.createSplitZipFile(Collections.emptyList(), new ZipParameters(), true, 10000); } @Test public void testCreateZipFileFromFolderThrowsExceptionWheFolderIsNull() throws ZipException { expectedException.expect(ZipException.class); expectedException.expectMessage("folderToAdd is null, cannot create zip file from folder"); zipFile.createSplitZipFileFromFolder(null, new ZipParameters(), true, 10000); } @Test public void testCreateZipFileFromFolderThrowsExceptionWhenParametersAreNull() throws ZipException { File folderToAdd = mockFile(true); expectedException.expect(ZipException.class); expectedException.expectMessage("input parameters are null, cannot create zip file from folder"); zipFile.createSplitZipFileFromFolder(folderToAdd, null, true, 10000); } @Test public void testCreateZipFileFromFolderThrowsExceptionWhenZipFileExists() throws ZipException { reset(sourceZipFile); when(sourceZipFile.exists()).thenReturn(true); File folderToAdd = mockFile(true); expectedException.expect(ZipException.class); expectedException.expectMessage("zip file: " + sourceZipFile + " already exists. To add files to existing zip file use addFolder method"); zipFile.createSplitZipFileFromFolder(folderToAdd, new ZipParameters(), true, 10000); } @Test public void testAddFileAsStringThrowsExceptionWhenFileIsNull() throws ZipException { expectedException.expectMessage("file to add is null or empty"); expectedException.expect(ZipException.class); zipFile.addFile((String) null); } @Test public void testAddFileAsStringThrowsExceptionWhenFileIsEmpty() throws ZipException { expectedException.expectMessage("file to add is null or empty"); expectedException.expect(ZipException.class); zipFile.addFile(" "); } @Test public void testAddFileAsStringWithParametersThrowsExceptionWhenFileIsNull() throws ZipException { expectedException.expectMessage("file to add is null or empty"); expectedException.expect(ZipException.class); zipFile.addFile((String) null, new ZipParameters()); } @Test public void testAddFileAsStringWithParametersThrowsExceptionWhenFileIsEmpty() throws ZipException { expectedException.expectMessage("file to add is null or empty"); expectedException.expect(ZipException.class); zipFile.addFile("", new ZipParameters()); } @Test public void testAddFileAsFileThrowsExceptionWhenParametersIsNull() throws ZipException { File fileToAdd = mockFile(true); expectedException.expect(ZipException.class); expectedException.expectMessage("input parameters are null"); zipFile.addFile(fileToAdd, null); } @Test public void testAddFileAsFileThrowsExceptionWhenProgressMonitorStateIsBusy() throws ZipException { File fileToAdd = mockFile(true); zipFile.getProgressMonitor().setState(ProgressMonitor.State.BUSY); expectedException.expect(ZipException.class); expectedException.expectMessage("invalid operation - Zip4j is in busy state"); zipFile.addFile(fileToAdd, new ZipParameters()); } @Test public void testAddFilesThrowsExceptionWhenInputFilesIsNull() throws ZipException { expectedException.expect(ZipException.class); expectedException.expectMessage("input file List is null or empty"); zipFile.addFiles(null); } @Test public void testAddFilesThrowsExceptionWhenInputFilesIsEmpty() throws ZipException { expectedException.expect(ZipException.class); expectedException.expectMessage("input file List is null or empty"); zipFile.addFiles(Collections.emptyList()); } @Test public void testAddFilesThrowsExceptionWhenProgressMonitorStateIsBusy() throws ZipException { zipFile.getProgressMonitor().setState(ProgressMonitor.State.BUSY); expectedException.expect(ZipException.class); expectedException.expectMessage("invalid operation - Zip4j is in busy state"); zipFile.addFiles(Collections.singletonList(new File("Some_File"))); } @Test public void testAddFilesWithParametersThrowsExceptionWhenInputFilesIsNull() throws ZipException { expectedException.expect(ZipException.class); expectedException.expectMessage("input file List is null or empty"); zipFile.addFiles(null, new ZipParameters()); } @Test public void testAddFilesWithParametersThrowsExceptionWhenInputFilesIsEmpty() throws ZipException { expectedException.expect(ZipException.class); expectedException.expectMessage("input file List is null or empty"); zipFile.addFiles(Collections.emptyList(), new ZipParameters()); } @Test public void testAddFilesWithParametersThrowsExceptionWhenParametersAreNull() throws ZipException { expectedException.expect(ZipException.class); expectedException.expectMessage("input parameters are null"); zipFile.addFiles(Collections.singletonList(new File("Some_File")), null); } @Test public void testAddFilesWithParametersThrowsExceptionWhenProgressMonitorStateIsBusy() throws ZipException { zipFile.getProgressMonitor().setState(ProgressMonitor.State.BUSY); expectedException.expect(ZipException.class); expectedException.expectMessage("invalid operation - Zip4j is in busy state"); zipFile.addFiles(Collections.singletonList(new File("Some_File")), new ZipParameters()); } @Test public void testAddFolderThrowsExceptionWhenFolderIsNull() throws ZipException { expectedException.expect(ZipException.class); expectedException.expectMessage("input path is null, cannot add folder to zip file"); zipFile.addFolder(null); } @Test public void testAddFolderThrowsExceptionWhenFolderDoesNotExist() throws ZipException { File folderToAdd = mockFile(false); expectedException.expect(ZipException.class); expectedException.expectMessage("folder does not exist"); zipFile.addFolder(folderToAdd); } @Test public void testAddFolderThrowsExceptionWhenFolderNotADirectory() throws ZipException { File folderToAdd = mockFile(true); when(folderToAdd.isDirectory()).thenReturn(false); expectedException.expect(ZipException.class); expectedException.expectMessage("input folder is not a directory"); zipFile.addFolder(folderToAdd); } @Test public void testAddFolderThrowsExceptionWhenCannotReadFolder() throws ZipException { File folderToAdd = mockFile(true); when(folderToAdd.isDirectory()).thenReturn(true); when(folderToAdd.canRead()).thenReturn(false); expectedException.expect(ZipException.class); expectedException.expectMessage("cannot read input folder"); zipFile.addFolder(folderToAdd); } @Test public void testAddFolderWithParametersThrowsExceptionWhenFolderIsNull() throws ZipException { expectedException.expect(ZipException.class); expectedException.expectMessage("input path is null, cannot add folder to zip file"); zipFile.addFolder(null, new ZipParameters()); } @Test public void testAddFolderWithParametersThrowsExceptionWhenFolderDoesNotExist() throws ZipException { File folderToAdd = mockFile(false); expectedException.expect(ZipException.class); expectedException.expectMessage("folder does not exist"); zipFile.addFolder(folderToAdd, new ZipParameters()); } @Test public void testAddFolderWithParametersThrowsExceptionWhenFolderNotADirectory() throws ZipException { File folderToAdd = mockFile(true); when(folderToAdd.isDirectory()).thenReturn(false); expectedException.expect(ZipException.class); expectedException.expectMessage("input folder is not a directory"); zipFile.addFolder(folderToAdd, new ZipParameters()); } @Test public void testAddFolderWithParametersThrowsExceptionWhenCannotReadFolder() throws ZipException { File folderToAdd = mockFile(true); when(folderToAdd.isDirectory()).thenReturn(true); when(folderToAdd.canRead()).thenReturn(false); expectedException.expect(ZipException.class); expectedException.expectMessage("cannot read input folder"); zipFile.addFolder(folderToAdd, new ZipParameters()); } @Test public void testAddFolderWithParametersThrowsExceptionWhenInputParametersAreNull() throws ZipException { File folderToAdd = mockFile(true); when(folderToAdd.isDirectory()).thenReturn(true); when(folderToAdd.canRead()).thenReturn(true); expectedException.expect(ZipException.class); expectedException.expectMessage("input parameters are null, cannot add folder to zip file"); zipFile.addFolder(folderToAdd, null); } @Test public void testAddStreamThrowsExceptionWhenInputStreamIsNull() throws ZipException { expectedException.expectMessage("inputstream is null, cannot add file to zip"); expectedException.expect(ZipException.class); zipFile.addStream(null, new ZipParameters()); } @Test public void testAddStreamThrowsExceptionWhenParametersIsNull() throws ZipException { expectedException.expectMessage("zip parameters are null"); expectedException.expect(ZipException.class); zipFile.addStream(new ByteArrayInputStream(new byte[2]), null); } @Test public void testExtractAllThrowsExceptionWhenDestinationIsNull() throws ZipException { expectedException.expectMessage("output path is null or invalid"); expectedException.expect(ZipException.class); zipFile.extractAll(null); } @Test public void testExtractAllThrowsExceptionWhenDestinationIsEmpty() throws ZipException { expectedException.expectMessage("output path is null or invalid"); expectedException.expect(ZipException.class); zipFile.extractAll(" "); } @Test public void testExtractFileWithFileHeaderWhenFileHeaderIsNullThrowsException() throws ZipException { expectedException.expectMessage("input file header is null, cannot extract file"); expectedException.expect(ZipException.class); zipFile.extractFile((FileHeader) null, "SOME_DESTINATION"); } @Test public void testExtractFileWithFileHeaderWhenDestinationIsNullThrowsException() throws ZipException { expectedException.expectMessage("destination path is empty or null, cannot extract file"); expectedException.expect(ZipException.class); zipFile.extractFile(new FileHeader(), null); } @Test public void testExtractFileWithFileHeaderWhenDestinationIsEmptyThrowsException() throws ZipException { expectedException.expectMessage("destination path is empty or null, cannot extract file"); expectedException.expect(ZipException.class); zipFile.extractFile(new FileHeader(), ""); } @Test public void testExtractFileWithFileHeaderWhenBusyStateThrowsException() throws ZipException { zipFile.getProgressMonitor().setState(ProgressMonitor.State.BUSY); expectedException.expectMessage("invalid operation - Zip4j is in busy state"); expectedException.expect(ZipException.class); zipFile.extractFile(new FileHeader(), "SOME_DESTINATION"); } @Test public void testExtractFileWithFileNameThrowsExceptionWhenNameIsNull() throws ZipException { expectedException.expectMessage("file to extract is null or empty, cannot extract file"); expectedException.expect(ZipException.class); zipFile.extractFile((String) null, "SOME_DESTINATION"); } @Test public void testExtractFileWithFileNameThrowsExceptionWhenNameIsEmpty() throws ZipException { expectedException.expectMessage("file to extract is null or empty, cannot extract file"); expectedException.expect(ZipException.class); zipFile.extractFile("", "SOME_DESTINATION"); } @Test public void testExtractFileWithNewFileNameThrowsExceptionWhenNameIsNull() throws ZipException { expectedException.expectMessage("file to extract is null or empty, cannot extract file"); expectedException.expect(ZipException.class); zipFile.extractFile((String) null, "SOME_DESTINATION", "NEW_FILE_NAME"); } @Test public void testExtractFileWithNewFileNameThrowsExceptionWhenNameIsEmpty() throws ZipException { expectedException.expectMessage("file to extract is null or empty, cannot extract file"); expectedException.expect(ZipException.class); zipFile.extractFile("", "SOME_DESTINATION"); } @Test public void testGetFileHeadersReturnsEmptyListWhenZipFileDoesNotExist() throws ZipException { File mockFile = mockFile(false); ZipFile zipFile = new ZipFile(mockFile); assertThat(zipFile.getFileHeaders()).isEmpty(); } @Test public void testGetFileHeaderThrowsExceptionWhenFileNameIsNull() throws ZipException { expectedException.expectMessage("input file name is emtpy or null, cannot get FileHeader"); expectedException.expect(ZipException.class); zipFile.getFileHeader(null); } @Test public void testGetFileHeaderThrowsExceptionWhenFileNameIsEmpty() throws ZipException { expectedException.expectMessage("input file name is emtpy or null, cannot get FileHeader"); expectedException.expect(ZipException.class); zipFile.getFileHeader(""); } @Test public void testRemoveFileWithFileNameThrowsExceptionWhenFileNameIsNull() throws ZipException { expectedException.expectMessage("file name is empty or null, cannot remove file"); expectedException.expect(ZipException.class); zipFile.removeFile((String) null); } @Test public void testRemoveFileWithFileNameThrowsExceptionWhenFileNameIsEmpty() throws ZipException { expectedException.expectMessage("file name is empty or null, cannot remove file"); expectedException.expect(ZipException.class); zipFile.removeFile(""); } @Test public void testRemoveFileWithFileHeaderThrowsExceptionWhenFileNameIsNull() throws ZipException { expectedException.expectMessage("input file header is null, cannot remove file"); expectedException.expect(ZipException.class); zipFile.removeFile((FileHeader) null); } @Test public void testMergeSplitFilesWhenOutputFileIsNullThrowsException() throws ZipException { expectedException.expectMessage("outputZipFile is null, cannot merge split files"); expectedException.expect(ZipException.class); zipFile.mergeSplitFiles(null); } @Test public void testMergeSplitFilesWhenOutputFileDoesAlreadyExistsThrowsException() throws ZipException { expectedException.expectMessage("output Zip File already exists"); expectedException.expect(ZipException.class); File mergedZipFile = mockFile(true); zipFile.mergeSplitFiles(mergedZipFile); } @Test public void testSetCommentWhenCommentIsNullThrowsException() throws ZipException { expectedException.expectMessage("input comment is null, cannot update zip file"); expectedException.expect(ZipException.class); zipFile.setComment(null); } @Test public void testSetCommentWhenZipFileDoesNotExistsThrowsException() throws ZipException { expectedException.expectMessage("zip file does not exist, cannot set comment for zip file"); expectedException.expect(ZipException.class); zipFile.setComment("Some comment"); } @Test public void testGetCommentWhenZipFileDoesNotExistThrowsException() throws ZipException { expectedException.expectMessage("zip file does not exist, cannot read comment"); expectedException.expect(ZipException.class); zipFile.getComment(); } @Test public void testGetInputStreamWhenFileHeaderIsNullThrowsException() throws IOException { expectedException.expectMessage("FileHeader is null, cannot get InputStream"); expectedException.expect(ZipException.class); zipFile.getInputStream(null); } @Test public void testSetRunInThreadSetsFlag() { zipFile.setRunInThread(true); assertThat(zipFile.isRunInThread()).isTrue(); zipFile.setRunInThread(false); assertThat(zipFile.isRunInThread()).isFalse(); } @Test public void testGetFileReturnsValidFile() { assertThat(zipFile.getFile()).isSameAs(sourceZipFile); } @Test public void testToString() { assertThat(zipFile.toString()).isEqualTo("SOME_PATH"); } private File mockFile(boolean fileExists) { File file = mock(File.class); when(file.exists()).thenReturn(fileExists); when(file.toString()).thenReturn("SOME_PATH"); return file; } }zip4j-2.2.8/src/test/java/net/lingala/zip4j/ZipFileZip64IT.java000066400000000000000000000067541357650161600240340ustar00rootroot00000000000000package net.lingala.zip4j; import net.lingala.zip4j.headers.HeaderReader; import net.lingala.zip4j.io.outputstream.ZipOutputStream; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.model.enums.RandomAccessFileMode; import net.lingala.zip4j.testutils.RandomInputStream; import net.lingala.zip4j.testutils.SlowTests; import net.lingala.zip4j.testutils.ZipFileVerifier; import net.lingala.zip4j.util.InternalZipConstants; import org.junit.Test; import org.junit.experimental.categories.Category; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import static org.assertj.core.api.Assertions.assertThat; @Category(SlowTests.class) public class ZipFileZip64IT extends AbstractIT { private byte[] readBuffer = new byte[4096]; @Test public void testZip64WithSingleLargeZipEntry() throws IOException { long entrySize = InternalZipConstants.ZIP_64_SIZE_LIMIT + 1; ZipParameters zipParameters = new ZipParameters(); zipParameters.setCompressionMethod(CompressionMethod.STORE); zipParameters.setEntrySize(entrySize); createZip64FileWithEntries(1, entrySize, zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, null, outputFolder, 1, false); verifyZip64HeadersPresent(); } @Test public void testZip64WithCentralDirectoryOffsetGreaterThanZip64Limit() throws IOException { long eachEntrySize = (InternalZipConstants.ZIP_64_SIZE_LIMIT / 2) + 100; ZipParameters zipParameters = new ZipParameters(); zipParameters.setEntrySize(eachEntrySize); createZip64FileWithEntries(3, eachEntrySize, zipParameters); ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, null, outputFolder, 3, false); verifyZip64HeadersPresent(); } @Test public void testZip64WithNumberOfEntriesGreaterThan70k() throws IOException { long eachEntrySize = 100; ZipParameters zipParameters = new ZipParameters(); zipParameters.setEntrySize(eachEntrySize); createZip64FileWithEntries(70000, eachEntrySize, zipParameters); ZipFile zipFile = new ZipFile(generatedZipFile); assertThat(zipFile.getFileHeaders()).hasSize(70000); verifyZip64HeadersPresent(); } private void verifyZip64HeadersPresent() throws IOException { HeaderReader headerReader = new HeaderReader(); ZipModel zipModel = headerReader.readAllHeaders(new RandomAccessFile(generatedZipFile, RandomAccessFileMode.READ.getValue()), InternalZipConstants.CHARSET_UTF_8); assertThat(zipModel.getZip64EndOfCentralDirectoryLocator()).isNotNull(); assertThat(zipModel.getZip64EndOfCentralDirectoryRecord()).isNotNull(); assertThat(zipModel.isZip64Format()).isTrue(); } private void createZip64FileWithEntries(int numberOfEntries, long eachEntrySize, ZipParameters zipParameters) throws IOException { int readLen; try(ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(generatedZipFile))) { for (int i = 0; i < numberOfEntries; i++) { zipParameters.setFileNameInZip("FILE_" + i); zipOutputStream.putNextEntry(zipParameters); try(InputStream inputStream = new RandomInputStream(eachEntrySize)) { while ((readLen = inputStream.read(readBuffer)) != -1) { zipOutputStream.write(readBuffer, 0, readLen); } } zipOutputStream.closeEntry(); } } } } zip4j-2.2.8/src/test/java/net/lingala/zip4j/crypto/000077500000000000000000000000001357650161600220415ustar00rootroot00000000000000zip4j-2.2.8/src/test/java/net/lingala/zip4j/crypto/PBKDF2/000077500000000000000000000000001357650161600227515ustar00rootroot00000000000000zip4j-2.2.8/src/test/java/net/lingala/zip4j/crypto/PBKDF2/BinToolsTest.java000066400000000000000000000024501357650161600262060ustar00rootroot00000000000000package net.lingala.zip4j.crypto.PBKDF2; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import static org.assertj.core.api.Assertions.assertThat; public class BinToolsTest { @Rule public final ExpectedException expectedException = ExpectedException.none(); @Test public void bin2hexForValidInputReturnsValidHex() { final byte[] b = {(byte) 112}; assertThat(BinTools.bin2hex(b)).isEqualTo("70"); } @Test public void bin2hexForEmptyInputReturnsEmptyString() { assertThat(BinTools.bin2hex(new byte[]{})).isEqualTo(""); } @Test public void bin2hexForNullInputReturnsEmptyString() { assertThat(BinTools.bin2hex(null)).isEqualTo(""); } @Test public void bin2hexForNullInputReturnsEmptyArray() { assertThat(BinTools.hex2bin(null)).isEqualTo(new byte[]{}); } @Test public void hex2binForInvalidInputOutputIllegalArgumentException() { expectedException.expect(IllegalArgumentException.class); BinTools.hex2bin("foo"); } @Test public void hex2binCharacterInputOutputPositive() { assertThat(BinTools.hex2bin('A')).isEqualTo(10); } @Test public void hex2binInvalidInputOutputIllegalArgumentException() { expectedException.expect(IllegalArgumentException.class); BinTools.hex2bin('\u013c'); } } zip4j-2.2.8/src/test/java/net/lingala/zip4j/exception/000077500000000000000000000000001357650161600225175ustar00rootroot00000000000000zip4j-2.2.8/src/test/java/net/lingala/zip4j/exception/ZipExceptionTest.java000066400000000000000000000037041357650161600266470ustar00rootroot00000000000000package net.lingala.zip4j.exception; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class ZipExceptionTest { @Test public void testZipExceptionForErrorMessage() { String message = "SOME_MESSAGE"; ZipException zipException = new ZipException(message); assertThat(zipException.getMessage()).isEqualTo(message); assertThat(zipException.getType()).isEqualTo(ZipException.Type.UNKNOWN); } @Test public void testZipExceptionWithRootException() { String errorMessage = "SOME_MESSAGE"; Exception rootException = new RuntimeException(errorMessage); ZipException zipException = new ZipException(rootException); assertThat(zipException.getCause()).isEqualTo(rootException); assertThat(zipException.getCause().getMessage()).isEqualTo(errorMessage); assertThat(zipException.getType()).isEqualTo(ZipException.Type.UNKNOWN); assertThat(zipException.getMessage()).contains(errorMessage); } @Test public void testZipExceptionWithMessageAndRootException() { String errorMessage = "SOME_MESSAGE"; String rootErrorMessage = "ROOT_ERROR_MESSAGE"; Exception rootException = new RuntimeException(rootErrorMessage); ZipException zipException = new ZipException(errorMessage, rootException); assertThat(zipException.getCause()).isEqualTo(rootException); assertThat(zipException.getCause().getMessage()).isEqualTo(rootErrorMessage); assertThat(zipException.getType()).isEqualTo(ZipException.Type.UNKNOWN); assertThat(zipException.getMessage()).isEqualTo(errorMessage); } @Test public void testZipExceptionWithMessageAndExceptionType() { String errorMessage = "SOME_MESSAGE"; ZipException.Type exceptionType = ZipException.Type.WRONG_PASSWORD; ZipException zipException = new ZipException(errorMessage, exceptionType); assertThat(zipException.getType()).isEqualTo(exceptionType); assertThat(zipException.getMessage()).isEqualTo(errorMessage); } }zip4j-2.2.8/src/test/java/net/lingala/zip4j/headers/000077500000000000000000000000001357650161600221345ustar00rootroot00000000000000zip4j-2.2.8/src/test/java/net/lingala/zip4j/headers/FileHeaderFactoryTest.java000066400000000000000000000446551357650161600271750ustar00rootroot00000000000000package net.lingala.zip4j.headers; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.AESExtraDataRecord; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.LocalFileHeader; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.AesKeyStrength; import net.lingala.zip4j.model.enums.AesVersion; import net.lingala.zip4j.model.enums.CompressionLevel; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.model.enums.EncryptionMethod; import net.lingala.zip4j.util.InternalZipConstants; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.nio.charset.Charset; import static net.lingala.zip4j.util.BitUtils.isBitSet; import static net.lingala.zip4j.util.Zip4jUtil.javaToDosTime; import static org.assertj.core.api.Assertions.assertThat; public class FileHeaderFactoryTest { private static final String FILE_NAME_IN_ZIP = "filename.txt"; private static final long ENTRY_CRC = 2323L; private FileHeaderFactory fileHeaderFactory = new FileHeaderFactory(); @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void testGenerateFileHeaderWithoutFileNameThrowsException() throws ZipException { expectedException.expect(ZipException.class); expectedException.expectMessage("fileNameInZip is null or empty"); fileHeaderFactory.generateFileHeader(new ZipParameters(), false, 0, InternalZipConstants.CHARSET_UTF_8); } @Test public void testGenerateFileHeaderDefaults() throws ZipException { FileHeader fileHeader = fileHeaderFactory.generateFileHeader(generateZipParameters(), false, 0, InternalZipConstants.CHARSET_UTF_8); assertThat(fileHeader).isNotNull(); assertThat(fileHeader.getCompressionMethod()).isEqualTo(CompressionMethod.DEFLATE); verifyCompressionLevelGridForDeflate(CompressionLevel.NORMAL, fileHeader.getGeneralPurposeFlag()[0]); assertThat(fileHeader.isEncrypted()).isFalse(); assertThat(fileHeader.getEncryptionMethod()).isEqualTo(EncryptionMethod.NONE); assertThat(fileHeader.getAesExtraDataRecord()).isNull(); assertThat(fileHeader.getLastModifiedTime()).isNotZero(); assertThat(fileHeader.getCompressedSize()).isEqualTo(0); assertThat(fileHeader.getUncompressedSize()).isEqualTo(0); } @Test public void testGenerateFileHeaderForStoreWithoutEncryption() throws ZipException { ZipParameters zipParameters = generateZipParameters(); zipParameters.setCompressionMethod(CompressionMethod.STORE); FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8); verifyFileHeader(fileHeader, zipParameters, false, 0, false); } @Test public void testGenerateFileHeaderWhenEncryptingWithoutMethodThrowsException() throws ZipException { expectedException.expect(ZipException.class); expectedException.expectMessage("Encryption method has to be set when encryptFiles flag is set in zip parameters"); ZipParameters zipParameters = generateZipParameters(); zipParameters.setEncryptFiles(true); fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8); } @Test public void testGenerateFileHeaderWithStandardEncryption() throws ZipException { ZipParameters zipParameters = generateZipParameters(); zipParameters.setEncryptFiles(true); zipParameters.setEncryptionMethod(EncryptionMethod.ZIP_STANDARD); FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8); verifyFileHeader(fileHeader, zipParameters, false, 0, false); } @Test public void testGenerateFileHeaderWithAesEncryptionWithNullKeyStrengthThrowsException() throws ZipException { expectedException.expect(ZipException.class); expectedException.expectMessage("invalid AES key strength"); ZipParameters zipParameters = generateZipParameters(); zipParameters.setEncryptFiles(true); zipParameters.setEncryptionMethod(EncryptionMethod.AES); zipParameters.setAesKeyStrength(null); fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8); } @Test public void testGenerateFileHeaderWithAesEncryptionWithoutKeyStrengthUsesDefault() throws ZipException { ZipParameters zipParameters = generateZipParameters(); zipParameters.setEncryptFiles(true); zipParameters.setEncryptionMethod(EncryptionMethod.AES); FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8); verifyFileHeader(fileHeader, zipParameters, false, 0, true); verifyAesExtraDataRecord(fileHeader.getAesExtraDataRecord(), AesKeyStrength.KEY_STRENGTH_256, CompressionMethod.DEFLATE, AesVersion.TWO); } @Test public void testGenerateFileHeaderWithAesEncryptionWithKeyStrength128() throws ZipException { ZipParameters zipParameters = generateZipParameters(); zipParameters.setEncryptFiles(true); zipParameters.setEncryptionMethod(EncryptionMethod.AES); zipParameters.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_128); FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8); verifyFileHeader(fileHeader, zipParameters, false, 0, true); verifyAesExtraDataRecord(fileHeader.getAesExtraDataRecord(), AesKeyStrength.KEY_STRENGTH_128, CompressionMethod.DEFLATE, AesVersion.TWO); } @Test public void testGenerateFileHeaderWithAesEncryptionWithKeyStrength192() throws ZipException { ZipParameters zipParameters = generateZipParameters(); zipParameters.setEncryptFiles(true); zipParameters.setEncryptionMethod(EncryptionMethod.AES); zipParameters.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_192); FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8); verifyFileHeader(fileHeader, zipParameters, false, 0, true); verifyAesExtraDataRecord(fileHeader.getAesExtraDataRecord(), AesKeyStrength.KEY_STRENGTH_192, CompressionMethod.DEFLATE, AesVersion.TWO); } @Test public void testGenerateFileHeaderWithAesEncryptionWithKeyStrength256() throws ZipException { ZipParameters zipParameters = generateZipParameters(); zipParameters.setEncryptFiles(true); zipParameters.setEncryptionMethod(EncryptionMethod.AES); zipParameters.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_256); FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8); verifyFileHeader(fileHeader, zipParameters, false, 0, true); verifyAesExtraDataRecord(fileHeader.getAesExtraDataRecord(), AesKeyStrength.KEY_STRENGTH_256, CompressionMethod.DEFLATE, AesVersion.TWO); } @Test public void testGenerateFileHeaderWithAesEncryptionVersionV1() throws ZipException { ZipParameters zipParameters = generateZipParameters(); zipParameters.setEncryptFiles(true); zipParameters.setEncryptionMethod(EncryptionMethod.AES); zipParameters.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_256); zipParameters.setAesVersion(AesVersion.ONE); FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8); verifyFileHeader(fileHeader, zipParameters, false, 0, true); verifyAesExtraDataRecord(fileHeader.getAesExtraDataRecord(), AesKeyStrength.KEY_STRENGTH_256, CompressionMethod.DEFLATE, AesVersion.ONE); } @Test public void testGenerateFileHeaderWithAesEncryptionWithNullVersionUsesV2() throws ZipException { ZipParameters zipParameters = generateZipParameters(); zipParameters.setEncryptFiles(true); zipParameters.setEncryptionMethod(EncryptionMethod.AES); zipParameters.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_256); zipParameters.setAesVersion(null); FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8); verifyFileHeader(fileHeader, zipParameters, false, 0, true); verifyAesExtraDataRecord(fileHeader.getAesExtraDataRecord(), AesKeyStrength.KEY_STRENGTH_256, CompressionMethod.DEFLATE, AesVersion.TWO); } @Test public void testGenerateFileHeaderWithLastModifiedFileTime() throws ZipException { long lastModifiedFileTime = System.currentTimeMillis(); ZipParameters zipParameters = generateZipParameters(); zipParameters.setLastModifiedFileTime(lastModifiedFileTime); FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8); assertThat(fileHeader.getLastModifiedTime()).isEqualTo(javaToDosTime(zipParameters.getLastModifiedFileTime())); } @Test public void testGenerateFileHeaderWithCompressionLevelMaximum() throws ZipException { ZipParameters zipParameters = generateZipParameters(); zipParameters.setCompressionLevel(CompressionLevel.MAXIMUM); FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8); verifyCompressionLevelGridForDeflate(CompressionLevel.MAXIMUM, fileHeader.getGeneralPurposeFlag()[0]); } @Test public void testGenerateFileHeaderWithCompressionLevelFast() throws ZipException { ZipParameters zipParameters = generateZipParameters(); zipParameters.setCompressionLevel(CompressionLevel.FAST); FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8); verifyCompressionLevelGridForDeflate(CompressionLevel.FAST, fileHeader.getGeneralPurposeFlag()[0]); } @Test public void testGenerateFileHeaderWithCompressionLevelFastest() throws ZipException { ZipParameters zipParameters = generateZipParameters(); zipParameters.setCompressionLevel(CompressionLevel.FASTEST); FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8); verifyCompressionLevelGridForDeflate(CompressionLevel.FASTEST, fileHeader.getGeneralPurposeFlag()[0]); } @Test public void testGenerateFileHeaderWithCorrectCharset() throws ZipException { FileHeader fileHeader = fileHeaderFactory.generateFileHeader(generateZipParameters(), false, 0, Charset.forName("Cp949")); assertThat(isBitSet(fileHeader.getGeneralPurposeFlag()[1], 3)).isFalse(); } @Test public void testGenerateFileHeaderWithUTF8Charset() throws ZipException { FileHeader fileHeader = fileHeaderFactory.generateFileHeader(generateZipParameters(), false, 0, InternalZipConstants.CHARSET_UTF_8); assertThat(isBitSet(fileHeader.getGeneralPurposeFlag()[1], 3)).isTrue(); } @Test public void testGenerateLocalFileHeader() { long lastModifiedFileTime = javaToDosTime(System.currentTimeMillis()); FileHeader fileHeader = generateFileHeader(lastModifiedFileTime); LocalFileHeader localFileHeader = fileHeaderFactory.generateLocalFileHeader(fileHeader); verifyLocalFileHeader(localFileHeader, lastModifiedFileTime); } private ZipParameters generateZipParameters() { ZipParameters zipParameters = new ZipParameters(); zipParameters.setFileNameInZip(FILE_NAME_IN_ZIP); zipParameters.setEntryCRC(ENTRY_CRC); return zipParameters; } private FileHeader generateFileHeader(long lastModifiedFileTime) { FileHeader fileHeader = new FileHeader(); fileHeader.setVersionNeededToExtract(20); fileHeader.setCompressionMethod(CompressionMethod.STORE); fileHeader.setLastModifiedTime(lastModifiedFileTime); fileHeader.setUncompressedSize(1000L); fileHeader.setFileName(FILE_NAME_IN_ZIP); fileHeader.setFileNameLength(FILE_NAME_IN_ZIP.getBytes(InternalZipConstants.CHARSET_UTF_8).length); fileHeader.setEncrypted(true); fileHeader.setEncryptionMethod(EncryptionMethod.AES); fileHeader.setCrc(1231231L); fileHeader.setCompressedSize(23523L); fileHeader.setGeneralPurposeFlag(new byte[] {2, 28}); fileHeader.setDataDescriptorExists(true); return fileHeader; } private void verifyFileHeader(FileHeader fileHeader, ZipParameters zipParameters, boolean isSplitZip, int diskNumberStart, boolean aesExtraDataRecordPresent) { assertThat(fileHeader).isNotNull(); assertThat(fileHeader.getSignature()).isEqualTo(HeaderSignature.CENTRAL_DIRECTORY); assertThat(fileHeader.getVersionMadeBy()).isEqualTo(20); assertThat(fileHeader.getVersionNeededToExtract()).isEqualTo(20); verifyCompressionMethod(fileHeader, zipParameters); assertThat(fileHeader.isEncrypted()).isEqualTo(zipParameters.isEncryptFiles()); assertThat(fileHeader.getEncryptionMethod()).isEqualTo(zipParameters.isEncryptFiles() ? zipParameters.getEncryptionMethod() : EncryptionMethod.NONE); assertThat(fileHeader.getFileName()).isEqualTo(FILE_NAME_IN_ZIP); assertThat(fileHeader.getFileNameLength()).isEqualTo(FILE_NAME_IN_ZIP.length()); verifyGeneralPurposeBytes(fileHeader.getGeneralPurposeFlag(), zipParameters); assertThat(fileHeader.getDiskNumberStart()).isEqualTo(isSplitZip ? diskNumberStart : 0); verifyLastModifiedFileTime(fileHeader, zipParameters); assertThat(fileHeader.getExternalFileAttributes()).isEqualTo(new byte[4]); assertThat(fileHeader.isDirectory()).isEqualTo(false); if (zipParameters.isWriteExtendedLocalFileHeader()) { assertThat(fileHeader.getUncompressedSize()).isEqualTo(0); } else { assertThat(fileHeader.getUncompressedSize()).isEqualTo(zipParameters.getEntrySize()); } verifyCrc(fileHeader); assertThat(fileHeader.isDataDescriptorExists()).isEqualTo(zipParameters.isWriteExtendedLocalFileHeader()); assertThat(fileHeader.getAesExtraDataRecord() != null).isEqualTo(aesExtraDataRecordPresent); } private void verifyLocalFileHeader(LocalFileHeader localFileHeader, long lastModifiedFileTime) { assertThat(localFileHeader).isNotNull(); assertThat(localFileHeader.getSignature()).isEqualTo(HeaderSignature.LOCAL_FILE_HEADER); assertThat(localFileHeader.getVersionNeededToExtract()).isEqualTo(20); assertThat(localFileHeader.getCompressionMethod()).isEqualTo(CompressionMethod.STORE); assertThat(localFileHeader.getLastModifiedTime()).isEqualTo(lastModifiedFileTime); assertThat(localFileHeader.getUncompressedSize()).isEqualTo(1000L); assertThat(localFileHeader.getFileName()).isEqualTo(FILE_NAME_IN_ZIP); assertThat(localFileHeader.getFileNameLength()).isEqualTo(FILE_NAME_IN_ZIP.length()); assertThat(localFileHeader.isEncrypted()).isEqualTo(true); assertThat(localFileHeader.getEncryptionMethod()).isEqualTo(EncryptionMethod.AES); assertThat(localFileHeader.getCrc()).isEqualTo(1231231L); assertThat(localFileHeader.getCompressedSize()).isEqualTo(23523L); assertThat(localFileHeader.getGeneralPurposeFlag()).containsExactly(2, 28); assertThat(localFileHeader.isDataDescriptorExists()).isTrue(); } private void verifyCompressionMethod(FileHeader fileHeader, ZipParameters zipParameters) { if (fileHeader.isEncrypted() && fileHeader.getEncryptionMethod().equals(EncryptionMethod.AES)) { assertThat(fileHeader.getCompressionMethod()).isEqualTo(CompressionMethod.AES_INTERNAL_ONLY); } else { assertThat(fileHeader.getCompressionMethod()).isEqualTo(zipParameters.getCompressionMethod()); } } private void verifyLastModifiedFileTime(FileHeader fileHeader, ZipParameters zipParameters) { if (zipParameters.getLastModifiedFileTime() > 0) { assertThat(fileHeader.getLastModifiedTime()).isEqualTo(javaToDosTime( zipParameters.getLastModifiedFileTime())); } else { assertThat(fileHeader.getLastModifiedTime()).isGreaterThan(0); } } private void verifyGeneralPurposeBytes(byte[] generalPurposeBytes, ZipParameters zipParameters) { assertThat(generalPurposeBytes).isNotNull(); assertThat(generalPurposeBytes.length).isEqualTo(2); assertThat(isBitSet(generalPurposeBytes[0], 0)).isEqualTo(zipParameters.isEncryptFiles()); if (zipParameters.getCompressionMethod() == CompressionMethod.DEFLATE) { verifyCompressionLevelGridForDeflate(zipParameters.getCompressionLevel(), generalPurposeBytes[0]); } else { assertThat(isBitSet(generalPurposeBytes[0], 1)).isFalse(); assertThat(isBitSet(generalPurposeBytes[0], 2)).isFalse(); } assertThat(isBitSet(generalPurposeBytes[0], 3)).isEqualTo(zipParameters.isWriteExtendedLocalFileHeader()); assertThat(isBitSet(generalPurposeBytes[1], 3)).isTrue(); } private void verifyCompressionLevelGridForDeflate(CompressionLevel compressionLevel, byte firstByteOfGeneralPurposeBytes) { if (compressionLevel == CompressionLevel.NORMAL) { assertThat(isBitSet(firstByteOfGeneralPurposeBytes, 1)).isFalse(); assertThat(isBitSet(firstByteOfGeneralPurposeBytes, 2)).isFalse(); } else if (compressionLevel == CompressionLevel.MAXIMUM) { assertThat(isBitSet(firstByteOfGeneralPurposeBytes, 1)).isTrue(); assertThat(isBitSet(firstByteOfGeneralPurposeBytes, 2)).isFalse(); } else if (compressionLevel == CompressionLevel.FAST) { assertThat(isBitSet(firstByteOfGeneralPurposeBytes, 1)).isFalse(); assertThat(isBitSet(firstByteOfGeneralPurposeBytes, 2)).isTrue(); } else if (compressionLevel == CompressionLevel.FASTEST) { assertThat(isBitSet(firstByteOfGeneralPurposeBytes, 1)).isTrue(); assertThat(isBitSet(firstByteOfGeneralPurposeBytes, 2)).isTrue(); } } private void verifyCrc(FileHeader fileHeader) { if (fileHeader.isEncrypted() && fileHeader.getEncryptionMethod() == EncryptionMethod.ZIP_STANDARD) { assertThat(fileHeader.getCrc()).isEqualTo(ENTRY_CRC); } else { assertThat(fileHeader.getCrc()).isEqualTo(0); } } private void verifyAesExtraDataRecord(AESExtraDataRecord aesExtraDataRecord, AesKeyStrength aesKeyStrength, CompressionMethod compressionMethod, AesVersion aesVersion) { assertThat(aesExtraDataRecord).isNotNull(); assertThat(aesExtraDataRecord.getSignature()).isEqualTo(HeaderSignature.AES_EXTRA_DATA_RECORD); assertThat(aesExtraDataRecord.getDataSize()).isEqualTo(7); assertThat(aesExtraDataRecord.getVendorID()).isEqualTo("AE"); assertThat(aesExtraDataRecord.getCompressionMethod()).isEqualTo(compressionMethod); assertThat(aesExtraDataRecord.getAesVersion()).isEqualTo(aesVersion); assertThat(aesExtraDataRecord.getAesKeyStrength()).isEqualTo(aesKeyStrength); } }zip4j-2.2.8/src/test/java/net/lingala/zip4j/headers/HeaderReaderIT.java000066400000000000000000000444761357650161600255660ustar00rootroot00000000000000package net.lingala.zip4j.headers; import net.lingala.zip4j.AbstractIT; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.io.outputstream.SplitOutputStream; import net.lingala.zip4j.model.CentralDirectory; import net.lingala.zip4j.model.EndOfCentralDirectoryRecord; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.LocalFileHeader; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.AesKeyStrength; import net.lingala.zip4j.model.enums.EncryptionMethod; import net.lingala.zip4j.model.enums.RandomAccessFileMode; import net.lingala.zip4j.util.BitUtils; import net.lingala.zip4j.util.InternalZipConstants; import org.junit.Test; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.util.ArrayList; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; public class HeaderReaderIT extends AbstractIT { private static final String FILE_NAME_PREFIX = "FILE_NAME_ÄÜß_"; private static final String END_OF_CENTRAL_DIR_COMMENT = "END_OF_CENTRAL_DIR_COMMENT_ÜÄÖÖÖÄ"; private FileHeaderFactory fileHeaderFactory = new FileHeaderFactory(); private HeaderReader headerReader = new HeaderReader(); private HeaderWriter headerWriter = new HeaderWriter(); @Test public void testReadAllHeadersWith10Entries() throws IOException, ZipException { int numberOfEntries = 10; ZipModel actualZipModel = generateZipHeadersFile(numberOfEntries, EncryptionMethod.NONE); try(RandomAccessFile randomAccessFile = initializeRandomAccessFile(actualZipModel.getZipFile())) { ZipModel readZipModel = headerReader.readAllHeaders(randomAccessFile, null); verifyZipModel(readZipModel, 10, false); assertThat(readZipModel.getEndOfCentralDirectoryRecord().getComment()).isEmpty(); } } @Test public void testReadAllHeadersWithEndOfCentralDirectoryComment() throws IOException, ZipException { ZipModel actualZipModel = generateZipModel(1); actualZipModel.getEndOfCentralDirectoryRecord().setComment(END_OF_CENTRAL_DIR_COMMENT); File headersFile = writeZipHeaders(actualZipModel); actualZipModel.setZipFile(headersFile); try(RandomAccessFile randomAccessFile = initializeRandomAccessFile(actualZipModel.getZipFile())) { ZipModel readZipModel = headerReader.readAllHeaders(randomAccessFile, InternalZipConstants.CHARSET_UTF_8); verifyZipModel(readZipModel, 1, false); EndOfCentralDirectoryRecord endOfCentralDirectoryRecord = readZipModel.getEndOfCentralDirectoryRecord(); assertThat(endOfCentralDirectoryRecord.getComment()).isEqualTo(END_OF_CENTRAL_DIR_COMMENT); } } @Test public void testReadAllWithoutEndOfCentralDirectoryRecordThrowsException() throws IOException { try(RandomAccessFile randomAccessFile = new RandomAccessFile(temporaryFolder.newFile(), RandomAccessFileMode.WRITE.getValue())) { //Create an empty file randomAccessFile.seek(4000); randomAccessFile.write(1); headerReader.readAllHeaders(randomAccessFile, null); fail("Should throw an exception"); } catch (ZipException e) { assertThat(e.getMessage()).isEqualTo("Zip headers not found. Probably not a zip file"); } } @Test public void testReadAllWithoutEnoughHeaderDataThrowsException() throws IOException { try(RandomAccessFile randomAccessFile = new RandomAccessFile(temporaryFolder.newFile(), RandomAccessFileMode.WRITE.getValue())) { //Create an empty file randomAccessFile.seek(1000); randomAccessFile.write(1); headerReader.readAllHeaders(randomAccessFile, null); fail("Should throw an exception"); } catch (ZipException e) { assertThat(e.getMessage()).isEqualTo("Zip headers not found. Probably not a zip file or a corrupted zip file"); assertThat(e.getCause() instanceof IOException); } } @Test public void testReadAllWithoutFileHeaderSignatureThrowsException() throws IOException, ZipException { ZipModel actualZipModel = generateZipModel(2); actualZipModel.getCentralDirectory().getFileHeaders().get(1).setSignature(HeaderSignature.DIGITAL_SIGNATURE); File headersFile = writeZipHeaders(actualZipModel); actualZipModel.setZipFile(headersFile); try(RandomAccessFile randomAccessFile = new RandomAccessFile(actualZipModel.getZipFile(), RandomAccessFileMode.READ.getValue())) { headerReader.readAllHeaders(randomAccessFile, null); fail("Should throw an exception"); } catch (ZipException e) { assertThat(e.getMessage()).isEqualTo("Expected central directory entry not found (#2)"); } } @Test public void testReadAllWithFileNameContainsWindowsDriveExcludesIt() throws IOException, ZipException { String fileName = "C:\\test.txt"; ZipModel actualZipModel = generateZipModel(1); actualZipModel.getCentralDirectory().getFileHeaders().get(0).setFileName(fileName); File headersFile = writeZipHeaders(actualZipModel); actualZipModel.setZipFile(headersFile); try(RandomAccessFile randomAccessFile = new RandomAccessFile(actualZipModel.getZipFile(), RandomAccessFileMode.READ.getValue())) { ZipModel readZipModel = headerReader.readAllHeaders(randomAccessFile, null); FileHeader fileHeader = readZipModel.getCentralDirectory().getFileHeaders().get(0); assertThat(fileHeader.getFileName()).isEqualTo("test.txt"); } } @Test public void testReadAllWithoutFileNameWritesNull() throws IOException, ZipException { ZipModel actualZipModel = generateZipModel(1); actualZipModel.getCentralDirectory().getFileHeaders().get(0).setFileName(null); File headersFile = writeZipHeaders(actualZipModel); actualZipModel.setZipFile(headersFile); try(RandomAccessFile randomAccessFile = new RandomAccessFile(actualZipModel.getZipFile(), RandomAccessFileMode.READ.getValue())) { ZipModel readZipModel = headerReader.readAllHeaders(randomAccessFile, null); FileHeader fileHeader = readZipModel.getCentralDirectory().getFileHeaders().get(0); assertThat(fileHeader.getFileName()).isNull(); } } @Test public void testReadAllWithJapaneseCharacters() throws IOException, ZipException { testWithoutUtf8FileName("公ゃ的年社", "育ざどろめ", true, false); } @Test public void testReadAllWithoutUtf8FlagDecodesWithoutCharsetFlagForJapaneseCharactersDoesNotMatch() throws IOException, ZipException { testWithoutUtf8FileName("公ゃ的年社", "育ざどろめ", false, true); } @Test public void testReadAllWithoutUtf8FlagDecodesWithoutCharsetFlagForEnglishCharactersMatches() throws IOException, ZipException { testWithoutUtf8FileName("SOME_TEXT", "SOME_COMMENT", true, true); } @Test public void testReadAllWithAesEncryption() throws ZipException, IOException { ZipModel actualZipModel = generateZipHeadersFile(3, EncryptionMethod.AES); try(RandomAccessFile randomAccessFile = new RandomAccessFile(actualZipModel.getZipFile(), RandomAccessFileMode.READ.getValue())) { ZipModel readZipModel = headerReader.readAllHeaders(randomAccessFile, null); for (FileHeader fileHeader : readZipModel.getCentralDirectory().getFileHeaders()) { assertThat(fileHeader.getAesExtraDataRecord()).isNotNull(); assertThat(fileHeader.getAesExtraDataRecord().getAesKeyStrength()).isEqualTo(AesKeyStrength.KEY_STRENGTH_256); } } } @Test public void testReadAllWithStandardZipEncryption() throws ZipException, IOException { ZipModel actualZipModel = generateZipHeadersFile(3, EncryptionMethod.ZIP_STANDARD); try(RandomAccessFile randomAccessFile = new RandomAccessFile(actualZipModel.getZipFile(), RandomAccessFileMode.READ.getValue())) { ZipModel readZipModel = headerReader.readAllHeaders(randomAccessFile, null); for (FileHeader fileHeader : readZipModel.getCentralDirectory().getFileHeaders()) { assertThat(fileHeader.isEncrypted()).isTrue(); assertThat(fileHeader.getEncryptionMethod()).isEqualTo(EncryptionMethod.ZIP_STANDARD); assertThat(fileHeader.getAesExtraDataRecord()).isNull(); } } } @Test public void testReadAllZip64Format() throws IOException, ZipException { ZipModel actualZipModel = generateZipModel(1); long entrySize = InternalZipConstants.ZIP_64_SIZE_LIMIT + 1; actualZipModel.getCentralDirectory().getFileHeaders().get(0).setUncompressedSize(entrySize); actualZipModel.getCentralDirectory().getFileHeaders().get(0).setCompressedSize(entrySize + 100); File headersFile = writeZipHeaders(actualZipModel); actualZipModel.setZipFile(headersFile); try(RandomAccessFile randomAccessFile = new RandomAccessFile(actualZipModel.getZipFile(), RandomAccessFileMode.READ.getValue())) { ZipModel readZipModel = headerReader.readAllHeaders(randomAccessFile, null); assertThat(readZipModel.isZip64Format()).isTrue(); assertThat(readZipModel.getZip64EndOfCentralDirectoryRecord()).isNotNull(); assertThat(readZipModel.getZip64EndOfCentralDirectoryLocator()).isNotNull(); FileHeader fileHeader = actualZipModel.getCentralDirectory().getFileHeaders().get(0); assertThat(fileHeader.getUncompressedSize()).isEqualTo(entrySize); assertThat(fileHeader.getCompressedSize()).isEqualTo(entrySize + 100); } } @Test public void testReadLocalFileHeader() throws ZipException, IOException { long entrySize = InternalZipConstants.ZIP_64_SIZE_LIMIT + 1; File headerFile = generateAndWriteLocalFileHeader(entrySize, EncryptionMethod.NONE); try(InputStream inputStream = new FileInputStream(headerFile)) { LocalFileHeader readLocalFileHeader = headerReader.readLocalFileHeader(inputStream, InternalZipConstants.CHARSET_UTF_8); assertThat(readLocalFileHeader).isNotNull(); assertThat(readLocalFileHeader.getCompressedSize()).isEqualTo(entrySize); assertThat(readLocalFileHeader.getUncompressedSize()).isEqualTo(entrySize); } } @Test public void testReadLocalFileHeaderWithAesEncryption() throws ZipException, IOException { long entrySize = InternalZipConstants.ZIP_64_SIZE_LIMIT - 1001 ; File headerFile = generateAndWriteLocalFileHeader(entrySize, EncryptionMethod.AES); try(InputStream inputStream = new FileInputStream(headerFile)) { LocalFileHeader readLocalFileHeader = headerReader.readLocalFileHeader(inputStream, InternalZipConstants.CHARSET_UTF_8); assertThat(readLocalFileHeader).isNotNull(); assertThat(readLocalFileHeader.getCompressedSize()).isEqualTo(entrySize); assertThat(readLocalFileHeader.getUncompressedSize()).isEqualTo(entrySize); assertThat(readLocalFileHeader.getAesExtraDataRecord()).isNotNull(); assertThat(readLocalFileHeader.getAesExtraDataRecord().getAesKeyStrength()) .isEqualTo(AesKeyStrength.KEY_STRENGTH_256); } } @Test public void testReadDataDescriptorWithSignature() { } private void testWithoutUtf8FileName(String fileName, String entryComment, boolean shouldFileNamesMatch, boolean unsetUtf8Flag) throws IOException, ZipException { ZipModel actualZipModel = generateZipModel(3); FileHeader secondFileHeader = actualZipModel.getCentralDirectory().getFileHeaders().get(1); if (unsetUtf8Flag) { // Unset utf8 flag byte[] generalPurposeBytes = secondFileHeader.getGeneralPurposeFlag(); generalPurposeBytes[1] = BitUtils.unsetBit(generalPurposeBytes[1], 3); secondFileHeader.setGeneralPurposeFlag(generalPurposeBytes); } secondFileHeader.setFileName(fileName); secondFileHeader.setFileComment(entryComment); File headersFile = writeZipHeaders(actualZipModel); actualZipModel.setZipFile(headersFile); try(RandomAccessFile randomAccessFile = new RandomAccessFile(actualZipModel.getZipFile(), RandomAccessFileMode.READ.getValue())) { ZipModel readZipModel = headerReader.readAllHeaders(randomAccessFile, null); FileHeader fileHeader = readZipModel.getCentralDirectory().getFileHeaders().get(1); if (shouldFileNamesMatch) { assertThat(fileHeader.getFileName()).isEqualTo(fileName); assertThat(fileHeader.getFileComment()).isEqualTo(entryComment); } else { assertThat(fileHeader.getFileName()).isNotEqualTo(fileName); assertThat(fileHeader.getFileComment()).isNotEqualTo(entryComment); } assertThat(readZipModel.getCentralDirectory().getFileHeaders().get(0).getFileCommentLength()).isEqualTo(0); assertThat(readZipModel.getCentralDirectory().getFileHeaders().get(0).getFileComment()).isNull(); assertThat(readZipModel.getCentralDirectory().getFileHeaders().get(2).getFileCommentLength()).isEqualTo(0); assertThat(readZipModel.getCentralDirectory().getFileHeaders().get(2).getFileComment()).isNull(); } } private RandomAccessFile initializeRandomAccessFile(File headersFile) throws IOException { return new RandomAccessFile(headersFile, RandomAccessFileMode.READ.getValue()); } private void verifyZipModel(ZipModel readZipModel, int numberOfExpectedEntries, boolean isZip64) { assertThat(readZipModel).isNotNull(); verifyEndOfCentralDirectory(readZipModel.getEndOfCentralDirectoryRecord(), numberOfExpectedEntries, isZip64); verifyCentralDirectory(readZipModel.getCentralDirectory(), numberOfExpectedEntries); } private void verifyEndOfCentralDirectory(EndOfCentralDirectoryRecord endOfCentralDirectoryRecord, int numberOfEntries, boolean isZip64) { assertThat(endOfCentralDirectoryRecord).isNotNull(); endOfCentralDirectoryRecord.setSignature(HeaderSignature.END_OF_CENTRAL_DIRECTORY); assertThat(endOfCentralDirectoryRecord.getTotalNumberOfEntriesInCentralDirectory()).isEqualTo(numberOfEntries); if (!isZip64) { assertThat(endOfCentralDirectoryRecord.getNumberOfThisDisk()).isEqualTo(0); assertThat(endOfCentralDirectoryRecord.getNumberOfThisDiskStartOfCentralDir()).isEqualTo(0); assertThat(endOfCentralDirectoryRecord.getTotalNumberOfEntriesInCentralDirectoryOnThisDisk()) .isEqualTo(numberOfEntries); assertThat(endOfCentralDirectoryRecord.getSizeOfCentralDirectory()).isNotZero(); } } private void verifyCentralDirectory(CentralDirectory centralDirectory, int numberOfExpectedEntries) { assertThat(centralDirectory).isNotNull(); verifyFileHeaders(centralDirectory.getFileHeaders(), numberOfExpectedEntries); } private void verifyFileHeaders(List fileHeaders, int numberOfExpectedEntries) { assertThat(fileHeaders).isNotEmpty(); assertThat(fileHeaders).hasSize(numberOfExpectedEntries); for (int i = 0; i < numberOfExpectedEntries; i++) { String expectedFileName = FILE_NAME_PREFIX + i; FileHeader fileHeader = fileHeaders.get(i); assertThat(fileHeader.getFileName()).isEqualTo(expectedFileName); int expectedFileNameLength = expectedFileName.getBytes(InternalZipConstants.CHARSET_UTF_8).length; assertThat(fileHeader.getFileNameLength()).isEqualTo(expectedFileNameLength); } } private ZipModel generateZipHeadersFile(int numberOfEntries, EncryptionMethod encryptionMethod) throws IOException, ZipException { ZipModel zipModel = generateZipModel(numberOfEntries, encryptionMethod); File headersFile = writeZipHeaders(zipModel); zipModel.setZipFile(headersFile); return zipModel; } private ZipModel generateZipModel(int numberOfEntries) throws ZipException { return generateZipModel(numberOfEntries, EncryptionMethod.NONE); } private ZipModel generateZipModel(int numberOfEntries, EncryptionMethod encryptionMethod) throws ZipException { ZipParameters zipParameters = generateZipParameters(encryptionMethod); ZipModel zipModel = new ZipModel(); zipModel.getCentralDirectory().setFileHeaders(generateFileHeaders(zipParameters, numberOfEntries)); return zipModel; } private ZipParameters generateZipParameters(EncryptionMethod encryptionMethod) { ZipParameters zipParameters = new ZipParameters(); if (encryptionMethod != null && encryptionMethod != EncryptionMethod.NONE) { zipParameters.setEncryptFiles(true); zipParameters.setEncryptionMethod(encryptionMethod); } return zipParameters; } private List generateFileHeaders(ZipParameters zipParameters, int numberOfEntries) throws ZipException { List fileHeaders = new ArrayList<>(); for (int i = 0; i < numberOfEntries; i++) { zipParameters.setFileNameInZip(FILE_NAME_PREFIX + i); FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8); fileHeaders.add(fileHeader); } return fileHeaders; } private LocalFileHeader generateLocalFileHeader(long entrySize, EncryptionMethod encryptionMethod) throws ZipException { List fileHeaders = generateFileHeaders(generateZipParameters(encryptionMethod), 1); LocalFileHeader localFileHeader = fileHeaderFactory.generateLocalFileHeader(fileHeaders.get(0)); localFileHeader.setCompressedSize(entrySize); localFileHeader.setUncompressedSize(entrySize); return localFileHeader; } private File generateAndWriteLocalFileHeader(long entrySize, EncryptionMethod encryptionMethod) throws ZipException, IOException { LocalFileHeader localFileHeader = generateLocalFileHeader(entrySize, encryptionMethod); if (encryptionMethod != null && encryptionMethod != EncryptionMethod.NONE) { localFileHeader.setEncrypted(true); localFileHeader.setEncryptionMethod(encryptionMethod); } ZipModel zipModel = generateZipModel(1); File headerFile = temporaryFolder.newFile(); try(OutputStream outputStream = new FileOutputStream(headerFile)) { headerWriter.writeLocalFileHeader(zipModel, localFileHeader, outputStream, InternalZipConstants.CHARSET_UTF_8); } return headerFile; } private File writeZipHeaders(ZipModel zipModel) throws IOException, ZipException { File headersFile = temporaryFolder.newFile(); try(SplitOutputStream splitOutputStream = new SplitOutputStream(headersFile)) { headerWriter.finalizeZipFile(zipModel, splitOutputStream, InternalZipConstants.CHARSET_UTF_8); return headersFile; } } }zip4j-2.2.8/src/test/java/net/lingala/zip4j/headers/HeaderUtilTest.java000066400000000000000000000254741357650161600257010ustar00rootroot00000000000000package net.lingala.zip4j.headers; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.CentralDirectory; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.util.InternalZipConstants; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; public class HeaderUtilTest { private static final String FILE_NAME = "test.txt"; @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void testGetFileHeaderWithNullZipModelThrowsException() throws ZipException { expectZipException("zip model is null, cannot determine file header with exact match for fileName: " + FILE_NAME); HeaderUtil.getFileHeader(null, FILE_NAME); } @Test public void testGetFileHeaderWithNullFileNameThrowsException() throws ZipException { expectZipException("file name is null, cannot determine file header with exact match for fileName: null"); HeaderUtil.getFileHeader(new ZipModel(), null); } @Test public void testGetFileHeaderWithEmptyFileNameThrowsException() throws ZipException { expectZipException("file name is null, cannot determine file header with exact match for fileName: "); HeaderUtil.getFileHeader(new ZipModel(), ""); } @Test public void testGetFileHeaderWithNullCentralDirectoryThrowsException() throws ZipException { expectZipException("central directory is null, cannot determine file header with exact match for fileName: " + FILE_NAME); ZipModel zipModel = new ZipModel(); zipModel.setCentralDirectory(null); HeaderUtil.getFileHeader(zipModel, FILE_NAME); } @Test public void testGetFileHeaderWithNullFileHeadersThrowsException() throws ZipException { expectedException.expect(ZipException.class); expectZipException("file Headers are null, cannot determine file header with exact match for fileName: " + FILE_NAME); ZipModel zipModel = new ZipModel(); CentralDirectory centralDirectory = new CentralDirectory(); centralDirectory.setFileHeaders(null); zipModel.setCentralDirectory(centralDirectory); HeaderUtil.getFileHeader(zipModel, FILE_NAME); } @Test public void testGetFileHeaderWithEmptyFileHeadersReturnsNull() throws ZipException { ZipModel zipModel = new ZipModel(); CentralDirectory centralDirectory = new CentralDirectory(); centralDirectory.setFileHeaders(Collections.emptyList()); zipModel.setCentralDirectory(centralDirectory); FileHeader fileHeader = HeaderUtil.getFileHeader(zipModel, FILE_NAME); assertThat(fileHeader).isNull(); } @Test public void testGetFileHeaderWithExactMatch() throws ZipException { ZipModel zipModel = new ZipModel(); CentralDirectory centralDirectory = new CentralDirectory(); centralDirectory.setFileHeaders(Arrays.asList( generateFileHeader(null), generateFileHeader(""), generateFileHeader("SOME_OTHER_NAME"), generateFileHeader(FILE_NAME) )); zipModel.setCentralDirectory(centralDirectory); FileHeader fileHeader = HeaderUtil.getFileHeader(zipModel, FILE_NAME); assertThat(fileHeader).isNotNull(); assertThat(fileHeader.getFileName()).isEqualTo(FILE_NAME); } @Test public void testGetFileHeaderWithWindowsFileSeparator() throws ZipException { ZipModel zipModel = new ZipModel(); CentralDirectory centralDirectory = new CentralDirectory(); centralDirectory.setFileHeaders(Arrays.asList( generateFileHeader(FILE_NAME), generateFileHeader("SOME_OTHER_NAME\\") )); zipModel.setCentralDirectory(centralDirectory); FileHeader fileHeader = HeaderUtil.getFileHeader(zipModel, "SOME_OTHER_NAME\\"); assertThat(fileHeader).isNotNull(); assertThat(fileHeader.getFileName()).isEqualTo("SOME_OTHER_NAME\\"); } @Test public void testGetFileHeaderWithUnixFileSeparator() throws ZipException { ZipModel zipModel = new ZipModel(); CentralDirectory centralDirectory = new CentralDirectory(); centralDirectory.setFileHeaders(Arrays.asList( generateFileHeader(FILE_NAME), generateFileHeader("SOME_OTHER_NAME/") )); zipModel.setCentralDirectory(centralDirectory); FileHeader fileHeader = HeaderUtil.getFileHeader(zipModel, "SOME_OTHER_NAME/"); assertThat(fileHeader).isNotNull(); assertThat(fileHeader.getFileName()).isEqualTo("SOME_OTHER_NAME/"); } @Test public void testGetFileHeaderWithoutAMatch() throws ZipException { ZipModel zipModel = new ZipModel(); CentralDirectory centralDirectory = new CentralDirectory(); centralDirectory.setFileHeaders(Arrays.asList( generateFileHeader(FILE_NAME), generateFileHeader("SOME_OTHER_NAME") )); zipModel.setCentralDirectory(centralDirectory); assertThat(HeaderUtil.getFileHeader(zipModel, "SHOULD_NOT_EXIST")).isNull(); } @Test public void testGetIndexOfFileHeaderWhenZipModelIsNullThrowsException() throws ZipException { expectZipException("input parameters is null, cannot determine index of file header"); HeaderUtil.getIndexOfFileHeader(null, new FileHeader()); } @Test public void testGetIndexOfFileHeaderWhenFileHeaderlIsNullThrowsException() throws ZipException { expectZipException("input parameters is null, cannot determine index of file header"); HeaderUtil.getIndexOfFileHeader(new ZipModel(), null); } @Test public void testGetIndexOfFileHeaderWhenCentralDirectoryIsNullReturnsNegativeOne() throws ZipException { ZipModel zipModel = new ZipModel(); zipModel.setCentralDirectory(null); assertThat(HeaderUtil.getIndexOfFileHeader(zipModel, new FileHeader())).isEqualTo(-1); } @Test public void testGetIndexOfFileHeaderWhenFileHeadersIsNullReturnsNegativeOne() throws ZipException { ZipModel zipModel = new ZipModel(); zipModel.getCentralDirectory().setFileHeaders(null); assertThat(HeaderUtil.getIndexOfFileHeader(zipModel, new FileHeader())).isEqualTo(-1); } @Test public void testGetIndexOfFileHeaderWhenFileHeadersIsEmptyReturnsNegativeOne() throws ZipException { ZipModel zipModel = new ZipModel(); zipModel.getCentralDirectory().setFileHeaders(Collections.emptyList()); assertThat(HeaderUtil.getIndexOfFileHeader(zipModel, new FileHeader())).isEqualTo(-1); } @Test public void testGetIndexOfFileHeaderWithNullFileNameInFileHeaderThrowsException() throws ZipException { expectZipException("file name in file header is empty or null, cannot determine index of file header"); ZipModel zipModel = new ZipModel(); zipModel.getCentralDirectory().setFileHeaders(Collections.singletonList(new FileHeader())); HeaderUtil.getIndexOfFileHeader(zipModel, new FileHeader()); } @Test public void testGetIndexOfFileHeaderWithEmptyFileNameInFileHeaderThrowsException() throws ZipException { expectZipException("file name in file header is empty or null, cannot determine index of file header"); ZipModel zipModel = new ZipModel(); zipModel.getCentralDirectory().setFileHeaders(Collections.singletonList(new FileHeader())); FileHeader fileHeader = new FileHeader(); fileHeader.setFileName(""); HeaderUtil.getIndexOfFileHeader(zipModel, new FileHeader()); } @Test public void testGetIndexOfFileHeaderGetsIndexSuccessfully() throws ZipException { String fileNamePrefix = "FILE_NAME_"; int numberOfEntriesToAdd = 10; List fileHeadersInZipModel = generateFileHeaderWithFileNames(fileNamePrefix, numberOfEntriesToAdd); ZipModel zipModel = new ZipModel(); zipModel.getCentralDirectory().setFileHeaders(fileHeadersInZipModel); FileHeader fileHeaderToFind = new FileHeader(); for (int i = 0; i < numberOfEntriesToAdd; i++) { fileHeaderToFind.setFileName(fileNamePrefix + i); assertThat(HeaderUtil.getIndexOfFileHeader(zipModel, fileHeaderToFind)).isEqualTo(i); } fileHeaderToFind.setFileName(fileNamePrefix + numberOfEntriesToAdd); assertThat(HeaderUtil.getIndexOfFileHeader(zipModel, fileHeaderToFind)).isEqualTo(-1); } @Test public void testDecodeStringWithCharsetForUtf8() { String utf8StringToEncode = "asdäüöö"; byte[] utf8EncodedBytes = utf8StringToEncode.getBytes(InternalZipConstants.CHARSET_UTF_8); assertThat(HeaderUtil.decodeStringWithCharset(utf8EncodedBytes, true, null)).isEqualTo(utf8StringToEncode); } @Test public void testDecodeStringWithCharsetWithoutUtf8ForUtf8String() { String utf8StringToEncode = "asdäüöö"; byte[] utf8EncodedBytes = utf8StringToEncode.getBytes(InternalZipConstants.CHARSET_UTF_8); assertThat(HeaderUtil.decodeStringWithCharset(utf8EncodedBytes, false, null)).isNotEqualTo(utf8StringToEncode); } @Test public void testDecodeStringWithCharsetWithoutUtf8AndWithEnglishChars() { String plainString = "asdasda234234"; byte[] plainEncodedBytes = plainString.getBytes(); assertThat(HeaderUtil.decodeStringWithCharset(plainEncodedBytes, false, null)).isEqualTo(plainString); } @Test public void testDecodeStringWithCharsetWithISO8859AndFinnishChars() throws UnsupportedEncodingException { String finnishString = "asdäüöö"; byte[] plainEncodedBytes = finnishString.getBytes("ISO-8859-1"); assertThat(HeaderUtil.decodeStringWithCharset(plainEncodedBytes, false, Charset.forName("ISO-8859-1"))).isEqualTo(finnishString); } @Test public void testDecodeStringWithCharsetWithUTF8CharsetAndKoreanChars() { String koreanString = "가나다"; byte[] plainEncodedBytes = koreanString.getBytes(InternalZipConstants.CHARSET_UTF_8); assertThat(HeaderUtil.decodeStringWithCharset(plainEncodedBytes, true, null)).isEqualTo(koreanString); } @Test public void testDecodeStringWithCharsetWithNullCharsetAndEnglishChars() { String englishString = "asdasda234234"; byte[] plainEncodedBytes = englishString.getBytes(); assertThat(HeaderUtil.decodeStringWithCharset(plainEncodedBytes, false, null)).isEqualTo(englishString); } private List generateFileHeaderWithFileNames(String fileNamePrefix, int numberOfEntriesToAdd) { List fileHeaders = new ArrayList<>(); for (int i = 0; i < numberOfEntriesToAdd; i++) { fileHeaders.add(generateFileHeader(fileNamePrefix + i)); } fileHeaders.add(generateFileHeader("")); fileHeaders.add(generateFileHeader(null)); return fileHeaders; } private FileHeader generateFileHeader(String fileName) { FileHeader fileHeader = new FileHeader(); fileHeader.setFileName(fileName); return fileHeader; } private void expectZipException(String message) { expectedException.expectMessage(message); expectedException.expect(ZipException.class); } }zip4j-2.2.8/src/test/java/net/lingala/zip4j/headers/HeaderWriterIT.java000066400000000000000000000765511357650161600256370ustar00rootroot00000000000000package net.lingala.zip4j.headers; import net.lingala.zip4j.AbstractIT; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.io.outputstream.CountingOutputStream; import net.lingala.zip4j.io.outputstream.SplitOutputStream; import net.lingala.zip4j.model.AESExtraDataRecord; import net.lingala.zip4j.model.CentralDirectory; import net.lingala.zip4j.model.DataDescriptor; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.LocalFileHeader; import net.lingala.zip4j.model.Zip64ExtendedInfo; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.model.enums.AesKeyStrength; import net.lingala.zip4j.model.enums.AesVersion; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.model.enums.EncryptionMethod; import net.lingala.zip4j.model.enums.RandomAccessFileMode; import net.lingala.zip4j.util.BitUtils; import net.lingala.zip4j.util.InternalZipConstants; import net.lingala.zip4j.util.RawIO; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import static net.lingala.zip4j.util.Zip4jUtil.javaToDosTime; import static org.assertj.core.api.Assertions.assertThat; public class HeaderWriterIT extends AbstractIT { private static final String FILE_NAME_PREFIX = "FILE_NAME_"; private static final long COMPRESSED_SIZE = 4234L; private static final long UNCOMPRESSED_SIZE = 23423L; private static final long COMPRESSED_SIZE_ZIP64 = InternalZipConstants.ZIP_64_SIZE_LIMIT + 1; private static final long UNCOMPRESSED_SIZE_ZIP64 = InternalZipConstants.ZIP_64_SIZE_LIMIT + 1; private static final int VERSION_MADE_BY = 20; private static final int VERSION_NEEDED_TO_EXTRACT = 20; private static final long LAST_MODIFIED_FILE_TIME = javaToDosTime(System.currentTimeMillis()); private static final byte[] EXTERNAL_FILE_ATTRIBUTES = new byte[] {23, 43, 0, 0}; private static final String FILE_COMMENT_PREFIX = "FILE_COMMENT_PREFIX_"; @Rule public ExpectedException expectedException = ExpectedException.none(); private RawIO rawIO = new RawIO(); private HeaderWriter headerWriter = new HeaderWriter(); private HeaderReader headerReader = new HeaderReader(); @Test public void testWriteLocalFileHeaderSimpleLocalFileHeaderSuccessScenario() throws IOException { ZipModel zipModel = createZipModel(10); LocalFileHeader localFileHeaderToWrite = createLocalFileHeader("LFH", COMPRESSED_SIZE, UNCOMPRESSED_SIZE, true); File headersFile = temporaryFolder.newFile(); try(OutputStream outputStream = new FileOutputStream(headersFile)) { headerWriter.writeLocalFileHeader(zipModel, localFileHeaderToWrite, outputStream, InternalZipConstants.CHARSET_UTF_8); } try(InputStream inputStream = new FileInputStream(headersFile)) { LocalFileHeader readLocalFileHeader = headerReader.readLocalFileHeader(inputStream, InternalZipConstants.CHARSET_UTF_8); verifyLocalFileHeader(readLocalFileHeader, FILE_NAME_PREFIX + "LFH", COMPRESSED_SIZE, UNCOMPRESSED_SIZE); } } @Test public void testWriteLocalFileHeaderForZip64Format() throws IOException { ZipModel zipModel = createZipModel(10); LocalFileHeader localFileHeaderToWrite = createLocalFileHeader("LFH", COMPRESSED_SIZE_ZIP64, UNCOMPRESSED_SIZE_ZIP64, true); File headersFile = temporaryFolder.newFile(); try(OutputStream outputStream = new FileOutputStream(headersFile)) { headerWriter.writeLocalFileHeader(zipModel, localFileHeaderToWrite, outputStream, InternalZipConstants.CHARSET_UTF_8); } try(InputStream inputStream = new FileInputStream(headersFile)) { LocalFileHeader readLocalFileHeader = headerReader.readLocalFileHeader(inputStream, InternalZipConstants.CHARSET_UTF_8); verifyLocalFileHeader(readLocalFileHeader, FILE_NAME_PREFIX + "LFH", COMPRESSED_SIZE_ZIP64, UNCOMPRESSED_SIZE_ZIP64); verifyZip64ExtendedInfo(readLocalFileHeader.getZip64ExtendedInfo(), COMPRESSED_SIZE_ZIP64, UNCOMPRESSED_SIZE_ZIP64, -1, -1); } verifyEntrySizesIsMaxValueInLFHWhenZip64Format(headersFile); } @Test public void testWriteLocalFileHeaderJapaneseCharactersInFileNameAndWithUtf8FlagShouldMatch() throws IOException { testWriteLocalFileHeaderWithFileName("公ゃ的年社", true, true); } @Test public void testWriteLocalFileHeaderEnglishCharactersInFileNameWithoutUtf8ShouldMatch() throws IOException { testWriteLocalFileHeaderWithFileName("SOME_TEXT", false, true); } @Test public void testWriteLocalFileHeaderJapaneseCharactersInFileNameWithoutUtf8ShouldMatch() throws IOException { testWriteLocalFileHeaderWithFileName("公ゃ的年社", false, true); } @Test public void testWriteLocalFileHeaderJapaneseCharactersInFileNameWithCharsetMs932ShouldMatch() throws IOException { testWriteLocalFileHeaderWithFileNameAndCharset("公ゃ的年社", false, true, CHARSET_MS_932); } @Test public void testWriteLocalFileHeaderJapaneseCharactersInFileNameWithUTF8CharsetWithUtf8ShouldMatch() throws IOException { testWriteLocalFileHeaderWithFileNameAndCharset("公ゃ的年社", true, true, InternalZipConstants.CHARSET_UTF_8); } @Test public void testWriteLocalFileHeaderJapaneseCharactersInFileNameWithUTF8CharsetWithoutUtf8ShouldMatch() throws IOException { testWriteLocalFileHeaderWithFileNameAndCharset("公ゃ的年社", false, true, InternalZipConstants.CHARSET_UTF_8); } @Test public void testWriteLocalFileHeaderWithAes256v1() throws IOException { testWriteLocalFileHeaderWithAes(AesKeyStrength.KEY_STRENGTH_256, AesVersion.ONE); } @Test public void testWriteLocalFileHeaderWithAes192v1() throws IOException { testWriteLocalFileHeaderWithAes(AesKeyStrength.KEY_STRENGTH_192, AesVersion.ONE); } @Test public void testWriteLocalFileHeaderWithAes128v1() throws IOException { testWriteLocalFileHeaderWithAes(AesKeyStrength.KEY_STRENGTH_128, AesVersion.ONE); } @Test public void testWriteLocalFileHeaderWithAes256v2() throws IOException { testWriteLocalFileHeaderWithAes(AesKeyStrength.KEY_STRENGTH_256, AesVersion.TWO); } @Test public void testWriteLocalFileHeaderWithAes192v2() throws IOException { testWriteLocalFileHeaderWithAes(AesKeyStrength.KEY_STRENGTH_192, AesVersion.TWO); } @Test public void testWriteLocalFileHeaderWithAes128v2() throws IOException { testWriteLocalFileHeaderWithAes(AesKeyStrength.KEY_STRENGTH_128, AesVersion.TWO); } @Test public void testWriteExtendedLocalFileHeaderWhenLocalFileHeaderIsNullThrowsException() throws IOException { expectedException.expectMessage("input parameters is null, cannot write extended local header"); expectedException.expect(ZipException.class); headerWriter.writeExtendedLocalHeader(null, new FileOutputStream(temporaryFolder.newFile())); } @Test public void testWriteExtendedLocalFileHeaderWhenOutputStreamIsNullThrowsException() throws IOException { expectedException.expectMessage("input parameters is null, cannot write extended local header"); expectedException.expect(ZipException.class); headerWriter.writeExtendedLocalHeader(new LocalFileHeader(), null); } @Test public void testWriteExtendedLocalFileHeaderNonZip64FormatWritesSuccessfully() throws IOException { testWriteExtendedLocalFileHeader(COMPRESSED_SIZE + 99, UNCOMPRESSED_SIZE + 423, 2342342L, false); } @Test public void testWriteExtendedLocalFileHeaderZip64FormatWritesSuccessfully() throws IOException { testWriteExtendedLocalFileHeader(COMPRESSED_SIZE_ZIP64 + 99, UNCOMPRESSED_SIZE_ZIP64 + 423, 32452342L, true); } @Test public void testFinalizeZipFileWhenZipModelNullThrowsException() throws IOException { expectedException.expect(ZipException.class); expectedException.expectMessage("input parameters is null, cannot finalize zip file"); headerWriter.finalizeZipFile(null, new FileOutputStream(temporaryFolder.newFile()), InternalZipConstants.CHARSET_UTF_8); } @Test public void testFinalizeZipFileWhenOutputStreamIsNullThrowsException() throws IOException { expectedException.expect(ZipException.class); expectedException.expectMessage("input parameters is null, cannot finalize zip file"); headerWriter.finalizeZipFile(new ZipModel(), null, InternalZipConstants.CHARSET_UTF_8); } @Test public void testFinalizeZipFileForNonZip64Format() throws IOException { ZipModel zipModel = createZipModel(10); File headersFile = temporaryFolder.newFile(); try(OutputStream outputStream = new FileOutputStream(headersFile)) { headerWriter.finalizeZipFile(zipModel, outputStream, InternalZipConstants.CHARSET_UTF_8); } try(RandomAccessFile randomAccessFile = new RandomAccessFile(headersFile, RandomAccessFileMode.READ.getValue())) { ZipModel readZipModel = headerReader.readAllHeaders(randomAccessFile, null); verifyZipModel(readZipModel, 10); for (FileHeader fileHeader : readZipModel.getCentralDirectory().getFileHeaders()) { assertThat(fileHeader.getZip64ExtendedInfo()).isNull(); assertThat(fileHeader.getAesExtraDataRecord()).isNull(); assertThat(fileHeader.getExtraFieldLength()).isZero(); } } } @Test public void testFinalizeZipFileForZip64Format() throws IOException { ZipModel zipModel = createZipModel(10, COMPRESSED_SIZE_ZIP64, UNCOMPRESSED_SIZE_ZIP64); File headersFile = temporaryFolder.newFile(); try(OutputStream outputStream = new FileOutputStream(headersFile)) { headerWriter.finalizeZipFile(zipModel, outputStream, InternalZipConstants.CHARSET_UTF_8); } try(RandomAccessFile randomAccessFile = new RandomAccessFile(headersFile, RandomAccessFileMode.READ.getValue())) { ZipModel readZipModel = headerReader.readAllHeaders(randomAccessFile, null); verifyZipModel(readZipModel, 10, COMPRESSED_SIZE_ZIP64, UNCOMPRESSED_SIZE_ZIP64, true); List fileHeaders = readZipModel.getCentralDirectory().getFileHeaders(); for (FileHeader fileHeader : fileHeaders) { verifyZip64ExtendedInfo(fileHeader.getZip64ExtendedInfo(), COMPRESSED_SIZE_ZIP64, UNCOMPRESSED_SIZE_ZIP64, 0, 0); assertThat(fileHeader.getAesExtraDataRecord()).isNull(); } } } @Test public void testFinalizeZipFileForAes() throws IOException { ZipModel zipModel = createZipModel(10); setFileHeadersAsAesEncrypted(zipModel.getCentralDirectory().getFileHeaders(), AesKeyStrength.KEY_STRENGTH_192); File headersFile = temporaryFolder.newFile(); try(OutputStream outputStream = new FileOutputStream(headersFile)) { headerWriter.finalizeZipFile(zipModel, outputStream, InternalZipConstants.CHARSET_UTF_8); } try(RandomAccessFile randomAccessFile = new RandomAccessFile(headersFile, RandomAccessFileMode.READ.getValue())) { ZipModel readZipModel = headerReader.readAllHeaders(randomAccessFile, null); verifyZipModel(readZipModel, 10); for (FileHeader fileHeader : readZipModel.getCentralDirectory().getFileHeaders()) { verifyAesExtraDataRecord(fileHeader.getAesExtraDataRecord(), AesKeyStrength.KEY_STRENGTH_192, AesVersion.TWO); assertThat(fileHeader.getZip64ExtendedInfo()).isNull(); } } } @Test public void testFinalizeZipFileForZip64FormatForSplitFileWithSplitOutputStream() throws IOException { ZipModel zipModel = createZipModel(10, COMPRESSED_SIZE_ZIP64, UNCOMPRESSED_SIZE_ZIP64); zipModel.setZip64EndOfCentralDirectoryRecord(null); zipModel.setZip64EndOfCentralDirectoryLocator(null); File headersFile = temporaryFolder.newFile(); try(SplitOutputStream outputStream = new SplitOutputStream(headersFile, 65536)) { headerWriter.finalizeZipFile(zipModel, outputStream, InternalZipConstants.CHARSET_UTF_8); } try(RandomAccessFile randomAccessFile = new RandomAccessFile(headersFile, RandomAccessFileMode.READ.getValue())) { ZipModel readZipModel = headerReader.readAllHeaders(randomAccessFile, null); verifyZipModel(readZipModel, 10, COMPRESSED_SIZE_ZIP64, UNCOMPRESSED_SIZE_ZIP64, true); List fileHeaders = readZipModel.getCentralDirectory().getFileHeaders(); for (FileHeader fileHeader : fileHeaders) { verifyZip64ExtendedInfo(fileHeader.getZip64ExtendedInfo(), COMPRESSED_SIZE_ZIP64, UNCOMPRESSED_SIZE_ZIP64, 0, 0); assertThat(fileHeader.getAesExtraDataRecord()).isNull(); } } } @Test public void testFinalizeZipFileForZip64FormatForSplitFileWithCountingOutputStream() throws IOException { ZipModel zipModel = createZipModel(10, COMPRESSED_SIZE_ZIP64, UNCOMPRESSED_SIZE_ZIP64); File headersFile = temporaryFolder.newFile(); try(CountingOutputStream outputStream = new CountingOutputStream(new SplitOutputStream(headersFile, 65536))) { headerWriter.finalizeZipFile(zipModel, outputStream, InternalZipConstants.CHARSET_UTF_8); } try(RandomAccessFile randomAccessFile = new RandomAccessFile(headersFile, RandomAccessFileMode.READ.getValue())) { ZipModel readZipModel = headerReader.readAllHeaders(randomAccessFile, null); verifyZipModel(readZipModel, 10, COMPRESSED_SIZE_ZIP64, UNCOMPRESSED_SIZE_ZIP64, true); List fileHeaders = readZipModel.getCentralDirectory().getFileHeaders(); for (FileHeader fileHeader : fileHeaders) { verifyZip64ExtendedInfo(fileHeader.getZip64ExtendedInfo(), COMPRESSED_SIZE_ZIP64, UNCOMPRESSED_SIZE_ZIP64, 0, 0); assertThat(fileHeader.getAesExtraDataRecord()).isNull(); } } } @Test public void testFinalizeZipFileWithoutValidationsWhenZipModelNullThrowsException() throws IOException { expectedException.expect(ZipException.class); expectedException.expectMessage("input parameters is null, cannot finalize zip file"); headerWriter.finalizeZipFileWithoutValidations(null, new FileOutputStream(temporaryFolder.newFile()), null); } @Test public void testFinalizeZipFileWithoutValidationsWhenOutputStreamIsNullThrowsException() throws IOException { expectedException.expect(ZipException.class); expectedException.expectMessage("input parameters is null, cannot finalize zip file"); headerWriter.finalizeZipFileWithoutValidations(new ZipModel(), null, null); } @Test public void testFinalizeZipFileWithoutValidationsForNonZip64Format() throws IOException { ZipModel zipModel = createZipModel(10); File headersFile = temporaryFolder.newFile(); try(OutputStream outputStream = new FileOutputStream(headersFile)) { headerWriter.finalizeZipFileWithoutValidations(zipModel, outputStream, InternalZipConstants.CHARSET_UTF_8); } try(RandomAccessFile randomAccessFile = new RandomAccessFile(headersFile, RandomAccessFileMode.READ.getValue())) { ZipModel readZipModel = headerReader.readAllHeaders(randomAccessFile, InternalZipConstants.CHARSET_UTF_8); verifyZipModel(readZipModel, 10); for (FileHeader fileHeader : readZipModel.getCentralDirectory().getFileHeaders()) { assertThat(fileHeader.getZip64ExtendedInfo()).isNull(); assertThat(fileHeader.getAesExtraDataRecord()).isNull(); assertThat(fileHeader.getExtraFieldLength()).isZero(); } } } @Test public void testFinalizeZipFileWithoutValidationsForZip64Format() throws IOException { ZipModel zipModel = createZipModel(10, COMPRESSED_SIZE_ZIP64, UNCOMPRESSED_SIZE_ZIP64); File headersFile = temporaryFolder.newFile(); try(OutputStream outputStream = new FileOutputStream(headersFile)) { headerWriter.finalizeZipFileWithoutValidations(zipModel, outputStream, InternalZipConstants.CHARSET_UTF_8); } try(RandomAccessFile randomAccessFile = new RandomAccessFile(headersFile, RandomAccessFileMode.READ.getValue())) { ZipModel readZipModel = headerReader.readAllHeaders(randomAccessFile, InternalZipConstants.CHARSET_UTF_8); verifyZipModel(readZipModel, 10, COMPRESSED_SIZE_ZIP64, UNCOMPRESSED_SIZE_ZIP64, true); List fileHeaders = readZipModel.getCentralDirectory().getFileHeaders(); for (FileHeader fileHeader : fileHeaders) { verifyZip64ExtendedInfo(fileHeader.getZip64ExtendedInfo(), COMPRESSED_SIZE_ZIP64, UNCOMPRESSED_SIZE_ZIP64, 0, 0); assertThat(fileHeader.getAesExtraDataRecord()).isNull(); } } } @Test public void testUpdateLocalFileHeaderWhenFileHeaderIsNullThrowsException() throws IOException { expectedException.expect(ZipException.class); expectedException.expectMessage("invalid input parameters, cannot update local file header"); headerWriter.updateLocalFileHeader(null, new ZipModel(), new SplitOutputStream(temporaryFolder.newFile())); } @Test public void testUpdateLocalFileHeaderWhenZipModelIsNullThrowsException() throws IOException { expectedException.expect(ZipException.class); expectedException.expectMessage("invalid input parameters, cannot update local file header"); headerWriter.updateLocalFileHeader(new FileHeader(), null, new SplitOutputStream(temporaryFolder.newFile())); } @Test public void testUpdateLocalFileHeaderForNonZip64() throws IOException { File headersFile = temporaryFolder.newFile(); createAndUpdateLocalFileHeader(headersFile, COMPRESSED_SIZE, UNCOMPRESSED_SIZE, 23423); try (InputStream inputStream = new FileInputStream(headersFile)) { LocalFileHeader readLocalFileHeader = headerReader.readLocalFileHeader(inputStream, InternalZipConstants.CHARSET_UTF_8); assertThat(readLocalFileHeader.getCompressedSize()).isEqualTo(COMPRESSED_SIZE + 100); assertThat(readLocalFileHeader.getUncompressedSize()).isEqualTo(UNCOMPRESSED_SIZE + 100); assertThat(readLocalFileHeader.getCrc()).isEqualTo(23423); assertThat(readLocalFileHeader.getZip64ExtendedInfo()).isNull(); } } @Test public void testUpdateLocalFileHeaderForZip64() throws IOException { File headersFile = temporaryFolder.newFile(); createAndUpdateLocalFileHeader(headersFile, COMPRESSED_SIZE_ZIP64, UNCOMPRESSED_SIZE_ZIP64, 546423); try (InputStream inputStream = new FileInputStream(headersFile)) { LocalFileHeader readLocalFileHeader = headerReader.readLocalFileHeader(inputStream, InternalZipConstants.CHARSET_UTF_8); assertThat(readLocalFileHeader.getCompressedSize()).isEqualTo(COMPRESSED_SIZE_ZIP64 + 100); assertThat(readLocalFileHeader.getUncompressedSize()).isEqualTo(UNCOMPRESSED_SIZE_ZIP64 + 100); assertThat(readLocalFileHeader.getCrc()).isEqualTo(546423); verifyZip64ExtendedInfo(readLocalFileHeader.getZip64ExtendedInfo(), COMPRESSED_SIZE_ZIP64 + 100, UNCOMPRESSED_SIZE_ZIP64 + 100, -1, -1); } verifyEntrySizesIsMaxValueInLFHWhenZip64Format(headersFile); } public void createAndUpdateLocalFileHeader(File headersFile, long compressedSize, long uncompressedSize, long crc) throws IOException { ZipModel zipModel = createZipModel(3); LocalFileHeader localFileHeaderToWrite = createLocalFileHeader("LFH", compressedSize, uncompressedSize, false); localFileHeaderToWrite.setCompressedSize(compressedSize); localFileHeaderToWrite.setUncompressedSize(uncompressedSize); localFileHeaderToWrite.setCrc(10); try (OutputStream outputStream = new FileOutputStream(headersFile)) { headerWriter.writeLocalFileHeader(zipModel, localFileHeaderToWrite, outputStream, InternalZipConstants.CHARSET_UTF_8); } try (SplitOutputStream splitOutputStream = new SplitOutputStream(headersFile)) { FileHeader fileHeader = createFileHeaders(1, compressedSize + 100, uncompressedSize + 100).get(0); fileHeader.setFileName(localFileHeaderToWrite.getFileName()); fileHeader.setFileNameLength(fileHeader.getFileName().getBytes(InternalZipConstants.CHARSET_UTF_8).length); fileHeader.setCrc(crc); headerWriter.updateLocalFileHeader(fileHeader, zipModel, splitOutputStream); } } private void setFileHeadersAsAesEncrypted(List fileHeaders, AesKeyStrength aesKeyStrength) { for (FileHeader fileHeader : fileHeaders) { fileHeader.setEncrypted(true); fileHeader.setEncryptionMethod(EncryptionMethod.AES); fileHeader.setAesExtraDataRecord(createAesExtraDataRecord(aesKeyStrength, AesVersion.TWO)); } } private void verifyZipModel(ZipModel zipModel, int numberOFEntriesInCentralDirectory) { verifyZipModel(zipModel, numberOFEntriesInCentralDirectory, COMPRESSED_SIZE, UNCOMPRESSED_SIZE, false); } private void verifyZipModel(ZipModel zipModel, int numberOFEntriesInCentralDirectory, long compressedSize, long uncompressedSize, boolean isZip64) { assertThat(zipModel).isNotNull(); assertThat(zipModel.getCentralDirectory()).isNotNull(); assertThat(zipModel.isZip64Format()).isEqualTo(isZip64); assertThat(zipModel.getCentralDirectory().getFileHeaders()).hasSize(numberOFEntriesInCentralDirectory); verifyFileHeaders(zipModel.getCentralDirectory().getFileHeaders(), compressedSize, uncompressedSize); } private void verifyFileHeaders(List fileHeaders, long compressedSize, long uncompressedSize) { for (int i = 0; i < fileHeaders.size(); i++) { FileHeader fileHeader = fileHeaders.get(i); assertThat(fileHeader).isNotNull(); assertThat(fileHeader.getVersionMadeBy()).isEqualTo(VERSION_MADE_BY); assertThat(fileHeader.getVersionNeededToExtract()).isEqualTo(VERSION_NEEDED_TO_EXTRACT); assertThat(fileHeader.getFileName()).isEqualTo(FILE_NAME_PREFIX + i); assertThat(fileHeader.getCompressedSize()).isEqualTo(compressedSize); assertThat(fileHeader.getUncompressedSize()).isEqualTo(uncompressedSize); assertThat(fileHeader.getFileComment()).isEqualTo(FILE_COMMENT_PREFIX + i); } } private void testWriteExtendedLocalFileHeader(long compressedSize, long uncompressedSize, long crc, boolean isZip64Format) throws IOException { LocalFileHeader localFileHeader = createLocalFileHeader("SOME_NAME", compressedSize, uncompressedSize, true); localFileHeader.setCrc(crc); localFileHeader.setWriteCompressedSizeInZip64ExtraRecord(isZip64Format); File headersFile = temporaryFolder.newFile(); try(OutputStream outputStream = new FileOutputStream(headersFile)) { headerWriter.writeExtendedLocalHeader(localFileHeader, outputStream); } try(InputStream inputStream = new FileInputStream(headersFile)) { DataDescriptor dataDescriptor = headerReader.readDataDescriptor(inputStream, isZip64Format); verifyDataDescriptor(dataDescriptor, compressedSize, uncompressedSize, crc); } } private void verifyDataDescriptor(DataDescriptor dataDescriptor, long compressedSize, long uncompressedSize, long crc) { assertThat(dataDescriptor).isNotNull(); assertThat(dataDescriptor.getSignature()).isEqualTo(HeaderSignature.EXTRA_DATA_RECORD); assertThat(dataDescriptor.getCompressedSize()).isEqualTo(compressedSize); assertThat(dataDescriptor.getUncompressedSize()).isEqualTo(uncompressedSize); assertThat(dataDescriptor.getCrc()).isEqualTo(crc); } private void testWriteLocalFileHeaderWithAes(AesKeyStrength aesKeyStrength, AesVersion aesVersion) throws IOException { ZipModel zipModel = createZipModel(10); LocalFileHeader localFileHeaderToWrite = createLocalFileHeader("TEXT", COMPRESSED_SIZE, UNCOMPRESSED_SIZE, true); localFileHeaderToWrite.setEncryptionMethod(EncryptionMethod.AES); localFileHeaderToWrite.setAesExtraDataRecord(createAesExtraDataRecord(aesKeyStrength, aesVersion)); File headersFile = temporaryFolder.newFile(); try(OutputStream outputStream = new FileOutputStream(headersFile)) { headerWriter.writeLocalFileHeader(zipModel, localFileHeaderToWrite, outputStream, InternalZipConstants.CHARSET_UTF_8); } try(InputStream inputStream = new FileInputStream(headersFile)) { LocalFileHeader readLocalFileHeader = headerReader.readLocalFileHeader(inputStream, InternalZipConstants.CHARSET_UTF_8); assertThat(readLocalFileHeader.getEncryptionMethod()).isEqualTo(EncryptionMethod.AES); verifyAesExtraDataRecord(readLocalFileHeader.getAesExtraDataRecord(), aesKeyStrength, aesVersion); } } private AESExtraDataRecord createAesExtraDataRecord(AesKeyStrength aesKeyStrength, AesVersion aesVersion) { AESExtraDataRecord aesDataRecord = new AESExtraDataRecord(); aesDataRecord.setSignature(HeaderSignature.AES_EXTRA_DATA_RECORD); aesDataRecord.setDataSize(7); aesDataRecord.setAesVersion(aesVersion); aesDataRecord.setVendorID("AE"); aesDataRecord.setAesKeyStrength(aesKeyStrength); aesDataRecord.setCompressionMethod(CompressionMethod.DEFLATE); return aesDataRecord; } private void verifyAesExtraDataRecord(AESExtraDataRecord aesExtraDataRecord, AesKeyStrength aesKeyStrength, AesVersion aesVersion) { assertThat(aesExtraDataRecord).isNotNull(); assertThat(aesExtraDataRecord.getAesKeyStrength()).isEqualTo(aesKeyStrength); assertThat(aesExtraDataRecord.getAesVersion()).isEqualTo(aesVersion); assertThat(aesExtraDataRecord.getCompressionMethod()).isEqualTo(CompressionMethod.DEFLATE); assertThat(aesExtraDataRecord.getVendorID()).isEqualTo("AE"); } private void testWriteLocalFileHeaderWithFileName(String fileNameSuffix, boolean useUtf8, boolean expectFileNamesToMatch) throws IOException { testWriteLocalFileHeaderWithFileNameAndCharset(fileNameSuffix, useUtf8, expectFileNamesToMatch, InternalZipConstants.CHARSET_UTF_8); } private void testWriteLocalFileHeaderWithFileNameAndCharset(String fileNameSuffix, boolean useUtf8, boolean expectFileNamesToMatch, Charset charset) throws IOException { ZipModel zipModel = createZipModel(10); LocalFileHeader localFileHeaderToWrite = createLocalFileHeader(fileNameSuffix, COMPRESSED_SIZE, UNCOMPRESSED_SIZE, useUtf8); File headersFile = temporaryFolder.newFile(); try(OutputStream outputStream = new FileOutputStream(headersFile)) { headerWriter.writeLocalFileHeader(zipModel, localFileHeaderToWrite, outputStream, charset); } try(InputStream inputStream = new FileInputStream(headersFile)) { LocalFileHeader readLocalFileHeader = headerReader.readLocalFileHeader(inputStream, charset); if (expectFileNamesToMatch) { assertThat(readLocalFileHeader.getFileName()).isEqualTo(FILE_NAME_PREFIX + fileNameSuffix); } else { assertThat(readLocalFileHeader.getFileName()).isNotEqualTo(FILE_NAME_PREFIX + fileNameSuffix); } } } private void verifyEntrySizesIsMaxValueInLFHWhenZip64Format(File headersFile) throws IOException { try(RandomAccessFile randomAccessFile = new RandomAccessFile(headersFile, RandomAccessFileMode.READ.getValue())) { randomAccessFile.seek(18); long compressedSize = rawIO.readLongLittleEndian(randomAccessFile, 4); assertThat(compressedSize).isEqualTo(InternalZipConstants.ZIP_64_SIZE_LIMIT); long uncompressedSize = rawIO.readLongLittleEndian(randomAccessFile, 4); assertThat(uncompressedSize).isEqualTo(InternalZipConstants.ZIP_64_SIZE_LIMIT); } } private void verifyLocalFileHeader(LocalFileHeader localFileHeader, String expectedFileName, long compressedSize, long uncompressedSize) { assertThat(localFileHeader).isNotNull(); assertThat(localFileHeader.getVersionNeededToExtract()).isEqualTo(VERSION_NEEDED_TO_EXTRACT); assertThat(localFileHeader.getFileName()).isEqualTo(expectedFileName); assertThat(localFileHeader.getCompressedSize()).isEqualTo(compressedSize); assertThat(localFileHeader.getUncompressedSize()).isEqualTo(uncompressedSize); assertThat(localFileHeader.getCompressionMethod()).isEqualTo(CompressionMethod.DEFLATE); assertThat(localFileHeader.getLastModifiedTime()).isEqualTo(LAST_MODIFIED_FILE_TIME); } private void verifyZip64ExtendedInfo(Zip64ExtendedInfo zip64ExtendedInfo, long compressedSize, long uncompressedSize, int offsetStartOfLocalFileHeader, int diskNumberStart) { assertThat(zip64ExtendedInfo).isNotNull(); assertThat(zip64ExtendedInfo.getCompressedSize()).isEqualTo(compressedSize); assertThat(zip64ExtendedInfo.getUncompressedSize()).isEqualTo(uncompressedSize); assertThat(zip64ExtendedInfo.getOffsetLocalHeader()).isEqualTo(offsetStartOfLocalFileHeader); assertThat(zip64ExtendedInfo.getDiskNumberStart()).isEqualTo(diskNumberStart); } private LocalFileHeader createLocalFileHeader(String fileNameSuffix, long compressedSize, long uncompressedSize, boolean useUtf8) { LocalFileHeader localFileHeader = new LocalFileHeader(); localFileHeader.setVersionNeededToExtract(VERSION_NEEDED_TO_EXTRACT); localFileHeader.setFileName(FILE_NAME_PREFIX + fileNameSuffix); localFileHeader.setCompressedSize(compressedSize); localFileHeader.setUncompressedSize(uncompressedSize); localFileHeader.setGeneralPurposeFlag(generateGeneralPurposeBytes(useUtf8)); localFileHeader.setCompressionMethod(CompressionMethod.DEFLATE); localFileHeader.setLastModifiedTime(LAST_MODIFIED_FILE_TIME); return localFileHeader; } private ZipModel createZipModel(int numberOfEntriesInCentralDirectory) { return createZipModel(numberOfEntriesInCentralDirectory, COMPRESSED_SIZE, UNCOMPRESSED_SIZE); } private ZipModel createZipModel(int numberOfEntriesInCentralDirectory, long compressedSize, long uncompressedSize) { ZipModel zipModel = new ZipModel(); zipModel.setCentralDirectory(createCentralDirectory(numberOfEntriesInCentralDirectory, compressedSize, uncompressedSize)); return zipModel; } private CentralDirectory createCentralDirectory(int numberOfEntriesInCentralDirectory, long compressedSize, long uncompressedSize) { CentralDirectory centralDirectory = new CentralDirectory(); centralDirectory.setFileHeaders(createFileHeaders(numberOfEntriesInCentralDirectory, compressedSize, uncompressedSize)); return centralDirectory; } private byte[] generateGeneralPurposeBytes(boolean useUtf8) { byte[] generalPurposeBytes = new byte[2]; if (useUtf8) { generalPurposeBytes[1] = BitUtils.setBit(generalPurposeBytes[1], 3); } return generalPurposeBytes; } private List createFileHeaders(int numberOfEntriesInCentralDirectory, long compressedSize, long uncompressedSize) { List fileHeaders = new ArrayList<>(); for (int i = 0; i < numberOfEntriesInCentralDirectory; i++) { FileHeader fileHeader = new FileHeader(); fileHeader.setVersionMadeBy(VERSION_MADE_BY); fileHeader.setVersionNeededToExtract(VERSION_NEEDED_TO_EXTRACT); fileHeader.setFileName(FILE_NAME_PREFIX + i); fileHeader.setGeneralPurposeFlag(generateGeneralPurposeBytes(true)); fileHeader.setCompressedSize(compressedSize); fileHeader.setUncompressedSize(uncompressedSize); fileHeader.setCompressionMethod(CompressionMethod.DEFLATE); fileHeader.setExternalFileAttributes(EXTERNAL_FILE_ATTRIBUTES); fileHeader.setFileComment(FILE_COMMENT_PREFIX + i); fileHeaders.add(fileHeader); } return fileHeaders; } }zip4j-2.2.8/src/test/java/net/lingala/zip4j/io/000077500000000000000000000000001357650161600211305ustar00rootroot00000000000000zip4j-2.2.8/src/test/java/net/lingala/zip4j/io/inputstream/000077500000000000000000000000001357650161600235035ustar00rootroot00000000000000zip4j-2.2.8/src/test/java/net/lingala/zip4j/io/inputstream/ZipInputStreamIT.java000066400000000000000000000273231357650161600275500ustar00rootroot00000000000000package net.lingala.zip4j.io.inputstream; import net.lingala.zip4j.AbstractIT; import net.lingala.zip4j.ZipFile; import net.lingala.zip4j.model.LocalFileHeader; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.AesKeyStrength; import net.lingala.zip4j.model.enums.AesVersion; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.model.enums.EncryptionMethod; import net.lingala.zip4j.util.InternalZipConstants; import org.junit.Test; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.HashSet; import java.util.Random; import java.util.Set; import static net.lingala.zip4j.testutils.TestUtils.getTestFileFromResources; import static net.lingala.zip4j.testutils.ZipFileVerifier.verifyFileContent; import static org.assertj.core.api.Assertions.assertThat; public class ZipInputStreamIT extends AbstractIT { @Test public void testExtractStoreWithoutEncryption() throws IOException { File createdZipFile = createZipFile(CompressionMethod.STORE); extractZipFileWithInputStreams(createdZipFile, null); } @Test public void testExtractStoreWithZipStandardEncryption() throws IOException { File createdZipFile = createZipFile(CompressionMethod.STORE, true, EncryptionMethod.ZIP_STANDARD, null, PASSWORD); extractZipFileWithInputStreams(createdZipFile, PASSWORD); } @Test public void testExtractStoreWithAesEncryption128() throws IOException { File createdZipFile = createZipFile(CompressionMethod.STORE, true, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128, PASSWORD); extractZipFileWithInputStreams(createdZipFile, PASSWORD); } @Test public void testExtractStoreWithAesEncryption256() throws IOException { File createdZipFile = createZipFile(CompressionMethod.STORE, true, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256, PASSWORD); extractZipFileWithInputStreams(createdZipFile, PASSWORD); } @Test public void testExtractDeflateWithoutEncryption() throws IOException { File createdZipFile = createZipFile(CompressionMethod.DEFLATE); extractZipFileWithInputStreams(createdZipFile, null); } @Test public void testExtractDeflateWithAesEncryption128() throws IOException { File createdZipFile = createZipFile(CompressionMethod.DEFLATE, true, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128, PASSWORD); extractZipFileWithInputStreams(createdZipFile, PASSWORD); } @Test public void testExtractDeflateWithAesEncryption256() throws IOException { File createdZipFile = createZipFile(CompressionMethod.DEFLATE, true, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256, PASSWORD); extractZipFileWithInputStreams(createdZipFile, PASSWORD); } @Test public void testExtractDeflateWithAesEncryption256AndV1() throws IOException { File createdZipFile = createZipFile(CompressionMethod.DEFLATE, true, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256, PASSWORD, AesVersion.ONE); extractZipFileWithInputStreams(createdZipFile, PASSWORD, InternalZipConstants.BUFF_SIZE, AesVersion.ONE); } @Test public void testExtractWithReadLengthLessThan16WithAesAndStoreCompression() throws IOException { File createZipFile = createZipFile(CompressionMethod.STORE, true, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256, PASSWORD); extractZipFileWithInputStreams(createZipFile, PASSWORD, 15, AesVersion.TWO); } @Test public void testExtractWithReadLengthLessThan16WithAesAndDeflateCompression() throws IOException { File createZipFile = createZipFile(CompressionMethod.DEFLATE, true, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256, PASSWORD); extractZipFileWithInputStreams(createZipFile, PASSWORD, 15, AesVersion.TWO); } @Test public void testExtractWithReadLengthLessThan16WithZipCryptoAndStoreCompression() throws IOException { File createZipFile = createZipFile(CompressionMethod.DEFLATE, true, EncryptionMethod.ZIP_STANDARD, null, PASSWORD); extractZipFileWithInputStreams(createZipFile, PASSWORD, 12, null); } @Test public void testExtractWithReadLengthLessThan16WithZipCryptoAndDeflateCompression() throws IOException { File createZipFile = createZipFile(CompressionMethod.DEFLATE, true, EncryptionMethod.ZIP_STANDARD, null, PASSWORD); extractZipFileWithInputStreams(createZipFile, PASSWORD, 5, null); } @Test public void testExtractWithReadLengthGreaterThanButNotMultipleOf16WithAesAndStoreCompression() throws IOException { File createZipFile = createZipFile(CompressionMethod.STORE, true, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256, PASSWORD); extractZipFileWithInputStreams(createZipFile, PASSWORD, (16 * 4) + 1, AesVersion.TWO); } @Test public void testExtractWithReadLengthGreaterThanButNotMultipleOf16WithAesAndDeflateCompression() throws IOException { File createZipFile = createZipFile(CompressionMethod.DEFLATE, true, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256, PASSWORD); extractZipFileWithInputStreams(createZipFile, PASSWORD, (16 * 8) - 10, AesVersion.TWO); } @Test public void testExtractWithReadLengthGreaterThanButNotMultipleOf16WithZipCryptoAndStoreCompression() throws IOException { File createZipFile = createZipFile(CompressionMethod.DEFLATE, true, EncryptionMethod.ZIP_STANDARD, null, PASSWORD); extractZipFileWithInputStreams(createZipFile, PASSWORD, (16 * 2) - 6, null); } @Test public void testExtractWithReadLengthGreaterThanButNotMultipleOf16WithZipCryptoAndDeflateCompression() throws IOException { File createZipFile = createZipFile(CompressionMethod.DEFLATE, true, EncryptionMethod.ZIP_STANDARD, null, PASSWORD); extractZipFileWithInputStreams(createZipFile, PASSWORD, (16 * 10) - 11, null); } @Test public void testExtractWithRandomLengthWithAesAndDeflateCompression() throws IOException { Random random = new Random(); File createZipFile = createZipFile(CompressionMethod.DEFLATE, true, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256, PASSWORD); LocalFileHeader localFileHeader; int readLen; byte[] readBuffer = new byte[4096]; int numberOfEntriesExtracted = 0; try (FileInputStream fileInputStream = new FileInputStream(createZipFile)) { try (ZipInputStream zipInputStream = new ZipInputStream(fileInputStream, PASSWORD)) { while ((localFileHeader = zipInputStream.getNextEntry()) != null) { File extractedFile = temporaryFolder.newFile(localFileHeader.getFileName()); try (OutputStream outputStream = new FileOutputStream(extractedFile)) { while ((readLen = zipInputStream.read(readBuffer, 0, random.nextInt((25 - 1) + 1) + 1)) != -1) { outputStream.write(readBuffer, 0, readLen); } } verifyFileContent(getTestFileFromResources(localFileHeader.getFileName()), extractedFile); numberOfEntriesExtracted++; } } } assertThat(numberOfEntriesExtracted).isEqualTo(FILES_TO_ADD.size()); } @Test public void testExtractFilesForZipFileWithInvalidExtraDataRecordIgnoresIt() throws IOException { InputStream inputStream = new FileInputStream(getTestArchiveFromResources("invalid_extra_data_record.zip")); ZipInputStream zipInputStream = new ZipInputStream(inputStream, "password".toCharArray()); byte[] b = new byte[4096]; while (zipInputStream.getNextEntry() != null) { while (zipInputStream.read(b) != -1) { } } zipInputStream.close(); } @Test public void testGetNextEntryReturnsNextEntryEvenIfEntryNotCompletelyRead() throws IOException { File createZipFile = createZipFile(CompressionMethod.DEFLATE); ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(createZipFile)); int numberOfEntries = 0; while (zipInputStream.getNextEntry() != null) { numberOfEntries++; } assertThat(numberOfEntries).isEqualTo(FILES_TO_ADD.size()); } @Test public void testGetFileNamesWithChineseCharset() throws IOException { InputStream inputStream = new FileInputStream(getTestArchiveFromResources("testfile_with_chinese_filename_by_7zip.zip")); ZipInputStream zipInputStream = new ZipInputStream(inputStream, CHARSET_GBK); LocalFileHeader localFileHeader; String expactedFileName = "fff - 副本.txt"; Set filenameSet = new HashSet<>(); while ((localFileHeader = zipInputStream.getNextEntry()) != null) { filenameSet.add(localFileHeader.getFileName()); } assertThat(filenameSet.contains(expactedFileName)).isTrue(); } private void extractZipFileWithInputStreams(File zipFile, char[] password) throws IOException { extractZipFileWithInputStreams(zipFile, password, 4096, AesVersion.TWO); } private void extractZipFileWithInputStreams(File zipFile, char[] password, int bufferLength, AesVersion aesVersion) throws IOException { LocalFileHeader localFileHeader; int readLen; byte[] readBuffer = new byte[bufferLength]; int numberOfEntriesExtracted = 0; try (FileInputStream fileInputStream = new FileInputStream(zipFile)) { try (ZipInputStream zipInputStream = new ZipInputStream(fileInputStream, password)) { while ((localFileHeader = zipInputStream.getNextEntry()) != null) { File extractedFile = temporaryFolder.newFile(localFileHeader.getFileName()); try (OutputStream outputStream = new FileOutputStream(extractedFile)) { while ((readLen = zipInputStream.read(readBuffer)) != -1) { outputStream.write(readBuffer, 0, readLen); } } verifyLocalFileHeader(localFileHeader); verifyFileContent(getTestFileFromResources(localFileHeader.getFileName()), extractedFile); numberOfEntriesExtracted++; } } } assertThat(numberOfEntriesExtracted).isEqualTo(FILES_TO_ADD.size()); } private void verifyLocalFileHeader(LocalFileHeader localFileHeader) { assertThat(localFileHeader).isNotNull(); if (localFileHeader.isEncrypted() && localFileHeader.getEncryptionMethod().equals(EncryptionMethod.AES) && localFileHeader.getAesExtraDataRecord().getAesVersion().equals(AesVersion.TWO)) { assertThat(localFileHeader.getCrc()).isZero(); } } private File createZipFile(CompressionMethod compressionMethod) throws IOException { return createZipFile(compressionMethod, false, null, null, null); } private File createZipFile(CompressionMethod compressionMethod, boolean encryptFiles, EncryptionMethod encryptionMethod, AesKeyStrength aesKeyStrength, char[] password) throws IOException { return createZipFile(compressionMethod, encryptFiles, encryptionMethod, aesKeyStrength, password, AesVersion.TWO); } private File createZipFile(CompressionMethod compressionMethod, boolean encryptFiles, EncryptionMethod encryptionMethod, AesKeyStrength aesKeyStrength, char[] password, AesVersion aesVersion) throws IOException { File outputFile = temporaryFolder.newFile("output.zip"); deleteFileIfExists(outputFile); ZipFile zipFile = new ZipFile(outputFile, password); ZipParameters zipParameters = new ZipParameters(); zipParameters.setCompressionMethod(compressionMethod); zipParameters.setEncryptFiles(encryptFiles); zipParameters.setEncryptionMethod(encryptionMethod); zipParameters.setAesKeyStrength(aesKeyStrength); zipParameters.setAesVersion(aesVersion); zipFile.addFiles(AbstractIT.FILES_TO_ADD, zipParameters); return outputFile; } private void deleteFileIfExists(File file) { if (file.exists()) { if (!file.delete()) { throw new RuntimeException("Could not delete an existing zip file"); } } } }zip4j-2.2.8/src/test/java/net/lingala/zip4j/io/outputstream/000077500000000000000000000000001357650161600237045ustar00rootroot00000000000000zip4j-2.2.8/src/test/java/net/lingala/zip4j/io/outputstream/ZipOutputStreamIT.java000066400000000000000000000206711357650161600301510ustar00rootroot00000000000000package net.lingala.zip4j.io.outputstream; import net.lingala.zip4j.AbstractIT; import net.lingala.zip4j.ZipFile; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.model.enums.AesKeyStrength; import net.lingala.zip4j.model.enums.AesVersion; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.model.enums.EncryptionMethod; import net.lingala.zip4j.util.BitUtils; import net.lingala.zip4j.util.InternalZipConstants; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import static net.lingala.zip4j.testutils.TestUtils.getTestFileFromResources; import static net.lingala.zip4j.testutils.ZipFileVerifier.verifyZipFileByExtractingAllFiles; import static org.assertj.core.api.Assertions.assertThat; public class ZipOutputStreamIT extends AbstractIT { @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void testZipOutputStreamStoreWithoutEncryption() throws IOException { testZipOutputStream(CompressionMethod.STORE, false, null, null, null); } @Test public void testZipOutputStreamStoreWithStandardEncryption() throws IOException { testZipOutputStream(CompressionMethod.STORE, true, EncryptionMethod.ZIP_STANDARD, null, null); } @Test public void testZipOutputStreamStoreWithAES256V1() throws IOException { testZipOutputStream(CompressionMethod.STORE, true, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256, AesVersion.ONE); } @Test public void testZipOutputStreamStoreWithAES128V2() throws IOException { testZipOutputStream(CompressionMethod.STORE, true, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128, AesVersion.TWO); } @Test public void testZipOutputStreamStoreWithAES256V2() throws IOException { testZipOutputStream(CompressionMethod.STORE, true, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256, AesVersion.TWO); } @Test public void testZipOutputStreamDeflateWithoutEncryption() throws IOException { testZipOutputStream(CompressionMethod.DEFLATE, false, null, null, null); } @Test public void testZipOutputStreamDeflateWithoutEncryptionAndKoreanFilename() throws IOException { List filesToAdd = new ArrayList<>(); filesToAdd.add(getTestFileFromResources("가나다.abc")); testZipOutputStream(CompressionMethod.DEFLATE, false, null, null, null, true, filesToAdd, CHARSET_CP_949); } @Test public void testZipOutputStreamDeflateWithStandardEncryption() throws IOException { testZipOutputStream(CompressionMethod.DEFLATE, true, EncryptionMethod.ZIP_STANDARD, null, null); } @Test public void testZipOutputStreamDeflateWithStandardEncryptionWhenModifiedFileTimeNotSet() throws IOException { testZipOutputStream(CompressionMethod.DEFLATE, true, EncryptionMethod.ZIP_STANDARD, null, null, false); } @Test public void testZipOutputStreamDeflateWithAES128V1() throws IOException { testZipOutputStream(CompressionMethod.DEFLATE, true, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128, AesVersion.ONE); } @Test public void testZipOutputStreamDeflateWithAES128() throws IOException { testZipOutputStream(CompressionMethod.DEFLATE, true, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_128, AesVersion.TWO); } @Test public void testZipOutputStreamDeflateWithAES256() throws IOException { testZipOutputStream(CompressionMethod.DEFLATE, true, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256, AesVersion.TWO); } @Test public void testZipOutputStreamDeflateWithNullVersionUsesV2() throws IOException { testZipOutputStream(CompressionMethod.DEFLATE, true, EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256, null); } @Test public void testZipOutputStreamThrowsExceptionWhenEntrySizeNotSetForStoreCompression() throws IOException { ZipParameters zipParameters = new ZipParameters(); zipParameters.setCompressionMethod(CompressionMethod.STORE); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("uncompressed size should be set for zip entries of compression type store"); try(ZipOutputStream zos = initializeZipOutputStream(false, InternalZipConstants.CHARSET_UTF_8)) { for (File fileToAdd : FILES_TO_ADD) { zipParameters.setLastModifiedFileTime(fileToAdd.lastModified()); zipParameters.setFileNameInZip(fileToAdd.getName()); zos.putNextEntry(zipParameters); } } } private void testZipOutputStream(CompressionMethod compressionMethod, boolean encrypt, EncryptionMethod encryptionMethod, AesKeyStrength aesKeyStrength, AesVersion aesVersion) throws IOException { testZipOutputStream(compressionMethod, encrypt, encryptionMethod, aesKeyStrength, aesVersion, true); } private void testZipOutputStream(CompressionMethod compressionMethod, boolean encrypt, EncryptionMethod encryptionMethod, AesKeyStrength aesKeyStrength, AesVersion aesVersion, boolean setLastModifiedTime) throws IOException { testZipOutputStream(compressionMethod, encrypt, encryptionMethod, aesKeyStrength, aesVersion, true, FILES_TO_ADD, InternalZipConstants.CHARSET_UTF_8); } private void testZipOutputStream(CompressionMethod compressionMethod, boolean encrypt, EncryptionMethod encryptionMethod, AesKeyStrength aesKeyStrength, AesVersion aesVersion, boolean setLastModifiedTime, List filesToAdd, Charset charset) throws IOException { ZipParameters zipParameters = buildZipParameters(compressionMethod, encrypt, encryptionMethod, aesKeyStrength); zipParameters.setAesVersion(aesVersion); byte[] buff = new byte[4096]; int readLen; try(ZipOutputStream zos = initializeZipOutputStream(encrypt, charset)) { for (File fileToAdd : filesToAdd) { if (zipParameters.getCompressionMethod() == CompressionMethod.STORE) { zipParameters.setEntrySize(fileToAdd.length()); } if (setLastModifiedTime) { zipParameters.setLastModifiedFileTime(fileToAdd.lastModified()); } zipParameters.setFileNameInZip(fileToAdd.getName()); zos.putNextEntry(zipParameters); try(InputStream inputStream = new FileInputStream(fileToAdd)) { while ((readLen = inputStream.read(buff)) != -1) { zos.write(buff, 0, readLen); } } zos.closeEntry(); } } verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, filesToAdd.size(), true, charset); verifyEntries(); } private void verifyEntries() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile); for (FileHeader fileHeader : zipFile.getFileHeaders()) { byte[] generalPurposeBytes = fileHeader.getGeneralPurposeFlag(); assertThat(BitUtils.isBitSet(generalPurposeBytes[0], 3)).isTrue(); if (fileHeader.isEncrypted() && fileHeader.getEncryptionMethod().equals(EncryptionMethod.AES)) { if (fileHeader.getAesExtraDataRecord().getAesVersion().equals(AesVersion.TWO)) { assertThat(fileHeader.getCrc()).isZero(); } else if (fileHeader.getCompressedSize() > 0) { assertThat(fileHeader.getCrc()).isNotZero(); } } } } private ZipOutputStream initializeZipOutputStream(boolean encrypt, Charset charset) throws IOException { FileOutputStream fos = new FileOutputStream(generatedZipFile); if (encrypt) { return new ZipOutputStream(fos, PASSWORD, charset); } return new ZipOutputStream(fos, null, charset); } private ZipParameters buildZipParameters(CompressionMethod compressionMethod, boolean encrypt, EncryptionMethod encryptionMethod, AesKeyStrength aesKeyStrength) { ZipParameters zipParameters = new ZipParameters(); zipParameters.setCompressionMethod(compressionMethod); zipParameters.setEncryptionMethod(encryptionMethod); zipParameters.setAesKeyStrength(aesKeyStrength); zipParameters.setEncryptFiles(encrypt); return zipParameters; } }zip4j-2.2.8/src/test/java/net/lingala/zip4j/model/000077500000000000000000000000001357650161600216215ustar00rootroot00000000000000zip4j-2.2.8/src/test/java/net/lingala/zip4j/model/enums/000077500000000000000000000000001357650161600227505ustar00rootroot00000000000000zip4j-2.2.8/src/test/java/net/lingala/zip4j/model/enums/CompressionMethodTest.java000066400000000000000000000014411357650161600301150ustar00rootroot00000000000000package net.lingala.zip4j.model.enums; import net.lingala.zip4j.exception.ZipException; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; public class CompressionMethodTest { @Test public void testGetCompressionMethodFromCodeForUnknownTypeThrowsException() { try { CompressionMethod.getCompressionMethodFromCode(34); fail("Should throw an exception"); } catch (ZipException e) { assertThat(e.getType()).isEqualTo(ZipException.Type.UNKNOWN_COMPRESSION_METHOD); } } @Test public void testGetCompressionMethodFromCodeForDeflateReturnsDeflate() throws ZipException { assertThat(CompressionMethod.getCompressionMethodFromCode(8)).isEqualTo(CompressionMethod.DEFLATE); } }zip4j-2.2.8/src/test/java/net/lingala/zip4j/testutils/000077500000000000000000000000001357650161600225615ustar00rootroot00000000000000zip4j-2.2.8/src/test/java/net/lingala/zip4j/testutils/ControlledReadInputStream.java000066400000000000000000000016361357650161600305270ustar00rootroot00000000000000package net.lingala.zip4j.testutils; import java.io.IOException; import java.io.InputStream; public class ControlledReadInputStream extends InputStream { private InputStream inputStream; private int readLimit; private byte[] singleByteBuffer = new byte[1]; public ControlledReadInputStream(InputStream inputStream, int maximumNumberOfBytesToReadAtOnce) { this.inputStream = inputStream; this.readLimit = maximumNumberOfBytesToReadAtOnce; } @Override public int read() throws IOException { int readLen = read(singleByteBuffer); if (readLen == -1) { return -1; } return singleByteBuffer[0]; } @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @Override public int read(byte[] b, int off, int len) throws IOException { int toRead = len > readLimit ? readLimit : len; return inputStream.read(b, off, toRead); } } zip4j-2.2.8/src/test/java/net/lingala/zip4j/testutils/HeaderVerifier.java000066400000000000000000000040201357650161600263040ustar00rootroot00000000000000package net.lingala.zip4j.testutils; import net.lingala.zip4j.ZipFile; import net.lingala.zip4j.headers.HeaderReader; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.LocalFileHeader; import net.lingala.zip4j.util.InternalZipConstants; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import static org.assertj.core.api.Assertions.assertThat; public class HeaderVerifier { HeaderReader headerReader = new HeaderReader(); public void verifyLocalFileHeaderUncompressedSize(File generatedZipFile, String fileNameInZipToVerify, long expectedUncompressedSize) throws IOException { LocalFileHeader localFileHeader = getLocalFileHeaderForEntry(generatedZipFile, fileNameInZipToVerify); assertThat(localFileHeader.getUncompressedSize()).isEqualTo(expectedUncompressedSize); } private LocalFileHeader getLocalFileHeaderForEntry(File generatedZipFile, String fileNameInZipToVerify) throws IOException { InputStream inputStream = positionRandomAccessFileToLocalFileHeaderStart(generatedZipFile, fileNameInZipToVerify); return headerReader.readLocalFileHeader(inputStream, InternalZipConstants.CHARSET_UTF_8); } private InputStream positionRandomAccessFileToLocalFileHeaderStart(File generatedZipFile, String fileNameInZip) throws IOException{ ZipFile zipFile = new ZipFile(generatedZipFile); FileHeader fileHeader = zipFile.getFileHeader(fileNameInZip); if (fileHeader == null) { throw new RuntimeException("Cannot find an entry with name: " + fileNameInZip + " in zip file: " + generatedZipFile); } InputStream inputStream = new FileInputStream(generatedZipFile); if (inputStream.skip(fileHeader.getOffsetLocalHeader()) != fileHeader.getOffsetLocalHeader()) { throw new IOException("Cannot skip " + fileHeader.getOffsetLocalHeader() + " bytes for entry " + fileHeader.getFileName()); } return inputStream; } } zip4j-2.2.8/src/test/java/net/lingala/zip4j/testutils/RandomInputStream.java000066400000000000000000000022511357650161600270400ustar00rootroot00000000000000package net.lingala.zip4j.testutils; import java.io.IOException; import java.io.InputStream; import java.util.Random; public class RandomInputStream extends InputStream { private static final Random RANDOM = new Random(); private long remaining; private boolean streamClosed; public RandomInputStream(long length) { this.remaining = length; this.streamClosed = false; } @Override public int read() throws IOException { if (remaining <= 0) { return -1; } assertStreamNotClosed(); return RANDOM.nextInt(127); } @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @Override public int read(byte[] b, int off, int len) throws IOException { if (remaining <= 0) { return -1; } assertStreamNotClosed(); int toRead = len; if (remaining <= len) { toRead = (int) remaining; } RANDOM.nextBytes(b); remaining -= toRead; return toRead; } private void assertStreamNotClosed() throws IOException { if (streamClosed) { throw new IOException("Stream closed"); } } @Override public void close() { streamClosed = true; } } zip4j-2.2.8/src/test/java/net/lingala/zip4j/testutils/SlowTests.java000066400000000000000000000001051357650161600253670ustar00rootroot00000000000000package net.lingala.zip4j.testutils; public interface SlowTests { } zip4j-2.2.8/src/test/java/net/lingala/zip4j/testutils/TestUtils.java000066400000000000000000000023011357650161600253600ustar00rootroot00000000000000package net.lingala.zip4j.testutils; import net.lingala.zip4j.util.InternalZipConstants; import java.io.File; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; public class TestUtils { private static final String TEST_FILES_FOLDER_NAME = "test-files"; private static final String TEST_ARCHIVES_FOLDER_NAME = "test-archives"; public static File getTestFileFromResources(String fileName) { return getFileFromResources(TEST_FILES_FOLDER_NAME, fileName); } public static File getTestArchiveFromResources(String fileName) { return getFileFromResources(TEST_ARCHIVES_FOLDER_NAME, fileName); } public static Boolean isWindows() { String os = System.getProperty("os.name").toLowerCase(); return (os.contains("win")); } private static File getFileFromResources(String parentFolder, String fileName) { try { String path = "/" + parentFolder + "/" + fileName; String utfDecodedFilePath = URLDecoder.decode(TestUtils.class.getResource(path).getFile(), InternalZipConstants.CHARSET_UTF_8.toString()); return new File(utfDecodedFilePath); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } } zip4j-2.2.8/src/test/java/net/lingala/zip4j/testutils/ZipFileVerifier.java000066400000000000000000000065071357650161600264720ustar00rootroot00000000000000package net.lingala.zip4j.testutils; import net.lingala.zip4j.ZipFile; import net.lingala.zip4j.progress.ProgressMonitor; import net.lingala.zip4j.util.CrcUtil; import net.lingala.zip4j.util.FileUtils; import net.lingala.zip4j.util.InternalZipConstants; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; public class ZipFileVerifier { public static void verifyZipFileByExtractingAllFiles(File zipFileToExtract, File outputFolder, int expectedNumberOfEntries) throws IOException { verifyZipFileByExtractingAllFiles(zipFileToExtract, null, outputFolder, expectedNumberOfEntries); } public static void verifyZipFileByExtractingAllFiles(File zipFileToExtract, char[] password, File outputFolder, int expectedNumberOfEntries) throws IOException { verifyZipFileByExtractingAllFiles(zipFileToExtract, password, outputFolder, expectedNumberOfEntries, true); } public static void verifyZipFileByExtractingAllFiles(File zipFileToExtract, char[] password, File outputFolder, int expectedNumberOfEntries, boolean verifyFileContents) throws IOException { verifyZipFileByExtractingAllFiles(zipFileToExtract, password, outputFolder, expectedNumberOfEntries, verifyFileContents, InternalZipConstants.CHARSET_UTF_8); } public static void verifyZipFileByExtractingAllFiles(File zipFileToExtract, char[] password, File outputFolder, int expectedNumberOfEntries, boolean verifyFileContents, Charset charset) throws IOException { assertThat(zipFileToExtract).isNotNull(); assertThat(zipFileToExtract).exists(); ZipFile zipFile = new ZipFile(zipFileToExtract, password); zipFile.setCharset(charset); zipFile.extractAll(outputFolder.getPath()); assertThat(zipFile.getFileHeaders().size()).as("Number of file headers").isEqualTo(expectedNumberOfEntries); List extractedFiles = FileUtils.getFilesInDirectoryRecursive(outputFolder, true, true); assertThat(extractedFiles).hasSize(expectedNumberOfEntries); if (verifyFileContents) { verifyFolderContentsSameAsSourceFiles(outputFolder); } } public static void verifyFileContent(File sourceFile, File extractedFile) throws IOException { assertThat(extractedFile.length()).isEqualTo(sourceFile.length()); verifyFileCrc(sourceFile, extractedFile); } public static void verifyFolderContentsSameAsSourceFiles(File outputFolder) throws IOException { File[] filesInOutputFolder = outputFolder.listFiles(); for (File file : filesInOutputFolder) { if (file.isDirectory()) { continue; } File sourceFile = TestUtils.getTestFileFromResources(file.getName()); verifyFileContent(sourceFile, file); } } private static void verifyFileCrc(File sourceFile, File extractedFile) throws IOException { ProgressMonitor progressMonitor = new ProgressMonitor(); long sourceFileCrc = CrcUtil.computeFileCrc(sourceFile, progressMonitor); long extractedFileCrc = CrcUtil.computeFileCrc(extractedFile, progressMonitor); assertThat(sourceFileCrc).isEqualTo(extractedFileCrc); } } zip4j-2.2.8/src/test/java/net/lingala/zip4j/util/000077500000000000000000000000001357650161600214765ustar00rootroot00000000000000zip4j-2.2.8/src/test/java/net/lingala/zip4j/util/BitUtilsTest.java000066400000000000000000000026631357650161600247470ustar00rootroot00000000000000package net.lingala.zip4j.util; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class BitUtilsTest { @Test public void testIsBitSet() { byte b = 0; b = (byte) (b | 1); b = (byte) (b | 1 << 3); b = (byte) (b | 1 << 7); assertThat(BitUtils.isBitSet(b, 0)).isTrue(); assertThat(BitUtils.isBitSet(b, 3)).isTrue(); assertThat(BitUtils.isBitSet(b, 7)).isTrue(); assertThat(BitUtils.isBitSet(b, 1)).isFalse(); assertThat(BitUtils.isBitSet(b, 2)).isFalse(); assertThat(BitUtils.isBitSet(b, 4)).isFalse(); assertThat(BitUtils.isBitSet(b, 5)).isFalse(); assertThat(BitUtils.isBitSet(b, 6)).isFalse(); } @Test public void testSetBit() { byte b = 0; b = BitUtils.setBit(b, 2); b = BitUtils.setBit(b, 5); b = BitUtils.setBit(b, 6); assertThat((b & (1L << 2))).isNotZero(); assertThat((b & (1L << 5))).isNotZero(); assertThat((b & (1L << 6))).isNotZero(); assertThat((b & (1L))).isEqualTo(0); assertThat((b & (1L << 1))).isEqualTo(0); assertThat((b & (1L << 3))).isEqualTo(0); assertThat((b & (1L << 4))).isEqualTo(0); assertThat((b & (1L << 7))).isEqualTo(0); } @Test public void unsetBit() { byte b = 0; b = (byte) (b | 1 << 3); b = (byte) (b | 1 << 7); b = BitUtils.unsetBit(b, 3); assertThat(BitUtils.isBitSet(b, 3)).isFalse(); assertThat(BitUtils.isBitSet(b, 7)).isTrue(); } }zip4j-2.2.8/src/test/java/net/lingala/zip4j/util/CrcUtilIT.java000066400000000000000000000056101357650161600241450ustar00rootroot00000000000000package net.lingala.zip4j.util; import net.lingala.zip4j.AbstractIT; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.progress.ProgressMonitor; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.zip.CRC32; import static net.lingala.zip4j.testutils.TestUtils.getTestFileFromResources; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class CrcUtilIT extends AbstractIT { @Rule public ExpectedException expectedException = ExpectedException.none(); private ProgressMonitor progressMonitor = new ProgressMonitor(); @Test public void testComputeFileCrcThrowsExceptionWhenFileIsNull() throws IOException { expectedException.expectMessage("input file is null or does not exist or cannot read. " + "Cannot calculate CRC for the file"); expectedException.expect(ZipException.class); CrcUtil.computeFileCrc(null, progressMonitor); } @Test public void testComputeFileCrcThrowsExceptionWhenCannotReadFile() throws IOException { expectedException.expectMessage("input file is null or does not exist or cannot read. " + "Cannot calculate CRC for the file"); expectedException.expect(ZipException.class); File unreadableFile = mock(File.class); when(unreadableFile.exists()).thenReturn(true); when(unreadableFile.canRead()).thenReturn(false); CrcUtil.computeFileCrc(unreadableFile, progressMonitor); } @Test public void testComputeFileCrcThrowsExceptionWhenFileDoesNotExist() throws IOException { expectedException.expectMessage("input file is null or does not exist or cannot read. " + "Cannot calculate CRC for the file"); expectedException.expect(ZipException.class); CrcUtil.computeFileCrc(new File("DoesNotExist"), progressMonitor); } @Test public void testComputeFileCrcGetsValueSuccessfully() throws IOException { testComputeFileCrcForFile(getTestFileFromResources("sample.pdf")); testComputeFileCrcForFile(getTestFileFromResources("sample_text1.txt")); testComputeFileCrcForFile(getTestFileFromResources("sample_text_large.txt")); } private void testComputeFileCrcForFile(File file) throws IOException { long actualFileCrc = calculateFileCrc(file); assertThat(CrcUtil.computeFileCrc(file, progressMonitor)).isEqualTo(actualFileCrc); } private long calculateFileCrc(File file) throws IOException { try(InputStream inputStream = new FileInputStream(file)) { byte[] buffer = new byte[InternalZipConstants.BUFF_SIZE]; int readLen = -1; CRC32 crc32 = new CRC32(); while((readLen = inputStream.read(buffer)) != -1) { crc32.update(buffer, 0, readLen); } return crc32.getValue(); } } }zip4j-2.2.8/src/test/java/net/lingala/zip4j/util/FileUtilsIT.java000066400000000000000000000067301357650161600245040ustar00rootroot00000000000000package net.lingala.zip4j.util; import net.lingala.zip4j.AbstractIT; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.enums.RandomAccessFileMode; import net.lingala.zip4j.progress.ProgressMonitor; import net.lingala.zip4j.testutils.TestUtils; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.RandomAccessFile; import static org.assertj.core.api.Assertions.assertThat; public class FileUtilsIT extends AbstractIT { @Rule public ExpectedException expectedException = ExpectedException.none(); private ProgressMonitor progressMonitor = new ProgressMonitor(); @Test public void testCopyFileThrowsExceptionWhenStartsIsLessThanZero() throws IOException, ZipException { testInvalidOffsetsScenario(-1, 100); } @Test public void testCopyFileThrowsExceptionWhenEndIsLessThanZero() throws IOException, ZipException { testInvalidOffsetsScenario(0, -1); } @Test public void testCopyFileThrowsExceptionWhenStartIsGreaterThanEnd() throws IOException, ZipException { testInvalidOffsetsScenario(300, 100); } @Test public void testCopyFilesWhenStartIsSameAsEndDoesNothing() throws IOException, ZipException { File sourceFile = TestUtils.getTestFileFromResources("sample.pdf"); File outputFile = temporaryFolder.newFile(); try(RandomAccessFile randomAccessFile = new RandomAccessFile(sourceFile, RandomAccessFileMode.READ.getValue()); OutputStream outputStream = new FileOutputStream(outputFile)) { FileUtils.copyFile(randomAccessFile, outputStream, 100, 100, progressMonitor); } assertThat(outputFile.exists()).isTrue(); assertThat(outputFile.length()).isZero(); } @Test public void testCopyFilesCopiesCompleteFile() throws IOException, ZipException { File sourceFile = TestUtils.getTestFileFromResources("sample.pdf"); File outputFile = temporaryFolder.newFile(); try(RandomAccessFile randomAccessFile = new RandomAccessFile(sourceFile, RandomAccessFileMode.READ.getValue()); OutputStream outputStream = new FileOutputStream(outputFile)) { FileUtils.copyFile(randomAccessFile, outputStream, 0, sourceFile.length(), progressMonitor); } assertThat(outputFile.length()).isEqualTo(sourceFile.length()); } @Test public void testCopyFilesCopiesPartOfFile() throws IOException, ZipException { File sourceFile = TestUtils.getTestFileFromResources("sample.pdf"); File outputFile = temporaryFolder.newFile(); try(RandomAccessFile randomAccessFile = new RandomAccessFile(sourceFile, RandomAccessFileMode.READ.getValue()); OutputStream outputStream = new FileOutputStream(outputFile)) { FileUtils.copyFile(randomAccessFile, outputStream, 500, 800, progressMonitor); } assertThat(outputFile.length()).isEqualTo(300); } private void testInvalidOffsetsScenario(int start, int offset) throws IOException, ZipException { expectedException.expectMessage("invalid offsets"); expectedException.expect(ZipException.class); File sourceFile = TestUtils.getTestFileFromResources("sample.pdf"); try(RandomAccessFile randomAccessFile = new RandomAccessFile(sourceFile, RandomAccessFileMode.READ.getValue()); OutputStream outputStream = new FileOutputStream(temporaryFolder.newFile())) { FileUtils.copyFile(randomAccessFile, outputStream, start, offset, progressMonitor); } } }zip4j-2.2.8/src/test/java/net/lingala/zip4j/util/FileUtilsTest.java000066400000000000000000000314331357650161600251050ustar00rootroot00000000000000package net.lingala.zip4j.util; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.ZipModel; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.io.File; import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributeView; import java.nio.file.attribute.FileTime; import java.nio.file.spi.FileSystemProvider; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class FileUtilsTest { @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void testLastModifiedFileTimeWhenTimeIsLessThanZeroDoesNothing() { FileUtils.setFileLastModifiedTime(mock(Path.class), -1); } @Test public void testLastModifiedFileTimeWhenTimeIEqualToZeroDoesNothing() { FileUtils.setFileLastModifiedTime(mock(Path.class), 0); } @Test public void testLastModifiedFileTimeWhenFileDoesNotExistDoesNothing() throws IOException { Path path = mock(Path.class); FileSystemProvider fileSystemProvider = mockPath(path); doThrow(new IOException("Some exception")).when(fileSystemProvider).checkAccess(path); FileUtils.setFileLastModifiedTime(path, 1); } @Test public void testLastModifiedFileTimeForValidTimeSetsTime() throws IOException { Path path = mock(Path.class); FileSystemProvider fileSystemProvider = mockPath(path); BasicFileAttributeView basicFileAttributeView = mock(BasicFileAttributeView.class); when(fileSystemProvider.getFileAttributeView(path, BasicFileAttributeView.class)) .thenReturn(basicFileAttributeView); long currentTime = System.currentTimeMillis(); FileUtils.setFileLastModifiedTime(path, currentTime); verify(basicFileAttributeView).setTimes(FileTime.fromMillis(Zip4jUtil.dosToJavaTme(currentTime)), null, null); } @Test public void testLastModifiedFileTimeWhenIOExceptionDoesNothing() { Path path = mock(Path.class); FileSystemProvider fileSystemProvider = mockPath(path); when(fileSystemProvider.getFileAttributeView(path, BasicFileAttributeView.class)) .thenThrow(new RuntimeException()); long currentTime = System.currentTimeMillis(); FileUtils.setFileLastModifiedTime(path, currentTime); } @Test public void testLastModifiedFileTimeWithoutNIOSetsSuccessfully() { File file = mock(File.class); long currentTime = System.currentTimeMillis(); FileUtils.setFileLastModifiedTimeWithoutNio(file, currentTime); verify(file).setLastModified(Zip4jUtil.dosToJavaTme(currentTime)); } @Test public void testGetFileAttributesReturnsEmptyBytesWhenNIONotSupported() { File file = mock(File.class); when(file.toPath()).thenThrow(new NoSuchMethodError("No method")); byte[] fileAttributes = FileUtils.getFileAttributes(file); assertThat(fileAttributes).hasSize(4); assertThat(fileAttributes).contains(0, 0, 0, 0); } @Test public void testGetFilesInDirectoryRecursiveThrowsExceptionWhenFileIsNull() throws ZipException { expectedException.expectMessage("input path is null, cannot read files in the directory"); expectedException.expect(ZipException.class); FileUtils.getFilesInDirectoryRecursive(null, true, true); } @Test public void testGetFilesInDirectoryRecursiveReturnsEmptyWhenInputFileIsNotDirectory() throws ZipException { File[] filesInDirectory = generateFilesForDirectory(); testGetFilesInDirectory(false, true, filesInDirectory, 0, true, true); } @Test public void testGetFilesInDirectoryRecursiveReturnsEmptyWhenCannotReadInputFile() throws ZipException { File[] filesInDirectory = generateFilesForDirectory(); testGetFilesInDirectory(true, false, filesInDirectory, 0, true, true); } @Test public void testGetFilesInDirectoryRecursiveReturnsEmptyWhenFilesInDirIsNull() throws ZipException { testGetFilesInDirectory(true, true, null, 0, true, true); } @Test public void testGetFilesInDirectoryRecursiveWithHiddenModeOnListsHiddenFiles() throws ZipException { File[] filesInDirectory = generateFilesForDirectory(); testGetFilesInDirectory(true, true, filesInDirectory, 6, true, true); } @Test public void testGetFilesInDirectoryRecursiveWithHiddenModeOffDoesNotListsHiddenFiles() throws ZipException { File[] filesInDirectory = generateFilesForDirectory(); testGetFilesInDirectory(true, true, filesInDirectory, 5, false, true); } @Test public void testGetFilesInDirectoryRecursiveWithHiddenModeOffDoesNotListsHiddenFolders() throws ZipException { File[] filesInDirectory = generateFilesForDirectory(); testGetFilesInDirectory(true, true, filesInDirectory, 4, true, false); } @Test public void testGetFilesInDirectoryRecursiveWithHiddenModeOffForFilesAndFolders() throws ZipException { File[] filesInDirectory = generateFilesForDirectory(); testGetFilesInDirectory(true, true, filesInDirectory, 3, false, false); } @Test public void testGetZipFileNameWithoutExtensionThrowsExceptionWhenNull() throws ZipException { expectedException.expectMessage("zip file name is empty or null, cannot determine zip file name"); expectedException.expect(ZipException.class); FileUtils.getZipFileNameWithoutExtension(null); } @Test public void testGetZipFileNameWithoutExtensionThrowsExceptionWhenEmpty() throws ZipException { expectedException.expectMessage("zip file name is empty or null, cannot determine zip file name"); expectedException.expect(ZipException.class); FileUtils.getZipFileNameWithoutExtension(""); } @Test public void testGetZipFileNameWithoutExtensionForWindowsFileSeparator() throws ZipException { final String ACTUAL_FILE_SEPARATOR = System.getProperty("file.separator"); System.setProperty("file.separator", "\\"); assertThat(FileUtils.getZipFileNameWithoutExtension("c:\\mydir\\somefile.zip")).isEqualTo("somefile"); System.setProperty("file.separator", ACTUAL_FILE_SEPARATOR); } @Test public void testGetZipFileNameWithoutExtensionForUnixFileSeparator() throws ZipException { final String ACTUAL_FILE_SEPARATOR = System.getProperty("file.separator"); System.setProperty("file.separator", "/"); assertThat(FileUtils.getZipFileNameWithoutExtension("/usr/srikanth/somezip.zip")).isEqualTo("somezip"); System.setProperty("file.separator", ACTUAL_FILE_SEPARATOR); } @Test public void testGetSplitZipFilesThrowsExceptionWhenZipModelIsNull() throws ZipException { expectedException.expect(ZipException.class); expectedException.expectMessage("cannot get split zip files: zipmodel is null"); FileUtils.getSplitZipFiles(null); } @Test public void testGetSplitZipFilesReturnsNullWhenEndOfCentralDirectoryIsNull() throws ZipException { ZipModel zipModel = new ZipModel(); zipModel.setEndOfCentralDirectoryRecord(null); assertThat(FileUtils.getSplitZipFiles(zipModel)).isNull(); } @Test public void testGetSplitZipFilesThrowsExceptionWhenZipFileDoesNotExits() throws ZipException { expectedException.expect(ZipException.class); expectedException.expectMessage("zip file does not exist"); ZipModel zipModel = new ZipModel(); zipModel.setZipFile(new File("Does not exist")); FileUtils.getSplitZipFiles(zipModel); } @Test public void testGetSplitZipFilesForNonSplitArchiveReturnsSameFile() throws ZipException { File zipFile = mockZipFileAsExists("somepath", "somefile"); ZipModel zipModel = new ZipModel(); zipModel.setSplitArchive(false); zipModel.setZipFile(zipFile); List splitFiles = FileUtils.getSplitZipFiles(zipModel); assertThat(splitFiles).hasSize(1); assertThat(splitFiles.get(0)).isSameAs(zipFile); } @Test public void testGetSplitZipFilesWhenNumberOfDiskIsZeroReturnsSameFile() throws ZipException { File zipFile = mockZipFileAsExists("somepath", "somefile"); ZipModel zipModel = new ZipModel(); zipModel.setSplitArchive(false); zipModel.setZipFile(zipFile); zipModel.getEndOfCentralDirectoryRecord().setNumberOfThisDisk(0); List splitFiles = FileUtils.getSplitZipFiles(zipModel); assertThat(splitFiles).hasSize(1); assertThat(splitFiles.get(0)).isSameAs(zipFile); } @Test public void testGetSplitZipFilesReturnsValidWhenSplitFile() throws ZipException { final String FILE_SEPARATOR = System.getProperty("file.separator"); String path = FILE_SEPARATOR + "usr" + FILE_SEPARATOR + "parentdir" + FILE_SEPARATOR; String zipFileName = "SomeName"; File zipFile = mockZipFileAsExists(path, zipFileName); ZipModel zipModel = new ZipModel(); zipModel.setSplitArchive(true); zipModel.setZipFile(zipFile); zipModel.getEndOfCentralDirectoryRecord().setNumberOfThisDisk(4); List splitFiles = FileUtils.getSplitZipFiles(zipModel); assertThat(splitFiles).hasSize(5); assertThat(splitFiles.get(0).getPath()).isEqualTo(path + zipFileName + ".z01"); assertThat(splitFiles.get(1).getPath()).isEqualTo(path + zipFileName + ".z02"); assertThat(splitFiles.get(2).getPath()).isEqualTo(path + zipFileName + ".z03"); assertThat(splitFiles.get(3).getPath()).isEqualTo(path + zipFileName + ".z04"); assertThat(splitFiles.get(4).getPath()).isEqualTo(path + zipFileName + ".zip"); } @Test public void testGetRelativeFileNameWhenRootFoldersAreNull() throws ZipException { assertThat(FileUtils.getRelativeFileName("somefile.txt", null, null)).isEqualTo("somefile.txt"); } @Test public void testGetRelativeFileWithRootFolderNameInZip() throws ZipException { String expectedRootFolder = "rootfolder" + InternalZipConstants.FILE_SEPARATOR + "somefile.txt"; assertThat(FileUtils.getRelativeFileName("somefile.txt", null, "rootfolder")).isEqualTo(expectedRootFolder); } @Test public void testGetRelativeFileWithRootFolderNameInZipWithFileSeparator() throws ZipException { String expectedRootFolder = "rootfolder" + InternalZipConstants.FILE_SEPARATOR + "somefile.txt"; assertThat(FileUtils.getRelativeFileName("somefile.txt", null, "rootfolder" + File.separator)) .isEqualTo(expectedRootFolder); } @Test public void testGetRelativeFileWithRootFolderNameInZipWithSeparatorsInName() throws ZipException { String expectedRootFolder = "rootfolder" + InternalZipConstants.FILE_SEPARATOR + "somefile.txt"; assertThat(FileUtils.getRelativeFileName("somefile.txt", null, "rootfolder\\")).isEqualTo(expectedRootFolder); } @Test public void testIsZipEntryDirectoryWithWindowsFileSeparatorReturnsTrue() { assertThat(FileUtils.isZipEntryDirectory("somename\\")).isTrue(); } @Test public void testIsZipEntryDirectoryWithUnixFileSeparatorReturnsTrue() { assertThat(FileUtils.isZipEntryDirectory("somename/")).isTrue(); } @Test public void testIsZipEntryDirectoryWhichIsNotDirectoryReturnsFalse() { assertThat(FileUtils.isZipEntryDirectory("somename")).isFalse(); } private File mockZipFileAsExists(String path, String zipFileNameWithoutExtension) { File file = mock(File.class); when(file.exists()).thenReturn(true); when(file.getName()).thenReturn(zipFileNameWithoutExtension + ".zip"); when(file.getPath()).thenReturn(path + zipFileNameWithoutExtension + ".zip"); return file; } private void testGetFilesInDirectory(boolean isDirectory, boolean canRead, File[] filesInDirectory, int expectedReturnSize, boolean shouldReadHiddenFiles, boolean shouldReadHiddenFolders) throws ZipException { File file = mock(File.class); when(file.isDirectory()).thenReturn(isDirectory); when(file.canRead()).thenReturn(canRead); when(file.listFiles()).thenReturn(filesInDirectory); List returnedFiles = FileUtils.getFilesInDirectoryRecursive(file, shouldReadHiddenFiles, shouldReadHiddenFolders); assertThat(returnedFiles).hasSize(expectedReturnSize); } private File[] generateFilesForDirectory() { return new File[]{ mockFile(false, false), mockFile(false, false), mockFile(true, false), mockFile(true, true), mockFile(false, true), mockFile(true, true), }; } private File mockFile(boolean isHidden, boolean isDirectory) { File file = mock(File.class); when(file.isHidden()).thenReturn(isHidden); when(file.isDirectory()).thenReturn(isDirectory); return file; } private FileSystemProvider mockPath(Path path) { FileSystemProvider fileSystemProvider = mock(FileSystemProvider.class); FileSystem fileSystem = mock(FileSystem.class); when(path.getFileSystem()).thenReturn(fileSystem); when(path.getFileSystem().provider()).thenReturn(fileSystemProvider); return fileSystemProvider; } }zip4j-2.2.8/src/test/java/net/lingala/zip4j/util/FileUtilsTestLinuxAndMac.java000066400000000000000000000137331357650161600271740ustar00rootroot00000000000000package net.lingala.zip4j.util; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import java.io.File; import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.PosixFileAttributeView; import java.nio.file.attribute.PosixFileAttributes; import java.nio.file.attribute.PosixFilePermission; import java.nio.file.spi.FileSystemProvider; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; public class FileUtilsTestLinuxAndMac { private static final String ACTUAL_OS = System.getProperty("os.name"); @Before public void setup() { System.setProperty("os.name", "linux"); } @After public void cleanup() { System.setProperty("os.name", ACTUAL_OS); } @Test public void testSetFileAttributesWhenAttributesIsNullDoesNothing() { FileUtils.setFileAttributes(mock(Path.class), null); } @Test public void testSetFileAttributesWhenAttributesIsEmptyDoesNothing() { FileUtils.setFileAttributes(mock(Path.class), new byte[0]); } @Test public void testSetFileAttributesWhenNoAttributesSetDoesNothing() throws IOException { Path path = mock(Path.class); PosixFileAttributeView posixFileAttributeView = mockPosixFileAttributeView(path, false); FileUtils.setFileAttributes(path, new byte[4]); verifyZeroInteractions(posixFileAttributeView); } @Test public void testSetFileAttributesWhenAttributesSet() throws IOException { Path path = mock(Path.class); PosixFileAttributeView posixFileAttributeView = mockPosixFileAttributeView(path, false); byte fourthByte = 1; // Only first bit is set byte thirdByte = -1; // All bits are set FileUtils.setFileAttributes(path, new byte[]{0, 0, thirdByte, fourthByte}); ArgumentCaptor permissionsCaptor = ArgumentCaptor.forClass(Set.class); verify(posixFileAttributeView).setPermissions(permissionsCaptor.capture()); verifyAllPermissionSet(permissionsCaptor.getValue()); } @Test public void testGetFileAttributesWhenFileIsNullReturnsEmptyBytes() { byte[] attributes = FileUtils.getFileAttributes(null); assertThat(attributes).contains(0, 0, 0, 0); } @Test public void testGetFileAttributesAsDefinedForRegularFile() throws IOException { testGetFileAttributesGetsAsDefined(false); } @Test public void testGetFileAttributesAsDefinedForDirectory() throws IOException { testGetFileAttributesGetsAsDefined(true); } private void testGetFileAttributesGetsAsDefined(boolean isDirectory) throws IOException { File file = mock(File.class); Path path = mock(Path.class); when(file.toPath()).thenReturn(path); when(file.exists()).thenReturn(true); PosixFileAttributeView posixFileAttributeView = mockPosixFileAttributeView(path, isDirectory); PosixFileAttributes posixFileAttributes = mock(PosixFileAttributes.class); Set posixFilePermissions = getAllPermissions(); when(posixFileAttributes.permissions()).thenReturn(posixFilePermissions); when(posixFileAttributeView.readAttributes()).thenReturn(posixFileAttributes); byte[] fileAttributes = FileUtils.getFileAttributes(file); assertThat(fileAttributes).hasSize(4); assertThat(fileAttributes[0]).isEqualTo((byte) 0); assertThat(fileAttributes[1]).isEqualTo((byte) 0); assertThat(fileAttributes[2]).isEqualTo((byte) -1); if (isDirectory) { assertThat(fileAttributes[3]).isEqualTo((byte) 65); } else { assertThat(fileAttributes[3]).isEqualTo((byte) -127); } } private Set getAllPermissions() { return Stream.of( PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE, PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_WRITE, PosixFilePermission.OTHERS_EXECUTE ).collect(Collectors.toSet()); } private void verifyAllPermissionSet(Set permissions) { assertThat(permissions).contains( PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE, PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_WRITE, PosixFilePermission.OTHERS_EXECUTE ); } private PosixFileAttributeView mockPosixFileAttributeView(Path path, boolean isDirectory) throws IOException { FileSystemProvider fileSystemProvider = mock(FileSystemProvider.class); FileSystem fileSystem = mock(FileSystem.class); PosixFileAttributeView posixFileAttributeView = mock(PosixFileAttributeView.class); when(path.getFileSystem()).thenReturn(fileSystem); when(fileSystemProvider.getFileAttributeView(path, PosixFileAttributeView.class)) .thenReturn(posixFileAttributeView); when(path.getFileSystem().provider()).thenReturn(fileSystemProvider); mockRegularFileOrDirectory(fileSystemProvider, path, isDirectory); return posixFileAttributeView; } private void mockRegularFileOrDirectory(FileSystemProvider fileSystemProvider, Path path, boolean isDirectory) throws IOException { BasicFileAttributes basicFileAttributes = mock(BasicFileAttributes.class); when(basicFileAttributes.isRegularFile()).thenReturn(!isDirectory); when(basicFileAttributes.isDirectory()).thenReturn(isDirectory); when(fileSystemProvider.readAttributes(path, BasicFileAttributes.class)).thenReturn(basicFileAttributes); } }zip4j-2.2.8/src/test/java/net/lingala/zip4j/util/FileUtilsTestWindows.java000066400000000000000000000107301357650161600264550ustar00rootroot00000000000000package net.lingala.zip4j.util; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.io.File; import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.Path; import java.nio.file.attribute.DosFileAttributeView; import java.nio.file.attribute.DosFileAttributes; import java.nio.file.spi.FileSystemProvider; import static net.lingala.zip4j.util.BitUtils.isBitSet; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; public class FileUtilsTestWindows { private static final String ACTUAL_OS = System.getProperty("os.name"); @Before public void beforeClass() { System.setProperty("os.name", "windows"); } @After public void cleanup() { System.setProperty("os.name", ACTUAL_OS); } @Test public void testSetFileAttributesWhenAttributesIsNullDoesNothing() { FileUtils.setFileAttributes(mock(Path.class), null); } @Test public void testSetFileAttributesWhenAttributesIsEmptyDoesNothing() { FileUtils.setFileAttributes(mock(Path.class), new byte[0]); } @Test public void testSetFileAttributesOnWindowsMachineWhenAttributesSet() throws IOException { Path mockedPath = mock(Path.class); DosFileAttributeView dosFileAttributeView = mockDosFileAttributeView(mockedPath, true); byte attribute = 0; attribute = BitUtils.setBit(attribute, 0); attribute = BitUtils.setBit(attribute, 1); attribute = BitUtils.setBit(attribute, 2); attribute = BitUtils.setBit(attribute, 5); FileUtils.setFileAttributes(mockedPath, new byte[]{attribute, 0, 0, 0}); verify(dosFileAttributeView).setReadOnly(true); verify(dosFileAttributeView).setHidden(true); verify(dosFileAttributeView).setSystem(true); verify(dosFileAttributeView).setArchive(true); } @Test public void testSetFileAttributesOnWindowsWhenNoAttributesSetDoesNothing() throws IOException { Path mockedPath = mock(Path.class); DosFileAttributeView dosFileAttributeView = mockDosFileAttributeView(mockedPath, true); FileUtils.setFileAttributes(mockedPath, new byte[]{0, 0, 0, 0}); verifyZeroInteractions(dosFileAttributeView); } @Test public void testGetFileAttributesWhenFileIsNullReturnsEmptyBytes() { byte[] attributes = FileUtils.getFileAttributes(null); assertThat(attributes).contains(0, 0, 0, 0); } @Test public void testGetFileAttributesWhenFileDoesNotExistReturnsEmptyBytes() throws IOException { File file = mock(File.class); when(file.exists()).thenReturn(false); byte[] attributes = FileUtils.getFileAttributes(file); assertThat(attributes).contains(0, 0, 0, 0); } @Test public void testGetFileAttributesReturnsAttributesAsDefined() throws IOException { File file = mock(File.class); Path path = mock(Path.class); when(file.toPath()).thenReturn(path); when(file.exists()).thenReturn(true); DosFileAttributeView dosFileAttributeView = mockDosFileAttributeView(path, true); DosFileAttributes dosFileAttributes = mock(DosFileAttributes.class); when(dosFileAttributeView.readAttributes()).thenReturn(dosFileAttributes); when(dosFileAttributes.isReadOnly()).thenReturn(true); when(dosFileAttributes.isHidden()).thenReturn(true); when(dosFileAttributes.isSystem()).thenReturn(true); when(dosFileAttributes.isArchive()).thenReturn(true); byte[] attributes = FileUtils.getFileAttributes(file); assertThat(isBitSet(attributes[0], 0)).isTrue(); assertThat(isBitSet(attributes[0], 1)).isTrue(); assertThat(isBitSet(attributes[0], 2)).isTrue(); assertThat(isBitSet(attributes[0], 5)).isTrue(); } private DosFileAttributeView mockDosFileAttributeView(Path path, boolean fileExists) throws IOException { FileSystemProvider fileSystemProvider = mock(FileSystemProvider.class); FileSystem fileSystem = mock(FileSystem.class); DosFileAttributeView dosFileAttributeView = mock(DosFileAttributeView.class); when(path.getFileSystem()).thenReturn(fileSystem); when(fileSystemProvider.getFileAttributeView(path, DosFileAttributeView.class)).thenReturn(dosFileAttributeView); when(path.getFileSystem().provider()).thenReturn(fileSystemProvider); if (!fileExists) { doThrow(new IOException()).when(fileSystemProvider).checkAccess(path); } return dosFileAttributeView; } }zip4j-2.2.8/src/test/java/net/lingala/zip4j/util/RawIOIT.java000066400000000000000000000170171357650161600235650ustar00rootroot00000000000000package net.lingala.zip4j.util; import net.lingala.zip4j.AbstractIT; import net.lingala.zip4j.model.enums.RandomAccessFileMode; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import static org.assertj.core.api.Assertions.assertThat; public class RawIOIT extends AbstractIT { private File fileToTest; private RawIO rawIO = new RawIO(); @Before public void before() throws IOException { fileToTest = temporaryFolder.newFile(); writeDummyData(fileToTest); } @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void testReadLongLittleEndianWithRandomAccessFile() throws IOException { try(RandomAccessFile randomAccessFile = new RandomAccessFile(fileToTest, RandomAccessFileMode.READ.getValue())) { assertThat(rawIO.readLongLittleEndian(randomAccessFile)).isEqualTo(InternalZipConstants.ZIP_64_SIZE_LIMIT + 1000); assertThat(rawIO.readLongLittleEndian(randomAccessFile)).isEqualTo(InternalZipConstants.ZIP_64_SIZE_LIMIT + 23423); assertThat(rawIO.readLongLittleEndian(randomAccessFile, 4)).isEqualTo(32332); } } @Test public void testReadLongLittleEndianWithInputStream() throws IOException { try(InputStream inputStream = new FileInputStream(fileToTest)) { assertThat(rawIO.readLongLittleEndian(inputStream)).isEqualTo(InternalZipConstants.ZIP_64_SIZE_LIMIT + 1000); assertThat(rawIO.readLongLittleEndian(inputStream)).isEqualTo(InternalZipConstants.ZIP_64_SIZE_LIMIT + 23423); assertThat(rawIO.readLongLittleEndian(inputStream, 4)).isEqualTo(32332); } } @Test public void testReadLongLittleEndianWithInputStreamNotEnoughDataThrowsException() throws IOException { expectedException.expect(IOException.class); expectedException.expectMessage("Could not fill buffer"); try(InputStream inputStream = new FileInputStream(fileToTest)) { inputStream.skip(26); rawIO.readLongLittleEndian(inputStream); } } @Test public void testReadLongLittleEndianWithByteArray() { byte[] b = new byte[4]; rawIO.writeIntLittleEndian(b, 0, 234233); assertThat(rawIO.readLongLittleEndian(b, 0)).isEqualTo(234233); } @Test public void testReadIntLittleEndianWithRandomAccessFile() throws IOException { try(RandomAccessFile randomAccessFile = new RandomAccessFile(fileToTest, RandomAccessFileMode.READ.getValue())) { randomAccessFile.seek(16); assertThat(rawIO.readIntLittleEndian(randomAccessFile)).isEqualTo(32332); assertThat(rawIO.readIntLittleEndian(randomAccessFile)).isEqualTo(231); } } @Test public void testReadIntLittleEndianWithInputStream() throws IOException { try(InputStream inputStream = new FileInputStream(fileToTest)) { inputStream.skip(16); assertThat(rawIO.readIntLittleEndian(inputStream)).isEqualTo(32332); assertThat(rawIO.readIntLittleEndian(inputStream)).isEqualTo(231); } } @Test public void testReadIntLittleEndianWithByteArray() { byte[] b = new byte[8]; rawIO.writeLongLittleEndian(b, 0, 23423L); assertThat(rawIO.readIntLittleEndian(b)).isEqualTo(23423); assertThat(rawIO.readIntLittleEndian(b, 1)).isEqualTo(91); } @Test public void testReadShortLittleEndianWithRandomAccessFile() throws IOException { try(RandomAccessFile randomAccessFile = new RandomAccessFile(fileToTest, RandomAccessFileMode.READ.getValue())) { randomAccessFile.seek(24); assertThat(rawIO.readShortLittleEndian(randomAccessFile)).isEqualTo(23); assertThat(rawIO.readShortLittleEndian(randomAccessFile)).isEqualTo(77); } } @Test public void testReadShortLittleEndianWithInputStream() throws IOException { try(InputStream inputStream = new FileInputStream(fileToTest)) { inputStream.skip(24); assertThat(rawIO.readShortLittleEndian(inputStream)).isEqualTo(23); assertThat(rawIO.readShortLittleEndian(inputStream)).isEqualTo(77); } } @Test public void testReadShortLittleEndianWithByteArray() { byte[] b = new byte[8]; rawIO.writeShortLittleEndian(b, 0, 88); rawIO.writeShortLittleEndian(b, 2, 67); assertThat(rawIO.readShortLittleEndian(b, 0)).isEqualTo(88); assertThat(rawIO.readShortLittleEndian(b, 2)).isEqualTo(67); } @Test public void testWriteShortLittleEndianWithOutputStream() throws IOException { File shortFile = temporaryFolder.newFile(); try(OutputStream outputStream = new FileOutputStream(shortFile)) { rawIO.writeShortLittleEndian(outputStream, 444); } try(InputStream inputStream = new FileInputStream(shortFile)) { assertThat(rawIO.readShortLittleEndian(inputStream)).isEqualTo(444); } } @Test public void testWriteShortLittleEndianWithByteBuffer() { byte[] b = new byte[10]; rawIO.writeShortLittleEndian(b, 0, 12); rawIO.writeShortLittleEndian(b, 6, 67); assertThat(rawIO.readShortLittleEndian(b, 0)).isEqualTo(12); assertThat(rawIO.readShortLittleEndian(b, 6)).isEqualTo(67); } @Test public void testWriteIntLittleEndianWithOutputStream() throws IOException { File shortFile = temporaryFolder.newFile(); try(OutputStream outputStream = new FileOutputStream(shortFile)) { rawIO.writeIntLittleEndian(outputStream, 4562); } try(InputStream inputStream = new FileInputStream(shortFile)) { assertThat(rawIO.readIntLittleEndian(inputStream)).isEqualTo(4562); } } @Test public void testWriteIntLittleEndianWithByteBuffer() { byte[] b = new byte[12]; rawIO.writeIntLittleEndian(b, 0, 23423); rawIO.writeIntLittleEndian(b, 7, 6765); assertThat(rawIO.readIntLittleEndian(b, 0)).isEqualTo(23423); assertThat(rawIO.readIntLittleEndian(b, 7)).isEqualTo(6765); } @Test public void testWriteLongLittleEndianWithOutputStream() throws IOException { File shortFile = temporaryFolder.newFile(); try(OutputStream outputStream = new FileOutputStream(shortFile)) { rawIO.writeLongLittleEndian(outputStream, 2342342L); } try(InputStream inputStream = new FileInputStream(shortFile)) { assertThat(rawIO.readLongLittleEndian(inputStream)).isEqualTo(2342342L); } } @Test public void testWriteLongLittleEndianWithByteBuffer() { byte[] b = new byte[50]; rawIO.writeLongLittleEndian(b, 0, 54545454233L); rawIO.writeLongLittleEndian(b, 18, 9988898778L); assertThat(rawIO.readLongLittleEndian(b, 0)).isEqualTo(54545454233L); assertThat(rawIO.readLongLittleEndian(b, 18)).isEqualTo(9988898778L); } private void writeDummyData(File outputFile) throws IOException { try (OutputStream outputStream = new FileOutputStream(outputFile)) { byte[] longByte = new byte[8]; byte[] intByte = new byte[4]; byte[] shortByte = new byte[2]; rawIO.writeLongLittleEndian(longByte, 0, InternalZipConstants.ZIP_64_SIZE_LIMIT + 1000); outputStream.write(longByte); rawIO.writeLongLittleEndian(longByte, 0, InternalZipConstants.ZIP_64_SIZE_LIMIT + 23423); outputStream.write(longByte); rawIO.writeIntLittleEndian(intByte, 0, 32332); outputStream.write(intByte); rawIO.writeIntLittleEndian(intByte, 0, 231); outputStream.write(intByte); rawIO.writeShortLittleEndian(shortByte, 0, 23); outputStream.write(shortByte); rawIO.writeShortLittleEndian(shortByte, 0, 77); outputStream.write(shortByte); } } }zip4j-2.2.8/src/test/java/net/lingala/zip4j/util/UnzipUtilIT.java000066400000000000000000000077751357650161600245610ustar00rootroot00000000000000package net.lingala.zip4j.util; import net.lingala.zip4j.AbstractIT; import net.lingala.zip4j.ZipFile; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.model.ZipModel; import net.lingala.zip4j.testutils.TestUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Path; import java.util.Arrays; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.verifyStatic; @RunWith(PowerMockRunner.class) @PrepareForTest(FileUtils.class) public class UnzipUtilIT extends AbstractIT { @Test public void testCreateZipInputStream() throws ZipException, IOException { ZipFile zipFile = createZipFile(); ZipModel zipModel = createZipModel(); FileHeader fileHeader = zipFile.getFileHeaders().get(1); File extractedFile = temporaryFolder.newFile(); try (InputStream inputStream = UnzipUtil.createZipInputStream(zipModel, fileHeader, "password".toCharArray()); OutputStream outputStream = new FileOutputStream(extractedFile)) { byte[] b = new byte[InternalZipConstants.BUFF_SIZE]; int readLen = 0; while ((readLen = inputStream.read(b)) != -1) { outputStream.write(b, 0, readLen); } } assertThat(extractedFile.length()).isEqualTo(TestUtils.getTestFileFromResources("sample_text_large.txt").length()); } @Test public void testApplyFileAttributes() { byte[] externalFileAttributes = new byte[] {12, 34, 0, 0}; long currentTime = System.currentTimeMillis(); FileHeader fileHeader = new FileHeader(); fileHeader.setExternalFileAttributes(externalFileAttributes); fileHeader.setLastModifiedTime(currentTime); File file = mock(File.class); Path path = mock(Path.class); when(file.toPath()).thenReturn(path); PowerMockito.mockStatic(FileUtils.class); UnzipUtil.applyFileAttributes(fileHeader, file); verifyStatic(FileUtils.class); FileUtils.setFileLastModifiedTime(path, currentTime); verifyStatic(FileUtils.class); FileUtils.setFileAttributes(path, externalFileAttributes); } @Test public void testApplyFileFileAttributesSetsLastModifiedTimeWithoutNio() { byte[] externalFileAttributes = new byte[] {12, 34, 0, 0}; long currentTime = System.currentTimeMillis(); FileHeader fileHeader = new FileHeader(); fileHeader.setExternalFileAttributes(externalFileAttributes); fileHeader.setLastModifiedTime(currentTime); File file = mock(File.class); Path path = mock(Path.class); when(file.toPath()).thenThrow(new NoSuchMethodError("No method")); PowerMockito.mockStatic(FileUtils.class); UnzipUtil.applyFileAttributes(fileHeader, file); verifyStatic(FileUtils.class, never()); FileUtils.setFileLastModifiedTime(path, currentTime); verifyStatic(FileUtils.class, never()); FileUtils.setFileAttributes(path, externalFileAttributes); verifyStatic(FileUtils.class); FileUtils.setFileLastModifiedTimeWithoutNio(file, currentTime); } private ZipFile createZipFile() throws ZipException { ZipFile zipFile = new ZipFile(generatedZipFile, "password".toCharArray()); zipFile.addFiles(Arrays.asList( TestUtils.getTestFileFromResources("sample_text1.txt"), TestUtils.getTestFileFromResources("sample_text_large.txt") )); return zipFile; } private ZipModel createZipModel() { ZipModel zipModel = new ZipModel(); zipModel.setZipFile(generatedZipFile); zipModel.getEndOfCentralDirectoryRecord().setNumberOfThisDisk(0); zipModel.setSplitArchive(false); return zipModel; } }zip4j-2.2.8/src/test/java/net/lingala/zip4j/util/Zip4jUtilTest.java000066400000000000000000000252351357650161600250460ustar00rootroot00000000000000package net.lingala.zip4j.util; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.AESExtraDataRecord; import net.lingala.zip4j.model.LocalFileHeader; import net.lingala.zip4j.model.enums.CompressionMethod; import net.lingala.zip4j.testutils.ControlledReadInputStream; import net.lingala.zip4j.testutils.RandomInputStream; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.io.File; import java.io.IOException; import java.util.TimeZone; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class Zip4jUtilTest { @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void testIsStringNotNullAndNotEmptyReturnsFalseWhenNull() { assertThat(Zip4jUtil.isStringNotNullAndNotEmpty(null)).isFalse(); } @Test public void testIsStringNotNullAndNotEmptyReturnsFalseWhenEmpty() { assertThat(Zip4jUtil.isStringNotNullAndNotEmpty("")).isFalse(); } @Test public void testIsStringNotNullAndNotEmptyReturnsFalseWithWhitespaces() { assertThat(Zip4jUtil.isStringNotNullAndNotEmpty(" ")).isFalse(); } @Test public void testIsStringNotNullAndNotEmptyReturnsTrueForValidString() { assertThat(Zip4jUtil.isStringNotNullAndNotEmpty(" Some string ")).isTrue(); } @Test public void testCreateDirectoryIfNotExistsThrowsExceptionWhenPathIsNull() throws ZipException { expectedException.expectMessage("output path is null"); expectedException.expect(ZipException.class); Zip4jUtil.createDirectoryIfNotExists(null); } @Test public void testCreateDirectoryIfNotExistsThrowsExceptionWhenFileExistsButNotDirectory() throws ZipException { File file = mock(File.class); when(file.exists()).thenReturn(true); when(file.isDirectory()).thenReturn(false); expectedException.expect(ZipException.class); expectedException.expectMessage("output directory is not valid"); Zip4jUtil.createDirectoryIfNotExists(file); } @Test public void testCreateDirectoryIfNotExistsReturnsTrueWhenFileExistsAndIsDirectory() throws ZipException { File file = mock(File.class); when(file.exists()).thenReturn(true); when(file.isDirectory()).thenReturn(true); assertThat(Zip4jUtil.createDirectoryIfNotExists(file)).isTrue(); } @Test public void testCreateDirectoryIfNotExistsThrowsExceptionWhenFileDoesNotExistAndCannotCreate() throws ZipException { File file = mock(File.class); when(file.exists()).thenReturn(false); when(file.mkdirs()).thenReturn(false); expectedException.expect(ZipException.class); expectedException.expectMessage("Cannot create output directories"); Zip4jUtil.createDirectoryIfNotExists(file); } @Test public void testCreateDirectoryIfNotExistsReturnsTrueWhenFileDoesNotExistAndCreated() throws ZipException { File file = mock(File.class); when(file.exists()).thenReturn(false); when(file.mkdirs()).thenReturn(true); assertThat(Zip4jUtil.createDirectoryIfNotExists(file)).isTrue(); } @Test public void testJavaToDosTime() { TimeZone defaultTimeZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin")); assertThat(Zip4jUtil.javaToDosTime(1560526564503L)).isEqualTo(1322159234); TimeZone.setDefault(defaultTimeZone); } @Test public void testDosToJavaTime() { TimeZone defaultTimeZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin")); assertThat(Zip4jUtil.dosToJavaTme(1322159234)).isEqualTo((1560526564503L / 1000) * 1000); TimeZone.setDefault(defaultTimeZone); } @Test public void testConvertCharArrayToByteArray() { char[] charArray = "CharArray".toCharArray(); byte[] byteArray = Zip4jUtil.convertCharArrayToByteArray(charArray); assertThat(byteArray.length).isEqualTo(charArray.length); assertThat(byteArray[0]).isEqualTo((byte)'C'); assertThat(byteArray[1]).isEqualTo((byte)'h'); assertThat(byteArray[2]).isEqualTo((byte)'a'); assertThat(byteArray[3]).isEqualTo((byte)'r'); assertThat(byteArray[4]).isEqualTo((byte)'A'); assertThat(byteArray[5]).isEqualTo((byte)'r'); assertThat(byteArray[6]).isEqualTo((byte)'r'); assertThat(byteArray[7]).isEqualTo((byte)'a'); assertThat(byteArray[8]).isEqualTo((byte)'y'); } @Test public void testGetCompressionMethodForNonAesReturnsAsIs() { LocalFileHeader localFileHeader = new LocalFileHeader(); localFileHeader.setCompressionMethod(CompressionMethod.DEFLATE); assertThat(Zip4jUtil.getCompressionMethod(localFileHeader)).isEqualTo(CompressionMethod.DEFLATE); } @Test public void testGetCompressionMethodForAesWhenAesExtraDataMissingThrowsException() { expectedException.expectMessage("AesExtraDataRecord not present in local header for aes encrypted data"); expectedException.expect(RuntimeException.class); LocalFileHeader localFileHeader = new LocalFileHeader(); localFileHeader.setCompressionMethod(CompressionMethod.AES_INTERNAL_ONLY); Zip4jUtil.getCompressionMethod(localFileHeader); } @Test public void testGetCompressionMethidForAesReturnsFromAesExtraDataRecord() { AESExtraDataRecord aesExtraDataRecord = new AESExtraDataRecord(); aesExtraDataRecord.setCompressionMethod(CompressionMethod.STORE); LocalFileHeader localFileHeader = new LocalFileHeader(); localFileHeader.setCompressionMethod(CompressionMethod.AES_INTERNAL_ONLY); localFileHeader.setAesExtraDataRecord(aesExtraDataRecord); assertThat(Zip4jUtil.getCompressionMethod(localFileHeader)).isEqualTo(CompressionMethod.STORE); } @Test public void testReadFullyReadsCompleteBuffer() throws IOException { byte[] b = new byte[3423]; ControlledReadInputStream controlledReadInputStream = initialiseControlledInputStream(1000); assertThat(Zip4jUtil.readFully(controlledReadInputStream, b)).isEqualTo(3423); } @Test public void testReadFullyReadsCompleteBufferInOneShot() throws IOException { byte[] b = new byte[4096]; ControlledReadInputStream controlledReadInputStream = initialiseControlledInputStream(4097); assertThat(Zip4jUtil.readFully(controlledReadInputStream, b)).isEqualTo(4096); } @Test public void testReadFullyThrowsExceptionWhenCannotFillBuffer() throws IOException { byte[] b = new byte[4097]; ControlledReadInputStream controlledReadInputStream = initialiseControlledInputStream(500); expectedException.expect(IOException.class); expectedException.expectMessage("Cannot read fully into byte buffer"); Zip4jUtil.readFully(controlledReadInputStream, b); } @Test public void testReadFullyOnEmptyStreamThrowsException() throws IOException { byte[] b = new byte[4096]; RandomInputStream randomInputStream = new RandomInputStream(0); ControlledReadInputStream controlledReadInputStream = new ControlledReadInputStream(randomInputStream, 100); expectedException.expect(IOException.class); expectedException.expectMessage("Cannot read fully into byte buffer"); assertThat(Zip4jUtil.readFully(controlledReadInputStream, b)).isEqualTo(-1); } @Test public void testReadFullyThrowsExceptionWhenRetryLimitExceeds() throws IOException { byte[] b = new byte[151]; ControlledReadInputStream controlledReadInputStream = initialiseControlledInputStream(10); expectedException.expect(IOException.class); expectedException.expectMessage("Cannot read fully into byte buffer"); Zip4jUtil.readFully(controlledReadInputStream, b); } @Test public void testReadFullyWithLengthReadsCompleteLength() throws IOException { byte[] b = new byte[1000]; ControlledReadInputStream controlledReadInputStream = initialiseControlledInputStream(100); assertThat(Zip4jUtil.readFully(controlledReadInputStream, b, 0, 900)).isEqualTo(900); } @Test public void testReadFullyWithLengthReadsMaximumAvailable() throws IOException { byte[] b = new byte[1000]; RandomInputStream randomInputStream = new RandomInputStream(150); ControlledReadInputStream controlledReadInputStream = new ControlledReadInputStream(randomInputStream, 700); assertThat(Zip4jUtil.readFully(controlledReadInputStream, b, 0, 900)).isEqualTo(150); } @Test public void testReadFullyWithLengthReadsCompletelyIntoBuffer() throws IOException { byte[] b = new byte[1000]; ControlledReadInputStream controlledReadInputStream = initialiseControlledInputStream(10); assertThat(Zip4jUtil.readFully(controlledReadInputStream, b, 0, 1000)).isEqualTo(1000); } @Test public void testReadFullyWithNegativeLengthThrowsException() throws IOException { byte[] b = new byte[1000]; ControlledReadInputStream controlledReadInputStream = initialiseControlledInputStream(10); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Negative length"); Zip4jUtil.readFully(controlledReadInputStream, b, 0, -5); } @Test public void testReadFullyWithNegativeOffsetThrowsException() throws IOException { byte[] b = new byte[10]; ControlledReadInputStream controlledReadInputStream = initialiseControlledInputStream(10); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Negative offset"); Zip4jUtil.readFully(controlledReadInputStream, b, -4, 10); } @Test public void testReadFullyWithLengthZeroReturnsZero() throws IOException { byte[] b = new byte[1000]; ControlledReadInputStream controlledReadInputStream = initialiseControlledInputStream(100); assertThat(Zip4jUtil.readFully(controlledReadInputStream, b, 0, 0)).isZero(); } @Test public void testReadFullyThrowsExceptionWhenOffsetPlusLengthGreaterThanBufferSize() throws IOException { byte[] b = new byte[10]; ControlledReadInputStream controlledReadInputStream = initialiseControlledInputStream(10); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Length greater than buffer size"); Zip4jUtil.readFully(controlledReadInputStream, b, 5, 10); } @Test public void testReadFullyWithLengthOnAnEmptyStreamReturnsEOF() throws IOException { byte[] b = new byte[1000]; RandomInputStream randomInputStream = new RandomInputStream(-1); ControlledReadInputStream controlledReadInputStream = new ControlledReadInputStream(randomInputStream, 100); assertThat(Zip4jUtil.readFully(controlledReadInputStream, b, 0, 100)).isEqualTo(-1); } private ControlledReadInputStream initialiseControlledInputStream(int maxLengthToReadAtOnce) { RandomInputStream randomInputStream = new RandomInputStream(4096); return new ControlledReadInputStream(randomInputStream, maxLengthToReadAtOnce); } }zip4j-2.2.8/src/test/resources/000077500000000000000000000000001357650161600163555ustar00rootroot00000000000000zip4j-2.2.8/src/test/resources/test-archives/000077500000000000000000000000001357650161600211365ustar00rootroot00000000000000zip4j-2.2.8/src/test/resources/test-archives/corrupt_extra_data_record_length.zip000066400000000000000000005371511357650161600304670ustar00rootroot00000000000000PK7,]8AndroidManifest.xmlVKO[G>`^e  .jIQ HK\ JBUAeU]tU]tџUUWYtU67}|yfΜ;g)FYzm'S9'7 y'" e;& xܩ!x EJ+ %X΀  _  #*qDC%:}:-?Ke14܇e ^D *óD{|jQ;sH_[/ʰiQ(lV;YC<{}r9=v Ctn*jI@w4[GwV)|,&ei뽅hWPfb_d]s=z%5>8VXcJ[Lw\=\P#r˜e>?g g/b)krU+̿Mp]"V'G+q3yO4^iήXOp?y}6vl6"\;6'_HunfJ[Q9Ʒjexp< F,w$߲.2;3uWXm_ԩuS ;ggx u~Z986D4]+ϋ*+U U~fbKnZ@m{hϰCUKx"j`ZUuK08:˯$0Y)J ף}Y {5~(T3ͮ~6<hRs-~jSM(.f7[Kl-(;įCt):B#!fSKg9,mVں5>8/b#|=_$/j 5^6/f'|}_,΀_5z|7 R—ѳU91|"}ҤmqmQJ'hI _kQI1\EG9_vCbk6KluT/vOǨp$4w`Ltik5k \{/˱;f0? W7 re isubp2mJb6 iִ샤ߌh|^~ _Iŗ\{_@r?`YVASWW}5[kݛ}-dPKrbPK7,]8 classes.dexy}{1cMd,eX%"c)*dd T$KH1He+d-K,Q)6[z|yxyss\k+ehTRTRm#:2f҅kDhz&|*M)5SX%KK7#I+&Π+)UݧTFJ}TjT[#;ajJV`ͰIB3*@=7L0rX_`/#\KPqVAxfǰȄQ .e B$(efHjP n' T J[a7 _$\ԇQ` K`6ix C'?P@/ .7Ѫ1 ^0FtYl8sp"?j|{@*k`;iuc-10>p_7hš4x PBCZ@+h !xzCa$o$?a `sP|a+gnZ %_DI;2^U [*TT-XgZ5:,oo(>i0(T[@2/4N0FtX[ %?ת*4N0ކ7-Ax(QSKJAlUZU"i (]yC` ЪLҗS:۰[>]Oa| G Zaeb02,mS0A>Cg L1 |9X'ZV`$|;< ?C`b[73< AmZu3סL5pS02oi'c 5];0ރmp*|KPv?P;Pp7tga&lP{C;ǰ9“"d2NA䏴Ԅ{ ^ 6O;\bٴtt`5lp JD s3 7>p?</G x^y} `PԇvB!8 2AsNjAc`A;t0>]p Jb샖+a6,uÜBS@ 6y(tJB/]6p.AQn;x2]~8E1@C'`<a Lu:CQ(eTju! -50@_H!<0 >Oa- `/oZFp'4Vp?t.0'!ExkvE;k6pn1 A0p `Lw`&̃e!OJA% w@)0x "XY=p Ŝ ! n<a[0 >al sn 0x ކ9!WIj@s 9x &4 N@)7a!cp .C4 4:£Ra S`,/`l8 ąUY C7Oh{8\́pT$h#t^ %Ȁ9l3rAk t `-l#yqAch=7 g`LY06N3CE)d s`%3p \^P@kxzPcא pE_U4#`a|+`/n9.P@s@{) 0,++{+u C h ]'< `8+ +j!XRX _ ;wCX JG8G/8נUy(Nha f'6  JB9H :BW0&.̆O`9lQN%O%ASzB o,k;8W JT 5  02`.oϧJAUhwCxk0f§v!>U@h AO/dX_ GO |,Ԅz@=X">UB5haHq0>Ր | g8AGTIH;OHJP0q0Vz?/']BT:  H*&~?,\(B]h+ %EX{ DET-x '!   3a,dN)|& t0f2 !XاA !C< #a2| ' PjzA |a#|$cPn.:CoH0އvpop΁.S!P*@-hmt)a,LȆpt,Aq( &4~ O*̀ T1K(ա)<0§O 8= z G OpCO"ԃy3`\`Ix ^)06|3X`+ " R JB%-<)`,dlX ?A8#2ԃt0l.B* 9t0&< V:ȄoP024 0ޅN8 T##Ux§@63(CPA#h]1x WurX 8 W!,eAe G #``| a3삽p yC{H0 &4 SX`|_pT9/f nO@ x>Ͱ_p"9'4B/Ha|ZqºBUC Q0z ?4 2TZpt'x&;0Vp.CDO p46z@Hg`0VO D5C$A]hwCH(Xk8 =C"$=p?0^y0 WpU 4K _}@;ݡ/ *L=#p5ԂdBx^ *;a7d^8 @ : }a,̄/`?TJ]96; [v {`/_W p18  8 4,p.?p .5]D@$((0( P C ( 4qp#px("T U*TPn$ 6ԁ۠.ԃnp'4 C Z p=< :B' ]AxB7xG;xTi` a0 < 0x^0^ڽg1 ^08x^oo$x ކ02`*L̀wa&̂}>0ѻ'0BXS RXrވ{VjX_z_K`l- Y5lmv7v'^A8p Nk^py[ \5:,!AKuYqU\*.3K:%K'2AV,Ս`rbyX&(zŴӕbQ }[u+$AMuԇp;4F&Ash 6Ȅ,6; &U:|򞒙b,Iح&f؇>.{{i~bGSͺWߓF/J4O{?Sc};2ٖKg>DFMZx)%2O eO0ckRF4O[|Ͼ~FF͸.y )7\%-]MVxd' vJ,q!w'K =q}S-pdߍ=i縹 ':g%ުuMmuFH}nl&)ߴ+u3C2ܜbǷiJR9v)|*7QDO9ȱ'H6{'M'bOnd[^A^'RMZ 7{K5ٗ/Ζ{lئ/m>Ic.:vԜrN35.II{&&9حZ=W<9v:ėؕ=S(mq?=fx={c(\s?ב1ɫ~x[ qK=ћ 9%cra'9痹Ƽ>VxX>zW'ެv'|/I=iݜmuؽ׊'~(b^'izc=ku Ϻn~c?籇y"D7E'%d)D#:C]љD}:~)><ϮSNp? ' ~"}:wz)tpgW[5]jP5U v4;BN>aԵki4Wt:3z92:@\xՀk52A}5 Ay3 {ZKF]x0<msIqT/hGrA4Yh'΢]D}Hh7tziOD/#tZUh]zD;v}P!ѮCDz=*]h󭍀*wlQS8?D3D)jO%%ћEo, (ZEhu[EDk$Y胢v(hFiE-.:NWENj.щ臨yVh.g}N~y<s.=E^NZ$<\¯.U6늹2pJeAz>:O|;/,j>y 翦it yh'ESEyy ܅^$EE?;o/y{ۋd^`{ygn]"o|D[KvY&Zn\EaA+VMn\,'mUjN\ϭЄ>c5 rauuދ/YMζkڠ6_紧h=h֝l|kFGf Ӥ7jmMY=doa?L-IgthՀrrMVVM:˔%gIYR~%gIYR~J'2lU[VG[ q^6o6ov.]%vE}L.ֶ...jk]m=wom;|Sm9߱YrA]57[-f]dѽO>97et/&~muJ?"1uqsM\qcMe/u 9~'ύr, =/zA3.H{^T [%0T-N Ak oZoeU\qUU{s|IkOIkNiWã>펏O&-[kޢZ=0RMzT%ox]|wj; "BCn5Ago]WIм߹A }Xw<7ߥQ=Onw_Ww<ڐy߫Zfs6>[2ngr%VKжr޶Uԧ:.}\ ݪDwH{%s?8EC%SiV7VPs.0\)=;tTOqj#_F2`k{Ѣ+E!`FjvSr{D~[V=5z6コzd0Q3dwcFS"̻.l}2dqMU3Dߵ_}m5M2V~UQO~hS:d^cujc>h]} KuJRd^{J-K̻l9<>.j򧪧֠Q7vJrӤ4)7Mm㏳v(o)7IRq7h5o+T?P?P?P?P;5h=3،VWsujl㇨΢/}/f~6~YݠXhu(ׁ2/g(|֦,3r>úW?A =jˀ8sT*2:B[I%ZY}V2!B:A"G G)wa.2-2/ "!AΕ8KUOGG5S/rl_RH d||?\FUsf˒eҽ7:cr^>+]*MoâOYZJ S[>:V-OZOs{NU7,j[i,zjoЕ{5{M!_/~ 9!o9DpAHvu9oƪ^6~,חQ!EE 'lq_Q+ W.sEh%k_q~COp2Ϸ\DxiiD˅/M)o'I- %%'KxHx3$ѧ湔kߩěs*3A4zL7{/ +oy8UuW[ZI ߜOS9^~~;h^AOh*Vfn^3Ử븩>*oS9.M.уGD9-l>{ܧr?`*r?`>\7ZY)'‘#\}ZY08;M4zvQw};MkqХ3ƸbAӥ?N8]tH3D):K=E?Pts|Hj> H}k>k5StV4Ygy^e~;-w.&Z\hIRV7.gøu<)ɺjn1z'./:i>#E?NZ*Ut]-uY-TglgggNI~vH9X]m{#Xn+vpWzg]LL%^ˆ>ߙdKUTƥe^&U v_(3Qk.leaχsXHg/':@Z}~T :WEZ-]k2.Wy]~^N~wfFy*u7:M0Qm=bFU.ҙvjZue;k8QYɀѢjUw>Qd>Qs>}!K ./e;T3tY=k%Z5T4޶ZɷVk瓬Β9duj)+e׫]όLsW%lzX}t~.E[ m %u2\==m|l_m}~4\>No moE3]~phu=j%gOoM*Be9߬ s‰ϊYtw7<DEq>LMX]Y]'A=-j.Gg5^ҽfub5[fa*iV#t7KyOPYVYGmmeJ9YſCX[rJ{璛ʐ u!Y4S4K߄ NXq; !WsޘzkAYj gIȒ~)Y/]&-zŪOsxyW.dIȒ%#KG,Y?dIȒ%#KG,Y?dz%'( ^R ~c뭺%'KO,?YdXSΪOYҟeI?ʒ~%(KQ,GYҏzKJ$/V]ʒ҇$ olʒ~5_mUVOϔ>>w뜭lw{ht%D:hA] lpQmmR6YGm6Y?mv/>S<_u+7OP7~T)ϱwy1y_զ%]-R뺚j<qޠs jh9?Pl9?H9?mG gKdd(y_PLJ|~/eȦ9FԟK6cHxa{%Oh$eÿHWI~~C3%I?V={/|A VmaypX.s\={O")QtϢD{^Ev嗲8iojor^ߥ?^?dT{cKOHOHO\-LpNJ;uIYIY7N)̛*VO)9^d=hi5n紌gg36m9ַoٿUEy$^;/lâ+'zyE{*mge9's2y$I_G q-%L^~ZcP}y6|I},bKVom5^,_Ekb~]V"Rjy_S*28ukww?ct8AޏbkiWvpcn+]m|G>ݼ_" Jl+/ZOhW湱T -]xO| mvc1RR~QI+ڭb~ڭb[h Qڭv_WW^qR o2N%$~O?aaQR⍚CI%fQ~(qH2Գ{MSu蟺jՓE OV7?9gx3%6\^ʯ YQT}^jU8 5+zOZ3_Hb0ʵ0O&Qw^pU{U;Gu_D}TG3 [l}lXݤX,A]f(_aμϡV]=~Րt5j#6} }Tq߮XuՐn?XV_滜nXS[S'! jyODNC-)^aFto׈FMzE|_KU߼!:kW]b]5zvc :Qw=f%<Ԫ3Gg]tYuc tkOLYR{R?Ϫjf_%Z;Uw}e=ohw}et]o7VNꮧL۾9f:v}F ݠ oNjSM jF:эcS}N Gv=\|/ǖF/7sWK9w8Zҷ7MxՕjhU2hn1yFk׉JnP潾$|?垷R ͺVKڳ=llޫ_^+}yvyּǣCzd犚@F]=:utUWd^GWGIQw%}gI庺wrLA?+~hbV VV%ץ֥7X=o DW{=xdHq#Eƍ7RdH~N)2~"G)2~"G)2~"G)2~"G)2~hw?'EƑGRdIq$E9)Iqń[uK/)2"K/)2hw?ǔU7ޤx"M7)2ޤx"M7)2ޤhw'EƝwRdIq'EƝ":EƟ>V{ڼG}>ItbKg{+{?{N[ͷ:9UoQ0T]ھ*/&dKӅMc[-jI7u{V/mMrs[jk7}ᶢ|[׉|SW2}ik%:hS~[?!ʞ}>WUlN]DO;?UW _#Ws~.y6>)??_K+^Ca['7-Aķ=-\;|˟,fK.ۙz@?T_4rg|h]İ;_?$W(tgOCnǗ.|tKJLyO017I AuE?h% y:o>X/?2<~IzGyMOyGj_jytso~l ˵ƿ1:|7|_#~z>GF?Hs<2~LO|]D񝜓̣~1ψRx8[y$7_GZSwF޿*rߗ3Nj Iv~u;}p"z(OF/n Qag់Fc}1+Ɵg۟?6?A_<WSo0SK6ጹyo?P<&_ /D/_c7< ſ i.7ſ(:ſ8W8sO.Og_Ŀ$=>/k@ſ,:FC3Ŀ<_z|׿9i*7 Ε|rk|_Jtw5_)㟀窟?IrˤQ;\?.o#77P[ 3s?gI\&aVClſM%s?=39!{ab(Vl?SnNS~]>w>s|$v>k5O9 =|ߗI@>/mEG~#ͧ?O?+)?S~T=ȧ~mg>'_o|\S?GAL.Yg;kg<7R<> !o|_4G[_M)/Ӿ俜k+EWs|-UL:o kߧz~'Q;Љ0S̓4K/sҕ;fq{{7CN}O=冮: Gn;1A;?D "<-n*ΗOFūQkA-> y**ZN8UHM=2vfN[e)ԋTc`5_l 񛰣U}U2I8]AUTi񏰍?ݚ]uiA\jA|Gf_wz*ᒳT]vFQ˧&N8ޚ2G T׏C[{"=ahBs8yo,ָzOO o{jygU/=mwԤ4oc.iۻh*yw})W{Sr\rw\]UڻmD{@.?ۻLR]Umg4]ޭ]ތIW36ORC7b͓T, Gx})w99|*C}*K{k0|lLﱡI#sql:65԰ǦfXc3&c3Frlj:6U=ǦcJ*j@.u(j{|9j{cUYƧnMc+,mz#%ާJ6|(=nق?㕎"ovg؟u/q7Z9<{>E1?6i!ی%|S'җ}mE//|T^9yo>GƼ92y1(g7JQ؞o_sP/@ئ_U])8G)wlST8jsۃeT1_/uͥګQ t1ܬ*רCr*ys'_b ufavc52ڝ->V=9tJzYj@ysM'IYKh_Q_Hx2*k;+(Gbj_L J]𫞁Ve qhpjLo#)7/R=` T>P* *.t/=b0dreD 4W&7-ΛY㖠? [4hOQ.&5BmcJ3GN&׭#aIwݓ](*ǙWb9K a?3[$Z-dO7ZoƈxaDߝ}Dmp;ܭq9cuy{6r\X}fX]ޟX]+g.<G8eGdLjqkƑ֔:LwF.`6jn¥䚭Y-ˍ]+v[Y͗?w1]zT)wwo"=sѬtQ_x4윸p]v43Z8]O^}뢌1(;__?NbՎ"Eeh;PG=pPZW'Xƨc~=ԷSNW6m|`1ҖWMѾfb'|_bmWb\ ?+ V*E5ɘԔdڴLi˩K̹쬾ɔIQ9Q5_z13Wgi ^#Ov}7I#>aߐ}LmGv:">5Ƽ3o`.qb͔Yo~Kޛr } Ozo`6TDgݜUu[9+*yQ 7P,k];ެ>=IKdvGpfΗ@厊Lw)*Xی$Fլy?><MmTAƖm15Q5_UЮd}1-o@ShUslcJ8>Wg;G:J_0u, ^^@E_6.TP5X~2*[us\|vc"흚́؃t;ʖb"\ F"ʙ +S95><ǵ@lyr/ʹ\PlH4R>Ӝ1~ZA~q3W4.R-C|tAcނvV7^#>*ڔT,Q>{T/mw.R,FسgڿY՚Om5PHTqjJS_'geQ>wkuRJ)mڡh8WoKn OK%t){#5!m`"5MTU(ܚP9h(!+QV(2w=_l+Q]ZH8$FyOmf] Bɣ%읉+e^M8SHN;T.BzEEՏ:~ͬT¡BE $ (pPdyM8Hճg37 W [{澢yϬ*.0uOyo`ߧ5s߇淛 Pr|?>m! 6ቾA+|vݑ([C :56{Mo nt\AM霸kuN棣%Ɣ yԄ;zhz*'ݍ&]\bP}y5<^qYfAсWc"1vIwhc>FD_#/Dj~yiL3{U͸M ,X86`(SEȈjpz/vMRF 6މ*4aFͣqDę<^kHTCqJz`{0A?k=誖yO9J1yT떴 [+^K]{kœ(lH_l>֝cj=X[g7*gq͞pΝ)? Bͅ.TOhm@•w 9"q:f5FUO=̿ݡgM9]fV0!6Bk3ڳ"iKv Gzt8&qkՇv/۪Z]C)?zQ=v;aأUP(Y)EqgYoTy(h(,jqkUTVQ*8JEGߡǨ舮wgp} $\0z#nDx'ܘpZDj焣"pzD0h~QƯET"]F͠$e#m1Az|v6k|OycV7|?>')@o07%טѫ~eVͽB]?>U:hs_&=f+hƛUNv=>i.O3guR?g_•Vr=mۗ>θ212cl'Ǜ|/rM<853dNNJ9AYTt@6HIBE!9XB6"v)bǂUlz V`/;sZv=g{3 T0'{> q:2F"MahOёMd,YX6AY K3)eVPCJVF a!]`L,gA y 7jYOMF+Hŧ ɦ"]6h\ì}$~GQ;Y,inl?v2M0(h@#Oև%v Fj9U sa'{JdJcͭPтJ淧;;9̻".gCZl'AlBA:3c :w\`dxx;vp,#?Z=ZɇAz$@uVdW@@=)j|rP̜bcdַ SX~Aֻ^~|4ǻ6ѷE};xGT=J(A:ESЂx9r ImN(Aey5;<VxMF iBh8So0o8:#h9UZv_U?kU£lzEK} F?Z$/J(a(_pZD++r@-m|k槎#YA+k5ڿu:ٵ0,ߤ]r;dc8aJ՞ gط@8Jɇ@J W{!YBy 6=~펜šha&iʁP)kl @l#Şz[ #a3L{2N/mf vP{F)IhG-#i**Rȵu2Zu : bN@םF!ˡ5nt.9wn_ΝsJֺH֕Mf i-a cH.RhLP 6#)| wوTxN1nyH&qޚ]޾%#Tx <ѹ*DD~ǩ)u^8^2B/|1+/_^+@AnqU[(?KLRz=kC0anR)/E?6//υ^ly0Wq۾,Y,_X!f 8s`?~|56 o8."1q(ehw?%/&їŮZ=֚nd\n3WzFbz_kNZe=3[&!!՞\{Pdly\tSЅVF&FAzݪ怼+-a{9xVAH;d3:YJ~H^!VH|z HdA˳{@9D@_\q>!zXYתH1wz3gH5K@25Z˪#H >v:C&FEJ֌|QlP@Kp0L/%95ux.x*}}Pg`,&&8 G0GMjI/NT ]@OaLl^y!{:˕zv+5=eςgv͕@4+n'D#Km*1,u@t0gJA?")¨s(.bolZ f%[fG:i{B?'^9J}"{őt5s@7꙳hViT(ݔ,LKs{HXs0zla/KW(֩2b-=JiNc)tS3w$xыa<&"W"RrIU#%fՀ\0pQs,ǡ^,g L[(=FH||y&Ogoݓso˩xZi8OOиGJ]*cN5(Q^WV5m!v,O>6dzFXVlbƀ22_\@2Ơl Xg<AyB^ 3Y؍q u-`zm2'gmq+'DC! *AOnfpi6RHӱLu2V;'6פ:J&]9粑a9MfdXվ #[-emus՜|]kn + e<VbuHYր@R؝~cX姥G:ofj ݟY%V~+k..q!ym6"jѼ@p6}ӣez&Cѽ2g|ytpۑ+mM\M=2[њ,СZ[V.OF+rZ=},.-,![ Ǡ>/*o1]TZ%J]ܝ-,|B^^B> ˓_EmeW b4V5QfJ>tl3 7u;F闕MZy#βd*# T Xr7݇*B>I#ԸV>7509Wk:CC< s ,_Cld%9y ;8*}̾5*ʫK֮B>R!Q^H PXINY <5dz4_,/^% :ɾWX`8B/@}ZfcmδfSm3=s~8Kfd?NT?B_ʱ=rZ)=~Qi0lajx8ګ59>'A{FE|kzV;)R^?&[\Gˊ:c)XHg{ 5KչZоjۖ:7Sz7}}/5y5:L/xn ۼ^dpk*,1c q:$mzeE4@k g4=k(hAJ^H#Ofxkv̫+??*1O{gAٚ"bf\u-Be~47xi@__͛/ ey M!\Ffre-7C-pfLP0Οm"jMmk˳Q(_w꥝zB3D<'oC6™֍SԻ~Ud~\vfx\YI/a0ǵٺYcM!lm.ۙm !Y!/Wv޻~]Wu"|p5] B|75WBμڃp,$ߪb{!W:wsܵfJ<3dqʚQ5EEh=Fc\T dfy(cW!fd ұLvWVoF3X ڟI0۷^P"J-Rgz)?g}D@Z:GTJp>IܿxAwtZ44|!$.<4$D`;P3>yy7wy/mș~3ݘP\nB0ݾ0Nwwtb޵w>x>̻bݪ0& ļe2D_V!dN Dq*d/]ƫyab2Lbɰ=Uļa{qyw$^!RK&&NU?Aٸg%9zMtfТe 4W@pRМW)/>z=tuQ Y}Z@;ۗ$WX[؋S"Nd<;/:+M_&n*Zl+.l5 y!H/ 3wXfE'Яs2 vv?\#sI S7]8 Z qi4;8bV4ypWK#p^%7x,E*(DE }މA;V / LHR*F s#忳x%ܗ2W1:~'o mdE%{ѦZNX^ْe]h]@e/ֈ~; r!J0u*J՝w#8Nf8G*N'#xR"7tT8&.#تXX8~Z (k)U{45|*3r_E[HKKQZgSazZc [ Do-wj71hpjgHb8i8ӛh(qgAlx!̩a8UWh.um{QGKz.cE:Y P>\,F_@\2Z̝ԟ74=b( ,g#xq.$"r!yGHl@ӗlHoFU`ϮZ sidsT4`*2]Ƞ',D4};_].$HV[Lvnw\pz1HaL1]}f3.x@~}Z$O&M&Qcs(VgQ9cq,ĕR ;2Zл3$Z>%B+0RE+ɮ=0 -=i^HJ8#oqa:U{=_~vecMg q5ZYoу0ΡqtrmːP9gvޕs"w8tzV[UqeyCEzkuڱh=L]#Ͳq*vl\uGv׷v;2o'RN =OL~/gEe{+j<~/l/ޔ[m5W]&zwO?,*XK^lxbL{ ·r)2f@N%J:Kя4y*-v Kj0 %(| cvصyY{5!Nѵ}3*G鼘f8sH:-. Z!X)6j ;;|a?yuJqb5C{J2,ϰG#YvqH=V\Ը UKDΝjy͍3Bmye1GO~o9_Ҽ $/xlMN r2}LK8.Z;V Hmq7 o-Z*򈻤DtxAyx8#?nBޝ}׊ӹZI_. A{uh{Sr w>*7{FBq}f@zפ@L⸕>F)r J<|-mX}Я:29]REgٓý.W兯,p) Z\[0~yrVF^%>M"L ]p;eyfNkcy%d͡u?4nACg!g_hylN%O=z3z[X\w%kX5Hw>fkzDgk#K[~F=~ݷ#GeaE⎇>Z=jSDJ#p7999E,M>;"D9)F*{=|%SțRc~P抓ASǛIȜQ+}JO ˛Ld(1Owy|OxMO7 cf *x~G\*?x&w)[mݸj~Xd[m'w{8XEy_o@=}6iG6j)6/DQ~nn{-C?5u{I.Ojr1o]客j;63 eJriՒ_DZ#棊iD}oqK"-SÓv O:ň$ΓsWHSX<0}e8ai2aD'Rzt^.7 1ñS[kEGՙ]H3K!Zc8r0nO%K$ͼ͞!XR{5߶3Q6W5@ʎ(Z3m2-{[*&x4S{[nOݣCvzbLgdzKsb ޮxoG &,ѢO>?9A?Jf)jGdftd˷ +&x- Yhe*S?=;Ut^[uɛ[Ҳc|OHЇ ݅̏v7gcx?}NR\!G3_ U8%}V-dI*f_U5X \ ;qaBGB܈JK˄egs|H+'#IKeR7plHw3,OΩ?xwi^SJ },@@@Ip?!.9x:G%p#@gGǽ;wK*VC٠Wa3 g@̙wNȾw>},ķ_煟 ?/^e2uV=e>?3[ ?7DgWsyy ;w̻㏳>zȚg|dH yQPh0pw`2s>~Af bz9F5\w *7X~LoqނbUusq:ĭCI:]Cq[:NCu~-WlS_I+ V. u-߯|$nԄV vq3n/;yI㿐jۺ,==|֝|Γo=`|!4{L@V(aP_Iwx}We˕7\?@r=;ĐЕ\i]h7a9 6C}~n6ʵpj3/1>kt-c|6Eۈ?jt-cmt-c1F=kt-x9ZGvs6m$#lݗ֩+c%ٺܕ&wq/SW*eٖ]l|u?9T|u{Am۱crƣyJ@V<?cnS8>mm43k\Q9ːzƝԊgܩ .KymlCu̥{\nF[ՅqwwA7qvwQ7q m/(Lvƥ]nyy ]62mn|WvS)]=kk{F$*-x.mf.+o V[txui*n5Wm,%x9CրP9w Lh.ES˻>JߏHsq喫SOQmY_[C-*S0}/(E>FO+ZQug^JN'tSwv;:ͅ7VrYvxMWZ$XJH`N>Oz򁤢\=Y>/wN ^~HqC/W!sxYAzXSr|9GxX"/~si)ŝxLޅ rA'4g> /-wb9W_i7|J9=K=1S8<¥6fKOTxgطs>H0g8J]g >\+"O 酀@`XRQ_~R<\QϳBZMGt.V,X~݈DғX 鞺d 0% \[ssax h`W7k:/@?%)_vB[G" x[t =V-KhՑVH#! upq:0Z8fnu|3souNJM(rm5DU"& ̜y <;#A׽3St&L2 "vtNSI8Y cgxnJYCoͪlnl/"_{9u{ETN{R#y*9ت4tX ',7}P ޹sd+بsd{GaT2Lwb;|rH΅3vxpSÓb Щ濫y S\_ y]Yz E(>?2R{@{HHΓHdkxBPn>x9!RgM=ɫu2ija3{~Q@Hb(42JÜ};ɹo:C`:xO4wMrd^Fv,G# r\qk ݹ)!س gXN1g@@$ŢOݼ\7wYG~:aO@ ѣsZi.C]:^h ap(^BߚGO> @.KF| rmx7l$ ƓdԊy]ac0UIk ^y29v2>.Eg>Oc;D ˚7ʳC|_w0֙gYQo&hAX!,zfn?*@k& 0uw!w3C_?yy&Es«>\يkY_6-AkHV׉Z'> GԻͭ{/g1?5c֖vj~}c]Bt/ߧCMkʜ5ɁUo|ƒ(o$--Ox_?IBs`hR|mVC0֊T5 zﳉzS1ง$?]ö2"Y,G7i柁i76OxἛ nn ןwj}/P@LoX!?.|RC ZFѽ.<.% Ɠ;A|_,? # 'hWKQwnbrt>W-۠ҌAMkYMlm0 :HikL%Up.5$l j_ Cr ]2\ǭy]+~Գo 8&xK-۠QԷQ:kRqHj#%yA@ɿ'0')U{lW ?l7x2׹#/yif/y6B:w42ţlW5?G&e:IQ"NdW\0j3ܹH D^ By>zY4 oOb:2$(k^_4Ubf-LDy4QLh9aU}8*8 Z ; q o) ǔ"n ugɹa=R%G['^wVnt.E߃L/]viD~9M?%#v,0 `5qQ*@ch)]EFxG(wGh+xGl $\TRs2>_OM,S8Dm]GA_1{ VydzE: : QkP'QSLE&5NgN];QD sj;|OПۭop4WF3Axoy,̗~Q*9s9*UG; %%N1|Ɉa>KFLۇkEN%Z Ol%̷ tlӅ-] \w>cAw_BH€rGW6Qc4-_vEvz5b4FyݜNIRh=j_3BnHLT*E62d/\("+BXq =ґiq^=n` z4߾ǜƙg0\d~>g.E=K\5ʻ+j??`ϠgC-J-&(rL8*= 4YlhLonZw^et864?u3lFOf p,n6l,awt]3:(Q3GKt?bO_̓h}2TDlv)R!́)b0׿UjK!py=tϲ]ACeI]--&bBjڴC#=$"Mh-Lrp<~5 ٤^A97joyy%ɛ7Ooh+a \!i8~]z'_ZmqťW}/9kGޫIGplz#h,䷎wtkxu$ 'G ek)vrZ9~k"9<89\#Y//>y&6Hۇ)u_6Kh]mKט3b˷x>N VE1:5C[L*7~o9 kٵ|2AʝΜ︝dVTgG>jejG;HGC]mZIጓ?I>sӧ6: 0?fKANzSʙ1X^?=4Y糕9i.҃xZ~=Yn|fɊz6w͵7 .nъ3b\,12z̻#JFm\By=;d04]d 8q1CHY Z[-5 'V%}DVɮ%h yLXzbyqP9-M5%AI ={,nvn'f @W&'B#k@g2tu=/ujk.1+`vK.ϾGVč~EttǨ:/OKC<y65wGu;akOSf)aPZ/bVaZ~X%n_'xz gxf8RգA{>wЧ&V7ܞrָչ6c.&|^=B8u2 I@r٠ѵ͠d+`Og^ (&Tziȫ bPVꡞRϣ^{1G:Z퇕7ҋ|S>ܱulHHcQSYDH±4ER.qMҨ=@oz9Sebٙf &PDIoA<]߂ ..ASGr^ęaJS7LAf 3E;0NE[n~+!kE A|j][׊~g{k5OS$oܒx1\r5zΘ}|k7q0>~=8L { XW"&kNsq=a?t2ot3 C}/i.OaXAڑE]GO= O=ID߾j/:Ck#dm$n>חQZ#nO;rY kȰz5.dMm,?K͎]wxr}nַi?ua]+L1Vc>9 +`B1WaoM/D<!ykfg[f=?y4Y|t ߞtMNMn?'܍\nO{~Kl"Yգ^;Č-}V/\O{XҲC?P.L1|^v%va"?RMݪXPW}A~y1A'\ -/_[g gӽ:>znȚ o\|HDǕ_aڞ}*x<_MC}`{W5fi`ϵ=dR'ɿõ ٽm~Wރ? ns -{lhk \'~Ox{[S:{&W f"Lq?qG(t\∃N<Ѡw'HC{#zwLjqB9)xe4{oAOX+ 9gj}Sɏ>=[ܱ]] 7a\]ti}/s8\v.Tw-g-|/)V0=ٷ`pi5(-g NpwZ~#+}SJ*f B={D+ƁVkĪw>z>%FŬDWIH:J  o?ȁ餖Чy&HgHm0)pߤ?~I-=o7j rEp&հ *t{y d&QJꦊyR''mv Ub1#U$0JH&WۤiN!@ -$-ɔ&!KbLPUAQ) E*]Te׺s}oL& u{o֨|X:G?-?qk7Von3~cel0~/` 0 kSmɉhȔ WDx|*߃^%/C^'uBǡlwCy}Ul;>?%3. ~7,reAc c<Ͽy"?g\iin)_\>nޭ6AɛxonzVq }zFwt oxJwv(3x~35=zj[`GQ(+׸}r\GX|ZDžr;-HZJZ,&Z vqw2BzTͱfz_TP~2,i^*ˮOU4][|w/Y uuEuչL›sGE[~[D›,8Ꙋ5+g;=?~?sϬWnQߜ\s2+1ggIp<6ߴ\hzbOݚ1{`/WV ?V#S=P/*g%_E$|C$~(m2C8.%_/ s p`w(gv@_' XҷEX(vQxQq[a(v@`5ߖ%ĿQgM[ /Njʙ*__?P(PQHQ EWoŏY=!{pѵ(((~QQ=EPQEXQ<}v:byb^ J{wQ9ŏ^H튘/O^VղoW_,*բU"<EV_^o,-(>X~"'{WK7qN%ˤ?)JߐKYI:)˜_ڪyɊnLEzǒ/kRT77PjfrW9eܴ{.L{j{bu.iz~'Uq#mǝ(XnC %+RP=M]?AN Dk,DqCޫqHQڔCb(oJ3Lg̼/G|M X_~Z2}Kva"; k5?H}w@uHr@9^/R+e9 lxalAII/!UĘ7)p,oɯRrf㕔䡫+%G}pΚGktI7!}QFJL>sP$r$uoXiW[ jk0!YChsv7︂ -ݡ`W+3 uwlHHގJ v+Gtu;!+wUo+*{݃l v_uU`/cBьaIfP+g f=ײڀ6{ͬKn$S<d\˰eE$09fHBGLnG1jW[:v#̇޾Hg_/2/NdeM|V` ҰE*hvYo$yF7es"׹fN`~:U27$A/`xX_1v .G7 r͒ˬ$-Zar3Qz35f4_w8rER\:2Owt0-֑֡TQI Kt2WNEu@,lCD6m!醘w[o+ u^و6Y.mJ%҆ mOB&NpCC(nH8&18CCT5P)[I=EAT tPZ-?zKM"9KQ :ƖQ@6嵠dLvxjqsNePf%q}$..W 4S" h4#[ܳ"6vVAro*VRbӬ*kI\\0Y{WGRjٜDml,$$pjoEHOZ;q} f%NdF#Zc_푉smѢ\Fͅ1m gsÍn>\mlB%D-MZ{ ֟ ha)h׺ƚN#KTZAZN/\~ ';wÔ(1@Nm?fsͣYYy#:" %%i%`kAФ΋nܨ G iy-'1RSJ2Y%|e. mDG@^`>FAKkɭZ<}X AX&Gz6Mۙ(h`6F,5t@ӻ$.cgh!IWN2 vdtꊠIX~[G௘;Ѫj2Z:Y nc5vf=Y uFmcճҊ ɡþ0ܫgRI1):RT GQnP2\ОP*E]`^;)|@c4aO8 ]|z 7aťF;hȭy yq3yjGp>-WV %8E(KYn8YP/9iM;2Bi 6v'iY4gN6>&WӜ۳QNn'BMWU0ZF<62on۔3Cxڳ< V4;2.ls#zLi=$1ɧm'I~/ADac0qYx*B5 ur`G ‚ G( m":ڲmw ha.ߊSG[b ϋ12/Fl^ m^y1ƊשbyGSl&El6MqZ .X`F‘OytcvdS7[iNW; ; Ow‡xx# G-4qtc G_8pX8jrᨩzQ3 G5]8jn{:pԩN/y<ϯx6rt!H AYRl!HBF4q<Ƣc09ONvƣgOpf#u4# Y5G:췇%^@iv~Lj=(S m\7+y@5>+eL&:]YixiԲ i;A,eqkK\l%vʳse' F9%6qI>oFFOfVZ`|<ׂHt:j17_F@hnڜJ5<;ǣ0]M~ʞ"JWaY, 6 `֪XtȆbnH|Ev @FK{C)&!ۣB:ArپQa -{]9q6F,l*HWS %aJH476'er\8l$;D6Kkl$ԝ)4کF[ҢeYS2͝N~FMhB_X`T8 Q@ f4 s̆Y+!Ak 243:E5'LO%IFv2\1,E=I̻(V`n< v$L^>Ii1 X)E%85xxa⊗* A.,.9S69֒F ⚫IKÀ2t'LQ=/NmEb"p8ԯiɽ埍+K8BYuג9Qyz+C`FGƵqI FHQ R Yyv3;!QC)*{a1`,= )\ +mAdۻ.fݑ` sn Xv: +yjEt9[Oky |2)6AeU"n;a ìU}=򭱣wG$k?0f+zS8ӝo}z/*Uq G42XGMP,~VjE3\Qt}-.:+T,?$unx^jk@ AsA s,*ch4doȢ~`e0_ֻξޮ0s!Zڱ;w`pہwoGHs e+:z:Ok(D{|r=9mcH3ĘcpWUٕy9Fx]UIa-\;С$mc5N|y'={FWDrB1 !6)&"۰:0JE dϬH[xT>ƬC0͂U[Y:+opeKfpou-)$ft9D{_!l6>D-CC"ݛ0=+ҕy"wڰLﻯ}w6YVbwd``\>]=aj,kƪEۼ- vtD z}1ϕ`,AK@vv4LyS"f ox8?cLe/:2QZ1CkZu BY>\iG ֭(i\hO0b+`=eCaĆvVj][c jw +ڻjJz[.*Ē|C.YЋle\lA/A/ؠ-,R zm %3XMt䚜y> ^g> BB"iVUH`w %/+WHD3ӬJ#N<̠Й0\Q'W?bv3q-6Cd ].UGSZxdӘdV%ݘعq ?4gS9\ADBc1dXb 5A,f]=9#+`U PW* 0f@D\ٚS!ADih:C)"xs/ߦ(uVWGY:l)`jPX$5d 㘸E8L:|HYBE("$SVTa4>] ʢ,YT/1%1m}/@BMQa;uJ[}s\Uo}!F}Xˌ4cd64;֠kinn1ShXf~e5; #a:jDf?6Yߜ~b~Sf~J^MAm(&՟Ln-[" G@d4 \d68`7zS@kB3Cx.8j6Ucu?/0%xl2\ylbNi<$9cm0Ƽbz/0w J  dA2qTP`j[ `.X.X` F꣣$f?+F4*1?4.BsC=E*i~P`#}%:ĹNzǖt4&,#Kl߄Wdz5 *b̜<┋8%y틥mڊ9u1E>c]fƻ EFd fl/ZXˎURUh6mQkEh'5qW&< _ha6gt` JւHMO*H股̤M\*ZV,R42DG'N1xQ!IfIڠ~qT]M"R 4 ,D7Ϡ0NpÈ$ LWnMͫn-y cƽl_ln4n@~@:QTZٽC>vY1.BЦ!A]֠Vhhn`-oua8 l$$AQfdS~A(\Hf84q ́_4ps%-#5abW6 #_B^t+a C9>A.x2as[4s1 y Ӧ ܁!Vw0, A#~kJͦ2cx"^Z1fʏZy,䷪mp9$u @"ܫ>L(seu׃ r.b#,ôc \. S Ea}"А7DG ^Q c>AQ\D~+Q鴴}EGyk%c aUzqa/C7X՘us*USYeҒc06,sI$MʌEV44aX5sA]e"tM[f@uiH-^ "Q]h4d &0X=Oyb`aWE5*-MЦVA#kl% _Zؒ2KS.؎ Q(BD>R^ ~ /QGGL-!*6LfꈖK0 亸S,WdMH:(Ty|̼#C-$ Si]L2Bwei-FqΚW-&iҗ$YtYiERY< &r(@:'<,Tboeњ h@f][#D2(0iևEv=!y"u:qZGr a&@qaJ9Ұ {ٲ<)$ ɀ| F3b|diQS?B٘M2bĔPj&DHͷ сzx \Pқ2ΖI&b/+`EcTrbQf2Ɍb&z+52ݏre) tqmKA\:Z EY&$nR2ڪR*L {K/m^wɆu]|نyԸO?9JFn"I!R] 6Mx]l&Y8hsTZ|ulYUsa `BE,dwVO[zbtIJµ0'"\fAL ̃7θqDI IV3##%@Hhz$9ua"o3lA#:p)#;m/f.3='0.nġB/ Ŧ۲[[SFQMX$&֚"(Uv0\[{}eAH2hTnlId3՗c 0Z2ڋRBh1rt -06)]ܐЦVi6X+F~c<{a誦%zc z@L#d3)l ximٛ^ݠmypqa<,; QL7:4m yZ[/cp "Mx@M ڱ0M#j0)tClGhU7^d{Čjt# hrk*Mc A/# #V $֊A|8@Om0u""DZ!I`nO rؠ;h"uH $$HMcL_n d{20L}ق 7+KN d8E2N mۑĎa^^5c)?x`M~ ?9ʢv|9&gj1q,T9L5Eilgx?_]%?h 7Qw2G߿^gjjxsQX?:j`T\z `ЏM_1*{u*Ƿy6} `Iyg(ZZTi船_\b%P8sįA:vq*ĕTR*Yb9",kzTaY b0ʉp*٫MJK.T́^A3c[;JfKhU~A8'>.Rid@1,([& XM["| ڰ#= ( mMkAW_L`Y2̈́Tln`O0n 6Լ=:.ݻz7e_4mu<ρq~WrC2\sv\-Tmcaާ m Ш-|δJGNM 8/}tЬV.Qo4vCkX F;6\烗 @4rԙf(2 jco+,̜B#y vtYqqDR#Nk_C/6(Tf!4!t 0e].3+|*Э~Pd@̒Ix)W0C\CSOziRk f}gb l)r?kۗ!F<A8Dg?n[GT1057ʍW&lne2GiX =0k` V؃9 )#$5Uons [3e?<, 0 Tiu yt{ X/Fa\ "3k]7f$YYDys='RN.ٗܡKeİs0y mmFQvݫNqcy Rҗh&&Ert_ '"('RC٭`Gf hlWO#ÖylQ[ 1Z&X/: >-udX]Dit468F%L@u%@ԍt4S/+aH^v#`0$I>Dq iDǧ9eOU.lUbFoEQ݉ HX 1 \sk7D`hEeC%B-0>AD0&40P\I,'{x#J0ޡtô&nPE7QI+Rt&LF)A+B%qSl0 GO8(Do|BRPdNXvV,jw?=d{s-YZx|y\?]+|9 ܗ ;+Зcs9z< ˤRQkhdʘaA;E*a>)ө=sd:˩*қٕBƐ2,Y;zJ{6:^. B+&;‹Ȧg,1"ݧ, &ACŮ$kK]iBTj̟WJ$RP^,St (+ ,}hb1h5? |Y wBGvՒGnMˡ$psş2d(+PLuI~iR{ĎoH*G-:`y>Rś>|T߬+uEf) +_,-N?3 3-b輼8`?;+U>$mqwN n[׳H Q<(M@8"] D6Ǎܕ^$)>^V5/a}%(ŸŠuNܜ9TsjIe oFW@,8&ǣ0'uӗM츼fIqO&+C{Ýe2 )b:?799id)V9|Ƨ:k KdL]=1J@"Q"NP(A2O҂4 {˼o&%_K =հLnwAnIf˳\MN.\V*5Sω"vrX7sؕ[O ! ݴ_۰-!֠|g/Uv_ߨp'c1gW3/q76ٕ a>*=v*hbf[1[?TxogV~eFԶ|l3f+0K\5UYӹmԋYvo dM>$TC$vtm O+Hq9Eq1?O613dn(ݯ(\|!'?gkMGKAqTvܿ8V?b.EgKClb:iXѹHyRpEQ-gX;W?˜f_Pon'Q Qٟ]pyʿWrU績۩PJ;y6.. dӀ¥\޳ lZaVKMp9[&ŧ:J`<ƽmQgr%x!v;0vt8tճ@ʡvu0GʟJCyF 0uXer=WUzS9{ c⾦@s3 ->1z}3{6<&Tou?>yU7=+ 5VUpχVyɕm㾶ԯPm%27T쯘r "Url wAv6)>3ewkK~rI6\42j+pnRW+]4^}DžC-/D9Lkr<~|fH_`7`Z|hGQU=GKNE*R l~Suܥ%ϕՕ-p.gt;KN8A\NW|G,sV!޳YB)YN9 p.+trlIOls)fm! <rzxElAiX 67Ͷ![3WoqY/{˜+ R|okسi׿zLͨ}=Zk|F| P,wV&`W~wZpdP)} t(tRJee-Uv TٿnWm=O]$RV8ƁQZ|)+6.g@&el=Jm `Y|(DžnU_]C]/ Ի+(Cz\`-o6ptjfjjK?/5YGP᰹Eē&Uzv%݆}Ez[rA%B)=;uЊnr/_JV'{ɖ%4=!6aCaY}ϊ#!%}h(=M7T|o{Tx[ P Sj*WT@N:Eu EOg`3f NP\̩$6AvtAXIk;aݯ*5\U.=#sDC"(*Tz8O#W& $ 6-cd@'@QssP:a(-zb=uiۃVeoTo۾iooWe!vv\,h8  <8Gs>s*w\y{~HeG\c=C2uB_v4{RW.c_#=vчBEU,TaH)A[x+,;tLeuMSch |0až g4fB.-!;CrC6y*u"K)K*T~}&A襙04$6{?1%&OG߷%)24h$l pZ AwF[?"iLa "V !h;x20!`,oI9zD i"CUri dmx$2RS*Sye*Wesė񆙡 |dhjO:q;_(k$/ぁ@{ Tb( [&f-٫ƠF _3Wls2Į\k;(-B,/ qhȾN[;{gPdؙ̻6p$`  ~(;]<{%7IKhя 9q;qwGfM3SSs(*!WHB=# Gag^LL=vVedY0HR`e)el$:N > !fu0EAU `S|s99 kUUfߕu 57͙JMNXTwTsV> ÂOx@?`%g6$T҇ t~k%~^v)UX֣E˯kV|hΛ[fTqjF0*CK#@}h!ܖ<ƃ5a2p澒aQ*zؗd9==ˊi!0`;J~ʡ+y +U1D@u>#WVSc[qi٣<~'Tk?^/x=G-76Ҩ}#^,T5n㓠zc!eMma9b(:Qw(%8uְ+-f.mTV^^nVJqJU9Ŝr2ڠ>۪Ͷwi:JvmyDžm,%֍C|Jb0o=([fi 4giS<3BHL&Z>[`4HVFedauv}6Zv~mi=VUXZ+*XB MC04 {?|ph04-\) M+ 6.СfzR7M(K*79K{TiszXNGNn2:tסJvzL[aKIJ%U]Β!n|E`#k/)<H$sS57n5*q)r]W>..ۃOcRtܲ}_XB|S6<)P9ZԱ Sx*C[hY2v;/BZ{uHu?\QK\C9ˇYȼ+Tu|R\e.KZr["c-EZr21&mӂ̶ dX=H9;PZ/sqvWVqV,Kh^`?'Kdأe8":f?4e ?ǚS5LAE Coyv{aك WW6'p;QYVWďUUNمcyR[zʃ꽾+7m~#!lBO`Yji? וs:}PPJ{?@/Т/NXSZbn(Se,>2|Ƴ?PgϦ\OQJj]z]>C`/4`{-k;bv$2~+wo[Kޗ(O1[U>=M6NYI:; YܿH r{F%6hh_ĵnӭlOonm9q)&9WqF]?:1OLf~LQգGU92uZ8 džL/! :pO)иRlvS4 nmc7 HYYXAW{r4:̴2?-8r=;?ϢEZӽAa;0@ۺRU_ \Ч=!YXTpY[OP HQЏ<3MV)Ky6xM{QSN\oRoAa ou9qǝ{pp%Ƙ:ptTt8qFRt<蔡E:TSN5:5,Bg1:KЩEԣ 4BRlQ)^ ,KRlG)6S}SN :,F tt8)ADž"Xz  蔠YN%:UTSN:>t,EjtjYN:.t8)EljN :4s:99As9'tɍ]vcX+v_ eC%9=#͟s`W!K{+<v8fFf.`|!/RR<oǕc_rag [\;އ)2 q߫d~|"sBWTyr宀A; 2ߧ(~~+|SؒʂE2~XOayqَ '<JyIx"8}w yl_{ jSnXۻߤjyO};^r< 1>+_o=á~О(/c:7WG䇎ρN~Bۮ_HzoCE߁n.8|Kaw9p}ayit^#{'%E}t:)}\TxO?Uz+y^ `#00Jy_׀ {>O!X_p(D-9or=!|ĕ _%WW%𮢌u~Ю"+o57> cg% y^mUiB8@"ggUg z7@?~WU,51aھUѬ;P5^@OHO;>/ ?^c< ӀC"ƒKrL@?!ԗUՇz@E@/8aX~_#@*41>П"ԟʓ]|n!/#/_FUr! )r_&mr{=B E#Mc>Ok'W}W<7u]q_=)L'ͦthN誋n.tvEtݍA(`K,bDUR lFHI#08eҤ3Ɵw޹?ιPMF';2 ,=VQH~!uLHPQ-j'80ua4Q8ԉ(fEV.xZc*E̺nYqOاҎ+ VWXDu! ~ NלUNV?a]P"#DD/Qw< M" 0Uxfrgx+^`Ѡ(ל+ Z\ܶ k;hl0T5 ^ JfXWOoS|J g|f9Z.9-Zиʩ˗Nw5ϐ?Cm>Gi}-KVmj~E.U;ֶj_2׏(mZ MgN;X0LZ[NZ6,{A?,k6@;tW1;$4b9*fFwf6fo¹867Q cukLq7t9޻(K*HfO1H_HJ}&*(~@Kݣ6uY]aAzvAe4"\@BF;Z0`H&F 6aVчSa[:Fuˎe8,XV-[тoaIo@E͛4Ǖٱl6sѾ*=PpA vf,>%`u@(7 J˖ so5b XKLWsor9'6p)5vLi]} %2MC .h'\XE#җKxe, U4XTD>֘#KhO*!0Q%:Dp:Jt`:JtuDׁ%יSf!D>f,weAD>D#&&jEojPT!c5{SqEZUO#d賷-sFtNF=Ǿsz+svQb֢-HyeQ{@=b>AtMQ%:D:Jt`:Jt!uDuM=? zD "D(wlQ#Db6 "bπ4Q$DGD9!JDȾ ADlFa+j>Ew5%11Rf#f 1pkзDb0 5=Rkf6{܈Z͢+rPky DD(ƐʓE::وPj6J;$6PlD Woz-cnmx0,(x @™Fƣ iy,S{^#dyA8X[6 ֐`NDMdv\gre텊9xR#a01uV{ٸG(~dإaDs*|e ؤxa' F,%4b$9tQA$g[Wa#7DgY;0Ղ9vjA{ 3 C9I_6ҧqOOH+P;mNV{ړOSFǸs~-dMM=ܳ+LɜC3JhaaZl]5ȍڄr&Gy Q۱13'pV#7:nmN0T?pVRB)s)ԤyiiTW`(s}T@ [1`@Wm@Jxf;3qq#sMtn7h&>;Dׂ4|ᘽmz}mbr\}mE&?<@#N@ sz@u6/(/C%IG\LC$"O7yhᅃzS,r9+6,{`(B%Qwɍ.y)7nemԦ6|Qdϡ=HmsEMW|2{th|">ƶPJbgO_ķJZ;tôG|[z-G[n\g7o(ַE+'|؁xvw}@(%]slxgka[Cu;=y?o`c4O_:@w@ދߐU#vԹq~ Gȿ y96eCCVgѐ<t @`r {oU:PK& @rPK7,]8mistats_versioncommit de0188a1f9bf999a464b5b00af72e95d19ed034e 2015年 08月 12日 星期三 17:44:43 CST * master jar-version: MiStats_SDK_Client_1_3_0.jar PK7,]8*res/drawable-xxhdpi-v4/app_status_done.png sb``p : $5V300F.:\}Q-:'*+*Ƈg:ʤWf_񗷼KVf]yٷZKj.{ CbE慷_~P{h4ʟ\~u3ohSUґ7?>[{7?>l\u/fk5ח>NhYї>jN{}Ҕ/oXn?F~}dۛ7UiG @8eC`X3pmo.( 2eF{䧋YGM_obqzч 1J+, ]#T=;-..?L*w:{O^:?f/+6 17*rU޹,E=3΅gusn^aB[5_*\/O߭Y[$9)uG"ƚS5#slzoȚ5ijhkD?di7L762Gi̓ Y՗XX_\煌GR֗x}ѬPy*cɖЏwmmn1ۣ?˛ .<~Ϻ+?{LwuV K(v]cwn||O҈a&o+wyUEJ*?y&S:&PKn`PK7,]82res/drawable-xxhdpi-v4/app_status_processing_1.png sb``p : $5VW>! c|t?@oƺ~{z~}7/{~u+*k *}kݣ"Ӂ\]_[}ʜ#⹫sŽcR+o~.{Z_*"S{}}8_I_0{ۆkoz0?B-Sf]dpD3oHy vDEyF[ <]C,NϝZd rl Wⴶ~63eiywa˽;eumߵd4FtgO*>KE_Ҝ!qC:}Zrhכ\~+g'w׻YaCu/ \X{R;3[s_'e7~7ͱ~E=2]˧X[ ǿЎ+G[2rJw'QI<MF.k5&42h>&~Pe{T֔'"Y^^3CWELw=)|3,Ũ~X>H.w)3ڮ/щxcW9i55e$?]oӶgI~4[^kR1VWOrsoe1{߾;yUazceSBPK b#PK7,]82res/drawable-xxhdpi-v4/app_status_processing_2.png sb``p : $5V300Z.Yξ(.k~vYqM;{5@rQ͵++MgҵZqlGgWf ,Ma[1_um4TY7I꿺=lE O>n@n=`Kr5חIu%&ny|>>H9:(%2+<˭pntMd043K6\{ӃqAwh2?DOl=8x ^׍q1C<:!EW/br bo=sdfAͬg^LΟ9xu&y^|풳ΝRfB}`&+`NRM0) NJx7abr5^2WG[0Nd0,~M*R:+H^J)QT]MCWWڸWL\ȕ֏r< e?DX+;yni |.8t[Gn278~ Lv_ oIJ_4Vc)|΅}/|Jtiݧ/ZE-0I`Vg^"*PXGcnܺaξrCU/mZ_3fF7 L/:8ceD~:۝&5ssDW5D01xsJhPK\gvPK7,]8SqIv=res/drawable-xxhdpi-v4/btn_bg_dialog_first_normal_light.9.pngPNG  IHDRWzτnpOlAZBaKuTnpTc (0ST<=[IDATx_L[m1 $qjh  k ]UU\eFv !M˨Ȫi2eWKL""j, hR6eD e0B dʤv7{IG|G={2al&@zMZr< ȭyKn]dZ,Klif2LY'cv@ΪJ[f9wi'(9ndVn<۵NJe`'DΝ{-\VVdYvG"CPp̶#ȉ݆VK^kii .M2q7 o˓,\p(]WWג4H2p]ohh4i#q3_ww[MMMa]+Ikknnn'McnpJ&T$ͣÇx< Q^8,pu= =\{V-n6P999y~7Nl'pFu?P-\Www[Z\kjjVW Ѹh4v& xZK= Dl\I&n>ਮn*/LLL\ qHDYYYHjP($cZp׮]{Mf-=~Tyϟot\u£sԩB 8-Qyf8WtT\G0<&,q-@AQQQǵfR7,$q5t$$qs/\Pm6D=n#Ga£A9¡t zܜӧO cp3+** 16U, cp-YYYBa ՚+Z,\pMf9S(4MJFUnܧ+U$+p7666VM0rW'kkkBaǏ cpVVVr ]]\\ p C(]/ 1s K a D8 ў{}ɓ'5jSg 0?<aQˍՍ6LVQց ^ 4³4==}E-<˃[n^Է3gܜ zMhT\?>x *Lqƻ>&cp? V_/--Mzܹ-\q&C###2@\T ܻ}   \:=D"!3p#555bgó+**ޛ0X>xdf^:^SS^SSSIy .k݂@ Ӿ_,// -ɟDluurMI%`ʕ+wv=c/=W} HD( gblI?-|UV7nܽxbfĔWa6y['>GooonNM0;-|][-np*q> `t@jllzUl6Sp͇n| [^^8;;nX-Klj'KUܯk!9@&`:[ٚC K#IENDB`PK7,]8ճO>res/drawable-xxhdpi-v4/btn_bg_dialog_first_pressed_light.9.pngPNG  IHDRWzτnpOlAZBaKuTnpTc (0ST<=9CmJIDATxLsss"N,pQNN;4Uq&횦! ?mlim$&6&kΐ[?M йlMa D[K)2N\y>*~(dMX|w8\dyi^sX$I):QTAEQI980E*xWW+W{FKTd8%G VWXTFDV` aARD X佼kWE}@^^zQU~]X7T4,*9ܔn+p۷ﱍ-]4M"q'jxD'Q*E]O|e[5 d@+pht{O 7 彧*^OLUUe#24JZe$ >ْ$i2*( 5QAJ*W0s %\]3Dwaaaa(UwD[" Nss_[Rrb_'7 7K\' >_wv_! N'OFfob^:LpO"z]+w@,:=K,L "mU2zsvm p @lrrrhط:M,p{cuhCQTHî-w&]Fˡ-G::> mN|/¶-g= "m&h ]p7K&{٣~#bb #G:z\wwuzl׃'2>@KH5Տضpُ{\|[`˻#'2 .@, q`ة~=pΝ?}ѱ-7}cOC0}=f{gxv_z$2GlqkŅ3pU.KSSS牓-.Lu|*=.Dr9_Sf{ y o|u^ٵ> lmu}'d\9tP[j1UꌺWdd|kk@iYXj`-/xː[ Ȳ\ GLvܻ```jj]]w\5 .IJ'CәgFޮYbV*JeN ى|^ޯhyv|E8r uۯ {@IENDB`PK7,]8<res/drawable-xxhdpi-v4/btn_bg_dialog_last_normal_light.9.pngPNG  IHDRWzτnpOl;YHnpTc (0<=GuI+IDATx_L(E(BkR!UX5lvxa f5Mلl5Mz6*1#l?0]e 3,t?b6EA8GayL2o37 xK #%ڢ$eY 5J[J&D"1g#L`||?22Wzܹ3 _@, B^5TWW"W_⣁@tWWW73$\CȺ8e-LMMUUU5)~5|7I&Qr*|&r5399yCCïהdkPa`5%;4_%r52777vFy \ "0 DfBPGGGөS~$V[OTE%5RYYyPq"W'! $r5oJUA`0p8>QI$v j!:N^^^oy"W3~pݻZi,rAqqqͽ{+q%rE?~]@رc5*E9UDfJKKk[[[ ssscw$W#[nv|O}'kӧOxggCCÏxƑ="w8f"w|k'N(((xr8s[dT*o]_޽{ާGBnܸYvEQE$ɑ?'6AOOO#F+}XVV1fXE2 Cyyy ȰL:::Dht6-yD%P @9[؊g022TRR1$W㝅)<]`0p8~ V" *_UN5GC_xAsQQGj z /v_ٿoT|u8 [.}.ܿ}vɓ'/l 0ZFn$it:T5BSҿ YBЖ}V__Y/CF&7 53/}ؿaR{[[[Ξ=G_Z_d2z-.}tZer-ZmjR N[+T*z=Owuuu3S@,rD]X,6 F^kCi%z /!q˲d2H$fėHd:OLLLL=̩Ϊ #l/گ*S#1EIRd_QYNYIENDB`PK7,]811=res/drawable-xxhdpi-v4/btn_bg_dialog_last_pressed_light.9.pngPNG  IHDRWzτnpOl;YHnpTc (0<=SIIDATxL_'TYGVTDTeVۡٲv:4djeuͺ-FLlcsY%d3 fKT`p/oP+y%w7|X-qPG"j (?4l<2 #Fbh4:ҥMMM~c0n\ DO ho=u~Dćo-<|y#1jVXM WCjLtv(-+-?]\l܀O4E.>_cUunD\$RY꤯r[QQߔՌY'@:"W#ýMMAW{۟k9 u5cǎQI;88-hGɾs@ 8/skXvֶ@.!r5bŊ?%,IZwv֑`;oɰZϋ\,_bw]}Ï-'THjf-+~X'rM`]'JJh"wRJoݢ-K~Bh_/ X)rM`۶/L=$55D n=v̙׀"5$\$%%\.--2Egm_\]mYzV`Xjb,˥K?\$rMšaݦJEj&99w= y"$_zj]jfeTk)))yyy@5CRE lظ9Ik+kK.&dIJ5cX)k?!5 I䚀bYXD)M$dD"50C䚒ш$$b0"rM \# &rM ;DD t8ȝmj@hT/eaֵkVT#ɝ"uv@S}ȝ55sG4Gي܇hq }}65Y fIٙrt?"w@%{]I `;.Y/>θ+"w65v`&{]t[.z{*r6fDu^]]r-_p s\{?~/?W$B ]uv#G.313^gms]|]:3_k B ej=iny'qI.ije DK^նV%}_cUu'Ig~x|4v]/-+-?]\l܀oc'0wͷn]+,|ӦڏrݣBIhc!u{y;~&(Q[X-2/P$XIENDB`PK7,]8&res/drawable-xxhdpi-v4/button_done.png-zTvB7&bBR ^wQ%FT)"7;;Ҕ:" s;3wgΝyvvhPrP*ϛd;>@Lo$Zm`sݺq?:KSNCCT6^I*O,)^7='zzx\}鴁:{68/iVMYo((LwZwԄ0nP!Hz!g,fHkkQBSi7/h*D$ !oLhzS ~2޳ P k ne֏n$AKb| v]Ё;^ "Ec}bƚ4B\#y#!]"/t.WzC6VMPj!1aδ7soYC|-P;D+zRj["zz:)L@@'X` !xtdBQ<Lܫ3b:oC"o((X{;bW0$)Z\%P(88=5v:ӝ,QR LRA˲2N:•4SCzo*rNv0JVeg14~b9A^7@x|gl(u( J6'E^|X^zֿ1TkX-R6@,CpZ6ņߌ #H{M z Sy_}<9go(D8$GZkJ)s,g+bEh>-ꐃawUTxgE✐vSLaC 4u01- T~If*Қe:@;@m7\ ޯda 'ʙ^Wb0ߑ[x.vv60wCJ{$1bhI=LM?LhiWV3ddNjSXgU}_.SdU gr.tt *n%dzlg5Sԫ[1rC"GJcZ@ )κò hMXCnȨ; ,H5 ˻ :\qzwV:i{cڐ.2,v8r>s(T-$D yWpWfO͜c2*"bH4 |WpBeKDkEMI\k#bz9=\1}*TIN0s6ږϊ#n@M)Y8n }bL3f3 \Q$G04t^VK'^~%éqg?WY2Wq:͒H$@wf^T.&X=7Fj:AC,"l4Y l]g$``kndz'Ix##f̗OtxgkTZ]YT+Kkh"DFTa fq$N&R@M kpB~{ 8ipJ8۸"KnTji' L#?O x&1<tǪqn3n2*G>lx'Z obgd݃sOӧ%!>yGҟ fz jBWOxe|Q:% Ctb#x|RU7>𗩬!@ 0}Z O'B?I`+ip!S uJ9Gb;a7vhMzC炜q_٩'^I1 ?v`B_n1OZi[Cw0l5;Ǒ/ܑkY>|gDDJ[H"OS *jKӱ~Sx!f"CMs:xZ~ D9Y݉B3 R9S%Mf&?! ֙5|+!WS18!ȑ}C'!8ie1'KqBXF<^oE5#*"CqtgKV7w LQJ]ְ͇KdbcʷKp v&jv"wW<c|!0. |)W CZ7ʰJUS V4y/|ITH-R{(sђ-΅=mFO‰V2CI\Yr(cGPXBx&5!Mc0Tt?>ol@1@2"Od*(|`{6 )"jq%!ouqA]T'8[q9h>rL`Ҋ6 jZ5qFiUzrCr[)ƍNyV3^'tY;M%7ģ =HrgQ^I`v܀H;2YCshqM~TKz?DҾՀ=B<9VN($0)Ҫo EYUדi][ 7~ʝL@`daP;*dcW# ك91M#Ʊ!dϕ7Pj"C¨E6@9(&imp۲ަvyq-ʠvGTP,Hsۢ8X1Wݳ[?m=|6NOt%UbHs>! xP mgJgyo8.ۦ{ Fd 6FHz=ؕq"y1%n+<Soj_LMuS3FJrJW}:_֞t\^qwr}Cv1}t4ƳIpB(6 bv7J=Y*>.x::gE>3hjʈ"<6ܩ=~;n? =Z nNfH8H-3xq1@rGmPj'sS[˧vnJ㖁ۅIl$mށ gJ[}芸3G2D.Q/Bwt*'{/&p#2vEr3ݓB迧RT%n6jG9"5E~[2B 2/+X4˰lWۥnEx\?k>9Mgo|$ӤH17jkW|-wٕU7lGm?~{W:~8f-4@ "eZ!a߸ai&mXk3/CM*[s%^(Q~<agOZs,.{X̫>b]s*4c\<(LK n2'W v$7y~əɝV0 d7tc=ad}NxIMJ7>6S%rhn]˸vpA雴R=(E=gŵ6lrh=&N-|y ELK;~'cJؙ|!MxljO[S`hnQ߫T鿃!5qū _kߜUHԸ$Mf&5׳;[Y5T;EF~tuQ~yb닸ACMGDίٗ˵T;5~nFT: /.:':P. }v_y'].2uܳ%𿂟,=Qo޵;J4>a[-9'+W;.0*z2GPi__<EWЬjXދ+Wso禽%G㪟mڬ&><]RL8wFz6@ن@u!iK#O؟Lv(>9 t x2^ȑWPM_i%3%_,-O7Xd9X4n=/]eX6w52G%᳐q~d5mKe["sP,n)~qJbJh[(팕Gs>&h^SP'm[K hT2rڕ\* \.1aV9?2?7y"F%sAO8,R^V ;Sʄ]J&._Z<4|!ԩ詨f3c9P`BeM+yJb֛rUs -u'Kb;f?{}?h_JZx'OI'FHA'V[5dc-kD)X6Sc[b] `{u "H݁` ֮&QҗnKU>fºֽw5BsAU ftu&.Ix_Ǿ= `tWc1Mc1)0!p2r?~^DdrLf*@T~J۞^AgtٶkԊ Cg?|D;غ1$~\,{/S8`cYOITPÁtyiwgɼAvFt#Ni5mLc=q5+U<#_(ﮟrd~cK>_xZi ڎI, % -<|vn7}O]lS1?gkx{.q2BeqS)m  z<50*}=+J[-*z44nzG*=L(Z sSc'}|N_]X'h/7]? ڿSM!7#ׯ6L<, ^oI0#0cE%IdΦ$+RKr$2_x`]Pjw, <J}*ph=CuuQוb&}_FS-&젢8_/I/ap X{Pi}ȕbl< OM©73$j&cjL1Fԧzq;B+7`mAn:eЃnd1s+WEEiI#뮜ZR64psm]lRKSl)B?z#CU'FLОyIe̒?܆>{ ʘ M4Ŧ'w}i|Ω2D!ZR+kB0k`r~^kM9 ]Sd?ՑHϿ/$y>eЛQ FȨMvb.ՔBWn>w Ɓ WAщPGCyJad6GNvvޥy޴-2|g9W~4ˉvLI:`='=۰o%E}etl.dXec9:$?hˑKRu5 ]JruJ,2 ܣ\zKvGHx?'.VUPBy $ѕb["C`66ʖJj# Z0)sG'1|1uCvf_t.9 s _ F^Osh}i&Oҹ#nf ;Ĉ ls~Q E.2Mb,-JWM" dQ<VmlHճnh@tp~s'4`:ЪE]`"B}"A!|YFqU@Oe0kKZKrnpiTs3_>;[!YGǏCpX[8"GBTtB?OsrML_[Cʭo|/E~U@ q%69˘^>$F[-%v% n Vj;۪$ wfC2nmhF[&v!ǰoy jY@ @;CJ;f@?AJ, cWDrqU \Q; O7+2q2i`ABIL݀96/s=(Ep {7\ G[&iSX[if cnR mVj19,"Gψx0gGQ's,@oCJM뿊_Ma,^nn:=gJvkz6/JN{@ЙifS>c&t+M `%ߣ (*kVihKr%CO(S'4VHF}dJ >0J%5OQ_Qgb\.-ɟܫd˨OXbsT;coւŎD;l9LKi#U>qngFD!jQI$Do򭎁2܉A.Plw@9π'$A[oJ/IqqM߿,Sx"*UA7kö#o-`UOl԰;o# i!~ӧ*wɒ;=; +hJ"!E}y&yq, MxAPϲ-#i0Su6s9!sC[5x.7 ؃_ߏqN&i1  " {64 c(VJ1o<, aN}}@VuW&f^6,ds{ CƏ=j,O5bwwNj4;x-* 1dZa Ԑ#u?`jr擁ėt>e7Wu ,bz,7 vl z16kg$Lѧ)Me;/=G(I)Ռ&T &gdwtgVoj)/NoJ*b);C{[@騖| ælk}ܓ|8trИYu~N>[iFF)2:2`}6Tom8>CK⼩GTh̀ejȄ (wm]$BӅw7N|j%jK/Tc_5hčBޛ(ܣSpKtFXa..UK.,[cї΋~, B ,q|Z&ح+0+%~%&ԏ.=*5c5e'|NT9ie6/./e,g D:z׽ԗtrv}EDAõ+0LKz pt |T*/!R|KRX{;pӋ^'^oc!n)e㮮'2;'v~zίYGsi_-:a2!o͹6LOʩYLYpB#En_;&4pf"\h0Zh ҮrbJ^jJ.y5/VgTr ]y ~AdK5y7jF$G^5X\iG\)s"K<œަ1h8kڞܟT2YcU6AJ[%(.zGF:軵_ǁ}8n:&AuӬϣvCzʤ? xfQrt α|rßHY}f?{ W^Guux}Vi\؀uxPP Q5fק͚i> ީh*mX"Qⷆ;U:}`R&~kgW:_>)m܈3d=ц;7fK{Äxߤ `d7rӵ)VMUߘ}CjF?MJ^12vpqqJJz9Am xD*/z:bTL?YL6&G(_5 BmgKA:21m㏘os.(4New, B"[fjB1P5"퉗>mU_jL՟>w+k1fY9~홹OYW#l 숝tA$XSnȃ:>99xd^FB~JFb7''T޻C+t{;Bq2UUPhW7(ZU} ^<>lW1Ub{]{Oo̧(ؙzͰ(fn9AEgp*]p'w?ToasNb֭Q$h/C\ukժV)= t3&6f=([N cсpd 1}͸R2_zo䵴i*ei-cS7&IY]ў+K S o qC=@)ͲzR[ؐ_DI,灼WB_u[.q6qmH/ 6rZ k͛c{W^vÕj!ܛ,pmZ&^*Ó.J3Jܰ)Ǩd{a%r|wCk"=fW|!yIVF+="5k.%w?hf ϧkR_p˻M= #P&;UKdR&-/ԆNy-OS%Ӏ!ʳݷSՕh6FT6g~D#͙M 0Q`q4=`RxKp ${U-w>,l3Zh\9/`cWrpT:ul'*E^+= E^)Vjbg6 FV&^}pxx[5ysZ1mpۼJ.nTZFh|?;u@-~Uы?0֤Uαᨾk];>/,ra&U!7경Iuv?f]ϕ5?,X{[QwCZo-AL88~K Q/.h~Y\38>xf@#PKJxpPK7,]8+res/drawable-xxhdpi-v4/button_install_p.pngWgSEH H "H =!*"(D* Ei;H^TDHxg;gpѐѡu5MȮipKFFeb:ޏ,cj뜿UM{o[sƴܪ͓ i#?4K̆φnΥNR^헚RRQ$o߭&gO"t $9fz)Sg&>_Q&7֑(],vo+,k~_L )›բB ă[r[UΎhVE}dd`& uf kg.$2H8+( EQ 'EoE5>zHmn.V!>  ||hzOE# GRd~[sj/f@Pcdi>}~ss\SWe*9{td]S@SeMɣ}j?$2və@]}N&`W(U6`4"OF\sq.t<2?z=<&j4Z+0U?PZjWVlzGׅ.*{/b_o1g6(rn_|K?b[E`5`C 4}_fyRi@(F9\eqD%˲yk1 `%)Q]@NXn"Ƴ€InK\Wn o66 w³+,^yƭSFBwR_gR^TFճLDWmūp q` \6v8ޣ,(E7XN5 2U [=L*XEd:Ѹիrlqz][)[-Z@ͬk76k+4J`\=/ w }}K;K^t,3!㥟3EZaWAj~Q{8ZQGU]`dmkotm~ :-©* s*Tdrkr  +OpiC$H9?1NXy/&I01apf/{kS&ZSbKzV |osΟcESfEoMЭ*Ry-}h{r*yow d&M.O`8sD)s'@q"q|gD>69nc9]en)5}8pM3wUPtYw{@!hgF Tx]{.tJ}ZJb`F[32 3 j?ѸÑA&[]yhm8߶xiKk3gȡ;ϫ wA0a`\wC8r>MPJGB| בON ۨ|Jk@؎EdDz6 ]Io{7 CzpxْX]99"z*L nDB]Y嵖DNs(soL+zGMZݫM5p%T}sm*B=Dģ#A'{@9%ݒjd›9rK493I>PϢ04lq2E.Dp\y_SWD-7Y./<ۤ\CQ 1ȻҀxt,Q8Ks#Z[KU@A[c2*K+Po%@yf{$}TYuSA<[YB⼁b 5,o0 sy+hoV[+s+2 S݈ L%Jk{!$g?"#H[@&^T)Rz *I <8#F1 Goq$9 XU)!)mY @ZuB}\Z@{O\r|/c$Yv( yf̼pVk wPE.ԟrFɩU*ɕN+yl"ګ~ZRIq< MW{}R1.,S#$ț\c!6z؞-.?W.s&c/,0V'@ttAv /%Bw`ݹ:}-Y-a*Sz tQ~;?}ʍ3+2Ƌ*2@&qUk·rMhӋI0)#]O#Uh:OVw2yk6XO-1Q!B҂n_Ͼa#5Ze[Y(6>Xg [}D0B?E߫'i,Q1Nt:S_鐴[gjA&s;e崀UnUƉY4uŻ://iy髦]gb9u3/Oخ!`bPʰ^KED]*4`a}gH;+\SJZ񰽆_LqK7 ֓/ba/jJ$<-Po}3hkQlljCDjG g}e`DxeXi ̶>;ʏk؀mH;1mUȳG"U=#.A⌧F|Kk LC%s/^U%UydACjŇqw eYn?PKPK7,]8,res/drawable-xxhdpi-v4/button_installing.pngWgSEI( @ MPDjҌ$THiz(@(=HS tw)ХIW2ٍ̜131`cdcC"ttH5K3:d3# Z0EUF_&^fg[_%풞\:|z>_-s|W◇᛼r͞u kU鿅 ?;}8ֆVR0*(uj =U/gSx)"U#QBT/{ZW J_Z&fļtH,z-t'@l.dIz^!g=[b#1`ۺmܭ"lb~w)F~J93%{#Źmȣ1dhU@ )RlCN+)~併_ˊVi40E?b8H ̊8/YNVѯ%ԊmF?qiɢ%")[@tt=~\0,8:+É[)SZtEJS &c&MT/1}zDG i%:hon^ⳆNǹ:jMk 'jZP߸$6穳H]HGTҤ6AaqK.Ɋ#'j]GǣfDׄ  bb6phdUy:-p֫ !Q;8wU_rnQ?z펅XvYwv&AXVwW9%iV`HnǛ W/1)VbU>\=IWlԆ%?!ݻ~~2ߕ_ZT+tym֔Ln}sU6ˬ.Iߴarr:E5i/:ٰBQҁ/K*KtT ]"QoX"2D:<\y͝A]?@D/6:^I;@x@7'??1ZC/qЉ)JXGa:W`h+fSL2j#̸LPĸPbuEGDG,nPOX+<񚺲|1[wrQ6k2z K]r }92xx[*:+Z |w$Y9"BjAE|} 8H `Mb1dr:~y8Te7ELCT2ׅˆD: J/W5,8D$G+9#u*EsT|JV4Ӥ_%Q}ofYlc^0$zb ݲyR)t;0i|j`_'oTYr'RGked/]LUs*V -(d=*::pjM/zlH]l_j.$8-̮[JWdq( LHw+?Y=)|ޱsjN>c~! e}0azZAX#Q΄UؐtMhX$d m矵Vһ5bfp1CtO:"P|RTr_/>C.Vz|b| cjx9[爓T_GC3N$+fǗ@0+vjrԚGd#J[8>ab~T|woPK7,]8*res/drawable-xxhdpi-v4/button_play_t_n.pngYWFBeɥIiacAnXCBWiYAt͜s/d}۩ieqaC=w)jn|E{1;516FrG u3WLܢb ύ`OyExgKihQaV,rBJr/|G0||~Z^m)qS/1&m[$k?FzlQVSR0˹DМ1$XgӅ"LXKk+Ǯ R|@]~AB/obM|]WA CmI5}1џ0@Z33ccuaHrڀoX-Ö!xS he^+zZ *$Z퉍|^UisUWtj *-!P13C{iЀFO>}@V6w]Sr_p2mylf%>fT|`tѭOC) KO47kM]$xS9a'[USkepQj{/HM_ps#VU[.k,YÓۂq![wYTb(\xq̛;G[\+՛ȨS(;)z"XU<~2Z-)#Kݠ#zO}lm,Ps>łg,=@";&P~#C&斅m xs\xdԔPq{,RT*+VZSjuKMqьGR) 1T|..T°{J?Cƻj3ɋ|jy׽|s5ӈKA4ZkYr5=>h;mxHYA.&du'|Y.36=BsW, KNst_ѨTProUձ 7n#XOǵOhIL;`ӎ~'u]Uȭg6VM|v'F\t54/hoLh}VM#=!,Mԑ;XJ$R*p*ZN4凷Mѭ  $ViܜX< l9aǭPKzggW> ffqS;3S:.Nf#VCc|a:|irO[Wy Yq܌MOƥ7IӁ9ȉBLz^ 0Kk:E [lnDL4Qvu:E,wZgFӈl9hnO !k~{J <<>a9䊌u@<.G\? z ./*q_IP#1˙kb2aYy ͸{S%N{P=ΐnFNJʖItgwGu@4M$<Ԡ_2vd6v3ZDjcjKx/_*V?%hZVRbJco2og螝/ps_AC1' HxI!'x"pe٦5}Qwj{vyoz3i9ZZR8%:5 &>+ՌS0s`ִ鍤' jӧ*]'?uF^6?Ie7JU-%~(M[W|HS?)ՎQϞSHw$X$&]s:CkBK3z.0L*~+OjE7Qs๋X`s'B!B*4:jزFu] [`0$ dW3 kX-'eOv(y9yuA3ɫZ85e*j[tPm >AKuUDi|[QHνBѝuUXS jiRFK W11U%!g- : 3E5cSGdxҽ&hH!يv|=A8`t{J2rACj( S5GuAU9>9%;:%9q[15h"j["`ܲy8bk%\/hf_]R˙9GkUcZ0mmE~? hֹƇD9ݥNuJ СXEZICx<ٷQǭAspVid1p}]V)ثoxԊlE` zPtKxЈ9~>(OM8'1U#C)+9- `M ||Wr# -Kv`ll/O"ӏxMBn _?֣z.%UЈ%ZXg a7wYeiJx@콢>$(yhZ T&,쇑.@siM?_kg9"˧**w}B-#80xXHo=SXGORXM? X&,|k2_p1tӳ^+̘`AxU/ߑm(e^oZV/*D1#Y@v[,7mG҂:L +2k'puXZl?:F@!1G4P=:d'iCV4?r؊XϖWPspT [^K ? VH4nl*ȗAn˟"wm\fVݷw92sF"VEW.HևET%_EO٦M^_\ߏIvLu  aԹ@bڂJb|(si<F73J8&# c˟V,Q6=moVX͸Ul8w5)QH#p2r*&,0cZ&}HՀDW~!trn񲈹HL4=o*vQu$Jr9(4Tl2H^G)DoD@ +jYeAw߃ZװdX6ܨW(&$Ldty} ]pܐk>r5y8ȍ J7E/1e+n!.lA%&[?Gp Q$E˞ɻE8Gӄ~8>0yf퉟V:9`ٓ=uc>ΗOv#B8}xb\~[)跆D oW4_9']ܨdPd`=}{Z"FQz/;5KnURfP` Em m{غ0FfV}: }dH53˴de\Ѿ#n ?N #Zbt8Ur*QV? ☦'60" :%1v: Uל #m}@\Vߤgw /]bzށ0͈'9Ms+45^S=/m1rbĤk}N{o-cKmF Vb󼥡b;&uI=ь>t\" ʹAVN>8na/48@R[h)\;Xz5u8aZ{SP_2r½wytJ{yguE|Le&"ب\ۥ v2kH$!da})xWDk,B*,?T)V,BO'a2l4(@lOXH|X5fHƶbrj@3S6Sp*inͫGh/q*ۂY~uOڑw;6' ͍ls`S4.y3]]wD|UѦKxt.Zho8,Y-]%*f~8gHC\}9BE!l|mHiY.G_gL nF[<'zFҫ7C@h1Q장G9ۇ?mX%$aDzX47+QMD,ԛܓv(k͒Drx^0MAw74qSǟIO_h:Rn.q&rTStF[N/ȿnf""P"ա`G7F0KI1]B49N1a/6(w KLCj լ([M|bi |fvsh8` H_gmS8kD8$i2H{Ck׻&?B;Zƍ.ַʢK^hi~<^BE#ɲv4y#x8RzeXKۚj3)k,}09tH-'hX&/ ;Jf*Rqcjt{Խ7D2!sw nߏӜ1&<[Ѭh)"DE]YwO%4ö$ڦ0.(IgBH=rV(oNj czDvؗ[~.x wri2x2};> nH |!D5[q yn؜%⳼bdH/|,եmjgl;g[;L`Jnj*я 4/4)|ic偗7?mCm S'K/$7fsknis)I]PK {naLסf׫Rv-7ot|[4)x)6lj}|s2?+3/.EKAx[FSY g/9o]KcX5x&Z/ǒ/mYvhE5h,}eʜ}*uX:,/J?IJ x%e؈}qGޖ3#Teyr;*{eL(esԻ'wǧ:j%$FQ. 1T}) -VFI?HtӬ`~kWGģrk ӘkQ,J{yJ@!u1}@ِX{ĜD`EQ:6TMGBʇ9As!B W_~4JMs7~XlLPϚS'nFfNC:WϞВߥw!ByGpXyU^Avum0c6ԆNm:0 Z$}D(4Lxh:.͌r=pZ6h=UhQt^, 52?J+ܭ[\I9xd6fE;6'@ FksGZVk1 wjPݵjQV|$|4!!z,2rxLuıAV)$E}hwKh J٭)j?4*isVc{!)A"L ՑH>Uڌ0Ki?v]sԧ%*Orʬx,jڮ.5+y>9ޱ[zφ?_#RfIhnORf t~.&S 0Og6M&q+0&i#wۑj',vy4J\)WsRH!3s8R0w3ωwoA)Aӧ2_@ޯD_aݐq{Е}zD#U R#A5K]j$x97vM ]#H;g b|& Pmx\9dAGFL-D،|@S-!ifOak?M 1WKtٚj-]:}.9<6 '?z6=~W#y84 w\{^q[ B&S9vWWGS߀+}naVCv<[|D}~0/K?[yxiH| ;"axv< x_\7A[| ~ K:DRMOI\9çv@MhN}64%l{roȕVzU[>+^% 5;S"!xLj0 Ղt QD]ͥNNkvT~^ICUܤV^i Gͳ}UXsb!X*o ̃c"LCEr0x%z(>J;w `}u|:یOWv+̊ezGNw= ^'F9bXX"?g|ۢ ?C c/a;dF SVeK籋^bHGU.5e1(i=ki*Ay>0_m=țXb:Tq(l2Ѝ.W@fպh5C̸OE m}6j]Hmjf(P(ML؎*07X mBkKCJՍ)Axv(<4wMkŠT(a[ÜccȈlBK w下NFgNh-"A(?~Tvg:RU(kG6_rSӳ^V,2@() kì۫Q-OfC@dHmĨ ~Zxsw_A"d?7OK f{Nyb+UMhp'Wc0Y sPUJDw @~+(f}9aQujj`Z̋;qU˿ ^U,ccz J ^"p}ި&Rl,ϳzw6mS"$ѓ.ֱc3 ,Ef@GYVh$P=gǩں#<#_sb%3u[5=d*j[&`Ҿu8d \Y+u; 27/p, (~1IEk˴)X}$&W㞗8MJy,86lj*4JBi`<1TMuD̿ 9^÷3N sh%̌ȜiğL.w(rY>$EڂZ qS36b`Yߐ:GWFew"]}ڪ,zm h6Hgih}cTFs=Գ0Yu#vS B6/+cl|?P `~,+j\ݬ#Hf\PmJe,Q`mv tOk]01IF^>g1dj@F bR~۾RDGxGl)3_͑X! zq «}>pA@)aԡ0P& x w帱onۖԁgYIUX{xEk3R`P8= B S*=ʧ!{HԉfvAo!1li즯g-]Y!mGиoӓ?j^Qب"_|kJ Lih%U`,F<$>ꢢ-gv=6OWlqb$3  _ը#<|mqKTTh6Kقa4EY7 KMM^@`=41K>p~ W)c@O+Iw-kk(y{Ɓ䧃wCEn Rwt&)sR[UqBטӾ;n[oFޓkĨGqiyó&BzKĕeQcFn5Έ1L#t;[e7*M"n-gEޤO=h@,$1WЁf';ҫ֬vՂY~8QlTBX/nٿWL;Y@>Sիе>h/7DI ۣx. & ra|?H>;';䇼[cfkǒRWV$vj}y-|r%xgTR;mmg/62/FD9]",ECf0Я:G]CD4]]^Iホ eSq_Nk\WPHtxe]) wL}9D?hFrM 8ij-RFąGgz@R*!u[^[(a>%0;͌-I[R2u N: x,v~,b~'#͋z?f+mz`6Fs{@X5^,Lnt+J]!GH19\8nH#2P?p{ˏ"ʏ6g.n!O\a%O?Gp,qDy·ԽJ{,?Gv/FYg;5f.`$9bcH e*g0=go@f*N?ׅg*%bUHbMAs:-| %NޡCX3(?\GՄ`)őwoQ( W&6$ĝ,LUAko*@jFLqErJ|~a{π-bo^KnGDwc4?U-ԅMzN_}g:Ɉ-2 4~LS7ZҐgƶ@vM@ӸufVdzP9ZJlN^9yKWiv|h|tI}CtdAHmp?trAXy i!@ T0Oj%&RFȏ cƵ @?Qvb7L&4J:LocoZ9ӓ K4㫉5[p,M Gj|&\{SvTBU[rƍ3f8g%0 )Z T(%lJyb<Rp *CNh%c0{~)/Go}]eٗ􉇿gڪz@Ge<}\ԮD\{ i*3"tL6(6-_J߾PNg%Y ZF[iIy'@q4-vX_\ ]\Du|5&Y-nZ{PJ8Ka|D>>@u)'H>i"v-'2Ʒ/d&/̽&jq4d1790qbr0r .%L(H74EH֥h14B}7z<߿D8wXk'LYj쐋g@,{!ox9*IEqwB#J"u3Ң.6GȒn`oޔX2vuu @ YlEݏ|%xx-4Lv͋(7y ФGEkc͵cѕ~~}K˯,,Zrf %b!KoBztJFCO"ZdLNqAOǁ3c>:o<0,W(zuQR\w z'Z<-[ёkTc#XjxXReu{P YTJOOK y{<2p\v~aL?3uO0PKȮFPK7,]8,res/drawable-xxhdpi-v4/common_info_point.png sb``p < $W |B\\:_I_0yJ_FKmMEZUl#cо@ !ߕ}fڏg<3FoY45s6G;'y3n\|%{4WҘ 4s 0dHR0 _ۙ *O Or9+cD@jJ"#BqLk3"#)A%EFʴ @ L EI3.豦ҊlMg -kjڪ6Jqr%e&f8ܚ,[%Ej]Ñ`@& $Kq!Ñ`@kˬ:MFg@#a3;7/"+ %Gb5[Qr_32u5F]Dəxzxu^:Sg`t|Uf]fX<_t 霩Fϔ~e ZIy S24V%L:0oi2.YL3v2(d8G府M&{@k/R#bHCmM=PmKy'[V{Y'+@vT5O;ʌN3;Bhu flU?ݶJ}W y M3kQ*!K@טdtfP3|h-:?iaȤvז,Ob+ %'ml#VJs}0=Yjϝ*PTO?{/TLdkin뙏n^r=<d&(ʹdCQNr=Ǟ+RPyeWMLzw| M$vW;)9>אc,+fi<7C/7b1H#7r)L(6Q~ɾ> $Ctz|;>]ClUڼ m= ƹ\ӆsuVI JیUUjo= ^dA2b2,"#r}6Su&/s9sz ʼn!@eA84>¨Hq>c]\_).%"4HCHC,\mc-!*$pb~>!uŘF )e1:TIԡr|7cs*ԡd4e {ey>u;Jo77V0@`61D@f:o{g!W2>lMgi5{Neqfrd գÝ!D&֛7+҈!>~?z6IV? C9QYnabZcX[.:=t~ 2/dW!>ةjr4;EUQ.JfY+VB| H*g{)E3mXEFX]NٝO}M>(2[x"/g^H4/U-ʌ8% t '9hrJ/O_4:# ɴxaum3<|&~!(1^K/ͦE#GrTBWQ\%D*5ۛC PEPB}ryi.KaYh {7ץHL!c/d\0Bh8,ڢ5Q ʑagkq-48 2@ (P2˼ScCzc4܃wHs^H~PŘ?GC0"˥K-ՆRlS^ S!D,T 0E_0Cf,ٜ%0@ AboZJ1Hy$AJm.1S7 @Z-cFLb{% ,Hˑo?՗3lPX!), VཆEvC@a% jY!JH\lZk\׊,Np^zzDb#F7@KpnTت:会6>KC0yMݯ{:) ϩ$sU)"2~g?'Iz2jb4j dn#|H?8AZXTU CT`?X&^" p7l\ژeaS:ج$ N ] ef34BKߗzn(La 7|# a<` g_B{jzn $@8h<C@G͇S{V}jVhP ~h 1@35Dv4Bޙeakߺڧe p}MlcTbH G-i(L]흁#IW'|;80dTa(D>Lp+zizl$%談C1@IoHC?&vSq.znn0\;G~mg@CPJWzzU w[0f—[ ,JctN1( E.4lՈ Psz$wz"P{&7"5wJzCa嘆~/q#&3D/>yiqDmnf(v |. >d%Jr=.q5v "λڝ Ե:w}fGD G\ǦHW4mps`KY9Ox\{vS@D>1O=|'&!bc-<<M/CVIbO5s{J||f"E ᩻oT8ٻ#@),M`SB9՞^S{ AI"Sԙƚ9d`@lP򪔓h^!"`K3@wve9ztd-^]j]UYbn[{;I4&zxĉT4)*x.mIjM$z,5Y+*W z;=!q8Ӑ=9-ZgYу]DvGO^=lZ3ݢC]xnzxɈZ7.~"GIcs-2ȡn !+^\cwa^)\~p7ԃv޹hS#Q"<^4ȑn43ɂ'=Rc7?eBz" upLᇏv#y#sۖ% \ z@jye/*Q\|f=C3C&Eւ겶,pn&:m=i>RWyZ(tYSwyhg=tz9__/]:h~S+rU:譓>8gѳbTKwҫ⶯;?mgzΈn"uq2-J ּ8GXvbfQ";xdD⬂פ[uP*Ch: tNۨq*C-_ADeK8F'S{>ĩ[Ƴz5pُ.{m9%68B1al8|z4@J۸5Ը!Cah:2~9UQNz`J="`\[Xm;1 "tz0\%뫳Wm_±7eu`3LZ4 _Jf 0}9Es&=uPCߨ3g@J z3*=&@ Eﵭ(T 58;]~0AD[b~u֖JN]{@yNzz&X5O\x-oC Ƶ/s Y ުm;޻C `jqƃUVlu9>z PU ܢ{/9Ep@6ƺ4R6{qd8-=B$Q2m{]t/F]֦ HA4:|=7&Q~^p K;>]syH 2?sOCK9R9pW7cW 8=Xri`ulVd?P2CG 9OW U ¡_J]p.p❄aTMcL Aӑ~>3r6 !Q˥Ao3 ]Px;Sgd*GD9"F; S?vğt}P`G1a|$MXPh6~"gҦVYWK2bvh:R8vhshު((,2~犄G$_e\Z0>2| eX*z^\%m݅'JQС?ILZ,\}pN6^{^ ϳbm ; U9>\ՙf2 :y@y$><%_SgfEh J::f[U\ZBcqyS-hxuyu-/|qmR.15} 9Mawm ]9m[9SwFt-'zi2MA}Mg{KTf7IG{hʺC􅯿v⟿mq061#݊=:E a_GKM^ r8>YaPҹ~ؓz\{ 4{<]$ Ӯ7NԿ۟rzF~.hV}~ ݩg?Ƥ>O'Re8Ÿ~d6b) (:]G|*ȕ/5qV:y{z Pץd9$~2U ^J f9$4WT a(^33IQ=glpgS@?~~7vR2에&z!9_}/YSS (vz^}/)Ɯ3@H?Hudk@{$^z$>w2Ar,m)_`IENDB`PK7,]8$res/drawable-xxhdpi-v4/logo_play.pngC@PNG  IHDRMS IDATx}w]uۦϤ$R">}ԧ4QGE:PH(! !B'JN2{s'QIr!3srg^`__OtcQn{m6BFw9FsXzQ(to7#=A}>:Tz;zC>t!@9a&(~GoxCҿlz 7t5CrN""POg/6~M0 "YuN[&;V~'=78 'AYr@~@Hi/fd $FAB "O5!d_; MIC!x2ppF\'&i_haTI5BDD@߽/AY7;Qɠs(hXnh`'4W>og&O=)VORxÅWtJ'SA-fMA@C""@$A1|ߟ6w—Xs+|=οDB<-L.en4祏K 8놈8igD|3'Oc9}YqcaܲiZ046'0D${Af9}_\<4+= 鱚9׽++=$jR|螛AD)AĐD +P|6Ny~"n7鱚K\@L*hˇY@Q"1bRC3"jGwSᝎlzfcokP^+zYͲo'B(M/@"Ǽ:BKE{)s{| /6=;j0y|(@Vl&%[vQLD": "!abHQЩ9;]%?i3MRz{A.,EBي>io,mHuX@ `b`1DZPPsFMacSg+DpͮΜ{z9 ?>A"J(Wi}Vrf%E) ` HROKW,zzة'7?Yҳoq @0\uWhֱu⦑x} A+gDMˤ75 z!DRkFLz| WeC `fl-mXU &h bV'P Ը@q0c-[\(p{mNLbz ?c] ]]Elu TT+JKwHA6!NJeex ;varh*|\! Öޜ[lh.O0Xq!`K"zΞW/g3=V/hfrҵTC*wgcfzgtė`&Kmʀ s4VoͰhݲ2\8M?oZp7!HhF-=L4@T̺@b#Ccq X Q<ҖhB7m{upkQ;g'g8=@_VgnRmTѴ)HKLB ց7CA @b1aY@-e&;z},,oA~DZv{#S Xq:1Ǻ$ {@a>&zUn/o; 5ȷ rÒH$ѐM M180WGJD52svn19`é;νpଷaS%8m3An^c(Hzz"Ջ2j2 IuVGcg4BևL%k5)]@]rՒ=voȇ!D%jD2 nF%qf|'v R ]w}osGgncGѲoAnc@^mIy0Z]wѫޥY2_H pqOzT$-AٖX}\"p p癡a`x_>|䮋޾ }+9?LeL!+_.,kD]5D$tdu\($^h)K;g{@ ^k)-Gh|~_lJU-kz?He}5W[;EIG<ݯÖsmj0Ľ~-0ێXh/$Yidw-u@T 26ŷs̛@;fhL]%ItɍjTvU VUODyQd N$YnkD 7S3gj5v4.PŨ82$d^&vMh2wH[A~{+[CZ" ZpBFN]yx"aYK|U;* R7&ĽݯwzW[4VXnxfG1-Dc7w&opC: qW@4-uC"c>42Փ뻲KKDmi>bZ  lť,FW"*\!mxZv8#$v<0%UT=~oǗQɤUWݱ•6(zAȨTUnKRbo^'qןJk<h{zỴ_2G N* Wݹ>y"%Q[n]~ &-RnXϡ 9D^ćDƷ9*aD[@8H7VqdC$wmf-vܒZg:( B@ +]_hHdzA /Ǎ갽X"SH#GF 1 1_XI.d~Þ [G!֥ȨcF(c V q"11.w}\Fte?|)6D'>seݮk}+^BZ;mn׆WԆ] g\Wd:L+lH N'y.y3qKeTv`šENZ>6g%ubzeNFϿդD6u2MpFq- /S;X\][&0{79ҩy - -z<iE u{*b$h1 yӓK1^Y1ڿݤt#gDžLhW{꣋0~|&PZi&7fU2)v5+Qa[-ek͏$(FHAљ]ۋC=(N2K>^CSJ %TD/(|gkՆ'VeCDLf9^[l2WH&t+VimG0A]$`;]'$w+V<աRcf?x :p3^7}C}{I`nOuF^ۓJEHl`@bvHBP9j",>4f4ЋJz=s1ع 1 ŪEDP6u y^'I<˾̮Qw~tLKhIp6L ̙fwѹ+Ǜ]3+/5hlH{ Հ{"-aH񔏀٨ #¦h,&Xv6ıv%~f5QMDfـ7gS畐mjd g,zw(| F׽\ n zm,=zlUv`>l1Y$܁Bx^|p^&YF+c bFf˔S{`]A@4d YG2ܻ]2*Diah(QNT)"\>k9vTƆZ׌q4 ܭo+Bԁٮ[!&60e̜j+^|/KKX(wr#. l/>O"qfQ@,Yn̓|oB( wdsK1s{f^i߿ܧ =b C31aʫo9绘jbE D3D*B!k_J}Hʺ7bm!!*%E~г*Ƴo6JEtSa])w\ o\ؓЅ)Y⏙}7_I0?q] DwׅBtN/2To{?Hz.ٺ0OSz$M؆=,@P$pIQT1r$rZ?kX* u!hyv̑![EPx@ǍgW>KTU_~?L%2Gx|7,ծ"LH1N' {&ݨ$ضMvgu${c$WA(OSEwUU3UX&%,k*]=č' B}oIt+S*2evIb #{XMpԖntRK)I ;1&SWx? 4& QMߪ~"I]] ?r4?.T,!z5o0+FiG{-7PLO(!5*HK+"W^zcxp&q?@LhY8\R\ȺTm˳ﺵp v$g"? O3gtJp!Jbxˆzdcl$H 6N([ yz Ln x_ ŭUDJʽ]*rqŏxzn: <V/)m7PT=b{b]X]4$!eF Q7PPGal|߭w#h&Z^L A`V ^DJx*] ve tz6 ?6c%3Q x{AT E5 M#mz!JORkps݅2%fn{?mJv̫.KYlDH'tW9@ᇗs_b:6U-fGԮ[ҲoR/0R͗OLbQeVsMf".wJMur@"T",!3o,0SC2׹wuPQD~.=>^AȀL 2*Ew?(<iȡ͎Qri]S%zsO5? :y/~X'L@170 HI'N]ݷG-70,OhoBptm/Ӊ)8|G & T I7Ȼs;_APZxZ3>=|'  9/2(7I>NI{h6 Ǖ ]A~N[F'꬚Q{aKy! %ep%*LtjQ~{o? B߭ nzU:x(- 7$ Р$4x<*0x*Hv@.Iw f:@UCRɻĈv#9j1VX% + `nNۻt51yȝ5x|U$HyM-$h , mT䂙}j_8ٱ{POU29DJ**\LHXol$0$;(F0. v/8 —ぞ5gX2e[yɏPv{m{xNyIWS1Mw@ @tWaE@ ~Uw=ju"7B4tU*) &eT+&frGxHnbe;V>@>bִ]0]Be ωR珻x.G|Np_GhMU2QMQ pF)H Pu.Hv25 X,N\3aN13RD eozY<jx{wrb6("2!Atq;/M|{tBZ=U!"!,cCypRk]ѕCöt>h0@3 Y[dje}Xg^0e_8[=5IRbZs$wewjaMʚ ubi&u6P;#P`b{ϱs ]5Ͻߓ{I|vSA\P A`z@b7G^'vx;P K^|!~d-pfm;7d6=VW?@T]wyH!|z܎$$D̶Hږ$5h 9 "R|~^g"cw$~fYWv~/(!Vu^^WZ ވh9 ̈瘞6gu@Sע:hbol*Zc#i&$GkX.diREomv1C(Hb'͕/< L)E.GxBŐ7 -lo{9nI}_}+JA"SH!-GnBWU *~vY4``ivPVF =~t:x=V*^ŏjTu>KvZRVs":1ɝ5zlyS˃+zt\< x!\8.{o@q7:O3+F,Z݊!IuQa}[$7QkŌOfk0skS=(:>]M[) NARPNaF%"?eU1n+ci UqePJǣiݔ.D^}d1jJ3.؍ER‡mo,a,4T~R,,].,ׯ[唣A6yaÞ8~FYWruDԚglSs{FD1Owc~ &|=( =(S9#7sE`8ivfA޻g{F|>3]JB@7Z8-? 맾fol2l$- &(ĴtZ~-" +Th$;m^CdEGA_ YS+~/x`w& "!,8%"ZN߄2i٪x}Hhm>aC܇O'RK)e#wŬſ(mClԓɴ}򔎣no.!䡌Ʌ;U.kC }l=`\y)ݻ $%ӹL 3NEj兩 uOH .aB,=Ei;R=W߱Q]&zj-Ωně+3Vn.[:ѤAvEo!o˨E 5(k85& iIw eold_`Ahl'*gq  (+] XZDTpsɾKр6r*M>3 +S!g&bAZi,V̈z Nzq 3^^YqO|֊1^mEEs5k dyQ[ \%JDqk*-8[:X=`9zb7i^oJ6 |nA+DpnZ8*jW*g<}tYѶZ xe o*Gݯ/(j9 J%R H{N0Țќ4@haف>]R.T"&OGkĴ^)+z0%ݨ̍u<Ȍ8a UZ~}_}# WةxRD Y^[5'5HINOn˽$XQرI{ ?Hj_^ۛd%`ό8]yJd{hQJʮ#EԷh#=f}o~)ave3]^r-' mlP+Һ'\`o;YAp lh?}eiGoO,#mR%,UG#~?.&iscpЂG{bxFUb}t(׺ŹM5-Z{r-";`,ˮ=JvAŃ63{U iPuW`^34m1S"uNPYWP8>tUpzn wBu]?gLihBbFLXp1'΂-H ޹j[ǼT3r#3. ) "@gM^<:_ss4,d+L3Ƹ H1Ѭ=nPo]5̈W)^G; +=6{ި.ڛPb "C4]&v Y\iEJ90ym逶\툄j$2]LBk4 (DžZԣ$<]u9]&gGi ]o :Ou1~"24yB ՋmymL}7f`6[vNS1Q"A"C/,DčUGo^$om / ^> ėBF%CeV"`" sᗟDi g=8''f39E 3Ҧ9u ׇqVx B!csԏ ƪö*vQ4.:Ú`;,ūd,)s*~:!-^1 "w{:9滯@f(C>ДB\Y{z?YYωMb7Ǿoj9zMً$B̼,i[5҄b&}['?* @a^$xj :~WZx`?ަP-A!WH FBe+ Nڗ4),R DqEhf2NW!~OHKz'!ݻ|1OV7T,Tg)B=@F DR> >:lhY<GG R&[fjPB8UvRҶ*٪w3ۏl5Z'Ef/ *Ihs'%MA}uL7H"# JI/Ֆ2Az .C..t|}N`M+ma4PwGpdC~sinR- kdM> JUgZcN\ =$9Χ*(u9¡g '_ː/udhFzO|Ɏ=VA6Z\8٨aE,6YI  Vގba(p;5٭u?фLtqZ㫲XB^ukI4YEI֞?Ŷ\IhbUS}M[v{ɚЙ0EG"TibmXF_b.%/ +ok']Xŷ&F*SAYC4N nך.s: p3:@^ zrq?WTDCڶۋoTQ <(EIg=>К0:YGqNHq|N$7!K#OG k턅SiTo}BHHb\2;Ո3Չ );0v|>!SV[\asɮBIfz8.JNRAO<"+]O2, n?w#,:lT>KC LC)iYFU?_0E/Kٰ͛ ٮ|0!V,"rV⠇{M! : ! (Ğz4]fD\EmOHJQG>Ej4Ýnh쫐~@s)O2lB ꫢݟ}GQ+\Dz6| JcLq´ޛGI bPE J-'mO{.@f܉ S|e "Ү_]~ة*Tx:I6>pAh,s㍒Ʒs(;[Nz1>`l1uIS =I[C3~]&VOg$TTMD tWH:!mw-MWn5Vpoo [h,]WMnDZz1h6 (RP-ß{mɮOͪ# J4mzG?a2GyΆ@`H. 5Ѹ;N:Vym0SuI_<BCä'ׯS)S;deWJtW^|gفNPMul:Lsa(ކ5 A RF[#t\UP$"5>gUϩ%9^a  !(XNh0QQ:hIq} SKM7 E/1;AP, (BLƪR>+1MK] y^zm/>6&C}ఙ]Q1c$V:%zz՝C^o|~ =7 @Dzj Y,)YzYa HR.+63K M{- wH+{oy}J|q0u"yH<oxNz;> y @8RYW)[zw=H]?qk}9V%I 9m1VL3PJ:*ŌzAOR ElAtZ@1ut{HTOvQnxB(8o.hPܦSѾަuI|%t0bitL>u=2qNBkp"ŧb7m-b0GIH<"Zn,{".D^"=}˕iLδN+C}ظtmS|k&ŪP013ρ}77 '|ɫ1#$Uz"s S_鿥Ѹ2ۅ'H/~ζvVlfc b×&Sw/*w̷ъ:4wNϦdSX7+[Q2O-Y8)l[wCT-N=\&t*VL*v_R{86 "46;`M3}GaӁ 0blk$A\@D9ԿaLi.Vd٭%u(HNfNrrHܬa^ 04gKp%G%So--v֪5=k=[.`Q(W `V!X(+hf۱4{ *6؁ccy'D+fT}YW~+kBaB$RSa^'[\3:UrK[pdlŴg/ΛDd<9xM*"iW kJP:>mAK"+=I$|6vłHfW^-"ҳerY3,9gѐ%e3)\V*2v]-os86 Z{k)]~URDs89[@>nq܄3ӳfrk4!!imEmC#z}PaN4yM@NwO>|Q0KSs,Ხ"wGuyRg!w"4F U܃7}|Nվ3[4v qf{i`Tyk8,K7BOSaly .q 7}Kg6=8Wh!anmpKaeuQCA8R !<E"w~$zӬ+ +Us+g2[JuHkQNXLm6q#r=eOd̙=0wbONdz _e'ADTM%믘Q>m*2=˶H|#T 0"~o^5ig䫮$}UeKbeR:7;on7}\¸WJ_?xt$$_($$pcWKעz aGpܳ7~%"BWo6'_L1wzڃhxB IےRƃ^-2i\07Y4OW."83n+à-o3 DML툈7s \LSE;_.y x,+D2\Qko[x% j@(Ahm8RRxT;&(^fڤu'хו_@Ţ_@p!RH(mjH X"THAT).tJQE 5Ҥ)6љ8ﺘNIQpf~-\\97S0w%}F|j:g^jkE z腕n3.];|`Ziyznv`&:dUkciԸr!GĬH\k~YO,]DflX']mޝLwtCVx%'JU߻^ ~=l~xVyeXDF103-jJ ssgJ[_-ۤӲ=_ Vp5Ek~*́B (n (ilVIvGHBT(J UH 8Zgc0Jƙdz=mji&AKhS!:׬תdڎ҄Ƚݱֻobܠ؉I =մ4%ژ]6MIENDB`PK-_OUOPK7,]8$res/drawable-xxhdpi-v4/progess_0.png sb``p @ $ÔqOS@%A~  0aC 200 {8D~;fW֝\o]n3܄F7C]wp/ʢ*i[ *u2uR(J ڪ|ƶ%i`Ҍ&i >Tumo+Z4.j a<}Nvr6SE%׳ќ6Ticrʶlo/yvor}KwOyWM7%^_q)r}ɳw.ݷ:ow ڴ kT69dm#>Fcx334J 6__mww>!I~w8 3,AB  p (1$ˎ\GxX/ܸj\VO`^TyNi[}Xu̫V\VJ |yiYm=mOCNի{;ӿDիnDg\{J'[3듆oTn!ss<3- bOW?uN MPKofw7PK7,]8$res/drawable-xxhdpi-v4/progess_2.png sb``p @ $7]] |B\ pj, f0phXp 6Pp[wUZ-cFũע< yes_qqY'cC\o_;n俶[wy0kgoשi}5;fQ/fŸl}nK. }_*{z[~Y̶lV[?tzg\6nZmn5E^mլkmw? O.}฿Tu3+jr88 MO<=s{D9ZI{mg~QIм,ɟ\~"{u ߪ{ឣhQ~myrMiʰ6i+SKfgVNLʑ}ѧ7+=syyXN5y?Q-h,}OJ:NI=~6Zb6ZBr?OJ9)w'jz˓). '{wgXz*3.bc;7)t \]=1) PKbSoPK7,]8$res/drawable-xxhdpi-v4/progess_3.png sb``p @ $\R>!IO^ 3(8$,  4lRub``tq 8|72z[ϔ.W]7a?<9ǭEW&֚m9EVՂ;^TtHhG~]ݦ:!i,d9'޳;Wˢ+N9/q:gF“n<;;e~QSSGϸƸ`f ?=6?\be{"wx`]ʹYq q#W;#)ړ^IKZ$~: S*OZ2?>q%S}Q3ä75n(7(hƽw*3 ɷsܾ<}Vr /};מx\y߃՞ /j4˖eCX5[,7S\mzVV osKdږi.ѓ;5i~wڙwSC79.ST20ɛ[2Lyƿo Ny?]|2>=;{ =fx.NT/{Oy+~_w,=6;Ώ_gGW.~Q\8#/ܧuv\[vh/w?,z>!Cym{qHգ#'bpl3ڶ^늮^.Ca;ӕ󜃻e{~Mo/)N&/L ~.PKʀOPK7,]8$res/drawable-xxhdpi-v4/progess_4.png sb``p Ҏ@ $R>!I~w8 36"~Eo㯟`7x- 0N<]\9%4PKm2ѿPK7,]8$res/drawable-xxhdpi-v4/progess_5.png sb``p Ҏ@ $R>!IO^ 3† 8[azZ㪬#5 kT'&F^yבƃ-|ߵ9u_qձ\lҶ7eG, ŵXx&f]Gmϯ;SeCT܊ )omͬYmnկV~jo}hˏߧDߢ--{οĕ+hڜ97dM7)m,bq|QZ]iЬm!lٹ9vwlֳ5bg_П8kOՁ_x?-'߳?om,kmy0xu߄Eo߻>eP"01xsJhPK PK7,]8$res/drawable-xxhdpi-v4/progess_6.png sb``p .@ $/R>!IO^ 3(8` .l3``` tq 8vIwx\Rq JH৳Umb?௭o7'1HIؠpAg z'X'((c}&abpe.v4忾dm= g2 n+Xt\z?JZ3>6{ٮ}N -흸&m B6h}/)puoC'b皤si`·Id#7Ellbbwfslu`HTjNR+3,:^JlwZ||E+]Қ_ ]|9Ypvв+Ev-<.8)?/O?A?vI VXQ9_O bGMa=Hu䐀o;<MYnSuWeE7f䝨pj|襒E.WXj)ҳ5U}ޤ3[5kE|)@yS]f/bxT:}. meǽmj,uO$ȹ\zx^\t1e&Atyztr.οg0Ԉ`q L} ~.PKU(PK7,]8$res/drawable-xxhdpi-v4/progess_7.png sb``p 6@ $7)/܁K $8WxP Å I100x8D>zݓŀ%0]q|eO۾)G7s)?SqH!s憂f5͹t݆_~lZW:u ZqΩ* Sm,LDZ>*aX3kjE#%ujOJ[Ux伵+]>)<镵sJ/t<+; 6^|jquP0gV2.k2x:W˛63o)gHZo-f-=K{q])6" Zj/瘳if0~~2ٺH&:&PKPK7,]8$res/drawable-xxhdpi-v4/progess_8.png sb``p @ $\R>!IO^ 3(84$l&D7000tq 8|4f9z;5s2v>w۵5z/bΎuD/9&Izꇣ86Yد`~[DX]>5RMf{cz&LsYç,VIaafٜY)&߰3P;޺ij;_.(M=}һ- 'Xmfs(bKaW6Fe20&3E`aߊ;fj-ZЧwTȉ:C?,|E'?t wo6:8WyS%xQȜT Y}}x5ɾ+G4?/n8<}fBCV.2>zy"7m=竹cEsWN`ݽ/1#~So) FYZ5Bע^̗{#Bgi$סF?6lӎ2-s/uΘ;koǒ\oj !O y6~X8lOՙ+3<|Y?',oP+\mLߣ2Qi*K͎-;~DQ<8+in>|vHlWE =O9O~tiBqU޿(c)B/ڞ/5}7hۉ^]?"]KJt8U>ܺk̗M8iNvـeSBPK5rDisPK7,]8$res/drawable-xxhdpi-v4/progess_9.png sb``p Ү@ $m€qOS@%A~ ?0!a Sy100-tq 8|6ҽO_ӥ| $eݶLsԡxksz35tDkgcbYh:9MIȖTg4o=Lʟ7j(I)(}Wy&RfnUgp|UAb /ۯ\~쿲޾]UWT*}XV!1unF{cvs?|W}zPQ ȗ9?I(_곞7m3s?dğ Ompp,T{/ؕZW}qM։k(b=D\1%<}KuLNKN>}3c/݋oQ tȬ)E쑰ϩצњ{u,n;s7KMZT]Wp9[9FOvjpYrn<?1"{c,*"ҷ̛U| 7|tZ̮`_/nHЈ5Zz]?/&;u2nǹo!I~w8 3+ j3:u1D|mP K\%H^S#;뎸(Qe,8}^(߷BgtZ>K\vJqo}oGqqcgVO[أg`!\bkܪZk^"(<>NA/h4zbZejV_ .C*LTBxffM9^_¯s{' Qi}1k~DM5e^a߳sᬢx-=^1o#7IFe?PK7gn0PK7,]8res/drawable/btn_bg_last.xmleN1#5H@TT9DQS((w%/)x풊c~ ۘ'B>O[اg!4ܰfi4ܩZiި"(<>X|ӗK1 ORv2LVZz/~w^ʡUTCxffM9^_¯s{ 1Pc\GGM5e^slBZ{cްaGo ({ePK(0PK7,]8res/drawable/btn_install.xmlKJAE|& GKp$?`8m݃pCzҝ˭թL$@_8 \1_Tt3Te'0-j\qB=*:d}UkirNga- ˍn  RF]XW}]ffvH}臁ͽ;,y4tjOR{]ilGsZjfdH1#2nma_/5z;z;PKPK7,]8res/drawable/btn_play.xmlKJAE|&B GKp$?`8m݃pCzҝ˭թL$@_8 \1_TR =%g\)HO`[ԸJ&z\UtB^9oESR?$[}ss^&d|x^ H}臁ͽ;,y4tjOR{]ilWsZjfdH1#2nma_/5z;z;PKiPK7,]8!res/layout/activity_installer.xmlVoEV X#B p*la=z7]oKCĩR9UZ!qPx~YKH'z7?<+׫Y$;1Sb쀥^րG` __?ɐY/[ n/_0hUbqmvRy${ UiQAtq.L\ϟѮYe>o~[;C/mp gD ܍V۞Aߕc^x/CW]8p8cpyC³ŷ|!7u1rRϟ$gOtHNϵr-ˠ+ xCx:,7*jM֗{@Se~r 9~iYm/e*#ʋEs_HYy'CR#լp#W%WS:d}d;i8Kͻx~gN%spF}^֩ \E}jGLUej5¿hA!fX[[inQ-N2o|3ua]S=O,sC.SjER--g"6gaԐkK AEO]ǬsF|dj٣_͜{mÖM a-C-Uc0/G{N掶2/PK Y4< PK7,]8res/layout/common_header.xmluJ1Eo:t Q`ޅ NT]!q޶) pxy&؆jX#)9"rA c>hZ7꾸uS9$vd:_rvhfS879aK5jxafέQ| !>Ȍ|/.{ yM#|,5QힺΊ٭ -! PK:VPK7,]8res/layout/common_info_item.xmleAo0?7 M֬ H.S8L%I\Rv\.$ę_n!p^z䵿~xХG> 5]p<S T '>@RTk*R:= 嚂@>oz2KSqi4j^4U,ϋyΡ7+55 v]V3ϩUZ+qpk=$Y.JDC'6避ȼC?qtSn{hq1G3c49#:^jl·=}dpc;ש~a#t?\&vnjl8yw[}W9KwF;w:z-PK&PK7,]8res/layout/install_overview.xml=o@;oNb@+X@LUՈ c$$Z$DlWΕ,eB|663s?#;#2Pիu^߽8W"fu".{"΢y:t چz^CoG:V0:tڄBɇh8%3J]S;y|r|z9||9||%5s|Y?[9|N/ˑ+$W3w[껙{=PKhx PK7,]8res/menu/init.xml]J@͏I Rܸ"ي++#$ 52WX>F'ѓ:CCq3Hq@pO38ѻI1$$#DhMBJI7ım|uoKIRt\/k}Yۼr܇(jUXr!)֬| D ˣ_*rwߌA /8e Ge{ٓfyoY=G91?;tOGPK8]FPK7,]8azU??resources.arsc ? z=_>~BqCj-T{6v 3>Iq(7@I[+ = g  $ B l  7 T #;H8.j}GL^!0G[r'w3=Ye8res/menu/init.xml&&res/drawable/app_status_processing.xmlres/layout/install_overview.xmlres/layout/common_info_item.xmlres/layout/common_header.xml!!res/layout/activity_installer.xmlres/drawable/btn_play.xmlres/drawable/btn_install.xmlres/drawable/btn_bg_first.xmlres/drawable/btn_bg_last.xml==res/drawable-xxhdpi-v4/btn_bg_dialog_last_pressed_light.9.png<>res/drawable-xxhdpi-v4/btn_bg_dialog_first_pressed_light.9.png==res/drawable-xxhdpi-v4/btn_bg_dialog_first_normal_light.9.png22res/drawable-xxhdpi-v4/app_status_processing_2.png22res/drawable-xxhdpi-v4/app_status_processing_1.png**res/drawable-xxhdpi-v4/app_status_done.png #下载被删除了, 请重新下载SettingsggConnect Download Manager failed, please check the Download Manager state in the Application Management. 基础服务%1$sMB Google 服务框架%1$sKB Google 账户管理程序%1$sBGoogle Play 服务..Storage unavailable, please check the SD card.%Google 日历同步服务(必须)00Introducing version %1$s Delta update size: %2$s(Google 通讯录同步服务(必须)##Introducing version %1$s Size: %2$s 商店服务 %1$s update!Google Play 商店 一键安装重试继续网络不给力e注意: 务必在弹出的界面中点击确认按钮@安装未完成,存储空间不足,请清理系统后再尝试谷歌安装器 '安装包校验失败,请重新下载 !我确认可以正常访问谷歌 '安装包解析失败,请重新下载访问谷歌相关应用前, 请自行确认当前网络的联通性。由于众所周知的原因, 若不能正常访问谷歌相关网页, 则您也不能正常使用谷歌相关应用。因此, 请务必检查好网络。 友情提醒!恭喜! Google Play 安装成功! 下载未完成,出现错误正在安装 %1$s/%2$s:   '安装出错,请点击重新试一试=Y请在弹出的界面中点击确认按钮'安装成功啦, 点击启动GooglePlay %即将依次为您安装以下应用: 安装出错, 点击重试 安装成功, 点击启动d
    由于众所周知的原因, 不仅谷歌相关服务在国内不能正常访问, 在国内生产的手机中安装谷歌应用也是一件非常复杂和困难的事情。
    本应用通过极简的流程, 帮助广大谷歌应用爱好者, 快速在手机中安装谷歌相关基础服务框架 (这些东西是安装任何谷歌服务的基础, 在没有基础服务框架的情况下, 安装任何谷歌应用, 在打开时都会报错) 以及Google Play应用商店
    通过Google Play商店, 您可以自由下载和安装任意谷歌应用。
#特别提醒
    本程序可以直接完成下载和安装谷歌应用商店的操作, 但由于众所周知的原因, 访问谷歌应用商店的前提是,
您需要确保所处的网络环境可以正常连接到Google。
 安装出错 安装中... 正在安装: Установка:Ошибка установки Установка…)LУспешно установлено. Нажмите для запуска.M
    В некоторых странах запрещено использование приложений и сервисов Google.
    Данное приложение поможет быстро и легко установить основные приложения Google (только важные сервисы для нормальной работы остальных приложений) и Google Play Store.
    В Google Play Store Вы можете бесплатно скачивать и устанавливать любые приложения Google.
# Особое примечание
    Данная процедура может только скачать установить необходимые файлы. Вы должны убедиться,
что Ваша сеть может подключиться к сервисам Google.
,HУстановлено. Нажмите для запуска Google Play/YВы собираетесь установить следующие приложения:&FОшибка установки. Нажмите для повтора.KbВ всплывающем окненажмите OKОшибка загрузкиУстановка %1$s/%2$s: )LЗагруженный файл удалён. Скачайте заново.$9Поздравляем! Google Play установлен!Google Installer Примечание,RСинтаксическая ошибка. Скачайте файл заново.Перед началом убедитесь, что есть активное подключение к сети.Рекомендуется использовать Wi-Fi подключение.2]Нарушена целостность пакета. Скачайте файл заново.OKНет сетиПримечание: пожалуйста, нажмите в всплывающем окне кнопку OK Повтор ПродолжитьGoogle Play Store УстановитьGoogle Store ServiceОбновление %1$s,MСинхронизация контактов Google (обязательно)/Новая версия %1$s Размер: %2$s,MСинхронизация календаря Google (обязательно))DНовая версия %1$s Размер обновления: %2$sGoogle Play Services&DПамять недоступна. Проверьте SD-карту.Google Login Service%1$s БGoogle Services Framework %1$s КББазовые сервисы %1$s МБ НастройкиLОшибка приложения загрузки. Проверьте его состояние в настройках приложений.2]Недостаточно места. Освободите память и повторите. com.ericxiang.googleinstaller  c @#+4<Cattrdrawablelayoutcolordimenstringstylemenuid4 c*D^ .AThz+<Qat .CXeqy*<Vs (>Sg|.Fm|)8JWgzapp_status_doneapp_status_processingapp_status_processing_1app_status_processing_2 btn_bg_dialog_first_normal_light!!btn_bg_dialog_first_pressed_lightbtn_bg_dialog_last_normal_light btn_bg_dialog_last_pressed_light btn_bg_first btn_bg_last btn_installbtn_play button_donebutton_install_nbutton_install_pbutton_installingbutton_play_t_nbutton_play_t_pcommon_info_pointicon logo_play progess_0 progess_1 progess_2 progess_3 progess_4 progess_5 progess_6 progess_7 progess_8 progess_9progess_suffixactivity_installer common_headercommon_info_iteminstall_overviewtext_color_resumeactivity_horizontal_margintext_size_normalapp_nameaction_settingsheader_base_service item_service item_loginitem_play_serviceitem_calendar_syncitem_contacts_sync header_app item_playretryinstall_fail_networkinstall_fail_storage_not_enoughinstall_fail_hash_failinstall_fail_parse_failinstall_fail_deleteinstall_download_failinstall_fail_defaultinstall_successnotification_title_failnotification_title_success dialog_titleinstalling_app installingintro install_hint1 install_hint2complete_count1install_complete_hintstart_play_dialog_titlestart_play_dialog_messagestart_play_btn_textclick_install_hintstate_text_resumestate_text_preparexiaomi_update_dialog_titlexiaomi_update_dialog_message!!xiaomi_update_dialog_message_diff##xiaomi_external_storage_unavailablexiaomi_bytes_unitxiaomi_kilobytes_unitxiaomi_megabytes_unit$$xiaomi_connect_download_manager_fail AppBaseThemeAppThemeTextAppearanceTextAppearance.CommonInfoItemTextAppearance.Hintinitintro_containercomplete_count btn_actionprogress_container progress_ten progress_bitprogress_suffix click_hint text_app_nameimage_app_status install_hint @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@L 8 0@    L| 8 0@P`p 0@P`p$#"!        @@@@L\8 0 !"#@L`P8$@@LtT8%&LdT8 4%@+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@L+8 0@P`p 0@P`p 0@P`p'=(&)(**+,,.-0.2/406182:3<4>5@6%7D8F9H:J;K<M=O>N?L@IAGBECCDBEAF?G;H9I7J5K3L1M/N-O+P)Q'L+8ru 0@P`p 0@P`p 0@P`p'](w)u*s+q,o-m.k/i0g1e2c3y4a5_6[7Y8W9U:W;S<Q=P>R?T@VAXBZC\D^E`FbGdHfIhJjKlLnMpNrOtPvQx$@@@@@L`8DTpR SV TU V wLp`8 RnLp`8RL`8SV @L`P8Wl @@@@@@@@@@@@@@@@@@@@@@@L 8 0@P`p 0@P`X?YZ[\]^_`a#b)*+,-./0(PK7,]8META-INF/MANIFEST.MFWIJw>F7 1QQQ\^Rl=K۞+YI_~ |\= }}/= 3y8<+Au/_V >>&I|© O,qܥɗbL=Max;#R'1]UX^֏uçAd):[F9G5UˡIgv;(slAnfqb]U:y{|$\0_uۑBV\",MopQ=LۜJ9+qsfIE S,FV8,zee .Ьnu!HSރvFz4wH,'@nUos S5;؈&&itvm0̀)}α΃v[4)p//=d9謎tB(|Gd@8u~{ze:P\rB٣vHup6oӒt5DM 'P< CzIn'JH[~{AÒyĖ9q8vJc`fStЩk&m=dt-}բdS2|j~hINyTCf,2wDSx+Q}#[NKxW( /ljg֗LI)H)U(؞-dy*J.o JFţ,zb#;Y~6S^&w٘l,#K]poוּ=i3dma@z].#+?$y/,Wp+g+tBYJw2Ey~MkT k\FXbgGHz+.IJlDjs]j0˿xLd(Q+:/,Q ?QzȾmIvM9]2E1j<*3n2g8n ;VХʕJ)VCt572q%aqkW)&Jl`u6.MGbD8:=0"uf{# 'ñ* WS1k4QKSw?ԠDB2nم7{ժZz4,mc/CIcZ ՁC_Z~qtaׁǘhr)3}yVˋ3+,jq'??PK_"xPK7,]8META-INF/CERT.SFWYH|F78\Q@qׯ_犝ey# /+UZuSgOOV 凧֩[f}Rn>3U|R>=YTNflI8bo.̿VUpo1eOKz}TKXq8H0Ŋ`Y/}B,of?2SF~`M]gR0A/pZcn\1l9tf+F,Ip\PNpe@ϋta 1LeXZ\2T7t)3t< @]<JIcK7@gV~bitZ]r`VUm-B3`C.5i/YJW VuS9w0'>u+ )44)%mwKg~92suU[lh,ݴ- l!&Ȧ1uuzٰuYEIQa?ӽՓ2 yPmc@tos5L$LE?<ɹƒ MˮX0YF/9<1qխ0=9`zW4CKZ y`䠓;OƓ` Jxp*(1K9PGZl"`Eˢe']U!g%4gb􂚏O8^ɮ/}S(Qf+'eMyWJFMFR{M@f?GGƍyv|jw7̉cs%[$a@ qn~V-=r:侕E^X g//#kLp*VncHjٻy( i:l4~$D&Z{A uYBsCNu ac )|J 'n2zjaun_H cXU%Di%KYXoJgj+BUE>(7*]dB]z֬'}?US H6|F2'LNݞt#`5ec͔O*=:JW$J^̽Z[^z'CH< \Z'{GyO$\ԑăKIMeT4ngqV 霱'dk.Q4'BVʒhɆXz\b.4f*aC,.&1̦daҬ>*HmR%yJ΁]|iZ]m5TImmS,^p< axG;σ7 bF唍g+5 <>zkc>J_!֊C,|$v}Um`Я>}O%9@7AtwYƑϖMyS˂%i'ܰ6J7O:1B?3/+)rT-ߘ-M [8lצYN>e<{K%m(6?Q_#S9Ğ5'7\:bn,e\륚L0pVKH* /װ0ji"H.jngibdhg{u ^Q2_ӞPfݔ]?x'u$+}R-?y-2鴆4g;xd>vڎ鏛 .qn<[\1 j.l]}svHJp^VthęӭFtL?vQ3aXM.\2YfQ=}dz?ݔƩ2_F:}#?vX/9!rr.6\kNՓ]j+.񢨛x䊫RPK}3PK7,]8rbAndroidManifest.xmlPK7,]8& @r classes.dexPK7,]8mistats_versionPK7,]8n`*res/drawable-xxhdpi-v4/app_status_done.pngPK7,]8 b#2Fres/drawable-xxhdpi-v4/app_status_processing_1.pngPK7,]8\gv2dres/drawable-xxhdpi-v4/app_status_processing_2.pngPK7,]8SqIv=:res/drawable-xxhdpi-v4/btn_bg_dialog_first_normal_light.9.pngPK7,]8ճO>s%res/drawable-xxhdpi-v4/btn_bg_dialog_first_pressed_light.9.pngPK7,]8<-res/drawable-xxhdpi-v4/btn_bg_dialog_last_normal_light.9.pngPK7,]811=6res/drawable-xxhdpi-v4/btn_bg_dialog_last_pressed_light.9.pngPK7,]83&|&&>res/drawable-xxhdpi-v4/button_done.pngPK7,]8Jxp+$eres/drawable-xxhdpi-v4/button_install_n.pngPK7,]8+tres/drawable-xxhdpi-v4/button_install_p.pngPK7,]8>o,τres/drawable-xxhdpi-v4/button_installing.pngPK7,]8nTuF*res/drawable-xxhdpi-v4/button_play_t_n.pngPK7,]8ȮF*ܭres/drawable-xxhdpi-v4/button_play_t_p.pngPK7,]8"I, res/drawable-xxhdpi-v4/common_info_point.pngPK7,]8_res/drawable-xxhdpi-v4/icon.pngPK7,]8-_OUO$res/drawable-xxhdpi-v4/logo_play.pngPK7,]8$0res/drawable-xxhdpi-v4/progess_0.pngPK7,]8ofw7$3res/drawable-xxhdpi-v4/progess_1.pngPK7,]8bSo$4res/drawable-xxhdpi-v4/progess_2.pngPK7,]8ʀO$7res/drawable-xxhdpi-v4/progess_3.pngPK7,]8m2ѿ$:res/drawable-xxhdpi-v4/progess_4.pngPK7,]8 $ =res/drawable-xxhdpi-v4/progess_5.pngPK7,]8U($@res/drawable-xxhdpi-v4/progess_6.pngPK7,]8$=Cres/drawable-xxhdpi-v4/progess_7.pngPK7,]85rDis$%Eres/drawable-xxhdpi-v4/progess_8.pngPK7,]89ԏ"8$Hres/drawable-xxhdpi-v4/progess_9.pngPK7,]8x)TLres/drawable-xxhdpi-v4/progess_suffix.pngPK7,]8:Ot&#Nres/drawable/app_status_processing.xmlPK7,]87gn0vOres/drawable/btn_bg_first.xmlPK7,]8(0Pres/drawable/btn_bg_last.xmlPK7,]8Qres/drawable/btn_install.xmlPK7,]8iYSres/drawable/btn_play.xmlPK7,]8 Y4< !Tres/layout/activity_installer.xmlPK7,]8:VXres/layout/common_header.xmlPK7,]8&Zres/layout/common_info_item.xmlPK7,]8hx A\res/layout/install_overview.xmlPK7,]88]F_res/menu/init.xmlPK7,]8azU??T`resources.arscPK7,]8_"xMETA-INF/MANIFEST.MFPK7,]8Z¥META-INF/CERT.SFPK7,]8}3META-INF/CERT.RSAPK,,Rzip4j-2.2.8/src/test/resources/test-archives/entry_with_checksum_mismatch.zip000066400000000000000000000006171357650161600276310ustar00rootroot00000000000000PKUDO6 sample_text1.txt51rC1D{bǵǧH9AaF?(?鄀e?lJ ŪM,uP/a *:t;j6\hf.mv֢%#R⇴ѽ#Otm̒ЅngK&uD=K_I9 4ғ_%)\3ɑU;)ҋ O1򜤝L YKZ? ]ѷ! Yļ2\bcLL91L1i)nRyڹa,۬niX\]ϧPKUDO6 sample_text1.txtPK>;zip4j-2.2.8/src/test/resources/test-archives/invalid_extra_data_record.zip000066400000000000000000005114101357650161600270440ustar00rootroot00000000000000PK3c{hN8, domain (copy).csvAEc٠(f:xxZ8L: ib|ISvx%Y7=S}oPK!N short/PK3cZM? short/212T23VBI5FRQeB6QT1NP8S9HIFG41CQP98U1M0B1JUVP0ND0TK9E.XMLAEKحG7FIQ¢SwMl3~n+ڹe4_ZK_ohw{y!RqeioH+` )\1 Ō f1@l_OJe:)BObj{8c{I?!HB20<[׈Fs2^=_%X,h]bH$ i*SEK}Y-.x@I|ܾ%WZ_2HWhy&6l3י V0vEzƩd1evNfCwPa,0xSrrɓ&'HJ=~sť=Z9mz,UL=2ŬK˄Yn}jm|' xN. ∩P" [{a vL`YxUB'Ri.W?̷'?UV,^3E]X?|{@M'pQAdoR~,*\X?Dm++UlwfũZnoq`0x[ פ ?K22\%&6 񧊞HAL&e31=ZYȊM릗c|8㉫,"hܓJU5$y.eO Z?}LFWH#R}B~F{9K 1>NػvPK3cZM]EEB short/212T23VBI5FRQeB6QT1NP8S9HIFG41CQP98U1M0B1JUVP0ND0TK9E.zBlob2AE5=`e%)-$O^Q eAfTOuQq~N0^k͔jPA1[^5i{\Fg'$ 0#1Do3'K.BV_*@{n[i|^bQ4U dVC̽пos5+ͻOԂ^6N\51 vOi+ADlZȞFM p2bS)^2B/Bee`t{~•W|OQ"J]u z]&QKCX[eB˭|)T8lΤ )+B(7 'K]rF7 He+\QÈ[ PP ~V}C v /rCL?dHy_✒[s(9|P`U3 #xZ_-\K\% ra-@^ug/{D3Jpa{j.* \0臶a/ eIC6"%v7}Rqo $5bZ` l#3+(zD=v`L+J;CKSӡ=+l5rs((Z| -F$<6 RHV濛s[tzcًY놱N(#-y5PݱtZ4ey͎Bh[GhaG®r+ Bv/v}98 ' ؙ,7u4GEb5Zrǃ"6FE2_"˗BfМRt5?pݩ ;_6~sZ9P8x(|_V4]==ݺx"_j㛭y B bk S'W8A֤ .7t_j|`Y 4wGw3#%žL !ok3= ,rڗ_\+QKWmi ^<.5,UJPB3I][ռ4w_z=0D90:J?B9V OT [L?KȁTP6b#R\yYˣp`« 8CME!}R󝎮]v$*5q]5[0^el1:4S>hBnOߓ-=:^:/ykGmq&,C\Ql7Idkx b3m/s2-3 ^.xnŢ]Alrc45tȖ #l|/Qe֕\G *=CeҏHْMSz}LG>A; 4zBOLz${C[ fq>&sQegfqRuW+'J\W"`Q_ŃK(,ZNӔ|uE8|G&ȇtC)@=35 fN*c+60‰Y'm}\xŞ|vRjNH6FFZJt3p^zFh=Tw1sӈC0$Ts4ʼnՅT,fJP@so(?Oӕ%/U+yP ߄F{b_x`;x}'Ƌ8?a$")Q`y\Sn(-yYO >ȳ^{I{S$D'Rqk#,X A*l~AIfKR}q-tUgI]7'-K/`#AYB5TT5nz4; .>ML\U˂DO4c-;l,^U^6nȚדq8{kN8P 1rԟÊuU A 㜱c})ITu4Hh"eyfa`iPlCQhzj#9/U:CJKG b[DV~ d\K :jL,6;xw)Sixf/L')L$ж[9}{ysY:=Zi!B9o+y,YrAz'ʎ)&Qn QdT+ٌ"x&RmSAM^>$ U`>=XӺlg;G[ B $T ke締f"a_ղ+v̋o1əTM%"ώH9b*՟Vq{c.#9 ܕYOU(P!k55+sqTSan{﬚uN <'˒Qswc4o:˜Sk#' W,CD =sM]ǷJYa@SbFǦkBUe_N|S]R1xA_R: $3BX?;̆blv,H%)yڮ7{{mO=IM^.7w}?2$5%& !F0jNGe\GNXL"3pPY0oH]v%=.љ\ ۭ >l'>J $*,G-=v(hÈ@cf0UUD֌mfY9r: 3I0ztZ|sd"67R c,X/;0 [8BvY?&ρJȔKfQEr6 ޏcOᘞt8/6^̖fOHSߦ7G6?@BHy.;gfT?'[z$4r1s `-w]JzBA㲌/FP<~ylo^M9p2g6 ,-0٪a="଄j<T f "_4.3mŝi"XvPl W kloU@r% /DZJ$J@mu1iP+[ G  ~A1!-؎Q Jy C`6D"p–g$w_wь _ ÷n\/(բ C 8o8α{ [O $olBQob3U䘢:1#pxCD fY Ֆn;0w-a.-Y2(#OK;</PL9};5.KZa^<ɘq:cߗZT'd2 U;Mo]yݥ:-Ai1~`0P-&VMJ>jv&mEϪ9+ѳ>"p֘H;jIQ%wJ3|Sdq؄'8ᒬ:^򢏙_.x2#׾Y57$YΙR2[ s؋zlQ .tJ(`2<-cQ#FfaS"!bDx j9&c[B]T(|q/}=? 5047*ӝ(h޶^5yv cВ!0Q W6!uWW\us:䣰<0B#FҲd&}w\*[nɤQOs;4u".Z T Jم~ݙx =0-IjBJDԔԪ iw7!XY$b*H_fxM$7vJUѼSz#TBKDp%Ew# |2_ ==N/ZF"igxRgLV=I_Ӯ;jM";z \l(Sr4tEA# _ɗoGfW%cnRUi镓C-k1])~',qk}'[Sr "E“ERn{nolfȌ#.[NP࿩f7 6΄r78ŅDyl8g*TE”d㺒?t}7ajPaZY .-BVx<8Ֆ3ܞ062#+EGHigŁЅ(mV GCP S\)TgFnu lف;f]&f*D?^2/ݽ#TN17c+k9j{]l7yQoC,|I"&ϔ"qj5}f5q$@wmuP\=N$ۯЀm Q!R)> )S=vyxKҶpF.[uĉ>r0A}Wm:J &Nh킋 <"+)z[Ap0okM١D9@<[_4!QAD!ŸR\1mL9933=kX/i<55l̰S+t!]^=U)yoc/>Pr0yhXcл%k̜b%1{YNBarj} ,T%K\xiS'fgس BSԏBlUoa!UFI>@j6$fa9PֶKH1H{h>ҴYpQ0- _y&?#}XߗGSA(罘͎N% n̐./qgC]~$dFqtyP`;2&5 N?4 O2%3(DDF˧mx^r?I4+ 3{XDa呌EG,d+6ȏVlIn2[l[5kGK8 _Q{{.C?6IX蔡I&591 Z捽qIĽ樤UIޖV9i(DY )$MɊwf0+=CZ &ALLԬ-:!2{W7ah lb|s} 2Gnk}!Ua3EA!ru Ĝt^yD5 -&>L)38MUTՃ_X 1{`p ϲ;DO%VU(5&@C`U[bck'y4L#ho s!̎pH_ΩSu??tΩ"dt0V) b6ZC"wKA'vr %%WVZnk 񰼖- ]Iѥ؁n!R>}Vhb3* ZL Y5hEĉf'.`c $E؄ fQZDO@O^L-#篳sqM:3xe(TKdLk {Q|eyMJ Z <D<'gj5N474ޑuJf'|n<l.ᒇp^iBp-;4Xf1v*dU|mW6̸l)XJ=Nt1K]0XG:a0'(4(+ C@/); B$!O$ֹ=%R|̕\`%4 a{^(KyI@a|K>:&cfMVT.kf+yn(z!"JzqO l5~\}'d2$'d̰|voTy1!Ac+ )t~ ʷp; /AG\Vz*q7 26+[ʴ $@쪜rhW6`( +UR8ܬu1Apd඲j4Mۦ'mJQ%'Cv4̷bj6?g,RY;+ո>sL S6lE(Dp&/)w)w8︶-zhc_5-{PEB}g}-.~*̇A_Qn+-#W`j|2iʒEܡ"3At+O|&yF{OMD ?8G`"P kaL ms,n^&Ny|,f~{QExQ!+{0B#ɝ8^3)Alcο$M, ò{׸QL"q\@-c`Ly r4v}}0W8YT)s= v|~ch Wms?@㴒K~O752- d\K:= Rh烣 11 }1r m$QV۱Bo{8"\H%֔#kN>)DZ#D5M9,B<־IL{TD7B(|}gg7!7n̞"=_TlH+:E;>Z.zqN)j>3(P>tQ$2s<:vIDm[e<;gIh 鼇OFOտ`_=ݕ|P "#-B٥Hho h@+W <{ I+IE{[9JQQOg*6tֳ&fKؓ-w˓L: 6j P$JK T(A"r'鎞EZl`-u|/NM?EׇYoMԾJu(`85c{ lĬ{Ifޘ"c۠3u99S3! Jq$Õ@b50ȴnӥ?D Υ~ٱz㔃q 4aGO(1Bc$l_w|KSbb^c r>vcd)2EpѬQh\F3E(l %8I/˱?x!B1ȝ:U٪ vXb5G1 Y+G4QXҪp\FY ܆5}-F?T3&Xb"^. ѳ e5e ʷ<Гۤ3 u4XFq9todϕE;VLB܎_)2Vэ[GDJx lo%Â0|SʪgjqjgfATX/l"Pa'|{E#d"ϻ[$# n(Υ Ǹ aiS0}b$?z"sm~=->^Xf+x^2/&N&zoH T9o\ ZڈTdg;/ju}'nuJ?]Lէ%qc3S4M6dܙT1n'q9)pTDZ|d{$RcCEiƸΈD}4NON( beٿ2v q[^>_O@R3Cu7j\f4 #Fx\KӌnfG-:5vd$uc' DjeG5Jn5CyXqy{i:J<0sQ`GZ8` ZRz(YʭC%4a5'J+f8d>̓B?,` ;<p{ړm_ktIE[E:WrNPotʹt&q5*'v1b93=rN3QJ^jewҦ||F9ttbnw%eiKvؼ&jL2i&ROvRg#k"/ö-#ϚUUg̦)py8fBn* JT0eE8 z"೏g~MxDK:jt(sCc.9Kϊn _$|ڊ}<+A4ez$(Aa%s k]S%ts~̨=s"y`iC gl/L.j5&={f ,pq^6 z ݿ$p'ԂΝ\@EhpjPW\0t6^pc :cP+500cyzpkSRܵDFG0tKl:8,fzy3'́C]x(#¢k tGAf;5JLt^}CGI;ʹ_M^ŪS1v6z L8!HBDij9\}rxTq!CH%@7Ht7F#^JNuGx#Uݽ'mMfˣm7qł.Ỳxt-!q9C;oc HjRaҼ!?KT5EVAʣ2% F/ʼn3^:Dk(YH|Y#/!T_'[ܐ(jj* 8EI"" ռ(4ȫM쪛Ā~H-; C T|Qԭ a`ctʈHz3&3a׳y*_ŧY񉧶RN[11WxT,D# Y$U4w-ԛ` ⛭iu|l-_ y`|5&st%2Uܼ6o?䠵8yH0!c>6 h}Ōc썏VI"oU"(UA+f|Z ,+j9s~ k0S!Ƞ, M 8րǷ9BRƻPss^ϛ#Ļ0H|нؠŅ!/g~6+IDja$qSBos{XuIR͛)u5N }"8ջFm5;J3k"N&f{ǿnfR4X"1}wzkV S@#PM豠cC:)r0FXOQ3O,:`y* T|P""> Qmx=uF.՝"d͜#Yl/zBb&' z\Ū́[Hc-?rcڼ!Ѡ ɃSZj6H^Eȗm|ܧtF]0 :MQU`FQ,D"y>{?FRWqEWxc@%D'g{ sVQ%mjmPR2T2xAIheMK_dRz 8`XV9v!>qEߤ=p఑o/U6F7qSD@ \WՉs A[8OZF^+4)}XN;=HBfw yb rXs?OZiOF(z5] ld0}5`楙`s[.z!N(r!r}='tqH|y9)UZHˆr'N{z7?G$$X7-L榛WRD ڇ֑A.&sϝ}chp\q DTQ/`?;I`ӛAmR$1 j|zU3D+7u_8gMᓶ޺Eo+Jdz~q6B-?w?t"oYj6b> x=d^g̙RǡZKY+,v7p$g4fg&=dg=g]Ыb4ǯ7W7g/l0e5E\7 fK3׍I*V ]I\.+Ջb`J[n}KF r57v* $yb6 Èho+Ϯ"D+l ?[OGsˆnl` x&A;VE[ijc>2) Ɔ\ZOw7;\yP Ubh'C.04Pn%Ǥre$dXq4^$-!`eq.,ta~Rlw.` \xףsZ!%uسX')?鷴 x9diWkNRKn$zhH6- D0?@7C;SLFRv,4h{ p*_IhRLwL@V,FtCDt!P B?KY5.ܶBF?pj\5Nٟ7O%V$C?uŻLRJcOX+#&h.4J6J:S˃\*}HPK3cZM? short/2IACI982ENEVCeEQ8KHEL9854MSG41CQP96L1K092I6GLSS6I7004.XMLAE=0<M^M g|$*~7Ff<q+1i~s뮣_@Jdđ fWϜ!}6. QZk )w'"y3ANF.3UbCZAGh}\8믌'=XXJ*ɛ>&EL g^6fM˜K;qy9,cŻbebp wҏ$Xd0u}''=%w IoA])ơk@%!3}-l _r1SteFIl ott1rOx"wޫN~H?dmShX0'02`WtR/Do5 YK{-o]?@14!DX.G` LYJbxiQuG`J_g8+/2&?ǡRiւk5α?,RË䋮BJ_*vl˱bl'ؾ& Oonԟþkp܋lrujbS!o]Ed`bg /c$?ZԙJllLRZ!QӀD -^gXJ!m.YbڐͤŘ=0IJ@~->@uiw*{?y9PW6@Xԝ%<~FUMf(&RaVRb{]$߸y?.. mojaϲȳ6Qt,x0׵fh'7^H+ m*$Y2[uhvlpE>/Z[ :I$زckx Z5j 3noigI\Rl}ϵ3d I|-Y@`U.p _lUv]TFfKoa;<`<&w6s>W QY9OYMJXu㐇0;z5܅DRr?s'ay{_;G{Xtcz4#h, -v%ۍj&?OVI*2(t_!e;˯xq5؋CS2ZU"WÀdeok}m2 .gv uzBq!Jnl)&w=Czv“%P}ANƿ at*0|r1]: XbʘL5`aݴiWǿEBr{< y&&nWbeY1P^\@*_>=6Ii _v*ef7Ś뿖:5}d<R;I!1g7s]ğ.AB(B V*;U6m2Lm:G+ {oqbhH- DF 9QAkG?yg,o? !INmP7]ٌuYuxDc%;LG-JLvv3谒B2ꟃRdUT+TF;@BCl%[F9MS?BLcģ~+atV y-b/mՔD#8D۝Q>=+s ]=;'=ӈ,5d\y^ћE8Y盏JZ'i˜$8lp$ZZ B؞S|7,塁.Y69z8hUBeIb4[䛵oyJwo<Щ ahW [`ec:IrtN93{X 00BОyGXt3~dOqfooUCdAcq `f|89hP`uQ5:) { )4f3ų#NDAHq,ӈe ,jxi^gπ)f*DYbBƁvOdf#)?0Nt?9`}J{wEbB7Ac#b=n%;`abhnYMHi8yT̽WLO#r3n48 hQ )gѲȴ.'XLk ]`_2ClBtJLTßOojTZh\cOyb~ 1'hwW\bR PXf`2Y *S6feɘv4Hlݗ$"K`iȎB>Uբ+V.BKgSu!y3+K ۬+lM:#vQEz`#ɛYN$>G=95GWSʇg+{bއJ[yfz:n w hPiemG} C>"Z ?'{E~s}6DPM9 7wg<03Bl7NUz (7]ӍCeo@S4&>XwUΈ9nSQZIaq,PDAOp2 ~%J>ފ;8}:bȝ#Q0=T t/ވ=fN'o.c+Kc܆9`wc]l:8!иt$ca4jb?r-]2Ctv[% g#S uI?ح"tO(e("϶nrn8 O&:<.9T;BY&آpIz UaO!߸هʜ:Gj46MIR>mmBw 7iq`)gIe$:^rLB9_'}[ WeNhbA:K6>3}8\@Jz)] SWvK݆xS(Ɏ tqG}1)c*F Wk|YWH-o Tm, x^zLq3?eFYo3\彖I ZCl6*Ưyqvkg1)# G)ʄK ~M-44_4c~:qp)Je.cyT<+peQ.hUa};".@>@ Dc^5^Ϭۈ=&v b9(IVR%:6t[SMC,Y_>ԡj}5\+*GJKrԮ" XdDsЋѝ]E@EjI`lIm_||mƁщ`8^%S EB/)Y  7@QQ"śEb,AXA<J~rRM½vm'"e~@9Li= U F5R+PO,Cb T4m? aלIηZ K Q.fCYC}|2Gj>@4QVjP#٢N@R;/3qRglx5 ͷ6: n;[˙U?/ vi j+>zr%)]eNڀb#"dh^T3^ӧ2ԇB@ [ѮCm<# F55BJmWͥQ&46D = \Χ2b۰el@O,*<'Y$ꔄ&^DF3rjɑe)q*yJ u|z^hu=(X/{MQ+ĩqTDYsh$Xl b%tr*tGf|o{= juR5@(Ií!{ʛIGBF'J㫈@ 3¯>>V!Ұ{H/r/sk'muQe믗w䏸\IÀ֛T0ˌ+x% )?#gu1iy1tezK'K]SJ}q%[>k)Kdڼɋz%˩NW1cil.(F)/3K*NQ_AXa*㗜/hV(Q/[WQtvh>e-`MeởNx0A <q%ǍՇmdZ )_FӖ͋7`s@>{ߓ bo7yoa6@3@%^|mC;Idkyog\1fڿՓywbEr^+. t*BnXSDy$3 &\RI|Jmyf vç^E~s@q˱ زhlIzJ>CCN/r\!: Pɽ /QmrXzQg`v83Üo`Kxa\c`o3!ί^?g?7&%Alnok~@p*~KIwWd uX/^A ~9z=x"`] 2wAB\\PWIVR uB9B]WSz= )IpoP"npRGyf-IR j2բąQr泠da8aq0[OHGC@:ILRa)Cn,>ur܈:n۠vR1sq|Xн ,8\T D>Gr(0'`j%N":"K-'[#h_\ ۺg+Bn ߓV.k.9Or|"X}PND;|)Te;v㰑k(vh][țgj+F:LQ30?.ꉫ_(Df9;G|S Ѳ*G,nEr@ɦ:Qk4Nh" o߹ A^yBz. s7}}蟯b0l#Q+;֠+3HX!ʯPl8}5)ho8%ѧ4r{.bSXO(d;#RųwQκGKFzx3w>01`+%YmWi # C[/$F-|kIQOH&B*yWW(ʏ@f4qXUTu*T\adiPSK]a$ӗ@SZq^:3G&᪠37LԌYg~2<}/1'^;m mZ=^*Qm yKA5lP55BAOD|bdy9aԽ +t)UAtqO_pDU8)&Wip$w9wpTxeZy+:#퇌;NMgrvB]mxZx4A͕!~f4 'x|}PVLTn̈UX8N~Gs UW[0D̮<4mmn$er& }AZ= M*MJc#D7Si{BM>D07Me'! (K櫘BEa_`~ g(j =^H\bi?ucw6@ˈ˃)JEieO C .O%ka0}y|%E؋p=, s5 +y Ciѝ в5)n뾝%d3j=פ=0X_ RY@T1QgCq<)Eڿ{Dn2:FG9B?:k`+$gp 9h2Na1V+-/Q"p ,NΦ+)NJN.~1mk9%BE =q$`q 2T<]bN dkr-*, >:XS:7aGSnx5@`L1HE]=-P&9Io$$hJx($21QGfxW.#7WaAˋ[>6[P-7J:\R) XQWa3}WšvIښ[CLrA԰BR[jOr[26NN2#mb6@D} 3pӛ:`J0do%1d_gZwE$%G4b+,:u?%W&ʁaJF!`ݭc!RRZcU7Dύ0*N=d\戌 5 Sث=-su޿ubl3cZILH} v@ciڽ%>!ʇ?пwssUb&ŵS?[x,y7,9ogE9]c= 0q$p+hJ3Jzܤ5JY)K+C|lzH? ;,TwZX R'EՕG*EDI˜rNj)5~y̽(tȪIqFX'G5TQv(3x/WnJa!˕rXT]C\Z:b<=tHY 1.fvvt1ue,SUy2'';*,} 'SЃ̞x,{]@{|[xAIԳ#nN$];>*M_zȌm .y oCβz"73%'QfH/)A%4RS`eCWbCdMfB(~xm7 5PFqަZv\2=/O3wiFnב-RM9ȭZjQkjQR'?8)$_%EœFX+tj4| y"taKwk3^JЪdJ_֫,-qTx#&e/ncD kF4[e'?| D$g['|#: PB+?)gJ??}.4HS,A^c-K^ h nQW.0",PUT{sNM̤si6m 0:;❬<&yqzRFѥ>  /q{KƲ?VOM>!'&a}/t'R">PaJW}לjt$ 7ABZsEb Nmfw868UG22흇yPeE C\7c{֩$ r) {m+8ϖGQ0-,نr`⽨M]c2hx<Vm9tuzT֔Rݡ`tdL)zaъ@@k91iw;kp6ǡ$h5MquWq,B*΢u^QO3j_-@3:Ys'mo=tqdyUMlmǓv?(3JcOU.4({rTKe֚t3m|NRPdAÿ]5.^|Q; 7C-;is5@S-t8@/݅mPQ&b\^-՛p!N}^ .y F-K )wN^\_*>Ag_ 4*vߡ:!OfX4Ӷr .1kS5#)l]d˧8.JǫS /9<~j:㗑fw'b!C\[#rnKuhr '8󥪮bTD6؏EVƤ'ԯ^9;c!ɎG9ȫMb;\FT|E15U f.!m)8iR7:$\KX:L>6wү5SO4˿Ng)ъ{ Omry9^zŦzĠElN;NA[Sy3<rXџ_& $ɪ6N PdH 6k ^Fs?:<;ِ)鏦O-~Fr@ /dd]#>kI'"j掯ɣ '|""}[yo{~(~8FkVP'rg?ޤɢ+ߍ;ږU !z'F^?_֭]8~Oj4` ~"tŻIۖBDq( n%"@RHDs}㇁(+tOJAh i!6[Q5/:E JuزNj{Oa^u,05BHIxګu[f-[9Tl4eU 3rq"꿓_C~a;zF;#n.4iME?2,&-2?pE 8;D-ɁS ό,v߅K뤎/RSEu2=;M#3q"hdX?Z*HNq(LT٦zX":ՆI9Jв0f?(칤#FQAaƛ ȃ?AE!`,vEGe*R֎Hoq M y1EMzoJv}9(nz܊1,]+*r&~zo"H,QZ_ 8~yf##f!xfmp͜J~roTcq1!jvÚW.tNר>6&]f؜ZQ5w7+<&6]nm E M:(n@f0:.={-KbG]_z #*B=q62ӿ?+3FG5mT`Nv̢^ f?x+>c4#h0`ED!؃)#Ljt 䭱n/Rv%af/-rUY>4E8,NoejЛq`?$Dtf鲙SAtD` WD} 8 `("a&z:v_jD<|/co'ǻ*-Q-gb#uX #/zus6gH- PRoyrE7vSs:! ReT))EqV1Ț15ƍE Vp>ոcֳlwwo=eU0WA"iA- 9y>DDyvsLP&}bI53%,aSs^+csh>u whqdž=/vqtlt@.!J5M';%qU;Uy+1acԃUtY{`Q7L*.&)H4y+lq~'?Gh }c,m霷O=y:*Au1UV@ߣjۙn{ Ve[飃v$@Llo(xmV::0C/E 1/ [;@ǤTJK0;<)QUmm(O- J!f;2D?Qt-?YJav"$ OW<2/Q$1S%h rGڝ(fkHzI `ƅ>!jJ3t:'yXIͣ+P$''`\Jh@Jm㡓h{)μ6>X@{5D3C3Ք= :*+^n#)C*BZ1B8 FCI}}Ijή~VݔM3,eҚA~x)߰LrSt z(~}CCe:S P U }1*LLBj+L#)`XY&nXi8j@"_TbKM=U(P$.`Th\wt|.%| vnlDxZm6)m`o"l hx9Ȏe|O"ꔺ_/uO l1qy= fZx>p09(й]x$A`Y ssGrД>wWIѺfv]pBa iJy PcLM7RG2 K8- -`^} XAʰcbFqvT׭,bG_AmS15wxYF'\;"tXvmuB=8F2DUfQ kGVmz^=\9 S= z2w.C%>}8CZfzffzdXɋ3 ]dd he%" = `]jO!N֏=F5єAؼr U%ttHhuQKSeaAp-~bv&P~79 ҿC\Lljxny] 'Ckbiu$Hγ>_\.?fdTYch~R9Ŵ]|Ƨ%943_fMs)G$:'bsMv&W\t͵Q;$ %@L ^cWGja4޼4 T%s׵lMY[iQ~|dvY_z|ܦcyi .gG^FA ]PH;t*|2/@XM>xDh1pW; s#3)pnKw#* vzibzrҾM>-hSљ[><(7 -[Ś~>؍4V혭XxC+^IR|%W]݁}~Ԝ b.@|OΉ/{6yncܞϙ~3S/Tؘ*DR.ܤdm NMQ6\oy?ci[M393شoOD1_~>=1l!o%Lp԰%^ʉaow&ixh8AbZkM[e8Asyvi lŦͳi,(oÌwl7MS/7F. Mb- 3a5 7"J)&XxaH--h Tnx/D T#yK0"07Pho|Glϥ v[5+@duO`dX)ݯ1:QD^-g"iSIXGWoEc];x6"CR0Q3ND6-m M~hPzWL2uKhMO_AmpYYb]'M_C]5l$0~_ ob[e݀`p3ܢCiMi֒]0VA܎$y/bR{pj{i%n3wɭoP4`PfҊ0"{cr ͉A?WLv}F\+wwCt$ h]l܇V>,X02Oxo#%|D a[q,z6*KR| muǀ}}n@(5<7)u˜=pO `YKxPK3cZM? short/3FHQEAGIMBBE1e4QLHFJ52VRCR5G41CQP94C1I0K6S8ODGA0UAHKP.XMLAE'R>6:F)=$O Z#j)زy:Dn AJ^Sm W({qOs1m|;^{K34,~[HN{8(Yר #a=tMң)CӲU!)6} %IB`dfqTւ PWp?T'apPCՈƩ/_习)RJCլf,嚰@5{՞jL+:u+6F}tܘxNK-e^@kPGf8n%r}?:pI[s3R&Li[l"ZkcPQfǘ8[sqI蹇@\]Q]A${RZ=-EE7§B"HwBd.OćeTA'f\෎~*4+2*H:PT?S+bBm[b\mJdz(6QKp7[]E6K:zӈBaSMJԯ/+{NcIv*^-1niO?{bҷ[SOфwoͮ:@ܤ# y?Ù|Wq}TaxM#uIΰ)@;5wZ\9* ёf?5JQIŠGQP,fJ<źTh/Nħz1d*EfR0931ādtR-i8\9 En**Q>T<]bʚmR 'N?/Èp~ ݜ OJJ)7uc|%  ҫ$z$~Qk((7[݋Ppy^mSH1~ߑ|xsS[H%: Ǵ`_ٗG h(_FHmd"`GJ{ZDӸ7˾3 0i$Τj%FC 6i_V oI; VAc*V:40a`zu}|rڰ&&d&~:[0G}Ў'$ړ 7c&#˲xK#/Zkrl||ЌG6zΌp &${tRz= ƪ@!kG(%wa$fYBvG7󔱨2To+yKkMsܖcsʺ?5"DeBvG cYc9:`jGZ]?Q3׎:0o#fyRJ* !=FȤ؈t\O ip•uYZ?^`| -)e0 ]wcPaI?CWBbJꦜOI?!ӷb´f<0BT7 α` xkoM|ΕTt 0>*,涩yk$߮{jⰨHhe#BoHEONZl6Cet8?+ %dd6/5q4 ZHn.?tq!td`:-#-06mY xk钚;EӉKx&'~4)c ~ZܐH̰g:J{YَaM]~A*Y%LsGߍmW]tD]LqaU-z[/ ſv`YnmvSPOXgNk5.|Q5;c=<@ZQ5^WVm΀+&Ru-.b^IAi sfbΒǦW6:lI2T$J5D4U%@V2cwg}AlYސXS2sd5V彆+Uґtwtg}~CWmF6*LPP_N|i%BXɄl91 Olv7,Wu/Y~#=CǘҴxVtcvZ Oq*OZ~Lg4`EN,SOt!Փtl]$\1yC. +*9 R34 QbScPP,Bp5bRO~hj e`V κ0B0 ׽N}^1n}{ɥ15o:5WwӪ/3;[ gu| tm4NGC/YBeHM\YjAKyhg5uYR! PVppsz,Rfg*-:2ų\$$E:Jj5ˆZ;E)$̆'u9o{?imB^Y-S7ص5'}&( 0P*FpU-a:B{~hBQMtoE=(C~>( \XKfbң]|_1`X [r+н;C'c6%6\|*]63k[pGiF(X8{@A2%)`: X.T!GܜaEa&<c{^z7J荶!B *+%F.(`S@tJԲ^ %HH㐘!ΐjuS xx?P5 qFDŽq)kͲ^Րȝ^,wSXb7yC(/ZY:re)*-DEd%M9\yT8עkt˃T>܁ @b&YCeU;W<9<VWld5lI-E'm-*t,v*G_]|Gt. tZZ>xF0 }]Q-?~Vh*5.f8gFGOܒh{yh )@olk0|AȘJ=)K/V2RhQR4Vm$_~ S Z{hBbaZʭ%B|x0m ~p W,=nj!wLo(*kke2o ~!DE/ 0u\qT_>!0vRGdz$Ex FmL[ D0Uirgka~nڛt3D4+"\WlA| }7uE;WR冊[ BHع&~5Ek Z7k*TJ(ʪSKZpƵ2UBƽ[őrGԯ-yw?ʪu4;5!Gʴu%yib.&G)MjQӵ}I*T3`z}4N903UhDi6rݹ൐d+أAHY `,(cmDA@jN zl9|;TI+Ml˳qWWƎß8y tnjFץ#s4q5{y(cIP2}_ >EFI>{ ^@KEG B|n]W 9,l ĨdY81_Kuȓhe<;l LiUK!3z?ts7a"b&?JNYf֦YdoTz* | =]N74S , D2Ѭ>ԠjDxM^:r7sȫ e=~ng`QxY^m w/qL;ʰHb4Ij_^}Jd>RXܙg:Ԑ|Nwz$%_* fw/J+f:HH>4.kGFP%j",]&2 3 =fG6+N- :'(l )PŽ( r7$vivwsJsx"Uf  劘Ul'`+r);c'zЦ!G⚢^pC`ZUa.@|zbkX9lW|쒐/o㚈\ߡ2k 9h ixkID$ʼn ^egێ#}J" ID٪i?&+HO 6,yfAKE ݂(bMShca(>(J8;.ӷʠ(hw'(@QBOaCޛi4/o=@yGhO b˞B.fSヲշ& Jd{ ymC~8Źge+|k b̗m*~c8~UEgBz{qJ&1t>}'^%oM2՗Ss/gGH,Hi//m̥"'I-ɕMs ΄^1֩ *RSM5H1G yae)Z8 kzy8 =8J +ReuęQ`#8V'tՀeI?LCYSNzRd̦ u#gXo>d9 -Rt':J QlĈtj7T0`$=%?//_ $aM6RĮ PX2x@:4)QlN S5G9bg욜mўK&S6c@ePJ:Xwpn7\0CWHk6>ɘ*V4$Dy JU}iX)w.vԏMRt ; $ CETJ,!|(渽{Jetv …pc.Ȥ+"1U>t׉²C Vx- S$Utd"4*ސEP\)BD3q?Yf\qRtkmoX3E xL>nGDl"BNmYa_"j]zÜs77{?߬l'hV x9x*/= ,䇛*t"{?;)yYi<#+0W&}(WJ夣h‡XC+(:Y@wJ3/|tu9s)ȦIڂwC{p#[Nr#EcݼҬ˱jEHk?eAMJ~XwK@0$7(FSۅL9kMv:}';,Jd_.,;s.#'R X(n7ꉟ: F{O6E:/\ݰ{gtwDȎ/r 2$D T~BSB4ůsvbv'k9[):4 ƣJɸR{.C B$ՙҟviˏ')RƆEG30[Ev؃ ~eTG+C<'\]L HkZ䂹\H5h(n4ܒẇV_6J\W)%sNOw',UR_Ddإ{c+;!Tz ~Pv/ W2.҉3}& XGI<=e{kL3ȌGN.CZW:-Q\Q1o~l7"b jK$o.zn支-Lh:t n|;&:67'plz Gj3q!c(EEpjOҮ̝<G:!)4U"9CD-#<P"&EͩU7r`f5ahoRw2ӽ2wͻ;*Ͷպud+G@#V,$-MUBbopRTOPIMWK~Dk>ORv'BaeX5$TQn]s /O+oy Cº`V levvxFH`(vZf*wfi2KzIec`1^b'}#Bb~a} .@iv4OiYpVZdo&)K؀P.[O,?M cz/'LYKb'ߓvk)mqQ_(nqB5 3bI[t[[h5U0#%%6rs=zǙ-Ӳfύ?t"2e D'ˁ҄H E../MO{xEEPj6 [ḘG+YQ +OK ):J.Ԫ 킗V'Z8Yk7Bu] j?< ]ֺO'X/)sy@TJIn"+D3 SHS̃HIGo5Jna+J+$'ⶆ'hi~_6VC뫁4j?ao$Xgp^-(:Y_2.G|Mt[^')N\lɶ3ܙ)_;L@E09пjOt2zPIHর*|.gw)3I?1^Fﮍ7{![eVJf~ϖGO@e.fdšp"o7@H wMHTcς/C'(/j[sS?4Ѽ]Z?-VhpspKdawڂ(>Nj q+h9yw4݁x<9\5d?LsB╮w TYTCIB^=އ}$y!qDV1tt#FpT (+cxCݏdNx0nS,#ݑyR*"m^ML%RI-8DdWH'4rB;ҸjJIJOtٞ>l W9:@5JzB*%{Pۨ5`-[Fo9<} PWBgi9p\ (CsChX$ۭd{i[Xt2פ6JrsDq ,7 ~'f^Lbi-P: Q/S'\ %Rij}ʞ .FD24oJ $?^QUBy 0H8 BI`4Gl:,,U{5Lj#F@͈ ҟk8w+KM(_͵O۵} `ʗ'VM?pҲoD=մR?~3+S gjc,t ]e9ſt+#lPJƐMYhHŗ:?2Xjq鐪$b]Z&v/iu##`[eH]*4ןl ߡwKՍ0 DZ+"D\=܀q}l-ulb}kjW0VtBPgn{Ď'` %92woUqړEGJ0A I[eCa) f/IDZ0ҙNuw*O9&FfE!Xv*.C3Y01/[eB)|tg.o-qD2>20h^C6!\ X3YsYڭY*}.iJrG*[R[ ⵭%ds֠c oE,_z NH׺Wg5 Ew?JA(!tx;{<}]W[rK?VN%^9{44;vvB(II_ hL~ oz?pIw[q5b,mS91&z3},5v$;;Р,,ͩ@ќ*g~:I%iUg>3Kzs…Cx-w(?^>9B^)hc/KF!c2l #6Ӵ arտ3ׅV;hJ6Th+y L~7㗌l:Y{xHMSp#g0\ H6g= VGuɡ`sY!L<]Ŕz;tIn. qrw t_^y77MRY$+u5¼e&ޅ&-BnJt 5švF%ZHS[A!:'Zgx`Ke(MkhAE# >^f9<;۽һKi"Nm( 2;j"99 3ٕr>=bkz7DkW7 ޗ֏% lM6rJfB*&J3Wd3> O-o([Kâp KpYOhWvgIiԺzu6ǝEoi~}䵑~jԺ9G!Rߦet?)[Wc+}.nHHykreڑQ*wUHܰ#3@@&n].qV"` ܝiiQnf{:~G#Pbu88heS+)QȜPŒ?%QuT z^JьqE8V\͡k5ŕbP0cVm~gZ}[ٴ`HL8JK@8DGJDi15c!q(ux&Bju?- !_WAϸMQޓ]i@N SȄKEpB7O|eT0LMA-xƭ'-^8op o>l(-vK ^5- ĝdmX\yS5x*C%4P)SF'|84Rl~NmVM=0d73 y[/5V2Vc^%R~X^mʡ3/c2Y P%rZ7MGAdUɑGBO(H8 )ȷPffUrv# 2b}N*mb{AI3jAtMC:ޥԾ)5=S|݇vl3cҹ8Wl79b֭LkQ+55ċzf;a*Rb ]yo>fXVvl,+-O[XCF=o3Efv/ٞ7y4b!ބ(Ty&tg,k|KA_}Cl7 d0LX }=q͉etu_LWVo(֗`S׶+E>>S-0C lQ:@BKx6@ĝkoBtSv8OE[LaqD<]tB[й9"O乊ϥ퇲uH TXIZzL=i.Hء1Q-ߏYrGkx (n=hg O?]-!KYQMNȔ:}U+7sWi22D]tF5Sp_י@Ne\TQchOr+"LT"4ޗ(g瓭WE\^Qo ()`[*2~۾?5')_L_p b^޷Ƈ5!b^ލpƈp$Ӫ;:RR/HǪnts9iga{xm}ĖbD4ᩋdtneϑYֱ٧rݨ#qLmZ&m nh s(fEpeߢ vHG&>W+C\R1)#k=cOg3ա0Q=_k(>t =2w ǴL" 8/&R?a7IQv-1m; 1mmC NUiƌ_kT4o.uQv__44n +%0}c&J>fI0zv?#I`(jNfٍ٪zUr+QvAf@ ?EF@;s|!vp$]K˪}Mi/i_ \';=G=jI<Ĉ0 "/T&-D"7IcrEa$B9ln/D@ᢊjFlԏ4n3MW>T6x6<讦81Nn.t;Ky!.D>7^1MӣA1Gh˱1,~ێa[-Pvr鐙;D63gJJMW=-Qi\NI:/3AٻďQubg+"Llp K;[e˵{L\*gG=h)2>6Bwrq{\xV zފ&ӆ\ *lߟW\6O;BX|^jy;v 4I2-ZY^=L Br3O`+̴vW}Rk<46s2oX+Y;MkO27T1z!7}`?&$(dXvB.lwC~rjj񎨃!cfM*Q(pyOЋ/\('-:z*׼٧$CO ϰ8+wgb hr? yףg -f=j:7.vp&@3l37bR' } ֟YBNaZ]e &7$]Ϸ_6b\iL)s8P&5^J50G](M،}]*re7#$zqIѩrWذU-HNNN>˸BA;Qd'RsQ`|,~5:ۤ|㝦LQxJ}^ JOrb[f>w R+MIЁaPAڟL~3R$̳ؿ_|1`?^K07SnXțMʧu5I㨐M(V [6l:R-^T1i:i"^\Wr/T9S:REaRIRk1FrnWl4 8 LIٝuP/irȅNjжi-ajdfn&Fΐ^$"+=+5aʀ WTaT\4ư h O)B b' _A͏Tq^/ ʡLɆM#Bd|4NlSY=&_gmxVݿ1}^ș4 \:L & (I Cj+-M0&GǘrG=V q6NujEtAQSt%-:nu#?iLpI5yUwWHwI>K|wKʪ^34x|{RI)OŹj;e寞S̤֩Q'''TԄz=okҚ\aKK^fA1KB?5/sn0Fȋ두^9'K|(}~Zo{%EB,Uh;vk͋l9rG*d ʲm15=+?YU{GMXTx$jeb0APicܮ3d:H+?nx_`1T ]BCgW'2q>um.rs,k8X^`mm4i> BH)b犔O/5 p '*<!*bAܩ7>G6) ?%BȆ4;"^- Bm榀}JN#b_p$].&/Sb:⬑Ud[]!F[vkv=A,Rb*,i)%'\zLfGGsD]c k'GGڦc'܈@XJN4p1eD&Katm64x]n5 xiwL<.i}*(r7ej|nW_}ȍ¤Vvq@%eH<5:Qcyt0n3'65A]h>Kk,-5HՄ7o`CxbO$mc}Cn8^G1]H]RE#Ůs>liI4R7lacEA^!eXbXG6 , ޱyfz(9^+M08ۅ7y?z1LjM>R0(k_.!!cUa[P0>W/P Y‚b1l d@ $ <ڒ1ySitD]Z ;A?Yx&r+Bs ohH[5=WDIɱ !JNiE#I_[OP56m Q-f{chJȬ#9mKo6Ȗh=: \X3]E,?k׷ksWyE{f< 3y}6^PK3cZMCDB short/3JFFQU33GH861e4R605JAFTH81EG41CQP9ES1O0N1J4IBVIC7BUO5.zBlob2AEJf:e~O |f Y䎩Gʆ l}{ӜD)aaJOqwҿုy,S P: YYpXCV<ukG2ڢ ]@zt҃+wOWi *?*Sw\  na[規*ΤR>F2Ӑ!3{KzTrd)4WT^f?M'ڣ] .z>`A[O;v 23˃G%u}X,e:C0pnjvkt<.G4nG^T6zV:[#}V*9T\$f#<7:P |$}Nf*{9&],X{u~$5:e(l`w k"[oX采O?2{9Q 'KWGS&1Rv]7+4v Zo< ұ!g3W-k{ pmD4.2BY.]0(v@RKtdI m6:L ew5q !mƁ~՗߲("u(~ŝ^b'!qX32SP Z@0Qν$YD7xjfɯ6}=u)^g‡ҭ2zn]\_-#cv@7u]6/>u ͮ XtQZμ4dϯ@+jV̓>@ }\TջHB wOryJz>ge^B{ذK 9ˀY6 4Ƿ&-rdkQHUhV$^ٟvO I%$T00H52OT!Ш,gX@ׅ!ԳH6{hX2[yAaXbB kBѵ$VԵ'FaJ >.<=mgF"1?HrIIeb{ZTzuGB,jXl;k=xY E#hbp7mt$߽[ `%7/w4wA 4byv'^$7 -0N0nd.a+K'LJ:|9~Z]Ye .3i}:c~5+h*i櫔:MYXv@5PyAoa,ߒҦ\i[zosY7@Ϡ0Ag m`oX>YEcP!I޳;T(E-͡=Q2#"7`HWrƚӐ'0V1 Ǧsdp/9-ڢ6 ΎՍ$ {<6%[ĠbZc"=]OPxޟ$fWy۪`LiDvUMR@+mEaMӈuY%Ub&T Ru2TR+`q€FeFS֞!i.M7^kq#JO}p71ބd=bNuPX?>|>h %^y=fY{Lp9xmWR`=)fTj7yvK~;%o_?a#He۬M8xR'5z" x_C 1p= j"+n֮h}R*$Xq.1oLcx:]|7LS E s ׀y*ѯEJ^=8 A+iKs/Kha\xOam>-) _} Tm,1ti~l)fGCČt-_ <$E.Yx߄gbDrOվ10JO)dn&Kd:4왺i Z?]v6>ngoqK[0oKDpFp&Mʸ@8 ,~t >-Ux ҾO1FԢ2//kÎWfWbNDso2a9v&jAd(( Lݚ|֘zg6Z? *:7N Eg]h:@]^~ne65΢=:ztTi&ivQUSRd$! ϝs4OzLC%Og06n`]W˪4MBul{Y@)%5]+ALGtܲHs1i΢?v{8_J*xѶb5qvHF5Wh:)n@w xZoy$;qA\0+ ^G}`48f7g+(J5/ioCVfeh:,#!sG񗓤Qх:v]"6L @F8r\ccGC˒yJ z `YμOFe6YjGP'ஷPψ a*[pI5ǣ'Q _zOPΧ'L!0D>>^]~C?@)\^ wJ sS=VS6/j/MnL56z'YGC0>̃ QR3YE9LgAVwmxZ:\U:JⰟUOsEmxN< DbԍVNdbSC!6VU΢ꢅ[#DLAGVwM b'!o}(}9EzO{c 0ФWXOM0fJdI;LYnVkS/MHu֐=%됦[^J5CЫwtʏ;4b>Z(61g1Cߠ5>@\$I1p7/ǫ׹jX&ܯvFA@Y~ p &vZ0Asb`+Cgᗶ/{ah,1Nb"U-V!5EQ d|[U`9w렋gWBXziُtu"ZWއdN/ҵ/P6C':OҐmE@w.s{T`!mm[7ܽ|&b:8Uܿ!TtxJpIQLMXjG',%Un͊2Q8+A_ OK&O!ֽWo", J*w^R%I{Ո*24%+j}T@]fxCŅB}5چ&ˆGo=SkBʦM[.;#6?š8M3OmKta jN}𳸼XЙuԻE$+Y6; /sxjTukffNK_"%g"ܩ=Xhfƒ;0tz(>3|ѣWXz({m?tciJiTXEOjQ;H${@I/z]K T 㸒ހ2ֻFɤ/ ^r١[z%*=[-z v.6f#W)3%$NY@u( ?'-HKQun4|͝ݪ'a)dȜ'Ki@8 +g e-PS}(qҧ(&RuI D~y DsjpWFR5؜ aU$oڲu$rmj̉` Â#|J.; nHAc,#jdMģHV7 |'7V6B^b@q]`؅omE_=R>㪠0MKm5uݟ;bI|/Szs Υ{q_F\2'9 B3 d9aDTLtj Z<[%ƄZ`IaLK B4Ϣ9m\Go-t:g ܌dO{"'Vq`\crleeޞ|0abo-8nK锯V"ә3>ʟj}>k:`WRa89b*섨,4$[y׫S65 )GFԉ-l=?BH%yem +^ ];w-J>LbSɋ"bOi|P?]b&]6O`B 8K$%/p.v *H1d!G16aoѢהrwKدj0^݂{ -I܉!Zʤϟ0%iZd^j=ІD S;|ם(b4n K=bp Gl`r;KSk:w"gTEe㫀37ȫ? D&/Cz5.+tĔAaQM6pÅT(|6Oo ep@ 3-ǕXys(7n'`= @AcJ[>UP/G>lU6t$M+Bo|GaĐ9M{&+xyK~ـ^ҢwVgaI@XӺ"-! @{I׹3gC8[$ᯆJ>Cvڬxʎeו<͐.{apCmoc[mg7Ptjsd}=AQ޲d|*'tFukK3BU$$wB]=/B;RP/]Ń6(;=w7/ea{%f-)pŝ`y'B͈MүL(Y1] ?x4ފۤ% O )iXv3 j#!1>δҧ3AhArYk84Uʞ t!K* FGLt,BH:` ^s~ -"3TQM5Ԥwv+`tj4/mTIig=;OXɤ{B#O ʿj!GTZ\*r:]*[,˥IѰY๹zKEL:  A$V! 7Rz P:!0eOv1v2u6.FUW|=JN"!8 %Dϯv\ QsUs^)}N| vF=lvֹ>tt >`p7Q_Ɏ aޡ28Jא/PLF8T,Iڊt607[P9'j@W [sQæ֞lvЫp IffW1D˟PǗ>g_(@j9'茵-R]> |D[}Pd$]h>|ΒUg7fl+y#ZIl %3^۬!*+gC_ĉ~0 > W#WD/E17NHQ<ggσX}(e[, |!uH2*:6)Ŵ/L!p~rxD k(!ք¡XG1?ҕMޫLB&y>0gtܶ/` a(T *uOtT_.9U]T~e?ۺFDwMW#JM.(Wc),+:B[Av45s/P&y2D~ӽ-]w N·ؕ˝ Ҳ>ƀq|6Zw:37I{ΣڗP Uƭ[e أV~jԂ071@[hly8َvTev !#/:ڡrt3_YArww> jm,V3:k g{}/7 %c\$mBEC|5A;ß"/2$ q_8XbO$#pscpY{^[đQt=^V  `\?44/%XێA0n^vK]N"3 'k~qE9~ _PM=&oхUy4@e+ 9p7P:_,dS7<2Uht^k{HQbs&M֛&@bTpVޒ:u5fE|Z ')7kbB%F<J_ak7NNQ 7?,rYv\04GGAL&CGٸ6 ;'yO-u[vp@FDukzיگ¨2mIV wEF:9YF`:Ł}KH g1S1Z\y(bIhdb] ꁑ9G{vQOWYD2Co6.jXTQJ\ ni2{/3ǩh{ZLUQ3VwdqV_Z__vQUP2LLEd֙a[cG<2ˌBdOY>2K7d4dW7F0~<G$]l䊄xkN1?~|Un,Ch'8A6.I_--R%}nN JHAsIJc),Lo?n&iot-h=ۘeqesVIZl&- ?`AGd:IGqu"߉̲-6yLY.]'I;sQ J _Q*pOer"7q]xj<[Dl~ [*O?3axhIh?b[%=i<]Bd0( z'OHQ\gPV].4-o<`c?i(mq٥m6vKKQ1g{%]䍸[rcr?Ͱ>-z+ۑC.:~M*)AY6jG=EUaHpzU@&OvEK~[ P6ψWDzJmgБ QVuLP9A#~TAid{۶DkT4fU8M 1M(QցטjCA0:3TɗəD -~; +._1"[R0IJ_ b$vmQ)Ǯm5Rh&"4H[0Hc]ZFw]LbSmUCL=pi*/B{,oZI_{#q$I{]",=[(<팠OIWA%h6ݖ(L 9xȈ^F iFR2IstP"ͷ XD'}DȌ(qƎ$%J[cUl׭qZ )'zG&*W4Rk y%z1q1-N1 .aKe `keodO.**(C'R-$ :maYWI!?Ӽ ^ xGd<2u\6KlWiVU9"QZ^[0U܋.F|5h»R`eW{  sdnw*TBzbxWo/jG <ۈ}zĢtˢ|sC\ 8N_rZ5q1B8-y1,4<>>; 8èb)W`*RՍ|.0^k@ Gl,KiJu.4bFifa2 B~N2M}1`A˚}:KYyz1 4Q}׭>D;IOa?3OVtݹPjgDI݌Ūٔ(B7I,)U[0 ߠ/7ĭ]|t|jg#&By Uu`xMe+ O BEr=zF0g ؽ@n~g2}J}FK y؆` Gl/0iR^ɾ} ab4 n~LvXR俧k<3 W0W67R}>^c哵*@2gZ Oş @=4PvG:Fk ti`WUD ߛc֍^1RtD}Wx@˯:I: otyQZpIS(I*D0vVnثk\Ǜd6 wםz{3GHgS飓Ĭ 2@ ]PwwA#etʹ08 ;9eI&H)үykpVsvGh7`i 䖸XJKWfr׼Bo_\HIae]:cT Qڝ#6.~T潨Ň5S6R^1lO\>e&)Yhh&WLJƱC`Ly*ZW1x)CK'8R& ua"Rad :(7)QU,7RҧtŪrUå(\ҙahlNT44)Ttc\-Xzލp:_^݅ac&8)BDPXH HTȯ;c4p F-| )3 UM@o]}H g E *"[݆2idJctNTE'ݴU:I7dذ: &Jk&zvǫo!79VYF`Om Ha&1 B;wSb7ědĵ*4keGS%b|YDܤvUϬVY w6\"St #?mbPڏ!ڝLjZ޵ Ջ+r6fڅijjAU㮼_R*~0r'^mz>԰,gwD-Ah1߅ZMth>zE]JaX3*[yB4c S_=[iW/*ȧwgwwL@~HPwŻ߂[biG07%2pųP '/_r!xͽ%S{;0@vZ)"q0DE irRz-D|bڬ*G'KT#HeHdӛڅZlڑO_G[J%yv4-v w Pܣm[_L7!1y̜i(@"N 0:V)Ť/U>z<&N`x&0s%MBVVppUo:/rq|"+`@tBmu QFBbME!{1 lf8EiOciruή*K 1{1m9!|mcK)E"{jbySa?}˛<_x׵M1ɭ@׏V 8\V՝ڡEpsӔއG) !< h׍Sq 30h$ّxb>Y?xkZެUsuKxcb}CXuo-@R8)1q7,/MgGڌ"{AYIt-Ez۳F=>زYmr oDž{ Ck YG@#LԳYBwCBqЀҾeqECcd8;Z4es-߲~4Њ7 Fi&y{2_])E\#FjL=*!ZNIx+ٶv,XH$2%2erK ) }b*K OprN)yJX/{Nxv;K" Kurw$GG !f '(4#2̧-&0p`kU#AuOgjx0đ,~ҹB*v*"ܹ h]zNFQdB།? ") z1t6ۇ@LTzD,AE)pg8Ĉ^,)\YuZU y?)eŶ\X$*h-aS/MO V [BW=R{%>_hԞ,)ubvER[mx[Fmʘo T;Δd6(dZ0b)> %a `ǕE5LR3Dy@Q0(+T927ɶ:~縡]yᎡХpӉC91 踩rߊ;'ft^MxA䧜 8-(\2 l毨jA(oU$ڐ\b0U)̓(Z' bfo/ӂ1;CK-~+,7~ 5L8:r1I]ҏ[ayk.iw4X81~Xr^ ?0rA=fVl_@DPcm%^!\i&i~ńhA@%:lJ6Gg\<0;m2p0p9WEV4 ,RQqx=9yOt'#Xo-0kqQF-wjb8IPH帒W[U_Q.%jc 0;Z0^@=2 !V8!p_kjR$'c]_dIc!>x"LRNmtGvCDJ;n1AM4eQRFR+q}BG/S-ŨI3X ǭ}k*b#JɭLn.,Z^c Pl?GY <6?@` Ƀ-x\ Y([{ct!KBcJ ?rIOl>O'Jŋ~X$3oI/ xݐ+ _P$H 9G#fr>s<N5F辧EuΏ-8V!+_OHFtO#_ԗɳj$::ɍ+&+$'^;EKKї8QiCh%CXƎ[s1`$Р4x2>grRS84 ~Oh1?>}E)336:-?Dq΁+G=ZM+c` ]re"[S"^4U.~2a5(]pbO IMO|J:$;c(!G8'FCppͻq7bgԥ@ _&qdجP\ߢKNÿ3Fi9'qsRx^F1S[~c+ޚ?@&iMbPf醚~/"քlCZ[[c2& 7*`KT#( Sl&~  ۧw D MQ&Nwm!7\3@·[.󡺨1{q$`]t'ij|W+ wq* k>º%6}V}KfoE5=/;3V)0/Ȏ8e *X:h;i6no7c8ybn (=]XDwEQnϛbѓ݋=~|'C %PK3cZM? short/3TIJ3LR9H22KVe81AI3B0KF3OM9G41CQP8M51E0Q4PKKP64O51JV0.XMLAEï JјIiP[km Co# P-< +zG>XH&l+KZl pE;!GZ5ԕK)ՙkO/=9X VoU9i5⮛%Mײ3+Ƕ#͓u2-䦖6)CE|CR"jQǣLN]KGzU:*}(i3'?zeyd_= e\le \GRB7\$= ?z6l%-r[Sq"͝ų!%1*4)h2ǣ,#?㊨Q%m&lNf }Hrs袱e!3@ʗt[9k2˶Wo"qzS æx:R!8^.SaeIiڡD%QD{)yic 1ţ9Nlv0_R[oӥNbhL0NJeYX:|-43b?dFXx+p+  ZZV-3ԹOД!Rݞ#إ|w,7-b{cɸѨ{!k") if&a]JpSלrљkTy[+aM6˞PK3cZM@AB short/3TIJ3LR9H22KVe81AI3B0KF3OM9G41CQP8M51E0Q4PKKP64O51JV0.zBlob2AE6[n lVĨ}*G fNu`v#.q.(FƞY[afDT̖'ݜK}dhv~hSv W5JGGhfI;`J& A.wX8_옯}[ zd :,vk &>h=r#s*`cKŗOh&B<'TjK@$Ux|lWksKhđ9drZT݌DذQI ':XЃI<:+»̲VEocZ&sRst8b Jk=vz@6w;kD)\䋠?1?+[ވKNӵ(7NUH;k.n,DփdYg]쌌@dO^GKtGB]D_^Ɖmbن[0BQ[c{(1uyB_篂D10 VWv4x $$9 } =bI[AvyOdk kSiwaE_M:E\51#lDߑ=]m,=>1U S w)WA,QR G_a4s, 6. : }2s AT{U ss#viJ;;㎝-5tRSA[cE6eb95M7%M-ܧeӚȁו;_m)WfGz!i,c qOPs%=׋Zk^DޱZ?pJv944u*VtR<F 쪤̓"9ˁk$؍02 ſFVz)?(c TKE/ P[kw9Gbyeul~kVqt<6wiU:gIJPQu! AiUՂ# o;\`3 ֳ[Rf-cpa|1*W| {н=(؀Xs}K+'?{='|fP10pUr,[~uĄ#,U-Rw~$UPZVYR4M@7sn3h:+{6^4Hg$5y(bȎҷ.1p]tg5#ٚ3[Xk0R&O"?s` CxG>%S¬O*f%bkd52-ζ>cˊRHϻY6bKLe7Y6W'P)>䃡gѤLD8n Q[ |&'K$ֵaI?V_囏ubԷ㍇fJL (}G$1x>'@ јr0qN9scP1[ˤFN|MuU-hΣ=A/!'Qd!qDb'z5鰦D|V-ßdVXg ex[t]<["9DˋZ5+= Nm@ hRG1cSOL' [(76F"q0iyӦ#2w`K`E$X32J]TNx& tDk_f_u/kմ6tpIIKOt!-)Vދy53A~5]:=a*ϵ4 =~Q)+/㣲0ƙ291Xc)6vjjRlg`Z-l"űi7ԙ^xlWҨ@szQk.cߨ2A4{fwփ;/6h|WB&KߺA'~o7U=F p}4ʶsi7/٘})=olQDpKvF|TLq(?w>4R6_jѕ\$wWv( ֤*@Wyݾ;1QMBx++WQC; ?CXfƸ:`(&R\WHPҷYP/ 4B&Ή}rQ kC52UcY$v"lIl#"n7a%F;`D53#:R5aEDE(s'k":Pfޕ NZmiPg+<[kե=s)N6\ #sQ$vY;s&y[ KH \5W dQRe%7!gC^""6x)Ť@ vL ik^lnhN-i@ygEy g*cU/3 )S>3*.wJ5?o@*nF9;V >p3^WoKOA$5O2?SSÍplYG v詳0BpA)/5g7y/xW[t+apg>ǦoZz3dH;6;a#TyK pqo7D[Ӯ)/]bB㞞F goviլjL. #vho&M ෥Yu+a0܎aoLd y:Lt^VRJUt'bp#%Ge%=+;ûO­"ۿ;$mH:4ĝGV@Pc8"~wŀ[':WKY`gYz3dO_1CySm ޱY]FSBW Trg r&IIT(z{n{'+`uFj~MA}f#Jmv#J;kLڵIxr9Fk*BˁDZ )&t*D?8>[P^TxHސwB<8_ɥ)g|XJu18rBOR} faE#zM7#y^zJR$:B"df>1kR+` km Vծa]H׈^&*C0,1.ȣu̻9^>Ve]uuhR?׻NyD]G3BF{A1%>8z I~ϵO|HH@pl0'EbPAX6,x;׍w ⇺E:"-{"V{SdF׋u_YezH5#l!2&(JP_8#};Xe$eh9HDHEд'JoP f~=''g7`mPNC:ZBԽ qhrtL>Lj C .Gi5ScIX6WW_GsWV1,y%5$}]@x'pI1wK NL*#tjb: @E;H.YӺ;u\2O5UNWۤx`vG,rc{S ;Ę ɀ_su)BI-,9ՠ!U ǵ V-D?ÿ2(Pku9M) 睸-bJzh|FUn#RLf Kj CD6|ʔIg."U5hn5^L.vev.tPPn`~eCȽ߸T (B-dyPgH*lHG.Fy -fHX[ 5BĄ ŜS՗ȕ[.Mq%9}8؍=[+lҤ K>hi|C;OK>k:($ш>kRNK F6x+k9{S%K>;a]N^mו#RZ"ˋyb,zxX%]b'4cջ9Uo. oc#pR-E'ArlDâ̎Atu 4&:ҧH[n\Y(TPrh:<+ &SVK${jjRʨ?!Yf+$g+bHpDlU`b7әH\8[nN.l?*U[.ANp N>LkaدP :ÎwIM*I)d|>R%i%Aֻ*wW$V !{tZp p3#˚9lؑ7 BJꘒ#׈oEBx_kR#>YXdڐa-)٨NOG!Bvf\eW̳)'!3TY:ɞx@@ms}%uœ /rÚnrա#:MPu0̰zƍ=9n98E3 vg,lM;@}*:/ [smЅ?cZ'r-Btq3)M {XO7 jryaNĎ h#3<IWG8dK 2T%\JEpI|;l@H=MB,ZL\1bN7Q&7#u9CڰEt .PQ^໠< 0xe{Q%?McHwp_}B@ Ϗ%"=TyWF es4g_YkJQ?=c ^ygUN'Vۓ:dT1eZW[5TxX cƵU,Sgic(dH\ϣ cq!O=iTΪX2 40 -y5噪XDKg GsrڙAПK]ѿo[dnȷS:[TEЋt'Y6TcB b>"]t?n 0pem)^* 2/_ >]^p*滰N9%CEO;iRO?Y?ȾWE7%|gt`ubV1rʕx53_+#)-~0 J0Z[-4{#p,ÜYfzr \ (0L#Mr2Bl:*q4~X5g\xǓ -bc'eZU0!(Ksu=Ѹ-ՋgT%ҁ4xM!FZty€hU0ւ z1,+̆ VP.ER3tLPsb0Rj !`㠨-^q!x>}ӮT{0M%F>e}G`hĎA Y(چCw/6v\.M6H~h'9e]cdTZw<Q,x'>6Ao.2WڛlJjV6IKJX 3L4ȧU}">Rdu, |N'?u%Y[do f0c\(rMl{e&e^g`w>D@M I \\G' .:shzX| ݸMOIȼH=Y]DKDB%H9U 5n 2r7a@QБmX 1HhUoC a3 B{꘹xsH*υ鐰i-UIMHL~jFoTȁr) $3Hӥ~K d%KVr#8E/R )uvL$IŠT{; =-6M4۴?|VVoLFW,7/yb,f.rz( UzD+C,zmOc?NLW9vBIq n6Gj2hZr]wՃ:Պ/lѴr_SIJo79$ .7D <2% o4&ʺhKTw'ݚq6vXHg:BrvyC. &ۗQݳok4s-:CH[!߲Lҙ=;|^Dmʕ9 G"N=5]z#^K]#uwIn;~^M@f!AER0K ,h4V46zre * B78j)fmĺXop<|Gxl+v:*2W (P,lWa) v-圄՝ı7J4嗤ĆptF)&J._kS"TjQ1Ly&l3c.ZI5e`V2"sB}l?9Ԁ/%2SX=R.jO_/?\!Yӕ*#tOodmN;̐[b+l>7w..㵸tcx']Ɗ]831]uU7 O9f\UJPGpCE1".Ju}\ Ʈ c"Ŵ*V . wq[NM[V|_\ Rph%lgD+R'ůg d=#F;\YfoͼԆ6FϨ"A@}Q_Üظː|mMw/چ WY1&]9&xwO>)lj" )(~aE :KP$~?zS߉U>Z蜙Lߔ_܊a a#%3XRavɘlf2t|@Rǒ0v¿V4$oOneBO, 6mH#YP߳mwлcjog̗njmŽjSR4%B1743}7oB@c(R3t8>8V'0~|#k(Q񹝍!-b-< qdUTCV3,%*ԉ ϩU`|K1]Vʛ@J߬wOZ&hAn8=I :_iRM9{jw8w7bxzwҎ:-)O8coZ"}]Hu]|r9uz'3N!g<.DiH،:$z[p)yEj;:Z`jMÓsVX4*ʴzb:k~McYƠnWhJ1 _ >$3h!M0]rp2{Dj#, Bl9gz7-\h_WPOKpn?gbqtB7MmN̋BeN6}(1B=9G549*~NjgPHTh U Cj$] l3/4O=i( T-D'>"_*n%&27@@s%L`WG?F1P40L#-rG?w[<ظld@[?ynIO~Pqƹ}.\S3 `jE{~>`b74<;",UGUGm R5a3ӄx:OZ}sC٣ `Bvkߦ P@{M4VזJ}tc[3) KY+5(n[f4"1 FzxF΀]NC$Q=3c{ׄ?RИ{$X Z0N"AMr+^oE!ƾ.+^uאM,z ϥ:_Z!1QW*6fQ{ww,ܹ,0'\ @zڊYBqI6}r/o S?`=_ so/V9!v wjx$Lj˗^QMvj- 3$j?vFUngXhpo7.ʡ{i՜R?3i>_t&C!UۋtpB92bsmwmnN氘pI{5"LrR]e!349gTz/'`aS/9ɏ}$ KkD-XkHS&:U#q;"  zڕ2G_2kJ/'MsNR#ۣ cXUsŊ" +^&a"Kcןup.7cQZ%zPs4CQi *|8L|*a뤖똛۞`l.6ډ.\o.6Q/*{3C].vYI"97qaV!}JKvdG}aqʼnl=kە);y|[U_I *,5U v+e:'JORS`vY(I[ O!f"Cu$jBl>`p"m;G9QA+ݵa ??Hp`B(È73 ~@Gʊ~ӨPsw,{@;ǫb8QkX;;H}% Be=RtP m %Q/bGRc(ȭ[=]^D l+RFUMP ,x'<(/)DS.!RSf#R^cVuuyղ+0 bw*H+pdNOV}Rx`M<\aϕ0w2̣4۹gmIAF_01/U!F7/@Zh静s[4frx=_C+ce,lA[}"uQ@@/c1$ ̞[ءb ]I:lqd )܈&E :﹮ k F|-S 06am)sX-(M5iruftvu *bKɈ^9ZHLܒֺڞ`vÎAD/o /MQTViΧep i B=&w ^puos@ hg =i*1sju )XRrwnh'D.G 9o@:un6,$W^ÒlZr@%ZFfb>?}F_G0Zkp~%HqMO͞P:{Y!.m,^ĄkYX󧮊Rb7DreWƀ U=]-FqcmSS<٦rgQm @ /? x' &X]C0xs'R"WnȖ8[ P%3͕H=XT;cBG#JOxS|L^顾lKۘ=B+,jӢ’HI- {LiDSyg],Li8"j_`mOVs [|g` XBInprؑQ= 0$i#mMNJRS=wuهt?ڎH}ybM&0ѫ( -~~rJ<$&U8?+7$CtrF`s X< )"A@}0{W?<%FE/=@HH(Pt=8xl;J21م!0U'|n/Ĥ#/_KL ZSs]N]䦝 ̘5jkBdTBE|e/8ӢwM@۲Tz{m 3!oPh*ㆋ*4%F$ tbdOV[sf4`.#nn{D[1NGώs**S?Pސa}03}P do;^GSO;ɫ@n@RGg"w=ji2M~Һ65!w^'0mI >i@qpAJsOFJ2@)a."|))'jicD׿]v$%lYW͎` NHZ^(ԿM~]Ѫv/Uڝ!۬X ·TkcFN2 PšDby j謢20-S3_;XF`$a~K`vF!Y }rFIijta-EqdGv8}{LPK3cZM? short/61P52PKTP08M8e94FHN1EMC6LEQG41CQP8UD1G0716217P5VQCU5V.XMLAE &l[l*/~lKrE>,mO~ *_Oζٚk5 ӹ|sC+熛)8𧳭vf@ie5vByAϒ0D\;դIhBJL&&@E$,TJŐMW8eբk3PNi!S#)4EAYf0;ȇ?*(Y09eȀ j-g+̋䴀A/(:iM|z?e=$q6a+WMA\6.OTc+ބ3wUL* c;dN>MfYq e9umaf.ze'*jL^B`{b,S^Lpg9P.t 7ãHdd#1ێ~.o[ܘEN᰹ye'I16mid< ifR=H!<_Gy"gj}c$ٯQ3y#tKwhI ~Y{4yYQh6PR!H@uBy\2̛͒au9jpsXR&"˭@+`n~M:]e1(`Sz(~JV=t MEƮ9pnq*{MF("#̃4@~6i?pF;w-D6Ti.PK3cZM2,B short/61P52PKTP08M8e94FHN1EMC6LEQG41CQP8UD1G0716217P5VQCU5V.zBlob3AE'W:F8ܯŕgI:b'Az{ݹ"E!NX}zɲ䮱9a/|Փ G*,AdZvuAo3[_7.MR12$,ٟ=g~1qs~zs$o!9Ҝ/q'Tˡ2Z'D;_~o)ʌ٤oXqͥzM`HHm\H)M>h|+F/Y3: w`V? E,?"BAC{a*V8|I!\"RVzKL]ea#HUEF5nAT7ܙ{Qt29f1h=KF<'8:I=P ]Y`9p(>voVe (Qŭt6#[blq6)Frt0TqX@Ƞ'.()gzBaQ}8\#O*;@:ѽzX4G$:,Q ц]|4]f"&C0EnN :e3!`, G[%޹L";mªQS!듀.vEZrEWv nvR+z1>eJOLGr.!NǀJG) #5e=u"tOqr }y/ $ԍ[YY<#FaI kk>tN%5cZQ/wu4b}"" n EU)bpWE d_ɠ37&Ͱ{*x _AtN)Ǥ~D˵ ˴uk])"_SowS>8%`?ٞ#jd?Šԣ:`?ķecVH_&Ŧzx`>F^ K~v˖:3p{|Eu2%9\S =bAtJa@ Z_.5\1է4d59; wR+#Zf㝏 ;߰:g9沈WJ4LwjH0^"3JL] 7yx,*ԟ 4)XML ئ>)z Tc\Jq`Cs_Hd,h[6j9>&=0+P0P`#8 [{jOANAx:J˛YH'`27kk)cX:{ztjElQLvtgS)=DhW#ta^`ʫjoZMa}p#%VұT*" $Ht-O쀑=m3O,>BAY<jiݕt(w|ͅr_10Dړ?ik0?9/0RXz4gV8\ Y{RL0=XOV%!6#<^LTxG#stmb&UjMu\'8;6I'>%Cf|]+.[w_DBfPK']&PU'{"ԧ+ t"ɡȒIk\h"/;J^~HOtccQmm)%W'^}hО"sy$4` 3@~)ڟr|f! aFѫBh6dA&X~ 0/:3x8`/%Csu8J ĢMskS /Y~L=SK 2M=,٢qV7~J3\;+ [9uuc`e´}6(r^ w+ S)w[.4rzV5]_8xns1U;16D2>k@]Sk%#_'کZL}@$vaONT\S;8g'rw+ey^&Ƣ2NUg;hN@j[GmK@b#SebpAR܀ ,iP0##ho*u;Iߨr._Li*{h}[*kC%j !k~RvxT"#I1YF֝?'$۞Ct(+[Ԙ\ ӳZIYIY&`rc;|oJm"wFY ~R2mɥoS7Wk[G5-Qp!ӠW2<=muיIʦYL+ ElzfPgl=Gh5au앞M݃A\4g|Il7Uc )|KnYg&OlH9Ңg3%k{(teFp{r &'Y >|01a6ܮ87Aa܁1Gݤ:iTC3v2طҢ6)ƞtB^5En㍦SD`eЫ0gLžgg; 6ęT ۟mr8O:hN5]V^.b&"_eaKg-[ + S;]Jc"vH.Ig0:R nU*"}ױ!l\_gīR!nwy~$VO:VޓM&enZn;}Hۭ˝Z+$$#5vZH'uBkf($UC|ɠBN-k.\й6겟8 "}r?6'^ P)}\p~vo`/O+i2K9/_P%a|.&}na4^?ܽ*§Qa<'<n +l߱G6, 79nDXuH-3 r7%9^by4]NpܣFg:aH^$$۶]6F*_~0M$RTev c&U͈nӄyVs*7j_Ծ@*<*o](KšGtrvA$Y{ SzUr fnQҨYfSkkP));@,R|ǹE (R ZӢc-M}FpSzp'FW8@_|Wc]VdGAJ4zG41CT&F|4,8qzq,22CyJ^6j:0D6[H<:)S ~Yߎj;:k[Inj֘:g VߕO"+&o\~ֿ3ڳayӛV;"{VSݹZ4gV:d!ytJ|=Td^,w.BkCtG\յ:re T7Ⱦ+ȑxv3H12{7ȇٹ7,nf>:k;jp:!U m&|6xoT_ _Q C#InةJH?$H5$_.C>vUOQU{ @EjM7>ޢ`}s "Ř#}]$ʷDB(2o ?nrSvQH="# "ʋy\1x/ip\&hy$kƹlY=:M^K)4*$ϛ wD>#i. 13Ibc^ZL7(v6"\y4Shg!r7!~ :G@24M8P. gb&5ﴦN 2>o;q.yeĐwJ(snbkCҺ*M)`64,)Md+H*e;ujobÍ:2l݃=⫓qeLش3]oYȃyƋe _;9Gq0Ny)6XXo" m]̇ܰEp]ۈS@)wK^$Xq0څXr`34 W@2Xvf45ä^/N,C,zbMG9\ -OjYb RPP-6"Pq.(<|I͘e`&8>3Lլ^:+>II4 {Bzh|-%wJWkC @O2HF@~*X߆  9EVahePjq5De;݅7ڌ&#+2WHZX%Wmq;Jz! EkkbVKAOwR6+}B)Z5H |5+·$)$O_Q5e+~u FDy* zA%ԗG(lu[9g _[YȦ!gk6 5,VWB'!-ipkוp}{ &P *|fqC7Cl!6klL!Ƣ-W$y)Ct E35;е56kz -!F]A=[o4v!yqM.4M~~!x /ǦSxv퀾7WѮ8h!+@V_]';K9H!qC >( }rk㝃ձu0IxLW8 -W};ow RI{<^aJQI+.^FS eoGTrhp$a8 K Kqٶdߺg+HPeDRɗG19Wo1|gR4 t`Jo8X8׊4YcB"&=]FٚyHn{( .mV_/Av.i#x(H2q,/P%pL: NV~>9ژ38An[k)Mb_{DiW}'ܘ#2Qm3:&' 5l>eH%֨햘h6/V~ۯl cE-BOӦFY>YYy)r)LJ5Jr4_m^S)KT ;RtF Q]r;7<1iYu?{乫ɱI[5Dfr'eVo+ci'm?왍T٭HK|<Ը=g6Ef6 c(=Yto7V-ITV8K4*p~WCBZ굢j&zq A[DNK@QYo&9qЇ!{(?хb]W-]3}ƋG |x'Tp-/)߻7Xه~?Iq?eBTRPȞi2Vӿ*V=c|^`([G=w*;ΈbM3&Uew=_ۂs>:d<"V.]rՌU q3ٽ i_3>wl2*FǧgܕflrO$w|ї1 98 b6vIK8Gܫƾ T`ނű8ȱNU}" 8df}RxZcrbpPt`srs7TkOT@J[w#JVخ[Gek=^۠v>ޒڱ:N겙\joX*,rPfC-&ڌ' 7).U;kimYlѱb /0X~G딑= Sd ]MK5p9 ] OLl 5YEC ~Uxq?_g_|l5bY{PƉONc9Ղ q~2[ 1鱗2#IH7E r~ {X} _4^lhD6)B-x(:/y^?LyӘ\ mO &$GyhԑNBpLo%+c8]H+m),|WG\ \muG._LZ!gC"R4(&>B =)@YE8yۚ毕x5}Aq&[Vo\| ;HkmyΤ$O^t瓆A`o {kp!m!|'Ebl̀}"qF01P)!7Bd@ρUЏ_,yQmRڄS2`cqmv hAWevuǜPZG Gp Ցv`V4wev[{ i*B+rk\-s'_F8Чjl {ͩkߪ6`Vqu,zPktp 1 ⢱6s,vgjd֛pEݗ<ZqB%חܓh]CMۨRkE)|Vw snq#ڥroAgIE/ה| GfM%TA2ɫj@ 6szg|ѣ>57EՓj Wq"EOĊ1Rj=?+|aE]grikJgqk\&t`%#로s[&qAgew@ yPk_5Ұ57]QIZɭ@zDQrd rǦxdS ֟y-(Ao7!W!mS|teSNyM9%ħTqȊ#Xȓ5r5x_L GS:hC~g@-9{n):(ڮTr„w^4MShxӬAlC . uM[R#%8~ƥ"ٲ/떴]նPrp};/ESÑѪ")$I` G[B elL3^>wa@W=?0в^XfG.vG U;uKF2{!Mw\ A淝M㽨9)-{y% a7,: ~bT /|p-&t4`+Jio%G "D$c+trfZ=AEu)8T vl?Æ\ f7OC+϶#h_C%FGc֝J8)+2JdDɖR ! y;J^a=^<' 5N`tw;ql}7XHNn+ CZ:'Z6iQc_^owqvqz1w`\4c>Q|:8f{@9v=;l\Om24 ϣT"!Z6 (qZmuE?Xm}!sez8.єL`ݒ4rfʢ-p[Fkt4ZWWXy.5+SҶQ׀?*EamEc] uC~m"IR7CIb9~/&wmG{¢3QQ#?݃ӕf.yY~,ѕD|'ԯjAJNq'6$jETa"Rf'50}?G\ Ƈ8@W<9mPY6|:U:j<~^R0fi;z4(,!+_Y_'DWWRQyϚo݇ KSJI^*#<5pKE }G:A)kO૕<ՂD;l okZi16Ao=.gUe!c~|\RXQ{c!3 o9g0!"5wȂƅfUKi-h /VUxu2{/jCկ8`<|?^k%5#ġVU4SIA~?mr%:EpꗰeZd0s6 y;RU/7ެW4P#Ue7r=gjl 1:#׍&e[XSn#Z0 Ox͝=,bֈ`3m:DŽw#@gy5ORww$8]0.)?M3u꒹E"d]7R_F}~D(o1bM6 =q}ɏ<`Ʀk*~$L'*V:^{l-_h ^;ԇ|iG=_Ӄ=gh~Ũnnw,}pdX<5Z v+;dUuU֍}ۥ M=8Uw㿹϶>kw/Ie(~7^.Cdgր2LGح>*?@/x( 5>oA3kݾr5=L@?8 4w)ࡶ~ JA"2 SG0r?=k0w;/F`%d|ԽL7߮uhiûa~,SQ]J ΄>#@ص4Sl0 !b!=Bp.)҇u%$<[ǎB +@Qkr]@`ԦS VR\{$M0?U9z{iʗz,EKW#:on$2'խN 53}[5-2 ,8s6\`(o蝝,ٲ^ 8̶w<ʦ \SEP{t&r#ś]F&-s^>avݜ"S%|%"sU}1D R #q !u'_N&1ɬ\`8v@1s=?.SR0`RJ?pIL|[wuͪ$ZU9ׄۢ->e} !mej_EU7.xX’UmADJ; f[h C%>;tɂf@DZ_\yak//SYJw(45j"Fo7J1:Uyt ]2iۋ+c;a&~.6"<&H8LR񳞽r-zX"e/<*uvn/ cn3r^jn92,ܷu8vHYMUyAcÛA,6ӜV"(1D#YPq:Er%m\)tjZ,sU/,+qs%t8"$$}KM(]@^Cа3O@&W%T2/j;rTt"HfI _Pu+gU/3xo9Nh=\dG95+/xνP5 *2h5ic{mpYݝp:.8;3=QgVK<wBEW=ţK p NCfW |҇,m>S(Wx#^~r |o4&  S^SԾT[ng_ t !n-I|`yRq]vH&܄1jc7µvd*|.L*xL+FCotΑXzG7knUI[@p­Im:yuDp;aa CʉDnM3:3 &"L'k dSҥȌسJ]͛yy5"G>Rmv+w7.HMV#C3d B =Cz{V0K/P48=J%`T ^3R(<̇PO:#Lge6maVLc=SǮ&&;AG>w/>)>cP3SޫrZv(WFor?╲JnPX2/y3N:UL D!l7I3ԡ_n5Sh/ٶ,ůO}l3"+UN*m >AWAb'~>=70nє7w x,~Pc |OCcLB<ft6~ nB<:hx'S0 cv XW0y/Y;xcp+%ʘΟ`We zvT:%l1Eir_j,uϊd:wrNh\D>L8 9gՅEN? M$2lz*^ Po$Q5.gJ:nv`{fB4ltҡT@(+ّuĝyԪWv|R |ޕS1UNU(QL^:]_[ʟve{Mjar,FU4>[j~6nLo|1yK7 /TCMwyzȖ2~ș.0;v@ٔgDu8pz!P]8ӾlsYRP`@'r "WL`4RtK0;'z^XBAöQm7W|sdTyT x uуs`t6J_fX 4^~]iNJkuo%jY?>NJ wP+Vi{D7aKprqҺ}lGkM,VB?oAodj-vߞP={a1 y! M䀳TL~߹$;%M R+L5 }I˼S6!.ˠxRIq +Ӻ@2[G1G+i:#dlͷ5΋$Z,HҐHoE Ĭ K^,ZC B5h`Z\1fC L%.^M{3(Sٙ- 4>Tu(,7 mMNY9ݦBTt22E슛4gAtk(G%O6a`_/r\я 4\K=wTՂ_U 6R#t|EǦM@2;IlkSe[J>ggփQz(75ѽT,E(Kdg$ԺC.!}odDB3~݊ 9bu2l4EL^Q倻'W3tl4UPGH6E{d_(>Ǎ-eQ_0Z9W;1h :)Fwh[v%24?tZ HR|;Xğ I11:'1mf f-cND;'+i')C+)&2‘O~U&"OBB$VD-[١+|#&IA{cAgDߗZUK[K® %V岸}f0eky-$:gCKiUi 'df͍}oʓzBS7'[^W 9e%LnƜH]T AKs"3oh;><ter WTot@([ A_iefUff|9AJЇyAL1LNڬuRa &f+tVr}p칺,:?D Ao7K9yڔP!hҫj[fiE@fv|5Z0С/p۲12O!m"B+1|jA-}!'2D0>_>q"k]dÒ]K^':6V2o(YJ'u?/{i\R;ٰNVmhuGqCYѺ > &jMͿ{Lfh D7+6~z?-025K_FWIV0n0Ɔޘ-$J,X$+AX^вSDsS 8s j;WCT4ʞqe6H7kU'4^i%RB <#@UuN†ӊ1H9xE*ۨ!aGO7o4)AsaNv{J4ސOYjP*,ȑk@΃'Gt],3M4pS=Af@Gf;;IZw;$DLa!Xס 훲?[ Wʷ1ޕ4̴K0MuSu|3%{(P9 aMx!HZ!POkTw;f[Ww3!J[*-\#xI4Y;`/ҽ X !09OLv%+I= SG(E䱞fg(Z )A=XAYM&Z#I~–^5g#b--Irdߴ )cZ*8䍀̥&9cqV)iLq/5Oq7g+yQbݜН'1U53jG>SLcwք#+\up騜X*b-JGFI:ʑD**&lf@2#W}dEB |+TjnReN7ai4ișe^ U`VB{S1f/HIUA& -c[?nFWC0T@߸$8ŦLt-TFÈ/ 2s=$[f Y5oRrT;'8vLR%n5[i'"J4.֓CR LȪ w1{y{?6+? 'G\ެ-W¦=J(~5 +c;{ڂPW%k"3f>lB!@ Vf-wE>`yзg |*7eΰ¬E@<{f"A=ȳxpJ%3Z?r}iF&b'%34:? $M1盏xrh\M WA*Ū;Fa?~nH~8"$jk%尌5K*;6ls_cu<}~~[~R/QBT󼘴CPِsAWe蓝 qV.hqh4$*u*d\(CĽx U mFsD.esK`pкܤ"Il\1jY^V$#*˅{*#Ji`)̂t #'# 922 z;?Xҍp9a72AKM`%KSa >3< I5}rVd="{O2T`Bnfû}dݢc=#Pp; @P! W1:a_lێ ;g܎u=mk8y8~ #E |OlCcX1'#K(Y_bY7рC9d[>`nO$ I-oDQ<?1V3n(@O%‰0_hmO$)1:}Hg>_VG Lh/"agT_zwNTybcZm$?Q.C][8ʜ[/!)6Ɣ> kWH0J7m͜hWTxA`p`'Ya3wM‡Hh9& IUVGYvGDV2<4ɣ9,lF i xf=hTJt 2?Z_wGJCJen<,:w+pKG{NʛJȂ`8+1<);Fj.+bvdn"DD',N,gK0cvE8XCɀsrifY;8WiUfcv0yR^[#₈u6*@E3]c>V5IX*l3Iԡ9Vr Kaw [ ?td YVC;vih~FcV3h3 /6B}JDuM ri 9B#wH C h=T1q'6s )rxouHP->zheR Ժ8֦~;YH\Vrrhf{*iS&@,eĢlwFaҟtƿ" hf~ ݁% &OR/q`\I3ËwsW0Osm[y)ex ږ CDAcd(!/-f5n8^&rhRV)<}e-w?;c)] *bxR(8򕶬)^G bHpq+ uf@3ncu΃vN^aO]fЗ|Tm3%\žfSG"i>`L]zd3+!ȿTN+F'z6MdG?7=^p'0ͻT+<”ظ`P,Gl4[\qCTXa1e L‹,u1$N\W7U = I,-m5uSk\Eܜpz2|l s8}Z[|-sg9 nQP 3 "Fc(RC@V!M0FF * wYedήKcִ߫Vy((XЕR : ;"Knml\wT:BIYHN0lO^Sn̷<.'+G0ԣn'9Y@֩/*wE[@I6ggZ96KMdKUܚpH܌,ۙ$ VT;d SW(+% HaDM0p?)ntve>YWJ xK}>=_ A7gba>yDo>{Aҝ&\(çeb Du]]We(bUkT/6%-. ^.y{h/ !{`Ki .t能Z˵imeº=؀PZz35i_+zQb[Ђ8 |9f 1M|m)u*iTo/Beٴr,Ju_K<!Pe2xu(by>_rrgO׀ x,x6%~ϩ=5 Mj4H~*'^-)L s`Ch|:,pwg%>Eu ;m .\xeNX@4薈 cQ(}fߨIT2V/^!9z+!YO RVS^33W9 c^Sr_k=F j]pF{ *]+>7!Y{cudYWK@Ο :-מ/s7Un ltp7阑Cmmx؍^|΂ȎZhZ||~jDEg7eCxE8Xq3n2 ;~ =Bʸ)Fr!M^"\M-ZQ!]@% #*!JJ^qMb.Yjs4{ZAEE| w0Onރy{sFX5+M <h GrmeC%l(J}Y'[yħӛ.<7|c9ygҬym}f4=Y,{ZvrFKҔEɻ`D 4Ί%8 }FNd0Y5#hi553S+dso;Hsk|MoRS8t6 0@k؜~ҎCn%I3"#й!@VJIvLkBDIkT #icL.cR Hޚ>>b'K&|;.aF ]o(Rżqz3'?f@殸4Am_fLCyurs u>[,~~*N?! aōlicNsUn u|o\.Ogg"t:2p^}yY'jŎ*=*6I/QHoB8gyG6xF;+|ƙN@yq祄nym<:xᗻejqD>DiQ2 la eqJ']y+coxg#흏)ch^ !t H碥.9hu43xj&' 2^1 7L?; R|%71U0wtWg+?ky2y[M.7!>N3lN0Y5ÐzDj?8dkNIRǓH7s `-S& !?’T$b;YZ3;\36L *+{*Nd؇{S^È\S3/KB/ʄH gU;E18=Sw){d4|{ < f!<(M9I+}j vNJ)e˴8 L{t >H!$ l$gW!4\ڡfmF#hl> օ?7chT,"3- G,'q5Zsq8ܟ\ʶcY5_53oѶ{1l-.gE;gijԧ:xa*m2$J=]FP)[_#jyc\vU8M2sf 7p/tr{BLn6Ӎ `'|$*9 ='0#hX IQ6.+#7el{~x[N$Ǧ$'l:/׮ a i7%]3~˹Y qqW^-34)D#݆^n/F]). |Fx0DV'P3m 16vQ|w7aX~Jg̞Ap0e,M?t)UQM9$8Gv-fj%w]P>p* qŔSBiAKP:Pzt- fߣ=mh/n%P TM߳%Z]$V6(Ah)U˞y^\ŲEyQeJT~ 9(U':Uzu-[>}<{55@<3u{mҊ{7=VL^6I ~E2#I> ZE;o1;%*+E bTj`beL9Ҷ00)ʒ4/klza$+Tr*ME!m4f. F+F!nHuX=~B}E2:ߟTLU3,U%Y28U))e$e<`5dKh۟k-Z,ftƖCwah6#8VBC,fzǻR@(Kmn FF=^0o l4Z/ӒҎ\ё3[0/T(քteUer}>3ADHcďC/K<ȫ5!=6pxPMЛt~KGtKwqlg_I& #9o,ppKR6du^_)AX`8:ЄAK`k}* ,^*fK1=RmȮ$C$5g`1 Ap}T_ qN6yIg*pި&`gdsEe4! ,>ppkuO93k̻Αխ" )UWNn$^ďɷ9(Z7\Birh~3Mh,1r}~'P3BpZ/jڽ͚Ln]=WV_}Bj2NaJTVߔb,E*E@з hx>:|od#vS%)!t; AEOО*hnh%MWɌs0~n B968q"Wˢs()KhQ_},~)J4$U^%}K %+4~ j!4K0ؠ‰{RB͋1MltyͭYΓi#Ǫ]Yc xv9hIdyt?ꦩV}֜ft!ɆviƯ{UtRLu*U!߹.ڪ俯-*SXi잁V7֒t٥>qh=A4UJXb/+wNew%X,3{Fpf"U\ġK1C:Au9)Ұj﹔h s[Wn rM;?8iCprpQ;v݊`AM&ƭ;?8qE[GB` 'c~UI{M0Zu&l" öW1{{&YiUPhgs@UΈY/E@I?J&BJIŤZ-`[i>`+9ݷދޡ&znfS$NVmotZs`&W)yW -6A(H cyl#2A{Cm֛+_*8QQehQ~- A$`Cf|'o)aCGVuKV@:Nww/fEgICy:3ݟR%i.䁟I?زU(X\ q'4 cx!ܲB7J$M jxRdә{W[o@>@ֵ_h|3{0Tw*MsV #T8s5v=.QńuzljͅVo3Z_qe APؘ<;,Ucy.,C7{Δ xt-(sFpnG'9nq1 4{lpN[z_?7ނ0k)YTNy/xUVaKe;>+gc,H AHKڥxiQI! AX\ krKuF˼y9|VIQ+hs~c37};"ź^PoF.z¢?t?ʡMpc,. El|};w۪_aaiǬh׺K[ؘ)H$x6}@` HM(+m+G`81޳FFff~ȖLtN.m;ÃsnIa.YaQ?>'L-aEq{}@H H,4g)O܊e)HwK3c^Zr N*;^y2c0ٷ)hћ[4[r c-́ 3Av5K[A|5[id+myԛc"}%6(Rc\=ؿ.*xBmdM0K?O I6r,m&/l[ y+;SzyQ8[S⮝d wˣw*u9b6wi%j 7Ś%Kp}2IWk:;AAx`֎HAG-t~.J3x$T?N,D7tVd|'-4T>V:K׋ dU?fGW24(UFwBfʐR'5,|>CZ`Agv5"0Yv):Ķ8UzA|͸cVFLgYj3*zO_. MX6=&5%-O=O-$+XtpsӒ76 j/  ٍ6]ьokJEeC*6vFx0}ۛ{,,93uJc.- G\@4gzȦ Z\Kbl :b:4KTLVhPC#R+WKeQ+^T7hpP8ثayg6J/a [z-$Y[IaXad?ʅcoE$4XUx5=I[%z&-ŋVʀB{=95?kgtP@ys7PE`Q%kÍ~ 쿈caΣg,_IAi~kzj? HuP3a^XGVo1Bua@pಱ=~!->uާ:Ӈ/˺j-ژrvKiZ%]+*~"V]p'eH(Zu؅g눺Śa ՉZ!$C!ݣ>P^%>.\ 5RjEcI=/aJ ą -nLs߶$aQu '-K{ic=.BqC_'GNf>K^$ cU ] գ/|܊FIZr&[֕z0}7r_cr)ƘqBpYؐ-5(E u!!F**\&^Ж*~80/RW 4s\t e p|l24"wM`Z%eS ;1;a*ڟQnJ^R%Rf3'^rՑzBCi!!DbRLjk)`Y=feCфǸÃqTc#88)ZswPRXZͺlJW\> J+51(:W~!xd.R  ji1շH}.~PN}X KO?<.A?ǸH0VhISC.hK΢;:a<־SepԮ\莕6'z#6.+sYh}|MĎ5, ߾^bG22OuH W 5umaq7~LoNK AnD%ɣuYp=7)&t@28~Y."{i_NϦI^ݎLJm6h`)H=̳, -%3n5blk=H ;!MY:a6Zϭ;C.dlT+%p4Sx;I 5)@|՚&{&Z5G'+jEUga甂DrsѢ%%ڞ-Jd iTR*EE`۷3h*+{p ZVUUy iwf-O?iN>(BA#(Cu`D<}?}e#W\M% QϤDQ0JdKO* /vXz}׆gg'lI$WͮV=ˢACMbs&w~;"^cta8 &PeՈȃţ̎B*;)VmCG[ݝO$ A#K31Ƅbq 02GqK<֗Nv?=-wPBQwOp 9sΔUVutp/Gw;(cU{4&*5?djv ;A||rMbn^L| ֫pt!P*8O|}B AK5"0, O-EhR+~K;-@faT`c|3;BRp%%/$H,>]Gf+Mьs2WHsfx#c{ eYTE0Z 7053A~+9e~qEHaR޻IKɳF&ӶRug͉*~Y^C *_{)Q p ׸ͳH 7>G kI:2GO^ݝ{e_Eunl&l=XAv4MC6͈=]$i-<a:w%s˞W .? +ќh4Yb `ӫ$*e^T_`b)O׶B_xE~DS/쟶eyGTcKa4uؗպ~I:Z +-: Ueil e 5!y %PO] @xGZr`O RAt2/]0G)9XW6wߨDE쩻YmRw9G!> 1 usM,ȈPm-KF חzO'Lm,կIv_D, q6ޘPbr jփϙxf< cjT^ >/jI67$fT)&?t _2&OdٓjIoHef9ǿ0%)MF,WM] Έ(ފPWvLYl+ap1ǒ?F`Hh^eD‰^QD%}fkw0fN1(>Pqle%@f.ѸΓbۖ9A1=n>#w>_6#E)"*/.ZV\T:Nv.`ЯGJ;pt,SӚ2e!t aJz7i'#NFg ˻(;2*IӹC)?!UCf:*LH0`t'g ^Y=#Jӛ&<.:3wհmTph$Hni^φ2kUL1ąoa7Wݼ+ QC! Pe9+S\/G=e :Wq1(]ڛP"]C r>VsIR<M+_<3oЯ&w: 騪+htV3m7vR)U!*1B2;ķrlM,PK3cZM? short/F2K02JJR28D2TeE19OV66AL1L2SG41CQP8JS1C0U6O21EI5LNIBVH.XMLAE m#ԣ !#.yS1-H\Huqz*مv@uH&hu4Zխmo1vz-\DzwkfU>9kgIL~dF3Yi=8&t?q-e nCn xk& MePmfi4~4@.bL>oO<*hY; 6PW&Kg1m``5ܓ:TRmrVo^߫-P'@Fm4EO9Mϲ$ )PK3cZMB short/F2K02JJR28D2TeE19OV66AL1L2SG41CQP8JS1C0U6O21EI5LNIBVH.zBlob3AEJ3U_e~I9zᗪ p3+FpLh-Zxnْg{FmeFU;٦l-D[)(}+l E^a._.8P\%g$rhUL!1pi?ܰ .@ 0Z >,am`c+)ci8KoqpK3 4#_I5Iۀ=UNSX!F}Zb'M1/P_@%gwKprUvY#O0X7l /}WZ3i3l"ۣq]bj@gE0%J資P#>U6DiL3-@EӍjf@qws.L@eY ıaA_ 07aFtA y<̙A/x5OeivtVr?QQd}]S0 f /J\+g^+Aw]"7K|wO Ȥcc݌R6#ԇx;3BL}tS 3fsvѓw(43?<164PPVAJ[њ]CӑVo[i|I ,T?bt@ys-H~Ȑ"Aν muU oUhpOE6$9YETֱȔ}+mygƟvoT=dh 0dŢm!X\͛Wsfձ QNhh-Gv}eLƑM@|r#lcT(vَqδ\o𪇼_Rgk: _5g.v#68x(ƅTm>G}v~ue6Q#P[ x&{}zPxqO*t"EN(/pc΂CIRNh[ ԰:%p",Hk *C9eoZK>9֖:k91fgO:ҚGJ/խ 7; f'6Ij3$qJJٔQ'wY`, ԋaٗr.B6cly'7Ld ÅeGR48a툵/ KfޏJn9a0(g,".K8(Ʉ+ϕIOsi, " r,?=AqJ8j'R"H;;U7IܿYb^^nUg:&XUk$O %-7`>Gzk$tz-͋իx:(L7ϢCa+=D &{&EK >^~ë,u9nrۏ F*]a3,5nu^:iV`*SZ;,9C}S阥jF]D/tMKkEq,MR|]I4 Lu %~(oa)CU_u;'h5 9³@r?3FMIX2ĐM'кvjjDMqnc圴Cl]'8V#p)GERo7;wi_ |?FA VsE=6%3U Ճigă%s I]GQ}ӎk 9-)3Ȋ[U=-j=c]xldHf]Z0D,whCfF҈ݘ,_W4>Y V&ӑfg>n{V'x׼E<^#ݛ)ԅ/݌x`6'4zNU: w,~ږoaoKh[cSM@y}OPK`u-׀v0=O0uP 1E*BH[AMY3+qH2͌˒#ʜ`WR4m,En1;5J3u8&yϊg^Cgxl")О!_{Šf[tZ6o$[AYz$a< WHmQ'vqtߎ*#U 7$22m s]%6/+7GM/i/pSBy8MB>^^N[LpKv>bRWq-mfp'}ԎAҊ'mz\Rѣ"9~EPn;b&C싲>"zZ.k%Ng tǎ\m )@Ta p'̤hSʎ%pC Eڻ=zjt #?H&%)^c.iŠ.{`f 18u^\š詆S!nxpiJV,oɫE4/S+{>q=;2T3s *6/Kr9wb\ktY7mJ9ު^m^q=q A`MFj 1πqg$Tan>i>kϰZSf BְT I RQU'`xmc{_) za:aҦ1f0&̒x8{.6@5QWb7gM :: UDC7 A0HAв9~d"nh wS$)z{4ʅ="Oia{`ݣWߍGQkM"\ y1[㠧 DU΋BҒ3Vgղl\.R?(j\LP((+Q&z zmBy;7` &M.%&q+^ x& ,`U,Tb@8@)%u2v?b~d rR]퐢 : O:~74DY-<[.L&j'OvDnDz' 2k.I0b |ZJ}sIw<3<~sg͓1mq'k2T71cɜh[y0;7ܹ\9EV(9 k{ɏnqi딍2졥-TǙxխuW%JdbVjF7pO-\P ,sXe D%h(4$G ^pՏfyYI1h tOYDQk{)%nWWI>gG6? f¡,ckSEOy>G3vBr%WoWӍ#WiUO? [HzȌ%3:Qۀձ'YH-M"h(`SᵉִF4bp/'r_'u\j,,ވi%m?'&H6pt>G;t"wMBc6>1;pcJL֥w" Kb{ aBdwƪ<-EjA}}!xl&8QBBETyׂ^4Y r<~2m ܝqyVEb4Y}^GKk3Е,FZ/h31&N1!u2BdNj Fm٦TX8caўl'H ګW𸏝,2 I =ˌ$Mz^atBN\:sb%F9B±gtӴip-| =f ,;*rxsȘk:O佚cx*;F٬1b$^Pv6t z0Y3-WrZ8SddƩn}>unlJ%>z^}KT}WF* vяⰩVN"oYZ (-k]`$| !! L~E"@&)?@C1WifDdf Usl93 -v8hp$0`,vsTbd=V6]U)/=%a(7:iN3Ѧz)eW3~,<^Cr6Joknp\LMw72cNCyd 凛2y K`ו-"T2}=?R'\xdaF|dxM ^#8AKEB,V5pYOB]@$ O^6b_] [=?z ! ^-L0 W.v2#U"w`v] Tfu!>vi>@2C̢lSnd5)[<; 9}I Qw=v߾tӗԓͭ ^1cܪ۔& q=e ACжؗT5DQo~Қ'sheܶ;1LA0T1>Ҳ$h4ԴSÕocԿ{Ncx(g/#'6)] O̤ۗTߕTk>D2‰k/&[[Fځ zEc ] !%a}HA,wBGl'8>lU$ Er@E|yKɑRf/yBNF./><%MTQM yV/gD.uB94T@.jKC1;x[Me,f/.Iن) ~>bz^Xx` \!"q{muko~ԩzdyQ-%{rɖj{ 'd~=/&^Qo܄t/0Xu5 5KPqR\dP*t#ޑi.Y<(ZjӰ.}aZw\HVҍA?@(Fy'_n]귒N&%QUv߾h^u*LLB*gBOPxܬ">|meN;"S&xch23\,J[}>3__l~zzs'ycم{Ԯ n.eď>d#ć =tUvn}{Wqs4ĖW.P+须*Ulڑ7@@ 2҇\0Bas反r{  1̥%r5uBx9a.=idTrNɄ"aw>]WtM -;]{8y=jIΛz%!jY$fQ L1 #kKj}`Jof#\1嬭4q=N_.W6Ǭgxp*Os aoY鑭lykM"홚Fpo?n"Y41٣Ō&F/|5*:|(Z@?xpQ\Q\s@XbNgG#x6c$WW-SfUj1u[Q~M12tBS\-Rvi41 f:LPàn`-\Jx&g- 80k7Y-3D(B䰺 x~Zr 3=[zkv-XlҴG>YU,VY(#RHP@h xGꄗrI؁ 1>@j&( ,[įJBKG-.[MIG[!KX>a#rv&&U#p<D̠V4%{+ ~,xr46)Wo,5:3-\a{{D'ZBlntz(}2c#c2\@eE`6X4o] w_&~e(z_[8ΒFpE]z_\Q<$-Q 1K{_gM; nc+YJwS5י$,C@uJ[|Uކ8_xmLcgDxVNA,kA[aH%.R&Hgiyo,d$|94i[VbVS:SR\ ~#rtME/-'/ <;7bE'U `\n[KhѺ6Y _!BX]3q<>*"m[C~,/a_˪_7R20{gH 2-L6ĕ$Hd@_ #csMܢԠI{qB(c2*x#zxqi ?g,2LpG-9᢮ T76SuҐXDIF8Q*om?{K-ڼNDmn!c9aYs ( \M3k# $ K{kTV:n;.3 ^u<(;~dLpĢ_$sPpj2DWXԱRD=yT[k]Hh~Eumnyy}Oe!V LX["X_ᬻ=0^>~iԩُSƒ_ZB"`/yfXI}G ' Df7|j ԭ]?(HCe;K.AKj|X_Lq[e5+c S-(~"%t9U#]%a [^-i"PWv3C€"Cї>͹!$]ByJ02psLq崸b%R;~ $zn􄢅ù_O$fPHʗ="61jբld;u}iXbF0((vD(AȽ\1 H=O\M8JRGy n|!gOsQ~eAJl: ظ\vX[^0@@"<=hY/|k1x>߉0a$-ёP蝚l.o5^x3:~[:cO ݏ[_A_Qx[ )$&  %Ex M|0Qw)(0,(fwj")9.Ko1\QVM~&2g> GB`L2(zqϫ]V!J}5C6<</.VDQX" 9>klșWT-s09j\qe C݊Ws'xaD,E $kfXgq+Xߜ%:)@>s7zːo1/yt)q3ɘ`%5Hdo.](Rsh AYw?#/ XdbS PSh2>9t| gԂvku |="dҤҬMy(% DH:8۝4AZf&/c>DBgtI|aݤ*<.NĜZOqpwƖPi3?? 8o_ۻ1j)E*ſmh1|-ΓB728^h%fw׊Liz CO:@oL #*#&Go$'Nb4Eͺ4-X^x':KPGìr*BW罻ci ڼ&%LZZG0]jf{=kUg)LC8\~oy-v $aRu<ݟ8P*A$K[ ƲD5-ΉNN lKzжزF~thQޤ~QvܓPUWYk,ȧd(E+ `MiK|BQB52p.sQg" ,+`Wc3LBN|PWHH HI~VY $[CBzlok-KL;0)zD۽a? 2* [0LY/dLozf"bAUi$!fooĚ4ena2O]/ϵ/8xkLI2$`Y4K%UJY )@J_4$%Ҿ<'*wzׅ&(KS\N}5ZB-e_[} j#,Y9ݖ(&! wyM d41~t 6B"u>9d[Fy2a]sP>Sr5lZ~,eͦ؞ܵPz>`^yV`sq(% BѶԹTQ#@g1ZuMn`poc0MĬ;ay%JEZVr.Uʝ?BzC$kQj9$@=,3.9c@e "ym2%+u[U PHL"yVnxN=+^1xQ%WF?P[0]Wp؞y15ՃzAU)Dc^)@lA%t82ZBݾF6dS<A =;*l[k/iQ ?rL(/&͡UmſެO#WCiA;絧5\ψ(8Bh#X(*S9|,o}YZ;ލ_e#n}F0,]v9ze%֢qWhS:"|GGznRk/xhЀ^[PY&:x#%+UV -aA9wFv.W %DՒm{%>/rb4땅pe#d^- 8} Ww;P) .Je̅ff<םzQT\Gۏڕ0O_"0;c*UR=9@Gץ0Hsw̉Q6+0D.F\Aĝī^ѪπmוP#ve=]#:g"[(Wīs^~=1 =N:kS/c:MlTv0)oThVQI8K!:=osOG5JZ]bifR@dDV`EqlTL+d։?8;멜!ܘOtǂlJ1qhFgS%D-,:@ՈzF(^^.4HTtM՜zzu|uH "ϧ86vZYݞrʥ5 h.I9v';%`,n**33e[)Sc`źק#Eׂq`!B ? b >w;^8*;c*ٹQQ(t maMM4.9sg.rT30%l=̜wq AΩrAhQBJ ~yy,+W7-R4|<&u%V"KWaxv=˷1G)M ;SŷfYC/%(C|* hjyperyT3CJ%i23ݞq<5,r_ĿwӃF5I,*sT_L$i*Z"{Ό8栿P_=x԰Y៭Nwb۞S`9;+Swj6I=/ZvAX:K :'͏ .aLx<%qP$슖ⅭqS3'Fv¬8~2[D5y؞xt:  Ӏk;gf(D aܠm ǁP]Yʼn?5W}Fq^+5Mﻠ MebX\,痭Yo"8 pg0aҭoI>ro( Q0 Yz|b"SSN3%~%(s$ gs. h!^.M6w2sdI7qufѮ  ڤH / 5u`N\/Yk׼{ m`OTOgt-u׭&.<$ \> 猪~5AHP˳וR68g2"d6Xօo."h1p<Ɵ>*J?TexiMB8H gU3F s8#\ga9N(Ms[_\?%L/qy+IAGŹ%cW[,;?&^K!>h[ryq< nEi5!M?AHI(WMP!|[T_×쿟9Ƽ,۷1^;qF{ qH@rTiqxajEn{6o&7RgS|xv!}&ʘlUC"kNlZlb<%׋(ߢzDY]X Y;oɢ!h) ~$(d]V eJ؇̌CjV`.<(s~@;>/I^A =v[^j0f\lܦ~v6h -Cd ݚ~_P^.7AKL#UncA<3 E,?rqauP3HE3?꛱ &:h?ccU^NwNOBd14] : w<)־̭ ѳ h>OrT<~=;Ig]0xSčӧn3#S0x\.GaZ{v@@{Ɛ;7,Y!1)K>t9eDT| J}~9:twڤ=6Є[ ^VPZxٷoErM0HŠOywc/'bT|_P6VgIdFsz6%N?v:*5kg9Z6ӑҨq3qyc ~Js׮؃EbԓВPQ(K yWզ{Jt^ywv(lfbWفFY*E؋TbMN&)½ny|J"ɢE\U-Z/FO|eՌW#7UM>Iz:/L?e[+h݇0%!+.,5Ww׉7*mӻXBυfL]}h4v.kpt=F)/|9er%v! *cS:DIkϲQ:8e^u证8 L3Ed@3p_O;~ws<)q^ڦ\}]2TMA82Ƿ@9L%A*}yzY 170/$)N`?ж xfDMx|\]{)01^FI,\giUw{לsS ͱe2tG;n[Bc򎯵^+rLdzB~\&mLd\%lBnd0Ea5+9[$n ~@L9ڄ_<8C"t U>cf>gQ^u ["#"> c熿3fO/sg ``"nAC;%D6X8hHk=ܡ-|2= N9`bVCyoߝ΋\W Ypiv"9.B7؅;+flAYRkGb3Nm u:+80= .BZIO%!DT㸤vAa ǃf[~4A8W&S!bK"WtFbR-0z(7 /<ZoBB /PxZ:})`A>rE0|^ս,(7 ]|Q,&DO+ol)aq1bojz$^@BDQ7AP2a38bA7eD/8uOR0 ?(˵x8lU`A08$"RIWEbnJ' VrW *Z2 SIRkBݣ!w y,1 I5Uo~E>-Zd<jLC XUi {D$Y1LwzSe$}VX†@L:~5V/Mxh'v.MGow/LTy.0ÆaJdUݫc*|#z79 /<9ckڎ2a'EIP%ĀbT~SM-1(9 }UNZxOheꌮPݍH-[ې1K9_ (a*p0Qڢ kي>_E¿eA5cRb)þ~v(!%1aG)eŝ!\ԸRc!B/&-u: vXb\a@=J(m Xٞޅ g,4rfw4 *Zx?S};oxv<gf8mqc<".P$eC_.۲p?R,q3pH/VB;*K;`I!}UQ{,텾ZeJC?'n _Tu61){[e\F턛/oexAIQU-]FI[AotȸCkNC{EFEk0 ."O;c dMda_SX+5,ӹ2E)\P]>}= 60igP}+S`̬!sj}G :-$ى/r!6^z[Rv 9k R6(%#~^ks=5tW&n?򍸣X Lۧ{2 5/숉 J.79*ng NA*UB9`D~4?#k2 Pɂ{ŽqOC' ԳZB Jj6bP(}Ɨt~4Ͷ?&[r@A'Ixr,f pA ȝCR06cMә],⵪c}ܫC/i46{;#=G:A2b16ycDm{ItU~B cE[Bh 6_Oߥ?֥hpHZ5\:> 7% mB.5=í+9jgDI ]xԐ:G# Hk)J w}N8r%.p8w~}!7يgg~7%z>AկTib25ogR˄xy F%3wRA皟'|)>3<$j2顁N:\ Mzc/Nl.Qk xc8QB ^3b4},rO@x

R1k2/?8}gߖIhnޭBKs]_9xTCr&2Y& @*[a`r|a@C4BS.k6V{aeJlKK\X }lndw[¦lڈ>apXjj f|]a^= LF~aF1y4-0v7iF,fŚ(YD]b MwƃwխŧFbwF yMm{kgVO / M® Y6E쒭%LX|sv7,yd{ߍ3dew1t^ A̱& m{y7vKG5wғƕRs>.C2XP*n;zLȘ 0ey90'Amddy1q"_o9^t*4Sv,af V4[T ˋp '_b-Cx#9+3Wmg?ErZWG 1 'J^RMnM,d~\I)9w1@XwpT${5ԇueI~+kq /=[Y2:c=X2-*F:ދ0A˸?ՕSxT y8aњcm[G+^g9r7#\?:< g. xB.1P2s5K?$aOCuB r*P_EbJ4uh͋9! |K`ȓrN*ܰ-C>h6?0:EjL[,;CZhfkd6xh2nGj"D14 Ӱ&X?.8nf S`E06w/e?@—hKPKP5f66W.QW&xmT.o2]sϝ'L@;6_hJwPJK&v th( ZT>gm1^zAt9]A?P[{ % ̗*iBݺ~p¡b%U#_ ~Bz,3OdwS I);kw_}:\|f*xiaRU`7`3/PՊW$w3\ PWPڈЪ7ҰM\S8%Ncmkɣ,hI̋(3Dݎ)q/B)PׂX3'EY8K<+jȗM$}jo՝ 2*c@_ךEmb Y;Ul=ό,T/vkm*\Jqy_< W-ZR"5˙gSO9Jȳd\\c˹{ufv_ R3:⫝̸!^`k TƘIɎT\ZWY7l6^33Ū⟗]D-N}b~U5ZO  aQ:N~'ں>$|84aAcpbSw qڭjoy ^Wa+z1:7K* B̈7 M8=ĆD-k0^A[׉?3>|Cxͼ{Y]uB[(וW6D@rqO4ɐeg?E@VW |j2rdw"c#].q-Y6>E|N\N>t{/8S[+:YB~aaWyY{t߶jyV.zebpHIyp$vk9"yT#JtESd0ӏi[V[rXP>=#w _BL凘|τ1:ut ̦du'X<܃'@, C n)hT˾1{̱I $bW̊Р3 \}=^ I6$.5==k0N)&9ThM˜JhSb9c@|.u% :iDV㋋|FF`*G!F 9Ogc1V5V7_̫/)#KL旞;TbMq )tfX \Cv,k#v/S =T ٨}b>rPg͛3ݩh %ďu yqF-tˢWO,O2a'K1F|X@VM<@J柟\יִ% +Fbpko$#7Wc}vA+_|"";k r󹕚.њ?sjxx^qw5Y̺iz؂BN=kZH=>DUm#OWnh˨(oQ΍1e 3dQ8bFE懽B?žޑE]1T]{K?DrgdIr{|Q+U)YGH=1a^z) ߠGO#mNũL,-wO'SXhgjy^%ͽ7iLKsYqtnj\ZZI +$L{/T{w-4dE Y⣓cu?&A:#: ,bG8  V/Xb2⶿?sups0G`Uv+bmܨUfFj_WD+TL݊#c ~„V]Wdx<"\tĭwJ8/J=MhnwY/*Il,pdzH7i2U@}kCTXfp6'~i ;#^O^Um% >BQ Б('£%: lr3M(zJ弬s:q)=]xܬ : ')=e!3^jn1 !#f\ծ/Ln|s;Fnj4 & YDV3t ;H {1yԍKw l=orn_60`FYW.uL}gz>L/@T{Dp."j^(V r UȘf?Xxܺr#!ү{rN:x[xh&}5)_?Aɜjz@!aOvо{g𜐭lθ~}C>5hl'X[Mr=U+sc+?E^<0(y' )9z!`}9Zhs{@r, N -Z 7s3v = /5Yvꥷt3%pb g6ЙU>U=£m,uȪQ{)|$Qlmh4OyzXg|{6e*)"!ENbK AӠOfHiwvfM?pt}ڜ's [j$ƒ|xxǛc;޲ŦQxL%g Z4l4i:~'=7L)G$^0(|}h=I"gNwS -0#wJF8&0ZLr[cqoVkZ{9u]e  ~xls#ixa_z?a##Ɩd18vahK\9A4!ћ>7x T%71%~A4pg?_n9Kۛ;iP˕};ʅS 73 ZOD?(-_Cy}|&^SX= c|K/Mz`]}ױŠ5,?sʝc?t#dl¤A>b$`` F}T{z۝eUԈ%F81Ӎ1 aMSE '`l4*%b]TJR` ?*QKNЕ2?$w AcuJ"i Z ,a*,1^zK''J׿|wA (` 24Ur&ť\ ?;? ,L;m9dn8))d%;;dϤ}1v@yl2ZDI{҅M{.1Y ,JlΠLKxto6ظG*Exb,ǍQUjC&7Pj /FLgҭkpK,@.uXCA[tW\Ӧw`s]0$PEf?xvQ[;rXxEa͔Z]RHT XE>bj#ۙ޶h]Q v~>HԡY g$dȞlh~q~)I)^\@PRg} x `7;wüh.kdHm&CE}݋sT-zco.nglPbzZM($xP?7gv6p, bU7s3)PH߆T(Y$w}ʞej/iWvL_šDlqX^LVIJquv}YkDz/-ųἳV+3fIvB]1,|Xl%2 &H /,[|UGChnPqBRBdw7TUdŚL돈[ xSڵ˧R1, ğ='j-W=.> 41R~"չvdEnKpuR.S`3:Q:t΍W0[:Rf.-"TyU:rH~]&{,!`d P%xDŽ|^W`?]ٝfZn*bn(HHi\Nxy(O9 Ozpلo"lzQ 0yJ!J$ٟ.\Hyy^mآµ3;*/v]t_Ժpv@!PqCSwBO/fl(LH katk. (mhRrMs%JUڑ{]@c1w@a!uS~ĻAfaPi˦OɿvJMƆz>H2B 3A1L)CrO'rĠ߱۟P]w)\0\k¡>zSr#  h` 'HhKO>LvI_mЉ P¦*泗Yy`.hBkIe N>3F#5P !$~Wd]㿅!IR{TUIWs& ً/׾,Iw>.<* _a̮Vouڏbc@]0@)5\?pd9&ZtڐhDd+==((<ߛ"ƑIzR:,9K7STEd@#.Q6@tv\l` 1f(SCVv"{d vI{jȾVvz_$:Yli Cr #6r$q;jr>4j+![kp$iPQ[I¢x¼>c'ډjU}oM,,+Oc6N@D3lŘuLd)K@FGD^Az/_}rp 'E/Y =Yn "I l /JuS_dKPd+C<, Wґvd~nQ(<)qZ˛1T/6Z?_{B&!wY@r!d"73GɳgO`y$oʤhi[2(-UQVp A3zy4N= jhMz$Q[==w&M.USnTht,w:œ#QZ{=?G,>0&GUkU$}Rq^"|>t`/<%znj?y 5`ǁJV@:ş*")ucKNCEܰN+)z y",CY$MaE74|;ݤJxd$3~P}2jfWI@^Cf9Yr8]pь:Zv6!hY`֒sW/Wɉ8Y܀ϷA%C奮"jJ|Di2]F!<ėXK3CcGf' fOF>NJ,X UI{.7IX!R/s{j.TSZ+WW+P'(G6;m{qQ}i& CMhK5. W=YpI}wȮp;ECc.ql>  rmwsr ˙h ZPk۷o!DprG2P^bjS5^_Z6&W7N,*[UR3óe<=6 SoTuA@,S@x[Y UwpM=Ps4*C)&M^x!5Ɛ=a"_%0 -zˆjNu Zӛ/YsچY1ϓŰ>{[];%HӃ.&dabZesg=m-s=XԮOymNXr?;4C6 `.wShD RQ_; sNeM^Vx L{6U !wU ASHԬ 4Zy-Z`K"89VC]w. 6$dj2Yy0urh {KuBCD"Wv,d7`-1h-F~ef*Z^\OO:e[0[Dr 5$?E@)9oϓJz|AB"lV1rڸԤrWO@欒ϐC`nBF"BZVTZj-<, O'5|t΃kOnFahs|d[hDn.:浞RxP;!7/,=,HɊ㌁=UU& _m{ZWH ׽"x¢O.yHlw'QQ)k7vs@q7dڑDp(`Դk1Ync+//,h 9O8NQ79f57ne.dqN@81pZaOX`P | L LI*<{p^!;MYr jmۻGas| Džt9P[JX :7Vy@TY`'LZˤxY]/h8-aMVW~+0a%TN6.emO/8Y(p2]E鍵/W}iTuG 5ti!6VtxtR+ZNM9dkHsBɬh77_AbCvӨJ -[6"m)s҈Z U 2'tlc랾\nطrc٦,_ɮV(x9>0PPpRXz8SaO<dk=F/Z|̚ϙtMfjt6a *I"_B+PiEyx6t0@q&Pqh˓@X)i_SճGhH2B-VFuE[5W8t[=ZEQ(,={ԏII[Lv|4`a6qRzYq4P٠ZѠk>MFe_*HvAHG䤻hNMQFzv8'SoO]Q4 M9e#-ͺ0kaz+*╆= Xa>  ,QռhȂ[$&ڋE?44.`XO)YPfoJԲTr.^ߕp3o/*dr59`+Q-39@5@gzo R|@/\9xYNpdnN g}|1-S#@៽]d t6.(D%n]??czV!~/Qߧ4< yJ)˹IyoqkgT %Sg݊PKt=aWw0÷|]i.TQX5qEvObPV򤰁,r"(AʹIww=O85\XYRt(ws"7\2\<7NJpѳPJabc.P-*U!H fӂH0 N(Gn)h(QM3LYP|dzlFW]NÉLcK4qQ/T3L2q>ߌ:CG|X!l(HѬ蓧hZ/cMu8H#LHzL#S ˺00IB<8 c Aˡuh*sox.[$1ϐlШ"8Z"%5"=Jܟ +j(^)1A:Uc ':\b{vgG1JQJƜB&5)֍ي:]!pdonK+a팴Gjz5mg I ZHJrnWt_?1-E%;`5!2!E"u$`13(s%,liy;QJ# _7sBo/ٕ/rHfKԦI[49|-8 B3RۇLbXgatt xյ.-p0<*r=r)I/{6lS4p"5zr!.3 xv˃u *>H5;7P9a v?,,L6\ XXdEmH pTߢ'9҂1C=Eo0 ݐ5CϾ `M s@nԕ¡m2P.`,Ph ~y`d*?#w"Y[/$YboY+ry~Yzp, T#_[J[Pdh^&Q*b.'I{-B[)^ r2gC6NS 8A<$$GvmK1:q H-O׿.h.2%<{p%m{@Ϋ62:wÅݪ~>h#NsƍɃIkRE,QRtg!?B-@cXYZe1@^lv ԝST."y32M!NAF8l.(8>{d:#m酙fxLzIOEFdv?fg[S%m%bRua'$?%(˟W 4\iPFkE9j|ůƊeV&tQΠ^Y&5r}xċ(m :w(zraEAvsWwTazb)U6LkNcI]8: GYl%|ޅ=n%粃B-xۍ< !sm Ete#0Hڲ^ Ԕ}}-+#5:4iVCWDCs(Yoo§W2wj*("])YDqU&pYwl=!o|Qw'䍈v?^BH\[ Stֿpr.>}R7j L>Gz GpƭtK7ɉ8ò]& H-lѐ&$9p(U6nW}7EjtZX^Nl9/uђuF9N1C~Z”Tnz)MoF~mX ('T*(g43Xn 4J~,F!#ZS,b §9q}I0a+FȚxa6ϻjlɘVݹ̻_\xG15G;SG\sa]p_*LL \֎'=d+J pf.'wkdy$N#PDw_KU c,f:ؓNG5V4|u\yӿ˾N8_Bnv':g/N?#xk?^b3n': ?ΑIb tvoW18?s|s = 6}%NX֍gælC?zrQI >ıIp$`B@1/>aU'kо{uOɣq6MO`^W?* :>{S|֛0GHU#U?,P}B#o΋:l)dEWQ`2S69pf{7}J-Y},FPc-Ҏ.`{9;MdJm|zk&ѝs,#[}M׷'M Кy +0r07(cǟ3HKGy>>.]w{GcvRǸOtWkDW IRr@L8H_Nu]J^}L3*Ҫ~\sm˨).Jaպ.%0^RqOFGG4>'Qe4ts~R9 ez#25Ok|[f ҿ4yZNݥJQZG)͌ĴPf@ t*n X/tΐ_gm Sk]XsW97}Nv^XK@ q4v >d TΚI@hxJy\!ٙF+aKh_A$:x2_Nҡ]~Qε?R~q2K|P;?6vM I(i=@ـ([p;qDl_!.E-EIC- qmƘ! id;%U,t˾ p|h ^k sCU'<,[K} Bܴ{G>C' Ɇqz'GH[2&mt@^Re]o:xeC\bhZ!nlw sxv`)v[鍑]jHfVrX }:!"ʝ o)(Z&,4]j@%X=3#ݥn/Tp%Ue0 ީc?K3{dZ ɼIUR"]-Xg?O ;x_F_ :-Q։xHVniFZNˮ.Yƥ3,gO<{ezg[x MKZDΫigKe62x`,% O*tw2{"_AwbY;u?W1D eyix6Or,z}3?x>lY!h,O 7l١c罶P1'I[ځsZ71Q&&i3Zb!a[0a qE? xھ8'BNU]&twi"~_M%R#s/MxHGx]Tl/=9'_CgBXS,{K`^h4ö崅x\*&y|^D[i&.ߊEVle%< YqY͎D?6jrf[@m"V" M Ia6mt5rB52gH%S!|>\OPyB۾xC^S2kO4ଢ଼P3UJI;@loڤ4>aU k/;o?*e6,m6q\]/`[ebz94dƫT"F5]!9s@Ur։!^Gè"RmlTb8NX8I.{!e% B g=]1QRvF\ۓi]xs3XJÏ4M4BC`i|{<o]2#OCT+h"N2ތkiSnK#B x+I5+^X~_gU$UNOR'ĕ#_oP~^s[Mu @%Ǽ0<fZ= 6٣Oj7NvG6)Ք ],z˃<^i~n8?R QOtl Ɔr8*Zet*TO:ZUb.}{['ZFp0qUTM8j"yϼ]t3y["`0l8'Ke6 H8$ RB8JP߀Fqi* DZ)wTrSj'֛'4Íag LjJ;[/ xH/?-Tf!WI~.c:n90qe2[k&7scϙmjV,nn7[\+l{'>JnX{­Dy(2q ˯e crbܾW$<}q܆bZXptXb:7lC6 {}͜4 QD&$с?go8}u3ӹՖ:8W5ctn}ٳV gٓ:ChƨbGdQĚ?{ev~W}4T!qfk*r.Fn\ڰSXQ&t`4DSC4E"0s,2]nv3ߍ?"K ". z2"÷~dEUS J9Ss5i*Ŵ.c!tљ]k-vMJNjBlRdbV\C:Ȱ/ɝ@LU-bӚa3xB6EbR#Z<]OD"kwhESzX9pAe~#RE-7biW\EƎ*Ij}x\nx&sP 5'SAG/D,x=( MGVmtb_3qrtpU8hTKChm^D ypx f@c<ޭQzăyd4ccIffXt,m|9t **+{.]BeއVzn} >(ғ#Y,#rq5tvˉ0e=^Դ ֋mo0llll@ ƌTS}10;2[%8;0Af3b~JU3ZrRo'7vi3,큵qQo;$\ ^[y۱}\߭ަz*7tlj}|Zjr}xfV'Z!ǜlIf}b<>ŤJS# kXW Izw*? +qv`ˍ4`@@^S -cnYm=ˉtVlI4 t:tw3LawQJa!5Ƈ37U|AR.o=X T^$ F3`a{?%\MᠵTa>8˟fAX_*EdQbalJyN\A&J-k {گxɘ9gm |I x%g[2_9Csݾ";G1i6wFu~zA 2vR%SKvg3Cjpr ,>QxJr5#xUguۃ随fndZk_@OW)K$JoXN0/@D7>(w,sx̺pR.x#L׍}T^3ͬ%,LZhcnr*1~|ppC(/a}jd%N$]ۙc{,7$64PQܘE1wN9A9'#+$uCc;NjZhW8x""́yF %ꖁgn fN~ 70Cbͨ:;moT7I,XYߊiby^}3hڔ=Nc~!I8Ίa?y|i QOVN ;iYRM^];r$pu6NGiL2cS`f?V#Ⱦ}=.idP3OdD#qntKm8Ǘ7ʯ`A[_]w/(TqQp1I:d :t[DdK w6kȫukq9tb8)v&j zid?0:NxlJ{ElDߦ$sҕ` ,r:'hCCbZ|uE}UktHmZA{҇ViKK6hѮRU?Y|8j+$7Ne?.ztTЫ_$5!} 0ݲ6r09qBfGcN0ꯓfg0g ) c@)m&鎘0:nowr [C(rk[d=;0 ϬU0Gq͡E ݄r0Sl^~cG)]hg#2LBq:ǵzu$c1mY yXrD!nT+J!)4tR.zg cُ;@rۖ(SUUl{ZBe 6o{8!zLgI{ScKHs>OiMqe02ǐ^ҩ 'x1|/ۘxnK DO_VD~=~g:zi#{2;K9mMc^i_d=9g879T{:3{᧏<.ǥNݱ<Қ-M)6$4!Ι?oe3IA~^e1_`֑NQ聼>ez?$5Ӡ/&IIy<ΣZjGo=yTVRח=prBa$D 䪹 siI9I~$b17i1DbFg#mz-([hzN%Gx룫Zq9I^ΝeǨ x >33"8R2[cvyRPi i$tV?@r+U7-)dDwkCiRkYIOsjsYT25Cx%-iiPtfOئϲʚy]D }59Ql[D{72c JԚ^}6řvė|P x+?IL^*XX"-OQ KѤRE8neuYՊ;IZ7QlDz찊B] ! 6RgUM5pdv٦QY NAmEDzsNO q=h#3G2Վmlb@DHpY)0ĘaSݹQV9?nhҍ~,U~cpcXKUۦN5o\AʍN'Ua_18 oujFxg[` J;lHa6,DtwqV5\ װLA \7:?EҨ\pO>JH;{\ԀP@CPw(SpFQ78(xU]`Jbg,d\ 7i0xzf%hc>ЫnP`u&jƶ؛&z󝗩,[$3 KvF UJQ}]5H,.5tpe"XuNJ-Hrւfu&]oŶ~n6g X寧b J5f#DZ+;E$I)E5Ѱ@K VD6DA>G ']ig MCtF`h_MW7DL9cn@+wFh8xrVʘ@ڟrZɏ&C:k|MD k~%SiSF'µ.orXMYYl)ȲGћVQvP/VxcM@뼚ЩH< Z$Wn[\i&V+-;{sl$WR=)m$q`U&d"$C>A8Ѷi復4LRJ&=qk^$&&I%l5:N-蛒*WqʚS\CQ\͊Z &*0[JK pLbNŠi,e7XH.G:PVע;E ,j"ݥd+F$xlA\q *S$sKW,ىQ1)6hΎHhZjVְЏG?6],O*P;=:MUwUG$N+ 6tvO҅Op-0d>ꢏEHA2G \oB 1m͠udz!6ر%O3SieJZ4Eժù=$y*<Ty|͹\Fμ@ t9ʕ3Y Ē<) 瞆Rs,1~`;rӒQp:(~dllnFEѮDl$,ýϫDOj-KG^k.`Ai*zy\szRЉNlsh@4ۮd쿰+S \)U+5_( gpF^jD3q#B=&LXj#JF AZ50Ys)L#]+W,Wa,kRy;aD➝{Sݖ,FWUaAy,+M:rɸoj˞?]Fn]~e)TƌuGKB忆VG]T~`k9kX*2XPVX|{|v j.IWF@U+z+[bH˒Kgڢ7wН+nT;.+bu `wC- +KNBK [Zo.^pNtA( mqA^MGS`EƝZՓ8IFO%"F˜sI+SާjVàyP’CŕPcEM$Nǰú0w_YhUqhL]PқXi! Q-2*BYn7F"ޟ7-OT)A1(NYIL{̸$͒H^4^o`MZ'}ʼvFԲ}+Ʃsq)aCvA휉ȪQJzStvBsaz溲sMM`9V(on*YQS/մ5תZ# ?c<&߫pSkD%2 6lx!j\r<;TpQq+vU`]Ϛ;.a mwEfWtR[g9> PѼu76 0Y)P@^[@gUDl4ntp xte ߆$OЧ>%)O }JSBЧ>%)O }Jѧ>%)OiO$H8p @ $H8p @ wA .`XÂİ`İ  1,خ0,aA bXÂ1,0,>%)O }JSBЧ>%)}ϣO }JSBЧ>%)}>%  $H8p @ W@ ~! $=$\ 1,aA0,aA  bX]aXÂİ 1,8bX0aX}JSBЧ>%)O }JSGЧ>%)O }JS}J $H8p @  $B $H{HaA bXÂVaXÂ,İ`ð 1,aA bXpİ`° Ч>%)O }JSBЧ1>>%)O }JSBЧI0-,X8p` ,\ ,X,X8ppӂĴ ô 1-X0-iviAL bZӂĴiiA)O }JSBЧ>%)Oc|}JSBЧ>%)O)aV88pp88pp_w0+YA bVj0 bVlWpĬ f1+YA LDЧ>%)O }JSB>ѧ>%)O }JSB>if88pp888ppppĬ fì f1+X0+YvYA bVĬYYA)O }JSBЧ>%)Oc|}JSBЧ>%)O)=<PK)\,O¢ sample.pdfVn8}1RkQM$F4E,dҐ8OA#Jnb s!.O^d:{>븎yuRw / R B%5nׯ]G:lR7ǺVhxǬd=aUp全 }PͲKPODr;iik[> z]zz- $ {"H? Eo3B8m•A ] 7Off>*S |:Gױ$Y:JY Ab2Ce'!\Ų' g# Ek1|`D)a!ND40&sYq(  p]H,rbv2JT`Լs1s%R"pKF<*5І!h{C!$,!IBG$If~CȇҀ(lf98JS6,CA_~n!֬?{je 5Ec5O]( I "rV=I@OKhO'7Fb@z M4WfO[ 1l~P:QfjJU7[+nsN/Ĵ{8$t$^h i &3 ^U؉d=f*9"LlzĻ}> QѸLc[|a{mWS7Q #j3~/U%;G|1.]ؿύYjE/X Q+Y=8m'3U:4|/p#`%4c<%,P#o|>Q_:Acm)p{mceAmB|٭}(T)h6]i#IW|Ҫ /M&>N/\ PK)\,Oz5 sample_text1.txtPK)\,O?}=;sample_text_large.txtPK)\,O¢ sample.pdfPKJzip4j-2.2.8/src/test/resources/test-archives/store_compression_made_with_v1.3.3.zip000066400000000000000000000023441357650161600303730ustar00rootroot00000000000000PK ctN/ Passwords__7a76eebf-0445-421d-8dc6-012d1d6bb33dAEuG=5$6[[ |H1T[eWɟ z)POεCOGPKB&PK cVKN, Note 4__ef72569b-39c4-424f-95f3-e35e90250923AE!ʷ0:Q>!XKMi6(/k褐@PK( PK cgKN3 Private stuff__7781229c-8441-4db0-b88a-906c30d0f004AE܎!חL`v9G tLm6UPKPK cjKN, Note 2__66313c2c-9d34-44a0-a84a-4d1d27c0dbb7AE$`04z+JxS1q>9`|PKPK c#dN. Note 3ql__c9984480-ef73-45f7-a026-20e989264683AEaDhG-ɂvG@KpPKPK ctNB&/ Passwords__7a76eebf-0445-421d-8dc6-012d1d6bb33dAEPK cVKN( , Note 4__ef72569b-39c4-424f-95f3-e35e90250923AEPK cgKN3 7Private stuff__7781229c-8441-4db0-b88a-906c30d0f004AEPK cjKN, Note 2__66313c2c-9d34-44a0-a84a-4d1d27c0dbb7AEPK c#dN. FNote 3ql__c9984480-ef73-45f7-a026-20e989264683AEPKzip4j-2.2.8/src/test/resources/test-archives/testfile_with_chinese_filename_by_7zip.zip000066400000000000000000000004711357650161600315370ustar00rootroot00000000000000PK cO]yfff - .txtaaa ֲ ccccPK cOإfff.txtaaabbbPK? cO]y$ fff - .txt p"S Q"S Q"SPK? cOإ$ ?fff.txt z%"ST;"ST;"SPKjzip4j-2.2.8/src/test/resources/test-archives/zip64_with_only_file_sizes.zip000066400000000000000000000003151357650161600271450ustar00rootroot00000000000000PK- 30OcCM hello.txt I Ƶ\f7 4 $fVEPPKcCM PK3- 30OcCM hello.txt PKKlzip4j-2.2.8/src/test/resources/test-files/000077500000000000000000000000001357650161600204345ustar00rootroot00000000000000zip4j-2.2.8/src/test/resources/test-files/after_deflate_remaining_bytes.bin000077500000000000000000000077701357650161600271700ustar00rootroot00000000000000|ސT cf_)J%i_ɵNR &Y(gL% grv-B ȿ{d'K'PhTt׬KkLӓ֮K~Q@B8-6 ~̱;þh3I;\A<.lGisC;BR)c~vCUo<(vO᱖<Ƿ(|͜RW,eK>)R709S;;d3qЀITJb[}G32HʩM)ߍ ֎sFb7=~MƸBFuʌAʵuyLKHpZg8:{1!U #nW˚z6ߐs\UL8GIVM;uZ(2zv=|q\d8]T_eq(W1`#V^I=o&[1|*+N02e̮=+L,·gx<?,Ma.xl#.E@O #Z[ јtpЫZCdZ\=>05FCV :t6V$-;DkW~)]QIVtخE3Ɨ?nB2?вD˺~# ,Q:ZΔ2- 6t  vN~CedS~IܕgfJu"q.m4 'K$)af(L.i=ϛ`i~gWd8 Mfek%cxe$NS{fqQ Hj}l(^_c xhKssꑑ"݇IAJ6oͩY.?maBU%a;Iz>4-ԔM:͢k$$ fDzrϝwcU̻_hRfpEΌX"00N t+9LOjO#9,^o zoD.}U,IZ#GDO}j [2v^}4`1+/[AfH~G|=U'R2ߤz |PF=e# PtuhQygI8v6{(6LxDT}S;o+!8!Uh)™pMn5MunsY`ܮWP>nX.o`@)[7^؇{Y;$#b+ $ωZ&"[T=l`z2DcձygZd*x7'd؃GJDAm]ݓxQ m@ƕ(wr }weݫ vBe;qENp{<]?Iev_sY޻Ϭ3^̜=K諤C  2Nt TP7"IR tpVJ$~O6 2cgM>ba 9Rx=vnfFL;:*joeN<|U\,E5g u]tm q%$BDŽHpיKn[+ 0հ:. Lv#nk4SW |_F,+մ#EFI_!}gcoubd?SI0VEeܬ0v33.Ж>,S2#;W(p12lwD;I#Rsotq6w{M 32u]+Jm\ 8baAb6O$A ]rք lv*"ey 6CF!);搹*|^yxAsvm4`hľ.ǎɍW Aiu)%0VGY_II1@ˁԘ&J$Uh I)k1q`Cs3_Rel3Y-ɋKi{ '?71< ΰ*eEgq.7!CPus8$0b&ZXo'jzSޣ 2 *`>Bx|{dcA<5q{_b&A f@fztQќA<øs<4wM8y\'}xR6?BR6 >"A'Ƈ خ<*qZGaG 7Qy] X R&9L!{%panuÛF;uXh`}g@ Cayu)nb?Z $ܳz-=US4>E"ݠ?(>0~T?qީ]FdJW([˥! ;&;co_д}\|A}4{a!knMܷ^V@=uhO>j0"WJ ƞGSֲ z\|0OH.jI6(ۣ+iT{G`Xb&H}8`Fќ`lQ 𱛢(@eq1!z7hHЂ)aģ5G Y` KᴻR_n8E3KAȀv$.L00l"r{4 F JL7٥9kVr~xDܝPy ]VIwrV/Ũl[xD Vw  d:yE$I=忼A5%nH\0gKMڿ4(KI}|=ϛS6= $Y XY}yE|Bvs/LLs`*Qklb[yY(HYgGOLׅ?xb>ܢO`oePdY7b6^V\ ޫw&H9u.lDF2頷49N4'P̌Po 鍪'(hǟ)Ά,\I^mzA V<\oLg;=f̄k‹`Hz$.*._;qֳ  |E$LdoSĴ#Yums6 sS^gr/^3ewT20ҿѳǶke+}` fRsSwA2t5?sY..&Ƽ #A-\%޾<$n R]~C63K^=|5rJY&)<|rVO!4qRlgL "u )GQ=nJ}vFKjZj ;ִ$v1~rr|N.FRh4Hϓd _u-)1h}+PęmqhۜQM|K?_ Xρp"7pq5?=)n3~fM{& jV/jꌖ)Zҿ+<6`264SY(e3^xwBvRn hg1(b9#] AsYtҀƩ=%Ը> stream xM,7޿΁䏲ah虞 ݁=͆撿Ye^w^˲$[2g82OStp9'=p''ᜦ7 Mf7O?Ox  k&\?^Iʏq~pvD5DCUq(lbc^+z6ъѻ ` Њэ^vf!g,bhDZ~^fW>_ >~nUt  GD1U+ 艗`=^ ?@, GFL?xT2ɐ¶6 Z-5 x6\ َve2%4:, ,Y<9D%g>qpHV!a3RI,-C_5 L& cA]?+KLaۍLlM(kܗ3J5H1a5Bca@f9 )R4tؐ2R_ 8g57b >xfLB?W<̥Xv5e+a%[FETBQYaok\f4`}yTLgUG(E #~mb, G%Z!ʂBhKOCx{;[%J-Zd$]Y*_/H9k($.JIS*U2h ]#Bi0*bߪz92èZjvB*ԅ_=ah A"f*"߫hnřV$!I4TM=UgE7o[6f:jvP 6:fQDA%)G䴋+>텭٪"'uFo_d1Rw/raN=;]mO}H~ Saw܎G UdVg8)aE?؊=Y ZTyH;(*\ *G5bHYځ{GrqFzVUcC/,}3L%=o.z76,Vk&-vj#vӼUQRJJ((ZpoA^UrݫGjY*= M㵫mj-t8ĵ1Ȅ~\CfjR;FwV>e Kyf+, *lQL0U;H'wKq7m6VZLUz:'<HaZR@ƓwDR$D"MO0[BXf2C}a;frirDୢbU6YA:9tgm<ۇF]f6+Cyv*5t½'G`њl:b ZZ)5:`]Tn_ ۥEȐaaGaaa *K\r{;B9z$ņU&teLHҠhϩm.ci)DH:X-q_j郉DI{hd҉8;?iIb@e@5D0.2ͷB3%$L-F1r<"f.Mgɲgη`B܀zCw2K!˥#v?m!_'> stream x\Mo$ WyvJ_ c!rL`'@UI{N%IQ*jGwÿOiʿ-=zowi~\Nz{N_SvKa^NK_OOn]߿{riz/ 9۲]zX>%wI;}tt9d=PX>K'O4{C Zw5wHz)>3&ҦKhEv9goI,OUF5ǣK~nSlm1zu_.O*r,,h.yßbv7Iy n ezwGcǦ}ytOgUe,7hq+ȇ!W7Y-RBeI"VY[ajUfE%4;E[lK웎%!$+ԅFlzmJ_? 4/u}kVw\кSʼnY-Zd_ C ezF|*Tm]цd4Tf'hwI  ZJ%Vwp{{ÈU^SΧbRd4KSN[3oh\Q0Dڬ*CK:{t ;?dʪԍ@Qt4WL9iԒoԼMgRnG2g^Kq%)`#9~̿Ic:\DJ/F:"3s$.bH:V6B-&n KQ&n\R޺)<9Cix&4(JL="x<HV<5ݬ}0goQ127LD/M8A= O֡cy4h<"dKw (ԯ|^JSmI#,S &|S! (Y+r\#}qZq.A A;6Z #F5$^u^T_7Z$P(\Qejc|UpBj^zt\fWaCh.(t tBfq-u|*49&4ͺŁ0$P~:"0xSV#8[):Q*rsENMpnqz51^F4!(ς WS󵝮T§CD| +z[tH;5'>ȆiƷo@YK⤛6C_&=O9?O⎡W&}R9I0M4[s~Z/iοyJ49\׽%6/m|y&7mbYX!2`9b9?@̧_:֍5yy9#"C+BLAA1*#H2@͵{P yV1)l$\Ea0]>bgSbDhݨ+s@% ZċImU$w$^^ lIGX AgHeK& d}l4*0 E`!vU+1 S`̀`uB_ks6&~•jPHsg4/4…Db0FÅ+ՀThAާpJ5|ژuz\Xcy^7y zyvKI\\n|X}WXG.ɯsqk/_RZp}|, ʏO}W7gAn?c/[O~3|U}#_Kȷʊ>:Rg&"+j>CEc:bXj+{z q66* k΋Տwi`^zlAz=3't5^ƽ[/FS}o^çG)A?2@TZMXD')[[(t!׸]jieP)UEZ*le(xh'0e` ɻ{ QMRUVJ-d7&lS3t.y=uO;&R`+!Z?za=髢Xb9=AOb| CX>}Jss#O>W1ؼrZ9X7 1( T(,~1mCPoUd$[)A+Nem ; i7o~f6lxGHRQu dP"­eёYֆݞ8+Fɲr(/lz]qL)6bOZ&x}1 nV)@exi^'JH9w٣iC8yϜսLA|9iQIu &~PO+%[XEEwP] JfC~ s /1,x̐ǚRq}V7$^OQ^h.Z}Ga%jn*Y.a-ۑ5!`p낤]>] A `^]yQ}orǫͶra>RXú k,ׅ5nxR$CWc풲:4/Ad6֥Y,Cͫoxt/_& endstream endobj 6 0 obj 3646 endobj 8 0 obj <> stream x[Ko8Wy$vz=,s?2%eFĖ(⛴y?^1~s|u)o/{Y8cɾΗM1-%HҲ1E0/J7c3`F3 h>&߾`q֦7!IOovpF37֙ayHaLDhB::iV"z4.񆶺5|̸\Ûy_ҏ:Mososq#-U=. \Ǚ6iKo\}^v5.Shaֿ3_{gn]A%Ûf^3/zpWeeұn͊Du򚧄+۩·@f?^'GP_n c! 0`4BB=fDKHP$:|~۴ޭ\tiV2q m>dK!hx/]gMth|˅tG0?J{Go!C[8{SŌo{_X#Ң }i% LyaP$&L6 rrVbJۡ@.XF*Pէo5PK8MG"Y4"3: o͕ ^"H@ e.ƺ"2ծ@|zd㛳;ZdPM(vFl}H H 9a>8%7/_H^UڑMαī+y8W[!:Юjޙ͹BEbzE-GȠ\Y6or[V> [I]AWVU*acpGLj"RM(JG=c b#ldiO6)[i~;_P h[Jii5bK\gct3e~6T^01>mx2(֓C{d@_塼~Z+\^^+?E['Q/e'N@&v+cX A೦A7V͖G>ڠb4|:ldšHSk {FnMP{j6͢jM: ]ecd>gayUK6RwX89"v Jn; Hp!:Ff9Xu@6;8ϯ~+O2!w< rl{l7{TOKX(ܱ"h=FZRuEk$ Ni{^|:tލcxL:??ϩTIًr6QD$׍LN[^AZM+,_}dBs_\hYBu[YjPE>`="@r"ap/!r0.7 hʣМ#|`n}PL%OLNdzh^ >$XX@~mcAIF#TM]%Gq\ !_k@8b.=*:8X1Ƣad/h}vzWGBfK7j'6 ^**GLQljj'pkd&ALRF=ʵ+ۉD@Q 9MUJ 0:>ơdy}[*66d\k$?] 6/>ҊY?]W$U}\MK\*5^o[8]8S5 `MyZDޖ5T6"7W6EJ+Rm0G`Oen{\_>؇Ûu0mrt~=^ pߡW5IK5h=uD:d:r(M'Oh<6|!𽇒>^1^)u!<À!9 Iq=,h}(˪-1ۂ5-|l-Bzӳ)47:'\& d,8-% Πb*qhn E(Rá W%ThW7AܑN MZV^qJGWC Phŵ)ǜ`þskg,@kL?|}ur*lb$pRѺ/ݖf1 k8r'>\@`*Ccy$Xf`7Ȁ׏øh<0PJvgop|X9T?Vp$KTP(|y_v/÷: =6|ɲqF/,_Kb6_Ğ Mh6Enٶ_"}ppXMY\fӴ3کZzʬ71錸H_℄L8T8(KݝFweCTmܡPy uFǟO!\iu3Iukzn0xxWritǑ;9frFVvʍq}( 6ZwhHC;ܤ -F>Y >췪.n-̴nJJ 䂷Tb)*E)/:Z4ϽpP%@G딗b24Z_cAsVvW@dFOͅF endstream endobj 9 0 obj 3640 endobj 11 0 obj <> stream xZK6 W\ Ym3 ӶؽW"%eOb4cS?QR拚9}iN\t=-V]:)K2E]dpIlަY*}+Н% /`xߧOoI޿U?ͷ٪93g;| OM#䏸J< ~z6EHIyЬﺞmZ- H y׍7Peʌ`GLt>wASD{q]Ԅw^SS ە) 6$äzRs6 ' hr1le4H׫B <9*?>j125ke 9i+ّ '9E2`_:=Msק6 sp4H;SN6%Byys;niWsre)R9\`aCGȲM>>v"?'zATL9+[5U~Ŋ߂=5Jؤ&BR9-As!Je(jR\-V6ecUHD֔!$)vȪrފk/%(-t*IS1QWˡf-ŝ˩%x*h PC]2U='U0(JJٚJj uP}Ba%X2H1(Fa<ԋ)U.l:/FT,F].@ OZbj ŴCL.b2+8i@L<}Z ɇњ Z.hNls}r=/i\錭ە޲zwcy552հR @*g_),gF~hיpgq-*z] ϥܱ!I2ksn]3pfRPB&2Yl@ƞ^ C"&C4߅ kZ$;v{aoBL_ 3:({C`G($fa|4$@G૶#H-|qshŒ).x("%ɇ ̘*]tvNX(r!u,#!uQܨB5iEoc[d6maE&Mu_%nbDQ@R- }MfS0fM~dL. :L w{*CHY_Oa$Z9@^/klѮIˎfDyLХPS SF$e}[ݕY"z؜LlԵH~Wzb}Q7cn;5hMg{dp[6z}n*3hwc,Cݵ];Sm$D3Vڻ,P2>-l/ $<8i˝ e*\1BK,ӑȎXp $\ӨO`64졣y\6v[M jXFPrD,gI\t[bBhA-D,܊*|k`AB=^-hO`̑w⎠z!UBܦzX)sx5SOՋB|wK5i-d!?k\͍Y׃'5P'%c^1&nS%31:9Dz3P\Z]+G6[+Ah,0x<պ}mZuK%ћ,vBB{]s4`*F4oݣDNH(:ؕZ뇘at^\ 2UM(dFMp<4$M ! Ty$#FldJ _tZl-O"3ouEEȿg|g wHfӏ_OY׿;$ endstream endobj 12 0 obj 1967 endobj 13 0 obj <> stream JFIFC     C   U " 8Mh8K6 *UJ6WU𲬜{~n*snAe>O/9Zd'Jӈ$m#WrhBhEdf2PB'$"(V\OzK'6_^md(MUPB2U]Ѭbً/n YYpoQwΏߪ%'—Soܬ5_G\IzWOڑdzIjћ?IxYoggޞ&J\yYByk!|<^#3o^S\,qs쾆x/OR% yk }}z(}g+qz|~+^˟Yov^;k_i~O<^zyy3)iyw5OOE%O"=)|c֮3i>a:>m=}zǥ$8{(xko#>9M?gy1g>/C#>z>g'"|}qg=59%/4/|}5%vI|>a_%Z|$51ƣ~>oOim~;>ȱ>4콊oSmɯ/',.M}&|8gO6|6ef["fݥL|kUG k)ψ϶՟h}f|{9/'%z5<Ͼ!/v|g%(X|/|Θ=$STrM#VEދǔn"RBؕM'(HicW8J$I#PeU'Vl8` J4%$QkcV L% +njwMn[YK3HLl".HHB8N6Bk|mL\|}LlL4Rɗ.jJjboӞפtinuVh]DRuY%+3L1r)Yqƒ_,`x/6dl. m )ʤҒDJ% 1+,-7%Ec+$ ăP&ַ)%eb q HPH)Fb@ DBũ @`ST\E3"eYqC.)/1\Բ"pQu鸎6ƙ&ҳri4hޚ icͭ#, %u'1W&\Zr :bv/6 BO|]`z g~29AOL<3Sj 8}=hFz2=pدO{;\OC!>c߉Y+*Om^5'YgYOO zP9;=uчv\|j_O>>cO1Bߧ˲gGS-A~6rP02FE $LyXYF}]cavkiBM 7LH *m"Y!ŒB3H%N:sP8Ȩʑ:bUmAĔaV'cQ*k_O=ߗCtyg-I(|MKJmHĆ%TQ~>c~0j21e̢Ҍ<>xrϣ0)p5aF6Lx3c?%x.rz6LMn1|^E7=޾D}sP7?Ӣ94՝1My׵|ٳ|7{t-v&|̹mnԽZE.|: s߭N>Yzrj[i7::zHqW6IҾeMfyϭd_H24{N գK4,߽4KEw8z` ׮iitQƷ:F\:@:-w{n2뷷Y޽%xßusĽ Vy[LSOfz-nˀwm}5MNdQ [95z),Y[aӟhsn%wŊ1&yH Bc%88%T8ͤY8`!(8ijjR4^VKq*cv=q%j*Hjq6k-ݸy)aQIa$^~OQ~?O?ɼW 7MK >o"Is {g>:fUu|}Wl%-97K|In©GٟWE|ۏ]NOMy5FYu3!69hF2u~ۏ)"`z?23ӆ/c|mk ~h_WF\wϰxlh9wok#$XccgEˏ_#]eߞrjkεs.:]CrIVz41g>Ӌmy&5wCHlcx/]oĎa_\n5|ӠSXABJQ9l"DlRf-,tgGm>GXE"4ƚ2"Bb0c션TEIHM\M3ƪ`j[=L{6F,k%T#'yΕvԤ"x]3r_2bj "bw8s 5˨C{Y?tq#sFuwU˵1om~"3:ϲ~6' ~gc<'/7K8+4\'{fqgqNL T㚽=>柩dPo4{>`l:y/2\Z)^=FwOx|鬔ycJ|i_h=OYnjQΡ k4'ӗ Ӈ'9F%95k8sf_U8{~ͮ_?CGzqrLX_W.;;Ip"L}O鏙~ O>S2cv{{N>Ǐ r,lH/_ش-_[F];=wWձK<6} Vޗwsq3kQWeՍm0!5 =К*qw?W[,?,c:*kȎ:sЯQC6s2;-^mcˎ-Si!Fic('8@D,%MLP} :Wt[z(%Ӣ՜K!0vS8j]&YMܴ#]v[:+\<~WW˖kH}o8ÎΙg5K8(ٕ(O:\u,,7 reD)dFJ"(Ae뎸K~=̱x#dl!桄c5eTCSWYWƫR{)kj{䟗`@@őEWex޳|d,-QX !=kVcc|<= WD.f%"UlZoY>J݌'}ͥY%,TIBc:= 9뮫"U#l) 8Ľ|SEy{'(]w<'U+} _ _U5yeoG JjZv=K韙>_~6r"pQr6*,֥yJ|yo?1^UQVE|=?͌,5 oSz-g:}wȮTwdWϦONBWe|cyGy7n_2M0"4O}#=3đ#;l>oǢ5^uTϢ_;4#˭q,z}zy>M*2~M| Src_=317^oLfX >/nu,֕8i_k=SvA6iX%宜ô+SOvݏO7xwnuqk+CO ak<~쟍ǮqSΟzHqLmU}2> :p:|Z3)oy~1~5/Ή]5l3iyvWx|^:t{ņEYtO]L/6υ&#|1<\N_毦7㯱~=Y+sΥ!}>^6n/~8^ߟ~/j#Wn8t]x2+>{\Mtˇ-YT~ڔK1yUc42+ ryҾ㿱>;m1F\.q v|WgcgteY?@x{6u"x׮]y* tg~'h!Kv}c?^|סH㢬v6QWs 4&6f~:s>U'E*?K^~\Jw=Jjī.8eϯ;mFێ|忩>Yz9BAZ%4/b=q^>>qty^Uz~BP bQps8^.eX+TsnKw #냝0vY9cefݔ7f%ïkiQ[|3S5[vngd&lsR `UG/@GFOZMZ_F5[ n'1r3fa 3d)A"B1le#3W\V6N>dK S|1c~|~$HJWjq2;~LlnOnǣ*.1KwR2 \d5OI}P] 2롲m>o:|z/^tꝫǎX̮ ZQƴyei:?WEkL2fl um!17X<{?psi}z>ESQlJ! ,o c=N>?ㆽ*ff0,a2HWdkXk.:?~-/9/޷ƷTqPVNmtΌz6|'|Mqmΐ':T[e[+\X0l1TZYU:{ǜp18;ָK纓c<˱Hg?r:!<.[Tls|` ;]Mٍ6 LBL& M iv:OO6̞=*njG)6(Z}&6ˎ1 f!4" QRWd5(#lLMg#";<Bpk /|I!5KCƏdGO`GdA9TkϞ#׎S(rKվbzlSei|zMۦoϣ?韕q&x ~wǡ2}[2>kqdӊ;Daٸ#Xy}N]VsxItb'6qGhL{z_o.7^8_-}Sug8+e#}N&]8^ Gf/zOBsAK.u󓽓\爛|KkCΡ)<ֿQμoM~Q^Mg/|} 3WRO8/}o~ɲ:~gk>=~gMH}Dr>Z_T8Xg6c&iq>6oOג)WLJ)fϲn]Bi~`>P!>e$|5/oV "߇ W"2ԄR䴞>d#&dci.5Yxԋ<]N侫I[5YV-o9Z̍X2!k3B՘;_tldFQZ,qjBUevSёp``L@0Ldr(\)wi[dl3 fI2RK,YKӲՑ%dPe 2XSD ,YH2eHY9l4Wc1,ÖBJ F39IXZBR#*C*w5:+vfFAUy 1HcDQXZ1PUcr 129 w3E6IP^%ह%̡^%%+[J&ʁ`%%ڊT[]'җ QK&U"ePL^ IkL5J]]ѫ7Hᤉ|\,eJ<$tuOWcǺ |Z^YahpS\yz4| E=(@]1ӓ24D4zz.ǘz s+ezv',lkSOy=݁G#Yi]v9JCNQ>M2 t2窯펳u򳖫ʁf5YExysͦsQilGf=l)d&1ЪDBi8@ !8굞rܾ/;,zuOy )[팫<_2e]Ɔ"vF(! WdkvUv.,]ed&2&!ha}˛]4]q\#.Ȯjm@ID=C6TjnkԣoJC_־D|3).|4/%69!΋>hJowGuށ9r_=DŽG:Ys^\:ɑl9Ug}8v7?bB/; Z߰8؝)8;uij8˱΂ϫ~>MYݚ[%dž\):+~}\f5ڮ ˀkz \ /zA;aY;ۏqO=̗}իۮNx5K;j}?|C<難'Jk88Wѷ~o9IoCkή<%%$-خBVu˒&fw/}sk+aԾH(΢<:NΣywY_|,_?W#}zzw)z Ԝē*]?ڏ _:ukt:Jsڗ4JΛ{3޻r{_:SRm#͉S[Ef4=?g~jY <{q#.8u"LˢeKUE5R䒱Si@6c+՚umoSζ1ƶ$A^Wa9jj6R&x`lpvNHdBn2QIM%14 JL>C cd'ˣ4؉D &ci<=w]5w(>{Q"1DF%͎.dTmXYxrݬ+a3}YzIΪNܾZ^f):eM13pdTŁ% 38J7_|_>rx{1l"5*(y(VDӼu1rp>7l3S7QXP*H[&NZޓ"~>%,T$R /Qˉ-\}XѺ JI@` CF޷y;g׉ U!,K`[&Y^Ξo p *J!t%g IBva^Lo?Ɵd|gfm{Q#]vPU0m?Gt꼧Uk}?ѮZk#d3bȲȅ=Ɨw}/OMwyntbZBCA8w?^u_ͿH|'m5}t골Bq1e!9J ^垩_Lcc?)g+{Qː$zhꥬ 3  N|_zn_oٍMBKsŗH'ϡt: Y>)*]ΘVmQK.YXKOXk_zpό;7uǣ=7,LM_9^u|c]ʢZ*#wZTznnj'n~l5O]: |`Um^~RS_Du̜n}Xw\|NT|yK޵ePFU^7cu6ևyoxw3}5bz|pȯlm)Ex=p K6>, 9tSn5w/|{S˵JaIOb]?6|YHRVUb} yRϤ՞W::|,6 0Ϫ,،"ITT& $V8VDB$2R7%L "eWiT cQ%(r}v([bXulaǻKUǤ8l)z_:^cuf!FQB #e1J13ikw|y|t;]. i@L.stO˺_/+ #((\R"RĐQZN2qzN_@xsZw5z7v_/x0zeuNjK/ o4O{ן>_aϽ^uSRf9S̽>o!R)27'{_VGX|ח'(K*U#?Ji]Dkg{ׂ{G/>Z!]k8<$XVnߟSۼFbcޘW5~vk>0塄LF98'7g}8agwulmƮs}oas9~鏚θ}Ro>讜}_ymr;~e˦'y랇=#՞:>w#a?:`gwN~?.[w?7=5׿!{Ӎ4}1MyCt]XtY\&|{zph=1Ƹ/ s{/>Png 2(ɫ:Ŏus[ uƍgqrz;'ן"&:qkʯ;a\՘W.z7C6qAX98W);밍atEXI~ʘbZj925ڈ1XHFeV_8j*q#% a1(F)KySY&: Ņz>RsqqL,]VEo/q~+~h<~juq:?Y|QȪ:Tg]WaeTS#qx?Cr @ 0hdX}˛]?zuN6W?MEudmrԽ?,5&&8"3 j^xxN~ +늶ڸL~=sGEpgoxy]<ceNe?CyOUd8"4Ag~ރc/tl1ڪ,}C:7m79ۇ+BpS꿔r' Ʈjkln ͻy#<{윯Uuany13uZXA-D64G{?jE/ok{,lkr7zuQx%Ȣ>~|_~V*W7OyՑg=93\B!.o>/T庎c\~,]0jrKzySguNp ctZ.Ϯz͖x@O]~?X K2#яU+@/zqϤm-:gN3~;~b"< L z.N7>('5}>&E$1jh^N6Dq&w~ ~F{=Ɇ=Y m<әN:W|9k;y/\}!ϟɼ]oȜzxݵNWc8 Md 1 L"&岦Vl%$b D&2U :/Q"[L!D*bdUvyػMUyؚZ^Pa,.9|ϕ]ݞ:65ϏKbO597A+֥ uثjW8jc``:^#u;kӚ~~08idSt[ tRμXJ=xsʝֹ!d4fQcS^7.42α1ɏ=v]|Ƹo9'tOى^U}1 ˄eVc.2aG&}1729c.s:o*_`@{?{׉.69DG)7?#FzW(CEi .l'tRU+jdj]5DbΫsKbRHH# tVy&nϺyĽo]Yu猳"-V(f롲;C>k k>w+>76=]=进z0Gy}gU7Vm1:u<ޟ018O9Mȏ~4%6J)e#q`5Lhi!@q+g`2Pl`yՏMi<{1R@&C!0X Bf6Oҝ.ewF|DTcBeU*27 CQ0duBp0s*c_LG]g9`Cid3^} iEpL~s&uNf)2VWWDyaQ_/^/~̟̟o%?ާ\iG۾~vkn1#]wag/{N&U}ߙۓ:[Z^z8C5xr}N]uQat˝C9XdrpGhw3}+玁ϮJt^^=n_:{o0 zvg.9itѷ]AUoog^W1N}eZzzw/-Bk6sn[~3N=3u.3>8S\t;4qpپUZ&EO;}SDN'ٯ _g忕s;:O 3:=y|b4<¯VM/feྡ͘?_'ξHzԫȏ]iH_⧸%{AGDLj|_/=|x<?AKY?C)~z>~u[&Lozu5$SyZϔ>nٓ&Iݚѝ}P|h͑ϴD"qcqj P'Y4Lc`L-?^uy0`74}K)?|˴uٽteVKМtG4*ҜNr.'Q>T:r,}OA5yhaGKxPų|@wv=.룆3˻zNڎv{|= y=^q49= }ink+ķ''~q}-yG~nJ̄wys_}amxo#^o}=lWԫ_Q=LҽSZ{'q}a]ר)y:=fP&/%q/\~w'9Z]̵8^WXylOWzLW/cW?C l8<oҗ=>+KdV\3z^d/z]f=>BoCA= ^pMzw>9=GM1^ԾLκrW,u>RQϐGdy:Ξ|\oۢ9dZ0J6M+"FLW ĮMX7[fHtBRV&=YuVi fm%Ǻp2gvu$4dab!QV6l50{ũUel[\%+$(LC@E +! B+h&<8imy '4R0 \澕=yl/OWh;pV.=JT h$AH,O:۟j޸FK !JQ;~0;\͟HRō6V,D\Fh{ng_76bC"ڔ԰SD[ ֟egٛ맛~r+wKJUͭMKb"9N"z?+OOȿ[\2c:FTă:ͷп;}~â&fßԢR fvegڻ-v~7<:l8Mmx*Q钉ם8S.'_VTFMxc൓ Γ4[\/1&>@n[f YcxE.r4ag{{_5;Kw**V6תy{xn=RxSbe Dz7u*5D-Yr2-+4AOq:9?U>:}* UB8MP1S)_g;)W>zV$ #eB@ c!)TJ@&MiF3I%UuXcFÜ]ey235cC@H(ZjFpԅVe4]F󏍓Eiv߯4 4 τJ<<ǯ/*T$0C J4&  D@ =}2LN_xϦ^5(Y^}bMJ4>o(p?7.]腑E-+uǜFq:ﳾ0?|O;pcV-yK"cyQa)\.U N&ӕ&} ]q[yed%m*QC60'5*`glpv+`  (h4(%84j(\2$V@ 42FP5Nuo4be;:C2kd|X&Mڭ>:Q>]y|8Tfg)FTDad0JHB MP[^m?Xbu}ey}XmǏRޓO=[=_3~@Qu\ i4|__OPWD~rKۇFW?V,m+ʢ?ϛԗS>{ƳiscUVl#%*寫r~ؼ`!Dz.{>}/Қj_K|N^]}39kRSoG{53Zriݞ~.2>o J<= }{z \~6:Z,nYr%W5uTg]06O7|?W{,..!қ杇Q]ꍱe㯱uߨ^|?_*ʧoyon/?fN.g-ƍ#|7G6YF\Ϡ~|xMfV%Y;8vQF\ݵUPB$m8씫ug#18yһ Vg:Mq*}fx_f+xrxk'Hmt%`Ɋbl ifpEj&8i#M" \d& ``M%ddbbd*1illmg/)λ; &0UBc/zuO Ѩ)6VߣYji)Gc,hh 'z>~.V/O7|iN]F5TlRHo;׏>1 %tAg^W\^MXD.yxbs>y{7Ա >r濥~k˲y'7O*oiu&wx\޳ew;+N\,M|m% I}=xBzlq1Cϧ] K Xkc˵dL=;꿕#oM5Vl;N+D=q:p%}-\~k6*YgtYXuM-{M^>v?B|wacv=yUc_F;_-yPLxdU lx_zp~'gs3xuQMwtunlvau|B|L.k~$lyʑjU8kǔ*ȀޑVn+HTɀJZ9`W#23IYUem8X{qjYYfj\HLQL{bcįt=3}8xƥ$M0!*hHBr# *L)XEX'1nkRVF-Y]vթU7SN6N6EjL!eWexGzouϗ}or6fvNלM>ϖ}=Zެ]N<2+(#jr[ThomnYN&f=Uu\lRڜjM Jq!nϯs~CZ6՛PF3BR6+OOo:y~L_NWf=Yq^e;.*ʆu ʰƯ&ߠuftUǥd7D%uU1LHfcVG썞o?|/w]>]Q ʚ/3zs!.,2ccC?~,>X󭦫m YQeFk9K,HfӝPXxyXy]5ʏ5fS-AplC&wŎMx-k۽}sO̽Pq#l58uu;:ŧ.]m}d5suFN- FJ- .3KR>D/sr5v GahkYՓge{.#e5BCRqToc)6NC;=K&uuvܥK !(@h#$F*DFKk!Vd8[lcLR'qjIeU2좫h}J {)'].t'tOKnQYUnuFnל,ڼY\k Fn㥕 8BX96(СtJUYwָ4t|{-cdq˙DreWOYx~W2vQY^grXU.=Yeo;ç/gż .42T%ů2^nw\zyǪ1ɊSpiV-#F||`,q:M>7;ޛ}Bʑ .3Gadv47W7/WQs}15p՝hUzZvܺvgi:g?ڽ}51촬M c3?I9)q+̯bdc_cno篡~x:d/W5%cSiϦrq?̿M|ǝnvEĿ~&*Ξ4m= n;ZjZ{s1w> ⿴.2|IIo^80͑5m1ӳ:Sҟtg=g9Y$&6,a5_ִJ1-]hPQF)E0`0@EIcFk8:Y\X2,*PDy4dfӖM8 &5h"Õvkqk18*`41l'Xo-Y3Ŏe }rO5HA'\I#\f$Րʬkܫ"Lll]J/ܻ6 %e7K᎘QӟT,kΟCGCG?@=~}>;wE"kWo]QzdU7yٞy7Ϸ8X6dsOIͮ/5'_k#^}ſq'\oQդ٦=]*$ҭ܎~=_Mo9~EۇĹj%Ye^ fS^:91fn/Ni@:a*Fsk5kmdʶp~ *>uǝR3e5ϦO>g=Sj%sJCu#ɸ>1y~潾bX:K]9ց駱 o{ɚڼgs=&}{yF3y8K`՚ŷk}9yvZ=q>"o(롞މDӜ}Gq;93h7g/$K|}k^T\։Gl=0CYO9ktsY蒳Σy}.K|'Ÿ~̆c[o_iG+_;/}b{8rMAi1  6W]0.󅗇k=/YoC73fu~5طʹ&BА$ X-vXD"¶LA",'U7f%JFlc88SNfT)2UJYpɺJQD"1 5 Jk(+S/Snɧ8j3(84x f=Le3\&p`0Lכ`.ؤ׭5fmߘ:iE96%kMMqk{5hBn)0]fmCVqچ3.4&lάٕ6lխhXa3c uؓhI [#VГVmCWnpN6E]X`BpZj?4>V.c/Ls.90/e*:+vGӲggg>gr'w>;y$oo&ϩ}/_;.v$z8ګQ+OE#>MrhgI~GQ㧯Ux^~BGPv{/|ڧ';Ng?b=54^r{;^< _:`;0C\3eo/QH۫R+\#/Or|=~+ 9AoGT{Ğr޽+޾11}?7^Z1' .}^|bn;š{>#'B{|!k }֟$\ݵԂLX<_!wz׆=5?q. /:qp!S#{D, gJ^c/pXG}?>AXy|Bp& 0m3s9{v?U?J~J*`J" G{k1}73E0ΩRBN=Kߖ¿kS/}cmyVE,ŎH6^ӗb9|Gt^QFX+Yimy-oٟ7鍑Ag:zחYٝum7;yC-uz5Ԧ!{(o}9ɾ]w7]lZ( 5 Hn,%S""V++'LrJ*8/ɧI0h& 740JA2eAXڌj!f..rs1c[3se b`0"2aE  %$ (L"4F3IΚwl,g %d#5PʬVUsV6F>..VVt0C`+]' t=:6p8h!`&`bL@i [_kX"'ypR kɽ^L>_;}ux#"X- Y]SOO|<䯭~I?3z% Ǖ,|ip[G?OӟXƍ21+J@<޷5[]^fՋ]Zk(ָsBVEG }4}1S~vwH͊TrJuN &zW֟'}c׆/_RsTdc57Vv|hp@mHo N 7ucPbn/6ϋ!ˬdz͒}Ak[|cm+jɷ'ۇ-G/@}mGֻGU=cӋUqͧ<7:#*Y7s >b{]mj%+HBRUEN-QDzrU8ؕבZ8q"LMQ} d  R@4 TXp ҥ") j6Q++K'UN&&h0CT& im2h.Ib%.bB*ԪjԦ1mƾ^1 L[U!8c?z?|.~pD&2,LC@J"` bJ ꯐ-gD,|ngM5t2}WNlN_;}u8TzUݺrwzC\{!4>?7}pߒ~wam\P ^= M˵o~>do^ą8}37>]ׇ|>M\6o =fY`N6E2SzgG˥%=D `E4-mVy> tfz?"}w׆7_Rs9xxVE1WWw˟Fy{~y_/?6qo <+;jL]1}\j)+%ײgy']o굪*ȫ:-jحd֟%c=Wz\|s=[6Ɇ]NQRn B,MFYeF 3rěZʵb!)39\"B! L  TI"$*EBMa `0IƇӀF)! # 61)"*qed!8jSMSbo88x!L@`.:Qyl'Ҡǯ٤:]yqR#(hC&"HC9"HC뿎=׉OuN=}UWdHpSG~ڠXRFWyoC?!eY$8^c_h>Q=#}+^?ϑ5sȣj*筍W ~u. T#㯱p_~d<+^*3Y_}ۏwWˬFRAM}nW`lǢzA?:s}Rxz \S΋/~i jNx'ҟ#S^B6GGmWֽ<.EfTC鏚=ק>+oXnŽ_#F_xIew^XK,Nrjڅl%J+sBubl JRJĂͤ+IkVz\'^\:OLl$2M i B&6m5`4 11@*Qq1  hRL$ГI\2B+Ԫ1110@_ؘ`pJ<<ۯ?'t;v.Na⾃k8 hJh A$m4"A }["@,FWy\}Eއ{]덌T,ƣmg~ȿ]"Džaz$AX%?Ӎ_4-VFF>v=Ufb,KKc-E N[^lu)颍βxdכKE-|eu>9~o'鿙vfSVT1qz\Ej ێ믓>aͳsݱKȢ)tb|%fWF^%5]{ecQߖ4raVFNZK[,64/ޡ徥xr}S]iwW|Wʓ# ā%4$J 9ݔn"2-o>#X/Q|ڦN {Q64&&2T  C @ u"(LȃUJp8M8*2,L&HPBpzm:R1snF7:N Ma8c?z_u򜪳hUJ0Y !bHLX Ђ+VD5nKQ4@+rFߏ>.Žźq3?f*Ϊ1R1cȄ/Z}A}Lf/EJbU7*Mk '_![Hx$X@Ŏ\eYQX%sudcvS?4omlXf+pc0b\%Æjfϼ{ƿ?_xC]1tvnY˃ 29U>9>\b󝆿wuGxïa3[^!jưί:<֮>rqלzB➋{HiڼoWNҬk]/>O(|(W,O<8׬8H[V C1|3o9#f~E8?O9aqz:ct5|cz3˾Pӣ)v2Ȍ(/QAtZU%2Y|G"NJt,mbG1fzUG[KG9~LɦeSM8Ch`i( b  L#T+)Wk̈́# ƁQ9HV[%)[<\@`"4vqITTB3klVe]찵Mwjqd> 0M0[.:crV/q~LBk;8ڳ}/vec[93kɝdS]Hr˪#:uԇ.p9ҳ:_l>x澗ιz=Ma1 53,0kdtG1ʣ|>R߭~>\k'eņlcjj-xz-p-zO gSUd4](Ć\\m3*,ǃϟ|),+V\RULVJNkW!^T|?)|ky|z&AJU*"FJ\z37su]UyLS!VEȢ9V˩]oKwNZMϝv<.r[$/fKgDy(z~T'K*lc;p<UNWDixex{4Y/QxU׵=úW)9'f3kH=+ xct#ݧ=%Şw=~W#=rOi@>ύ=?}>RI|Gа >k?A-=A!|x'U g7_>ξ3ǼgnߢoPO{<?UzWƾzKW|^o>ϑ_qoGc^ҼZLKFg#Ä+6Ϫ{_̾2y.727^'SKiTaVr&_};_=qqDH]s:.Wqm9u?ռÏ돑7l8Q D$Ѐ0 &! l @ځ)" R(@BHi0 ɷrD'`2'>){ۋ9|um{wgLHMXM%hg}{yGy%{VS6E>y>OLYOn|jQ&h{ǁ, b+h~t'FRedn{q R &EBL&MQ'  h `ЩBq֘z߉vϣuI"lz2ޭR,18E:` eB[%f/lU[睏"NwGLb& HiBpJl}-` 1 b ޼sZW:EY!Njn2ƓogcghI4C@cdHPjR*VEËadaqd\6@T40@04(TbJ+_! k"\!LJHLL0L&RJ M2, ( ZhI!$ ?{66V5?szgJ/< v'bWLVВC|?&'sk(q,X ;lvfaf爚&! h}6^y@d”\T@_,rϸ2@@ĉ$ iQa^D*ilrءM6v'ޗnxZ2%(N& i &TG.0̈Į~m/^z$эAqh6{7ϯ평mG.4% I`N37]|o|WڼW2B$&Ȱ[]ԀH{ v7|:y~VQ `& !N1r1) &"P )@ 6Dh$!!V$JP?fb>ż>9.jz/dlqM9g-ǒ8#iȿ\oQ~?+;|w!`(B JOeGO7O1_WϢ}y~<eWV[^Qr^hs'W yc掞 ;{*~~⏆׺/M>>t:C/b}|CH6f›Fqr)`je-]l}_Lk-[3msMXhF hi t*lRUbluYy'&MRC)l4) .(DQ8YVVx@FHI-mg>R\ 0IMC}\}ƻpJK$$&#lp"%2~r}Iw/{^9ZVNS+j@$-'-H"!l p@ Q"`( MR 2*h" $ HCh>F=1r׉ICeL$ő3mԼX_mqaH# meg9Dž?这6CK",rRT[**?\{ӟȶW/I+^_4c;g.LSoMu]Um}<7y;:[ n~g&UmseIu{f:mox[Wc|Ľ&ƌ?#q[o k뷏(_CkSϧ1[/GwЕϵ9Gw^jx~Ǐ_}w|8ߎlY} !]ȁb " 6Dd$$E X$U͐v2R \ŬUO?*!lS)*lTUQ0JHM 8J?XǼS~Ǽܽ/|{F|,_@О +<-w{ܧr~g֟$}jo)mu;z+nO9x19پk߉/nns͓ʻb.j"V߻}:r"x:~23\m~sU'8mZ{ώPQ2o`}xy?J|z]!)o)-׏ 7 a6}Eu!e!T"Ih"#~{>vqkKsǰ(dQ4L&'@@4!Ɣ\iVar1w2 #o*K!qEHI&E u(H{u*+- VfU}+ 0C1@M8c}>{ӗȶg/]Fg;ď>A5XD.Y+ߟ$"gyq<WOxWݲ~>y8Ƨu?@NחCνeǷfRgeѝA{~Q|_!۪.Sβ:s5ϲĿD@ٺߛ2w曟QO/|hv{o"hE}ؤrQ "2#$M "I0ƛHfU&&I܊հ*#4%6FSQPTZDP[XՊ I 2!J 'm,LkK=N}C-8_.vzYYsFo77 = Gﴜ{a>g~oq^Ix.-yν'WG7Gy;}ٸx͟J:9c匿<]1To5Klwxٸ;,w[ #Ec?=[bdqWqvvCW3NJRΫRKZ;.0vRZjVލ=LD0 4 4UEUk:Tˌ+ڪqd"%*[*qc I 8㍗Ҳa\'!z~d\vcQlg΋'(h@ JHB3tٍIY ^ ۬,Lke`25(2%qL~+t#)c)rFdX|Yfeoyo}7ξ1_39ϮAv*Qr5=:y};ʆ^zf!a30 `-#28,K b}:-q:Ѱz6f.LAPʖ(fA)bfarfj `i&yfZ-xjݛ(F:Wf|6wjC4ŒlV f<$luaPxXܪ"TK",vői&혹x_Ki[w ԴE葼z'Vn*]Դn7/Kb~k#ϽKq[FizPܚR]щ4nV>8y?iozl ;MՆԳjZ'IzW?>|o˟xӶZٱzFtpI XMrl[c5xyG˿j|uzJzMvK\&k=UY; Hp&04c#e.FRExLdLDfD! @I" P#(REu2W)NXifweؘ΅ͽ NCYJMuR̂e(!8u,JL-y5Kk(kάlњh@L@+3Z1syaxG^2//fك|2\U]w+Xr_^WOq+?rR3qu{>3^ {_=O^//z llxQZ{w __@>}>A>wB0<_?K'WW>{9[%'~t>g·A19FGѡ=G!t~JH>k>h$ϛJ>l.l_JZT>kAcL>l_JkT|/_I\+}$I5C6H$}"'ͧE|}$/͓A'N>l|}"Go"ϛc ߠ1eP!?'(?6~nH:_H8G*WΧѮO'`|ˢMFb8?7;衯|쾊e@;D>"ݛ̈́m2=3D|+1(ˆ 4#0C1qk9Ȍ`IY`VX-eY` Y``VD eJ+,   +, Y00+s+`VL MSee0 L L $$$$$$$AAAAL!bHHd 2Ł2H*.A2XIII " $$$$$$$$$$$$$$$l!R0C0C H$-[$aAA00 001",T ZYQhTEE)WX2PHN I%QNY!`&45dD7WJBia-K m !Q&V7RULRSJ8CYf8ԄEcY~]ۛυ0anܽUlNSAp;eqԞ:g/N$vxSvxdۍ>b`@011':ҡm+ UAqH\RB\e!s ) q*B!+I'L a `Hq4c@1 AbjLL@F0C PM0H`4  CC``4&4 @10h!)DLSG LNh'@(b`4PӄL14BhXVP4M+q!8 vyҐ bg +VcgbeIdaBRU@Ӊ0P X4 $ T$Ѐ-1I!%I(cCS"6!ZמTu簢\ȧ>\noAT1t=>5L*D@BM! 5$ &@1C `"@$fH CLPM% ICACL&5EOjv:LJLv KZU %8DY:ee"VTr *[pLF&AT#eRg[ES2k1ƕb̻0ʞ.uXL*X,U-0q,YO b)s%02#*Xh˖L׊.l1m:;y~}<!!BmhB hCBAq` J#"v`@ C%R@02*'鞻 +#Z!SuzRbgb`wZgA~\s}Q|z0I  LhCILe@@ bD%n-A@q&Fq`вBI &m+ @*53Q02@) $FS#H- "D($lBm +HFI0C 180ȱIIc"JWǧ4L0 5`Е@ 4Abj!H#$(J4E"NB-* %(2VUy3!s+jflf l$cqp ' @ )II&&pj '\]5(,ALZ9>yr67)>LL^]XY1 Wl+u1tp\vѰ"3Pđ  0J @ @ LCqdP DAThEhh40in+"(lh5C@4@ ('f25 đ,8dSUI aF݉X9FP F$S@& 8I0F3`RQHj Ij#0L` CC"$7uSA Pt `A5@`&!D4 @eb!1 %EQ" dqK|!0`0CDS,YmE:d-+! Ee8Yanq4P*'Sh$@RL"Iaqu]EV@L{qruY 4fY +/S]pKzF\w$0I8 cJ) hd!DAFB5$2a(Qci L`CDJACN[0@ $!mQ`& ɪI[4 ؒF$+LRI%LMQ)I"dM%&*lR'&E+"DRD]i$ b37XI%CH"DA@@M\&p pPȰ44j5@!@MP5 " ATAh" @!(!!(HDL.{m8b@Y A6$@uم[ft,˄YnKɁEYk%-6@b*DXE :M r@#*萞=&'UlL,yJYI1R,vWMf.^.03q|v i!DpƑQD"CBb`0T I EINU @ډ0DT!i0iW$HC T۩yPFN@Bq`8( RcXd&4Đ"2Ra4@(&BF P )ʲ%1@iH`IȍXQWp]3v@ P@*b @!  "M!BCe *IEcČgZjE#mĉ!D7*و#.6dXJȔ2ydrtID!!NUxSd)&$E88QSTetdąI-\-*kM0dSD+붭JhǬLL=0.:h! 6Tm` !*C"2$(RLҩLM"A!8 LDh t 'TJ"ԐJ@Y$DBKN2)F`iX H ) QrPjDD8J4br4Dt,&"@A(ШhA$ HJHlCCLLhdE! T9|yϨ1 @"14B!R!&e"5*"eDX24D`j.*$Dn,l- =ݼ6[͕rkV7YtisvʼL(%XV o2 ,$HA!!8QSq`*DJ8 S7XZHR5]B+iƬ\LMb/QdiFLAjȰN)!0hRB$ L@&  14Q$E@`4,RHAū@ f&Zh68ʡ4 S)J [DJ*&8(6&)6$E8h#!&4!(BD2QX8bH")>=I14X@@@4`CC  - GiBDjd&(J"N!4D Ӎ(4lLIRTYcLJA^y^x˰α~s0fF3D䐞, \/޽^V5 4\S "d)!e(Li&FmaeV4a&@:D 4XE! +]ёpop*N  H` ĪqdVR4 ҶB$DM1J ؉)!J," hdp8& FqE]J*Pĩh`q& %3@I:"܎ PfDn2$VƀjJ THRb& b(RA(AIdZbm I #ڡ! %%^}~}>k@D $40@4 V X A4 T)D@-" 1H E"jBbQr-@Z+^ 2# Ʊ^y}]i[ϓ50cۏqlXEJc,eRˁǶlp,ҩE#1 &9 bʭMUCdI "dBBp&!\Q!ew5pS/Ynj3- 102AE&e ![C" ( ŬJ1MRLbdZ))N[Eȱ  I !d%`!(R`]VOH0Ԣ7*SE Dk(ICMaHDI *LR8̶2jDP"HhVA[ q BTҔ d E @ @)2#`4 0 Hм}HG#0 hh@!FPh4L@#8M[HEJ4DE$RiE1! q^WjQ14 K 4)RP8 U29Zϟޕr:[s4(EL&EL)-V ¥A* uΫG(&QD*d5]nPHNP7eNJ& 0I## ¡\V>F=ؙX zftMj ĖQi;NV0U8QD@h(M2jB`Dd)H1)b 12 +/QE&)KԢ rIDM)(bqDFMLBCjD@XJ!(@FB'cB2J#(3T@LJQ&EνzVhр`   M( %IJ#4$d BČg)"P)Y: UdR\:TR"HI# ]j$h%HBVctM^auFrBEIP2bRBR* Q*41v%nQT%(J$' 3)'5[Dkǐ+a&&(& DQ# FR/q251(<.ZM1 @H1 RE CD Se' qcp4X5$" Ha$%N-@&#dFA$0&.|BADNDeIŃԪ-N+(V#S9F1AgD)J.`ń$h%@SC I)&ʱ8U;O?}EVC@1& h!qQI`\lb(+Ab"E8҅XBJDDVVF3nJd FDE @@DTf9jJQ\%( 0P „»*$@tNq"DA#M3V&M6 RN4Edk,rc6&V.q|:b`C"4@2,$ L$"QD4$$H(8¹TYH3=5ꆐhV@hQ0410#$l"IRaj!DhUc&*J HC$ӆ+@"a DM@$D$"TT: F>(g +#\LM`#I@ HH@(D1&DFQ0i%I5 Cp "4Ԣ6I q  @d!cҕʷdCC$i #L5*$SHqmP4BlI lNQ0)# 3ĦI4Ha 1Ż`RA4(I#iD$M+Q8'<мs՚8hC@"h14GM:#$ abe$D\I¸T!$(%aXFur IAB 5`hCH X *A5MŒHMR 0\IŌN MS @F1J2(jpWu'5[]Tk+U*l͒BP"Uj;F>[84]3ټc[ 'H !@gnFMf.:)̦BY`%(J' #1qr)aA(iTEL"J,D[,RI @,Jd%(LMI14h)HL(8`u(JӁ!e$& Y(@rqu !( V,XJN2fqapd&jJʋJTFC0)!BR i)@+#*RĄkIЩ%HJJ"K= Qi0HV! !@&` XRSBN EJ6HEdiUuvAN%u #Mal+vzBgalj#VpĩX dI!NB(BkEq]-*[(uY7_f/[mV>5:ml1&$*Q#U l8HQHHFqHS|51m2`-2u1hQ)"1(TƢlBR24H(@$BElLM)$RN%bJB$80@ȍHM8B"I ԐL"M RI%iFP0 Ūn0(ĉ)!DjdSr)Dȩ H$4:!H@4*H8J2-\RJ İiqt("DfP]8JC61DT10@ @t!0i0@ 4 HBBEI @FQDF3 BRejp5,P81,q?'a]Li". 4@e M1S aTM!vH2mȶ`VhjblUwBXݍee+JrM@ jLdc8qa# #jYEKf;Mz4>aTI!$ I$4 ՊQ`0QL!!J0jH*4$d'* "L R HrHIIA !1$B\""Hc$N lIH@4 TBJ╄0LpDtf8%hQ!N-A%`S!`88%QS7khD$&ċRqq%ȴ羃tϦ26"ZhqjL2#@ `+B\F%c  3"MX(J4BP%U Jʫ*gj,kPaU%Br& 8h2D!lt2"ReV\UcYVE)Īɂ*|IQVЀ# JI-cSdgU21hm5XeY[\k(b0n(m6AI(JDTJI 6ŃV"Ѥ(9#&"E,N"$D$DeH(J@ر"d4(cRks4d frDDY `ć(:E (+qNJcP`VqS"E(ńlIdDABL84HC`ȍe4Ie]OuQs嶚V`@H4 %4i#C@@F ` F2QJ 'B,!4E gBqH%q! *BWzA=;rYdIVVYd,(hbč$@R$D$4Qm$`Q2MT$%47\HC"+MiE ʫHm U/L 뎷l6VoeKX 2hP\z`|*HH8) 6ҌC FHR@B` +MPԐ!"J-XiL"`4JR#$CN5$'E!2I DChQvNY"CY&DX#qj8S4RMZRI:Bdeg*hTIDR`CJI8qS@4ԊabN(nhR@)F)"'kd$m+fEJqRHTHR{]J D @Ȍ@&P6D (4Bh HhtBbjĚP*YH$ql,Vºw~˯gz|܌RPnUVSflN()ĬUa"&`]LDY p0P#$FPq` F3 qnKTbud h$Ւ#(PiEUSYeVj=g;_-겯UF&>]^Fau,Ҏ,C 4"$& 4`Iؔh@hi&8+ ("*T؍)IE$"ѢG%dJ,'[Y$nPQ6B,RJ(DQm j3cY!Eq&)e]jiY'n; BL&"PLV Z!$B,jDHB%E7j q`jPĒN EŢP؄ӡIE|yϥ]Ev+C!hC @ CM@A0C ƤjBM!*@8 #(ؠ+U`몡b?vxMt[Yˬ :[yN^~nCyjĤRQBuefD"@sKlc -U;Ld[ YivM9SFN>f-(q!)&QtPck} =nn\X`8]a~w+ZnL:g 6*1 sΧcvuI ,@aDV!`QjƑ&V!a VR݃JV$HC%CmF$b'LSƣ&E4"LŌ@( Ek2J+RiÄEs'c")DCI:Q# CC`86M )IőcR$LJDN22B"+DH IDNwF#``M4 $FHj2E IDT !dF" !%*BE8Cԭ#\d!dj(h7qϯKC;9:Yِ\ۗޥf|>z<,8R$UU_-T56 QqcV55۝6610HBU8Sfy:6=\q |οY9>ߞsԑ "S(vcB9͖ Z(iÎgLg|jL<<=K]3\dB&J# $1 #! RT4Fȱ8$ I1I!hVbhT1 h(h G(2I`avW"J,iIHHc Qr k1FD' 1H$5(QFՊP@$D[v0CE"PI%I(RMSJRj$BnPԢ)Q3E dASP ":QcS(*&C1&iCL"4 ))!@CAJJeBN ԊaQ*lB7y>xlvʢIFݧI^yjw[:g],ڥ:{y-RӍ۔,ǞYu=?ǦyYE˞(n,m"RMFcQ +˫y?t~wd"6z xvgq̌isD%0QHU-~#溮2XDiL&矷cuع\Ul#/SQRH-I &@&dU$R'd(M-IA!18RQI8ED؄48!!b$BbDP!(0@22!eS 7 JI8TIH%q#`%iFBS&! PZSD$(9TZc@8إD 89 0D⤄%A(&!$INkm@d0&&`@#@1&&CTZ j  @A5Hqh)¢0 &Ӎes>G>iy-N:*͚>L;1RکNasf(cXТl{s-^Üo:>;p.6.|,jFULf)-' @4(8iyl,%cS 1*e\ XDT$ Ozy]'{uǗ_tޥf-Hu7Όr2ljB\Hjg f^n9Aāʘ M $5KBIQ0QlPu le98Wl/"M*r)ƬV/BيDF!e 8$M)*SDB!:4 8%0qH$:ĚLM 4qV0!Q7 &06F8"N:M$rHRbb5$%.;c8 BFM !$ !Bq :qlCddN!(Ҧ` CQP:bPHIFDd4ЛqA!D$$*dHO3C`@EBc@M1$hhI4&4 AQ$0WI@ED-eJFp)DՑRU!kRTtX<;n2Bv{OC Ykq=5kQDkxY귈Z䶲[!) <+K4Cq!9TdYT-BR'Fa$Ӕ@ 4!b1e+#8<áɾEMf9⚭J궫1qu1궙ziSf,Dbj&$%%PqFв"%vqC4dmH3H8pIq#!"F5rH (HD:(qFJ1 8$4HN2Q h8`}gF2! P*BpQ(8YL"qjRd'Hq& MHR0Q$NQFP`#1Q iqp2I 4r-ɪ Z"HqI$(`2& qb&@!CA@&11)E+"h% !&ZN0BKH`4B2\wB;stWc˽;e[O=2~mNnI4vݪ3}QE]}sk2MV&˞.RA"q %)Fb U !1S+9F@$a"jhT2p59S&Nvx9YQMBO(Weu]V¨+Swa0BZ`14 j@M2.2I Z& [7H&5bb$ ,@9$TQMXj! Cb EB!X(pi5D8%mJDR,hp"B$)D% #&R23"5BNr"R(2I.,N6B ĔdM D% @r!!R%n$ #sNPN2d@A4.{jlz< {Riŀ0! D:@L#$4!1T1L@IX H(H,YdT$t%((f_{,ikK=ygvb׭a-RYpJ2,6 Q12.qu6`(4ks̆ T0@ʜJppF]\`$*BTY8Q Tb좘ʍjXmu]vqq/Lj2)=/+ٸJA$i#$@%41qh""\d$-J,nAI,\JUQ ŲVE+BX%0IƤ) 3+$P\MJɦI8nA `)rJr΋$R#DI 3!4$i_,I&I$Q2*J$$B*NXBI"NYDijMW"iDTY**ҙJFƸ:x(Œd\HDHN)^}zk0ަU[&J0N4E0=_:-% @ǿLSQ)E# «YM鏉5NF>ut:\t5\zOyi6xgW^Xّ*ީIteUvE(n1&K['©RLlk׏YbeFِR%ʑnt2rRkt* eH^T:tk&"ҧmʧR3J$Xj$D, P V:9Ʀm$"[5 EHK%UQc924FjЀ5p섬+&$6@%[$J8ҖW#cgb%bE4@%R" @A$$" @ ! 81 &J((ʂ(rB1"DY('D] " .$ "I% !If;[Qfso>D*lR=o"_]ُRqc&bRVS,;vVvg廜'Q RF]m[4jMυܶHrCISB1LeE&eKE2oU9lpdI F!b1Ѳma QFUҋU d+)ǣ*ѝk4G>Χ`c8|s&S,̖ 4ia30uўaF\&|p⹫ &\qbf ^<!6o MHRFjQZPu-ĴGOM$ft\'Sg?boW;Uu8N\:7*^fzE)·Fuq惧|:ˣ\tb4yr8|zH)#|:Órל:;#ھ47Fޭw,Mthݚ8BhZ%뎆^Kϗաdz|΢|u&\yjrxԝuR l;q;^\l\Hw.Rzm=H=PeBՏ)I+⾸=r#yx~BAEk=¿ ֟Be|={|gxǻO'UJ/ggѓIm4gF?WϬ_?^ϟ i{i|go}|}/>}xg_gt}w_έ~>vIQ}xKA=W?􌽮·5X27DǍjB2 ~Dael5*ȩp̤fzrnSϷ-u|zs4m,яa^CLuwtgS%1Y,L)d^[1j0b0D7rHJ3ʉ,F3F, C(1c(fC"5YmeqQ+&P̝6ڸ岷,K=+/Y9ՠȉfLUe$2Yq0ͭ1mʚ%ke8y!ьeJ{ۧ3>9(30bz#gӵH+Euu%} 9_^Wg]$˝Wn8mi9Xvu8=MW.΄+7UJT/ K B«H0INFݞoR֧a`՞ǟUi&yU`]OF[c6Zھ2VFa)HQ6cJQ+A72*r*m+J$B@(a$ǺUӮR8leS(N5+y0q啬4s6^η k'@ԢF2SʱR!e5B!ppiu-11qӐv|zwu :Hat9FtR9H'6ay4X8h3_S ̝Np҉.G3_W ykή)YpZi.ҫy:99z.S.QpqjNjD}tЗFٌ-먞*f]BU:f;9 .14ƒϾk} 9tNr]4eN盷vhgzΊ[Yeg߬gj\kZjRy;[ty.}v釙mNzɍl]! i*к*:Ӟ7sL1Vz+iΆ3\ݛ#:P掊'>9] S{:,ѵ~>v,i_98֮ܭ9oɭ]*uZ;7W1.9 b4G5}Zz$hN5Um0L,y|ΞZ wMf;s&'G6j% bH-YF;ZZڳR5` lk bFjLh$HcJTAbHqA4VUK_dYUl,s& "1<[cnp*`ur mÿ͕RLhhbE•6S\w5ןޭY8ӅѦ4E$I puV[dx93Yׅg"5BإJqcS]k1j1T餧uKZ#76D{9&'Zsqg#Wk65ۋ[hn!Β;{)mY۳IZl^̍lMSIMMƗzWˮwsc'˅t->IFno1,_(emrM-Ryjͪ5hR[jyB`ݲ^.d6M5fWfVk,0Rkf;(ٯe7*WY̅ap5ivm9F,deY!q7.]ceN\C*ɬb&fsk}ki^.6bAu[Vj su5I:%@mI[h͔[fUcШWy*;%;Yx7/˙'dvjji{tbh+1_ɝ91厒Z=oacm-UwZPn\EnD]! ,whK[ĺCrFCvH-tf.o"h2hͰ6予na5[RMDު;TKሩ@2P( @ 2ƿKe BُdhFd! aXw׏]2'SvO:21g%,Ӕi@@nį.N^?̻M> Yu/N]/[a\N$ܪICX]V56Kw'}6]vi[9EXT#r1˫Sk8Q΍`QƬsN̉q^\3a0iWf) (l`kVlb( 3ƶ%s^fc"RO"Y\C-V%Y3sgnC7?{.>D㝣-KUl %qȺL)f֬$pl毲̋o9X1!9V3`bU̍#i,R# yĖS\Ye82b6`V4Cu&οWaj[|R6.Y(dNlǷ"q~u+E[+Sgbּ%8hk5 Ig30 3R`,ĸK5QF 1 çaRcMpF̉e)q\W%Îdk e_ҔlቌNQ4(N6af뷙l4j16UgC*TfD/ƾY+崙ƴ9nk&uKVDr Ё#djҌaq1n +ܾy Yn.sVS(4Xh75:.sW-gʺ 5t6+łfL؊œfa|lǎDldF)ͅ*&Ocdr]w5lp ~s|],Iȸǖc¯bQ=Q.2 c!Y9uYg1aיٯ6`-6ҥȬVKd$d#e@EᎯu^c!YBeE1cŎLW^9q1*Ψ QJ S <-ȄɪI DHNVQ\l2w2cJYf}U B&$j7;Σm=bgSviU*rXN  #@F2Z$WFJ1v:.No 7\mIci+q2)j69ӭ/?*]c(:MsK(O˪JdeQUQ%ȅaj~k 3Sk-"*-EP5J#xc"0*Ϊݮ*."z).f5yѬ5IފZEUMc45|^Y2vbhJ1y\%1LZj:3.psXSUl[mm c,%Kݍksq-ES+VjЦ7JV@ԄxY~we݃U~6+y徳˦nvZ@Yc)- UKU+TZ#_)SV%^5Ƶ*JFXש3tˊ96ZUm]Q-.֕p$61Wa/.hs!̮u|kqYųJ'U+1ȉR(WƩ. TIBYAj*W*L.VPTXF4oUŪ%"%"(4uu|p "AJ)J_Vf>JqTn0.)kv#ʔVNKT*r*d,Dd h&(&DU"}b0s%d&$WGJl$"F ( 7ˬ',ёU2DERv)D9`N$c8XN.)_;lhFNBS ӿr*ҸFD$ەqAaUcf=yTeUyJQ[EM)TI5d 4ectBq榃'c XƓ*ɫ!)` TenaaY`VX;Yc)VJD @DÄ9 rB$6E0u}wL7:N}(ysWŨrrR>gŴsR6`*B@%d4U e.P$$Ȓd eȁDm5(.kȾr !)`-QTo,/KS%pE7k03u9\YeVan52ZTwءt"jDsXٕdµb+&TUTVF6%4ҪYRUPSu(I%p5UveTSZCMdL\*/&- F"i7f4$Չ% D``i!#( R-%SV iH$`B.#ewRl2EX8zwBrD)«fU~~>V+56BRJHaA F̍˅Bl6ܩDNJp8NҮlŌ-URV,Jad5#:l&  DņL51cM3R& F@'6EH*N"UBX j(uMss;cǞ2\5DxKu66*iF@2cTSRv`i\bK% )T6D[ I!RB 0C6D``)! "B(cco:}՛dr+ IӑdOL-~:+]dsܚC0 І) Ƕ X H"H(vUc!5+CȫUmRu lU4)$*["H݋Su#\7MTdb8%MZA+, bAMSD#jʣljU8oKV[D@C@5bʌ(NP`!1 $& 0M M 201 LtJnR$@l&]8$`U93U4XY6rdNDEfUeJ)"2dA"I`&D*ɊFstgHM<UƮY3Ǘ!nfDʘ (Fh7b[c +ciN   P&0DrqL0Il0JHvBKP[I YMҜra0hpS"]ae!m3nqki HC) hC!WuZ#(4Tcffu:aԘbEHt,Z%4VN Sbij뜝GןpX?u]XۅYPrMRd Id! hlB3mU kt3gs=7|Roݝis%ëi8l0ƭ*59 Gu5ϻιE'ErMIbETDY&1@hI8Ć10##L)]rFD d(&B씫E4ޤ!+Ʃjl"cFuߠrDnu k"X+d2{z'9<'MjwFJJR%! *ePMd]0jBc @1 !BpIJj4Tk 9'"ISU01Q蹞pnD,jbp&R%$EIYZ @@BPXJLJʮ5ʆ|KZĆE.!Bm21'Uiw~9t^iV<)egU])sڰj$+dIdI"$4B6F'' faouΓSט=w=f9#&Q 5{]5dX&&~RkdM?b`+L5A5I@ҶY(8b&18b`в``$-HLB$pp躭2 TUYVbQ`F6FV U Sʨbm1*Il7Qs۽MWEsoeNIPc,Fi"TI)ȅbU^[ f>ӌL6Ehe'+8; 6\\so!\-pfWfrS!J X)) %$@&pSmf&}cOLsyens7$.=`QG85˧xZ2"5٘y-.>w $F@ pRqj#"TJHb&4(ZBpDIBɐK28rNQCH@ 28;Jͫ*O.[)WW2i(`IRC$ eՕnQSk;rK"Wmܞ⾻ϦT˝@R&D!$vcԔ5\K|n /=o>enevXN"JĤ섪@@ Z+J%5(rRԝY.2Q!FW,ۚ*TY\哋 b&NoF$%N ņ?LtQo-Ӕe4VaԦaK-YjVKR*EA I4  {evV2bj0 DUBl,ԂMf'!bW"1e*آNȌ&P,Fh@ # #PQTU^&V.ttU׎Mʚ_w-1U[bKS6Fjd5f}hJcFCǰ9X2FT p00 ku[b+ Yʬ']8% JL# 8i,`!-fE\u܏ai?ا5S^mZa]Ћ ,["@4QbnF6SW,ٲq^ƻ9MOI=P(` ܢLL@4"\jc[qHQUPԮQd<DlU]9V&Dm@l @"H !!=8CԹĚr!hU!! (B`d_*'( (ɤ2r$'IIR)F*/6Dqr$RcЭIʡe ^Fed:' @ۏvv6Dk ⩩2!MDЀ*I$UVg)޽<_gebkqRFiԪ!TrMUn. wZ7M6 ",]I22`& 0$ЇteWa RQ^6]beUUJE[!F0HIFuP+l8.]1ݩ>/Ijt{cmSY2JЄ H5j(9F'I CM iQ$HTjL-VcS5 g)+UDW kʍH}\GQ{,k,ȚTS* Hj:gc^鞟&*cH dKdI ($$@%,gsˬdfe\ZQ DJRJP'HjN(醦|5+4! 1jԜq[25vZ%$-K6 p1s1gUd.sRɧ( $С:\5h @&!&@Fq!"UNMi~|7ʺK;ŷlEdpL|yj2p N;63^6f[Z=,w(r,ppĩAZ D  % f4( 01 B$ln*BI(ͲXFr  L&BV0EZ{%cJ+r![W=p]m6R.!\WVgIta(Γ!J)(BU+T`@0@140h`%$ 2EPYSIDJD @+˅SVjVe+\ڢ3 Ġ*.UeZ;'+aeSHjmMWy\BqPJ +Ȯ"2{2V k7,;2eDaH(y )]f5V8!T $""N)SfdHb"I1Cd`[F^>~.MƬu5`!e69TK^آLNQ!CTWev7U!8J QUA#E֯5ݱˮvBrUkvxo727CiȢNkQu*S2U@PN&J셄y@rb`` h@@0 $,PŬWtF6EF..ʝKlIh'pueRgH0VNpjK(EѤ'a.f6Y!¦Y2(nLYD L\  LhL4%$$ՈhRӄi:X8b*I2M,iE(Sڨ9yVfg-ݧ58.umfm $iʢe Xڏ_E|l!nl1)]=̫lDrhuFR C@EcQ!ΫYm:R:9f b"ƾ*h[Pɣ"V*RD`8RNP &M5@l(!XFQ čWZ-ޛvٕ\.N=-S}j1mg.IYP0QezM굹N\$(J0!E' +h L @FT*Ĵm0bRQ L|MYmZ*y/8'FteASMܫ*Tٛe6R0@@%-Kv*꒲M5bk5t58Y._plz EJuvEY=̝ƯaWESy740@ h\lb*M",ưb qbB6*laU8ZHbj7s>'/׃͖Y}Cqɏ-ko%"BpUVsx\pݏ)i.[qbl#f#>YUcðxŎc!#4r8T&BpJ]/99F{lprQ#4hIC FEv4MhJH,ɀ4ZF.4r$QR"2"4TD*ԏ/q'EɌb6Uv[FX+:oY䞴l2*C=q4e$*)]TW0q h$FPe Ua7 U*JY:LRɧ0nucf»+!<ʫTk[8[v>u)NfK+u[NQ`  HuK]̈́mFYKH8rR2ZV"$FƍMny{CѮ=c垑Z͗-MVd~#a_wD/iĜ)d) b10h C@ IˆY9cY ĘI10UE!f4%^V,ʌ\-qGQ+\Y<<jȍv”LhPμ+VJ#,qdG'=F 1#̊Sd`h"5aV31>3S#A͎esʕRͱ8:W2[ 22BlllC"HHL0#) }zZ.;ϙ}Ȏ5y9!6ӜvRMq2,!qCLh @ІG)$J0#(P獥%%r=3jIܰrbg]`bj[oqudr*&_p^LnQnu a<)W6dءdJ쩖mDj ;*J2m1 0 DhQ$&\] :krW"Dz %1e bJL B` BWe6 Q&\Z=: |6$ +#(aJLMiM7yo>cyKeKt-> )ʲ+N)Kl2)MYn<"-@,嘈dd 0Ј+&"I"iUwj:YS%XŰP%Df>E:Mܜk.u5*ƠVQT&Uٚ08 IbF P&` M"BhBJPQUcUR' I0?RV qN=Su=밝"ʵ:>gY')' dhIEMMf}w2Ԕd010@`0RX{*5B8z{{.KU½C#*q3dd¬*&BRBlIUuVَ[<ݼu7r(AMYbe[b6©j25f-+ii|#vUb b]_쳯ez\mƢ":I]qcq!,m8@TC*"B묜fS|ݝ'{fesә)F @d[HLCB#U٫EY\7)ª$W:M]ʹknys,=5)H]Mp1 ch`&%dTk%W v8-L:vMıLiȪTfvfDJ ,Y"e3R T,c! @*57 C1\hqW P;$- (V=¹H#]TȲ3IEpU," "1-np@ 1jI0PN,@ @ iL ,# F V6uU]N{KF& 0#(!*$8L8Ң(FM Q2Ę A8Qd`@AN%xY9)f104S(cKOV>F5d@pI c U규NSι:.?:"OUܢ7CUVeNU]1c&Kz3-\Kw5fWٕn'(X %@)F m @0Db eed楍!!8q_]jea,QH"0$&0ȥ69jd$*fAXK[WBV 4N2$i#kJU! LL@BN.$k+3*˄eeZ b0vV#67 v&@%n,qL@hZmyIK4` 0L411 "DS,lYVk SIEjqSR]&p'Ԕr#1shE3ԛM0r \Z#5*p4  L@"2 P\XJ,Ȫ%exQu; fHYM4@ @`dHH&a8 +1)20QhL!X4ƀQl$0 &ABh@I& BE"i#4*::XNaHFP땘VM]ȹHӨc+q&i@@5 h4ĘF.(J%{)gnSꬻ oEJh0QN%TSW7}sq~mOBBuİǑ,3Dl&8 {0s3O6 ic4*!:FJ6N-U FxDs/x:@@ b`1 8 r"aM IX(γMjΤԄ&HM S<8Y1L1:g:韣19nhk#ȤW ORjqbSP#ܺA(TLH 1")"] ĺXg9nu"J.T_c!6d0`*X((TTR@%(BBFmVم2P1 (Iĩ$F!9@ &_3zL3˟jѱ* &2EfBf9aSfl!jLUdc,& @ ! 0LClD+ Is޹uP7rC!` z=O\Gf_;4(V JA]1SU[<3:ydJen.")GRȁ$DXcגq|?7Y%,& 1RYbᦄI# &FCp$D-m#cwcҰw8'VO[b[# IH,^5dV3װ+h7%.zR0J B$QʑhT3 y}oq穸m`!$(JN-hQ#%HPWr^}Wes\!,n GEkQ*7Q# +5[]J2vjzmMܬr0!hC@$UMWz;Q_ZT1X^lXSuiMœ\c Li;:'Bʩ$ 18LPLxKs+.8Oq;s}p(JWe%-2D#lj^U#HclkI[-jRĤ4X&V^6VVc_Cyddݮ20C@ N"(h (X[SA!@2.lhf@4P P4. !:guKe9tLvNXUjUqJ CUoSw5ns8B[Y6-TgFK%ϦS3#mBmL/A\yiy^hZ<Mg_" \JP 11-q\j)"R#:)\` ^(ft8֘ 2!. la]IXBD !$b"y[FޚyYyʹɧbh0@ 2VF#XY<%@cp6SdӁ P2  o#lHjH!F42wRɿ#6<`(UHWj+RU [Wd$ܵđf0L")ĩY ĺtVLaQeXؚ: Sv~IZVEj*JrLԪanzr޹o\[-zr=5r|boqG9o 9o p[-|r޹o\ b4 1ඦUr޺w[-[-[[-㊢;3xF) q9m^xr/]˼9w.\xr[-dgU:mm 1o޻xz]=wj.QoލwyAr؜* 8ͮUO]뽽wwPQwT]/][au|R n`}DWaU0-[ǭxr޽o[-[,qvUQm Rzz=o[-nZܵrk ['[Xo-q[Ej*T%a#Wfp`)mvŽ^JҴ V-ۖt:T0[fWf հ;U^zx-[wEyw^\٬IS\M`1`7mN -qUl.(6+ȸ&,!bl&P L$b(vw/xog7ސnE˪ҋc r%x?A}7< n'lhqTSlY) !Bs%AP((ee^TQOlG[sGm;9NޒqM pr剝SVݜڵfaY{9͊=/|8rqQΞk{=+yt/K{vݯK]vqpUg)ҕUųk7ˏgsήë|:_Q!G;:ggYhua% IZmg7!|9_oWÛë|9C|9L~꓁썞 QOlkfv\voU/Ës|ޯvsx/kqvfHvotvg\'`,j be+wӨ+kp/ kp_ kjvk\viPiK:64SGx1M{5rkQ|5zkQ|5zj᫧_ Jl;5a[L")핈+voeQ|3vᝪ=/ᥪZ{4_ ,K5ͬپӆUkVW޸qOÌ=|8ì4/Xjwxby!av}!v 2UFTج.6/#K8R-y3 ^Nד6K lvg aHӤC"%:Sz85^ hj[/ ^n&x]]ƚ4гL7:+ p-[nn[ @(-Z-[[ tkvnn yG-0/| U{Zm7[)wsWg07رQd8܇=rS ZQ2oOCJnqSEY~ףQEiZViZIn-hQ %h @Zд#HA@B @D t-[An t-ZTpBҴ van݅ v-n݄-h @[ v-[-iQåhZnhZ:VhZ-hZ[n-݅[[ZG(ZViZViZViZxB (PiZVG(PB zGŨY?Y eJo1Qɧ]Sx Z}bUD7+ i!Sz=r(84U&ņ0azWa KŬ׍X*Wm!vU˿.lBwe߭~+P]jq>%).Io.Mweo5bޱoAkjթPZaj PZL-akj[V-mZ™-Aj PZխ[V[VxռbޱoX[-zžF;XިިAwu߭}]~]wp'h:pإXGv pr9xw^?/0a qJkTFل7a+͸Jn߄7 Jvʹ{77d*[-[uSjQ?ZX#K vou`֫hZxVww.nxM sf-sP}nW~iq wۣun JW} mX1K iVLhH!x-^$&i'x^$L+|@aۚ AIAT W bHxr88x޼ZjxUC Qw4 Ԗ#x>~#|.FkYwF{UުUw\TkS6s(RXn]^x-j1+QZzT*Jr4pP)͑D~˄M(PG2(( ] rr7x|‰F[sB!B#eF} u^ΫjuTո#BFQ 2G!m|{=8545B!BiP0wyG"եi (B s>⹧e\C QAz9B99j5RS(p8 Pw*Q9(D-P)RkJ$m%z]ReBKQDׯGE9mQZ|ڶS38zC}w[>㉟nEmaߨv6=-յ'4'-[U9vot*2 ^)w\ᎊ?pBݑ ê*~zvh| a[շץwllpcvT<4BPVVU/vdTmΟgb6h1#BFգcO0[*3('}v|]|j#8Z6E0D'*iCk^ 1oKf3ރWfX*}VQ[=mBpo[^G=)FG Td޷llFV}~W UH #[ 巿dgϔJ"x%r.?X5FEW2jS@5wϦ[E5Qdz(-KC-eSOa yhl#C{ejҺVsN]SZ;Dlc<w]6[Y6|T(X-/X2+m Y=vPN@Okb8 kۼn;vjU|sØ_wW{EwG _.kC-M=;}|.m'X ;?Rm'i|屸{0<nZ_> hs>4V(Pe8 l[Fp~W.qiB9BٌW CIaTρ-*~F9"1_f#u ?j\P~*ro#ۖ׏TrNea_L&kpB ;NO-B*&__Yl;[G[5]2!Ziz߱G393 r}NZb+*v L;C5 &@8eJ(kDnoW|Zw0r%\;Q'TE [2 8*LˇiYKP- }ˀND"9\}8)uE9v&BBag#^JSu,fV}m< ~r*1͛#!{xb v[^VF핞!B Z8q9-ۋSöVcWilO؃h~ύ1<6wv eTy?Iƭ;k? __v%e]pogvluBbZWn:7X{Mgkcg~W.qe8@! 0'f|09[] ڟY}'!DKMx!G>oԬC9{ Hx ;#G- "Qkabk1N\vsmi(3ۺ\g)T_XSQG) Rԯpz7|ٗḽ |s?IL-̠n x;^v,.놋T 0*k7?49G%Z@V:TZ*QVչB T\(QEi^TSh?cgU9\ʎaqRk ժ/Tx0zW_awcwKݚ?B! V~+jm6~j1,^ۡ G.Ϫ֨6V۱N+ VHWߵ?68,F3Ba7X|" sieyF[5-c }q]VS0j_i>G9L vvyl1P(P]{-.g'Ky(7r/1蜢dqQ.'U:mO߬y-FE.)x g[nNͱX]YЌDeBm#N<$j؎{6agB!^~nKȌS.+|[_gr߱)WLSGZݵ[K``mĩSJS\7TΆqQ)wqiF c )=:NTU6F9JJ0"I*EҠSs[^cETxX61{ 02)EOԺN9QV|ȧ.~ΥO_3kw{^T~zmgة}Odxgk;g>ha](P1 mZ0CݜEW*̒*b(C6|b߫ _>b \`U.jZ4*uWfUv%QU:m77br9:{;6VsɹBpW 2PB.ce*PJ( l٨.Z*ޖ !mzeVkV[=n'0&SZB!F^%;IUDH "a]oưukC\\\mh)lYVAҀ!GNNGEZ~ץ"A :V?ʓǙǡCfd+k>KLGgZ=vk^9a_W}gVr,,>[c˴ģPȨ=l\lO_['"j(Q"gB(AvtDsE9P T[WpD(QUSoV!W}f|sPxj!-6Vgr-9 dQlܶ.BEF]r->qQ9\QKm|20حKTR:86AՕ.kk N#HAk@*P*l-)[/YTn8\3ጧ BR +<;<ȕȺbHPVQ94Tv$ZINcTAt!z3E99i^TShcg^*7-suV P/x})f2گ[j5{ UChvJ`QmRv5aG{4Y_~U*!'>q+޸C ciMRNDJ eoͱ/i~e{rXow#Ԡ]{OWŽG+·8V}+a1{1S:3wQ֠ϑsr`kej6# (D(O CjEpP,h ԅ #֟SV!co4SPV _o+Wm(ѿ_N[[:R=QFPݘ-U.l'"+ߗtX}?Dr+]л7F=^f?ło>ϟ8W8N;ڻ*OѲ}\7hq 1gb^6? e+Ǽ92SڦnF-%W)^h󡩥!B-ZP T7 3i vz6{!PopxeN',idUgAJZ ;ʔL*⏰8E]oURx[JwUpo0?QNE;+AϯKE9vgx˂:ԪmO=x-}GYiRttr9vyyPlG{=vK_Pmmn0QeqEa_oU!V ?Zu.ε-z(PUO/_9B#,kW;gOlgWӈ| /`5wWX2*$Ei{:6VȌC [L2rbl\LprD"5=7i *{8Qȅ %@~*MC#JBٍmmnQÓٿXQyb-'ZM}̧Oo|mf֩ SëKR<$_Z=umJѫm Tp u\r)Yץ"A >Qq[}|iڴq(**!G mWL.q 8UMĕZNR(p{=vAm>p>|+bo)bވQ^>G 21 gչ[^o%68OKyeB͚YbqWf(N ͳs i!B0MS9}&g_ ӍjPZPX {?wY7"9=vP~#-+7W^ TJ" o#JsE5ᵞ QjUGgZ_,(P*VeRZUӦ,v˖։ԇ"T-*BZS֧5+,9ծPu=k[vj< nKhBҡD-&4+IN ݥ0>xK X?T#.kD8X^'[ s@}|W4ʧuTmzx-68LjU+TtN RZTDok1 JN;xsr_N)6T&[6jcM-lwBEPeV;o}7*xK,W 5ABҬq9{OS(FD(V`6߳:V}hB҈tWAy{bBҴ-*7"ϑ>iJU6 Vb-XEտeTȰ5KVJKwթR+JЋFAoV!jVҴҴ[t(hB{UݪYwZY+\^t6졅^!&`UF z)xکjڂ FZVI.v*еxl\Ө&^-W@ݵB]KG$ m%V)\XY6Ґ6 Wk *pUP;W[޲U}z_2(V5vkvqỠvn=|7z=_ /WÊUs]S֫坳e i]6ᘏg=]/hyv]hv}xW|?G <X[gM>@^@Ayy@/ ǐ  &v}9a< ­dUݳn)❟~b ^Bė%,Iy ^Bא5Jד01 6K ^TW༱-a=7gcB2OOT#^l"|&x]^Ew KR]ʚTrb]իwF.ո )֭ )PQC}Rӕ쿣bϲ.S.+z"G鶔ЃpU1jí+p#>Wti4p.t'XPnJu%S?l(n݅ van-[ t L t-܅ r+r!nP Oiܭ[[-܅ r+pә J#@--+r-[ r-[ B--[kv-[ tFq+Jݭ[+t!nVnVnV (2gVnn-ݭ-[KvpiZnnnnnnj +vkvkBݭд- Bд(h[[[h[h[G -+JҴ+JҴ+Jд-<0+Jд+JҴ+JҴᅥiZViZV JҴ*8B (PB (PB (G38!?kVZuǒS0MTjoHhxQ(~ ˖n+jb\48ȔBU.\8ĮQo/i#J)ȢsG"6ԏ/^)fJx˿ۮn ]tWy%).Mo5mF[BխPZ) BqR)RH<(*TR)<3*G!HR-mZڵkj֭5]4yTW|U+UeT|Vx8ň^3bn/^9`s^=/×0,-[_]֯Nݧi|ф5a+XJv߄/8X:僯9yƬq#cv8b6 p;u)y i^ 5а _p;@;iMv ê|@>|N/:xTҥW?n\ 0zu狙()o-=slT:y+θM d~9>dƍ/3 ^f]^czrԢxJhh9_[5իt.s&ݦ'6VU?IʅMJV\BFW̖Z?]1S(oT=Sri Õf5ҋj E-IeF뜊)ȢuE=-{pzH^k{]4yzK}I !o-%ԗy$/iMe2\S_gVuZE^ j7/0HTU1a./GhmQxE/Gr"I*/VN}\l.(gr57.8CG^8񋐼bxΟ^-s8v7ڷg;Ai+Q,ox{bc5^5Yxuu׏\Ǐ܄q뒭6 s*3-`oʓ뗏 umcrwe='S*՛oK˫Q"W1o B{mAKD(ϧE+U;6 [B[J9;oP"2JV'N+r<1*8]?2ym(D"ե`8Uc7wfߛb[`ىND(]wm=r[aU<vIl~UB*WruD+a =PB!M }V'yQե h [z*2;H5&4'TU\meeg7ȣ.OOT䊌8 匧>ᇶ^=OK:nᜓD"!Bk Rr9G)M(3ƊkD(PlmVxaeWǭȮP['r:y7jTj-h;R<3Te 2Z!QEG'6]Rmn JwOd6=H̠xrv kmhU(>Nr*մh1իB]gsjm{5i*n3riNbй=BEUγRMmB\ʫjb~/`o̎AЎ"܇.~7퓇3[;QRuAo۶[v' pB~Uc†nEcqBOQ߶ipFGҷ?*i;/l`jfp"B!Q9KG,FG?rQȨO] Bc/MCt{y"z6-v\2o؏V[aȶ"([)M~XlNvU[i?F5lRn=u 5Bե5ҥkZԠQ(=(D-+J 3BjRR=nOuKVơ)4h,*TU8֠V®V2ӰU/kީ|,ENpP-CZ WiWW<N[|?3Z[v'Dn[Vk|eD23.%#»V*1ﻳ*2[[Y+5fϳٌ%U<2V~G<~9~{4r6`rl(nof F-|ٰ`w QQ[9>{9X3ȠZ˴ϛ+9 5lwZWlϰ(U:m7!@f?Qp{6[AR{3\20=v?q$2횭Tvlv޵PÇ}',3zr#?iv-l=Z2=S~mʢۮ[Ck!*9 ձӃlWXpw(Vo"2 atnx˘uټFֆUvFUg)ZO/Bnք[7-(BP|ÿiʵ)T#B*+ 뚯{N:ӼR]98'O+)WiDЬ0^̊r7TpFðOvPK8.j8abCx^+78}:Uyݡ὞hmlfڋpSsJۿFPwt9v{Uqmx2x:P]?;x5~mn& <6_ayBim+ߗNg,3|Xn䜊P<܏8N봁.抧H}A;3V;bzh8ki\O]pϨT]~g*c[!luQm$KO$QE8ee^aSΟϱ_Ȋla 8"QLWk}Uzm-HPL姚4Bd*6mB pBңԕ?ՕsOhC(N/{o4B-Z{GNx\Z5.-Xb,\Y`Q9HBxUU5ҲS fËzdS?Qn]KqJ}2VٜHzan?;]j*(cǍB\U)N*}0[[l'Csx^Fe*?WǓ黩^e+횯`{0rb8w /|=8N]} t# ?eqU.1 [5]>mLnENN CJuF\)6ň/8ZR9ȄB3Ǖ^mNPT(DsH9z2\pJԧ*ẩGSiP@0(Oj &1iVw5Mzkn1 qisnqFEQOU*᚛Nquk{3r+㾽. ?s'8p<.}˴Z'*NX] na8mv[TƗ}}T3#cَ:=nq\m ƃTEv{ͻϤ[xz߿Zx*a;K0;3f+w(vs}CXiW.ҾuȀt['Nxm-uWY`!ocZ9>;{5)X3aDe,_nx a7^FM+ˑBj.hF{]juf'G_u|JέDZ<>4#9!x^t!7 vwxV#BԢ!{„+ Uzm3:m_7#8Pd{?Um ~ˡòU Vh0z꒛N餔 Pr!@Ls"%W@'+g0U@ƭ-mkïqJ#]֭O'vG2x;zdSg̡3fҴfɖŽ!3x?>+fsvp$i[O/ea_aDmRh~O٭va͠GcV<4˖}vG=ƛb'5+ G <4>θ;iS}鿮a;kpjٷxpo|8/S1"Wi_N;"G,kiaU:mW߭>94a~*yn9cX_wo6'dB/WqB*ޞ;ShiFvwլ+RvyP;:ngҿgWLm[qpB6˘EQm =#(@,;Ǹ(RVQL3Ѫm?a#(JPB!t^W=vlvѸ}]3 ?QB4B#3ƛ',s>b-*L鵲iQPG"G(P[ {NtړvPaqBWferƾ>f(Z #0ZP8v os"BEJF]"㝛b봱5UpG(Q*""/_*`m BseT,3$ZJ!uMcƝUV":8"R!lTsؿ6Cr([z jbrW7-B*eOϟ+N}Ҥݪl;~ xSl|̎YW7-zɣRe(G 4kU^E!Qtv ˖} pPO=O[}OB*-%hZVٓpB (D((WiB c(Q"SFPQȅj J(}N# >v2T<ڋZ>݃}aǝ 9-+Bҋc"(Eb3ʳ3]abkVhLjB s~[Jt 9'3 Ń1vb'R#L"Mdym" eiQb )pJ{?Uno-+Jc*69(1bomJ ?%PPb ] O橴7Ȟq* s[#B9Q)T*W4We]Cq8KE9m_6BhѸlu,J;Gi,ur  7z(T(Q9FXoS;T?!FPQ+JҡFG'}`oXKa B>MiS}"T(ZViZT(D(D,0Aڋ$"=2BҴ+JҴ-+Jҭ_c)Uzm'BZr!iT[s>@CB J-ZVZ[vih1}iZ%hZkIPD,6Z协㓘ADiPiZQKvkBү4ԵiV͕Hݭ BݢŦm}jmRiǰBhZVnѦjҴrsUAI}hΘ6mJZ%G-!h w v ݐ0 VM#rټ2%ii!Mkvs-Nb*Ұ{9vӾ+ w?6Za@ZVh"E9{Ӳ}O;Bfs m *ZP#KCW@5*ؗ}T[{Lǰ婑[V4-M(mE afhNR4C H_~\-Z'pi[е'utRShåiPJ҇S֜^[]^- +~/ً:7:Zk+QZV9k+YZ+YZQyZV _Ѥ(r88T˂N]˽9 ˼w.Wx+~VM&{7ת6u,Φ]Wz+Wy!w[k+Y**߁aY:7P.z+{+.Ew |J\6#\=T4Ƚp]\}tw.ۗx+9oIT; ohvOmg1wܻ{r]˾9w.Wz+`7C\a!ŎKė/+\ W+oW{1J.=aϫx41*UDx^&n^$N^"E+9w#r&q-NU=,bm+^%UxUuWU+W^#UxU.QwyQªⷴi4,.7Rڬ%^F+\/Zjū'X]T'+U+UJa07Inaejk^']x*U׉W^!]w][7SgJ?ᄉ[sUV]]ﵗ|uk.\YwU+]W 4>; O[u?QoQo-|+[&icV fk[z7 zrrsK֢9~* O*7O8+wv5h =SHp|eNEWggBNXK"ץ"Սն0ݜ|:_nWë|;Whwuû|;_>/ b V. \*=C؊W~->DȘ&&"bkȘ6&#b[<Qe4BŰX,C`.i^(%x^(!x^(GEQ7bG+~ϱ**$mS]Sa6%ɘ;/&bqQy7^MWqXv*&⩛8᝜U~Q%;Ĭ{C+T~)>PW1@JWqUQ7b1B(vjCmiYPNXK}_,Uy^BWŲ/bp4'VakgRp,)y CanMZaC"kW8%nWpak8bSvW i6g^ZÂ~G_TGm+W߼:49RdK7)Ml~ANWR,BmTT}z_2(X[R t!nn[[ tKrnV P(F-ܭ-ܭ4Vn %+pop+pn-[M +pw[[[]wp pˮZdKpw @- wp#- p r .պ[[+r+pB[!Fpnnnn܅ p)@AnnnVnVnVnn[[[[[[ BзkBзkvkvkvkvx!iZhZiZhZ0- BҴ+JҴ+JҡG -+JҴ+JҡBҡBXJwSM=xy\ BzsQ-+`s”_ |QNUU^f@RV؛(]o^̊(UcWy%Ub5okjլ-ak XZ) BJJRJ?tSrǨ-MZxսb1wkMwkMosHg ]w ۯ^!lJ/^)fRx{ f&V,{j7 U,1y 2ak̸RFӄ4+XJNׄ/66ysJ{AؿXql×pvu3L1y ^uByȃ!yT+*lq`Q۫NY 'I _- Ϭ}*馫6aZ07SvQSP rpO*ŕ)s0UrÁ֩>/S-:n+^Ҭw[[Bm&ExJH/^(.Bg/+Ŋg+xrJƞ3lRӻ,,,HK V^3jiC@#tǭݳyEaXq;i˚8i^d^f1ݯ1ݯ1]/1][k{kKv3^3/4^5-^d^g k6yՕ8oOT=Ҁu[C(Sݲ5"C4rӉ*-.+5j}6JzEY= l_lke9L9f)j'oڼC \K>_w;˷\q Bse}Nir}*+rr1".ίSNphM؎%՘^5Pb.e-EjZKRԵb,nvm +LIձJ\B#(ᜥj+RW[_|.D(P8i}'ㆸu_Q(V؞˴AuG8"2 3<2{v|]ߛ Q (P( }.&ݺpB "3+ Υ_`nwUGn5pjbLx:SeB92dx\S}^j-M,MɕN~YES¾brG/lkoѵwE*T(Pgٱ'Μ||GN_~h8t=;H'ĩ N"wòw. pN {UKR J` *PoG׀9`ُ0?\+J!2^ Cw{Q![\_ekʷgcW$gNW5<>8S eY`awUl!mG3uW8Eл( '3wJ! ?2)嚚FVȧ-14XF_`1s# Usi=,8K{6 ?#Rߕq˴y: 3vݘ̫ߡsYDzn23il0Ҵ}]tbzOI- 82/,c@ amr#t~"Q^p\+R6!q{a5URv[ R5U9:O^h .ioz29GEL NUz_2k0uOE9m=;d9…;U[i|"=L9.[L5`}Z,˴ۧqea[wJU >T+i?l29QF-yXQJ2J>qGCGBЦ cik?=9T(QO U:vD"PB+6T鵟0ߥUBBU~UjˌgS?gU ;w5xl~fb+j9pvmN;X#i~͵ěbx[twS6i[t6g0`KjkϡW᲼~_q5╱U9v_Qɬl m+Gh5),j#Rgժmy_A1ȭA?3oɚvh?,_M?ud6Ώ2p{7 .>m9j-lzCpz^p?*?aDfg&袇. T~r<kxt~aʍC+PBS~Ϗ /{e{Z $!sZB"3ONBԃ(D"AvSsȢ)JϯKE9mNf}J?WgFU:mZTFP ;3Xgqr-[QQBU¯mvb kuF#J |] -:=vnpmea[WB!Gc {4 Tvs.]Vm^KB%a/A}z[mQbjE D-A`v%_ʘ ml0S fnXտ(v=rDɟk>?ڭ?uT"Q€p u*ʄV BS#(]G3ʮ6T鵧_Ak9Ucv洢P#r/{e*T"2$AK(]r Z@Bni 7i;'MOgeGQm|-üO:iehU[JxsZ73m?-(Pl^ԫoR:mg1FTH>WdrjԪl7a&g'V&"-EI+Rr|2QG*5 :3V?b𺗸hm}N7. au#H9ky_JؿGu;2r`.TJkV{ xJkOr~/m?phlWڽ]-aG O]%u˳hN4XB>UL7k>}99Β(QˮVˍEiE f/q 76B(UrqY2=9 PQXnpp7k ֭&G(̄d:ėTNs(CrԛQ%BpE9;+_}:2)n-* fZ֕lO\qK`:*mh)|cqL|_[;2S8#;F ,X/ݵ=~}WQ*ގ]}#gM|"⇜Ke{Y_+ݛ~Uѹ#ɧ ɧF\FUwR{3u0#S٬M]m[^^TTN\v]Jlr-)xt1ųSgue\`n5#ЌlԪ6ٱQ2<(w=j>ϝiG8ϑ˴B(8e`?٣*B (8,倹v­\ZE萹)j%jEJ#]s]T[l?uEQߨDea&f+oxKZPSZ}~ˉ}9 ҡa6-.&* %uڜBΈ*mV?qYx &zoxjXT*&:r٬y'\hs_)H(P%Ij()SQE99OE=mu*ަOڛv>m-피F帕:t ꛝڌ*gvk0:; إSzqT[l#g (QdU*j:PB 2Bqs~W.F~u Jg@W_J뎅Phپ~QY- 3lGMp̅ -*DdU:|hdJiN o+b7.BʡGZդ8:'3hhӚiP-(QL-aenn(Fзj'lmî66Fa-*V"zPbݭ 'I)UU^DUehD5@ \:y9"*9uܫwxQr9-9…iD "ZQh[[9z"Pl*6Ҷo|*-̨ZIM9BC(UɆPªԸsM o 2HG"r(OXŘ}[ҷo+xV[[ҷoJޕ+zVkxVeo TJ },C,eBWz]w ~Tv7+-.ir. wނK.z]w-FD809.Ѭ]Vݯ,=wW~]| |].5S{6kfUoݡvӭwڻ].|j;FwF*Ow;-0%w^"#ĂDx^ ~ #r\{=]yQvbib4:BQxbX،56EÍjNn*<]x/^2Wac^.g/r8AϨ9žj;BÝNgQpnj=x@n^7QxETG1PT+WV]ﱰ*Mo曆'XVc^1Yxe^'YxU|n+91*bSaeVXCzEj.P.Q ]5 ]5zEE*.QwT]5xz߽ohVhhMUҩ5JTBaBa5҇B49"m<&Qt#iR6 rTl.=bxE9U|ȧ*Xglw+,Ay ^B'X#CW%[Đ٬H{)UXggw05xXO7&ϖ15My_^Xו5MyO+bknT6'p`4/KmtO1UUy;Ccqb/&ɸ^-Kŗ1eY7bb;?;g6JZٍ/x[c:$l(UBn,O1eYy/%ɘnbS\Uڻ݅a5rm,S皞EgȘ.,"ȸ(/!b|!xneRr;8M(S'2gu5>Đ+&K$bK#?_;9MQ ; N i_Qٯk|4GK.di]|3fu:vgY7JoٛVKO tmkdA|2jbʚeA|3ft: ᝲin!ٕghK5|4_ lW[|7_ ~(D'knk!)7XZP#ӎQ Tȹ5#鸜(NE9oz2('R[[[[[[[[[[[].kx +p].v[ B(&ӄnlNQ ݂w`]. ݂6j 1!nwp].v &ӄep].vj]٫݂w`M hw`].w  `w 2bNQw -[[F6A9[[ tkt--)9[(ѕ p r!nB[[)8E[[ tKtKrBA3!---ݭݭ-ݡMݭݭд-ݭ vkBXkBд- BݭзkJд-ݭ BзkBд- Bд- ڱ;oJ TY,lZUE+V mZi\CaFsSiZVS)*x 2("޽?QEHRHR!HZrB? (=(QpB ЅG (#ЏB(P(P(PH*T{5zžx%i.Ew ]]/^%hZ/^)fVxk% ưxx^9/Wa0/2K̸R6ф4(^'[U7]*U+; --„BK,o?".ep{PxmP*r4)Ȫ[ק"+i6qVaq m hvɘUJS8cט1t{u7⠻h1e,e7h_xw1m6X.7oZessN҆#79R1CƧqms4ō/1c%QDZX~\[9nX[`m7Wm{Ae~kn]w!Q 7ړmk.dqa8GWَ%q,k#e0-TX;icwPw`p`U `3D`*0)Vw8"n98"]]^)/a ;I\$š511x"[!IM% ${+[nm}VcJ.kMtV*J!a؝GYp5qlqSY⋊VEj*J_n;݂;s-ENsVXQkBkZ`5*Rg+ZW1sL+#=*pFqpiNbsy`m[CmJuWթxj\=JOR{.tݵjTfxNNONEP/'-wvlECj_8۟6̭w>ì9(+ټW{Q͆+WQ]QqΜnCzP;6zoD#X}t`QJ(8G%HM}'|~Qb83vA\Efj&-j#Եy[>n&8D(IXF_ivk3ϳ[GuB9Vq,G+AR=B 2Tyl\@\[EE v_K hcsw"isA@*xs^υ hF#t۬*-+m_z*sm+ uEYОh)Ȫ\zl!N?"+,Fk؏V:T{oB=[}FESV_ץ">o{x'\IϝGbv6cc`U:mG |l |۬tbw6 ڶl?&^WڭxVb}}7p7yt'(v՝m[ji7u4lUj?Ǜ3]43/;ewxmho1W77Va 5eg{6U\[HqU]i9.ׂi2Q*uIΨ^OE2m9v}?,B[]Y28ODˢjTSrϧ JBG=(Y@zQNNO(_zdS}oɎ\X}˴S Z\x+oxm+UJ<b+k=xmv+lQG=WWuaT'֝dr_4p';i{FZ|6?f諿st(Kl9S`Xv(lw hdg= 9pOaM:Y0ypohG.Z /l k)ێ-X|L1qOEC"5ŤjFVЉOSaYW+ǜ  (8G<1˴?R"(A ZrSDB.EcŠ)ȫ^w?`o_neσG 9MUh4V}bG>qT\R]*p7'OKu0HKñn?c\8J}as/mvAᱠ+ tE 50\DXP{GU:vDQl;l#vYӱ1&ۆvjH.1+?ys"!SK\5:]X.@~9ʅ[vP[|'g5ΔB ,+Ur(BV`.оD88 +lN Ÿxh06{UlkT?|gQgֶͭ5bp{K٢um8n%AW[v~^GzV"?h1{d(ZjGוʼ7?TQ;e ܴTggw28Y>R(E… "UrèջG(ZS}2RXK5=!sZN~{ -< XVR_YֿƶMK/+Ѧ")Vȧ- \' {v?2hn|~*~G(&f>ةwB! 8z:SG`mz٧T Ge{7? # "eBiDdBe׳0D-2BFPP%L\|<޴JrWb{oiV .?!s #%hZT(\愩6#YX0Űl.vލ=vJبESćS}z6O[iu*=\.FlpNd>}Z2R 灹v?Ѧ"->GE?}gWU 2Mi񲶿9…L4xN[ e (G~K}2!FPPFB;+/SE"(PiͿ6 J1r,Rd-Í8D-<iD(*>.t`&"2F51fPhVko>B`|!BE@Z=rۡ;:ޭB`-nus_JМ#/kWEmJsQ.cB-+L.*An1q_Uܶϴ/Bo](SqEZץd`+|rҝN嗵SƯZvB=8!]ϱ9vgr(PB 6Ɵ=mgߙYBҡiPP2v}[ZrFC&( %XIiۛz5!bvKvRıljM?J*TSOQEUG'sSBuQC7#קVS e g'(pu +?ZVhN д-+JҴm_V*6ԾUtPiT(3 Jl:?[arд- BҋVVZ!m%'N,I͠(nh[[ZjҋU'vp6w"[kv`[F,EBЪ@/[~QдMn+tkt%ZM^?([&[F-ڴS*LwoRR)[{i2GX@ JVIZ |kA[ZQiEi q;<&L0-#Gn9nVF#L"i ܰG-iP5h1+vnZDIѹ[PnPAV:iPBҜܣ hQ%iZW0QJ*s.pj>sEJ!T+QUmSh9Szl^cunZVEj+QZVEk+YZZ-ZֵokZkZ֩Vm&x]wނA:wޗz o4ձ5QmݓV,].B!wx ~-[-<9fcycejaw]wƮ| ނ7kGc˴'85/Dw./~j]6{{jM]#p.ljS*ˏYvi-^ M^"-^"ڍ+5FfUZw1k-; ͦ1M^$L^$L+Ę 5:V폳 OMNAxWq6 ڼH/ QZ;ͬ\ X [C1  pX#`}(Usp2i(6z#ŐbW łp\#x^$5J]-ec Iƈp0/^4d+ XBW|smV·u+ 6'PJEx]wA˿w..]˿9x.^ oW~r]˾w׮|z7o]9w.PFw-%.uK\-Z֤y8M/(zxOjsU^̊z[Ctqu_`;N(%bx;/&bɘ;/&bآ P#^RPX^QqU,QyO^SE | ^ ~p[x%K0,@[;T8'g5moNҁUنf1+Wx-[/ׂ_^ |x-K`WZ+pvgaمNUA*Wx ^~Wx x-Ko/p BlN?0 d--y| ^/W x;wqSv{qrlX[{?Oyؕ^/+$W%JFN^\ė46k)/mG`6Fqcs3QyW^UPSŗd6KT?rLN j"6Fl?~[cZ'*+b,qT6O+x%)b œv;*`1:ݒ)해[O[MX. bG`q`;?WU|=WU|=WTxy/xwα%_%_q%_qm|_/mxޯ\jS_Wm7kbriq=ۑE9VG^͑N N p p---. p뻮w ]wp-p ( -[&n.---뻄)B3!:o+pnw @.w`BMdpR[Z[]. ݂wp]!DљFwUݗw p.#l 5wu62+ r+rn- p R+r nVnVn-[Hd'Smr+rnB܅[PP d'S[ r r+p!n-Bkx Q+r+t!nVnVnnSi Etnnnn-ݡMnnnnnnnnnnnօ[nݭ[nօnݭ DUGusW^͗#yJ@ c"32E߯OEQErF.9Pg (PB/F(Q](PFPBz1G(B(ZT(PBх (@(pc5Og}ocKV:qkF56J(.нJ)ʢzpV?Ȣ bnwޯQ]wq|f!|b(**[9T+vT1 q1G/IqB%O^#1\Yx*W☾X9m/ڴ<[iKq|`Ɨ)7>3֖8棍x:DZ>=h6ݪ%m6W1i Wό!Q&,qU2 8mN,8X^&ы'm>,qu]y F(ˊ#ؤy^f\Qy^eWiF,byCj1uY ţvb+̘y^bQ<]y^f I!XGiq¼Ɏ@\n<Ǎ60cS,nNcy*,qyChu?h1nc@\m+6c`y+X6cx0c?cn=j{"V֧%6כu\Un=1|ёO'[D{DG^-J_iWm/hb{BCBPq T8QnYaYw]iW{F*Jѡu(j`[ECoը4}e\J.}UU}bu\/0 ͬHUnZZW.a32c [2sug 3N_SEQVo}z2(bV0*Xmv]ߖuw<%J*JVek)S8v+gw*HLMةyZԵ-Ej+QZ*TeZµ"sM5x9ZIֵ-EjZV-JTEj*ZF/l&ql _ +;Bi$~TJTR*T9sSRT%jZT%J*r)*JJ{˝')RT%j+QRVE7SkYZVJT%sRT9`\>oukYwW]uzUjUwBUx+gc_TW8n :Yo[Ǖz+#^tr/_[hԣSx>sWi.9GWXUz&ZU5t[TTRJԧ=i}fhi{Oڗ=KA!B#8u^J񫛊aε?ql2`2ݦ&B2a΅7ʭWv57E0T]] `BOE9mRg2Vq˴'È{7 +}EcT]m6miSk_2OG}bi[ {bيcn|ooPGof`+k| r#(Qz =IS>&2?tǡ|ݟT{Z Xul;6ptSα\v6z8F[tz8U}r;Sha]/C/?۔2~=2گx{:ThME~)1L"95Ur*iPa-7޽?'-QGaΧfWt@[mVZ7/V_軝w]*8'4ݲ{E;A3j6lqZ_Zٯ\G(QJFQ|ryT L~uimuX~sŰlL ^TpNo}g"!(j}ЬP-u?zOC0 /ְrWB*S'8'ܻI莾X1[)Rn}3B‹!N[dcGԡPtrmJu.:?S W68ٜJoo^jb-Z+imyyVlO_b-SrǨF*-8]5";7'.ϪxeJiPon][ⷥo j-ֵ[Ŭޕ;mv( Ʉ*s)aȌ!^Rz+2B@Ї@Sa+h: uISwE1Nץ󢜶gxއG.!aS8e^} t~O۽ ?vhᕵ_cQmgߨV}(Ş3Cca' MG&mTj.|-1i`+oߪxr{/ <OJK/*T*B-Aj B8%ldJsNQ.0ڂ<h#Hm`% cA9j JM9UG JXO:xץ"G:?T;/ܛþfѺ]OL9]}u#hG3S<]v*M8dLv[[|p}Wtl?dgܲ)r7[z(!gu} -qxhuwKoV&.Y8M &;?zz7g ;9x^vz||`x%԰e^I n1i*` $+[5`PQQNn榽9xe\]6n5o5*kT;}H?Օ(ZT"B1+ȢUZ{N.HV'J ǯKE9m}TI" pNR}Vlj=v?7pGc:4?+4n~ z:ﱹv' 7< .̏b9G(2"9GB8aGᎻQmw|mz4NJ!;FQpJ%b7`9\!IRPqZ-Es\5NJ.qw gRG-ͺۭ myAo-@4 ۛ6Y~mq<%n26+toB'RZjMw$?UmJ^Mr5 ΅b²tSy2(9_h:9vQxw}oXr>?Tt HպpbN EiDg*S_3׆?2'A̍^c {,?2Ewg}Dx) 4D'JB,1hZT-ZP?Օ ҁ Pzچ*F:a9^ҚhnX L}o^͓}4s8vfO$zEr b5_ IOc?keaJG+w"Bǂ\4姇lGn,|D BҡBaȶT+Jg`Jrp фaBD((PCw dQ (QeԞ{GUE-UBҴ* sBҨujWB6't-(F뫬2֭(B!hĮZ{sKL(Q%`yo ,Ϧbjo\[ҵkr嬕YZV\D;_d*FeG>(pC n$&sSJ 4Z{P+a*Q$*?W ϓVw75ٌ[7l-m>(PGi˴_3TKjf Jhp pM^`]Q7|'lڜi?FXx'fWg&kV8FyIZP°.M62hpk+.`V7TGiݳ;,`v56Ee-(m Mmmg0[ VM+o>~;+,"6 *fx^a5ŗYZ#|[|Xafa~%:St9v㟟TKZR` yFX]SBܻJs~*!GwsO~˴??(̦¾ُko.BDIaSsƥ5iE?9R;K[@9n@DgCzl>k0NUca;U*J\}Egi`C*mlָyH9[u[zck6N_g+]suʻO+:cj6>=LobGeGË۽ǰAV vADB2Ӫg>Wh0ejRƶj^`;Zj,6oci}1n{Lgڠh1FbO*ʲ ̾V}QN`ϡ1…9ҡB: (ZP.E.G>J( 2-iZT(PB 3-Dtdl:gٻrd}̯^iW[[goՊ2Cl1"kNc[wod!YmOٌv/cݼ $׶ij-lЀv2:ua]sV*7-Cnoj(QJ-nO:rEBWtk Wg.HQnH]QyP0%VuB {_m'-KZQw0umRU~Z3)R:'+% ȣ|ȧ-Է$y[qGG.~΁jbwUi[շ}[j5.jcժUP}{$׫RX|٧ҼʡqVk]zjܻw +{/uJϬUDshh`iL.J{dsT5kժ+\T5krV`sUwשRpկRVr٣em$x={V2.0j!jyZ\(PO?QDy^2| ղf~ Ժh>U*TRWwm'"%P\ztSm5;s<r&rl (\-*?Fܕ[1Wp+BҴ*(Vn5- f -x0whP!i HZViQ LgfFV3y&S565hZVA[[ZVZnhZnօD+J ZT$h+APTIz>iiMk@-iZjЋPٍ%hZ ҴbNO[j;AlVzh[- H@B, H[TB- HQȾYr6kz&L-Q JV !n݈s+Njf p.`^4ZQbkT-*PiZV@\FPFPjt-*XVƓUM%VIamӇ9m3SVs%j+QZ-C"2"E^%ЪT)-1 '3PT("V{֕qWuQ}WZVxrPB_P+Zd\^͑NbsNai_p__p{/ry/rgghV;Y=!ҕŞ&g!sclWÛ|8_,ὒqdgKὒՃ]`6xX]Z2ᗛapojZ֫ᵲklW[hklZ٭kl٭Zvwu0T#ٻlLTܻ֊mE|5RGZHviI|5S_ )V/_ Zٳf֍v" 앉1*wRsfgfkfe|2r=̣ٙ_ fᛗ:ED{2C:7u:If΢d;3_ /u*EʢcQ|2'*UʪeU|1V_ UʪU_ j#ٓUʪdƢ=TCEʢeQ|1_ j/5WUʢgU|3ΡG'U'uW:T.I8^9 jsU՛.vwmrٕEʪeQ|2T_ f._ ^9|0r=d዗'!ّLZvooMf kD'2V/jeD</~|1_ W!ِLժϳ:NaaXbfIdɱūW&MG&/MLC6/}қ.Ϭ(kJV 5 _e4)/fln|:vv>ϛbh kjvӕ6=Q_($;<K%S_|ѕFN ET ^uRwQBCO5:jʋٻk3 h2$9J99V3!U`)WD+NW^޳y;(P v-ݭ v-зkBдPiZnnnօ[ZhZT(P-ݭ-ݭݭ݅[ZT(P vKtKtktktKtKtKvKv-*-([[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[Q-+Bݭݭݭ vkvhn vkv-*2 BݭݭݭݭзkvkB "աnnnnnnhZTfZ)[[[+r!nn) "hnwuw`].t <.UeBҟrLnQ P\@l "%tRs%us''jP-sU[ /(Q(PB (PB (PB (PB (PB (PB (PB (PB (PB (PB (PB (PB (PB (PB(PBG(8#pFqk(PB (PBB -+JҴ+JҴ+OHD :PCyJ PJ*r%<4)PDHUn*TPRJ괣N~*_S5[a2*g09O(Zen(z.t-ZaB>Zy[J[J1]oqN5Bk4696_fSs HtSZa-zפ= Zܷ[-zr޹o\[-zr9o[-|r9o[-|r9o[-|rr9o[[-[o+|V[o+|V[o+|V[o+|V[o+|V[o[o+|V-[ҷoޕ+zzoVoJޭޭޕ+zzzzzzzzzzzzzzzxzxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxzzzz||~~V[ow]J.]˽9w(GԉJk!ƨӌ)SiWxghM6]Kk}*"[mFn[㴯.Zg4JuVRUCļS~NNNP~9eM?ܐ>Q)RjDʅ ?&= uj*UZͦU/.V[\QW#8ULMwUu:,tVZ%pSLQT>Bi@>U*]?eBju V셻YtT5-U:Pb҈Dn֕ ˋSěNkXV5iӨ)\7CQDYTBTG AGCdeE\ی97}7Ŭ{Z[ZuiLeS)FT~'|sUe_ȅ Io5j(R ] 'j}J\ֱƶVW5(kQkױPȧNr*=XsBc>SĊ?(@BP*2rsӛSf5.߳cL:)wN,s tSXwG?$j;od{(^u;O{(Ŋmbbb```,l<<:O,p6(|=m/;arL9×<=y$p,>|/9ay^pzᇯ8ap^qc8j;c<5y Glм醯9y ^nÑ58b;a!i^mã8jǛ;]kg͘h^n05yi^kÀnۇ/5a͘hTVYDx&pa: /)ZքiBj PiU(WE+/3}U{RF iXoN.sß}z̷Z>[7E7gm]˶eebVyv ^[^[CfB;9j[iͬjyj[^[Gfݪ7jk>Z^Y-څET6n|jk\(ݪݪթ^\^ZGfJժͩCfmIմjyfyfynڔ6bV6CfWWW헖헖PPlk\^[^\^[^Z^[(lݤ'˶ݪ^\lyrynCg--c˖˶˶ʶPx]6iAEFCRR)QP:.p[F ^)Tn"ڗ-vugOK4QONEQ~tMlye.t#9^tY{p(yuP!NQ]8J=2\g UѳyL~I[Sn[wb,5B9NQ(HAҫC-o) "e*TX5E[JLUqGR(SqD'1P) s<dr{/n8:S]3.{{(D\>AN}2*3Ξx4Џ㕍Pmk[j9V+#S-d{#G]&m[T)5L)E8'r 85ȗ˕_Lz??>C>}3#G ^2D!O [ʞ *M,&~&9Pʞb D&)pƵ`TR'"̊pOjJUvp<=Ig8ǥtΊ̞h)C.2 {B(9{.ˀ3z=s]O e픡9Y 2q TS [JwXFaA='tYZOJ2D'Q!8fxzgςɀ<IAuWU0)'wӼe5*r+R&M&CrNOO [ :quFVe9OE8AB']qGߪ%ᜂNs~L 9u.ROvPUP(C#M<qHPB G(?̆Q><ϧӋ\~ ꧚{)SWU(Q>r(J:L%t(ppCE]G.a (Jzʟ(1S}5 G)ʏJ㜽gOcs(t˦~Tpr PG(矺딢s'8]2tQ9F}}?t sȉ^OEfTg<%*Q(~ CJOHҧ('B!RxG#D|G /n(xEtP9Cy*&?s9{)ᕫN@;[?|RB#MJ)Ȫc3d}_@.s~U!^ 9)PLd3Odq+euE}}>U,w(WTTg#〧:*&Cz.ATS%F>E999OC3\^c9J?6g>C9]R!8g(Ϟqa{>˯px|_ {Jg#V]_9UJi3H94'U0K8Bɇ4BB\9QNNE3pt9qNQ(CО(}Q9ǥGhFr! PP(QϪ>FQLʞpN^"W^~#tȣye 8`ɿT\?Nr8Jry޸ZD*e2)Mxj#xPeJ"T9 OQNNNES n)^ƦAr^9t=xt+/eӂWE?cx#)(]xTeuׄe9L fy`#<5y >|{˦STEjϨ)T+i \vhmZͦ\8[R{OE9хӜa79*P>.ŽSESžFr뗿G9QoOᜣ>Eψ/u(g(eӀtx%Fau]L.Ϫ]r)WsmS>rƅ{&ar WPoxJ(@f]>)H)Ȧ}A#BGӄ׏fTzLsy.C39{! s˯B9q{zcK>[U{peq@Vf'ky;wNbc$ Y8:TQ햏SSeO+PZ֥}Cd?(gs<^s^ .]_ȟZkEjyeӘ'1J3JPrSe8zӂ8}KN[UG,.JaY%\3S`Y]V( E=9ϩGQsѕӈe(r>(z+9G>iG?~St]sp콲S˧*xeFS[o? r躒껣:gE8,K"FV t&⴨_r<Ӷq-)G-(05iZUj;hdc7!C("L3џ_ NeON(P)C(91u^Qy!Nc耊}7|(Bj=yLB<>METE*Qm &=:i>rXC?Br}Rr(}L)]}/i&=99)>އLxzCנ@+QHu\$~{"<.sx}<3) 9t< (OG rT""y#{N+ q oz;GI4&Bu0)M=Brr)S=i(ۊ8NR#}_nWN!J lAr>{~Џ,.L)A8'W\_He(r>^ɇ*d9N\WP]<w@-[2❖TkaG(z32Տ\rr*ʏJQY9J}|sJG W\s#9>gg>^!L/osO>^/~f#"B#T"ɬ qC2aqe]Sr)Ȧ}HQӃۈf3\QuD')9('5JRT*x\rr%S8zӃ{DSӀ.r=n#Q?EG^NAB 2(2"9Fs(cRENs\5t P]xJ~dr*2]<<(8ZV O2qND\Ts9+ \]x=׾S~*}34u+ۮR\9.29GSsl \9@p!ȿ>Lz?~O9N]A2RN\(NQrd"rxES#)(9)]x#/oR29{(g<339u!J2S: ؜/`c>x%{ h]2xS=T ^\^p9^[#"ȧ(yJ)ȪWq=2pt!OK9qǤ}oeL/oe )/g J2+\Q99t T8.2.x@<8ڎhs8eBe~a{!(͔޼eS> Spz>J\)˩:sLu.yOɪW, sUWLQ溮FE5 WD \䧋sW#!OCr(扅B9SJ)"WСL(P(.R/p{@QO9DxNX?*G(JGeLZSTh=J?ׂQLwiy|SzeGsX(Tzr>(Q<}8f8S.G%9\~K/n{3szk%Ϙ(.hJLB(gH?[r.u  XP;6,MC8D(\%U/<>! WQ:qGф9z8 T+Sgs]TDH^ǒgJ43꽔.g-(.e<%t뗴rꏥr}NqqFS0?? s9)Q9zdg'^RV UËU7eЮzVniH"Z-H5|-/ȅ J.-*x )ȢU3U^ Rk6Zu"N'~F5F BŢj)ԡ:QiPP)5J('"}X }(sQ 9FQP]3eOL^ }>2kS.]ˮ^WdzNX/EErG}VQjpmM̯@񒫺^ѥ4ST~s>g<#Q]Pϟ\uODT c=}F]x:)N9us}#) G9WU r=29QN]r<9]U9XoOT#NQI8#r8 *!J %hA*!M4G +R 2n䃹J(~-הyU/ps^rˮ^ȩ9̣U .xRfO#!_C8mG>(2G.|<{{e9G dx=EuAJrtUFޟ?)K+ȅ׊UG@kP! 8ܘ p"[ %w֪W pUl];m*Rr(_T]s9x uQBQ3Ԥ)]x}prQ(^G^8 {{)x:d jyuE+L..~뢞L(^]QrM9t^)>uˇn(%U(]<{t\@eJ9Q yeptA%J.M]sȓ @ )ጺfG5C?z ϯL~>9 !O*Ҡ`z.3 T(sA9{!uN]2S󜜽Te2z&^Ȯf r>t=zeBK39DP]T%{oo? pd=)%{",!vզ"*3P+R:M_]uY(>rԵ z˪R+I@G(U;+V"Q(>xfVu,_Sq'bvTLLr~辻Zq+\SNaET.)ȧ'rl\<ru9=pN~B9F~ˢNZ%>HdW˃(ȅ(^VLQ#(L>PQ?u2F]2 ^TO{[L[p;qr&Kʻe5ۺml‪ jxhfUYlzS\OC"MGO0Tʭ@| -HUHTsi[KzcJ''-h8QuN*ћ+ 5Q-TSNE[e PS)C9 `<|e(s<׺3\3 {dWe x @L+(S+B]WD^@+]G#Y{E{d>G4r=BW™EYB}m^(({zNAAuG%huE>ե?!])M -˟ɕu%O C.beVjePVSlgg RԞs>r\Vۛ6GTdT":o0<:%jN*4q rrpʀ9Ϯe{*2qJ]3 ߆T*s1TE19O8?uӆsd)̞y%m3յϡ{J3<#>VW֘=wT PjJTFi`nYZQn?CmY=5'ݚU[U:opgG[V n(S&ԴUayZbttꊝx廮E q;oXo0mi`Js1|7־)ND2vWխNEB?ʽeydseW5.{\Bc9A3B闾~ _ԣ]Sp%drR^MUvѶPs)ȩUm OhSAO(E#mSyRSuqyK6 -( Jx]\ t^kg (PP.D'R">2) QT\(ȢQT~1.xsLEN_2R g uDLpT~콗^( ry"dG%9PO$zt23<\P۪ { r.Hm OcOzD# EtȄu{ M5]8矶Cc.Jt{]TyRuSL(ZED Cg6x7q8'ў><NX_okᬡmV"@@-?IDJ4enC)ɣQrԥJw *w*,mo-STFGJ.놵bWNmc?Hk­ۉbE,IAZҢN*)TSN*r(d˦Q*x'y/q|<Ρz헾q9e*Fg.\0e1| Bʞr#.R)ӃܨAχ]Pu*s0De3#'Ӟ.s{xEZQQ%Bڳ'hJ}@e 12OX5Rp*#ggJzuK mUˡ\]kŰ>Z\S5Qr6T% J,965aO%JXSkT-)ÞN̢g9G^e*rerQ+<ʐuajDR1<2ퟲ躡^~p T:uϧ"D!<g ]2Ժkg2>9fRsQUm-ݗko$8IR጗l s5jnv~ԺŖJ7tbpڷogkUq [UUE5=BBPE9>b46r_1`PI߬QȢ90!eBuSF^3 eI#GEF`yt]xH^r\ p~g*s P4{t]x =8&r+۪0 S\] a eC+/hG))Rdz!kmŠC9c4z |ixuzԵ)OtƕVڝjEiRUҚʭL!Z -*l pBn{kyxȥN"׹ 73EQNEfuR3*2!>  Q C9+ߢ!u(2u|Kd%<D:\{dW\FTdsAFg.EB*2T'C,˜ ]P亠)e{)Œ^=WDW\9ʙC3τʈC?ltaO?{9 a J!\Q֫RO}QASugN嗘miQ7XZb C[syYh)2ިK(8֩0ЉU**tCUơVځx Ǥ8PkvUװ2 \?Pr4NNE9bN]TGpBs`/BW(9zf3`s*`uG"95+!J 9Q ӄ/xe/ux}r g/euҡ/|?i@F^*]r9E{D">uPT t[XZqxgHQ'hj1Pt%ҸXvo]®/1N SsJ\(RI!L)A-G\1 BUUZ5Q6(U9U:QUjǪ觌GJԞyS-J}R(M ˞d arPd3 e NY{8^ TeHܮHtȜQ} x: 8#1[>*rs霩]7_L)UzzjR: ^@69*>Ui -AN(s(xצ|EF*}E99QGubۄި"y{s躯dT.9Ƞp*r%r+T^9<4y]9JFrϟK?9S,OpTm07uj{N 8ʭfۚ8] ܮ0^*9 BзAb_xelhxL*r]:6H@e ϤJ$ènZrYe*ڛ*ze999QYU a$)( WEˇdU)TB( B$W j</ _{De+ 23(s]sGT8X%o).I+Q]W|(E EwKQ]Tz%ީi.Iwawh\[kS]][-5b1ozŽb4~o ^tʩX11z.mI`Ct-۬ }#X8]Ųɦ۵庨Tԧ% -Pp]rpX_ilB*+)Z>Opu P9SD''eT*VT*T֕ouFSԥ"TTPEh( k*WTA%ke DiE˪+ߙDRSϪN]-JTWʋs<O"8{_BR*Vj+QZ+ZԵJVE.kRkZ֢-JTs*r*TD+RkT*[߇Ÿ^s pRО(˦Srt{`h=>JSEmd `B'UcR~JZT *rXO,D cSsel"ŻEAQȧ'"U:U OOTIU}AP:oT¡n3eQ .B25ajR)2# PZ.P\R-aH Srpt$DJT|oB5VoB/oDswDVRJԥj\JQrj VJS)jZ+VDs*T@ʕ*rԧ7;Kn. Q(̕<=PPFP(8,2SYS*Tpϥ} sO㞜sʵkU`QvX_䧼.M3\p1*-N7M Xe7rrMuol('Wsyr2,sJn* {OsM(ET U;rZrH[PQwU]+~EjUw]UWzTnQF}W}bxupwpW|}u^#pBx+WKn\Ljܯ^'rNxKK.n'b rx8C^7|7ɸ^=5CGOKG0be7h1㸁CC^;|v^?~~ ^`^?|~yy1_0_7X;E~Lj#M\@1 ̘^c  Ɉ/1ߡx ɈO/יq[[5. WAy4+X~b^3xjx^7znx㗨cw/Wޯcǯט/oo1 E|||||G/kMM^!#joګf _4/4/4^3^3ޯ3ޯ3/4ݡ7kW*Weo<]!+yyyyyuU۪UWǞU۪O^{rۗnއ/>>>/ i_XLe S_)'vH!>!PGZ M&Ž۔6 Com;l D{A_>"&b{A dv&%{[%K;;+凯8yGk=y6ax|y5XzVՇ4=y2y^e^dCh,\<Ç0yy z^=brrx݊%KlٕpqKB'jN]]^ iѡBνĨU  r̪rZdҵITJaMuJ):iPEJl#KYcnc2S֭*S{Vj'9p#r)Ȣ8'BP5T^OT 9J9ʞYJR)*T@R%u}RPEY)(U@0r T”Nn!"(Qd8@`(!pD%n$]ܮWw+2h.V#@ND6-By[M+pVn#H<9n[$-[ZV1UۓHhawT-Dn瑢I q+.@u N]wuݖ yFs4h. q ܭ3An4XN\ [UjzU¢TisФIn!n$-'RƠš-Ib|5=6hnf%wBo tKsS[[kvkG- Zh[iZVhEsз\2jҴšiZaBBҴ+J!BBѲ\S bݭtl\ Pj p)'77Ʉ煽ThrjbCw;룇b~-JV鴚aݑ_Zm$;x U%8eQPP+J-Nbs'SNCvw[=ܭs-+ r p r-4nV s.h-u- s s r ܭ.WwFS"UYIwVUJewQПj׆Z@М} &ҕRC(;1&CAn ([jA u[(r뻮 qp p pqtV yBnVws!@-P%[[1hZVO2U:rKC%n뙤5RF6MM` NRreBnn%i]wPVHѭ e([[[iInaii-----HSZ[XkBVBҪ(8BZb`PZb҉ړi- JзkB ZhEkJзkvkt$iݣI2nB-ԣE rKw t9rwA>wi]&m%aURQbնMWi :<2J-ܡMKrH[2[r TƢ(}%6ըڭ]sN6nVnVnV(R[[[F--ܭ4nnB--#InVhEnnhB#KnV[%iրDJʵl!Q ̔vTi*u E'Jթeubh`sZnօhZvhp.`ZT(- BҴ BҴ w KvhZV-+Jд-+BҴ+JҴZVB-EԥnjS#(E+VAJ~ZBiP+J-ZVhEL BҋViZVhEҕZth-ZhZ9ZV檎m4k0ʲ7pvB媅BݡMiZ9ZV-VZsʵ9΢U;]%ZX$ +Jд Bд-+Jд- Bд-+Jд- JҴ+Bд-*-+JҴ+L+Bд-+vkBд":3 23ʠڠW~\Yh- [[CPU)Т;AZK|B͘G[d8C"#R90sNYFB/^ZS*ּjiƐ*@~Qp#"* ABrM iZViZ$Ҵ+J-EBҴ+J!8nс5J J J҃ViP)c -E~5+ZzC)Mn:o*i4 աhEBСBd MKiҴBҴ+JҴ!Bݭ JҴ+Jд+JҴ+JҴ* jҴPPUfhEWcrMCP5Sv j PB  J "  J J,ZyZVBҴ+Bд+JҋVzV T"25:!l,kN+MfA աhZV Jҡ·)[Hr.)vBhZ!'Pj ZT(ZViZyiZV JҴ BҴ+J҈iZV JҡiȵiZT( JFD-*(PN|Қ΅%JsiWz}F>%V}h]\& PVeWUi[VTa+rOjE, m0Bν յ)\Y k_=8PBBsS#P9 (P*(ZT(ZV J Nݭ BдťiEi@-*(PB!UoN^'nLABд#MKth1iZViD(P`b`B L*iZViZVBҡiZT(PBiPBBҴ-*-K~ZH\N:UmYPjҡB (PB (PB(PiZViZV JҋT(D"Ol&\+qPbд'S[PjҴ*(PMo6R jЍ9AP&@(@(ZViP*(ZT(ZT(ZViZQjiEjҋQ…i eBGOCts% Kt Rҥc(L0E*U Z{V9)tux=ȥaw8~%N҃< L=q[bSVk@^5`2BC[96=Jt((PPU))I5 (PB (PB (UZV JҴ+JҜŧB` (PB(L2֘ѻN)̄aҴ((5iZT(D"9Ig-iҥzE H5iPB (PB (PB 3 "-+JpWT6ڋ6&nPEjx2AB (PBB8PFPPiEJ (PiP+J-D"GSuG k.}4 ZPj ZVhZ (PB hBд+J-ZViNbB J ((PB (PB J!B "ե8AdTpBD~ +NFUKSM'WaiidQZg?HOd U[FVOSѣLmZEk oml(wQ @8#-:P'& ((D*F-*3 (PBB!B (UA-*(PաiEJ (ȧett 5SAMjB JҴ*(PP)/MJЍ%N ݠ -+B PB (PB (PB (Q(Qf^OuF WMƇ_Q ok8aBPBB (P(PB (PBBҡBr}VWxXՈReZ8 WּэZ JҴ*(PB "lBҡiZVZiB<R B -* (EB (P0r҈PEI(#d*u>Mkj;9S2-lٽk@RhP(PB… (P8PP8PpV e*VkVyc5 B 8aKy<E  Bҡ*(QBB*DJ!UbkC8QGEéVIAB (PxPI #&\ ?8=r)"rj8ጏ+tFrN4*s J =(B8) +zy;HԶj 5x^4gw" Cх(PFg8!BB(U\*Qu⡇mA5B+w o SVZvd EDo*IyŒpBGǢx!E8'7*Ua5HyG k(s Z5RfpP@e (BB (8c(P(P((*O*)Þ^;}imnV*{kP)ŌFP2#(ZQ w}e@AJ e -+@ZT(QE>j5Nj ZrЙI4r4(Q qP߀4n@NZN|&:BciS=`$QGiBUm!+R^Na@AR(!„BS!B 2!nT\ ( եBBҴRҘC"1FQTA.cO#LRT-,uHᘥf׶x.OS鑔(PT(Gp;Smݭ B @29u<pFQe8QG"(ZVh[ZZXr($ܣ('3 TuFkSS4zViPCԅ ~w…TeiѴP 2 (N MaMp(|UB^}@1 5v{(Ee{f[TJ983Bh =(PGۨҴPB!B 3*0FPFQ gFP8 ҥKg}P#(S٨ S1❍JX)TT!PHŭ_jle+Xmi SrG 3(~9CԵ!*Kfoкe cr:xj.M%u@p…GC+B=Ebo͛ak\Jҡ6d!9-*`ŊWe TҜm"EQPiʣ<Ϋ`J;k*i]hS Ѓ:}xPPNt#Q2+x-m Ī<[5̶oF=C@hA47U\1wN0SP{ZX 4ma:8؛δ tEJ8o҃x\P9֦,f0Ql~\-+NncsMȢOU˜j6~J`rUka4l2R$2tS*Q]9Gr9W5H~t-(Bi%JԉW5!SDƙҴ>Pt Mz UC#Hc^踨ֿ@n>Nw6 Bu9Lgu{_\U(Q GiRkZ(:&c&POvr?T9;Pȃ(ZeKYCWn6-*$9B9TwkvM\̸4,J}w͔J<QDJdMCemoBOJ.UkF"*6նJg(WS u6e[,JоTJZ-e;H܏P=)SU*6*Z2='[xM d9Tz[UG*Lp4ͧ PH;'G)Λr*B„B2{C(6jНBS輛ySGS&PN ^Oj69G*<-`9B{dUCEGA!JWw+1Z U,~d'Z0,D"$ml훌R]ݤ{7 gҺAޟ_FTy)W VmC' n2!>)ڕ!̵I*8!iC SS:Q2UbiS֭+OC(g Se nĀ\9 EU؍Zu(Xmq4k5P#IT!\/rkSs{HV&GE{yFW4?(2NU c [5B-[5҇Ó ^U;?h j+oRҋcijeBm(z-]hzTpMmr }enca uwjj#jR2%t@JT?Ԫ,5DA$&VpC-J ApBPӤ=M Zo'B-E(U:VB{y'ю)xFZ42 - Pn eZ^o}l;fosa7)&Qs:)P)b 6XǮr:O*ӧYXs)SHGOrZzO/99iִ|4z0O e[7ܳj5.Y(!$J(*[Lr@Ԅ*~aJ]6D.s$ "l#(mT݊osTv-mp]5wts*xg"Ҡg tYAZEU:[ZVbkkö]1 qk(Z(QT8r F0 q9+,k]S˚9Bu579S(~ʩCR{PV8SQ¦S۬MZTX!0JAݾ)'i^wB:\U 5[~aʵ=B:Z.ۇ]Vm9eYPۚl|SQ0GQn"-AkJqKxۛ*LKt!=C(8-(=!?L'U ՃGꪃTd? 2Cjp"V9ĜQ) l=?!W.}{7Z^*ڡ9qַ>4:YThQ_Jjו+R?!j!&(? }wwagҤX Ъq&,nF)ɦN:#"n[\d͌$*Q0[e*< k : *ЕO˫(Nj ͔(t+C- #aB32NPG&ٶpv_SC]+QT*y4 TS'sdU4b3SrVPGxʕ0B7J8\Kj 94W6V-`E8JC/tH!iZW0>Zb0&r(yZs6V2ݕ;@x*~E ET`UT9Eɭ )zzZڳ^9f. tQf(zb -(3?pM@xqN*ӦMC)R $„DQo5iz~F#x)Rlg\~s|rTMNz]'Cr rx $RaC7vB85{ sxێHh 39XC*w'5fxN@pTjE68 z ?5հ}EbUmV[u-AåC*2(`#MÓTu U@6x}๠ H5'P6f~aRKѧrnE:TiM|>BЁZWʵxj RP@~ C\oipkzu-0&6QQN(T "yxrR^1ރ¸cm52%ʕJd3!HSZ5-+JvB_N֍!iR+6+R=F5iPMrܜ-d-hCk1qMӅ?1 anw_M F֎eJ+W*S^ʔ6x[5S~ +P@fH)D [("Xx) Oͧ!%)ʯ!ۻÙMT09B4uB'^@z!UA%ZQrg: ! J Vr}YoRoS a JMWҦ)Z(T-(YhN#gN[IPIZg'T7Ml)R%NQ*VK(s ~ !Էj4\[YaPfN sT PoE hZNUQ{M)ʱZ]Hٗ.υQB'*r?x$%@!e{Q (P+ʸ>⃵4!8'fQt&FP$ YWұ|R X-kWcSn6U(+b7ëᔍJ᪭#4ܵ4B_u_Zo|ޙ9TImh ()7exJ)Pџ9RA^s.[ qe09 }2WlmJJ MqnCS 1>A*eBVoeJk C!(!dI)\9yZ<5~^Q7i SȺV9RWfT)-MT<*4ZQK^[(C1_2)&?5-C)SIM)ߡ䧻jG!ѾUݥ%!{a!dJr?9JVQzEUަ[2Br NSYRHTy^*? ^J`<8-վZ񡶷5tw]AQ~i@&t&2i+G1V~b;Ҹl]Ëơ{X61FOl=A5ml ӽo5&\V5P)Sǭ \UfN= SeJo?6TQr{(ZMrԥkNz}m5w5/kn&؂t\Q2Biܕ>F9!̦rvPg-Bh~lOXdױo`̊a%92QBu/*(NښŽ1C~mOnz"cTb50 * QMtL ?:e9^]}öZƭKw'S)빣vP@{z)A߫V%^(51)1fX 9~D(̪RMH(p@z%J Qr.NzUVZ!U z4B\UxMaM~kS*h@z5m?'?9SPF XR{F4g"ȪԨ7KSΣJSMbZS'۵mʸ+ 턷 7 Cj$ԡmWU0sT9Rk6(ĺhRAZOAA kcB'QoS,iZQ}I@j@"kE%'ϵn9X9ɪF<3ri3«zB(U+nSQ *9 AM7iY)7UiPWl.e;7C~LL"S\8g2Q|+Sm'x4RZaENUB 0*ΆQ*3'76%?-*+b9) YYJ{g* kQn){(XPA+t~=c oд*SO-v.ZXU`*S"U7HN+SnqMzUO[KI}B?吆EVQURń8J9OE\ҨEL=~UGxp]عmX OzV&Һ5:]h5:pT*'NC)b^>6G(k@'R-JQuBu˞s,KvTؚ zO?HokZWS0:]Z?r(sA2 ׼ƸkHRraͶFFǓNRZ}AжP9B(6E; ßY.}0f"skU;[+D*TQ@u(C֘KSIy`xueJ|$`%[qFbX=;Sdjqbc`(E*wAPӻp* M9;z%THҥjZRXK6K6A)U)!B]\?*NW uk.AC2С5i(ԩE-o}ORshLkn@DB4jV5EEe(pJNQB(ZT~r)&NΣgN;oX*xvRU/JզAN(2>U BUFTi>! iE+ W 6um,X*5iRx-<%QDJԵ x̆QJseQ/NW7F{249Wdta4m(O 282wAZ9.icjQ?\u:U+U·a7++6iСNMnpWLPe+d̿իAҡLnD+W$@\fj*ʷnL*T)z֥jԡ{TS}!T:)͐Μr$@?V bT*sVU'rˡo洭>9Fn.ҍ 51mm+pṘUbu ӮPl(sf0wKn\[GyVnڮ)p'&UGʥKH4j+\7 e^]lg.WtQA8(fxjR& kZM%_QmZdØȢн$ꁩC-R@{SxGfӥ:-e`)N0/JvL'm(WM%KIwA9\gO:P.)C~?,B\[a}; Ju5OOdGRkUJSgB9…YRVysE25@ZSݨڐ M9Gpk[Rԭv! @ C LR?=ʭ-eQ-3vsG%s@ƙgX\ҺKBҷZe>M`e }7S+Kgn\u7HBZ{ӽufձc khf CIN|!S9SAވA!ASJ*6E7 mpjeB22BKPeB(~qE#F=29-  ‭Nf+Y]-`ZPoRTJ ʞOJSia"Σ\cΪʁeE8P_ڶ+&?\ ͔QD4Ȕ$z[M\8EJ:/)m-}J2yA8ZT+<=OFӭ -r!TRI~O@(rrLG^b 9Be!-KRE<Z @zd*)vtӷGNEk0*Th~* g/ @Hna#v+b5K\nuHĶjrYkoZtW^)Z{F* Cit!XJoJM]r*̷}lA[Sj*vH8]J<;]2ЄTԅV-p۽m%V-f=r<'3tSF8)e?3OR-YsR݁I#kZMO>> ljqJ%9R՜Ð誄 !S!%uO鴖=ZιE0 hȵ>UhD"حB)r *P?*r5yь%LT螾FVZ#-RNRU[MbOGoQBWU ;3[E9NUjhm \@%ZRTT=Tre`Fn \J5U>[g9+<ԫiOKFgx+Ypʔ[vܬ5 ܁RφW^D(P/ ÓP<'&DdP%Rm&Բ{j]TBTX-6co+ VN9pxTȕ\QWQm>ǬOh+^bRfzs RxeJ([VkZ*SmsXR|&P}mOÕJԋNnˈ*PC"Vjʄ!U],ʵj~C֞~TZ!Ξ(OPkLtr@J9EӮ!ӲCY9I(PR֘2+|Q!4]()w~VtATw[˦6L!uQRM(-H2딩k2jmsp֐@F5~hpF2౜* ? -N.62(R"RhqA2:+]bx{aAjZS& BP^鲳fyL׮R{}c g'"4VtNMғ2*_ SA,iTUA5*B{WL*|&aTjCɃn>JEtC2 NO)Eo;EҺ&E=[02@UjUKE {f|;ì5@5m}p*L\Z6P̧iKfիJqx(zj>QM<. !102A"@PQ`a#Bpq3C?1}[Pg2I̤Rs)9rs)9Ne'2I}%Ujk9e'2I̤9*и9_I}:ׁUccI̤Ne'2GTR&Q̤K9 ׁ5NQ̥Ne'2Ne'2;aEV}%_I̤R^z>b9Nb9#G1s_G1}o.Qs?G3s?G7s?FqIG1sG1U#Q̨/Dԋ.k&k3zžnŋ8nV,sƺEr^]䪸jR~JYc]Ppqc8[a^䨨xƧ(ȱ 'D+O n{5$cuUߠQس[ JG18jD;bpX TpǞAa xS"sUGpQQ c8~pI''O'lZku:A}U|R\X;V5j[np|&aHƩlvŸ(,gLJxŅUxQQ&Kr'I.DBDUK¨DN]//S#On`8@ HcߠQزpʷ8^rJuzU}#Rn+c e~,gLCpǹ$$^*ʎ1%Ģa$CrI%¨lbl?#f!Nŵ} U[W}WЕE)Ƨ䆈f2 [Q ڳ=Hi ;x.^Ne%_I}%WaEv/K/ ˩.K/ Kܺ .JGEP]AuP_AzX&h']Aumr]EGw u#r1q)kO O ].u tepɠ]AuT*ܞ4P]GuB)3O̤~/CU:Y_G1}s_G1}999.Nc9?c93eEьdyתIq/a{D贱"X"Xab,#Gb9hRrEt,,,E KZ9i!DaiE#Z sRX*ab,,CX:Kb9tKj1SIe%YIe%-Ij-E`hѐq:/bJ͞ќWA {^]TQسpBOzTUA z=UV3 hԜ8y+rQgV{xX.>z 꽏>"BsGbERjETJnp2s)QOzBs5lTEW݂)ܜj%?dGq[ ]ԥDaj[e&1 {tQGbʷ8_ۡWyQPv9^ܒIV8 ..$ࣵI$N+ $U J?D.$O {'q$"bc2m[V|a5ld= ge[/턓Oz䗗**....'%IqqvrU JqKX7<2EPBOqtT*3~Hؙ"bo ;*wa"rKe-ŭa>}}y9_I}%_I}%^\J^ԗR]Iu%ԗRUWi(eԗR]Iu%ԗR\MVUu$MR]Ir*r?KUEGh&JZI4A4Au#xŠџzAg rmr]C'dMԍZj*'dB/Qi<3O Ne/r .躂 nK/辟K99g1}^b9~^N999ٿePL fhhhhhhBAAF0AAAAAFH #$AFH B44AagJ%ar$$j#_eU8B B-EX),E(Yj-- %sXb,EcZZZb-CXSN j,G-:c Tj YaaaJKR9g+r 1壖aai%K?e*YaE"5FKj-EZj-^>1`T;g{np} ?= *e >E=3"rѹ:e;cQQzUn헗6X>GjȚ{b+n_NJ޲^j*i ÇKZ;8L )PǑ9f,R䧸{rzdg1a% xFz**?I-ETԟX>GbƷTb+nbt zINF5lxX6(ÇPuXȊJkEXk '!O*2cOsP,VZ7*QQqkQ*tUY0n3v,jNT/b% Rơw4% ̼j*b߾j-S$,!nB#3Gb*naweGt4Gi%ĒxŽK)J=&dৱ%EV 8e1 de]$I)UNw2I.$18N :{,*=/K/K.K*pI4I *II(T j=$zOI%T2C='I4O^0ўEPzIu(z*-xdpង7#)5&zA4 *= G"8drMnG "8g@܌Tdp?u>k^<2xe&FQą Ne'2K9Nb9Ss)9I]!A -#  `11 KKKKKq2AAĖA  #$Aiaӫj#*?]&ZQb,EE"Zb,E yPX,,EaBKKK KKÖZ=0贰ZXXK YlaJÖrKÖrg,r SÖZXrKBr Yaai>圳:cURZ[---AiiAI1ٿi^Ǖc(YV BC<8{t= -\2n5It8~zny*J*x(])*`'?2 (^[WAt*yTS<nܧ{cr$WUbqp^­쇝t*T.=$qcؒ\FėNr./ENI*KE5p$Qqp'LKX<2Ir\I8I$P +b'u?eR]Ou%ȺIEԗR]Iu%ԗS]Ou?eȩ],M$J44C8uI4I(m,(ש4zOI4r]MGHB; " h"M#0ёC-HԘrM,HISRexe2šܚxdpȠܱ-xdp? SUM]AuAuAuR_I}%_O̮AAZZAicAiicAiiiZZAih0AZ[AiaF6ZAZAF0AA    0> H N----EP="Ѭ,,e" -,,,,,,-,,,,,-,9l2Z:YaiaбC-,Z񴵖 Yk j:oBZZZZ4!/l.Žsך_MrU [UIMWg<?+%N5m}(>GoBRY.BaV հű[K|(=G媆܊*]/풪dJ14,<m= uI"ʷ8^~6-Jr$y]%dgH=Tve[=WNan<;I$qN$II7} 8{*LszUGGC䮻G5sj9}Q[GRP>U^Qʷ8{yyqpVǕ0Uk 8^~6S:/G1sb/AN1f/<ЙF/0+ ;Hsk,-,e*UܩH--b TaVǕ9аT* ak 8[aS#Ac9l2q`e4Yc<=:S c%\ Fi1An>D?A8lIv GG4$I$Q$Q(I8mЂF Of=3nH~rz>D!.ЌFs}jH0FX!B!FB!D"MD"B!D"ZQj-EZQj!D"B!D"j!ZQj-EZQj-ERYIe%K),E"Kb,G-rG-"XZ9h壖Z9h壖Z9h壖YG-rZ9h圣rR9Hr#$^u&0^8$O椒q$I$I$I$I'9'_qfe*9 '=)$pr(I$IDKYx.d*qqyy{/9ȸМ$' qpOB}Q7"=¨K˙qyy%ē̹SV\9U]#UKyq%ėI9YrN36$ *s//90]1@. ^^^\^ˋYyyyyxlKo֜fzNNY'IqpIq$N O#$sI8I$II$eI$I.,L{fzӎF#PAO[Ddtg ~e|3 k84O2#=MjB#GN?uQ^0\uՑ${DeJ1 S1F/b) ȂScݸ,{ au bK' So=KFƫ ܲ%d0}IZW)٣ou|Cp-x,v)hp~5"}>{D75|\tOÿ*b}uoЏ~XF Vز:$|3}2:LK_} }ՑҟyQ}k#Xfpа~܏2rGMD/s;lZzPk&":[fbsZ,Ok @2GgO3)ͧ#3/YI>/|5qm-!1 02AQ@P"B`a#pq3?!idN gQf>mPGQzuF JWNO$aQFTaQF,J Zُ鵱FTaQFRΓ *0£ *0dAKUD£ *0£ |CPMTaQFTaQ!эF5TaQF&" M0~L0LRݍȚ0L?[F/&`/&+*!xչcO|ƓHnDxEPEPE$P}N }x>|sv)r7H{hO%Y2 C!C!MMx5555555׃[jjjkǩړxfbYd2ᘳC1f,}Y1f ŘCWŘbY1dEzl=6`CW=&zLLO ϽTU+N¬ԩcI$Idͪxd̙2fLNӂ6fٓ3flU>w3l͙6fٛ-GT8FL͙gٔ7Ffl͞=FzUvZ)=Fz3flnuU=Ffٛ32U(vQ3fl͋ "p3fl͙&7/QQfl͊#ћ"YFugQBjugӗ#j%d?#z$kyW1R"tV+NLƒqt iRN :R}D7W)kuAУQrPRuj4:SWC@+أr+*WdA2`ȋP}թ3+P:_7 vĹ)Ho|S*s*+f.>أ钽WTv[#suFj݊6}hvZ[}ہlQUzJ*h6$wbl@檚 K$pI<Ka~ڑqu>t"#HӁU]Hd;OaYh"qlQ| Y{v (>ω2T+G #ЛA $@; ؓ~ɻGjx)t"zw+WE{+Iw~5u wQԅRVWRP{>l;SHأsmhU;h->@ًsw~ܫ^Ox dY2pBt1En"Lbi \ڕ$%n-JHЊ}HWh.v"ѤvdQԅAFE]J$lЂ-WKb.ƆvcEܨ}%(44TFݑg hHr׋@+ؤ hGq"m&|T-H#*Y 6L|Ţ7H̉(-Y!uZȲ^icW.v Fn&h"h"؍;>"ؘZϣh t+~k*IQb:J(=OCܯd+ʳ4+iP} ̩M&T&i7IUJtQ^U&J%y%O*|ReI&t'f"u+L%qJeQ]#d'ULicI&4cA&4G%LjuWwCzikN(kf2$x%$_I$_I&I$I'I%I<M_ԛC!I bd3RlQ1d2*"UmYF,Őd2l£`*0f 5d1f Ř3ʋRUtsV\S+>U*L%2L2fLə361)3fl͙3&d̙6FLə6f2Ҥ͞33fflUNw3fgzQ23gQ=Fe ٛ~FLQ=Fz L3gQsì͙6fQ"E5fQG9( 3Σ:3U^E]PgW*QFLɉ奶pK%Q&dZ!!)pU…H+A|jE-%4#mO;ѿ!\[nP>$=Ǻ*\uNt]cDWWc)Zj6zD>k$ܨ$( :v;]P Jځp5Y|ŝoF⺦Jtw*CR%%[sCKԪl~,}#r7BxOA!࣐MPmN\jmj9Kam}/ؤ}n(qt"wqVkWF7[jSӅaqTbϥc)EZ}Hd Şc>MZ@իOB]~~lv2d+M-"ƄWmƶ(ܤV}Hc)ZA]t}@Ї>AAlvg  h"+?}>* j45d>k>yH|jEidRًb-zG 8_AE)ċѹA&&&$[bb:nJ6 H;n}iPVA;^D8+?赏Hm}~Ȟ5d>U#F £ *0fTaQF,) lcQcQF5"e䈬j1tdVEdVEf5S5R}}Ef5%T9HϼȬơ(ΜEf/ kc?ϼGKC?B+"7dOE~H}LfE~H"$W䊼iTcWW1~H]܉L?bgn`&+Ɋb٢)ԫ~zhԗ&DfDL%̉I&Lo&Dy22$&M&I$$I$I$d$jjk}I$SSSR I jC1d;*1ƣaQ1d3C1Řf'Cqքdm1GJY,ə3&f̙3flΣ:ٛ&㡛3fLə6Le&lə3#6deԣ#3####+#6z͙,gz33!Tffgz3=0ffffffd=CgP =CgQpe=CYVi3fl͙67p3&d̙2fLə3&K%gث>C6"4"[NUےHӍ{v],[q#R5EC;lQVfSLP1K/BSWWQtRWҸ0|őww!U| NFPCWqoܫtT?-)dܨ}%=# ׂHM&d̟-xؾU<"ХWB+poqtmzztSނ[ڑK*}"e;*qniz e;1Ŕޭ)OXPJzx̤]wbCk_|xGWU鄤UK)+\ŵڔTtSށujC,*߁k-ʊw&V V5~zx*I^ ܨ}%=*feI` RVOM߱{Sj/)Z$"5DqK֎*w;b\>lAdS t~G@՘YGI45jDSh^tEhh[ iunREMv'8K߫>C6Z̙.KB*pWZ/],kAzNfD]з*IGI&&#;A5-R LG@PYH t)c 0T> hR.( jK\ 'lJ4'Tƣj1ƣj1ƣcQF,HETΨED;FT#"*"*!ڟOϼϸU.eqyyΜO&~EVZ>v2_P}B~tO?ϼY"~2_T}ӱ?PO'}HLIB~C܋]MKb~<O$W䊻D"r`?&ɋb0~L_1~yi$G&K%dY,ȖK222$ə^L3|2fDL7&ď3###"o$I$y2$I'I$"II$OY$jjE555555>Q "Y{4R3fLɒ̙2fLə3&d̙2fLUYx222222t2332227Ľ̕22233Z`$ffiU332FHLb2GzLffYVҬ3=Ojdddddddde$ȒdI>Ds1cܪ{:}%>4"'! s;vcR!pb*])tb^V쪘#*~I2r %[a_C\^˃ )xl;vm 5zD.ږ[l6ZIMܩt>ߥU>Ӹ)Tu;WB+^SxE'{ӱVGrõ}W-H֥[]]CE[lJ.CܫoM j9ql+WJYԢE{+v_/AIN{ӱV.]j j*[Z9BOrRPASC*!1]Yo`(l?h[q$w*hgbЅfEܫeh Ă-NDj5t"Uێ92L~ Ċ! /Ŕ Ă,Jw ċʻ[,"11GI=ʶBV LF' GH R-A^Y%{vt>5bY1Ƣ*!C1Řbj1f#>> Et˔EDTEDTEDTCfG܏j|U$MdMg}@r2ʲk>#͚z2}Bk&u&UVe ϼEỊ/g 2CA/e &gh)1i_P}B~?PCmj&HYyYYYYcQF5aWDJ?~Y##"L222222&dď7#&d̙2̌&3#"o&FFD^L|$ț$dI7I$II$I$I$I$n.F-I$ȓ#"l ͶRdddddfedȓ#"fjI232222^ #3?x2^ FFKf#$f׃4d*׃5[E%"Q(I(RTH%DQ&rґ4d)z|5ȈZȯe}E WؤWH ')rߍZB"I+|m"P b5- q}e^g ƭWO! _NW+IOI000f#P.(RbbbbbE{?eJSB+ߍZJ墽!rq=QIE=W^OB'US#z%̘''k>R&<*nbbbbbT!tI11b``````Fȯ|m"P %HQM01F{v%DjWr T9-=4JM.j%FFFFCsdURNLӦFC+"E]E. 2B$d[$75g_kE|zwW-*LZ#HVwW!EZI$Om$MI$IDI$J$IB+љ#$dђ22;/dђ2FFD&YVI$I'h)N DI$SRI$I'.zF3ewR)#$d(I"J%Dl(J%x%N愢WQ(*}M> ^ DM$Q+(|I>C>BQ4^ |OQ(|DWWWWi%x%x2&I3^ DW% TJ2^ WW%{OiM> 4&UT&<Gh5HW5&!",E-^AAZ  AAAAAAAAAAAA   HȜ0Hj`d0kR*6D?뼉 m?*AbbbAB ĂB D"11!D"1D")1F(B"cIv+IUkL|&$ j6IOkdzciMIK&MI$I,Y,K$K%dId&K%2Y,K$K%dY,K%dY,d̙2fLə3&d̙2fLɒ̙2fLə3&dd̙2fLə3####&ddddffffffffdffdfffffffffffzzCfDiD#AGb`(#LJ!rZ#_70f,Ř#gآt:E0l1W*@0111000000001010110010100011 A1111f&zbċAh ăbA&&( Yh"111f&$cx1111111111    d$lL qMAAAA*\ L Ă A^  LLQ鞟Q&&&&&(@ 6k;|ć˞D֜PG*}>sױ_ΜshϾdRènI$:A9ВI&Tɳ\uЇd^4"*_r -)2DѰ[I<mHqjʊ&M8kR4ۑ#J>& XY~tY> g&)1)CoFlߘ2&⟓~!y ]LA+Or{ n=ƜO{E[oĬjI]?JO|q얷q6DT:Yklʈ#{xfɧ} _6'L0FDqEI$k!h>}?HOzHWI$%%J/ -G<12g}q/ `kޮD]#4S;I׳C?phUFCCQt!2);S[䦊MJbܪGO!3%gp#'%:>Ӆւ9@4CZ1*'In R|[r 5ip;/x ;^9hl|"m<{T;j-Ar$U6}f\VIJmD&SypMG[Y FGq>OlZ{Lnx$϶(>]8q'^IVǍs^{hhmqmU>^mIn{5̛GLGv$L{O:DMA򌊜)B1:yC6kΌL'N(a?0k56RuyS=I"z5krpO#ByRBCAJ7=zsR_{X^)6ؘ2C^9Cm?MRhSälThd5PUF1$4BC|idtp=QV'sϥd?B#AUs‡vIW.$QWLz?hO-' >2Zy wc~LKA$`bbb_$GADRO&Kf>I%إ-۫jd |\‰\I "Q rmw!7cQc{U  !1 24AQ"03@Paqrs`#5BRCb$Scp%6t&D?S)ËBd#s,U{]+HIRCX|U)4IB]Q #fұr1XvJ\fى惲IV9V+bX7)nVT17Cǫ# mh#u8c^H(0~s{Q`?QޝcL(ح6{+4ZV9xr/7(qQo66q!Al+V+bX<=!U>OlOz,+8Ⳋ+8Ⳋ+8℁s'#1}]Ӛ$6-8圳+8Vr+9g,*X-BTHOkw=b+8JĬJĬNF8\gRܳљQdxc `YfVa\YWBwh'S)aBڤ0(QN-ڤO*" bJbVʴHEF-/h|C8UZ$O= 6vЃ?qM.)eŷ̳?`2̳~+⯋)jOԞ = \o9XrYEis:iiWR_ഗx+N|)% Iw8@w7oJ;wC  ҿ ҿ  ҿ Ǖi_iCʴZW[gVmGxjUDhx:&S l7= ox.= gx.= gx-%؃EVܛ !`;lԴ~ kkkkkjqWlhVh8r5` q\t5\sdzq\{< qWr?!uݑS@kk q3qZC<omH4 N.E`{NM1i-ZK|IoiM)UʴV*қZS|Jo_JT xϋ(6 B /[WVVVVVVVV|I>U*ҝWҝS#0VhЃ6g"haokI?b"H-"/ܴ ~F\toq7>)-}^r/ XdX,b<lO2'f؞e2\[_TDav^!DF}墏AVu.-.-*iPip|N&nphRL)!!!!!y̸y̤ح=G*\c|W7q\c|W/hw GlE(mc+thF:DMģG V޾o+A+BV Z8_z`}EZ,P(vfkCF}#pߔҾ։n3\l/"!Su6-:gWM 1)}._J{Uw˭}/}-į>%}+IG_Kk}M˶yasnVUBpRMh{mn-Iqaqaf$j9,՚V 5:Jeg{# 7=zkGf]#tTYX,`a0P&)>/7S^6LE̮t@qVZִ1kXbV.YRX> mM&ѩ-qt[7s ؇rD|6'bjbx:':'Ku\|O%O1ZzSc.|^掵<#jA0k=NE*)+H*D_bbbbbbbbD |XQ5o_he'<`Mh)4xTRb1|i1ٗBv2YOMT[i ee$n }7L]@G͏ .u)a͙9!Q\]m bNGm:i@l~S'QC&_^^eJk,\6.L^2Doª/mE3m(LJY֙W@J$7)'!ԋlMShT[$J$OaSBU3M3=#PhlwNUʇjP9_!bsF"8Qνr؝@Qq:ĥ]3.J( o}T^_՗:Y/_e2*; G EV)8wbudÃ&b +)0.1[h6ֆ2ت/XMWeN*d&{ڧ,KpY*+h)9K9#Aqk #`_EFѕPNUeoT.*?c,UR~ɞ : YQ*QQ 2'_pvXeA>$mبԸ?z_4CX޶N^7/dֆT vD֩m.,:oª/m8,{>ħ'Nci/}5n-_?t ͝ ~dJ(T}Sx(\uwƪga7*%.-CND`EvM ߾.؟; ;򨝦!U eH˩{e|v-R _'"unS*PMt޺9-TG &>Fw}Tn_Ժ2kxTH}g-oT^}+ucW<[.ʒb?_ZgՖe-ຨa7" 8Ym cE{ Z0f3jVlt-Y5ok7R&<_$h#bѳR\ZzDtCyPʱۑq띱C]+e͹nQ;U/Kw]Sbqd "RtBd#NF{LiQ(dƒ0n2# lOQ`rBwmUErwR}o8(4`3i<kY{;%UӻhpCga6 Ceϼt<1TOZ|8խ]"H*)с }E;/{{]7(w{Tn_Ԇ]{ai7 !8=ĦܷTZņET>Uؙ^j))ֱQ;T[E, IئLl )pk;tr[!HޭjإdReȦPs[qحX*S^)M0/\ P˴dA+-åEMTQƸ=XO!ЭkQ;ؙk=KңQA~PhG⟿x=3eШ~Q{hnse>4GdRIgZ`8&omNRgx꩝ܠH#XV>Y]ktwIV1_U*xfk.Yx*{T^_Ժ.U"jv8Krba1 bت7XMZn*ǀw :cbKnn '>utU>ȫSE֛qjzʡH ε@Wúj/zjkpQ[~7p^7'dד} 7U δM_u&wLS| /*{S:6|PAalO;UU3 v*:o EQg!*AWBYlذ!nÃkZDNJ b:`\=(2 RξHG* Tp ӥ#cQ{ک@Oro˩vbu*+4b# =;W;M(WSaM2'R7pmeUoª/ibYWGʃp+k-8$M*>S ՗S; t6@irTXVt2؅T?jr;;vŹYݽMNkƼ:uݑI-Zс ݿ!tj(vXS=S'7 ˘dr*/U3CQkfH#Πsbsv#)T^*ou, 8`]V xޟ&{L \Q,<8 FfĂ"p[낹n>aȐ]|W౒_kaI 2750L8K.$`^/*\aQ ZdHiLT }u57QCLޣwS%2c967G!l/7;b9!Q;ExWV1hp) S"Fێ6̉ e;'oTN_ՖIROtV[oª/m N "-Γl8jyʇ֡]7WnӢn.ffnʋ;;UL&))zOd$VH7dT v*5Ϋtxd{-Eur+}mԏi PYgr7NWlTN ݴrX 7)|j1#1ӕ 4齴dڣt9Ui4J:bgmR NڙEF݃ZU ڝ`BĊDqr MM]/ aDq5^QN2ԩQ:"V'&֨ߍTg^\+jC pˍn*%W'v*&u+>Y ݊{]9[r/\$PEZQ M ̹,9Cۥn>l16ukBHVP[H+g$-NiبGuqMQ.!*)X`+3W,a|ku)pΞ/ISoTNNCcD7U궎GO.I7T3WLC*\dS; 﫢!ЛU iz*ѺIkO܋u/99\J <ZVd:jƬr6/U)0XV7V5cV+ ;|t.kK9qT1ur[֣\`YqW{xYgRZmTf"*c)վ+8,pY+kcVSb,x.g*F$?]i&v!zAƢ "+ t-N+]KN91Vp˕F'.i>3d։Cm׫a.{>VTHg (^$ZT[07ƶR > Ѣ 1)Ԏʊ7nN # Ph6Rk  mf.X+­oHl+ >]}*嚰X, `X,1pQ< lH/M`*à Nq|bR%Da#zΪLcf%'ȬV+bXV+Rb ɓd v禢DboLyYgVyYgVyYgLn?b`ONN%VqYgVqXXJbV$VtV]hNi'078p)2q k,1Yn3Q"bA7jRsH+fҳ+0,ืx.) wทW*y yȧhU/.yWJ̿T6LiumUz v HHcL4aQ_՜ŋ!2a6.>.> i rC oƹ#^~aKHgYഖx}% Jദx-% IoҙശV*S.jv5BljA{OV*VV´ZSIZDG Aۯ9ӨX萺ZL_i~FZD_qWqW.616/ϋⅦ:'iMb0<ENr/̳byc1uſθ2\A"TDdoRC"NhpSu5hZ$?Z/BZ/*!yV ʴ8>E-|&3dИ~UCq\S|c|c|<hYf `XpW)]"ANu!5pX!}DtS%&3խ0J\DntG*3 C7bʙ6 #}x p[[P0f `fs,nC[,}"OK   ̸~e.9e3̸yƳ̸x1+oooooobpYbXV+boXB,B,B,B*Zo1+ox5+gq.:_H?/NV n J K K K L L L LZd:`֝G|xq{M \di|OZt/BZl5Zk>9zYV/)RRY4C;'zzWݕ0c;qeݕh{?YvifR=kGQ4_M]B}@}@}Gj:?_FF5}緵GlO8_GEЮtI-}wz Bu7L^+.qt:$ m[F|މ[CT⾋o|0v+ Z(8ڴJ?z2hܝp*SSo৬!oئ8 ~OnSO #E^qAZ*{B%.)f)nk5f,՚VbY+⃬Zw8FvixnDyZ&S q*nb؞bؾb+++++()+}I:Wʠ >/e&HٌDĦ:~3 27+L ).J>.J)>6J)>2>2J‰IG]":FE}E?OH>JIGLB J7F?+(𤘈¾ F7+¾ ݠ>] z!۟ S[jڈ䐶39_H}!H}!IizVIixWxVH],#JD_z=[ֆ[tRKEī rzܸ,{Vc=SG޸ˊqBQ"Q_P3b4DyZ&Jt8q ,awZo;B?6qNhhx-Gwܴsgy|Gc-ɡ}7x2Q}L }vkvSbljoZ2Z0+F֊բhZ31hZ3VZ3O0cX8I6>kiQsz:Ss|ԄWf^֟bgf3f3fC~UʳY64@ODD~CUlQf$\ AoH:]Aqq\{9+w9+Ev(`fo\: 7Y{o\ޢ2ϭq\cW|VqX9 NԄH1"'. 9#9OJ;AF{-\8[qaJCs%ޝS9==APb}Ug;18*e1k-oaG{W[TJCkLV2;*+Fy Ƣ3fּq>D?Uq8 u/GS/cqځP+n 4q E[\a=!epP-d>IE3sI<<'Z@psJ4֙4ףbk&ꮉ2N` aWOq:˽uӗaY1p M1\oA.`A S0,[PAbV)NKsDÙ)r|'X^ˆH$g3\~Q|#Kaך]ܨߍQz<3z+p*~BWvP[8j9jȪ֏ J'tkvᎦ|}Mȗ)5E𩝴WOO WF-\5k}P#/?($Z3NExGeÎֵa<4&CR9l\^=N\z `X, )3Qc ˺#{W T(] #uD-R7rFuC#yY !WJ'X֨'dpwߐݔ dUENᚢw'C&@_vRLA^Ey=ѻ)0T{PL"Ȣ÷<X@M\Z3dDGen8X5죗NIU6UaT6L\p3]* zlXr`ka>rvjR7j ϒE;zzvFRT> ݾ~p#wut joZKA\Ng dyZUI`ˇ dUD)3zWM· }QUJb1Fy=/t^z5e!,-u]'Z7O.-)thv\>7e/'J"%ԛkz}E k(mV\Xߓ 5HEM3c]Wr2p(Z۔Uo$+:MUF'avVGZnahՓ5O&aһ̘wb:DBoY6l9Zq O!> b/T^(pe֋[Q)"؁)e3z8/Ŀ 4m;2o rGQ#Uתh﫨UIܡGql5u)KzGdMլ27&33{.ȪNm6m)mI>!]{e1pDI7O$cz64&`Sm6Wv[mtm[%%Lc1b#. */d+d#_呸<# 3 5]#E>DL8iZ;;'vkƆcޝ{S~UrnRJUπ$RZn @Ps'oT\Fi%oY% &IpWxf_rMUFHjsP_{FJ;h[Tx4#|&zqGWKLFTk@3^© T4\QT^{2 &,6YֺJZTX.y#)k}\^iDAmC|(ϜgqPc8J p}T)@(֞|޳ң] ZvETNvQ|c}b-d 6m7)h'C&?wʇYjwe1 ڍ2/v߅P̦%f :ӜXU-E\ޢ5 %vQ(9aqHn.& 9 #PJR䑺:V]^]~Qr`X*unj̍,0DM#wP4H!v\1LTGXgp;2ORWK2FvX67rn߽Sl]jg)2Tn|j{Yi;рSsLT/+`e2p)hK:_񊦏*=\nTf\-(]UG)j֣齴2l85p.$*'t߅L'blX 7)>(nЁ Xz﫺:}LJyXTnnq NռrRҮ!0qr&Qʅ\Zf! =N\EϽa]Xk VKP2VI mqMM_$ӊT>I|kڍ#qF=%J#D{X~੔46B}%P"6%䴈?WB9\_]/ʣT~jdo[gWO L )pн`UD'pL7]MdcETM@- aVޥ@؝#%Dǂ ѝ຺daC5No; 75Nxr9;jحݔV̨aԢ"@`s,ӪnXNZNʹ*#uߊGL MSap^8U &-T4DABi*tڨݴWO zut3Tn|jεAEg#,:xߕTMCP{/i9c³ kvsGƪG`tc5cVQTN xl$6JMtNɯvGJS9ΫLq~Fʥ)VurweɅ{[Hn6y{;Nל*#uDpqdV=ݵI֩1M7bq1|Y}gW1rh\wMGwd˫~nHԷOgZ|B74*px-vꪑ(dB(=US<+:fqe]3G{5R{7 T:t߅Mk,%]2U Ue[0A:4IZv5aTP/PO_BV@:9)WҮj?5K(KTth(CG!t)p2T6!V+;s=8,A:M˫]#o G;U"Oa:b_\5.f0%V :v 'MaPlvңwR% hHɿ#&Ԩ}تI+mWc]+kU*jU%uޤ2I ˎhD8jUбXWrb7f ΰ\ӕ.2MBaOHlm4V+$YLc\M1UjFIFl:yo"Ղ¬ mɹ2Tj665uiFᑅXU*'?W(TVJ`O 1#dTQ`QÂبfo]V5T"oSjY6,Ղ`X+Q"`+ⷫ5fՂ`X, RĢ;~mOi(qVF'bGeL%Th?ZWu\ZXKRԱ ,BA2(0]+aRYrY9g?dYg͋aR J JJ J (j#:Hll8m N*WVyYgQߕV% GNREtⰱgVqYbV$Jֵ|Wܬ5u|rgdۊH5i ZC<3iK\t5C\t5\l%\d5BYVt/gCw5Bo8bY,X3̰ga̳YY,yc<1eŷ̯ h l]CXÊAFx{v+F>+F>+F>+F+F+G֎KpӷP! ]zljcg0R͉ Fk֏#Z?޴hzG$xa|طD5$M8)n 9q<ˉe}KK$x(x-eSc : 2 Cm 8( sv9f7̸c|1eŷ̳YX3̰X3̰gf̿?oC`B,odoŗp|Vv/:+4vF]\Q`2͉YȞuθ2Oq ѿZ(=e|C?;:V--kD}CZ*РV ʴH^^gtN8!c`;Y3;d1ۜ¬8,20Â/VvPM}Є0͗kL!n[+v P6u7q\c|V{|VpYg!b!b+Xc^?9xVpYgp>p>p>p>p) d{aM8m?;NӠy]]@->iqwk{ҽ^"S+4"i~ERGR5nh͖lRN}C^8Y=+6h(,{y+X\){\I\SW|ˉwqn.)ifg+cy by걉+:/>)qV|_9WWYWU-?:??>J5 tGS -Σƽnn?TcE=MڍA7BWSi!uC0 B,RX6nF~/c1t'CmI[OLI>e#̃`Z+NnP螘`i &4Dk'෾?]He2R[K!3Jr1:S"CunW9nk&F})})LN1?_JeeoWZ?쾔}8ؾ~$W鐾OLU ѡ21߷ZY L;\ؾ>!}/_KwFצ"OT_L_L-by#K_~MQ6ktxCPJ$~LD#ޙ/}bb o~LƗi}1H++H?#?I?%Vt(~ %ͥ uujjFu{w.~?u7zf#i~k鈍{}7}o{~鸲F~>C䞔tg:k4'6>4^tK'q*C}NHR/._H<_H9iJөg-:(yʾHw[J:+~֭x3q2"jY )I1mL !9򈬞nzUE?OtzVto_şXDU1a#H]5,gQ&`ϲkRb\hŧR\7ȪA<dOj̈bˋyktJlPQi{{ut}lF[hC*K!>,Mn&sWQI~Kq>5h,Z  h0G[ю4ZS/t"wPXiRnUm[x`Zdg+8̅M-*uMvy\90U䑫s l( _t("NMTChx9T[5DvxP*tGƶ/`+J?{UH>Eq/GߺVjG_t~(T=bvQnlh<>؝A;؝ASOHʔ[p({66 Xg j%)05 6պǀ3 [E aCm[71r[wLH^75}]AJ?{U#W_>j/y=ugw÷F-dHΊOTU3w *g:q==rbu*Oha]+g|-ȂP 0L˛fkҰc<g&JwGǐT?1( p[94+z1n jy۸ I طa<ΑwNQ^u?;#@9Z#Qj9T)P{*#!bQ~纰keaεBd t MÅQ)lֆM:7ɶeVB]P!YnWnrg\mO*'t߅PrR1֤r,E$ܮvT[1D&Xs , nUgz. YI 쨪wx~(pe5SzNTѪx˾09A5VٗR="Xdz.FsIeĥo פh8'4EOMT2Djl(Qj%lP+o  ]ģt[(1/ZE툴2I^㽻Ƨ'vDž+l15ʍ" w;ǎ :(w|#E"8rgv\>.T巭?u]‰H{+wj*r^Cȳoڬ:\ZHg_$rGtmiO7RGޝN3$*v2 -%ʖ9'+?Rwk.ݍ -;{aheReP;*tFGc[xc^-.vR A0Ț&Z(߂cl< nKI( șiy%R.X'nT> .-k/3Y~9~-prL휶5***(d]5S=J^p9e = 7=:%#z?5:wW՜ԛEV4g$Ȍ a!68kNp#Q{U3D[n-pU Ğ=06vh޷B7c*h(}Xu޾F#Hy>S[KL\|O9S7Ei%PGMU Or!@8=ipᨨ1N |(pgg%T t tQĖmoZ{**?liN /ɔxa ]i|(Sw#GwM} !Vi%]Ab Fnv%r (Mtg7&=hPfVIrVO$dSdPY)5dPT"Ha4TiZM&7KsTn;ljI#QQh7j&pj|OXCepQb{LAfK!?qwZ4b[dSD^"86G:4ODMT=Z~UFY'zQMoHԥ%XeCjr߅LÁoZw~u;!d_S{ʨ ]zf70.7S{QNۑ)|v ޵LXμ+ NRٓ;:U8J[:齳~QoZ**~4=HhpwJ" Z?-TVŤZߪ%%j! 3M4p9-c0%MTNEG=ݫ1 }y,,>K=jdڭŬAح!&䔜ԇž+?kܾYJjm"{q M˰c[b91 rLu+ѷ{vYJVHnot0j#RC0L@{E^C.Sta[U1Uom8U թc#bk[Fk^>*WGHD̉{)gE9&tWM75GPdc]R[X$=Bg_$g-b|"U' "D]U3(WX*/aW[5;Ei fߗm趦wX)ɍjFR!^Wr۵Q%; RYɲ]~pVVO9Emt}RIJf@:wκ]('oT ,WL#[cek [ڊD#VXj+*"fK5*gt'аG.'W.g_$]OY{ e#Lx"\*P8YuXUF X,Jnn׶GȪ,#% Ĝ*aE`#iƸTw¼+¹ҩɌd*!GE)XU`X,iX,0j$Sd(?`mQ/swOXWX, XWu+gI=jjY5fڳVjY5`V| A'uTQ8JNHްY5f՚bY5ftf #`Ɔo. jY1f,՘V B|~O¸tzڰYVb̽f,ŚVjX,(ސ{l71R7޸` 0,5Y5`BSw}"(T\OJLթfXZU3ݨQQFq1+~+x⿇C3`2gf̳YY,2Ǚqm.)e2sd .0 ,M+t(҉=eح#\G޸hzϊϊϊϊϊK sc#DF qWB:ZV墹hZ+墹hZ+9hWQnf=ws&1N8ϲq DzHDG""x-'"x-'"x-'!hxZ7|BCi*HgWUe:-8ӂ-vJъяяZ7 E>!hxZ/ FAhxZ? G!@{Wcv6dk:ncŦERܛiqM.)e7̸2gf̰Y5e?2t1AԸ>ahcF4(,Qgc c ̳y|/2΅ⳡx~+>!6}뎅j ؑ>˄֑i0&ZL?iMZS|Joimm-U1b؍iUQZY-UʴVʴV*_ഘ Iഘi1<"D\tUE\toq~q|V|_ǒ-ԁS }]|Uw&ru8\8-jÃa^V `Á`X,`X,8, #;"}n%pD%-ҞcGmtOsJ 4|ֳ1+gq.:q.:q.>ii0i0i0i0|iP|iP|ip|ip}Zdxi=Zuii|yE6 3 uFA,umN;'kQkRb<|z+s gЭ8y^_Rv/̿XoxT$sۏ?w~{~>?wꯋH#fqzҽZ]+қ:պRZu*[7c/)^:RޟiNݍrBJF zE+(q}'JRg']:T[W˽DJ'D=%8_EhpMNon:ئ}$a~kHk?UwyY"%]a+.A}(tҤi}+®=pdNF7+1\Q_@6ۗNa}$vґ-w!}(}S_K~Ae='?H/ee)~HPIL}d5='\[։m-!ûWK/&Ғ_KgfvA}"<;w4lSoK+q2c&r8]bD*Tm -\i]n?[tMjP쾒e9k~-9#}!}@!6Gj@b|gfn%4vZ[WzJ/zB7zB7z xݪ>(;̊"V\&SQ"#g9ZSb3VZTOE>үĮ2/RNΉVtOg+:'_>¦[꿋2'WWWWWUg;'b- Ar\?r<_?;+߻_F7ьE\?wU}W+;Qi:cb6l?\sͶLξIH9IF~ afTOIB#S4X2|Oѫ}?xTGlkk|!V98kbX #Ҙ:TLQi'9̿D}ll$6V5cb.aP#(4e g}2FXV+cHKqf;?fQ,JĬJb+XV+C6؏ J=;doBhJ#X%4Qc.Kn+QAލI<3ڂER( ;FLKW3xб6Nڇz+|eQΛ:dx[e8H-rlLQfA5LdžK\L9koT6{rr ѓ5LJt݊˦Ҹw3d1.XFξIIvwQMUcG WL8xUHup?{fmΨhRT. !6%yA/*+ULoJ݊uȷ6r_=H-sfDFl@ m 4FNnG0uj=Gre=ڶ)L6uJO3g*ߣ;WM8wop*.îOR0^|EpA=[uD{G:T. dσl9{+ 6ꊥvȧR%A n]̢wƪ_vSrF!#٭r Dܪ?H-JF>^:OT9EQ Wd邁xV9Ŀbہl䔮go ,71UG dM8xT/FRw\>+}oSAP팋@4Hp~hПzGQTʇ 6ٳ2POmO'A3pd\89E7}MXeț/^Mȗ[8e>BUk"/Ymlu(Bo QtWwn>3RzaQ;UG)Xx2|WA4N{]7ܠCtXe:V:Կ66{ Fµߵ-AP텷(S/"x*-vn4X~9^3ۯ GQcstjM-ujSj*9ax#DA(ԣDݷ(:PClј氛9ZRi; tQX]ӉsPb #ŻJD=1v+lTnnFcX;᫣$Kg_$s; ~ت#06!6:/11Dm t~{̦F"pIﮛWc!:x\L {NS*[5RʉM-2JEDa2/n%+P!ө-%,ц/mprYNaLüv!aI]Խ%oS2h]GmĞ&m Pi:XI<H*t*V94Ft /D}*ew+TH Ro0!C{eA ˇB0Hu6}+g9eM몥v j&Z<P;ʢm]9.|D9Dŕܯrk a S|62`*'v*wH^J}.%ӿ R" WM2z:+LHRN΄8{}vC*qߔ=F8wuK&=-5)BGoTz #(ISmBtzAX$$7hQP b%|񐗊Bls  $RˏC(>+ѽG&6W(ο EcKEl"}.?\q`˩Lj+ R5D*ت7XLrwbe[y&|l4JڨssQr_|<+]^tpsdNF;bFGD(x8OP{NK졕iAQZ[n3cl/qHNN Ν)#nNV}Pa10\jThoU"sYhxt*VWlvLhTOJ;5 fX1psN6e;['Pi.9*T?[EtॏZ@$L43u6jk-۷?;bMPF e+!9ۉn*rvQ;A3Z¬V5aȰG>Rz[Gs6șjІM>-FJ7u-m"Jn,uU=7V8vzk($.0}hr"gbwM#% _ut3T>۾5R)0^7+h;zR_CwWЩݠ }^;bZS"U0NN]U+Sz9Q;5R=7O #oܙDaBǯ-ʋ݊ug. 8>&ˀTOxq"EDLMQÎ1Eݩ/} 7aA|JKIz78tzξIJ꭬FS:(!h_ZL^N)݅γoaGZZwB9q8(RL+1PnMႠw+GwRΧu(Pm'&:WOgЩݰǑi:䗆W*}, 靳1s7rbp [SUI:ЬVYX;#Z9_I|d*'fO⚆SO{ſD78לVCFVK mg-OGD2$5AG{͊[WڙJx:Ȥ#]Tfq#޾P؎ly5>KwQwfsSFa9E2yk6 ܅T~T.WM+ಐ,b& }ک}*|Et}Ks/Χu(‡ wٿP;SA {h8<**t+gZ©I[͓:h 6gKn[^H:Яjt nS4KX,՚Vj`X,Ղ`Y +WUZ{yɶ>n7 ym,y2\S|ˊoqM.$yG.$y. y?Z? GAhxZ? FѾo|BяZ) F>!h-!h EwZ#BG-DrGZ!7k ݩoedbp7e_D CZo*yVȴ8E-7hqDZ$o""J<=vLѯme=b3vf֭/hq|CZ$_*VʴHUE/h|DR$OhiqpBF6"Zp(zHVʴHU/h|DZ$_*"VȴHED[ug#:! Ț8FʴXU'h|DGYu-'hVʴ8E-7hq|CZ$_""VʴHUD']C|vbJd- I5 c7kʴ8E/hq|CZ_*"x-'x-'x-)" )VbG۵q Uu'7 Cܴ7 Cz޴7 Erc|qFCREKwKVhQ<hQ<hq<=hZ#ֈ墔-} Ƅw(u7̸y:\[3EPh.T{UX{:)QEu'_ME}5ޕozt?_MEf/ԏ"l?t1 LFD_MDb{Żä|Ů4nqtH{+|h d(vhpu9orcWԏ*ԏkUޖj^7-Ⱦ*_:";G辗?ϟK跞.; O .R!c0Em͆C6y^wX?}$<}%`~3kf }!?exX OQ/V1ijt"G: ,@|6{n1^dЦi`gKl/}% ~~_HC?} u~WS~N=^G/u-6d3i쯥*"A69M,l(yBP7+ts!iTo(RMG}.?7iod5Qhiiݯ(i_"kKyF-6֙GcZe~{M]it_'iivꮦQVE~Lit_w2u}/֝F_hZm~moZmM{!ito -*?~JO%|G?UQ|~K{it_w|?:uP'Ժ'Ԛ/֓F֑EEE>\}ݕ_IZEWE?WE+)tv$f-ZEݕZEݕu&-[M6j4 n nETj/=CVD'JݕQSUBҨEQ}ڟhs@Bz< c0EnH}Y|S f4?d[)nz#-&?`i0촘^C {ip}^BϤ!{`}!ݯ {/t84Ə[e:hgAh(bH[lxPuCi}#?~NW%A} *Me?}(>Z.NnNhiIf|b93e> ]Zni]޷/H|WwG˶"OW73ߺԮ~}F_vwTp39H@@``8C䔈¦L /'\1!]9\ 2kFV+XB5.}eT&)V[8aB>CDc5`DaD~+3,xV5bXjp n%K}ݟ+.E٧qux|V<y̜V5bXj XJbc^+X/J?Ӫt}](tuw9+w_5+wx1+wxx1+=+=+=(9%Ҩ,R!Z`:s!}VyR|V%bVqYg|V%gȄA7zC"\0#=Idc]zȌt'N[S')˚U(-_$xvxߍlÃ< Ψ㍣[`a4'{ ^ۈnP*GnEl?[u[(]5)j҈L&SKo Һl%O&*p!vr+ \+ٗ&I>Ğ#7vrj| [[y%/ ޵AƸ]}DPi~6ŵz/rc6(q^]! ڞ!*t߅T~<,RnORhsՆ"|wfQyPY wkNd h/TQMT}=OA;箋P.4a79ʉ4-vŁD*_95袉կ o§v(e38 ,g ܞ |lsDҺT.LNC|o66=ymL9 9EHdO$M)}jޏp{AeQ:~FGd*4z.erIlֺng+@ 7U:<(sM TX%m7j3b仸l1d`9%8SpSU3UGWwCJ0 K48Q7_wJԷq j?؇л*wh!pfScň1#OeT>P#;FdbRFElIͮtjgWx/6@2)֨}&"u(cmzwMUG7LQKEUMo:K _9uITE 8 `rZ1s7A xkCI3j mD vN:# ըQ_ B>uadP;d|* aE4wRI(bE,u:WrҔz'޹}'I?w辒)Hǟx}>8{4;n87-1(oZEVˋ g_  ju NAO??xw:Z5Ao9.*7}ULo4Ǖ 5z7*t*FЅf\ `UM5YXάV+N+9g P;rY=g,圱X垳r*xP;8Å)ՉNDV&n:ƹuJ_e )_-h ܰY֨Nuw|scni-#2$qmz.r3qP")Lqy<WSMm:iopCap h֬6wQ(U3)n6qQWL-1qPmjtAQznL߶Yaޟ-5 *?`T5 DG{LWdV񖮙jUP;ʱXJbjbXvFnXmlW.15ؕY 䔾ld:˧"M`pT("#ۥ+Ґa.tO޵@=wp;"txdmQ"dFH8G\zNG4xYjP!yk@%G!е: Ekpw*-"-X`kZ/GbDSV)P1 ^xMzC|jQQ;F_HBC E _gzBFhs~DR'v2TH0R῕FdJin.T/JnpPl86 |Z: POBvѪ/X(=RQD e!n&KѾ2^@aѱph(sc:g~ 04P(w2GF.Pئ9 ɔ5&z4=E%+aQ'3ɐaosEʅ7(TjgGZ7:FQʪd)eJzÄV:]m TX^shiPT9ԗe_E7)~PiTL @K,FIt,b(EMQ7L)K+x6K_4וvCz%/~{kр<C}^jtwuRhQ]cwTxlko`HGH{~Q`4tEOU~O^,m.-)LyɠFO³^?E핂e )P%= 4G0 A@sgRI{~Qrh=ʄ柽Pq Jz'tiz Q7C~jܶrhHQN)RlNzӨXfə^H"FlVZT>`@v*'eA(dC4IAH"aBx ȵǠM&CɚwuSéR;ʪgvSx H\UiWut$߾`T*E6 'R28^8k%DgP;QzP̷J9hZl7n[jg`]* +PGZ^ v*vBU7!h0oRĦYc߃fAaS#SHa՚JR XrGJ-zBgE?&*ox Zd@b6[8(}2d,7+ԣE-_2pW)Qm͍~lH!4b9n &Y ԣvVDt7ma4g?$4Kzۜ|SܷGx؝B1 ;F aEqs-%v5{[I"!rlϊBڬ9-p.Cu(iģF8m*Gk":$ Dkb>$Ӧ4ÍM7`{;k":%!hKslhaHntNѝDTqݿ*sPno@ȗ.y xnotG x1%7 Sc;wٽ|u?(I&S:K)4ڶ&,BlM$4?N_Z-ц/|vLH}tZb4HyhmD;[U!7"2_#>,Ll*He)4|wE)̄qN]'r.|Z]ipFuCQ{*_[ۈPO,2u(Qd  sn aMiڷ ,y ӡ!k]r%Q#sW3 SPU6Z!uD(v"!-w6zwB4! }kqWU!IwTp iJkɿcoszr/[ց2v$f&vd5к!u:@Wbƹe D APORiV5ܮ &㹷SDŽw_@*dTb+FMن%#`Yn\TqPXή\ё< Y6<VuȚtW\7-|dp)S6bXV)Xk3[Ƴ|Yx,V+bXrD8L/A2?|JϙAXkbXyL{UQE5bXV5bXV+DďZZ`2(4m`MmڢC-fҮ+cV+bǖbX 9ТzF;ehYLcg+"F5 n,(-ceTHG ("7qWXR*,Ŧor  `+lHp;Vk JТ7mX,8=S[M3sTX''V+N^+3%^U²IVH8t+q.RIVuZҡY:Z5c6'a̳b<eO2'a̿Y<={Nf0ȜhBO{+>/ϋ+Ⱦ+Ⱦ+.} o9mEZh|"~\|Utxr>/ܸr"Hr"DZD_qz Cl6l= w-"'_ഗ-)UʴV*ҝZSJwiN)U;ʴyV*@čb/5k^E p㽣dZQ,V|K>E~*ϑigȴ˻ K L K K K KU*ǕiicʴZX,yVE*ϕiGʁz0B#YCain-UʴyV|Lwin,V|JwiN@/-D 6 C"q%شyV*ϕigʴZI$-) Hw_ഇb<B`cF(ÿ%6{Bx.= த?iZCʴ.= 07!E:j@biM |f\9WrUe5O!lgLrg_!_wJ 32.S[֩L\Jvy&+XV+bXV+bXV5a`` # ¼8,8+'IX,+Ú¬+¬2X,Ե-\ۏ5XӲhBxTWFhlGy7oz .c ،ɨWLUs sݴt1-ԒF?X)g XoNtNuyU׫e' S&o:PB֟pX0juzz,iW4 +,|(AsuPlC)l[˕|:b=Hگ-,'b@:ЫvhA:57_ФL9_@d'%+*:i-Im[?r qT'GqT''r"{.2'r"{.1ˍr"{.2'wȞߢ9qNVGKf?9qtDNv?9q=ӿE<iߢ_ LG_\kۿEƿݻ\kӿEƿ;\kӿEƿ;\kۿE![ۗtqNvqNvƻݹOuww~w{~ww讥4vjeɒL"w6ٝW>wlSMiLjU =>$GYcD(Q_Ut'z \XY0,Yf,w1f,\XY 0,t5`\XYf,R, 1f,Řbb\ZY5qk1f`\ZY1f\ZY0,Řj 1f,b\Z 0,Ů-qk1^Řb 0-zF|(tGSew &EIHT)~„O7|ٕO3Pb7ç()6Y298+Bǁg_SQáS(a w#K^ +vn2'g_uʻ$U;zoVjCVZ1 OLRbu+ gSIU5óxnKNrƄ6qPb c @Z 4Ih(lp5ҷ%9ry=gԂ>_c5G.F\rpVI .3 C4+-ġu&5S:gγ{9qTJSÞCK ϑ3x^AJ~~cm4^y|~\l?SnՆuH9MWy7ww 4=E*7w}Re~?,|(I>xˑߑϨs }g?Xrlrop8}NQuN<YW޿Yv]TݹYEYzރ578ߩZW杼I?!n,9gԼj\, -o](\C"^Yv.\cݻkEMn,ڎsۜÒSajM ڋffm<wwcPǔb4k NR&KF"b*/_(`0ЬR*C.aaV j6bQRՇ0U9i]\1W޾W Ww8=R6zRIǗR:©7r:Ԭr.Ѽ川i=ciWw2PԜjƩz޿Y=cᑏ$b:_=$wkϔgÐ]V7x{?U.f]CsaɾҿU|%аRq\ro_wǞ6 roI{}so_n|J  갪Rͽ|7cҿЮiwԼ*¬WOMXzHo>W>K6P'X=Yz޾Qpp$ȻOԼ26zzVYqSdteM]{rsPzr}5m7DHTg7vrkE#/7̲থnv[%VymޥR;-9I6pxr<8{}84;CRN+!2AJj-\6̑ ,8 Tr%qeʹM~>t} <(8J*Ԛ,,Seov1qMC&ķ f~'ț.}7|ydc*:4,2fVli >no_O# w .enR&7a[wUw4>[ G?wsrG#uoXWDUp]5m?l|}L CXx?G>v7x9\e}J]>V)NSV=|zLaʢMWa{c⇩\xzv24FtΏŭh2#McS"˒m{N_G2'WO. %ە 0꥖bjsXMy5;WV!|.w?oRQ.b.,=Gg:pzh4{PhJY[}L~'"E$ǃÓKgXM+)zH3*#Hiz&<U7j,$|&~%K''e]$l֛~ 80(A؎hءCnw5pk$89X]s[ލ땗xi'^%~O+C:Q!BБخ˿ u,`,xJ yaJH^(f-:JPr&O];+Ö?Ϫ8ޭ¬5u:qVtZ~\#@qؙ.Hf=J,CJ$85Mؠ5LbǁgYqدmfj7:Anb3-l$Ѿ/_5F ۬Vj63ltP/ Zd7v'[TIL!m1y-܏o1OX7r,2Udsx{"Jd'!By}M+"ϖ"e"qX1MOo֞)i1Z݁bXo!̜\qV">+ .&R_6:pF޾I.yz//r8;9ǣmPjWN""-vE*YMcw!ҍ j5kBd7ޚdJv 6UVʉ8ʾW.:J`s Ȁuܷ| x HpfhA0zkGU:ۇG(*ǖ_EǪnkN)a:m#ƫ,l9BRU^Q#!;))vp9ɮci8zcH/4.*$ @rmbpr˖]s>EWr XI.Q{=+up&!tBm$ػ`k7:b-7(S:3B ҭn[ͪV;(QJ,,dXa*(Q#QuţDh(=ޝ ;4Α؀Ok*lNh`qWkSpme.|x{|G&_K=Qo^95&Z%͵pԚuWdl[:n(NsN3.^]A X2^&Rޥ9I%/0 2 oZ+G'\_w.gRʋWmpt wMڮH],^!-j׫b; mJʷZ{D@YoWZ9'_vMܧ['Q>ܦ4azi3[~͊ \dp&-Q.< U۔\$ uK~ihuVy e룓[]vMǜ5GTn Ovba6]JAZ5t0[Ia8?RLn>,HׄԚ/[ _ K6Cd+8 ՗#%x))r*3r-]_KtzfJVIr)IN&ZnEڊY1J5,2Kd/%MkByZ ԇ!-㗷tp «Fܳˆ^jxr +P7\ ͓`Wd[ű[F)jYJua ++ 3X=4ÔB2瓳 \8a~FWG rÚp8Wі%|Fd>^߰# ,UOUW~:6<&|Mw>fX>-XCq-\D/{"ˈeCe3Gg{-\C}츆q ?euZ3}fhч=ǼZ7 \E---#:)jOF>vvvvvFwCoW'"y/bkUE7Z$_3UoX-Vſ;'q<ˎq<ˏ\|O2"y̸+\|O2y̸eD-"'irk{Ѱ)5cs¬/ÃaVa^_%Vr]Ek&8<+"Yx,8 8,8iWݑ+`#rXW%X,8V9w 䙅ё~E¢ܣ\+ɭ%+maMcN6/tԇ"*6 1-W Hrl'9`jM˖r 8l8+Â76Vizԕy]*ZкWa3 ]'zD58| I*n m^WV9Wn©+ X,2 PpV7ocy%|4`"FJAar«Թ$5aa8y2)ս R¼8 58Oxx+sT"nVDs(ʮ*+eaoXd`MbWV兏O8D %~pMW1 \UधRRJ\f1\\`+ZORWsWW jtU o-BxޭJ7i@#KIO+ J39H:7GíLAX:y j+#1 ¹Uq2Η)/zaD6AI=<8U%<~χC$h뷺F.L{q\5+Vb"&v(5 |CeU_$>MKgK}ʿC.zV,}Ϊ|!R!aTLJLI#Pk&J̘_6i{LXZy;S\17hcN}`nݫh9m1&b]}I6fp@KW8RFF5Cd1htމ3A&]QdK{qAaetvsz XVl<^IN$A9C10Pht 鴮(ޟ 88 bU ÔU:ЮE N J P8sd1$:~CGEV'pA %>)jǛGzf@! ^M_h-.rYrmA_WMvMHt>$j)67KLoK+UKQWgXU[znLx3O ĽKkNaX~5ȶktI?553iu]oBRxXWz> %0<|R V_-H6OMQ!GuY%Qc5i| cV7W ZeHxॕ>c#uxf[Z.NDe8ܟLn9ڸ MBt1)!$Ù7 w'ʝ6PMEn?m`??jގs!-HsG~wܮ7$|f8nձ& |C7'g75]ة9]vntИXpRsBsد 8_w7g\?2<6O#So+' 1HLJ6Ewl#D &ˠs H׃:%{ c/z,j] ˮ Qj &ș:2UXЬmr{ԥ4q_&WB{OٱE~DM@E!Μ g053M:>,?;R.FUnj؄**@-N`TЉw-Kb+[Tc"sv}^{oW7fSk)͠`vv&ىp@#YԌWRh Ds@sOEV2vu-ao\luiY78r׽cF9(Zm\:"TvdTeQ˞X557 im鬳/W δzuW SYbRS\kR+0MfDD8[˧IT8$iQ_9ݕH 9u_|ԈN[,8, (!n38#6j4f3FAV;N.Rkl^wR|q-7ZX&)V\9t1)wB] p[7 868ouQuYDILPޢovec4葳;ռn56WwB͗1W"(wܝeNeC}q&|4`oW45]16W}Rԏ9%V]RWW2cɷp&I^;᫓Mk|=R#tSJ/\ Rzi6[em<*"^#=^.C&JG!RDŽ'77|!CBDe&$Va%#{vG!]qXу+0"]۱MH&;~ zyS&Rg<.\ ޮܧPv۔ 9s6>[z˻0Es)VQ5n t}V)?rS&ԬAvb&{ބNnDL}J&9`UׂIDޡ}\W%> ^^TyS*WɑCteXNsbФVX^f! 4Qf u`r+SEWɮ[I2 tR}.y7]V%|ΕF+glrRep2hu+NKF ~F 펔lA͹Ir)k\iJWpFbh>F3ZkTZDewMEیB¿n7co9Ijn &JlkDFm鰠P_8zڇ;N6luk$V;ɩ8RrBA7nmH)jڃtLoFD 5'6LBapd.A.G/Q5ˆ͗ig"pĆDbJ5d\sY&ʜWӂHĎȀH`cc; , PWz<x&J3)0~ЄfHS#:ۆhZfzUU]˘lQ42j¢"3R,qG u'r]X'6TRpV"Ej[x#CbTʟHC֋OCkj [#(ݱVOfN!ݥJ0Mɗm_Ԟ\ 2_7O`He݊ Hf uZnNk+. Ȭd^QC^.F W͗1DfcM`Q 54ţ!IYVn؝ӑ 7Β@j g_d`(Wo,*`M^7j$rRd3P/L}DWWnJ|,!X"E|AIFӾ&!bL[vrP#!ܜyө9'әrmAlS%Mjir0Uo!7q!b qjW (s)0:PoUB5jZNF&@ɳ:-tوsPIRc}b 7[pW)'2z*;D;v=8gEi}SUn:P+!xWcuM1'+rw{bTF6.˝$pE #X [zjm*N ӸHHr"B.pRR < *B %H XUΕRW z%n/ioZ11X,^}g(eonRɓUyC6I[EGk[, }RW;(v׼ r qRnZО+˜Ü0onN ݍ&D |}SXVI:{7s/Rn6gԃ:x#YQ(qqMvqv\Wf%ˎH։]Pe*.S29`Uq `V 65.orcы q+¯q]+MBK}*|z+qVm6^g5k{]z[2 J{iC.1+Na3slb5V_~^{n d^U\r¢9`QtKBo1_z+|fy^Ԏ5λ5`` twӺJF˚.WaERJ72r,9c>z‘w ,8EڑɥrA8cN2[ 1Bb5|  sTG~ͨŎUeZͽ9qإ!Vm`ݷ{y]^y9'd] ϐW~5]VGpXxC b+.>ʩO|u-2/dތ`-N^yaqBwO8g{ Mp%ΟBQ2{Y qM&etňe,ΆMjSR= # 6{ ]GdXZu5L6ByS#*s@0c] tr'^:-pRD!Thޛfց+iOHE0$[B44kOdKgڰ6[Eظe hDrÚgϙs Qcz-\72d=m+2%BlO; uYnqSy.V>^Q/31/w*J;S7NIw-%,3H6]~gsvYbHDujx<$0p˵0[!nMXݪ|D ԑtHx+r[ ɄV=GJqD+V@;-/x!S FY3WW{[}R;dtĺm@ǣOb FmUܑ~[I9)*K9H3liWsBs,߱:Ȑɻ% + `E񽂻y9cktx7)k5Ꚑ˖7*00%ё6[oSiޜlӮGJҢ] ՋoxczPpx $.[ܙz߷|'Z'nOGY0zHM|WZ`ÀkH;~'<9]Jz.S @)G+: |˹!s  X78[d[2K.⯮| `򭈒'R@;ݰ3з%M!oJp~g6 JF㝥͓@0I}Y.Gz#oH7וz2[ܩ ߍuaO )I۰@zwgf?${PJ1s׆?E/a{_9?A Sex6S /h]`{c~V_$~B|F7嬰%h*1$UJ4?chOK"`Xki(PĸX*r%|;p&2K 6ȧj1%Еv6Lx* Ka? qc<RBK%'i"I/ZPfr oJ@="gJ'TZKLyi,/CGF FFLDj :fY16$!}چesν7АBFBoDdFَ]iۃpӡ9N`c htM_3}11Ư].1~ڴJ {ZG ЅEȮρFD$&tn ObF l?jb݇_f?"n-b~&(bqm$O)H,!V0N0 \ȫ fxQďy%M gT(1Wئ COq bB_XbwO S ߓ(?b~?4.a{l%؆s2P@&ُ̫F''ћ}cq>|( OnCK_(/-ZfjU\Ne~VߙB1xH~RHdWOɺL?톤|Cw(+sCB́IDw6iȿ'K亖?|YSv9H {A<&~뺺A=A,_:o ny@G !y.IL[`Cho3 د2@˱eoW {߮_&FO7#Y轙ݿ"sio"jKK,;.Bdw+:a)%1"$QHAD2HDȈ4mA1?JrN[>HP#N%lu XWёHz(cа& BPhj΍8p%[i!#G$x ǩ)Mdyq=S0茨IrUߙ hZ701I$$Bձ}1DAvn[-k)vqi(4|:d' &-] =>7YbR)'>&q#<-oJ߲'DQ,L^U9kn=O&)l>lR?UВ0DIiB=hb!M8Bt6:OEF7G& 5ud7cI峕_|S]9hKВe:662F41q.6~~2@, Ꮔ- {Yc A e B"e( IsR4^^skDjR*nbB%e+ _E%SKr(!(QK|XFKbgٱ# QBKcWѩ~QyH}S?~RÔtH4- Өye;g'`C+ddn?/_w^_#]q~דdފEFVdK*#~~Kܗ~Hcse1H C#cܐ@ię)tfǸ 5{A}ð<`/rXt}o¥_)vSmmb `o_iSs$7Lt/ \S[KCk,B|ؿZpyؓc?,x^s&C)bS]옰7̐)KFΜϕ |v?aE2?j!i`HR$ANp~ƒ=M`&7od.Z%,!7NkEDl6'RF~ږ] ٯo_Y8|B f Xbd{Zs ۄ؉ F/A:T`jn]D1\č~QX 6L ))'CܙBKfoqKr={CƣaPkn XeMAb][C~"d gOǣmYdʇ0X=#yhk.z#>PN1IliNO+"fRIDTM蘑=d>}k9&v!ȝFbNtK 1->PB(_2,'ku)PܒKY,K'Bvb&hHW"CePMFE}pWaSG>fڎLOVvCYͻPĥV>"_| 1GQa18.ƩD8X,^ID Ir Ÿi$6-$6-$&K1H"zPu=p7A8hMN 7% ;E8SБ -X @Cs&fzdƑ6FA6K:߂  *-9-%BDnNň¯%4#Er| |ѐ#ErMOIZ48ӔdV29!b fRCPtdDYDz,`#S\6{1cV$`mw l2gyN4=  Hi" |6qz"`JfƢljPMB7*Pr?,8"';ա#rύ+ i4N W´~J墇Z77C?B7bt!#8R5Dr-}쬎ES!BvidQr7IM D1()G(V#`ȖY/[ipj(txz)-[0~ClEٙ`vTb z"[s߁g?'ۡ ibOXs11S#rQHz<=ƣN $$"Lt#< xC7O%YL}"Lj&Y"Ƚ9wEI1ɪ#N )*F|*=x$~ b0q= ņbsRK'xˑer!8j:U jtB2~450VY3WhFL$oJ12OHB21qH$kC":{F DUnD8LyMR6zBI$YYޤW#hC[T<1#B)Kq II_"&;p0+f0AXǥ4ؑv 팉"mеzI@ӏ*Y< Y7C Neމ*WIx6[+pGzH:.c?t`XLk"Wǒ%ѐ(+H68ݙ?dzT3A ^Vܑ*>o .#O(|~ǔi?5bH׊[Q,xcܳar'aF"fTa4'QBCIo*+!$vcP7 )s{~ۘ1"Y6bYVǸ,FĽcN-b&6;|ܱvcq97Q?X9\l=7^Ȇ6l<0'xO2I}D_c%N:*aptGGV6'0}`w6kqR< HGJ}ĭ1!O@vqt鸁9^ß,k{$H\ޚĐ)Qe/6pb#l+*vNٛMwB-(ZwmbWLF(mŽH$#{ŽH/b>I:% "-L?1"kwШ1hwkjAU%6''$2Ɔݰz1-TS8d8@EhZ %± ItS( FBb$r& "I K#~DǺA!jI$Eв&>4?'G&h $LKfɕFQ)Pe*W.-#7Ix;`L%7"#؛ yr;;0EtNx6`'?M_tNho+M5P,#䍪^Oc,IRnb\ MNu_a͹ka* Tĉ1ߢa'89Ѓ&)ll,p<xP S&[)r@$6 CQ!VFBXW} T! [1&ZR;:L .{[lBUCc B #F4P{"v '"GZ$ͷ>}9x. M 1OI&͈⏵? jDCKw7R/ܱhB_@C҆М Fd7{''< m(sK zA.>4p2aʘ`~49Z,6(iDZ{RP2GfcecnOR]""a%qY?5ը=\RZ(A4^r3KR;"{B@f f-dFVODX TJirFJE6a4=:fǑq&iUA~M)hwȿt̓yvO I6VmCؗoc,#,hS D_<?YSZ\:{^lEPEt5&THNdB5ٶY@P 727[O; dI?6):4a"aRb~F[دUWi 6'(!7R9.Z=n$$ v7<$F+qmt!v85"yy$8[D129vEÝ:@ǒaC -̗*[DN).'ge_h\ce T9蠃I= >Iȇ6!i&t(BynaF n(,EȑVFUq PFf-EЂA󅑐Rv#խjI:61LpN9:(0r즒'lkJ$KNB1CTDCd " ICtGcQg@mbQ*3 <z?14\+e%,>w-I-R)_ <@ρ9F:"˻[1OۄPC%S|.4vPnī_옇xƟ=5B2<`<~r&\ +%43KN MںFG 5 n\$vӂ/)HJ߱fS3WȘrL\{B<W7."ۨ7J4^#Qf$)_D;i?!2Ec5DrfGO+hcOkr<.)WƕФ¤E#ɞF$}M_ҢtJ.b\6(BSJ҄542Qg3\9ьe]qL\x-T3$p5dD(S( Dұ&B7CdJ%ؒNV'+(mhzQѱdPDi$OLd5H9$6D̔ϰz5B[39I0 A"=X@]OB~vz1sy+G$~E820^ I"<#%"8)Ml@dy<12}x$cJkuЇ'OA{)iP&HjOR"J( $qm4Y2Ulj${ d"qL͓H MmA#JR#o <fKb$Y:Q[P~~3w#CcV rڐ̂hg 1XXm43>HArTq"{ʒL"ۉC$r ߺ[Q"UD|J\Xٹ">PsHX(I5cԍp~[&+󢴥5iLKT<En:k%.y;ISnY4Bo U9aH{IK, dKX9lk> oMo#Js#Xd0fbi(-12VJ"tI$`baQ$":(A G'"6[=63\1c PRzHHW y Ai (Z'4,:#(ŹG&1sGNp6wRvGfXmNXZ %c4!M8KllcV^&f?r?-4)XhL& HyZĵjHF)I##9u#4=\rFϔ791] SRJS+[x3(?@$qm,<Cm1R(d.B2eM,Nc[-UΜOKK(Ȅ,1PA3̿apփXAdey?V/fT ΂5G"wjL!<Fv5L-_ɑƱЋš$7ؚ݌B:Oa1ɮw&[=rYDkf$Y3 GJ:Ĝ?켆dVb;"ђEؒA>̬?c%#Nf(CpKmy&HFY4[)G' r=71F{nY\G#k_q!rOR˦#ԠbĘp)!#rE ~E'amԫilUv:2g03mرCGNIԹdbBh%QQ#%gq! {UP_ Q،ĆN\DB bik#TW&/PG7[ 7%TϟIr, 9PROKC6| w./ӯ44c5iH䖢CxYc>aFRN~~ Ҷѿb/= c^MR[z~SI&. 7faSjɥUrV#hlմMIXEjo'@c#EEEoo>#ېhiE}Nۗ*|K i wr}l6$gJ0=CLhsww.> [` YrN HVS}[>Q2 bqgc|~I$JmJ= <F~'FDn~='c\5 s1"=%S9nY/܆35lJRDbB˾$j͎Ay`vt'$F8Fcq[Q4{ qJ6.;2`xZ9$t?CPt;m  ve"/a&6g`P5l)*iȁIpe쿡R?WΈ%Ўdp*Ƶo/Ri*؉jQLJ4e|Q6x"cд91@Zv=E$eo5s) ZųpXHA]Q،D"ȶi$0J $Lr' Lc8 HKԂpEF[1Hf)h$4EEB`A 1Dx\zֱ Db\l5Ot1nNǰ#\U`wGsY?$bF 켦1_rI,`mSZcÍ$$< Zaɸ)iU,O/jf K= X!=܂ 1E#K;F PɖI:`h!n1_Bp2lJ5nI!ɷٖ? CܰJo(b\Jȣmf|\Nfw*n5Od!Q'tɕ7XR೔2G>D>sؤ N"K QiY9 ; L7bIѲ(eS{I+hG$?8>NLk)HaƓiD^m#pI%qvTGEl,n4+R<X{COągE&l܃D3"l'3*NH +d5 XӳL)0/+nd$NN&6CnmA ґ?teNuÜ;R*_!D?Ȃ#Þj7f7[ȔQSCKg!IȥOka5jzUrNYnh,&&68Sn&O" iQ)VBSc$ftI5nVc{:0!'BjΔ Q Sza/$7PS67oFa'c,h> z1 w(}}y}'q &KZ% $C FRҒOi/kKf7>䃉)v2ydb)v-Fn+hE}=1plg 50!/q3YȜOh/h^`F6V|4Lj?Ό)%fz^[ VtX̮ ^A *y0"˰Z*ݡ^hSg.L{*S B T61P#r|."M^FPɣz k!jj Wwu 6Xu@nܕ=c(BRUב,Z j2RȥdK&fgБNqh|nD˲5$[Q<Iw kDr0$od1]4g\ )mGB%bJ%e ~dlB)17i^ '02 yd3ȨWDb7ؕ)$d~I6Ǒ)n fІYBɟ34fRb&Ydaf9$nu&$R¡lU }2s'rKܴU;%/d?8)WM&9p)^ROy i- ,nKK%JOQÔ'AM{CB-I2y!~Hrh[wF=(r;K"^nkh6 ĞFD/ݐl.-=F"Ji4C^+> {al߰5kF)ItV '-`~ef mQ!hJ1J$9 4qjhMN#t DR#;IHQIyC!4X%fJF eilA!4d("NFNH""dA0FHz0_"oE:owhWĝ#YGy9JBvG:(C&r}cOٙAC`ٹl߾y#ƪY؆m|ġ/An "n7)2qsƓ<8:v0tS,Ȕ42q5LPu$sy2Sa8 IKPAt%}qH`$͎k7s0FN2dM׸Y~X(U$ \XߵZF <6TB 9·#jkK%h;x?k_AJ .و}mI ԄT"ԌԬ{x v\#(iT>E2S r>$ ^<6mWXaABvfbd@ԏPیx$t~{4˘mq`sD :ډRo(u,g`N_xywO̸W{4 R/ڕG[<4Bdkqx#Ф䶶I`)B(I !zZdHȗ#4W=c vQYٵJƉ A!>Ivz ANGQ7pfs33 L,ʄ>cX&I9+B lB03%OBYN2풗9[XLq R_4 RFA<"U$,b` EC `'͌#HFN*/e,T=mQ0WDbC1R3̋Tu9c b=[z'($df/~Z%?5!TJ_ߎjBsȠgL\&AlE?/C3s9G3y'_ȃϒ_%"#)I-DFdɴ5xy, F2IΎS?t1;\RUq`ދ$j4 V yˬ+SRldkXU&p#! %$qhx[30,DڟGc/"|D6V {SDݼ"XܯUSdd-&h-! Zt!`;`st\ )1_8lyO ;4.`@8#LР? SI/@mwō{=$T(ZU$F&B2Sh4RI4$ݝo-p x ?oNf/wD_,$`lhe&K"tv/K%c I'jk眥_m%Oi^OK_?8iWCH:@Y6!:و$[$Z% X ,2 MHX"D(D}BsDHHT`Y!KV M ej6?ID#mb!$1)c-z?K(n'-@75Y#1ASЄ 'H' dhZFti xD;DiE0%J#"B b`BJ:@lu"J#HWp$MYաD '1:C<D8#p4c^pclJ"5#C(Qa9! DpG"WBR9H"5"@%#V!$H"@J HC1"E Q$H#D@ Z0-$P$ @׌=!HzEJ0T$hR2K!+y 1[KqEjHb@Ս jc[aFdSFJ<$JQDt4_FoFDYэK,\c<=[Nt&!7RdBRɌ./g)`4&0:2cc$cYe"lTrܯbdn=ϓf?#^ϓ~'PB>NVb! %p rAnW(Ld0Џ;&%rChhC{+I(B'Y%MƒYZJT)ZrJ%rIE̵ƲBr;+cH?2}P'o{Gi_1#Ώ>}.KDžfr-$??c~t٧t",ٲ C>5Դ}PlнR?A~_Ѕ3~B|j 4KўM9!vZB@mࡹ0͏1wwGHuQS`#Efe7ȱ?ohepʜJzcs~WA.??+GtUBDXih Kd$b'$HkD˒MIK66剽^n0w+S$1KeA:!j@`jA8@@y:[ sB D!eh"[4>K$(ka$F<: A*0҄OLimk?ZM])*hzn#`@D"pə3$`CJ[CH=~G^Zƛ==<n}LM^/remA$ۏ𯘹3$_ 8${(țrɨˑ1O$o69M'yVj!T.*gDy$ìT>"|]mdV]0m hGe kir'˳LljIlsZd16t )ˆbBǵ1h'|+ X0a#T O:cܒr ةN%΍5<ۉw$Rz((K3̏|&l,u qMf8շ~KJB1`fQFo4#{ئL^աqoo'} M~a9Aߒ_R'IQI7t!j9;OYBe MiF҅sd(xόpI+gvmxD-\~IFD)DV!DCGQlL?Թo6\Zft$#8M]SF3ᄠBHȣ>Z ~(Q%Ha0'r㰕s}^A?r~~VK\ѿ'}'w5O3װVp,Țމ~PE7]ll,b0+??[ _'o 'c|AeCU${?</SHIs*VڹGKy| LD 8,PJ#cn/mFЩE9=o[["XLny L_Ifʵk 51ſa/jFf%q|{&l0j"KAW_`̒ۖU {%qMBZ`%Q V|9ʉد`HWD%(R>ڰ{.BugY0ּS,Ll_f w7~c@d "l*,SFe"'WOX{E; ƚD{4%z Bu;EK(N38 R= E DaYo!hlB9kBЂ9Cz=$aƂVNkYm,=akX%OhK Ô>Ɏ&#s-%tD`#vHԵPS^qC)КیvNȱ]?CaLg-㑞{hDI%tL儨n&bF)3Δq_n sowtK&Icc}049_Ύ U, $ip6Od6Kd'Y#K_0(fk؍r9KrYB3 D̥HI6DdpԦDz#x&XԐMD/rhR@؁L)#x%ȣmٛ$zXi gw#pV烔`SChBMl$ퟝFFq 8Q΅Zo8(b‘).Qc\2rգpo?&3q".O:$7f R)`%m9AF=pw8|d#s=-,<%.^4%q}5̔0iL% KQ b(D'TiP1;Qd"qfXdCˡodCǘ6؋6A`P2#Wȉ*XjrԎ 2KQFr%ș$ȝ 5HXx|h-[)IS`kLh6VDboJ \,3e,zPHĴX`FFЬxa!^FD55O"[ 1 Dط0?Ι1g~.Ҿ$KyC( 2no1xd$*̓$ejSIA% *#:&&^Qcșl`j):,S[ "#};73;%)h|b$drE(U3ɝfPiL벋xmO"Sa&-Tޔ RNKQHc!5o|N4p"Z@%Lx KH% OI$C r+LdfF,hU#TrƱUL.tQ;1v#:Zǒ,A32[gi}1!M^F#/ҳ'IkJhm.G i WǾdo9(]`7!@24Y6lyhXЗv1Ѹ{ܲPBx7F4o'G/< k(S MDta2WV1#/&Б(v@ тܑOt Hv 2{J;vHI#AD1ؚE!?6~K65r31}ጓI#%!P(sôyRo8KcI gtN^ ldjbh&nŘ60~ gTste5n?5^ 9OCM tOm=PiɺflBi1gf?`qܠTH\Bʼn2Ak:&u4%-5!jGfcKVdD$]P9'WDzH%&Ȳʵ2)$DZ*ϵBYX'DCO!`, $ qi ,WCܚ\E Ȥ-JIi{ fyWgK[{crRwB2'gh D=j C6 5yرB!E!) 6KM@pRVP5"4r@3qk5* mľd[Ds{UJcQ"&JKchj16$[q3ӢaVB[Mc݈G]t=`LblZhYtI (sb#-hjВ2/p jgT<"\ %1sCM6{Cddd+Hċ?®M5#`!,HXm@hKt4Hm i&n]B\|Q9EAMH("Aȕ?ԿEwK1#t7#KU:(xQ3TbM c+>6FHΖM {uc8DCoY=]0S+'v;ޟdڗxnv!&؟[yX KBP"ӖZ"@U‘`lFXKЙKFi°4BBy1YE^ћf') *XM &#^Ihu0R ØWaEҥCCKhXՊRX ĄDH7fCR4)6 j$\ʁtSnXƯq-$;?i.ݢ^X4L+(S0rkb- g=T|0ĭ3lRr.Y[0ut%d{_&ĹYY^ >CG0Sr 8LSOf1, C;e <`Tb [|jfPL] $Yue\44aݢeg-EQwʑ{CHiD,ҦodM[LW[h$f0b>g1x2苉-* o$/#6VCriX#<#+($ DW"#V@jHѲdOK a.֍hf H@bЦ~@#T)" &YEh$(b 6#|Zl^0v A%Z8jcCCCB7A7HN(KDE䦝q=#U6\[' ң3B0[dgcZșʭ^_ *DeНS=(00N K'~ƌJ(i%/=TI%~hK ,t,†) Y~GP֑Аnʚ<8~4@O6Z`sbe5+[[dJy6qwSPxy;cOiGX*[9ĜWa{;crtKfӶ|#߳CO܍{B'mK'?mC92HbS^0L ~H|/+,TJTc 20uΖƅ0n"p'"s)> /L U}#,K'ܵ)rȟ$~="1*ch%7p T'tLUθݘ"_|RəuK?8 71ss ;eᗸçYA'#1qO,tu S I^_d 9lI f"Y$< l1)T!  &D bz Ehhg#,^-( z)rZHF ZRJO QRdԆ!  nBFبэF= rQ^Cx hG+.E>NO V<SPM  aI72ΙHqSDv'QdV_1 o}◑ZSDWMo|BW>N)f|IO0]DwX_5Jv(mؠ71f IMHpy_"wm#KB8 0ŻPFB<1D< ȇ"|*#/ا !$BQxѱhB>,ZHD$tbcBH00A41gb&D``KP`WEr:D$c^z)@cıă|)JFޗMM99Wf卉hNkm<،X:;I/a-:Pt9TYSB9ȎL OI2o6)xo.yM{MBbVdȓ8'1:.mjdw I!LK/_Q//- 6ˢ\ t`{oݾlC{I.sHr7v{ ) [xv6? *qTGI_NNooN^,s߁_}4iYtE=!"<=V_)=ـd:x[Cc;j_c%$/L脉h$F${ K/HJ|@5Κy ^,ɒtFB$Ac΅DD %*l-mi=M+ebE/N(L*YLi>vc1!B$8>8OrbW K#,?#&#'%߳?Sj~ >&D6(fxѺ2ks#aĶ5rnEul0Hg Q1tmG1"FL; +$vX;L2D#. ymE<ȴCT),"Wp-! J6GSy>iO@BUdj +jHeY-$2zCб:[p'rw9d'bv$iIGIx_Ɗ@lܳ m3HStE(dD)~@D45H~Nh>68iyHK:q#I Cċ;Dw~`f bbw<`/ a%ˋ܋`cñ)3Ru{Gm,~E+X L#Z?! (4"d>,̉xğ/T?' o} K3*EM/}'yZS`Almxv0.$'i9PDۂ (Q/o3|=d !k{^ Y_mܦrvتw `l. 5mp4/bmUAe OA k=#\>4^@LF"S,Pd}Ǒ! ÒnӱG1 Bk,N"l&7l4aKBn\Ñ@cqǓDWR\hc}KcAh݋t܀{: hO `,mJaL8 dHΐ=F9'$tMٰѹXQObnMs25Y(l&5j2L=yac/F#~+C HSNvЋGsrZeOGbEQr͓FCvpO ^6 kCPƙ02v[BfGFP5KLHjFHdhDYԹ)ydsؑka;?H}ȎƠc2{UU7kLD,Ng;Gd/an">CWF%J,O9&o#r}((n1$C'T9m ${`.Y>ICH<6a*J uC] ga w'"݉rxCbr}}GLCGBƗͶ[mX٨{|~l1IA6!"v,2,jc?0!$SI&|As=N2Az*z} 7'}O<)/UM' LxCIx S*ȑ$dwz0{.NzcĂ5Lz6ݏD8s",b"_&"0`ZOd]-^,Or9_!$tF#нx BborF$DF:Ӓ()C0AN,CP:Gy̗Y3pUCQ/(,8MC&ԅ$)vmSИ%tSʂ[(7BLKYSۢi+sأ[ҷRLY)rY>C} e1gşGzزV5Dn}pA%?YN 6;X5ItGB Zj%ؙ/& 4.xjh}#Y$OR9yNim$:?aJǴnmƎCV8 (1*47 &)dD'D45>CذHJCRjo49a 4C"~Kr1^eIodҿzykXG熖*L%Jσ4T^.ˬʑ ٮȎ]@/?rPN%r[/bu'(x_ $['\ K# oJJEqiBVG'.9#1>`q[OC[[:+ Nb&dRKD&H(ed]ґ>B$37RFy}bRnIQX<# Q (BpO(K-궜|h66]{4pwJ2d)?V8꺡?7BIMtY]fGDH?L꾒ˑhHh6NX+Lh"!-$x)|:<Qqȥ4#H5- A  cdZc?& je`'+c^6Y&{i nT{FA; #GhjLdCg6LH3/&]us%ZӁDyV7EtPo#RYc=v@va SEc6{FHe$rL&6`H?jv t8:ze-͍LUųGgǢ{E.?!%+LE F/ߣ(Eؑx(qm"E"i$b=Pv`|y5DtQe#s S1Cn~ä~?]&8Ne8`Iű^2"Te+Ѱ%D'͔TD[{Q,ЁS_T(cͮD9K%8VF_q\QY ۯ '~wu6FF> s1䧒،')q!䑺Z 6^ĵk)tȖk'2t$# yc ݱ旂rH>`,ht%J,DHpl?BG1N')vNC(xBL>wM$C!A̒ %>z7P_cc1+sI lK 4 [xヷ5!蘳'B֍0ecz̍d&w5龦R=lh ,XА %֖F cо!b 4m)zpG`DaTʂ Tt?IIKL#F5H '6Ȝc*EOd&[^9j'NjڼLbYؚIl3wG! x7G]<#RA!$Qb n{n2[Y5/+cʁ HLs>F&?iB8ΛK#JۊvcRG5#&tLMoLH$s XtH4%Df$gj}˜Ư3&{.Hb2O"pcUlngL ,DH%|-2n){\OP>//LdZXFW) * \y$"^_"ǂ'AwB%vI"q$pLݔʫ2<( dQMY4؉z7@d'4&]!2Fñ(|{79dZG4ov/$sSHNOrF=y,|Lk)C%0H J 1d2nif0gjP69| mitpCeDb̅虀L4W"N=m&$B Ց"@ю%&F" z1GP߂L6*: ,!PSM9h3'ldc#`aoؑoH\ #2 {7 :lu K^QhSU6l'n,JF-G1q"fV'MrtjKU0/5ɀ$2ސQQ<4]r7S>BbI WoXlx!D}YRaNە\=>6l'aO'|"v/(G, /bAZ^!JʼnR]DЕRgGA%N0W ׼>VfY<?MFCrf.%1EG8Vyd7C8-Q.ۙv7rœ.`r4 X(b'fCq?s!D!g4HW< 3"Itļ|K79lf~R͏0ҕE\8颮^ܐBOrq.nyO$İǫǦ0tDԳ+Bcm޻zcX4iv0 &{E| zA v#1XA!j ls!: Sj ٍdTƸu$l_́h//[̆R6_W &)/g&goa8n!Ti&d+ |L@4MK FsГh~A$6P͵jЛ+spiĥ8Dl/h*BL2GnB&O"~ѮstQOlmjϘ?'   b6cc/K}[k˂kx M.P;)/&ŒE$mz`1$ʼnnKpɸѴ$X vo"1ByW!E UG @k= 0Us#LG2&BP.R¤1 "~&LADȪT$E 8֠@ԉ\8J4vB7 xx:aCq *CtCX2JGjE{lu[le!ov`#IC)8Bh!5j]:GAD @qD["6!<< HM 8#"Gt@^!թ%6[D# 8# /(jHCh{fDF s%pCفUiCqqגjq5 5Ȧ<$'z$*e>#v8n$D"E>6<&tK:oWMn4ǧ(W))9.n&, QŸ:_#cv" ѿ<<}F`9!zOtΙ$%i$1cWt)i!:2Bt5P +};'>ZoI$uPDQ(QG+\4t4:#'ad104/es$9BJi}(kp58T?iӑ S^OnS_#OЗ/M2VF\P?ɍ+؋VD֧?ĝdf3.(Pa Ag|Om'"PHO .4^'$Qg)v!%w?x[s c5hS'W_ fzcnE2&z֏E%} B D AD7:L(z4R-v'1(H\e 5MH+N/(M"}--2Hxx{#?rZGR#;?i|o&lU"!8K4RK%*95 ryG`m~NM+aZ{&9K |)zhUj<dlr7+Ju'LJ/!(Hq|' ߻0&B)ځKJ~~gm-^ GY-0}P-/{;MR?؂"Z̧<{KΟj "Fi7}Z4ΠZG9PSH"RI'(Ɉ1󉒽ͮ NNIkHLO#0Y9Ḝʱ=$Z/w f|)&Fi%ALDlj‰^o/o^~ȀT0v3!q1O/ n6B)l,2Wr$^LP%nG>g&'z=3H2@p/?ؑO ZВO)xӭz I"͵+^-6c -I`a&$P-FcVmʢ\@B%)7/m$^,biyYX*VuzؑO9 ҅)DRwa7 ǚ՞<G y2J_c~-XC!IRC ]B=6j)/O ^+xan_d^|-FE%I\6 !y2P Jfm`,ϸ 3^oKOfwVp؋v4N-L*>$D{$k1nӞD!$~%Fq`!_0ZM,I-H ȦPH\C'B r?$/} H[B2'P_Ѵ(Cb ^_-S&fC[ɸꓢGXTgn_<BfN8l/b$1`N# '(GU/zN!Cd=%"e45ז BͩGb_7b!KJh-Ua^Аh=3t$&NۨR[c,&Oc#;d;pkVp9m]{iDJ%g+Ҁi~㙷e,a 7NÍȶ8 X7ёB;!"i!%bR8i{$J(cG!&Hb:Ʌ A~dAjV7Y49K[ 򆩦'jR3v5x)"YʈJ h|^脊B)oj=I0({f‘Q%a':F.WS8Ađ$,~WIs;4- r7D &TU s ܲS*кbl}Lr1Բ"0&iBlv(&ED=!+լNED8B$`! I>zǣ,VbB1WZ!zj3+Emn5 vdًDNe5PZap71a/u8~PTdu ᬓ>d, ڶ rjqdyCKgâȟ'G Ah$(T8= ?dƈhcGp|ؼ[iDAj~ܒn<0#c#^ub; g"N4fX1y "6OlJbP#"<\7M* ^ّ+8ꆔOJ+HD 䝰%iAG!ͅKB\ DW͟ΩO* Ƕ-8$b2GPo)L T-h/ ,d/v1)4A艛/r?4x6LG$:"r.]i""mI(Oď*FJș $MiOCU Id$@2@ğB D z$Ee0'U7DCb,'4Y^cքrp9%2^=1j΢! 8!3޹vrD$v)G K,$LhFɚ AHEj`hBvgåhzHG"Ks%?Ȓی,0AcĦBt=ar&<2RȡPfW?{$V#nD0Ã)#(bjx ŏs2\pw#-֑BZ3aؓ'sjQ1n3DOzAR,v.v#bVof~K;#kյ Uؑl $[lg qJ; D2?>=5u.:ٙAeS KȧKb!=g ǍIK"tCRߓ@ń=$A8 F_ף_IęٚQB L&AiؙpF.C o>I_s*tC$ @ÄIS+هbcd[]%$~Fz& F X#GN iDc1Wapkoz=2@P-g ؁6/J= oƽƆdPM4!(CaҖVMԇoC~ǢՈss*[UUI8Y"7 2)#dޘ$gƒSD#3Z[ɞ?m An@&`3*F$3m8}cYAb>y{ RFΎӰ_/r"E<0sb^Gzv}NO%L*45DqQ&U%bnF;lbEaICq|#L n-*\t#B2҄^ <@EH܃ "&OJ~ c,0< 1Gx%.Oy(ϚAHyX8qضP":FOR.~|[q"QxI5cBbw#h !aE4uFW(Y%R2xٟ-&ZEy吅{ކ℧2a’LPpgV(*zHXW_G$ң5ǜB4+ ҴHp3N侦2}hձeg$ai,X%*DKXGǡPA4LBǥV~$U~EcqV00fDdc&F/C0*Ƅ QcP{i~+V.IV͡|1c)}PoUPr,6*epw7=$ڋI'!lDtl7wo&ǰ,O)g'|+#_D.Ƌ3 3p5( {gF/Fˆ <.L!Rƶܲr '4Y Xa,B f,E?'72U݊cۤrJGECLx݉6K0p3Ɇ%~Dt+s% U9ېg&]U*a~BGă rTUnL-ӹ̢2G܌3d{ښp2 =\l |hX%1T#L disrZG%Voh+7D/s>1FǔflCsu ' #@x-lGvD"i'!qE_xAG+I>Gb ؎RQ"v}}.GF^ẔrROD<fE79X"}`#$($jrp>Og}+h:zԢTKBdH z*6$ dAhr\ lv$cC/)dhY|hdo~ţ0)2ntPKKϹu#C0*{Q7PmsHj~C|F_Q؄嫊CWMpN"f )M?M߳1V Hwvuى/э4sc[;"\{F-'Xed~~F@1yNx]y_t31#K[1v#\!%5]o_q69-/#zc/PZYJPettp?$nV]\㡭o1p$u;{N` 2iE+˜aVoY E辳ѡNm6(,`p)dcUUY,!lECDQ[%Ie97BPDlM8r?J/Lc;#S3C xɥ a .9 3&2iIa;6hd$(dǰ>LE?q91b{oC) '!MRnJ.ԡ6.H8fV󋱩ui5m.NOہeGI/2;ɞI8 on՞CH7a6ϒ\"R/r&3R)Y7d 'Tm)e;+ JK!:HQ:aHz1&W2uR "tIO"rF"D?'\9:ѨBG^ 3BAzXƔ@cLz: /ABs$Ɖr`խ9ɗDy%S"KlBd+*L `.f4=+>6Oh/cY_) qM (ɟ%DˏM16{np[b~c\4_wm;Ecj>P3K Fѿ3TF> IݍqԽ q}f!@ʇ&03--sП${BFBe Xd4` $ f DRa 9rv\:JB}#$æPEd|NM[%4HQAFTMr."R4AO+q XcȻ. *+;_mJ\ȤQoG[)TM'gF K?b6Ŀ&2gupKd| _DB~RfG)Wɑ}ڔ\7e0d/8`[mXRCVf>D|eッaxOZP% 䅵4E+U+2č1h]>ح?/$Bk%,ܷ@/dڛR8$YC,bk|;D~͐&P6r2:cnyJC1n'dҼSoty2̘|#-vBT+]*lj5xl-j HҷK> FӴ~dY|+^z`e A)>I^Y!&pNAt 9!>'=X˞кw;3i>Dk`Q:W 7hW~QpDlxitLȔ#+"V G%Z|hd삵޷mdC~ƒ/ ŊC䊸tf$6!1It;RբSm ,Nbơ GŔl!iepŠ笆 mE!>#/?w\=sv9oF2VS [ht}cNg q6|jđı!< }i Mob"co4Sqًϑs mA}bS3о ]6Kj[%Y-}җ#=YpM(Re<ڳ+Y{fY8_qȼ< I)p;BF_ʰp99/qCd8a$!pm5Uy6V+3ܙ063"lJh,pLk; w_3 :Q/~R@fFV@!fX3 b*{ #㕹q$.\rTy##Y|OB>T_nS{ٻf'q.rFd 9DS~#8)rMaǁ[K0/'a5 ghͽ&rC+rp17hލN"/ʄH±)KQa! Yz!Ea(!RоV>ć(Q1 $ Ǥ1A, GǫE۟~!Ј9Zgp C\3"(hfIG?mn4HSBhr BRl RvDYun#nĶpA}&à[(T6Hbٳ}$hcGo1j %+O&CE1%#I) qӁ~F-1Sϱ~Ħ?aFel}kwD#08[R,r:F'es+ _(NvSk4nܵ+-e4Ntȍ{ EVr%eM[vFKɌҁLxj:H wf/b{Ј, ؉*.rG&e:] W$yf&ndj&F\ZPHl] Ty#}Q$-7D}aB\UXxY))>H 訽c(S=cXdL[MalRm06'q! 1 hIR2NDn|% FI$ DC<0KDYhlc -@ tE1l(a )-OmUK/& Kɥ~s 5;IZw0 l|j q9PJrfH^ُ:4DHp>%N_Ɔ"6oavP5hCǓӒcY>HrxI;N'@)QrӸ&Bg~GLёl4E |1ͨ^H> 1+5%)f]56@n&;z\!b4k|<sZ:"d&6EpÕ>e#^I)4e{ټct_3asd,\ěټ֢dP!2~BgcCS`P΍hL3C ]/8q~Ca?wc!1WV*aheeZ?7rC"&GS0J.F=Kc `5{l)nw?aNp}+q*R'- q#8e?%ɱ'(4U{ 7S]4R#TnV&0'sQ;gǃ: P%_pĞdz%Jhl49my?S2!tHC09(BR\md p~7-2b"C&w=i > Nآ#݄  1%(n$_|9"?)  .ƾrr_kJ&}£}Rck"Yܐ(Z ƈA+qp`u $Ć, KQ2%Ö,mDH[ 1wnG$/DgƈfD!L<@億Gi:7ҵcd`[OEJ_GՀ)`*3|%Z}m #:θ6lOJlB0~T}4qg4=mG*fpgK4!jbcJ8 B%;; Z(1sa(12:BvGM!㹟J4&N gDl&gC".f4Shj a. '%i2g7*B\ 6CD.S*̰[7Agx䐝p%C' 9i hPmA +q$'Zܺ%baǰAn#aaS e `rY!)l?slvXC4$'" yߒ3"ۑG  Oj x1dH]n16dgFi_͐$rR~G-%FhdyZEbisb#șq@y^∀_$K\y`Ȑ\sFdӁtMn%rO"X\I22y2dQ6(b>aV"]0h 8cCtIMwhlyC!RMp%9҉zV3JpcB,'Xp\: {GpK &?B$ <lbS>-Vz$A+nJ^I R#3Blۭ h akD0hFIK1FLgT7PG @K2k"I:;?C·Ԉz`Ϧ5zbDе,*!$^i%)M ;8 Bwqn ^Gt+& rSASG {7W9:K$I2LW2ܶ>Уƨx| &%8h6!wRAn_4;(:5@RNa9_fI9|'h3>BH| ᴎDp 11? ICW^xM& &"C$B61In)q:)4/e; `#JNBCcPuHj^[5R@ߢԧ[OerF~⿲GȞab}rw8~,AB-YG {40avŭRHlIcben~ ?-CVDКݕ'n> e Cv9x#!>Z? *2;^Dd;N̩~}DQARbf^YŒ4L=qL'E /a 8 K7S <Ҽ2B~X R" "Qu&Nm_УWBy2<+Ȼ~G CGMtXW@ %$Akt}dP*L[}0፯ߐ(s<hnX2)( 2 . qYOrwEr| ??Կ_Qx 7- KBHnE 9>CB['?A&eQ[}.s [{}G0HAYDModfgT!RBՌ% Aȟ xI$WؤLInġ"0bb$j$Pj7$z#bt=>!+e%% Vp"xV5Db #&؊ODѩ&tHLk-Ƣ' vݔ$ XYZkYSHi-58 Hѩ$xCoĪy}?80:V_eKS]4L\}7z1 I[ c^T+`irbPE4LY$L:AAA@ֆl'#v'9F-XBGqeB 8ŠMhяF1Mch\A=74Ωz71&xƦOo2|D?HdXJU=6pZ[VZq H{_oOiHyx'rm_sM|@îOҿٯ+ kЬ n\ `oW'Tפ%cbd#eq\Ի,^視+&UȂ- kbASKB"EQ¹};o LML{@89](=6TK?O2L!n"JmCĂRaOhE>FNDž8}zG BT{|7`sK&Rn<('6N NeW^ e'IN5_?F#/Hs|˖]Cu5`L_hFS'_>esmY&?D|iXՃ? "W=Lh`?!9`6d x"R *,F6_Yl:_ȬOmm$i+I&*VKc2e"0=7Cf6\uDf k$}6LdRiБK#,u(ɮ\Sїof$L[VxLh!/#\>P~F [XcYY~4ĕn$&~Qx%&Y>|# .32n[GISyo(Ean\B6N"AaKo_J/ʉ6I4$wQ{Eb5C5@@;R_$J]I^kkW&eΆ Ivy?<3kzzR@ޅe-ĝG #{<:~ A[<8+5L&[8H1 o$}_IxqW]=2~5Ǫfa{{!.{%6Rlxid&Ԫsi?#2{1 N?c,w){x '{$n.h\{mtW%] 6E+Q] QQP)+4C'EA h"EM#3BHabϑ)zǼ5&G/h/~+DV!fB[2<= <芉0HC1T9m˓&NG#Fe _D 뾯]1E,q)2yĤŸuŕ90m,KD\%.IrO;γˡpޗn],߱gs;]vH?& BW}lu'v%CV"\;w h"ݝ|1Mg}7pWXh\.pewN;Gpa6`h Fai9=R9˲I" 'g$q3| 0<d_ugH-O,m8h̒TY*;[ = m_ѱ='cn{t[qbh!K;PMv@sqrG)ޔBZLқrde]·׻0 O#:rwZP.|s܏2[>C<88.jf@/ K& 1] mle }l쌲gW"vK8%!'ȥ$Lښ#r>J_3$r$PcˣjF#} s#ɒeM"9hxPd9,$DkB4zAAA 1uxelO liSsIVC0ԕ т}fTrSQK}6$oDgId?1uH)DFdD OnIh'(G'F FFC?jRCpGcSǠzwOEGJa~A_haA0Q[䔼Y Dr=?@:0|Nd{؅eޛ1Y&u{.Dc0GB=K+t~Ab i, %mρ(B]R'Ȟ᠍Krc] x({Pԏx-Z&6)b@{^?-=m{~FĖ}P&~a >N"6~ȯyk$Q%`Έ$p*4Zp<]^h#AAȏ@L eQ>v ؍]<ܓ!aJ$ Z/V#Z4F#D@3Z DcǡA5^<Mz1lRFO(<`EBz_M8$L)4ȱ8֯Y$SUeVWdA%Lji9oOhxB$;r\l0گp~GT(6@g8-E&D3m"X=7yx|CIB0/iZT,2%3Z^[vQKâ÷S]hDJTr}GZS W+h([t)edtɞ2Q!"ިɣ T MdSܥ1&4 BiДCw?ح.܅RBBrXiς,#*ȵ$DF嘑%;&]Y' !UffD:'fKD< ƹЅ}ڲ a   KmMiċAKѡd@ W?P0k4zaјet3K1^ƭ} tܶ6s)O>>ǵfF$k  BA#"t؍v4#qrZى?ɟѩ!y`&^Ɍ $c4W2~vlbS#X!N)"H(ΊiM! /1 aLd[)̽ QHt%̍ L 8gWMcf UHDdԽL3 g<&#K0`"a~bV9!U dt*:rf*V6d&5'yS潆KؘLȻ'eV̳ a@m !^Q+uuD;4pX)`8+\6(1#8jpȶjdvT*F.>L'>G%^f0}L<q2⟡AC%d[ F?Ov k@kF1~8끶 kF?CIi "Ib\ymw5iǮ4]b.7BvHhfHljMDw=LIn\j~eUH?S`v7AM<' h𐊁/ jԞ)3Tl# R F)#raὈjg/~JObRƗvu:Cy͉UI0F"2_ NY,΅p!P[6"yF`vE$6pAqy |U-'q៤l`5z, I܂FN)G5d'Ĕ-JL)P} XѨNјl,M1IpE/G Ly G;GB8VA/{F:>sjHx91S\^BSXAR%rҽM r)25cQ)pCcIi+T;0D}ňN2!X$JH Ht>(`iLFЄRBL).$4[승ۂMIAC?5C] [uKdqks_~>F/36;ZQ`CL7fDDl$>tvR]o{A)L9%Ɋ؁[ 3t~X#IC#6EqN3l"R|:uZ%'fDT;9 $Ec~ MpEɅ{p41B|^6 tf١MuD1>  QeSg ѐFcXya~DI?"E L!%Cb+ NW_ x/hFIp= 6kLhk_L?oS-HOΌ5,) ̮a:dfYUuPYA%׈!#5=3."EK 05%o`Y1(LJ\ c Jܐ#2~MYmز ry ЯQ#S|] B|lpWdtl4h"Np"N)ds26s(σѳ>&[; ' DC EHiIbQ&<p1؝y'FYc,ydD46GQݖۛI qg:0AG'=i&Xzcǣ<-=`z/\`5'SX3u ldkLF=v2[#WIMm*܁ {}pr &4W f0Akc X$g.u&e*Jb(lhu;!G/G=ΘBhc RB1M5?ccP+9[>])($p07bF#nOXr?XFELxcCv#$WhYoD~pn~-fEɀ1M=[\:*6t&-dS44n_rxgdtD#"(ſ~DŽ?ȎGقlIdr9 =(! APQ(`-|+ɲIH Jׁ'$wЛЩ,/qrɌ8b&6M~ODĠrt;fv="JBw'B1_~wD  ]yюHDek`{q`/n9]㿔Ȁ(bF] X,Cr`{P ڑ;MvF+aA#aDp4I6J8[} No-P%p=1q+r5D: K+v IOa?Opd"'p4y .kE~x Pt"Fxr>MIDJcvQ]-cUqs"&lٍ*,q7EVZأv"Safj'%ye]cbx|RqcH]3RveQ$v0r'>M ÷į#tBw\ ,oi ;(9pB(c;s'T숓݌7$^?p<)"jG4rhGO97۲_|Y)f; ϑ4% 9lвr)p% FIg)&Rwk&IE A0 &( "JCu#tg B)Th- #؊] r?Ȝ#no#\ح}IՋ#ՏV1ja% b>d1 VA.g'GU.rR97 f+<ϫ6#ȣKxܥ="Ɩ'ܜn6{ q6 GƚE j "oh$5&{!>d|}#؊pT[/:|MB^ jL ؎#m1Ƽ;RNb`De"r$EGƅK!'͆M G"|ñ4a#Fx,ı{RqZ0.toE#roԞGI,dcĈj-FJBRڲg~G,("u*=.r364coc>Frr&D|-C6M(f*팱šx<iMt D7a;d۔H)f#'lmh4mȉ[N{ajbY/ƣ N[OxشV-ԾdT47ryg)JCIwT#!m4;oyHLFQ]E͢܊E2)ØRn"e:[BɉD6x +%ZwkvJ}5$&v._tO#PGC4W2?k'^_8>qΒ1B~Fc/V1̔s1dw<ꙁ%L!G6772-*?pXDy1q'+ܐ܃;ZA i<n<#,JmσAdhbk3lF<ʖ5qFcvnq1Fdw'c48(I~FIŮ1@@KH\$\4,>)//?MK F:m <>qP[d<-oŊlS83+$hI`v",!"爽cQ΍qeӚg")|t'Цߤ5UC^dISH~M.ӆgKU{ 3P!(a m(HFx % `-5p6'm'$}ɤܡ?r^ެI,v`%@^Dh9%ji&VDD]TS*x.IW#o#Ѝ%ObcJS{hjYBl!;~7PA,lKtp:p,n{$~ 92 yaG5J|dnYȡDd Вp| Yy'QฒV4b$̿g$9Xi\x!hii FPșD̄s!ef{(ZS2TiŞHrrBdF9pXĮO 6PWS2셧 B`伔"z MHſџ@a&'L7_ояz"A9%KF.~ Q!&a:!${܎b}$v8PN8ёFb$\ ^ ~LǸdOB$ӑD4=q7g --3mmp6%`iPoaj#j` t3!Ǒ7O؈T;4>v`,/7sX2):%ؓ&$IIcDs#b:I.K:d?e<@&R_%ډr#1x8bQ&|{J^= .˜I ou9'0I) դt: Cv\CS=`YD-DſUwes>T3 \- +Ա74Yq%0b! aj,xs,k-$4ɶB4R1Y1?HKVUw#{BA+@  [/"oP??1(}KCKofvxK4"`,qP69*7*:dbw ].?Zg)O= Ҕ Mܤp7#sJ J_nWQ@L}NjN蒦^IãÑJ; 1.ȡm{o&KMY'K JIcŰЅ'G?S$ MNF2W* Iе>ploxЫ1Xr=/dHcXp>ipk`S I2/:O`+_1[avG  K 'J0xĉOgMzU/<]` L2xM"h(՗B2+b7Tȥl4ƻ'#X ܝY8d|^ƿ3C1Dl.6vؒ5ԅzq(#I9 F"dQD1ǮZJ^>Cܙ'FFi:( +>F cEQ'6Bf$ҟvJa vC~?+ndikȟqşJ3>_:ukl}1ʄ˦Qc"_`&92w5c'HG`a{W ;mlb!{l,b@MPկ,3~̹IOh7>aP7n'0IJ; z![X&[xMg'*v"qHݜ(%F[t$(CDL%H\?7?4'*p?\1rv"aw0M{V,3aBmޟeF5+1 'c >Bkq$-HJZ y5| >Q&֟q*j?c+u?qL.rT| afb,v%SCB|՟$,DMiQEa*8NB \9 +(ApVSV&es!mnNĥ%KW@Fұdط5ʟ_Be2}v&UbX,fB?&`)W[&?gW4I?~OV>AEșIAo/%>"g&W\_S K>`gLYOqBk=o 8c_j +<~~3-2&!DXǠĉH:a# S$$Z}655^4Kك.F v5L_d# *,CEr)g+^ uT4%Iԍ@%lAG/d$bȴ$KGhrSb|DHE5H# i&n#GȈD@+U@-1.^ V-Vu-$k-&BI"Q \98Z؃FN+`*BN &`" uzq$\RE ,q8" شqa"#`иJ0!#+2RA@A:ptBQD31c8#Nd΂<#lG<t"#H DAdHK x $܁ "@pt5Љ XJ8Ѕ__tܖJe@cEKi9Y" , %>Q1(x"(hgtȨ k?.M! ࡲ%%(BBGaC)Q (EhUpQƐ($`EJ !Z`2F8QzTR$F4 B,C)%B"H3N+X B H ̃v#HKYB^GDD @DCRjA hgH  4=PAdD^7z4A A25FzGvЅ辶gF<e6ZbT~/1*ɗ7^R1-RAօ,T`x'LHa$ڷu)$/)p˩bOzO&yq<t6iw;/Nr Fm>X`3lphĤa/Dab'!؏w.hEe!WI|i܉K6P:m2$tGR.FjM<1k4[g\Ȗls@,_aI-pU—ɏqff`S-[ǣoc.D[pdXE.8:@s'eɬس*^ХJN $[ Xke#oDV[/C)seGJ֚ěB}ʁN*Kʃ15)!&I!ŏe>bApbRiVYiб1,r6_OgՋ~Fm/cXW2qMrNv'%%"|2ofPw*bya-*dO1Qq?$^n<ܒ!7'pNI7Oc"dΖњ,XI&F˲IKK%_v2['3|ñ-$;!mwm̎.Y-#'n~c'K&B6۔'| {{YsoqrdDQR^DCX]vr\-= ~Fd9md"<+"ț6My[Ze"iϡ.*M4^=) xahb̥y *cAmhAzZP@1z^-""LFA(eX$VcLZ@<\AކO8'E3b07$%iy!)>) 9&I$J6&2%gh䅶4I,"2!;"Η C;ا8JOm(dI$cGD jһ޿"&4%n1NwfK݌2CvKFMpHgL='ؒD$er`RY=/B#{i&`ؓډl#oMn:$} I&Ed'D>,_$&We!B7JF!w]22B5#[XŽ I>Am*iG$_"VɊ)oKtb9iS=v#':"' 5/lF(؆T|"aFBurŚؗX.J3U<4MFaI$O%F$!*Ș&-,-vF1z$!!*4hnvrAD/DεfZ``#pm6VS-'"t$G3o!@2l5pDg/m?MbLFMdlkw^^dr#L"fļ"c毣y:J - AoFЋ}do!3T`º&)_5K.͡ B f$4HOc2EX:#5!pE ɸ%-D$ޒ"؟x&:&NM2I;oD>.6i2#&-=*r L.\䐂NM 2s%‚_gũIgXRFC̋raKiGYun҇ё&TD hZ+(=>?#AA P`4N qǰ # A%%$i0ɬM8>#(r+H'M5dF; hZ4$4FqA }sI%bZOG$p;{>L5؈R9ET_Nuyٞ}Ƙ1H&]*y' +nZY#9͋I2bQdI)1Ƌ.嘴#{ "GO` ɅXn+$\hAJo_ J{C2q'I7ϡI;I# :1Zdܒ+BpB^4RS&4:%qDHH^NXIՏ>1VF$N+cQUCKl:lI&=00< _%ʉA$Cѹ̔&ԊlQ%龹 ǁ1RQv<|RE|$g$JX< ֍Aik`t1qҪsXl&Ho&灏 Gm~"crjYm[ 9n@41xkX9r2UFI &h"H+Hқ }L1xOmΉVZB$L ̏ !solf+\b؇.x&Ѕw.l0#hB ias/2d\C"ǖ춌M1*`7%ACyjX{#֦ Ԓa`R]"w_iVoǂuc8Y/2^x֐mydn=ȑry LnA>CgI7#d|=2ʎl'6At2LnF#RGHJ뱨/Z,vc'{پ#MJ[YpǞLOҶDV=1 ~z$6I2I$ TgDl^AR&H'"G2)3,$y2 (BhVA7fIX1bf#I CPf `ϧFN?ۑc<N$ݍBpй>CwSg73v)ضcIF+62AI1k(?v< G9+H=`Čl+TJ^[&`PPZ$d/Є-4[=1 2pEhvAE d`#ՇQ>CF4`L*7DdhBDi} }O$Q~ >Ʊ5i%*g1r!"#,IKReӺY$ܖ$KQ'>JYĚ" ~ kp>P_vcɆtlP쟹Ԟ6OzIL{ i.o[3Jټr{z!NGsieAގoJ|B1WX$"-8y~ EAlIп/iQ"b1#v"thq;T"N(WR{v!d7kYfR!T@؈SE׀ԕD]AlވPd csktZEiNAI10KfE~DH)8kC3!'Zΐ@7"/f#fΙchѦ<F'F" C7х(I d=P*$1}?919 66E j${AD [kcȫHT[ƈ`+$CW[42YI'SghR|< ~$i%%&w'aLE/bT)СL`dr\D93X,[[ [,>Z"M2X8I-¨šA qp)dI_ĐroLYN <6U˲$y/j/bd>LGbᓿ؟Igitq2Oх䔛B'b'*#aWCE͋4~hțIswlrpgbpI2 /șrȠ% O"j%3 ]C N'RC6&&̣C( Cw!_x7J~Ӎ\@m$+$I[hx <3%^AoD6"mL*K$J5Bd!] nB-'b'G a )b^2bP1`I %t.|RvY`ӂz$71@Ys"dfEV5] n!2-x=Zq>9-"m gc.lLxFňVY+EѡH0Ɲx0-̲61E3~,nˑ'~%o`Ð  rrm$$ Bv7  ȔdqBB#ql3Ѵd{,jΨ:n;kF/1Il.dNJ%}(J^@ra$[hY8f}Ą(Bs$-`' X 7!9r0L,&$puB}_/r%_rscO [*iK&pIO'A*~D)Pշ I>bH-r*+)1͗˥Չ&v*N @lK~C.$rD3,`V7r-z.(G4aDǻ΁| .k)"Ko=ZM*+F&+j}ldڰފݠʤMhK"  Dȳ5iaјG$+r;*7\7IdRx}ڬLFW|&6gޞ$Lȓ,@ܚf$ &餒̐PN H;QHx'1#,IFSث 3"nğYK5 +I 琮IvlSDİ&{ ͒%2 DjIٻ;ȉp,$4ޒG7wFc9HD#Qa c `Q7:DbA"ȏM-]]@Y</:V V+yԓt^PE;O35 2:.E'j:"͑mC+EIN]:bK 0#Q# l&k_e᭎{%=n&jQ|̬dCn2b/n~$6Lt<ZȩPB7¼;P$f  Ofa؅C'e ByKIChF+6,-]W=pWxOGڪ OBٺHq]Y[YF KaL+8xlWM1D?8jDNs$7U"~Ȕey!D׻6[E]rj>I6;[N:ܢ>d6Su$0E{-un6^G&7L\hӐ׹ʢ$feJ{ֱ<`y@mJ-Jh_/SP/bK $zTODgm ey%kJ2% .0&dcaggpwoTܷ?:IAXe<)/81,KNdzMG,I<(1ϰ=Ab7(2LBBl5 JyCBt@%#sj''?pOk&Gh-a8ICGV"nd#U:Ylg3kª#·ps7 .4#k|a6I*Nǀ>:&t%\^cI͡iY*#Y:q 0;䝴]U٩E{.l-,4I-_t2QpAo3}Z֣QɵieGBk 4Im$o 9OR~'fFu8^  $c"CmȞHNI8*M2D $S# Z\O4y[ȕ*ۑc>d2RĘ7#/&OHmm-щi1N#zI$'Y'ϭ=@8t9LR+"tƙ}lc D$-؁PK}EBΘ#Ѿ]fn<]lW 咡d"DzFё(dn"ˡGݥf%3eWPՈ.'@Wl|%D3| 0?GN${ܑ:te^ $jhP17 ocC#J'DB S^ OH"/Kh]l81ܤ_ ^"#1JΝ cc_ر=+|LAg'/k5?sЧDDKO&:{>y&2(B;ː?JAKT*ilxl(؄Ԑ2"GMPat*"Yɇnmhϓi/yatB|y㱨NIC%ȡFjT qto&PAJka&K0A~〓Y0ԙH}e6?vJpcQ'ƻFN}*cLXwO'RC$Lx܇I4C* kD>틧V"5 q“OًmJVBىȇ aIeZr/ Rr,=W`O4~*b- 5 A_ZK3BةnC3W' Q(j^!C?aY<&in1T,83B!],tR> (#p;="ƄSuL,Wg}%-C$OIǩ@K){)+E*E;:+EY"di$ɨ6ϭgQ<Ýzbk!$n\chR3DdbBXϢ\"48i7l͒I:I7}|me $ǝE*|&N愻f0q5gp+g2P&H"WSz8`a!-#̎&$)/l7yS蹢ےd "A/8\OڑOjш66"hXQ7T6$"M 4#VB_"ع$NNTh4!&{8' ^ BUw&4)B[pE G'OƊ>$'e˶`TIsz1! bu6'D|5 j`ZKxfh\B+iK&`+q%d ?\JOA-W#D#DN`YHMـn<]p!$\,TX:na0El$8C\:Nx+7:!E$}f#/*>] mA?/C6rB|~QNSG`!,ܘ1u2D:ÞeY'h*߱I9k@RQ%ld^IQ'VfVI/NFdBȕm(#[28pLNK*)*Q Fy<#2Zm^Kq7,i%bY4llc7وBfæ.JQ'tԚ)f* 9D]t%䢳Md|r!"NoHHH']e'1LLOEzI$ YF$ "T H 4D$jMp'f$35?KѪECmhhK1$I{Q^f:;uΑd z(zLd=؛BRڄhԱOpgA|(00(Dcd$SI:C]T:y$ۣKS62Ʉ(l&VBKb2 1kQH&=-6`D%u E"86~*|8Ε8:c2zotdAs$Fdma:P:H H= A2 XH7}eezN؛"$RW[c>rz$KnD4F%MXd۝Ó"}b[#NlﲼI6?vٛRIYi/M&aP6D^J'yVMG4'S#\#ׂW!.PMSJ9$4uLZ@ЄDC'I S&H¼̡mu5beDWtq{8.IPHšKm2l#45>B.5g*Cw_;?o.?Ϳ[ 6JBoBkhDy~dCt$"##i-QjU毌9=a<0sHME[0 0HphL~luj',Ŀc??ZtѺ<{PxRQx#ȭ4$u?!h 1)d ?oܕS~$)+q="v`0y\Oy9G~Oҿoy9:l{+H<`*ط fZ0U(HЫ3ՙ|h]'*|Nbco4-&9\Ac=Dd"|#/䏙ew3E"8Iя">F~#)?Ҕ $gzB2y槈GZ҈8De2&JXDxPTR%W/?A:I#+/M{Doߓ{?ee0T}.pB -Oq %&\!?+t~N뒟w_IEmeI Oq t8~/c_W~$ʇ?wE|&D6wbQ CXm VG1`q~I{_ȻEƬaI " Su*rZ lxoD5 z$E2F2lG(ErA`'F"ehifV:=FԌ0Џ!A Dr!,@j@m0 C$8 -F#$$NBE D8# 4 W(P̐8'$x"D`DA @؇]qQ[k(P:4ruHٰ3 ~zi н ϝĩ9ą5llgWy *AHL+HѢ`OH KF(8DƤ7(Sކ# Crt63AlbŒCmDl,,h{dveC^=_=!{kn- J4GAF AFF#DA#D     #H      AAA\H##:S_M1KGDXՑ$ ݨcՌ^"(zrHJƓȈ 0'$$ƹFf$.bNU,JѠDЍ-hUtr1 mLlW1bZ&vћ0$1- tTz#TkAz #H5?AGG}hPFA/ԍ= ՋHO MCY7սuF#F璚 Ж4^bMVb*Id0#=lI}'6'(hgxbQ2i>r@.NSpFX 7B6I B5vMD [;$.]yGZ.@nG.cv}s/Gj;>ZKDv~~~~x~;Q<'3>x ::Q֎u/D$:| ppKb\"\%. pKb\%.\< x pLO|[!%%> `KKK| pHHGJ:Q֎ה:Fè$Y!Pz:5DI$ k(/ ; A 'bK$3$θ 2`9k݊@E{YZk,zxCxM9F@Apadؒ1!"r"FBJ;} ϫtcF` NWϢcM^QꇦH<m^Yő֫:dǢ5#HGa/:?/QNF~ g$ldje,u(Ta A!,,R(.R-#A5: KF@Ќ(5Y JGC,*m'MъL4E5d)bhXUF{@BV앹:JK94!Rݢ\ȏc& J"~{6cU slL"uuۣܒ2Q?MF|n=2Mhܞf411ZX~$poEZFdycL1uGgzϮ}Q/?A׹^Y'&}3Ɂތlot,u>t1?#؈j7},zsDzW'\dw~}77gίL?EP&Ld e[Ѩy KBΌ_F 7DA#םdLK##IRщ/@"XLBkY`z-d6_%+(DoR B $`H6t`?qE:qF8V =6Q}ir,:^ٝV Ťj0N/L=Qҍh΋K\'LzϢdFMȾOFo]4z6dIOevH00$/I\Ep-==  >G= HAGb bDFeRM ƘI'H!gџ>}_E:jbbu]1: D_AboqDunM#ҽ -u_#N$r`f*2B^aW5 CҊZ%\QC R!SխF9W]Ar…DNQDhoIcV/%>{ƒBF46 /Bj r#jDczV Ch܋ Ζ#c}&Hgt$VADo6ʑL 5.EexLz40ɋgܲF~rO'䙇^[Xd4c\-V #s/"&` $#LRF-ʹ DJyP{Z p9︣}sNtncC_#5]L] ,)[l~ׁ$ \Q?On&~JSte{ lh:h%~Rj3Tgݯ YAB\Z#[<"yy 8CD}Ps_ Z*J|1X=73)M'Iѣ'L 'д }~^$$lCg47,4YD^1_R5I'EljcB^ؓƩfމ0<*cab@"'xܩ+܍b}|X?"D /_snM(&э)c]H0G\t$<OGoiVoNGx2"LlaZ=`!TO˟a+[#.&a< I\^*f8JWV[7"RFL$8mt'I@C] ,t)38Yg"vCV9R9!c; J셛RP#!% =mt64Òb@&7`mW u)(I@$_ K %czAIP].TCq$|. ZS"J. Tr!5b#b\!l$ TӁqxM4%&DP})X밴I>Lc6R"$%l?/D&}(#@kH:z@בld^10b4 RE,&KlFlm@2p!$1a$SD1qUŌMi! ĊѶȘ3t47\2`N͉BɍέqSҚ&ɲPܛ I0mђ<:͏C7ђNY;mP&BR9 Ly 2(=x!\ 80ȅD0mRl8("\7L&Z&E< V3v-RKoq7ƾ_"y0C2('ܔȈ[Xp#;X ';#}0o$QafCY-yO${eGV7<䗡@FOя'ZHN:ic\iGēS3c7iɲH! Ԉo@1 Kj2%iu y$7Ւ"(`tfeQ2eZ#DH&9+:-VޗLIBH:iބ-Yp-t)I'dJ5uk(0ʏfEAcJtN+,I+YC& f艳pL8z1pMdɗQɮbI- 6?2R7!Q0"D,Y:{JHb'$sWdJ 0'o%T70$͊~ D!r<Xe*T33BU30,JQy0}`o,xIZKH9DN\G{ <aPE xTGiOKJgIJcq[oaG[ ˗fS7$v]rGp|+mIO:^އLy'L h %38y2LO#D^X2LuM (J,j/bf B=C $z oT!͑-.R;aݤ!6 !շeܴ) @ԍӲ,p%'6OH15dGcT{pB07%䛝f0ȣceqѵFL5/$flDƎ"zdl9H'O#W&hGr_ vcxNFJĸܸjt6Gėqo#\H Vr&҇qIp/+iփ䋬{;蘘ZȂ'eeB)L5=1'1g:0`b{72v.0Imf߂&GY!h=7| f66]oxr&щ2R{ a8cę|2$7F3C} ǩߦ}I֌tdH1tYl#}܈,9< QwvDBO#’b}Sk} 9<#2$I2PRǚȊ!Z?C>4b~F'ya!XؿgCJÔx$ z; Ӝ3-$OI:I"IHЖAQPVmbTl$nF!Gh:ظ )NN$c3"a2.z¬&Gh_<ɞ`sȗD OHYױFǥ}F4"4@K,L"W:/EY:$ u!4J[.8̎z3Єh y+څE Ii* ;0/#fY`tAZVt.Hۊ-h'b2dtɛN bW"7#=3lMx7EKlt(dvD{فdy6"ls&-"y='ɝ% ,lX-MheOZ?/z?C@ӝ06"nSqhrm((nP;Je?aEn/I=r&-[1z7oZ:\KcRAF% v&u@ЂjnXn(z8x.XԢM !cD>rPd=NdI$26OYi'}f;L %D "ߖHv& abv#>}NhK 0s Cɑ>!%#xHr,ZZ.DdIVI/ȃȕ[ 6v#D5h۱`pe 'c GDCXBCECrt[6F~HWA(o'2dFr!T8g|l2HE"W(3%/"U:{-E~NLF` |0L(N\p5QDȩXѵ h1'\3rN}iu% ٔ:3K%A̍p؏'R~I1'#o 7%Fm@xlT bcrLb)bzHŘmzFdzP-I/[аLZ1jLA%~%yƑكq|HēCЃ}"jLdb5>GtrHT5?e sá5 JZ?{"#~'|cs :*z[-AI(Hj4Ym$7I g*5`jRQ<AT[148& N+ Iۢ|8^(v%3㶡K,=(UL(fX(|2b&ƚr:rBmb'8&La܉FJntG"U r&|*P$oA,v:uc$V6)]/1$73fXx$KА%FO?#%x2mo:3mdSv'dj127['DF%&2'}/re}[ j]Ksl9.䥺'N@!22 4^_]&DC QhLh)+-D c!4L(Ɣ%v6|DɒI, 'ױ|hL38+ISV0\߹CGIx'MuؓH:FD'/2dnod7fDX-rђ6d+ы$D y5mH8HJKHC @#QEMRMB#=rgUft]7ttoz<`nܡ(܂.J}#%4 `|BdO؝fF!S3vDy%{3Ycq6$(pYm0dt^ kF@oH O6B$UhЅ}3"'R#T'*G Ș^41QVIsލ ASdBt-'|& Jc}#E 7#"dh Sdi'm2sݍсI!CEpdp6?hȜ1)mjd%:*^8paпcEY#<V]@| ŴJm݊ʬZJY@4Te-Nv%L8vL)gj&=,Sb2I#ن>A,J{sMQdSŋ轌bPa!c"Q"#M`I.adSnP(5+7 ʝ<-8߂.DfǸ lhEL[M}.EB?b[HI!'N~ULuF弊 XIH21In{!aIdT .6N-x(;&4JdIt+n&w"=5_FI e\Iؼi$v>4(n5v(O܂ՌL1I$$άcZpI~=2ʼnr:!$e5ٲ#C5(eg8dס$"A l+4" K1єOXlei$1߂zVdO IBpCn7h΋=# 2neZdչ&4,UG:% h+I\2 L|D ME0,9ȅٗ h!<5%v+}PPxՊ$2@P\]'إcF:GOqɋ J yC}bvFS7*dC6$!RXo8AP(LP݌bǑ M}y&wJ̒a-7t_D#'C+!8Ct\`bcCGbRɀ)le`OeoJarJ( "TS|0M<*#Ǧ&'2=1DD k[W %гK"/aol!5e)#hē.̈"d^| ;)RgOsqW&w|Z-7av5Zm~Kf L/p4D}t1Ғa-!EdاY2왡0IN̗ pl8g;#6Ee%Bf :ڈ}&4zS 1-YV5!+BY ^: w%JnV챤B!yDt! `ġ64(5@!ǥc ei"QC4K/jL&}w"sAy&j [7N`NIv,AF'yI<% aA1d,r}4+pY1?$=ƛ3F$uٱILWfXa$;+:"y(IES})2b 6BYM#cb&GV&H%0gb֍\|i7c4BD"쨈Y12[0gCp+q,p].,m/L-B/'hɱ;D 6 $Km0n 8 Cn)I%snL#Dt`6 D~I7SZΙ8^y3HMc |/cpdkx  #K`@I9<#pir=ĥ欸o= :L?-(܂ArDvnY;0~Gȇ0-`F .b FHTOЂ ^zbcf}y"D8&9|gHٕ##O'B䢳r{3y.:-Y)&EmI(I!aFt.OcE|Q>Ke{E}Ŝhh%xbpR "(NGd $SlZ7!F4"}%QBMg6DˁK'QcP!9SO#ْ]H&ĺ7G)0'KjV$jCn]|sqU1x&^qܼAlo *nȗ; hC2'Xnt!%?dӳ 4D*r[s"8hNfT4%%)=MĨc?7IN{ irٰN nGxQL FFQ JlC۱!F3hZF, "%6G -lI56_&lcH H hg8nE zN?NX'jƵZ_1H翑F`wٵ^7!{T~4&X̓ UU\mjJWqCka1K"I4aZ8-dN `эetEdO&tRiB?#"(#ikt7.c ==dňyh2tlrn/[Y'bMC+ nżX&ı؉E(/X$)E0L&䄞TY k~Ŀ#ͨVEΔً3iڲ=jz$cָgH&=#@vHz@y/=^mdCymHOqّ.A/6q> #`hl}RМ LSք5P$Ɖ(^ /DihAAbI0ՙΏQ"!!/CYz=>bD0'2.+0D9i!F̎Ȝl8"} b(lϓJH| dI-"G zf 2~GLnVn$pF #qB"|t7%rȷK_JdkX9!(.FO.3aQ7O9^~L0[7j3 Q"Ҝq!\n3ec=Ԟ!HDKbd.VIvF`(R& )n@^;Ώ._b] <U&C{C8%%6؉RȎlPd4TbJE\ 0܎2G qTx᜙ cb=oCDE~F&Tlm&m1ǩ~Lod\i&N1IQ3s}2d2Dv% ُY'fG9MCT>#! K&%Q!$!2;dBZFFF Lz%s: 4">zzX- J!znre/D}wc?Dsx򏰰[3Hdcf?}ȁ%4*#$E;B!>K#JRv"r92cc!yؙcH!`xLʬeYF{$i2,Y!M.ʍaȔ"#bѾVnJ'l(Uf IN1КɼVB0蘧Slappݖ7ɔ V܌ Nӽn%;7e)t^K2O d-BwBۂp?#0\K2 I<-*DA9B2CUI+a9lC)4sLG3 X$$b/9a\f"6BĤ'* ܳ(dni`ޗ0L,)c$gҾhI,c.c& 䞋ݘds1K \hD>{:}.uctY67ћXA@ݬM EG&1jh5z$} Kcc%Qߍ!KȜrn;~ n)jfCnķ1&TD&26 y'Dafiqo&)Rˈ0鐓 }O,e1?J6EH;|䉂!X'q)\dr"n}F?:cpxX?2cT ;ؚ(؞엄vpx3>-yX [2%%,GqF.C2#t1.{DqTvSVD:Dd/s" m#Wׁa*%'CCōᑛ6dڣEм3ȂGcP"$'eŔMōFP{ =Nʶ#L3ְFNNNq)$3hhZ2Ⱥf(aI٘G8lHv57wJ"P0-}.f҄ZH'?@jK ~42$KQ- S/J$llc8Y> <7FWdNE%JD@EPBNp'-x I)89af̋,YC~$lĤn< VE.DqDC S(*dDI)Cb}o0S #Hӓ|$Fɋ'Tه" –4Ĭ:d- n%AX(Mc?#{457 ǖ ~ }ېfgnMn&s)F9!BD9[,Jr"%:8wJ,lI[hn19'c+j2Lx0hMC5_c.DB9iF1)SD0 8@bI1c&ڼif4sh܁7.3|Gr">p`v)9}fu;) :[XF27,o(fє<2۲OU[@ DVkDJ*iBI}PWMclz1C?axbh߹XqпdWXE><,r&(pcQ ^dP0Kav+X(c<[Z\F,N q7"M Xn% qFbJ%\N "(S?b`YvNĜ#$H\ :#S&Ra0bR#$ X+>Ѻ:pan*Hhx,wH{\[1K.T_w?c;Y92m.PI}'vpa(VdRIáߒ\$,mOBx ;BA%vre$ӔAsn,8p60CilHI$z7f&í#܉1mH:R+t;:7cs&{HYSY0aNDFL#lG+qOc#bVM5jv)m? ;=oU聡i=Z@ˑ @V8az!ZF1 އ!rFAټZ]2̞ t7De T(j[ #-& ԎRT YS&,r?7o%{%),R+&ɟ'eZlff`W+L8b*3|+"h bɸF9JU7attĜ)BIbtqP5)#Mw,,Hy\تsDc&PKu%,"}\p;ڹaRrj){kca!9HՎd&8BrKQDn!Y 2C̦H^2 sIgBKgwhu39؟U0KT~SqQG'H& 2'8a[,]`;l2~BǦY^$b=;HFnnMl3;š8H!M6F$p%NURȋQ y\!d2pf+F;tC-Bō3yBc/EY'OI=JgW k"/Ɓp-FhkS]>lCgȒ%HN l,掑YcRG#R~ɾp 5\3{I &f3dfOqJ{t{Jlё00W~cB^H7(0oر~L1dn)HJty:hEnTlRV=-CqkBOJSbP̔6M1l3o5CcB!m4s)@ :Hs̋cn)f̺BiBQ4(`0"6INN[:b++a8G$XlDÍ"[ "E!dOE"WBA[T{D `mJVn@2c??HλY'OqiΙVEiNdtO7dFl'C]voh!)(u}퍼#+oxEclĎˑ1tA~9^D(1 ~62zOTyn!}GO#)0/ Jm|, %ܹGXav{q!6Iz*Ne>d+؝cEЧB;| N,OG'm:nLIJ[oyС>LeA^I ,PѴ=7#RPGnb>G2낊䐘y1Wn:3Ksb– acoq!R:#72kvJ e~Tvr)e ;nU%db 2LEIUnbLF3HX(JK {jL1='FI"ΏF:&D_A9qk>:0H1zhxd`ȈFK<Cs`D类̊حNL } w#adFW얈dJfGDꌨgJLEYBv*^ISbM? noh?0n=9;CWe-~,El,I/&Jě73GZj;ع CG!`uyo04iMx3d#An,͸`-\HؓsT:x8D %Lxb{3nHm&3D˲(N Uq4DT1_XE1 1# WAh2=LCQkqʢCbm> hXhPd[CUDF"u7ӝ-dM̽dK6ƞGb= d<Η$zvl-Df jCMB9nmT>7.MD{%)]=^?1RlRIk$J:N?^mHƔ\T5Vdcϣ=cJN^~ ϰ96SIQ߹8'gBw&W$~F%G1F6<`q_Jp'MA$-M} ط6[S;(Bm8j؞P6&Fhؗu:߳{RX_"YfD*V 5{áy%< 7VWSDm/q EF&n(E)6B2[܉]%'jӓ xD̆F*BS7 lXіE[ OHUd\)KMil33.RiOdUB- (rA/aմSŋx!X}hPX XizX6uT<ǢZ1/ LH=ƺXa$w6'=v9E<"$P^G(C6ԿBsUԓQ،Z)ؑJ~t8 PМ83БD/?E,\ qI1.>{D"xВjpL0`7xKm# "ogc 6B(2Yjx!qоDE d~k5ȡ7;C~ুV DCiem|aM< ErH5ٸź?Ed^TȷqFhf$(Ɓ:v)lObQM&'ܙMv:Kv+);dbۭuv<SCcܟ0hjbmhef^슍ɒ7]<`ɺ[+ݏv3d%؉͉,77N][!# 0΅ j_؝ƹ؏:x' L99]Lfڦ1QCW,b-3Ny3mU1rW\(#І< "|=Ӧ0m2Pb wխDMoOs:M $WA$eX4qEC"#=ռ>ˈ6PvvA!"Xb];"fj8bw3oFHGj',t5I$"48؃G*{J{-H7RGEmc^#ILͼ & ՔBr"Bhe (LnFDd?"\vEʱ'n&hn(N+ EOllMX5ɜC&1ɑD#|gQfrlo3*FL`O$J7,u4/N$S&b1Q"9Dl7t1cEKṞ̑."<7%g"n+f0Ɣӆ]16Yi &hˈ%_BW#&Z?dpQ(Nȩ܍lYx;#27tX)Ap%Bv&7+{6cRjZnofDP`!'$$*u=L1r&IZI#v{z5 z,Gݘp= Ɏ_%2f JCy %UFJEbe" G!r/7} < : C"#b%~K<I;ȔD0mK4d8[] %.s+vJJ˛N{{ō>IFY^~I܉2N[%\nn&Wts.ȱ[S%-S, hJ1QEy03xyugɵ2 AcR\k_}+ ” 8Hmh[?gln, i7cJ=gȅDgF'ӱιaƑnĞr"hNx.Gep%$B+j䓽˒^/Y3~ ]2CB1qIy>6+c" F&y,CؐZ'&Oz290BnJJ>IjPĒ-^B@Kq536Whn0'4HS$ĪIX ɱ7 ̗M#buKM(΅IICQD"5e;@~ q#yr;"vW`;7ŒI#(&LeByF(ݕ4%VC3LJ&DM?%4|ŠZT36}8؄VbP.Ipe ˣ8"|k xprlm[7rJU엀G,2 &vF;9±f FG n_#Fp9ypIDCd&E:<+[Y29LK;\'gD,QUI@q'ȸ6Jk t}pr6'8ѧ7jƛ\ &q/d$W4CZH'5zQ=&L=H[Lev. k3bC{$9Daټ<^Do dCbO3W>JuZTKqe;=ɇCk DjbdX4!3"fTLXI"#VI`t%F$OН'1G#J#BR<'T+ygFrlTCBW4$#m䇙2ȣm>c;:]Mip]9Dl{~I)d ܘk'3i& Fȏ|PEK"1AD$ώDć}JnZFVt-> gN/XθtN8Pg,&UR(&&O!Z J9p ufR4CA G1K,j7P| R´="R*);&%D jA}<;lPVDr4|idCA1"2vĂ_$n 6H71j3[E`u=iPBx'7<8N9aэ#9D589YUBM=ͱJH PL̙BH'$bcL?$ODdd, j~z\z㡭 Ř\k,ܴGgܰ:GgBu2604B-e LmCb3hHm*Joz6rϡ?Ch>% ;LZAv^$R*!) 5M`F2D2^I!$vLBƋE_jG AcF1bGt 1}[21#%CTŒD0l>~GVeQ v|ZF˱Vm>&LMm05 {%/= )tvFz"w1QdC%;2cܐϠ\e2vDB9&Ww"vb†D:14o77H5(\ ų#E;-MАD{ y+U 5z&?qnXLhJ^FF:ѳzcOblg56}8V5$G+#G>By!׹2rBE] h4'NEvjCT,zXh]%Bl5 Fl\ߣߠm-ń$ < Heqj d$UP$h~Dэ#IKPc䑍{>q Bi^|l9򄂤47 4XV\?r 6| EIF&COTBCݑW^F<NT+Sx>q̩.K'5&_#h"] L?L"[0cwp`e HȓBup J?+T>Lb6+D2;ޅ/$܍&%?%Y"VpH"mRrYٞ%s"MX`[']W!"̎ X6Q^(:% Õ"XBj cd(qȋS(J^#8;'I~hEEybrE(aa;eY/(0$eM`t- .2[{A/CCMBQ 2b$<2_n,HCnK7g%`p9NzG{.G'HDљȫFYznBF3e ]pg7iAd*|/rQC*s:)Y;ihۿsjȂJ^%|ƴ5!9I$DmzEz'ӐGJFƍcݖRwGhj4Z'.i;Mb cqtABF4=JhN:=z2: !l9hGbMiӢbFX?"0X' Aa9Dsi2B\!^6y~.܄@(6ND].0č~yC{2mƔ':CK>nx 2/q&g0Im%!,y%xgG12QCO,G}ؗ&źx%x(͍9vUHȫye{n*ƝwqEP*gӁБA4HZP2c0,DB_ay1Dm,&SxKšذm 9,Gb%Dr<ȝvU)-l.+r[u< T_",6pK49)\14Jh&[؆ׁ\"ȖpI Is6&_#~ŲLU*a} FlNi|ngOcΓdf#s HxG2˒hXd(6D!8G u oralA3i"&?s /el,\lBǥBdMV#oS@5ԂXCR]! BS_OO J0FB n5=[*F'E8l,};AbH͘%n, J*y7SbD teV3 6$s0bvOP{ ] Sɓȓ܎p'BV 1BKt2ljd"-$N<$ $o Ĵx$y8[g!+dH"V>Jv4ݑndYf&L?y47H)<$y OUHMEe"DaS"BÁ(hc%}Ld-QVGqbpDZn! %ĐcJĘbVAT$ܘM1‚>ksO՛V;$k p'&XL>NnO`=r{1Ddm"'b6*N0%I<#MXɱKؚcSt"#y$rK:1}!i֓,#ؒ nh"JX黑6%XI ڄK?ryly6;@ܫz$z2>#Ml I"&k(Z!;& '(@/`NΌKFgYIޓE#s n@ۋ3JUg&U] p"v+%DJڎJS%BY-8ؤ6La8%';)NԒK†IEɞFӢwd䷅=K_rgl @6P!-`w2ѾPіT\"CR*\ &v (l[HtaRb76{ Pa &3 ~D%2 pY <}3;$Ȧ-'+F1I a-ɩ^D\$ GSՅ "Qb̟>Ĺ)r-*b4y\!oF E9(J.;Dy8YNWHYN~nPM냤_dLtՊDFlĒI$1<FkBYz#b'EhI$xr@%DEM ZG8nQ6c#0"rb4/.-RKH^)!]5.ITܸb%#|fjؓ[X&D:tУ1%bht$w-Egn<f-B:6Q 萚Yzɴ` Ed4%~۲j#S(=|n C.͏f ̓I3v+8~Igr",H7;#yTY+4' v0eڄÔiBI-RVD&]9Ch~JZ Nįw$+8IA {>Ĺ O)dk[A&$_E!_,Pl!`&%2fE$VI}{B [:!MlV DC'KWƏF&EiZ`z~Ɖپ@nLh}- fTj((;q9hpYdIH`D\3Cik6-|Yoa vFӈr%Y1i?Zuz2F&_HV=0bbi?eN}DdP%9*E42DXsѳ8$yfXQ? gfDY3ߑNI&7rz&tdތu}-Dv󒰢Pn.No]J\ YkB݉r2\Kŭ`{oos`ucw/xmFGRN_}E*(9(eev4ePoϱ*MHBBCOb|@6deK7BIBl!Q6m"} r1NWJ*g.3e܋w%ʣqJL]P`9z%y3h㱩BvX,"a+0.BRx%& 8둴ɀXf r 0EHb's'`DTFF"lyC5x+T%,AJIDCv!PcF,R,R,y{'F(F7Ci%>Ky&bLa%rAJCQeF$<{}lϢ9DI21<EzG:*"[΅c}Yp,cy1fW{K; CQ2ƕ+?!?q-5TL$ea65C&'ȓmˑ8I#dDt*EBY,zU"uW =T=(A8γIjL eԴ_X-̊cA309q| /mEdGQ$ˈ2}1OKKq35q6!Mjxp*u}͊\ә~Kd{<^܂!ĖX&Ul]bn7FG0>RQ!MPk&D0)b@#z7 HxV7]Y8ddXH,S+2FnXdq*gd&r7%WХnarĿ$:'PZZMZ h雞Q~u>{=rZ(%,'D6ݓ5WH[vrKKk نy^ÔLR),U$)ŌvHG&brG[ KɊ5JʇG6XBY"2$^+F"E^'(܌ՔSqfl>=#CbaȾ4t?$K#- NRD6<@F` < X`R6RC824^JzGf60e,se@Ʀ^ɄM4Y'Zoisr YFµ(S ief4L^-)!Y#sXTO=e71 jɆ. i5P%Yl@C\3 'i5MGeťc0śc ")"_=QݍO"MqMіLKe 5@< ޼0&ɣe5_rSaU(U-~O%/!J V+$՜HC>6S"i4\  /4B2&%)LpUQYqK{0M.ʈh37 ob~np8<)e_'썏c9"3!b$f0(}"%CB$xmddfMd;F; < )Hv=bQU!yn?(0,LyMdcJFRrcLQ"MeBWTaYDRKyCDļ @wm> *-"m"Kr%"(!9]WB}Y4:hȰxQe߹TB We-1ǵ t",N4$Hg-F "7'䃵v<82 ҡP]ʇgIU{e༩ %;)+l'†~C-0#&Sq%' 7O:IgnqڈD6'qS&:PYx!OvM~An/Lȉ})x2HTTk`ȩ1l#4L,vKW+82z2vQ+ѹFQl V˽2Mȼȑ cёV7+baɝlObEe&RpJN͉v.Y(dKayvBk wY*ŊFfu؂zI$Pa?#e Jh&P'MztQؗDbD BC?%< P"J р C/"Y'pٿoh[ >ƹ 5URrDCE sěd4e| JPb۶y|lr]'I;"Rҩµ$ב{01| L˲ZDblFӊ$<`Qˎu;R-P6^|$DLy3 x[j26VtΚOb882-Wc%'Nx"6TDrc$PP MB tXog(T`ˑEYtXNUϒ'lMR7Vvu}2I$fdw0ĔGbF߁'c8t,M*^p&§ mJ&V} VDmc}y&R,Q I-ফBY@v-ԛ&a'eRCaM%jʁTxOoI,#,z&C%B؋0cb$k6ȯD:)d2:0ɽ&kC{L鑣fѤ :bi Fז4ȱ|=`G6B,UO(BHPtb (KQ}:Y 12I'$1ɝ&Pl*(b#dd}`8 =X@(SzXƄ/m)ID/$C[̈́[ j 4|"SjYܕxC{[|t7|=*$\3| -'%?"I-NxDYga`n8O[H}Nv8$]Ɓ:%,o!D\QP2'쉓DZ}Ԋ(D6w䏹UpQ'.07\'GRw[l(M vfw ̙N<5={Mt^Ct*WB"/RVZ%B6['Re,PQ%3$$jD᳑\2}mM-vEtNènQ=':fx'=b'DNB[lQXt] ɀg{fFCzb$HbRQؘZhsK$X&PLtrc|RL[ѕg"'CM2ʏ!#_.HI"UB1srJM }".6 Z$F E3X FLh2ca6)2.sq䍤kq9JR#<& 2\soCU{ [= FTOm1I#aCsfƳ Qd hMKB)$$H % -eG~;IDALaJb[e(=5pf&6@gfBU(cEK䁘IJXEFy) qF`cڲt7Jܶ*H,|Ȝ҇Eop{߂9i%lEhĎ}Rٜ,!P pǑG9e+ܺ3SOVgT7&9 +g؍ƻ$6M^G-O;hmqyH˂"?қi+.6[G/Qp)g4'$O"Y>GfhٖrTD$ Jt*Hevn`[]E8Yj%^ 309O; ,("QmdMv9mfxu-ȲڒpLC pG4kwlH$,r_q֪&eb3 )lB6YJo!JTLCHMUJ`PrƑNyDx%$h}۱U,fH62M=#~F~ EPhrIe(Oad-gFI{ 87p&{nb '1Y]q/\6xrΔ$%IQ\V#şIKv"P[$yb! Cȑ\>=F1xfq-,Ņ ^E&'~_N䆭 q8vLM*2NSh?'tKC:| t=s0K9p>*ENZJM8o'p-̍D!ggxbNdT*utn(<.W"P^{GTBzD88'!!f5-ʂoln;,[C!C(J{| cci6cFt!0 JL'6o8sx{#k/4b~p#OKqQ]7 l.F}!o8"%Uv"=^F*Yq9*݋k)-Y5;Ƅ LT4;l% 4l4;Miټ"6 vӈ007EVgqcbF _7+b}kOe<6Ŀc@+L&9BE#Da2 7Ģ̲5BT~ٝ҂ƥ%`F$vOܗb~XƸ7j(79"K66!FaW,Tv9mӡdE4ѹ.8f"%0Z3']LoSSdս; cB!m9E+~E; ]-T’) MtIi_wBy =U师l2V1 D,;M9'?";J )nn@uaQGoqϑq "(;$#v0dKP:8(ݷ c']2V%k(lN.G-W؝-ʊNI] ch$'.4`۱)t27ŊFN`tݱD 'QxVOEJɾ.A3Kb07 O%"\oDӱ;+ܙ'۲;Y'&JLz,D-"N3 }؄52d+DR/o##6_^Fybcƻ50azd&y2`HqesR$у.t$ͷ#d?t#ލ`얦eF<I) U:&d  ;|=i/qZc<}iG 5r'(I=&^n ulxGʔ8^Qy/< M&v[ =´W1_Ȅ&I\sIBa L-DI{CZ2II5O 41PĊ4btJƚT nKfY H,Cc$"ب(We`7lRL.;ٵhC'+*onas+<0$VYoE(t8݋QS2QRRź~՘UCj+r A3ХE2A1XcTȔ!Pn*LGyKn<_ȡ[y&w6ݱL] mr;W3p|܎X>tLtJJ)1K < drR' #卑?iǓ*U!O 2l{m+!d5BwBxĴ`kE!9;PB)D'ڠT4ʴeG /00Gb!;d5.J[rXe'&jr+~9&sBٜtbSD%gBcs{S#7Ʌ{WB\;-ѥb ".O9 9Fz,Y&9 (á"[@}+V^O 6uѤɴ ɂƨ؎'}peBAn9O/y"9C&67D"+$GCg  l@Lr8N6J1XD?&hu10 6K:\.`#H.ƍ/zY@!an4{MBђz#S$nƨ~ h)A VaLTe$F^h3Ѣ4cA,ԋʀ#C<$J@Im Ϲ7PL'#[&JM܊rԼɕPNBō5l-ȒPb[p2dgI('`,5u4Įs l~x;.Dte!TƢbmRʘ[;[WܥK#ɓy0\+M灢twqdN q&F4ѷCnnI>GBV];P |Jsؗ4NX }KvH)>8:[i'R2^J m-ob%$PУDH-%Vc*4RDC'$ Ϸdm&w+dx?T>gO/܅ ߁ɗE3?a*=9R"7-wr+>Lʫ:P$RJQi}R<6TNᴰaE\2ZN '[uIxx2ܙ#bRDIz$F 1 ףa-gH+sa#.џaBcF bwX*lv,[a?pv}?aE:clHBf0CpCn8hyV-RhSiK:I28E:H/jlB] DLc 8DqkBd66H1$]8BiAI?CL]71ΖUl@DдE.LD[ZQ>{ 'fa(I9Q,ĝ}{B'*7K`],jܳ";۱>U9-0")!',{bЛDIM;u.}+{pя.)G:Kxc`p+-j6#NS~tĢ d' r=DՕ)ۆmVH d~3GFW&=K21`\! $R'f$r'ہ$M8tGn` NVܱ,Q,M)91i@ӛLLJɄ ɹ%'pK'ɚn/q;.VZԢ9!hSEor n<p{Oߓ pn]#6 1=rTCܞPEDƦxh+m&g2dQ CBTDh}MؔP5;ci'#bBC ފ ,Fd5'1CU[X.S%3vĦؙ#c%ˠ) t )jʄDB1z /Z*(9"A@CP7v@Խdz \VL4/CAzXІ/KD i 0qB $;[kCZN!lIPI8b{iGlhQuvb#L2;؅HPR5j">iC†S8ŸP7&w1vϓ 3ȭcrR'r94'nZY vv'XRP읰+"[ 㒲":$T((d[MB~7H`*^+-BR&O*n;ɇ(46bc6fKBQG(g {o Ld/rj)W#se̞dKIOc6"4E4ؤ" F) '-DPRRM7L^?2o@xB'lٖnۢ{"&_"ؔ ^_D's6%-&_2gh H=nGb%HхA("1N%9WKGzyBMؔ[2&| )c v7P-},(hV)ckdSt%9 7!mRr݋dѱ0rBTLIQ)G(▖j75y܉/̵D&M)eMcHSd+p- gXH9 CK/D}t %e(<,"wEAbٰG"^&ȚRdG GLz2mUVE1GXvXJeHm%.HA:70`"y$j0@0< v2 s_|Ȣ4nGJra%ZxA7-`W K&hKO^\vYӖ+( SZ* "R:hj @DZl5 jBS>ҢXS _"'F6~,bk+͏5Dˏ" *鑇69-1CvD(p< 4I^c$P<{Ф&ci {WSGdۇc\ʁǓ *$y ENNRC`yՋ(0'Z2JgD7w?& $l*6Q{dhݞ$q'ɆGdIFeYECu"XĆf#F6:v=1$vKbqCߡfDJ9Fȁ5vسR$pˤL#?5)Ẍܷ́Nʖ⡹B[D<!YdwN)G$hDɰo#F 90L`di}7,%'"Ch;X,̗&< hƻAM)8i mi`HፈPoF3 yYut+`JR X49LI"" VĥLEMJ(.Յ8pJd9zWd1\(].ĎK`f+o f"\_pX&e4 BCDS.RpKX_CKH"Xz=FⶨCYR8'Xv W#Bn=&R!oB[lPANC ŏ"`YI,*D "q99瞄JhRcJR\H)lW e 8sv%.ʽW>EqVBjVZUHjJ]q$=ۛudD[-;{иU=g>jgȖR*peO vD,瀭&K\v(<䃸MRيȷg-zeL."VcQ7FNvhR>J&J:gUa1Ga=!vQٱUቺDb330L1CJn8H7t)ĘfȬ%nx `K-  VecBC"zcpН)Cd%č"Z&Zr% T`Dƒċv$VfGxb)R2\;8xVr$|-_J&"اT@LDEfǁy݇h\6N^FL2 z7Z&XNM>ރ6э-'ngKؙT8Z#h;y00?;cSNFLl0,RK";)$VK a=D-k1GcnDˑ_0M(Ska<"J(PZ*J1ygj "鈠~HaM6)8IB`н*FKJ7 ccZXǤф^!_(Ȳ!44 Oc4`>%DHn7d-6LBD\jRX&XbF2H$[<XTNQ"ۓ2ijw2HBfLhZip9N Fڢ)q#д$$A,KX z=PG`=h"h{T1_Pd4|4&| ٍ[)ϑ(1H :}˴2P,p&`M DV1jCt{g&S|IWMx\)#ӡo$[sV';nXbMt+u b*0WBvdA6=%lly,mC{i_Ip^G]Dl7PR VƄL1•?$l$ȉ.2ͰE?x-),)FQz|m#bIS;F&'Fe"ݓHLJAhK>Y.aZ{VkIc#f^I03Ȓ YItML!H19Dl'g FL2$rEdTLr~KC)Dg4 N.<Lj_ Ȝ{MIh8hx v16#xliހٻ8Y$Gz3ajbA5V.I΄zBD4%ZUM #Ag1D-B7(`5IaK97EQj=m6%0K,`^B]yh2ئ<=Ǝh%`y:Bɉn6<|{pB#})lkY $~G8J-=|R+SNkɛ8džBʃϱ&8%"E}culjT},GЗ.4'7 Uw>D얭>EȄH2TXqMŎHiКbRga$ qSKM7(p+ei V'-9p4sXd4ș"%>DU )cK{ is$lx!Kw&\wG\A/Gc?qFCyv@ݯ}ͽ/XyQȭ93y*y2dA֌LGQ*R3RP*{t6"͆4E6`X>-$ Ah4K$NoC&Hkk˃]KAR,Y{6 F'8'LErY H| sF1t%ddYF;d},R C{h˃L$+9a'(ߖw`1+y")7ay> ,L1"n&$%E o-\{1 oT6YfCCǃ\Taɕ Kؒ3f1Bp 4/|{K P4B+R>E!7f"obanI:䌄vE/A^Yor3"QHBW#ed1كnV+ؐM#$ʾPx\)7FXpY"0o $S -6^ [2>s{D+ʓɱab0ZQ8}R;Ed<{eg&RBc˂O߃hox>ĠlOu"#tNG5Cnݱq$#wi5c+): $@Ƀ&pgB"EA7#ivcW0I[Ufvlɍ-wnc/a~~ Â72Kȣ+Tk- `x#BSH!=d"cCLTcĨ]7 q %D9Cr4H(-ȗ !pŝB>Űr&lĈ"Ū#X ;Ւ1?:vTCĨ鎁l1rتèBz cC#~F7Hl)i#[Eߦ/~ CE2nhb%,m6I s BdYpJςBSxLi ]%>&-hlLI<ޘ2#-2#z71Y0*&<+ypL :mBe(jO6W7,re@YP>F?ЁQ&h؜)TFHnfD+*QܝW=S!q!c!'/LSw 'D.LHuHd$D@=.֋UՒ= zcԏK}I$LfoF3JCc2Q`Z1hZ^ GN,uER[ALD/L;rdi.#F.D+!-c&r{HIp8(807RS&4yǸ ŗ[t4%6FxJ J"ٚ&OzV"q&:%/JKj:G@,$&_G Dlh6'^]~ S2{?u$H6`(Lj:0'XC/-p\?Ț햀7r~JcBQ$pa_qg;&+_B8]nsCx8&废!&I@uO7)s wo 8}"$F=ߧW6W_Q'=,"}pѼ iwV ^ Q?c6QO'D)tK7 AfA7`F z8JBF.q[]~`GgHH$d{$͓ }P`=0K0s c^uvz5.PD09'n+DY0A2;sˁڡͼcC9!hvʟfy2,A \ {2-gG~ _MPH^шAƆ1\c&HijяX.E~Z G). Y-18YG=R[kap"%*Fر (D Lm\Ԓ.v' 0"0'%OB^Jo)pBٲ%32C"4is9ext19E;1رPDQl4!d(uQ܄J؝ǙkM13˂Z XW4P,sö$JUzHq=i,2R7ʡ̨s\s$5%ied\d(ʶL8DSO0c&{ftt"{ȬtI#MՓqZ8?adhk&G7Ȇ#smAc0'cc$PƟJI:$DhHj8EBqd̄JFX)/3z#Hsh^1 q1T4L[Di4Dq 1t~=U5Z_"o-Q$px+w81y%BQX" tI -2hMG wɊ 'doo',UP&77 !m}o{d"Uz‘CO])t#NW4s BHp"_s*}_fIb5g7Vo!ρP[VJI%;+u&8SwFM| i@//10&KlG[VwI)'`, J*J$Lڐ2.BP ctS뢉l\cpPœYيd:LTl"dn5JF)+$v<`Ru&uc,j $؟C bqG1޸f3e]bc" H O؛%3|.,jWfP1jnF pK7pؙ@7^z6h^\)mLAx\D&Fr*uheGm䥲 %)bdjQiJS9{툄Xy&BBNr^dS#J6hSc S0˜UIT'-#3LTbnl66&'K)c} :<޹67<"<,y"I8,l+uNn5ޓ0RDV'm2#@c?Dssd̔1ߣ$#mIHcRh2T8XZ^Ʌb&SΒ#V{[pnnalD/afL,INyI?OE1XlkV^|{vKFN5 O‰!]%h"<!tNSn)M OUsZGI.,N0TLB'G#hҒ%H$aClQBDTVD1cֶnR;!Bm\(=nS#‚_ j&¥dC[|;3b'&,JU,2J^g\ndՉ%R>\\քX+,lA lхȄCsC#W\=1YGiM囎*H%0_RDP yKD'iO$)rC8T[u؝{v~60NvHsXtODVY>RH^7bW86m;v-\ ݊y tC$j' dNP$L7/HOc'L2LP88fT' q8x&|;TFQc,n |ivb&)u#f'VDt">Z"JB!j֧=5^F" $G}6=ģev)bGrWE M-hhjDLdȨ di=1zk8t,kYj25l7VxS ȁ|I:H~"5_.T#PjlbI#%‰Sv bwH^ň{{. HosD)iy)$iwŢ[C:#{>C#/" !!f F!x.'Ishd.qdFTĉ  )D]&"Q‚#sJÏrg4ױsu?bjZaKDX)<DĊ$דؽ]q. 6 Bh0GpY/ dS'f6!9(Sby3h1˂j'@(n3$#rF1!G$ӑ(0A\QPhL9JH6viS~j0[ 5 )J\`X18xi7,C gdVF9g̪&r>0-#f#IR Hofeȩ%2{,rucPL}'pI01FbrY&xH҉1l[PO&cr)d͘Cr;) $ah;<9'XS2gxo$zɔjHl#{췑/:d!V6%2QBLR" j='d;E-d^6NW@= dsT"4&D/%d 3FZ HƣBbDFD1n-i'əa$MD <I6,8ZiEjᇀ1Wn/Yݏȃ"ZQ̎7 \e},rG'un8Y}0+GF|Z#aľ+g]1y;>cVwC^5&+4B2O\_4}fbq? 3e|sJȕrߑV~a&Jk 5 l+qYVh6^bk@jſIOoƴfs~9?" 8oL߰(/IN 7}{I} ?d[ RR8a$n:MPp[ cP> iI R KBW!H߄G|__Bq`q!QK&a BA7_jd6=[E .tJC E sJgFݓdb,o(ʺ8-ϜW[0pMdN5| U9 Jp)Դ|? "\_4t ,젟9>B`"^Hxs>Py"σb\Q "=n!os MC43hCwX?{nf<~bevq ~Hik4rM$MV6ĶOU! $,pVFv*Ι-((.5D2BRz"ol_0ǁc$ׁw<y n. ;̅ٙby[!M44ׁ&ތ#w٬ *@.T'/KfVg E*)h%YDdF"gȻ4Qf|CwlTɺ=h]7PDw9ܗ#G# } f\ HDr^%rĹ)ȝL`#y7l 75AEM:#<,fCf[#q|і A>J&xqw%6!#ǁrglCI#[Wc2͊T{f+] "DRZ dLٲ2Wsw; ݰJDiꐰ"'KI~G RdHKCiGJ;O/8= {PL?* 55H7(_lܲQʡ Vc<5R- =2Cᑫ[&BDPt~VK5,IB,SSeA.E |Idcmyp-"P6SU.ZNHGzH {bV2gqL16)=dl0Y"~`SG`$BA,ca6ddtxoBކT10Զ:s6xQLϒ ;//Ҷ)TJfZOMIJ0Iб&Pѓw#Y0QDcqy%5)_؎?b+Ȏz%DYDL4mHԒe*ؙx>D IĔY(yiIpnnahN ndjKXT$a!K)ZM;TQȧLjbNFdF:% kdJ$}89y2Oa2dMd87^ TxNQ+ؚ?!(&Lhʎ:6J&mn5Q* #l'MSPJ4H7z=]"t1BQBwL% _[W6B\N?7!N37-RsFV}lÙpK7wR'!x.XN?7"M$ML1/c8E{+ fo+LʑnhU8>A c~ NȐǗ 3|,2XYQbx$.GE%8O2ͮFk@0*8dY aN<Jh[{9 Ȳs)>D7jEmfXד8DPK[- PGѽ dAe.|sG0S؏B714AO#~DPB. mMҎBB).N@P?bDZH5/C`!}1IFđ ;W mS?)H8"нƥpkg YLd ‘8(Q`N}$ X=GȝќE#b-"3(l*/l?"\,ad{&!p9mǸrEa/qN_䔍E!M,ļ+DH.)I葾v2>B '$o. |v>aGvDcXed;+l(YأchVd؂p-%H'ȟ೉%m`촒2&C sďJآsGtCiXS׼Լ&NxkRv8)B&(MⲡH[cFG;p]4+?^c!RE}MajeJKj9%x_%\qlG"qd y.s&$!% n19ˡFrcy7>qǽH+Fї#ʠ[b˲njNX}$&QbIxr '26~\{2P15QH-Yg1؆ςz|;lT`fQ\ `th, B{&Q:! fμn:($[$jG2J T ʑ<1Ф1#")s_'2o) D_jk8' K)F//"^D%76̞r&[BXsG(mn8 h2-enRBZJ4hZW(rn&=^FMĊ Y("G.PPRBB&uZS}3R4pǹ/𥰿"$t?[ą8LXqL)-qɜ)h,WP'ǫcDX;hѧ'[!\ey7g؋SA sa)AM2C":{W;Eƫ d|K/Q77I/Ш "ERR>P~;FUls!| 70#ISX(H,FP7 ,ìBL73oؐo{6E o.Q S+< ݢ6>ȄD_si X  IJɹK#>I([ '-ԇ _m,v[LZV$݊,B5[ b^&-m>+a-bb8 Q؟0%^qУ+V둪| T'Zq^86RHK5#!2#1 OP,jA#|ZvdiKUպJa9?(y2pR*N+a_`A${l[ ЋM$ؐ˂%H18=Cn콍Lt6wܿ BcAzk#uefM@\rWZ7&.rPța+5$iH 6?bRčeqF7D& ȯ=^ãaQEv| Jf̑R2ANPhJEwdN=,p'nx&> TK>6 n(Q-V"AOcI>W#gD:)P>\Ale``Ję*p mєK,-/24Udb$T(iB" X^ /q;ǒxBz vC $m5(I%# | ?qt2aA t:`Q 4: Ş#KlG+C};hK`hjE#D#`v -_1`edh%obn2b!AP!"6Oܶ Ca@ۺ*fx e(\]ފFڈԈ]E?S4 d  Y4iR4$Ym5)"J*.…Gَ(Dz m8)yHmKd2v-ҝ[#Pͻ5dhoqYbIv!t>Dҟ^{"l2+HxO ?[̫} h"BDk5B.B bD KQ|-BUDrٝ0š!} 6#i@E`hLi7QlڃT*J oHQ(AcjXcJв"p&`MD a2|d [P AS.P%%a RӔ'tsyT8 `LIqZn$Pb*;!5 l@IU.Djr#l(PE|j2I,F X `1P+* b'FʢZ $5Ş.\le).Q^`ׂj\46 ь!5JӒ*i s#0zUh< Nb"#  :B2޴%  Ȣ<o,1BЕР-&X"E!$a )*CT!ėY 5k%X!B(ǁo`k'fMٶ1G$GLwZ"2I&W *F(uIGZ,x"AcYĖJA{" i44L%FK|0A?Б PNĤJpa֖r4_cIӂIIQH{ HD z*FYbքA>%) AIJ瑵 ؝HkHJ9L@:JҬN3oHz!% ݅ R(\*F.QdIhN. h!e0=^AVDwk6J)ehCa[GOH2N/Zd=m&l9 #˝*B)8B6= @ABCȘ BlHZ C47E $܄ZPf^ i`UX/\it5R=Z0 :Av9(3V,>4AP:B ej:)k =;Э E ?!aRGoؽ3$sTQ8.6A [)RC/@k B4SUG"FLn] ,"@Q+Ns$RIh#v!\ v'X1APFMSĭ >v 7hp&wZ#KG8cGȒzn.`iNCgodtH<4{uZMjM)ZrIN{ceZRK![~0)NGv> FBo>Ж- /q kPYEpcI@[7ahdC$bĉ619v/Xubжfy:BA"+NF%czC ]"h, Uz=]tKIլ 4] z,+jYÛ7 s-1 Ћ1BG4( B"IzdBP Z"BB|WJX^KG?Hz{3Dg8IJe@ OY6-Ǻpٌ,1iSgYMC tYX2tfQxDHcrҲ {hz:4SV$p Dqbɳϝ,/5}hǸAKk:ϳLtT-!?H { [BDw AXԥ] L~3bP+,)!9d B piGROZ0y'} qdp(DKb_QެA/fr2v!6*G劘$d,HBѡ&#TP[Po*bYp #r2H'jp&AY%xB:ًzOPzn#N:^\REI*|"9%L1htHs$w 28VzAԍ% @,L4(*}Qa)`@,fh _rjl`|H(>zo-5$ v$c5Eb/ARSYo9}SFoZi::ƃ@B!5!$BTT1AV"@eC\Ʒp+V< "0'N45dz^SY.*PJj:flsA##RJ,G&O"9ogT% e͘aʆ%Z@"AzBb!XelB$%-/Ag= ^:D ThҥƚGYֺ^l#)jiP"K!숐īcZ< E-ԔФĊ'A3f7b QǢJЃБh~D 80ðЇAn%`;XA AL ?T+N(P^\~|42BTE%C 6xW!rj-* )GH8jnيK&hvc\m&3nJٖ=;[ H#$s ;JL;PKR4 LJ;s'W1`9h~ mRt/Ґ'FJ!І UQ"4 P2=P zzA@%)) uZ hM r';I'cUY"!pI"@Gm4AC]xٰ_HH  Ѣ1i"AMѰI ,M  =(}7XO;V+2$RNQ a w∂3I؆!,#Z ALzhrH-`FA 44PƪFNDI܃,*! 8cֽ- $ץH2H,AȊaeQpBNX `a8:,">- za0 DhSү(Ѳ܂4QzdHPZ %T-#ԁcVDд @AA* !mi=GcZ'b0XB"BA=`YKz hB  #DhFxHМ  JœXVM *.5+`N h^Ae hr(HDF 5qAFG(8, .nt.dm\1!j@FKn<0? hhWz[4;Dh$pCEhADDDAЁ"449"hrB I1 3fU*$8+(sL= 7bVzFV"("!H#ҽyhHĖC#р⇡M#HS ,BhVni=jKS, 4Ah"%G PSbA Z2:)^ MIQQZ(  %z AQ AAAAAA \qEQWg8ܟnu&6a "mؖ")E!"#R z R#[c3-pqJNlu&}G x?őnߴ$]b'%͘!$nM•,)LF[EPA"V$@ƐAD h, H>4AVFGP/Lu!AD,B+Jc(6AD ~2VR/Ti*$/L4@ӰVFId~ F:/ G=*:e%4+q$hBƏ::'#& :IP4BDA Ǡl%h䡀 4BqMK+ZDTc&HIA0}F    44+ @Rˤe蒤Bv:e)>xOpS%% R!  #H `ΤjA4hB*H!DZKB=|[=040x^O) 'A c= @мDr4BKD $DXѴ/q☑fXvnK  l4Tш]hk6$9#ԟT dG=0F_EhԍYLxF-_= l$CM';4Cj @(QdX(AgbFAi@= ZB QfF"lDAAX(AQzGip@ br*s9 f= B GQj^*!- H-@PQbH :`=ʊ>/Dz|A} ` T}ս@,Q,kC) NoQZ% 40MĂǪ 4h$ B4#CC@&/M05PAh2ta:0aW"$5[#^tA(е (z#DhkC4@ cA܂ VKC2e! CX`p1?KUb>΍J06'GL#ҡAzXTA"*݄*cbME+IiJ[YbϠc R!Bw.t?Ff /P,#r%l6 "zpGHl *R e"a. q9dBE= ODM8ƅG4c+NАF@ԁDf hffW//Lh hcFNzhH@ #FCACZa& ֓HǠ[EOrI~ ƋȑgUz7zS?]ƌlc=Q(yB_FI!kքHYGJJF1t-d ]N m40FpYЖzbr$D$Z2[tr&A=:I;Me 2tdHT4c]G 5GC)5IY0OL{'ui@YH8Lg"?-6iG4+} D=Id0BBdL"C_VF؁#R#D4A{ ILt&D Mr1I\`)f_g[/oHOLOД5(Xю@dhǫbzbIIl/AO: =LnlH> 5AFWh),a?A0ؐH \2CR-(L^F@LLGS-Z׏^DB[[ЧkHSK,r=`HHp.,D4dpK, }B Lk@p1 YN5 Cw: ls$p,hoHb#DjAADhhaEE2'5%*]?m&nP z?W"HDvZI$^0!b#/RJ?Ǩ+ ć24/S$` a 44dFA lAzԏDTejj  yZ#6*)@HQșP$^FH#CWЃDHB5#cXA)b70hF-$f jٺ/1a/TŠ ~֌ lr+AAEW,: I+P`rF\ IEB  #Z5"IEp Q:K] fk,hQK*%Đb qwJRҽS돥(/\Eem r2NkFDi7HrPX" CY #\4#Ĉ(UoqBQDSD)dD\ErG @A֐0#XVlORv KTA44v`$e}K|',-ՏK hXddh.␴ ZFn D^u-cƅ@2nGI+ 8|b)B8)B , xLG&(/DX֐A@ְ4DhǡC(8zV`&-IPXlMAoKBU0ÎGX羐HiJ%WD)pWZ/[N괝$ίő4Dh H]Ak/:bP䕍jC"4`HEhh[- S0-Pa 1זNllr GQ:ci+D%(H"tn|ܷn"9g(f]SB5lxz'1ꏠ,ZJՐ0 [AܐN8о#3%!h vB jؖ"/K[V0B`iQxtcҔRj%Xд Vcz&-71+rL :MU K/a\9%bKH 5=PF@6@=$ԓNE P2U_=ۑ >Dl΄h"D4aaDo~sYԒt2uR½q%8"MM('"b@"ؐT&HZH'e<I Yi`F Bw1(ԁkz A鉒I"J294AͥʈW)CIe@FWq c6anHKt'"!J1M$DC<RBrJIY76'R\e &u''$F遖KCL۔cJ VP]4AZ!Øh$@ƑzFNUhNG5c_TЂ!MD*EzQFXcC48Z, NCY!cyZγ~nA hu, I} РI:DNϒ2_!"AϮ4lFƚ a jCKf{ŐLd(Hٰ8{}Mz ]4*KiQyȔU^ " Рh4'jD'>'b I$"5d"tGzԐL{%%l%ЂNzY%#駒ؙ=Hq2tlPL0mSЎ! UlK뱏EDc,tj-:kH6.FEt[)Ó`͍Ⱥ5$6'DI0/9i'D>/\ICI *$3ʍAK M;R$k" ^Gtc#AL~Ha sܣ],RdYA+X3`-I!zB^r "\nJд|T+7M δD"44oLAzX%z@ :6=j!hc%7c CV)La[lR*>>'ӿYdN!)ֺc֖I$Y@-v EcQв1Kc0*RI[ms .`NJEHTPBa rM2ot5BbU!Wqm߶=/:o 4DINXzšاb 9BD1}$Iddams>bJŹgJamx%pEЎ⯢t܏VH&Ɣ!-Ĕ ycRjS. t+(ж#0`Bb@hւoF1&%"Bѱ+,C:dJ J)3(QP&DW5D4#/C+J6fawbLBp&DI&hO鵠׾6z#H#&7bU3 D,M kEH菠f4+QyrELo,DCؒIr+hKtž_C2Dxw*dZMxb++(sh-# Iѱ='ؾ6053_ q)BmcE  Mi,FhhI;bʈi$`LĐ)p`j I;ތ&Hà) aY=/>ۃcT2%vSJ% #QۄFH"3'X>FDX" dG0ʼn}&{ x HtƂ E2`sq8I,nN$[#Ђ>n&-`SF 3J"QE"WlJBhhhzTtkV AYQVEfMMZ S+.A;H+FIYGR86AD1c ЌK(&!SHA,6CSl1ȶP}dLLoG6=(؄R1dNĐ-C,BC ^OOF/Tz)%: "Sc rspxv΄G-Ɋǥ F #/CBфMH}+E a(L”xB$,mAf#' #F Eꁌh/A_E#z1=[#?h&*RQ/ h-c#0$bbm K$LcYSAb6hDcbdT@VNH hhEqV%pd)2xPVEk1ZY 5C$lRli3w*PCNlZ$$zO`5^s:͸O9"^=z !dי2H1@)lDRM-qZ+I2 ЊTHÔ(#lA6a) j>BPOj؞[]:BэiDay aۊ"$:bu'Gc3¤z= hXD|˯2RдH0_z(m!+AM>݋lr'JQWjXDPqǦ}[//G~ Y"D5BȐn#МQ*撐!&La`rE_,9dʁHA8ۚ:Ɇ3 P_cHX֗"jrf(m Ô'#zH8Z HGECiI[a`wǬ6-,r'qpҖUߢ}LcRCKȷF%dH(~cӌa"oFl( O% dHlqVOȖ]Vm#cn1!>ѱ߼<h!\a,ڌJ0y ԵnGo:'д~F6a"5+hPĒ .t/N=H0?CB6Cmp-lٱ:2C`Ƀ p%V)7%$zY$lCq IG.ɚXC,%jD+%j. X"ܷn#aP*9lZ֍H<JlէO1?K)ܭp 8l-}I'$FIl4Hi12! W菤=)O&6dHkІ$Ȼ}q,OFhpB47:ƯCP~hBC[4m9 # :"T zE$ƵZ "-E|"H Y=[k>zwɟc`J4-&䒳 >Ep '7B„F2TqődwR;^DINIp1y"/nR @#cAiCZhPhAj$I欤j"h܈7$?A&o'[Amo9'S2)rތJ>$)N !i+t_1BN~^5 H= d@z6|Ĕ$8 'B1}jԯǫf-}1a(&$~Y(hFD{B H(4= IJ I)?Sӿz7vcJ$$A/ GfXBR#؁lVB#Z kc4;W{,=vSH/C1D6I{Z15C$в/SBg6$Hq"TBddpEK31HLd$z$q֦I"c 4I CX 1/b,ZБ!l[@+jƉ2AzgITzc O%ÓXXqƮ_D# z5$,误:Fљ'~gF71M2 F[ č v4vPV@$51&I: Y ΅s:~z?EI$`I:Aa"V_V~Y> ]jPh|8 ^oFf۱!blm?0G0AFIѱF4& bzdԊ[)!x&&D&xT"lD-`(Lbq>m"*[QQMƎQZA):hwerjĕ?Jթ$G-(jZ1)e & }1ԉtolL~r2^rbP[W #JpNΏBR/t\22I$Z&?='D%D $B z%!hC{$&OJ5 JPz}qոLbte kBCd=ձK-1dOELaTcQ3 ђda $AzY4͈Xif@ldD/AňĴdD9CQ9:HI:ϭKX-#qGAic/WίGȚz&4cEcyFBLGTК21S'J d jőskӢнOV,?6I:2~} n0)REFB0#UcQ 9 -@EMqV1S܉ ИwzEDmЉ(I H hh >$1@IA0p̴ȱGnܷa:>1Z'Z^1h´0fIfe䑹"3P j&Ѳ#Ee1 = K8&L1r/bэBH"&!X:>LST":aX -݅ t*M"RyL[JOK#H"EKOB%["V0SIN蟤r7/8(a$)DB#cJyi_ڧf !&3hƅD9!BJ9#AuRьHrt"CpȄO1Q(E 0I'r$ hPc51{ lh&EЛs:]Fs_z^2IN2>p!]QI g`OLn8D"MH,C2zE,&z Q󱶗lbF%v"Hd^E ct0@lccW Q:$p̑ɐB!daa .IN}Q!H <lKD/T&7JmW#m_IR>Pu~MPE)cD˜)hP%OpVIQ#_YƴȃՙIf$.a^ 8{nE$"[hL+Mp VO ,B# Y]nUk_ߥ$9H jM PB4JZo!JhR}7CđD9yDkL&߱A!dXԀ,ƴKC,6Fb]v/S4[ƈ36bPyD tHa"g-s}'FLzX:-$?@ZHG%Z J#UENiAxE #jB Rط>8Z̖Q!""z-'ˍ] I$`}1zwFXn@%Cp,%xZ0P+ }g"A hjD5sFi1H ۈc!F|1iMy+9qp][q2I kW 9$1zE F7MژՐ"* L=BԱ`d0fm"^&o``,.O  <1$! D &#D1zc hHh~""2@4!|]/KՖB^12-I3ǠD:035T- mX 9/ $༠S ezu `P-șIO]z61'MRQRt$Bѭz2D/Cމ!)!FHz60 *D`! |M4;Ea$[ h^L66N lQ2Sm cf G PM/*]UPHT%>Kc 4yDHF . hZ-. |r0I .E4l=-ؕ ɕ\j<+2QBa -S:Ą/:1$+$l0l^S'AY8"e2N_7e} yKVcV%(4[ͬ`R$lJ/` #}dD$tb4diM )EkMdGCZsd^%U~>JF*7!%h~ Ӱ$$Azߦ} l@V@Pb]L"D64UD!(Lj)$ >z($BiǶI0${I$0& JefSF!a~=$QjPm03Y=it^R1A1F`H$toCC3r:M% M1M64=DXؚJk1b HY1,5#- d@J)' Mi$^4TĄ5Sn PCR'jiDAj6} ;ř*Ӭ {]Hc|y%kh1bFf/-&ElC#9^ Rd$FǡDAG4,ԁBD- MزEw0:@Bhj^ĢQE"6Ⱦ`I- T÷gtD HKHBz"I$b, KlM2XG"J p%u 0蝩F@o2cq"(KD Lh4c6 ?Cxʼnuڰd1}@(I$ES LcXq(&HД8'L~0gVqK6ѸҖE[i1@*T27e#m1CႶIca m`22I `bʈs B4`#1$TjJ$!#ċ,}CEe+d[B$Џ3 6DmYJ YHK$H$NoDLd$/Lz`A-Qm ,ˉ<X1'8LHTZtzR{!/4=A|RKZH蝂1Di<XdDj2tC3#$?) ( bބ&Z#Dr(3.D$@P<! lmA 99 *#^H/h& ?ލƉ(ѕ hdd<HIp3!:J'49C, Q1HeF1 t UaGc*Oϡzž O{Boы$z 1[= #~,!+iш@p9ҝrUpA5 F3ﱎ|ALXD :$#GEcZG5ˡbyJÉSb&D$%dEI:I:1 F4 %>mDĈZBAfd,Y`HҺawѓYpTĨd1U% U~c=Dihm b3C0_J4zA@I()%7+ ' t!>9[Bm&Sr0.[!pژW)Pz b'C!aH5M$ HhQ`HX>Ȍ+1Q͑aEEF-XcGޯLH_AޥED5f@2*6PGXp?m"X| <B@0G4hь2Fd(V 6e%EhHL.$;bIB E$)cy6i!`N H!i$@p Y+$$$h4FtmĴA?D H9/C'0nLnea,Ośd%JHHszCFA3p>rF,8{C4^"Ă=A!}wcq%*a /x) hZ3CDL J'Ƃy0׵O &5B ƔIz=ZY2#H bQ,&[2*$HȔY88!ԡ C$:PQXBB=^ֱXY[Da|@,-6\AT!}7ս)YD0%#=D"dX&tN 'RE"J#FfjDdq &NLZ1A/q'џR4N =Z shH#OkI"Vp#GHJD:#,^2G&/\c4ɒPll/ $uZobH TDNǓ d cz4F$$d;@ 3%d؆|o䑜 FMVe[1pi!#̈́=[bZ. "؅l!("M2w BƐG}f3r'o"7/Q01oXwȕd,Y hM "= W7u t2bhNYkK i(!Lk"(H_MMRO@]"D@ӦԒD&4NCkF"Z?/DJz5#)p>.bJ&D$oICQC&v"KLꁑ#@d=#t1P* z0$ġc*j_88 *[A)Wb,S.*I]50Ddb}$sFDY&$} DOйJ12kǏ*Jyi!-cD%/A,Z5#F '32_S`$D@E1hHYfy2+,jܓ$,WbD@,4-iW")зbMbv,1"L!Ph&"P?Fdz':D>Et0t@A!B4z4KbF5$K!Gz$b?N>IGoN5 ZZb^H t:o&À!SI'E&@zIB,ukӲMZb^z3ПפjabZsgMs&3I*Hj$dN=dݽ#G$EGEHHAN!ƇʌIҕ &hԍפ!E cVH=Ih{*D-GP4M'O=6"%= 4kK)Ί *f\иi DAF!z#Ҿz/8-h&&NJsܘ\rf Hb x,qT]!@Q#zG19HR ! cǡ3lT4@A B!k:O>41DcS z7& EARo\ō2.'a!upAhFzƈ#V4% cȈHѢ 4h56ȯCDhYZpanl1EiHH_т =6$ Eؓ WBա"3BbCJA9mD[$^ШhBrjX=xH0܍OO"F2hn#3Ix" B/(J3&G0sNARmiɁ}LYg16KDm RwJQ.U"&! ,+dAHXC BLjDǢI'V$!zz2F ?=cJS>ED XΆRJ잌IA::ǥ<H#=AjS6ZFэ#! Dz_#gI KV@^"$@@@tzz=Ȥ (I=^82h"iD 3m[08OAM+OܶĹl65y AFPɘr5{TN}C-IIi٪zB31\.), D4LZ>eXވQjKb$}"bd4# IB"IN½)$^lDH^uz?Di%-e=Ob~f_^~ьd h"=1G1D Dja*"Dk 9Ջ. !D i 2F%5z1 hH QƄ)P< t%)+FK"%RCb0y YA4ehэkG@bK*NDM jm\?#Ѹ-+tNj_Tw} IhZR #KHdV$.^FAG:#$Z 1X"gB fգ9"r2HNbP $u!c4z41C`ǥhL/Z?E DֶP/ZVP}F%h,et!}iކ-[% tJXތr\T*DTdco$ZKI3c$p8C$KH!}FbR>!mv$A'E}S F1 vO[gA jm h BPC"Lwϡ9ZVp6; cތ-#VD06<&b%C 3&hB3hSϭؐKƉ~0IPD} }wOI$OE2=/}6HQzя j d(Cy Dv{F{2)$KDBԛlu,#ᰔ!T+T8i yR$ $[&Ez1#CQ"Ǣ}lW /cF#IΏG'I(XHҝI$I#7ZLI[OdbQcA,s) CcbDH#P^Hld;݌If % ؽLέjzmZmQn< hcc eq%/N?46LpC\$cLXƈmDzQ adFBF\ 6$,J$1"+c 1:%WD,S#βAĊ]ADhǕ`"*ĝN40/ЉI#f'3 (mDޕ-Y_AOl~!HНgWoAh#)$1W%k`p~OEl[=.$/KB(1 ]H4G @$$zl%6`fǫaPMW>1ގ^G>b$loA iBbE+CF$BCI&I$pUcc%C"4[Mp$MZu^Rfcf_$OQ&H,wJ3Q0L7+\h`xtqdgo ">!.$1SI&}%3-p BF+#hǣ?Uzަ| D:mBwe/Ft~AQ67[ 0=A%яLmB4T$(N2I'WI H &ՏF!ǥ4<4Tkq4HI'CRn HdV"\uK#H .5!Fަdz?C='z1\ߪdIՔdǽ GdDZR:@MH'h! /- H)N>FrV-8K$/C^FEGX&h.=P](J$Fa d$\ɉOY7##r qQ}l!k$:'Dz!Om%_c"SDoAJ~ WI; lkѰؘBޖC,BS$HWcѽp}pd^B0PHhKE^C$7CseF% bb &} dd@VFH%%R n$bی &H?]4|<4"E1$ l`5Bl,-|r]%(RZb`"4c W"=ȏpXd L )(baB*@c+-`2D,Dhb؉!hCDA C}DR2nN"pd#fG Om) (ζ'geM&d8\)D_q3"b^d x%^Fdh脙aK-arnDE&P"OIё#Bؙ) S)X+B^k?AFLz̏IFp#|CȿIc"/SatvKaI&gu%>Ba4VX5^ƅ%,*&&'Be "Gҏ[2drd3 p tK"+bm+Lw(A0! $!LRyT%Z1Dhv&P b9)/CЂc=EX%C@I$*!9o$h!h+N~=[@زEI$讠^eBB2w {(A(2Z`Ddc ǁ7BY[~kMiXX':Hޅ #Fd_M#I6m&ɑz+р:I:@=%m-0$" $0q#Q XGhBɰ߱CQ(NcwhaAnPp!}Y RІcOѱ!1($dt$-dwA0J[''+fO:6Hސ5db (]dMH HjYQ1ѷoGL=45)lt40jQ"S&GNG s4E&au) ۔1ьڷ kCNxnVb .Qu2 m y @'Bv0Q'>Փ~_Mb $D%N=+VP7'tOJcsJZ΁AT"Ab$a`LLNCކk:RWВb=&qBjD&hŠ&B 2!u4閶Q 66HcB$#-|㛉JUEYhlZn@^ޟxbƒ6*0I:ϥ+@ dcɶ]h!IJmPX:' 5܌L_}-= XM̋1h}* P!rdX$Ȑ6G BF4U8pD HhZn)%NƌB}DA#M zNf&"+FCRY7HqFdHн/HI:8wя4v t5ĉ`9͘R Є2 DHYHI[7a745 +F f ɤ*D(֭qЮp~(]CyJ(!ȂģnF6U1BoEc M^t41Kz=6F̂D\BṊhcPծ"ȈBbzάr,0Ѕ$Bta1FQ1iC  &"VtCՍh Hb3z&#U j6|P8ěO} LˣVĵb<%Qc։KǢ豌: Z2'І!k}S}#HƇ%lGQQ"BdrBFwdM<[2d>$uNU.j- C %$$ lR&!  P\{QCv0"&/Ma lw`XDz9`O@cr"> i-t-V!"4(,EPdR%CdC/n,te7B &-Hh+?3abjᅑmЙ Z"%EwemkL|{Y!R4YxDt-@A-IzXړ$7;l4cjБQlINH{ bJZܢT#DlGFo5 !jDl@UBbdHm-+V9 hjz2Q"dA_وO[ьhdTeG4LQH$~Mhqm-ЂAN[ ,CLe 괟SmEpE<Hю@ֈ__} DqэK@u 4&}5զ$*iCsaջv' 1:r=HV"*9dWbʸPP8FpP'CGC5T Xw8$rHyJ=&64dJ}O%leh VHHPYǚTz'FϪaңv3/?2^_W4/NnhGits<y{kAON_ʱﲸuZ~0]v3 Q4J[i`A{a[: 08\1E|;0sz~y8O6:DMkĔHpn+3 C7RډG<y&>,?R;#*f6(2;6Z8_1eDjCqq6E'۟?b_UCb MK똧9_Z{48,Fk.s ;H.s+"Ib|>n nQ)QD.gވ':+7Nv#`azl~ь8 %FB,Lp=Ğ#~L&-rmD_LS4{]7bY{CSsu4crNR /Mzo6)T߸J݉(lVBbp=Z $SI=qF7֍x,ٛe9[Q4Y(%t">)5@@e~ovޙ{R⯷>L@)?{ l7k-텲.׽Ύ&YÊD7j\<䝆"6ig.7MGx@nTнWg}:9[k׵Pީ NMkA 8$IR Oud*&|91q-L .T|d@Lj}e:4[T'ku`޻Swt;^w5S%+0֑zΠn9>S qA@QNyr7M+t۔AŅ/& [`Xog/Ah[{,ItpВXIۯʭb5{yq8a6Z$, G%Rrq1%+@ o9О҈?)<@^A~rys<q}n qYRkayn>aiKK Zoʹ`iZlWC~ࢋ]¢^OOm =\o#θՎb Q,[Ёgn|;D~v܁[R܆?=7p)C!fZIbM}itK_ t,zP+"\;&ʨ4yؕ+ og}k|\S:Pj%klI`-.f"v' s- M층u61kP֌5cKq]Z>@_4Ծӗ9ys%Ȋ?g3uz/]DN)e_ J@S7ՍkȅvN>_=HKruEcEźO;DžR`?ps73D,:x2TXɀ9~!*)_ޗƛV$m~c/A0]sl-![۟pBlkL p&YH7TRuľK f:Y׋;Gj:dզY'38qa3ҹ6T` naTqDdF,qDJgI!,L׋❖Uiju :\O;"T]̚1Ef6`$ZKȺ40޿yZ;v IB;ҪUo;)VE6T@W'ݩuU< [N^fa]U}v2mZBy<&uT}՟Z~Ԟ/iudt?Cnav0%'CV6@Wv--PkATQ8swPQ\*k96Mr=׏{Ì]޸}I'yF"U4ly0V y{Qg-8Yck 9Sʼn[,S~ΫyY4$"u_ zc `5 ]i5U]]E' U!y{sN!Y.3kfQTͶsBa˔ϱe}WWqm]&gEX~`3&ZZҒOYlNU}L u:8 A_vTox Yex0GLʝUaZuw}G ]y6oОJ~?^^8;ÒPi%z@~*7lέ s&~-Hu{2r􄳺>8"W?BfV3`fьHUxجQYx>#MlumR.R|RJS }e_u_\*_lm7+,u{^!zGqq|N)]x5Hpe-!NC"~h~䓄`2=pDUfLj BKA 1|x1et׳h}J\U `Ԁ^q뜃t4ߎ0_};{RGQi$&-ۖېiohSl33cR6\]n_ Dy7jJQ{ز\ˎ7)`Jqq.j2am'&l iRd1}7 QJrltO疋<ܶ$/t|_Fm͢\L4 08Jߜ\0/'y]=E! c';BH꫾|_8I]55l`t7xEܓus< RQ?WՏf2NV⢋sy}P$\E鸽]jvCef QA=cHYq.5XiJҫ8Hm:cO!ׯm_?m |Z1 UyyVeT94@<ߜSUPaŗqw]@M^91FAs&QwF$'eZm>im[YƑ:Y&xEڏRO Ie@ݽ}fxaQ}lwwƙO:G ?̽}lNADIYrM^c뙤mTAyUxLvPUN&ykmyZބ,1FDLMO{Pf;:{l h(-P!_Rh4[{'d|_G>ǞK <Dn1 1YD7"i9R2?s9׍RUE&[AG^gZN(Ѳef*6ʃToy԰i=N0Ns1 54A}8ư 2GM(i6˿YpݳQXF{(N҇,Y$4|ggdaҝMIlx+{lw*7>qUOFJ0E.Ȑ0BL "dc w/ ,;%LN4 t?mrͅTg.Ճe~Lsx0rl%3;X"I <,1Ꟛ=ӣ 22g`:.ӣedt/&d ]b~u׻120z~`1NvLIU kLL|5ͤ4%3mc*qd>=5Ă=pqEJQa'{]2q;c}MEE[_֓VcziYQytj(齤Y^|m؋Zm"?)U87mOUw\=F\<]NVqF}=Gjl%;TZf#=0semj*;OSdc5iaO;]'m~aqu5`iokh(C#ǖ4g}Qm7RvWaՆl!ޟG/l0"lUyYl 9~_۽qnb{6Q}ihZ9L I y$9gT2+|5F}$tڐyq3-FV\ta>6iD9Q PXag]0g8OoX ݅q?ˑ9knG iě4p!q*_}<m`}ϻz)㿹yK0N 4,73nyy  ()hˬ/9p;{yzi$AWm}UwU%_rXk<@a|gC2YZ<Կ/{4rqlцp$Ezvm?%xQ0h3N "ǠY,U LtŚ0:I2Y4#-1>I@ǢwePO IQ]:.(WYk3$آ>8}z\?P0bԳeaAg73ᨈSˆ8,1B0ZnO 5\}WO;q u׌v=?%-`(vUW~?woݕ]gvQcOUw1"8{-`NmQ=wq'yNô=0 k#z-IT#40Ћ$3BWDC4>yθ׎Ktš~bsW߲4]yk6pWITAQBZ.F9wUgM[M.6}lSƛL+0E wum- Lu6['Nڔ +^ynF8tOC}܁qfW\06% LqU>qU_f[Ǜ<;_f쪫\M5|C0Ӳ7< X]K ߛw:)Â官 Qw3.4z8XW$=Ӷǎ];sZA o}[X>}ky孻}yk2o+vjj1Ͻ?qmS<>7A,2Q6, 9;-.{ci.?B JJyښ_l3AUtcz%JV .=8G-s.uuK@N)bKYx5`5ߦRk8CW;s ¹ JUhQMr/*`,bu*j%eDr.g9SH\Š79Y(PSUmF!7\˶SӨ6yb-}ߴmK>?JaAtO._>? (f"\~Z wޏYfPIF6xd~Aͅ`8=J]as!TEZbن`脱^m=.},崳ìK$C}ߛ.X"0atM;\0?($tj ٤˘) (o>3a^x;G5aF,"-V( _E6Uŗ5RU{.Y;4v&oΛ VPKOxe͜ϰ|0wy7sV@b2-u ޱѷ@rR>8 qRt=?!<˻]PNƾpu >ʞyMCN_JmS2}eN$G 7ȅo`tG8f16N &?)xn|u{LPy2L8䶼58µ_罷Rd @{NiLt[aX8i)aE`TWGVYS_ܓd((҈0./^>社i%B޿ }ӛN=TqW=]Z6$|q%W;yajZs.fc(g[!x0FC(ض>8݅uM˕~ܿ⧐ucþ,Œ0 $Y}xGpj,DhGmRs(=HxiMw-գn&3=aE7wyo`4^ꇸ9OH1jN0K; yUgX02\2UQG 0MY!1Rc;X4#%T0QQ6طNL.r^0O[3kzp;AyvKU.o 2뻈O+rnK8D~^Eā.'?4Lm],pIU2_igwN﹂.C]nN- vbMPsî7$= ^k_ցeCPM3zf,׫nxʶDFC QÞ?l1DTTKO 5)peğUكHg K&ijGp&wR<_St!5{to1|G6KQJQ5:3-qj3xl,?_>&K6۹t 4AOp)Y&E Y&*  m;I-uIoǻCVK!p" 3WZ -aC|S> 78_{۟ ܨc6| an3U䞹/rdRG=^8 %ÊI3^C+}m4M Mw1Z,/|JHz&r 7'2/ӗ 9 fuIGָ!<o:}twQ!$|\1TB6{01 4DI^ d ) "E p2Vix$t~}?aS|@ *Fsxxܼ]9 m7n4nj?fQv0s|jdிa9?mks=qӪL#g-(:<RMĮWuU_by\-#NyuA/Oߨ*@ 꼋P~2f0i?Aǂ܁ Ǚe_iw}5gIV4OIsÅ. z$9sL]-ިhǟy6Q8qG -[ {b4M:g7/U>rх{et(FGIoަʤKd]$ ƞͲxkRl\tGSmN8 u~ gY m%~:hruT B\d1V>Uc=uo ]r#Na4Zd<݆e]~ӕo9M|}7Po{5,0M0[g5Ӗj:Q*|QV￁l1QWZqvӵSB*D7!۝9?2*Iؔr/e\!KɌFqVcTcp9yý=e=0K}\;kNt Cw/efZ+(NdU}gfmt֡uGKMuOA}RڐdEb xl0gĜ'bYkeDX- kZ@nd b/sQd_7ُZ߮ѪEV|^v`*44nƬo(0]ݍ0* I9%XAslKOI^8$ P] e5[TJ|mk.vvsׯ]y ZZ7S8Sin%~AiN534I#āo̽buNKCT>t1,8 ͮΖ@| NrtuH{k/W1L%V[i`/:`|]To2Tm</HWj~APzsCҐ|y4muX`l0 4ܵ>-uy4 8,zٴvxߏUtW]5&sy 3JJ lxbK<Q8\(MAiw6(Yk/͝`ura5)>ox-vd]!>8I衆y]qg Z =ny鬷ڿ7RB5o$ _DyTF{l)鵱M>Y BuBPgMduTeNѤGE qdjZ=%L$pPب%(v 24i$Eu3 7 >em.v_VׯLێyqM :5uw1UE`ܓ͈Si[N~UNV7 !Z]t1uI7Ja]g-2YqqV,}yۋ\FhF6| 'R\ :^9zM%ۿgm`E|QTJ1]oϷG"낔c%S]?Y|vJuǐB I uT[pKJk/רW iExu9\*%oνΑ&R fTwfQpiΉ]#H3XkרlUT돠w0,w}vD7\u\w(~rWyR }6CD%G.Q-Ŕh p_bGdzO{l̚[q_x)$* %~`1gTjHj\/t7G%Xq ]# _ÜhSe/abw~\,? X{>`9!5T׿De>O8}%L?wMM ǃyQAt*ioestBYPA=#rDUE=[AyDqIiַ7IvzVdIQz#Gc U=TufՅ(ȥ×Q;n<7>{uBe>8XJ~QnI'\V2+ume[a-DAd YQ~:qL!m~ Z{0\.wk[$Q qIn̨Κ( ?2cDMW>6Nvzq7;8$7wudR+n?Q/&} 1ȐLߤpþG3]tt)\5YpBd]DiYOqQP{6m> WW|/֌2NӅ,lZ.ׇiG{dxGXKh' J=E!=ys?YtZvSP%[tYtya yFFTq1&65M*-VWʂUYKS/ P9aޝWXcMy= yS$mVMD 6vZ2~-x?"uv k>r TioE-3C}g A$yq1_,0Q:X!O ő3z+/R#i.tC@#YS;y^ڶG~0| ]`Q+f3%Zhd*qV]{Ƶ6%-8ac9^Z_!ҐrY vtk1b ad6:כDݒzx?Q-1J@hyȪZ4L'}V@&g,qz׼=pZmc\N[ 1W?{I°ۇLj9ZqqxRa_)?-@W-1jR1a~$"eo TB"'v$1#_+yߞX ϒk6=&MU܋ \P=Dr=| $s} YBDôz\D~Kf< f]w : )$iaAIz|=&vʾI됉5l)L>_?]3kEaG=9ƽUExuz]y-QsstlBú/D.f"JQ-FA3maY)"ß|5丸 zԑ/JoBqSm wQQ('315i9_;(lf]p s-ӭǎg.wpYX59E?,ȅA8Zݥ:CvsaKlS8 ᇹM_{ȱS#hvC=RـxiEIpVyˁ> Щ*O:PMde__}z3QgT1&-̙IvZ)5ۏ }r"ȡ[dO\vv(K"3[ J̚t$۴Б 'OCDx.ɂTwXyRRYgQS^iLo9Nud?1Y{ z]S" m_s7!8)Hofau4ٟ qH4;lc?k}\L"n@c@|C)RU' 8<8qGib|t_X7d 2h+]$mN񠜠ޫ+OSWIھ៶g&%m!E"ȬS.U$ubL׬KEZ{VB v n,S]D) I1>_g ZU]euMwAa=fK{HAPn?=<)uB~fb>NعBdVv.5߿ˎwwC=#G7Ԅ HM<Qjh^IygNS7&_EvW/|M׮^p0BQt9oaK+`Dۛ4:8LN/!7\4ϼ4Kړ f-6[T ÌrwyG}ZVB ܝCaV~Q@K ۷P}dm+xU $ E`7'PS? 6g=F|l+_ԭduβ e Ƨ~ܵPMIzX?zt5 2=|]%,u MWE{(f~[կ_~ufewwp~!_ ?a49a㕩Ҹ?cz[/4ư]]~ >cq) VxpJ4_Hvq%x)DO6ᣫ_K."կ}P^!U2/EOhߏoDMu9aHXx[ b_!g% u !P] NK_^? ёLU}V20JAvq?i+R\cQ<`/hv1Q֛哳vQ11k?>*]#7_p!LM>9 0$,} v*6R[[*dDt>im'IMvIle2^ anD/Z0ztXO}L>{#|ۇeoljz=gqvbC`jr 'NGw{[Nkd@|$Ca;Ee}S}^EbGn9}VZlD[UY kG.?iV7|ˮ(ˎ~O2iSv=z!b٘ܿ?t̝Jϐ"}>TÌ9ޭ9xVr5ǧH }R2D~:[M+?=g~;j/\EgpMc_uߌ:?;翽..;x4i37%h{^F '6mm2AOK3_τFQ3$q7W} \ڈc8 :*Rͭ]sA`Tg&GT4r޵vۭ| '|*>8, gt}#=ҳS-YH`_6Yt9^:M+VAĘjӝNAĈ3nuqٗ)6.hcb1z+0a&ov &ncy SB3}|GTH댱|ꆦ狀°m:rl[)eyEwۭj'{wZl]_˟8un81kmR^ =82&FLqI͠yZ{V2C2f 5m[&ۡlvix)'";3!DuY` rMP]u }p@]fv~F`gg|ʨoD߽~G8l-9~"QKK>|OtD?x!v@C C U\TQQ}mjik!7m gc^@3Zl;VZ*V6kN]ebO?~ԐZ涩{KeFs2Ru,{SWpY`m&&; Ӈrq7QU}X`F_B(PQ)~VM,C5/v-\{S  V]&oݷ착^.# u}iO@.B7:/uLo< D)[vөXvcۥ~o3CE%2mv]uAz$:`*g$CGjuHu@L]宒؉Ĝ҆s=O҆㿿5y=; xTso\=y7<6Ԓ߻t7m4UؕtFwOq'_MWqZPAd1K8z&/ͷXAזr3mDA}[FMX>r{|IS993Lf%j+֦͵\m<}#3< ])6U[ȦSeyc7+#"2 o3=y$MmiA}q{]U]ESb~o|-:k,9*p+sTᅱ.-7>҆|!̜N/ߝ3{Qp$-^axwEtm;kt8,]d~YUE~p9Gg~=>ayΙ{~;̢]%{??~W^E󪡛d˕QV70 KZUC`(ekdMW<'92:Qw~K;_/Iӎl:5C0Uyw>˿p^Ͱo=Tb|/OUQQ_.l4_>]QFB#$WDjL{bOjcܝl3AVUSztWy_$]1 7ϟ>u!%#ns} "N}߱SE'ҙW]zYVQ4jCO_mY9=bsE:(=_;7-dCm|렮-Vdq?_=Fpuj<eMmSgQg z 2oeڬl2UImeq]5xJ ϰ8nS^Z=sVakztkh78k,o'yRW̷z;ݳriYQ[]eC]簉hѷow;uZOt%]V^g{A+NTjmuI:h(?O v0~V9.şM3=SJOų,x%Hmvo>mCWxaϩ*}BdEKFi/ A_`r֍ɦ©]p40m5, {a߭=3ys:-{>6G6>1eT\QI)UU85]9Js"Quk݆ic)(` C}#8}_ ]? 0 7~8} 8ߏ}A_8aЃ( /ca'"/A8\8r~(߄8|`*1 !Aa0Qq@P?s؛؎ƄX.c.ZGv\EPTؼBƯ%:Nk]=!j'_q=V"5+ў_=AՎb{a0,4%&{xaSwa_5wֆ=bCR/=E<c!$XKCUr6%{R'*z :'%A:2m`&'Y٤3>4f^6$ `EH!8%X˥O^͇Љt.K-To bɇZ `lBt$f)h{(ڰbuR:!"tA.ua. \tR.thv裤*J91ً ѣi¥H: K3J!24$V%m/X!j"mGt{bt;%ؓ :2z" @CHP~B<ӣӏۢbbPl1*[]Т(ЋГjSs.C 3d!0ąOBBebSd*o QY$e{q=mV}RQ-AG{Oy?(k6Stʭ{a؏iWC4/"rxz=#܏rtI[Q= D4A+X]O/IoDSp_T}0Cd Qcgm~//|~ni!Կ؏ W/]#A,G KG`J.ΝkKAR_|QB?b.L]] DШDž.͌n6ɡB `tQI$ H DI$IkXbI$I-bI$,dH$DDI$LI$B*"d?!EOdA6D &QͲ'=7O}al^GC ^GצFfCW%{m4cw ؗCOؕyeU}OO6y؟)h@bGhHS/~3J" + CX{E1l^d3eXC},E;_5egB\Lz6l/bxh>bb/٬mêFbQ=vl)J:.jz8(/C: W'A/,yI!/=ptbv^FHT:Y%{4v-EǑ^dfU:Gƨ&IG0dl l\hMUJ;D 0G/_߳Sq13^,lb|k}l; +ǖ41!o |Z(M5W')h]嶗C_zg:#?ϟ#F-4ZY }a1+'J1_.I!mE97BjCb恚 T;@SlLN l:0 p a ‰ΆX4/yű*4 (G!qax1 B/k򿑌^&mBgMR; 5b(z>BƮ)'d -+8xhLcZ=,m!PCYI5V64 ;}4CUb)]`C:n#CBU'h]$HHt)s0ToyLc_;?'6#xӳ$NJ~E؎qKZD3b25hRu!QFR#Iv:ɉjT:#C:5Zt/b~jM5~ b;H:`zj#k= V;]hA [㭆L+}cBZQ>pE4HlMhl]bKƉh_7#DdiK> td<}=;Ls=ǸLb?ї{y=hU1uML{~[wxHy&2 z66PF I%@ $KtBHFL Ej  B H%: [vD4 IA!I$%0dI$$YjIHA@M2@H'+MA?%u R;Bm/ vy| xhؾt%l$P>!v%đ v_r~ScQ=烷x%O6n%t-0M?$GI4U_DLmJXъTBube$FE.)_nQ<*Uo~_mitS~߱ 7Ge}m'?b_y`^F{ič?ܛ 3aǾ~rg'LaM03GOECKJ!/b}`Oe< DON🌞2(Qދ=هyq{O~/p~D$|i 'iuxctPlckI$A$I$ J$Bi2Y$Ą DƬ dI" RI# BjΤI H%54dA%Xj #$) $D RI$I KI$AAAD|jn%ؖu<>y-iؕy? =}x>>/OЕ<`?v1}_F} Phut p%CFI댞$Go%yg}q"^bbv4b%W_ٮ7+=RzWwoMwbd"o  6%1'+?AN<6Z>ֿ?~GB0sT.^1/Cv[:BMO|Ŏ| .(~3)a 9l>7=1=@j x&͢l3C`&'ͨ єi('Bv,A824nĕPybt<\ 5x!ѿ -ytxjy>*Q)S9;-l)EspGjit]f虙bk\z6/h5Z'-bΌZ{M3íV:򼱱!)812fz wjvܽc@R47~\_zs[0x| B"k:Xa"q C(OyxPo[e)Ku{z7)KqX˞;x*!#_hkA.#i4kh&c=8-A*4&.7A2Oc$.ГJ_͐"foX7~ 㸿A>dX!Ԅ'L, }'wb"1h t!B7_.g6;ݡREI5=6N] #siYBu;ȅ-Y? }T]Li% =┥pN4 Nm aiCBK)lwlCk l}b%mu- nH*Խ#Hpi!M[?OlO)/lfe WFLhkؐҽ ^3 4ĝؒmvtk& ?_c7E5vz fQu&BzmFCpV”)KʍƦ˧c{[_d^FA GRi'Mhlj HZ,}< ykXm#Ktƾ%4tHaD1;leM aJ!IF)Bb QA]HB ѦMB]v=T<y#+좊########(((=(LQEQ]x!:?+S/W^D/'bPLDb(lwp1EQ#d5G~|B|ŖZ0'tn] W|Ce 4.ƺhI-)iw''={/dR/{Dlt-/{K/|4ݡ/^{Oi={Oi=Ǽ{i=K|G/Rq*^S%j~ǰbi~1'Q"ڡC`Xf&A?<!ha(PN)P Iںb"n"I?" |_d~Iq81D& J>ſ]!B!B&!B!B!B!B!B!B!B!B!B!B&!B!B"!DAB甂|w0ja71ン- *+좄e"(_e+&((LQE}X(((MQEQEQEQEQe&++(QYEQEQEQEQEQEQEQEQEQEQEQE[(|PNƙCQ5vSZpл7||+ŞY#xkF 4>F]1$숽 D3X٣ؑxo7=gv6%'+hS? 07D2=쏦/_S=LS>cOd_"_G%bv#B)&uiBg#BOd3?JXčORо&'n SM B= 7J6Bn!t$"C$Sǿo<> }Or{,EeXY|[L.mGOzbNvB>؅|w.7͗0YdJS_ư_ xn VQ44Qk' 6o}/\3EykZN//2͵cb;l_Ǚdǘ=,_|a>76/׼γ{'(upL2TF.K͔C͂w x: pغx?k7<|(|2qb4Ly5,٬;' O9+Ga&v.Ʉu\&<./4!K_;'Df^6L1a .44OcDžḺb|n' a⍌p|&5|42#og_/\GFuZ諹tL"2Oqux"_VgXy# 0N+Y\ ^< 򉞄LN7>J.ذdžBpYY/Vo|kκaae{6_2]$kr0>:>t_-cb.wyx]p|~=fpe.5t,"qƗYn| cC 缢a=|O) f- 6lb'}_Go(38ϟdacExdrxsCk+=4 *2epEMcXذOPXP|R/b5!$>+ ]lyYr,QetCB1bb+Sx|^>/dLlB!k:f:cƊQwxkC|bšDt f hG 3Ɔ1g&OY|()F/&і2`!,^T|oL/)bgM\xQ'y~gصLCYy}q{bq.hBn VOcG'q*=t4S6rt]lxkk< :!PذϜS.7<&&Wl”ѢYWЊ1xG{QEe<>y XW&tsOă!sŅ y&iNZ.V.5gxC<. kN/ &n" m SbO|.o PX%-BwQߜv-3<1a\5. u'9<;^.[^c4_!8yZ)tyL@Ȱxǡ:~ĝЗ?_'XXƻ "0Ca0o} 8xhY{'Cº/)?LN+y²,Aeƕʆ} оEEo,XеDM6"a/(θv$BpC =S/%#M}UuuuBlsоIHCM=?be~e◼>: +4.(xyYrf#fMY;e)JR +xEʅ[Q $=U aIT_Z,WЗ*. _3B0C_YE*,E*)QQQSEDGJپ4x ^[N.(Z'x|.6k$P_C@əJc#+bB28FRp*akbcW aƍ(N|Ępa[.vhN$.tho?8dpB" 菢!%$=O"A=}DA=DDE5=G+zQ=CQ="Y=bOzI=GzQ=GzOQ='}g}gzOA=E`zOI|וc|~y=Ǹ}Ǹ#쏳{̟>Ίq -EgcLѯo;2ƇN $)QDE9R)qJR)JRX)JTFjV4^DL_ x6PBPZRb \L86JBp} -^>8(EIо1=L;—}%xvt;)qfȿ#s)aJR6CB=ݔ>q?c^ I#<64_g81#<10l&΄͝v;P^cs^ۡī+%~F[_bwɯ%e~ R I&VV Λe! ReY)K$:Io22gg!\kHO퉖%΅=,7,S'ߒBN=d,ILHz6Hod Ӵ8be,lP;:KB5 ENȩFc+o?#hJ>Z}==`mxкv5}$2(1;p L l'R17OC1cy?_elR:%}НС7lv+ T tLftAgCf 1hvv=(h 4VplnH1b 2 Yy:ص HB)= Il]FX!H!l( ذBtdZ&ى!LM(OZa1.\"QGx5na|#j!$> ;&!ِ!%D $H4B<"RffbR2͓wVFrqf b,, + Щv%Z&2bWGL$B!15HA$t2%DB hCDB D3BetWlt$B!B& =(&;B 6tAD'qX-S  vRa L>O,x!8 !2zIB&!#:;!0BD!BR)!B!2:zn {IvOb! BaB>!~@N6C}COeBwZX^<^>GQODDRqJM!|s 1Befzk+ؘGS0X߃U%ƱGذ[^ %”oh,LNS a}{\6<..&A$D%~ Y|1!sHc]KOdN>o'BN hX\!8L\.72;n_8jm[}h}/gf(7Iбs2ar|_#L"WB 'IN+'Gbzvk'LDl'8<}wlw[\| }'keXD'> +xoDNȰ q<" ;j'z:ܯq/62!frk,5T,/0 _H,jMWteYɗ+呲&!2ЛEX\XBca I/&%N/\uVxN{NA!* 򸼾ĝ4D[d׃$v.E"LO\[)p͚dBhg^ą:NurbҌ\bi4Dw\gT> Qys Y׋OuOC>YEڤ,N`i @|^p41zĈBm4CJe,TP 7w!;o}!S,1t.'<467\sKbQ7!{3xDW~J\wͶ,fWؔ0EXbWKsb}bSFE^/xn|1(A1p[?CDxB?Fk IDS]|w_)[KGAF]t$Xu雟JDgx+B!><1ᡓ_>gBqybhb =b~D(S,>+A~Kh|_5Op_\[_6K+4ba®?~}xK.. 3PL^~??'5pϊ_B㼼t% 4]}#jR\ZL/O־+d'&Ee/p9:qҊ~>UeAalbu-Ǐ_8L?Kƍ;+R}v&J^k KD d˕W$S͠w\\zf.ic|^`s87f"_x NdzdN }!2UtO1KϿ">R D脾 <\:?Eʗn GPy%(@eepSH-pb7~|Sc4 PA'؝po:ae*\Da~F.V@ˈ% jvikɄ/jxĵ3]>LnؔWȗȒFRNmJtV! A -$ DT\Д/6./:F!΄(%1zjĽM yhZ 5> _+О ͈K pJ+'_%ɐ**Ŕ7=mشTlM7Q:^~M<4o|P>O zc xBϟȇ<5>Yc/?c=riǬn+? ±O}3g-(;tLL/NT1DL.}[4UkxUlOd$c^},臗=NzA=ˈNKJA< rp(j\ ]=%Ăz}|^a酌yM2#lmM Qt1a Nx Q/ja>:$1拜-EoqmBƌx}G-tLml#ĜQZBB~~&?%%bc-j^Z_8b! fqMTiV+?NOX4V.~w87pD C DhKMa9">/ R? ց ??,"a{&袭1'\"6$>(t]#L]M4cXzÿPLev;?"}amzel}=߁LKl·(T×88j~?85_4hbNO4 P?(~w-((h[6~wFpBqWFm5(Ni;7~ٳAh$!l0zCX\b8ALBN (*lw7+eT\"c:NYBhj18'BB^$‡sBpBXz~ЎL>SnͺǎT?WsN}>$Hš@%Ǩy^ZZ# ­P7$A2 4>gTjx}W1,,,=\ ȉ*ȘhUbd0LlCQ68 &hjK҅,=?CApz(k:"xd! cT?= WAikk Z|x(kA)\18flA#|T 8a)p$q pSLLmC 34 M3gУF5F_785;aDqF +\>\WpG. T`*B >m%hIǡűS)z)FqvsjmX\.E!/)f jXhMEi3>X^Grl8hc& C^MxZx}1=4k sw7|(hiGe;5 }4ةpN_=Ta?ǸАJ@uf?iA'ƆF%\"HCn zR4K#I. MJ6rr4Mx6ɤvѬ-gmЕBy cbvc?qGhXŇ+\ƬD(6{[xyBZ1#]L66(4qflJ> 56쿴sbgP>y dix"$k><wg!*cl&Ʊ5r4!{E"%Q~rrK-^ihPgi 'I5Kl~v5S*;16-odACy~t{BQ57|Z"~qxG3,@yw=XDM'{c|#(f !T5p_AlFǃ*7# aa G$נ|Ug' yѐ|gDzyD:ӻxSYTBʗ,-Mr񏐸yB6'//? ¡q:y>?==#?f4BW~s1?߃:/#cNĜY[VlQ7ܯ?Oǒ[?Ap$QF'_K߸=￱￱￱}>O]; ^DTV \<&V#S,NYnn3CwIɍAB$ "'2%)v-5Zl)Z(D/DMǴ{HQRǁXŤ7vG|?Ba;7K Q Qʼn pyNxO|b66:=p|d}!8HsyѷBspSW ,LM\fÌ/_b0&VDy}[#geai ŋRAelzB;, Kry6Ϳ8'N–Y9 &LQ0͓ҟ%F+rA!Q<44[)Mn:Uq#4iIh8eG-e}Pnb\ s_8ZFAo6XdlNY[Gp&khkᚎzʺ!x~Z!K(cFmqg7\5(ݏo:߳4yЉp0?Z _0b9KsÃ\6&??5'£ Rf 9KXBLe$?|%+'dd9AeD!ڈrZ|oǎk?e#DȦ) b"xIL)cX45cdZQlD-G:lzFߑ8ɻ5bAy&},nͣӊIy9L#=9%e^1o??~pj hXq aVshvcEho+p?N u#cq cRIQ\,a1 7qp^7v&ZɾBkUfeslc_-zwlbjkد8&?*͌_я {ųhVxJ0jbG7 Ūn͟8*1q|%- 1W4w4h7? -Ӄ8I8qH4>Q>N $5oңHk)\1!hE]M5YFCFD#]zDm x;1}̵Išld'GD;a&45CAXB h6!Fǰl>CXOFlE%ϓ# .gWIngZ74~v AF|[AqNX#pr@\Sq??d7+ KեAJ\N"z.D8!zc4kBRRN& !I L.5PW0Q)iahOtf[Q7%h`'{hLc#GD",/!QkcЭn!Mp$#T"?9qSzPiw8' K8' S&" zmK!w:5`{{$2[w#{'4vv==hH8"#h==Gnȕ:-Qq'p?'k/ oj cl ϶8I }˰A(1 _/!Q4cSN,Q1!^(zLc>q!1}FljqO5 ?Bk/)5L 5ѠJ>m4 Sq7!3_6-Б %b կ0cT2SK'xaZD/R剷Cv(F$$5 >Z7ͳk)/B[bWlѬ1i5FgssoKD34Ѩ5؄8l4!#z0.XntDs5J4mB3rAEM1 ni1?,lS}kg N*k7ZahBFQz蘝-'P1%OC6~;٧ű>?(\3y_LƑ cuѨ:DQc>?J|1r'o798|>Q Ctpo2a ]e#X"&YDƯ(}L!2zX~ET7qr67 B G1Hu> )`؅'Bfhg1C6~3!9QhbNv!4a8i&j'"Iթx412㕃Png2$Ayo41 hXbi+n11F 8H2x3y>MǛ[>"~{"}$=I/{Bv]N qQ1!_#m4lxq_) ]cB~CQcOAI؆˃w/4bۚƅ܉ }9wEZ!(c~Vb)EN<{/_B_1~/OEc3؉&]/ Tk(!zpbסU E ͔LX߹X)'E(mp*pYE౻7!EdogqAXQcgL(/NP-&eYES",c QE:+_|O'<{"#/_( Dx~F$="bJ)G Wh4ӌQS> |hO5ع);f!B!ЛF{V!BR 49"`!v7Ƙ QdcP;@\*R ?shsq7_ oqSbܮnɢJS2r;)]gA]N?=fGlb^dgx#(6d)CT;)pQ be BeoCL4~{d{{ D"D''Ȝ81L`^`cV;Lľ'>&{d#=BþBlR;}%(ѨB}_cؿo؟aH,6%Đip~OauMŭ&@i(:4t/ 6L5fq'~u?*{T~Um GN=[fӬg J2WZ ^L.1BbE (,tlYX)bn͚@3xL`MEV ,n6.袆Ţw ((lQEP8&EYe ",hleQeeYEQEQEQEgAP#/BI螰{^Ϲ )?WHhc1r䲻=Bzx# JaDWb|x>E^"|Oȕv>ߟR@[D&/`=Hg?AF'?n`& |H}G n`pIТ=||OG;Ҷ r|߸ >9īƾ&號L-9C$hm{zGsl^Q/@h4q j9ҍZ䂡TO61bҽߝ[= ~{K?U㤔f=/#Ip:(HHrNlcq q= 5Е]ƽRrIq|w+6Q121-/r z9s<4l~G{?B DX^;!(=/dQd{ca|`={={'_{#G=0/ľ'/ atql{c=|P qG{cǴl{b5lP8$ǶG֧b={#cOCPm^&`^!m؊M/]`$z9iC8XrƓ<\N.G aRCCV,?EX)JR)JR)JR)JR)JR)KR)J\\R)sJ\R)JR)JR)JR)JR)JR)JR)JR/7Ҭ}u^|!8rYLȪM VV.E](E "DE/^B" "/DD    A          " DAG#zpj<҉88)q9OȄqr.`+zfE'J 2'I7;-6"J'L+tҗo+ׁpdgu"h)2t<^6Nu?~p}нiQ&$ɛQHO'$?B /]eth/C'x$D>H#䄽ؼOt|>P٧d'>O{>ѦGG]/>?˓t_G\+76"//Bs'=Aբb(eQ) <>E'Щ3WJy' քk9cFS|$ /7%Ӭ^w&&-]:/򞸎^(.=4!QO/bL\l,cQr>7Ұĺ;_J0BMt.:pN-P ?_}0dƱtCO0z ,EtيlO(Lx@,JwYߡBszKCƱ]+zK; >B! R:B-\vN飞;'7DX߫3 t뙂'B䄨xE1$"c 5ֆKN) a _WwLxoM<'L 1b OBt>RHh5"t\,N/[5{^>EuAyI0%r!` \o3::qɲ}*!>bCYZBzQв:ߥ Xb cF(/}ނ}kfz3/NI "[:vl=h~6.2 1z ԅ{A !}Ⱦrma=$JW{B/Uf^^kX1 }SFOA&) TоbL|-Cf! aakF.F2}a嬷$5-ea K!E# Ж=R(0C4^ zs XYJf7z2\\v'OB ^CNv=,ؙkK;cVV4ocFGxSr>"uh=f`9&d҉>k|aጢaK}8~Ld%P ƉB ECWD9<~bq8c! ;ٮUV :51OKt#bI: (#$)e<># w"n$C'E$E^ A9а>tB55z}O;KB⏪lTB(CtB$!*!_1w+}+-r)!$\QNZQ&Ăď/__:Qxa}X}Z>^t,,.RN]&ĈX*F%"IlJH1yD< ';G QB7)QN'ב2etQ1".tboe1(d}(ѲzCRB.ZCP[F\JF A,2m:6bT(Q_  R !zs=V}Oa>kaUrrDc$ . ǰk'x!XXFGȴ\_Ky}q%:7tM+~2f6 x7Dpf1< y"H-EB;ɳiǙЎz!a L`Se'WVw؅W'NѼ#^JTm}528rXE ӰM˲'eNP[JFlF9啇Ϯb+f_oA^עFϦezae eq^d (7.6i\c"-(7pnT~&:NtBѢ>~cώ}k_D3ae + J&jƏqCh`>M1-v344Rb,uCb8]G9ϢcyoV)zlBhp$e=>KK^X<,#⮅LNߥ3L?ƏͣƎ9x4s#\k5H$2?G¡ I6A<``N{d; 7{GS< ρ< =6N"OAt!c 2|Am!ģԉDQNDju1_E}"aap^f&gD&8!LΨ/N ӘX]k)W;rp͗ #,L4,q0#+ϗr1&lk[yD5=/">ex+^G#X\tN҅螺V1#!Z LDeL%fhE(DS(BƎG&[;to4"!=Bjw&!1غЖ:OE 2t/RuζKaDH|2Cc& e6hL^AIC䂅X&%a ЅbA2e)rJ( bbG_('R{N{Wf{{Ǽ{Ǽ_=yxpxx'{Ǽ/1/!Ǽ{Ǽ$w==='^s'ز*KN'Yx(Wx+^|#>'G| `4 t54%;dAi>Cɇ\/JTGXبnD,5FBRS Gq01:a1B &!2219bvQg{G_7[E(e}H3, 2 b\R,]zn𱊟y)Ecd \<޴cDǔ#R+< D<4^6V+^ (LV4YEL(S/aHf7XNfQE&(L' !*8D $^]EF|1K%5?e6X%N7Cl('ex+EQEx+E,', \R qx\ v/BgcVI$IDև'|!֏66# #+R0䒄F@k8YKnqtH't$o";*&&(R , pXRaDĨXRۜSce.Vaph>J S rQ RDhJ~;H<{b#Y!$H/CаX>͖p}H$\$E?R\1W&:iteIFrc\\R=JR(.LcJ!8!L6< c͏cV\)KȞE^F )\)D˒O%(؅ 1r(MO JRLiBtOA * D&}7 L:A!ak|fbJRA |┥)JRh)Yi`ҔT\(%hADE:ńR)JR(FiABRj/"c| RR Q2)D[ OӁ&R| OȐn Ҕ R]k 4y|̉K(-O/d(R(S(M)JRد%)qJ:/`<┥)JR)JR4)JRLQ>E:(WYD%l)JR()aK˼AJSSs {*i:J&R)K8؞'hdE./ hSlRkJRNe(#^r4&!"w)JR)JRR)KQbD5Y~,cGsJRO6. w.(h&>Re(hs',E\1 CcQQ-Q&XV2≊ txUbC)J\\R)JRB]yhoˏs1r>J^ RҔ()Dl7%)JR()Dt+/Њ"loĨRC8$fx)E5OHI- u.DD7Np7J\hƚc)KD rL?.I>QzӅ9(\SNKN;i=tP7}О4m}i- )K `l\ri=265{DMM[m"1>HcN5ԕ'CG?\S1$ƞsFSS(n Rn0JOAtl87}Ue6p(OЪ< [0Hl)2e = xL 3M'RVlDn'#xCe\.8>l$Z1I+ٹ8hc`#JEX׆5%Tj\hЗR8yb1s] 418>yCO>#'E} e `k sEFؕ<>?Fh1"!%GvH$\ m9ƈ"+ !ؗ_Xd5ҊoS htkbTb&[ɻ| aAe\.Z'#1B6N9(c/JTbDJ|5x"CңkFrLmؽjB ~FH7}Nέ  Cl.9f HxЇsG^D .Ba*1.h8Мbip0Q\>LB1(N! z</B1BPד!PשJn TqPELB D.G.{xpƦlPPo+)Y ^'E=l^cB ʅ0בJ>?]8rC& hna>;pBcr:,/BNlQb p>! ptoa2f'|%*4&Z-LOEa \BqeEDAIMؓɪW I%3 C_L<" 9j(қCnƺP4h|Hh#8V'O 'eWPxQv<\1.԰rDi qCo nx>JA(:*/L1bJļ R;V^֖#b(l`>a2cXHH\ J}tCt\Q \L*" . )R>A2th78D?w;>!7%#b}'\ U8*8'Do+kbL9SD&ݍEpRQ|bJ"8[xXqE\\XogTnpֹXn}DtB^NP_ENR#5h4)I:*Zpk=f87WṲ萄T|} qy| A:OE:B'~s ;9@71еiq8 *>̣6D􈹽T/MVI!")At.>++/zc{ 礆E2S/ք D[谸zWXCd%t#RyDVMFAPaA!G%c\I>WM4sE5oi=tL/U!"uE'L hCU1t,o~B{cTǑćطT}Txi\Qp&)pTvgJcf0~{긝3 H%Ȁs98!= FǦ2Z7Խ6(LNO$B}:4VOa0}Hى8XI_NLk(D= c.4b|p>K!и&=i ,k3Lh~} :(_Zh\.X_EPtDi ="A#ziq,|ƧܸfbB` HJ#Y}(S.X&!,,<:EmΏS#upTn"UHCGPcߡ&6.5І91;?׭ (p|apJjFSE9h^C¢pi*.T&BcL.8YHyDlOeJL})>F=z4lbv8 RMa(5%5)zR RFeo (C;s7VFE M=0RDttkca*ED^0#NCg4cpxuyobylҋ!O/zf& JB5 \1!NCmxY剧1B ̦(-t- D~](UE-cZ!C_Z}(c2A]H]y}H}H{V+h$(Bufܯ W9AS3L}]<@k/7Ŋ P@Z9%fJ80k3mU) C-m^j:rR*_rW ~%T-hQк`4U01`-efyx#@:ĬƮ|p5KA3f"IxF-ܲ;Ry2zE MVb #ʖb#1O=J2\+@u[a.Q) D1$e%˕aGgL;]~캻G:ϼs_ʹpƩ0s=ϖ_چUZYNa17Tlf%0~&F.&"Vƙo peᎾU#>z{+@w]j+ˊDfr2)t†ff[) `bY()ZyUHuXk;+\=)JT1n3,i 1*-|YU85 ._w/c". @Z*rޥUyH Db~]M2nbʠR`JY |J qU>B{- HhEB4p?pUZ !=m RSb\U>(}RµEJT|A| ͵''y}\Ժ粘W$ .@3^"[Uy4{~~V.VR)Utmp/(P9~9̮e}*', k)R~B9m1{ԍ~AMeKե ˎqH m +3*@jr1P2xe?owN.m6~ 6/awvP%QحCW2Y[dkm2T0ԣeMT..c:'NE+V? RҔ𩏻yKh3f噽 A1xt/"{`Tq.#(C9<T_Ҏtl^^,pMN7q^8|)P ex=we+Ȕ YY=7Zu-nC<]BӔj`Eef @* %LAE 8x!Z @p+r,xAZzˊio]GqoQc̪5;6C`.4d XgĴ@`D3ZA~Æ ܷt!r 0rTp|(˓_jUڿ:?X7fxCsW}ʖf3LGer!N~&UE}Dj7Fn?u B*,u*m|IJ tnIm_+ķƑ==PJ~W!3Y9R/#䬴Ry%0֯LEǗ:г~ ?Jf ͂Ika+[\+2g`%:\[W!]p?1U>Q\F\(6W[FPP9 j-!0eY^1_07GX /R%5Ng@A!kt|a 'S|G1AQs(2wj Tǁ,N5a^``Pu uiD~0pq wƸX KIqW3l\(e8u(A<p9F&"]N%E:(@ȗTi(A00 TtJՌk4>bCh.Q| y,tbq±.!X0@2 RVP\wؖlǙLj]lXP`ߥ˖`y.Ga}J]= 6^ Y/1qͥU77R\]K=Ve2|"Z~SSo\@\ )NLCQf}@" Bc{q_RDixܵsnh '&{![2-\@(ɬq()X7/ݻwAe.mJ/`w(5b3t=Rr*~"Xc6R̬y5B~ /;A1a'=*a0Kf%ARs}iOK"k?u6-JF궾~)J'j* jd(oU/_EЕ+;ayd+֗'mi p۸#KDtW4H,+^J/*h}+/)f?Q_AB]S{t֥\P֦#SAn87`W#͉\Z\Y?iKuv<`рߋ ϴ` aH9y CxU.mqE6rE?aWP+?lN[`682kYqe&𛲏/u1Z~44D(ho ơd[i-)IW7$5ng@_pCݥ9ߴށYnvBYLAY)Q[%N{.ϸzE@9.^ΞHX*kp Ns8N~B~?tr{b6^ϬA: 5eQU,ˇ_(oPƵ(_K6>؊).P~rt׎4;%0u͇*EfS^⨯(z%<7qAj+TUY*6pQ0 ߩP~r}J4ܳS}L)+[i2 ydDD5C)r[r*a,f7(6fFXmFYAVr & J TAcQn 00=اZg7(HeImZo:hrJ+CY"fӭ@b~'uJs7W԰oF :o"]W2j$(s}DJgϙOYۈ=?JQ dCgwuL| UD(:!_th(|Ej' @t8O$c-H[ˋHhR*')AViW$i#<1ȣmgC#dqPQa\v37xҶFZ؅Ի p S޴ {@x.8 W4#/e.X̳xpA@/>[4$b-Ȱ?0bBBQHw#&3p+HMY Vau9V\HŘfʷ)PC @-'DSvBTo0V2HC?mViȸ¤0&@= nZ#?*Q||L;v{ZiNVhZg j]Iw*bAq.p~ynsHTY'D4c~-QhR<30\aU屴k8vtkDx$Y $«SA2"lկ̱Ӆr`+W_Y8F$x-*_pV%l{؏L<ũqvU _aUCk IH Q/gP *_RPQF/`@`Pc3`cZS;=!4#d ,TnwXX0 ;qhh)ULK(;|lṷK[ rZjf6~oZ/oC V5.`E_,bQD lphF?2h%\ã 7lƯqEZXp;32i"--?kQϏ1R9t.4+4a5&LXpQ}Q`LӯPljmqQ{@ %i ./> *n oSX%nha"5-QB5p8Fw\Uao+Zap`W-P1jN <>Ҋ%Ibct>*!m,f1o)NGrV2VewX7q ͫT |4*0VVVd3=g>93<^H/.G ed,niQ١-5Վj!j?y1+`,n,&K>Z/rbn혩" VoXݡ^>bt9abvm* |J4fKmpWpA*K73!*>$QHK׷ F0>hrQ&⢤gaf ~aPΝ+{||C+2V y ޢl ,EF@AVH:-HPv]E|N!<. (]LwrN x!&/[o-ziTu/a˙D3),|b*&oǴ[W09+ˡe kjcܰpEQ#ͼVX;*VZ%{5#9B e VLG`J/QYX߼vou^Un f:l^cLiZTH7}%u*{@%yR|"jszHJ/y&X?h23R0XTtUv Jݎks`U$Y ͸զe 4p/>%*a xe=23{8Դ nUh͖fq*-h҂p eY~ZKˑ12Zx|’VYYߴW"Bۚ.4]0S Kc soA3UkUfٸ2]&5Kj1 ; ,5XFwFYD\(64X"4FV[_nX7?ym*)N D"Q| >|>ҙ]-_Qh?eў9Bc-}X[%)qPkbFgf7I1'=B:XkkS2 B~.laȕƞ9:ۍ3h3e:Zl%]C(rڹ^di?P8&&%t aC;FEgBxmq 5ʦ| GrۚU @ѭFR5+\[W}:h>%ɥ9l?P/N]K,*CЍLɂaN8,: ZB|ljJ! ,k[mb[Tr+dWjd 5+2b)YRfmq``D Dt:F:oh=5Jl+; ( '>,Ao`.A4b4~Qm^#E  &MƭGQ9l0()Ԥp R1o2|w:TRXSpC&v2lѸ2tPw 2%(HJ TBQsUẢ]:!mLEP&(5'֭CcLp8ԥN߼\W`L]LұM:,!L'Q_2. %`Z(q bMjue@5U1o ECFsL?n9̔ËB ^Xm*.e5Pyݰmc/#1-rVn-^| flV@f>,0&X/6s [Ux8G"gxe?!^j2吂 N_"lmg"9Æ 'VaE*; A6$&Ȫ1-d?0ԥTP Y jhyc zn?L#-v*e5u,[Tqĥ8U) dPO~lQܶ0rlb*q^])r2Ub9QUFPYrVC_5S=Ⴖ@+̥ŏDjfr=1s][Ms4Thm0W8yJn8LlSU׈A(eCJ1@ zZFbOGrMɁ+CHl$0L㟘Չ-4PB Z1d kBYJ(4sc7f0ScjK57銠1lqE r0 u@̷ϼG,*1.|W^5~dj7`Z8e:/-7OZ(?3lbVVC T1o? d%*ː\ <+8K(OEQAcY"Ppot )(-K=@p̢⛫07M4is"2&" Ca/_x-&cu5<TQJi`*vi}5P |FVk9vZ##)7 S a-o[\Jr5Fg0Y # (Za+0 =D%qCޚr؋oCUFв{#`#lM'nd8 /2J=lFIeP &&]c9=pC/ULJ/̰A|hVRX_2V:Yw<@or18#i̻da5<=ך{TL[.I*j1~-GhKjWXk9tfYB`Hk0T_yBNxRW*Bcӟo;81/l^Zե}Y!,}ְWF np@ yek"E.eUe[״%]ׅ &qP7c e,Q.Q?L0[USxSnEn+c@j?-*2(آ'60&e3T/`kb D ׉P"nxYe8Apz"S}\ pJ7 @B0 yB*ҽѢSܫ&{vj.w;*I2Rb,9Pxr`ϼ/*qWw)**+@!"f 82B"lYΊUA*x)Iwy`.)ܠX&0Cg"x5߼ f%\H QhrolMT\,:S4RGDZW(Y˩J5DApW.Kx{w(LH ( ts x B a)'XrsJ 8TE(ZcwȔ-~g.njTXogn/u-j3oƺe[\TqH@%jT´+-qTs@_U501ox"??p&s"Vj2pRUTrbNCwet@ţ7pVGCjZ)]ۚ3 eb4ȆG->!Aҁ@#q~#,{ \d+ yq ,J k%.\\fHȼ% nP YPxl)Vry`<{^ukJ`4bt28k7N![iX#CB=9sh"bW%%.w ,g,6q ϋo PRUhej  _SaXbPVȜT UMƊY(QP)x 0HeRy!4Iq71mtJ9F! )03o2E3s `7p5 5zY8o TZYĮ-uhAK Uӱ.ݸ$.te0{(cQ_)zx)_tZ BVkQkQ^M[8JDJP0X:,&s ,fSj-ctFOxqY3`Pqp>];}9b)Ѩm O!^=JR͛yiw H-2ΕQGVL5>߷ys-"HBqH@MCh%ete[nF>jA7jT697)pS~K1&E;WqVn9`)m#ٷ T1e`f;(S#WlRZEb*Ule%EN!76aPA+%0&X :`b61U0{梵$?> eBY!Mۜ_RT? 6yUa?_9Դpy*ayGKb\t!30G-9M{dYELX`)H,np C˚W*&Z\`` 쬬_$K@GALr6߱#z}0Z|o]KQX a.TO0U7܊*-rf`q/xpG `E2)4kx XBUDzP0cSLHmqR  !8ZQ)5RA10f% X.Pkn;kைƜ-HˁV⫊QcDN.g ^|i`paF!AVG\"<1.j+} l{3< \>I Z7{0,oWu. L\T-wRaXbaEh1%3^bh J9saJ&b  9%rh40.)1bBgLpE滲觭^r1 JqGUyNP1S,` .@M9jKM4i9"cXط2oj؞ߘ 7QULo̪UkJJ7c>ظ,@q2xJ쪫X)+[x{.,+w*鱘Q0 3W!E/k{P G࿩w_hXZnqkRVp7;6`Re3 *2=~C`м*hN@Q*1ig2<iv,Tk&^i{S !H{|DT}.R$@/VL2gQ8 %q7\ 7_eJ,=ƑX2MbMʙPv?Q[K 2{c DE$X$/&\sE2@VݟRM)QyW-ylon@RU Eخ,qK}B/Su~BKu1\ x)fyQqGpp^_- 98{ߴui" >iP>G;a+y4qwv4y|nNNU;% nTE!X7p#4+ &(֞ -G2`ǞO@rwAf-ոh ھOn.IZECRr斩+kgm  !n5ldwiVb"1ãkpi.OM, ]+7v`i.]v%5Վ)ߴ@5p`@ w12j@e1P6Ʊ k,<\B -orIV?*XtaHP嶺9A"bj\U,UkKYa / al/ \S-ݳ7`hk0>cGLYPEMA-6jm odYwF̬}gڮ0j0t7L*uk2p-暯P ]B-ʞfS{48Qa9k5fkj^"Y%vb;ˊ[ۜa(bܠ% 2Y&&TDfU'<L!7(ᩨZ-DTdb'q<]߈)hAaʦ^V{zX xa3qf D`yZ@%^34!ĵ*˫ 0aPq)3ʢ1YX CCMwm7BO< 6Ug5H-;a /ܹ0g34`]eX7 +Wގ2M&^)\/rJ°7"U[.Vѡm4V^~e0*e 5 xfY*ݳ̢0%f+PtP> pjf=L[q)kImDBmKT keDFGH—G JN4+ѵmFdTMtc*xvbҨ+h"*7EԶos_ xܶԟ.iK]@kv4ڡhFE.!I!{?G `7pj6Pe66p"Sc5|K,QU Tg _ 2ȏ)ZeaP+>_Nc05zJuґԄ䱃Pc8#Gi30UB%]!ZiOVǎ"I .&Q[0q=bV*uto GW&tlե{bN h:K0n;3hDUF偊̢c:mL'C 47n 4dzeعw Įsa21RF53И_(6ؚofR4RT_-F hhX^`UC `Sf%X;R=@^ 0YW:_4TvVNZ6Wqɞ9!jVFY͛'<8an25s%ҺWyqXeXg.q80d6ӲUTxDc R/qTn^-ͅqBGL6(2~[e'lI)μ\P V^OLj\ /\?PkЋw^ Qib[[Jx|BM9eE/ӝaH>[ojm) X 3+XDhKuB&:,E8#`~VR P3^+" YUy!2Wkgݨ}qt .k :.MgYc:_3u 1t.yoAVCy|Jhb/}k1nZC)mѿcƫ ryYk o7d*v L4Ȼ"S)Ѐh BSdb04++RV0T$o`A.ZJi}ڍգԵP-ͨ|- ֎YylCgeRbjn+T~lSY *-(_7jJ̳>>#aP:Ʊo*I^UaE\qH JJf80%2fKU n:W\PFҽEVkLq/HA8䍭 /97KumZ<@<8KH@IBa#QaE^rL?ffdġĊ,xZp" <*\Tpߎ"d|*,RXfڜi_EGQ쎖gEc@XZ,LVB-c@ e8Јrj8ijw\WxV N \nBYWrU\ g#vC64[C}2Z-]۞`Q#"*qJ9DZ`FXڇ#S~Эz3  [Q-{RTȀPtry³ e1u_~dP_e,[{Cf=-h0@^]K빙mW4; +fp>08Yoxu{i \8%X#l*|.m5~C>&%9߉D[Jdh E#YW [ @.,x&xy6^Aj!9iTbB yvP^~Uu̾~ck^boCn.b*Q E]3fGUV!U60MbZ MŴ-Ź"U=$<Q.( OqRYdDbH\~݆d&ȥ,bQ 1n*5iƥQXKᢲ~9!QwS=Bߛ;~t";yÉzb5-}>7h [eg?fNRhȼ $ H036͔ _yT7KMp,Ǵn~+BQ#5,ZLZ(ҵ>vnolQf2ՑxgYVW[75.xPS\t^ NAnɏ&R±h2W*cM&^Y|LTxQpsi4i3S{il_׽2, !U3 (Fjv@E*kDِo fƽ wm7Q;.+A)dz@j()] X;aWMDÚو5KaTCq4؟nj֟ZnmP͖wľX "!s=E3PGxv}7J-yHR__PkHJK/ <e z^ ,q,n-.JŠ@v@F34v;uc- XB:5cV){̀w)m,h0pb!m1xHˆ^gq0=:z; .1IHľ$HUhPeDԬx%^, *Fw xƽ10q kZޱPw _*\m{Zj{\)+pmdS{G›`BU=>`#ElKV1x#XS2 +b<ܩP1K,_S *qy-kQ(VCuYQz1cGeM[FqB?;#y|^P9^K._=){?ܵZ?^'H빂w3(Phmdr$n ڕq"Zm/(# ^Őś*q%^zZUVꀗc]M[n~%Ҋ /<yO xwLj:Z)WƻHxi hb[\uQ o{ɻZ^3iE"ܪGŽ/V@x>|\l[?5e@VR}2ɿxRm'RMPre[hƵ`-iZ 0Y{I˶y+ LȣN`6aAlØE- 6 # wLJ-J:[*]3-._\A6 f&kjcϨcWm2YrE^{|+jh0D1FZc0\D.a1:HV9BFa4M=1bB+ψ W5QZs] 6U1el; ĵR me9ye_1kRMVx}A s58U7P.3XѤL0 ].nc5wQV/4eY[KKm0i2]ɘF9sMÇMiX1 9Qӄ-9s*<{@-Z Uı 6?R@ @( Am"~%#⨮Xӆı%)DM ; vc8eLۅu,0pw *"]b--nkThX wn߉UhQcJh@W)y9\0 r[*p c;Ͱ8&PT穎,QP6ɔ`UDzVw |MIdiTP2O&u3!y2^feōk^,S uJ_ [6:/°YFB6mQ "0X\ T^gDIslH-Ͻ(c޻KU Ī-xw 1)f5—gM^vd? ^Ol{/Pm{ 3-{ZCqNCΠZ>S,=j{mnfh \=ʨWPdY֣kJS$Um {W& WEG VW-W7K-s2"8. *7ܪm_^X;O*`q~#<^WO^i-gs]mLuZ);H+t `ЁES_+(GYZ J{E.KYK." p(d5̶㊌X9&*6}-2ed}Řעg?d7[(1])zT [1}"4}Kkf`JNbT{= Ph3Ǐf6{s}q& # so@,͕ 4i$Y,"4inn(JhAX::oŊRPbtF ꯗrޑg"FbR Z yF% W >b@<@Lp.VH{Kg\KG[6 VTkSu=bDX(\Ou]F-F *9EOX\vL6Z7X2^"ceℷ]\̱0<8dFb6h̠cP6evş*Ɠ#+D3V-$b3L\ 0\LQz5WzyO{ISDG"5gNNQ,W9`R㝯 Ye{jW2ḊZj9e$2^T8}t5S9$sy]Ƅ7m97QT.9ۈJӦ .k5xJi> [&UPP3<1XSm5`/`dn.BPM*Nwׯ+E˥\޵,Q(KLԪ/ wȹψO*l5+o-mԱK82j(kZ.UB󯚇.tW2r%nj(v5R!yb6Z?T'CF</< 5R<*َ Y`- \@Uzb@ οhєvXn깏Ѯ|:|+&^pt9i2{}Ԣ8tj.Mx.|U)VS6Y)uVu~"|Eҵ=a Ye˱Ÿ!UaI,|qIO#셕V#qӭUTX^Y-N֟p.//J_[p|ӵ?4HCx;bjs XElex: Qe(6҈%m*ΠWJްFws  tڀl!q\m+ik_L2_SA-d4ޞbݡKTУohW&$A0?)2 m7h\b."Sį%L}'PiOb]6v_JϽC4 Ki?1n%x93iT~+e*)0ȳe~Cz 8Ya&3&͓p`Ӄb99Cmr=;jPM"y_~aIC9 B!yAy?rSBN \[.,LQ<+c5T/҂%yw J}2C슸a/_Yף-EqE<ĸ1o%6c\fpevMhdU }KzØ@7%W+9XҩS.6xMwpZ˪YpKߟgaLEUfjR[ ֝ۢZ/;"^bĄca06̭[2Bnj8jZ'q'XP!ŕ3dTx>IMV#E"woj\m%FC¿06=3!W"a TBFp[b-`a|hajp.$Ye]K 2 k('YB*${ 28>j 'oo ;!P-/_K~A6~a;PjF'Y!d0j%a<]2oĵ(Yy8K/mh$:U|zB !E-x:I,+<Ũ MdN,˶U&۽wET_6&"$X\AHWԥrLfC؜% ES? ,APd\_kBj ,?KTBWg16 5`.?8 `&~&mKtÆNdž/ W~GJrR"Ae[棪ՠd+wO&g%ݜD Et_E@< -68H7(jC}/!I0~#Zơf[卂DcU4nL=~33lurXŶ7- ! \QP*,Xc* <\MW?dEZF`- @<R* b T)0Tq(Ryq; 4DAl1w*)]"ۨQ] z6w.UFU*Sߡ:Xl+4%fU: _TJVU~%I{7e͘T@jCu"j 00t R* /|j_ n+2}mRhb QX/HL&X T0b^.YEGŢO)nģ@ ܣCq)f Sf= :Bۢ{32ާLkBQ Mg&8̫l1kao A[F`$(NP&+IQM ĥǃnnyt*%KsQCBFk!MM@}DԴ"/&"v /is\PbJ@2.8W1LKJd>v 35Q r0,JD9JbI(&b+J()!XrD2A|ę"jxQ.Gs<2 QaVnqٌWue +0#:'84.;DUQZC)rPꡋ\NдT5iDaD5zBh !aU@ ˶]T`)BT73tγ ;2t GZUz.bn"S2B'0Aԉ:rXXP^A-25@aLdEWi+ڧ({G6\B30AHr[偹væ%c_Y#PS+r\",SDDՐ0Vߣ(7׹pTE7MV=/y[Vc_~.|_\WX )ىK>F+(u0J-߈Ծ|J;$<" [E[5Ո #8ERa?4[½27 YޠZ w0~(`q}VorV4 G̠}Ü(]nZ-EUKq<2By}-Ws^[3ƅfʜ$ar0ʦʀt 5.ȷEͫU }~w, nY`޾ӳY3h35(7{w\(vK%+ wJ4 Y\K|wgG\w>eTh׈P†;"ģ f We@..o3PJ<ǡ-Qw)߷#`F<$d`;?x";/q^]7?ň6b~='YYza;PG_*@?1]?խ-= M ,j5GuZ{嗮]`?& #8Y^772WLdA̩h{lGkHmO`%ŒhQ3Y~7bҾX~%&Q0iH9~ V~Y)b>Zy8v4 X&'-NOWl?3'1pglC tMK֠X+{㯥hJ.]ߖ ̽.{+T%c(ﴢ?.=.oM%q[0|\l=ac+?j%g )E9d c@X* e?nIA BVZBu xXܼgg 7#{ⱸb^(8nGo@W6L=i"[&qw3]u&8U,*+y].U[nFN, ͋JL Aωg"A~ҶM!SG ь1Z&e§-AL$ 2`1n5he,! Ko0SW"+B*.?> ܶa/2n zRЊUH9xG34Sµp-27. +)&X(׼8b(!t/e9rP%0ʸ0u ZG~TW 1{@)exN<ν&h vXFoxϘQΣERCuܻkD)Χ3W`7I(mnxkm(KG^/!mѷL^l50QJmJdrX+I~cBUjdSTeX;9 o\CK]?yHlJ5혟{v>EcE*:Co G,)83.N^HQ]!¦J9LB ̊`Lh%51!`eC%_ Wf_|ն+s#.g mnk+ xX~"1N,pW8X,anb}7'%VjU@/0pp{eh= 7qJ`f?$T5*,t苸\h3c_SRc($,4\rkhi: —U J4ԭ+(9J:xn!rf*. dgi)ŷ/M 41K@-e.~b3yEr̢81O"Nix&.:o& [M[E%K MgFh*_h۩Wi,*13u.'irīuck͇m3 \XOWxeS=5W o@9&[E+ǹgJSjݏn4ؔhjFGڋic(z6+#dk@ؒN9SHh|񃙁lJmoEw-v)zQ ݑ\LGĻFɶbJ (¦=1Zĥ ji;hҪ@GFg) _i=# BTKS+ch _2Pz[xíY_.9ěELJ[JoY4ۗ7p.R58јZ¹iogu{H~6Ln*8jImM\p/?0W?E@tU+^pr$]fr<(G(=-< YUʶFJ0BhQ?A*e [Ĵn~1"t=z4$~qpϏt7MhS\dHp󈿯^Ư1c><8SX} E׷:7`rj![RyR< rB?p:}BǜwVq`,Z\f9S$l cPݽn0EqUZo_BYAfhVӘV7Rڅ9_N&F瑩Hm%EfL~2fVXU/~`jT,U,Sݩ%K*u(Za'8\JU>⮛!;]⸗*nT1Wh)*iA3k(-fPDF S0wap7)3uYTf.1 X_, 62S 3sxXٟ0B+`љ ޠA͹hc;q^YC爦hj(E+s__RQQW t_03J1ىTʬR,@T6~&WZO,e)^"ݲiUz L=`^E@m3|o5Aq-L.X^X.i2KvAcXԱ_+_l@&(|l`-haGQl ʦ+U?Aybf8]>RU,,-gZަњ9&Tr=Ƴ"(@c .TS%tZ &Ew^b.wn+ḻcelW2~% MszhV܀ojU N7B[L} ^\n5w~`6}+}T)WUb,Vi2TJijJg#y%S/hs i q` {\DS5Zѭ8JacqbrscfVxU0J3*eZ`wl[&qz@4l * ՀS3P e s !ʬg kl)LFWyVs:j)%;*%G^ٸP.TtaO$w 0ҺAEqƠkЕsZf-4KPJ~„5)aSpYP-̹;l)^2) PQ=>Ģe1ט.QR|)o0~eCOK,޹-8Uӿy.cCE^M=FĬi}slKD$Cq`(h ȸ9[پe.GrD3@,i/U,N3V [,4**^QwpOl%Bie b.<9Y!=W3qNL+s0I8 Y~auԹ%ڝ\{؉ʾe5ϼƥ&bb<#90-Lq*QU)Ol5.h\0tHj={̻201'!.d>!EV"CEF3(=;S/TbV0=AQs1,*.Ow1kұ~`1 zU}j 'X*4.Hh΋rq<#DwWx6X &^'" Df!ZҪT0R޹>F^WBul "f8\`+W y6u8J^.Wlƹ|>97kP=C+PSaɺirU>a=#GTOߗ_Jڻ%umIf-3a.^ gQQڎ#x8@[78HwsҸb]L<43m [xвT#q.]idsucj ȿ2-Ek($ T5eҗBee\+JGT,;*L p4^u, [͍fGaͼcWWXF\<\D?.QںCaej `R[3=i@Wf`"7 +Y@ԦJ:6f`E :fiX.<VWM R8buS ,\ع_P8JqEhni[XrfQ/e >Ҟy`WɟXS p[A2~CU@ Qju*A/55nw^N.T͚T~KaQi7n h3`Zjf\`ȋ_L#kܬ zn2BJyVZCSwRx U )FCHԵ1)uOG8K"`=@puհQbRN FpF)@n0$V+"v׃nX-s!(唪4c3),sr6JЫ(+%BeeP_AF!UPp]_hNЦ:C%KOh`eT/]{MJ<0 8 tbxe(Q+UepWu,-̹ӈWYC69`&œ2g:14HvYN%Nඏ[)l[.<4\~T;F#~E */quuF)VaX}3#a*ȮFغI:c9rM.vB6 +ȍ9*oHV\FZq7r,ohߘ5 hAiF xpVn]ܦQQ\G(890o- \6[,s`K`7 B0wG%XBn >1f^u.sDиu`oQ"r~fËR M&(؈(hESlͥfz.g:a/SIdZ^` S#Kıe nirn+x>%.!ʵQ.ǫb{lGR q4f\Ƥ ~7AFBh+Zoc+Ч "OoV/y͜oۈGּ>J_:@^J3UI@.M(͹(:o};=b5F 0Pk++$6,:P ܬhc r.:A\qjM5`{U7Q<`(KWLK6FԤWdtty +y. ~EG0e᪯)y<О9&f]zNfzpw+/f0(-<-t*L_}ۙa. VTSDD4 $A/*.ˈ(++LlK ZP.TwVI_6-m(7?b'.w~hR_[eK { yilGG PUXԩ&aj"aܖݎkrw XXkoV6G 7 `H}CЦD]3 C$ls/LM!éJ:]bCTAS"#8uXLnQt' h$WuX7&33"2Z`ov4g(nc[E^XVh1Fn. }JF ^W-ڰVle̽%hXп@]VU"aw{ u(9mZ`yXx%Q.,7U"Vc=^ j]j`,9PÅo31JL$0Kʁ{HÄgSp#jvja0i25Vb(Z^Ei}\ߴ n#q!KjBߺ*Je1)iD<ʁt5h4nMfc`` Ob G1Cm\pUE Z2t7)Vo0qæ‘SPS0qr3am%c$ 8VrG<`t,LlnloG3PoE*"ѬK01@Mx‰U)6PizUEP8 ^‰vsrtu00Z&)\a*$#n6ƚMn F3Da\Y-ʪUW/N&kYE ~%2)mdkS{opW!lQ3Ơ +^ZzTT, Ai57ޭ|Ԇ%ڰk<{P*:=͚Jy Q0|Ds&,`O emeoJ+R%To 0m2pKe,lU߈Kx侇 F)wW.Z*<,Z֏ޛNG1h(uI2õ+҉ P˔pYOeL.Ġ-mt-LOg[`qً Yb4gI"ӣغkS1+[_b&'e#h6#Xp(mSU͘)d!*~90֥)v?7Z%tde~2!MX7]Soi.6"9oGT)|Ǽ ѭ҅~WF(01p 7qi6қE8s2A\qĢǍŷn*8 ցPP7 HM9;N%xZ *9@TxQoLkOwrHT;=%`1 Åṧ TA}+0KW2DE\2Tm?+ gs]9>O(q ,>׏Ġj/2(kΤB];pEQ$ !OYjPwkPqZuX%>N2-]%Ctؗk&*;;L0`f\r潢*e%74 TX% M`Zt^F\X`v~q 0aU[XYty(/=DQUE.g) je,@ _#xR(Jj c/b= UN)kĦAz 714ikߔ*(EeļqRsl*#x`0cT5.PAN{GX1{,bS@ >6V^*g\G0KPe81q*Yȳ9rL`?Z|AVa+ cҝd+zQjiq]M\˳aª(P6#V9m]N0B0!K}]J%_6׹ʧEfk u)3^飻bwPAQ9&B[Fz̢ E.ѤL M^ lZ4<̧ށ4Ļw8]ÀJJƥPaKJD*5ot{b"#z/,q0 sWQ!֐Wc3(hin^St W1fG5y̮F\mr{ vA*Jr*DN#"$,TYkʭ^cwRb²Ta'YTSJܗQAwHKa 4UUQ~縒>#{* "s˯%fXiM,Z/95 sJPF ͪR29c[`U@Cu`7OCu=B5ĥ@L9oL"o|F©][1}8\x9fA,[v{T98 -N-DWf!e̢1*0(@YCqPBSܰ1 ^GRV 9"#5j1('\q,FR&n׼l^_P^VRJ9{vD>X8^ c{4][Y +h䃋xe6rg).jTgybˢ0KN9#԰xWet y'"+>xTF֦l 1k7A1E. -q6͒jtԯo?Z2G0LT>PnqkQ4)x7E?l- -|iLZ^KgKpb?7Ḁ4Rň* j@(f"֘JUt{o!5غXb6;oʗ-,pv=-e!W%-8Jm2IμA!2av̤QH&@v 8d 'yEJ--QW1Ŗ1ywU+ 9yyAE%) m^pέ@! d8 `! ~%13DFyR.%x!+;|="_. 7-Ƀ~-FD_2RY+8559/\ŒsKLnyN2Pʁ0f%BڥI0h tl P ){(t WRٶPEFY,j BTi;1]0@y䤔XCMbahiR ?v[ZV85Qto%_3!״R%]x*bj0t=8y1 x9(Y& Vyj=G`(`,r5߈"^w,q\GZ1 Y:>VwJ P~b[?n3WԿž͖yl>`^m-·vJ>aUOxÖzCUCƥ k9b9"M\fw1 `ؕ4ߴrڣݮn!DAKsL,H]Ƨź 0-a00Fi|(\Q4%(_HTpVϑ15g뗱,aK$ 5V/D]6Èi2xaJTW7`Ī]W,w]~lpA X}>Ɉ5, V!Cj+.?t@py & ~aT'{BMÃ,.4Hj5grڈjF PB3dr#X}@(s(!F5,b_w{DME# 542C}4q© v-ҎɣiAW_5Ə6oqg -%m1o!zE|DVbbah1QQATņ_̺ =g4GfG7fXt/ݩf[*as4$E - ({JPT$ pS;'?PN|1MgkXauU$RE7W((Eil:5~"KX^B m`.Iz)-e_Lg6-ݛ Y1aQ)UDGOTٕLʸ%ٸ!"Jr#J$t/c BzJhEX.?üeȔ@|鮦|DU.q. *Z q4B6YZyωXӨRT:5zu(v1YFX1^K!;,#ilc1*m5ˢ^BaƷa8 Љk$1AH$B3Vr/M[*s[\UUo|(~k%Ca-@7;Y5Zںj\!5*3m(o %~eZ8ex4Lf;XM /n5mb[%V媂PٍrWn!CO(uuɗRJ3t[Zu8oZ}YkΥqD- 橥b&8"Rpo7&g1kygi#3q cŚ8Sb2dW 3 EwI,qv*9fF€BD㟧ջF7UWŋt \n0 8~op~_` 12yTSY0D$[9faݨVfZ y+ɉ|*5.=Ө 琔A|'rUγ!Qh{_p)19Q|H<^w*THَC=ȈW7 z C Q^zo):×,E돹J=G-\*hr*X_~#0 VeRWd,2/N"Hݘ! B-u(C.)u vDPf7@&`CgPhP?6j֎}l%ԥ-b"k9A#% ̕qb7BePb21μr<wWrFg-HX%_,f\)nCߴbhUs0.j |FVL٠a wS%j︵k|EV7Xҫ3 8ppfߗ8w sK Pq_T:ۑ6&⚶O)84t%~&*+8\9s,8ޮPQk[ ֓+ټF+̶%U?vWw-&0*īmql հL2WXsN5Rۜ ?:pdwmkevF}6cR/J_bYFow4ux.%\``#Ng#$cu 1EX2HEvP&q—O&w r(l%9S5;օ*#P q[ />]W&)iSP#Anl qI:"+Iܾn됄ӃqP.H9N,Z0os- }T:]nʹq K `cFe٤ ~BQG;ŵJXrZ)5_n {s.˾_fddn+ =LɱToQ/?'pn:#Y|f1Qc  mulDz:[BCP!AΟ:Z"lJH]+Ĺ{doR#z`by\J EQeaP+Wh{ʊU>KC*P5U8Ƣ3GxkCQ24ЬQp,9K Ըz8;Z}l*W*oFq|eJSb(JN<ʖjD@JFh}@Px@X6Q#ɯ(mPᫀa+ᝮ(^B`ZP4hCFn! Ijtte`d9J-$*y ~x6Pc(ʏד! +j.i$oĠˮ}0+MbMG\F,ū<0qm=@NwK[hK )(=KC99JK7=CXYT5Qu7ZE8zߢrT A8bĕ^"!#dCM lUž孌sr1(Y5(Չмʍ/5ABT81v0q2X7qX}Gy]f?MvY)[eX 7Qb YOèa.-m/k˖F%L-,;^-,[$."1VH+}O ձm aeL++`!*fA!`4s50\4RWj`Mu&<5* <0ͷ2-bs*ռ~cl/>%!~q)ݷ*bC.1f85KoqİQM=kBJmKHLq)P}%zAG fhCu2pZ-CX\ޢ.mSgeXXm!TڃIe+Vխ1:"eL&0L2LǸ?jNC[l\e빭YGʄ SFAI%T} ؜amVU.{;1qJ_Eݻ#!lUb];l阔#6WsS#)6'ޡ!(\ |ew3RĥqZf KV襚 +a]8EɌWRœ?D\~ҵNp&>}㠢eQi΁I Q/&qFU y LPPM3XMo)kRS& s}sgq;^)`;e4{ n\PPL w\OS1.8bU-#ek%̩>V`˹V{-ʩSFօ/\L b*!fo]b\SNo˚1O2E-W e(@<'S Mʎ_Te]^-ı`k z3SJ7VK#v$0+9xsln/c#*{K U-rWClL\2cuoʋm:u4 bH`'&MuJQV. #o)|["#Fϖ.֌Q -D:-}-*.1GBnKXŲSv!L̸*谪!b@E2Gbqaʋ+ahxb2+\ػ̸Xve5` bZ1oR9* ц )&-*,sx5 RҿyC!qmyq tN<ʷ?r)$4+jNsFh*L3hLX!x2]\CML8&-w|"+L">+Bvr^ d/n1kqQ2WRVm/ pqFe3"X>ys*mk3`60WFb( ~%!NqujĢ(9VL?KMe`@3K!V6Y! J/ ^=,/ꤔYAd:b#C Y ~Rvg_\,eSOjV >f +UXeT/ 5Ǣ@;![,;qp 5x+5©)Gm–&1P19/nGJAFx~&%zK&*^ uV)x5jؐ8DF BiYM@39فu@;HY` 6@uR[sT(͙[9؞%yh?P0x:̨n>Kg.HPm{KC\3+?U\Q@i88oauT Q 41Z|NT bDhíph50TEKIUu^s:* ׏h-V Zdڬh%;B\.1f`/!3ºi6(/e[OJW4p`DO]ChF,}PA*-jDX`{$>=*H1S4-1Ns1jm=^S iDqq)s"JRFX( D56('oע=.UC.\njy\ף 㩣0MnK#hћ;-&P,ԵT kPSYB*bU|P#V.Tj7L̫-e~D#"RTx8m߼mw 2 5Ci6Vjhbh Jxi/J3/p\KU@;j{ )W(`=!lr6&& 3Lm­Z[ZrAJ!a_pۖq.!N 6iQ^%g ŗ7݀_VDXt@80/5#TUXL6iE>Yl(4J ڨQ6̟'9nmE X7U ,p83faP`/8ƥ0GS&U[לsHJWT9~ ,G"6;(FkJT\V5 ( K8CܦP ExP kp"L)!9 oW9-pRHΈ{ʖ1^`6y/bń%俙[ıѕߋy?=D)nK(yTFʈc}ihDe*G$,aJ iHX.jx w^m%k/Qet;4}7.#(kۊbD44 EM5^"*+ő~!"e Y8,l9nT Vk9lj]^JMY&{%U˴ ־b:2V//n`5Pl﨏"u +mwM Jƫz.EecPKcete9#PPmX4Iͥ]æWB&1l5^%\Ƹw 7YJThsbקD 늪4wg1'4wh},92oG|\*xJW78(zk0 8{T]QeF< 4mW 1 `ReO0֪aHddH,0!dZ#aPJ`"%@{ ڙpmTUyGHox.ܲqU+<0ŴdFRߒCfVeUg Ā΁l!P4 ]NUw9LUDky3W^RE^|Ʋy{Pf&3튨+@\~% Yk|"#Ǜtd[))7!rJܥ_%!ޱ%" phƚ G&0kRY2erpZɗrlx؂E1(+@A7j5+nE[ \l֟Ml.LhM bTQGrE0\fJ: ;иVN|rVrkGYfC^ւb;@@ 58٫*u49ϣj`[tzbY z5B$1~ _j#؜D[bd8:b3pn ̲C~r}6_2JJTJZ~cO6B\=: ljH+73<13Q=@Je@p*^勣&%&"Qy,fi<"4)r"(Nۃ.cQu*FJOj{Fv]KyYҟnh8\5,8>⭋ń MJ`hzr_mq rH֮P e9U:pPT^Me7}0+1,i6,3y-3Yoh\u/U+Tm&)JRМJQeF\Y|&0@l"xA߹p#.)UL{tzVΥ}GK U kYİ* Ʊe/pfњ_Rܼig.vlt|V3oxu_vUZe r +/ w7t `]^Va2Ow ^b(gQ u _hUN>_[}?s hEP͆)8P8bΘ`"S #xaR%+u+,(Vb v]Lj fHg ]Ȃ;F/RlVfs5|ӈB]@^;ZRa-\BMX tP` S> 4[ISdPnҀ)s,V%)`5Ŷr>IS)WvKWŧroAGq6dr2A䮮(T4v=] [KĬKÚLibFcU?ډ7l Ɲ{sĭ˙Y@Q'MV@P(ևĄ+Ĭ .[[JPt\[iCTEKl aw>bZ=s8:yv ULR]Acp, ++5%>`J[_hgj;Qdҫ:/[+F ltfL՟2-Ô7.;*_ctˎm)d)imrsxp0A{JR %eqCublJ~#`1## ,".W /WdP"Ĩi4[`@seGв_3 xꧦj=~aSsDQP2*PDb wqV!T ba)W:SHX@LK z5+r rB}q ]):FmJW^JϡC>VDeo`gVr=)uڄgpL(#W"0< s(eTkī16 j]Y_0~{!b\]7džg+Fܾq']_T ƔP8/ׇBg`NUe&hZq ^"m8qƦ]腱K,]ճd=?w "wZ&`d{YĻk*f=+Yr>~f`Po(EP:1wj< J'.@.бGB (4Y%%Lro)MUIǒtHj`X"c??+TO`)/t[bgbOa +J=jfhP4K:| jܴCBQ+.e7㯶c]5C#vǿBm~a-\̈\SRGbۘ1 ~1E=`^aEG$wAm-D14HtD/IsCV B㪫D5JdJv%S<'$^نJ.?2f//2_r/\kfҖjU61U3U<%+jƉB!,F\i. EɣuxCn P6P7\P+`$YzJq* Ĵon/G;]̈́kii+@%5(* LpN)m=㗔MJ&ya2_ .`ipy8eىPl&e0uWwP?kj>O[, C-FҀ`@P=*AnVw M:#u! q4ļMTʶ%E6̬ĿQY`5bTqPUs-W-,ꇮQ8Y\m5B+W%C(*SM", ž&(/!1qQsˎ!uR3 ¦04{JYܻp#R5CN) 6FU.WADW*7:f 0 T| YA9t=j@ tFOP,0ԭ3ppg̼)_qyeʿ$*!x%.=ʚly#+8N/P[xV- S(Byf ı(Q&6esH0VUqR[AbxR$"FCpm.(r(7LSѸiLr=ݒS(冇U&9\1!lu'>&|BBC.icP\!Lm}U`="+ Q8H#tTAd# ]B՝C~%/Zy0+?=^Ҝ(`BKy[a~ D@>"RDftD֥`Si n@ K/ ŵ*%|D^P *4+Ils6u.zHWl2~&Uj  L6Mflx:\ҫ6dʍ PBM3QjBp5 ѐ>H6q6z 2kLD_poGHKJi TSX3Vm(5zz^nD&nCa{A>&68%m,?PUŸb("?$P!@oSe8so73n'owyb,+2VyYk8*jst8ux?xqL( a-A V1;䬸p{cwk?Xc|7?$E,ܤoFs,[djQj >bE}ՋOf\.l#^+q5WyD%S^`2E;K*5UYP׼G6ilkF%YrkQ>|K befBR1 ΥXYX Ī8SEP`Qfr`1GQUk}K @TzK* `U?chPK)̳V+ʾ*aKQR0̫Yٸd } S ûe^e0S.Ygdq2bettϓѧ%諀xc'ML&/3OkRY~}ƜAS;gW^NY"8\s:e%te`T3uia1\WX! ']?`2G%*~bvoV/?N*XXRhlHJ[dAجĩ,Ղ5q ,8a'G3&>yS'=!tuar {Z?0p77[S\< bB:*5 P;ؗ0Gx8 dqVij#`J:)a+w7:U&&h*T6ա2h{ep92@bG6ӎaЖĢ%ha81UAb{`Pe6xP]H0ځǠkcj]2/.`P`zTToAaX٘2Yf#ߪ8XZV@ASDK#G Kb"iuQs-9c6FGqd*=^etmҧ=ʁ7늛z%=1XFZ=+L w=8kmf i ط0z}1b6d|'8ၟ51ŏ0ZiVkiWJQZbĪYsV%: cqzh(DW=|A520PIS2(!mfaƩ~ YLX`jN,["]h b~r@Ո;F/Ds!x\*3*V=cU*0iglt n%: J,m3uTmT/[CmۗŲ@֕P1Hoh[~5.!)yxj,_z ?w+JuZ_VB S卑6kĕ1+%p6 FU^& wK<dH561| ba3)b @P~~@8T(>!n,ڲ@5Ķ G:8ᐽΗz[Ec:0@ݹRs|a F%ᅒYGY?TˁɮJb@a!A ~"k9kF/@<u4Q]Uc'kGH;EbqelnOnElSFfۢǽBZ!FH5ؠbWoKoS`n#)KOLO,O ;v^fd~Ti9z}7XKx@9*q l*UZ$4<(rrXx6Q^?5 S樞1Ϡ BL%A2zcj *(- Jؕ1@ܷjf"fvo1l-yZϢ gY{H(~=+WC*J-1 2ŸN"_DC-D(`Xu2fPPרq3W`Ad2c"{&eXW^MRP306|C@cI8PІa>nY1h4 s * 9s\L-(5Pm[-7|̱_6\[EӈDʷY3;{E,n-.5Υ+ܺ7g3-LUJ|GE^^\:/m*Zl_fSs ,Y* +{2k|,rfw-㨸EɏnYZ״ÛP1 {TtWah^X4x*<k*hZ1EG+- }Z!8qsԳgoP<-"Y4 k mnFiTehD07LK"G.e)rNcu[)L4[ć &#U[" 'X= nb K k@8v[kAU|DXM-Q{ݬJCb,&Iv?B+0؏_eiɍ<Ƣpg0'K1oqoͷ(""vȅ<5ma+=3)0ZUݜ3C-9,ݍZ:cz*q(w}8t-XD-b Q\Z)ꡖ& ¥\._? sPUܷn)UWZ̹ss)r|q;|lIv,L :ʋtsiU jйEW% ~2ZPxFw:j1Itɮ@V9&i+OU8\XJ(&mim5jw nVn օpYFFhYψvcYU>faq=.FE9H(Sə)Puv6G9i7V?b5}exEf,ÇŐ\ aEl9g 6?8#*XrٌGD0"l(;a¹ ,2 SijWFP 9a &Ӆ0>!J5ǘXfZĢ X ,/Xle:F%ܬ\7+>#(j؀#S1F]M.' R6MPj6( ԥQ=ߢT>P35%@(8PTc;VEԶ !Lq.Q(%L\T[n[we([xĠsZ`jZ-Aq, ܶkmfsVLHus\ (()|ˊ !’d5L3 mJ]{D5w!5kKU2uxK_eN\u\ݾ*3f(=x)>o7 1j:#u]F$3ˮ`ң,Tl('"p$ l/p* JqCήo刽w)ޯanJj_SLQgLɘTR%bTx牓{A,۸9>X۵r+pSnM?ibrť㨔1ܲx{ߥ1Ur/m\kn]jC#JWl+8+&* _ હcְ=\ ~asKɾ!@6J,\2d)2l+߼ү1k ^F+Q>M= n.TOK|~K|Bb8@8[Ef](@EĦFy0dפs4ʼǪ adHIm+1af*U W`:`(ee%BԿu E+-yR[nbdbF1&K2SsS2ruq$վ"{#_1Lq}_2fiՅw{. 9,۸?fMf7Mo#w ;=%d&y,yZ *cZ*.dk̢B-0gw*X; ;"#u^Ұ.+ {jpt<ˤYM_k(8fr)[x@Es](ϼ ]Fi5N؁4aQ#z,QL^lX>jZ ^wRkXѫf%mXhn j}3>j9C{w P48TY:]l)ʔՑ@' Z~%>70f R1OCZ|nݰ0P*7kL}?W}(㋉WUиnQ.Xg<h987Gڥ 0U֥je:`4i d5m-3UYF9 T;u&,-NH9>"Ue] 9ݷp쳪uUcb5K*" |e["`.po;gL:M&-wL69qog,f/dU5V(-cHL1oy|^|*)0xĻPT YPEp%esS;iBa\ W`uqH_mx=ٙ'킇~ij1TcjYEI=Kܺ9FTN%7Z3.`1MuD_ mB2w K]ڜ.MTi~"W2N ؋ V״N/w p qA3[l>neԎ.o(L:KL 2] Dqȶ]er@T#aлk̡Z5 mbh*t%pM8%T3:92dCDaNUٕ$4俘JEl" |0듭p~fVH*N#7V GqZ5ܫOĭ=^k)Lb_s_,@3d3uCbUL9et{`!1 Vـ]qNfgglJ @8Ko%nzIbY׈جn4\Ze*D)wgUZyv¦@|UxHmhf`b>OmD=%dV9s,Y'q a9:z]Ta* CFv$ԋ̠ϠaWs#UeA c1J{%41Xz= ]p?~}GXDf_陌S"[oRA;1䏨̠͓~bLPx28[ᙽ!+G,D⣳-SH9P^u*_yvKR{ܱL *,t3E ᷄h&kTu[\`7FG+4фM+k')oraL]ܸ2[5-V.UewPj]C+&-Ys`.<rܡ`g. @сsV3MDElsG"9cq-Ϩ%9,+~Bn:DJ*m50~NXl8#Wb߼;}-`M>*S3-*bR7_CR`=JW½`F q`d.^ ˂=!Hn&%/G1Dɲ$ (倗9j& Vyj;W, , 4dJ9"C1UCD!*nQ3 +=9t~`Bۉ~t6U1!(L"\\1 'z-zQ1z}Tb\I`)pRń*@ @ ϠC:L_QD5p~c(v4.rn}prНwtu%@br'M1U6x7Rx}YʉS%f- }@uRfE#HTQe+9x#cna}勍ƨ l 6hS4Czm%nPۣǟioCOӹZXD[sMs-6z>heY JWn!MW2U%C-/PR7cociGKjMf`?xS C9CH& B;iV kBQuXap&p8a{sChdsh2,JvhŶÌZ>៉mbp5}W%<1kd{uÙ`#Zr똸 ;9,St}Άg+~# g4rFZLgoch@ٙQ1+|L w鄫KCӋ哏rh liGsB>#,c(^#)9e, h \ˊUW2|P@CnBԭBe `7+{_m<` BvY4ϼE^tkJ?&9XF.`PZDzQkJs@ez\sٓE6]J}ʨ f΢d4<e5*)f9߄%t_88L/ct-,fOy nEB0x*^PoQ#r n DhqB¬l}Z@E}".RJ/(W卜m /a+8jeWv^'E iќcpXXע V1DSj.n,UXoC$A+1Ցz6@4,4.Ux!ʹzwTj Le L=6Ƣ3T`hbB ]_8&iax V,UKQ1/[4~!Jܰ7Ω50ߍS1f03s'KW q -i+K#z HϹWulD <1 %b:0S\n . Q6[m2[w4u64R2ĭr״[>f@(Wy@M/1(r++T2ڷ5Zjܟ<x25cMрv1p ;2rد0 AřtP LbzշE8ήbQ~xV@+j EEzg:ß[Z"q A9 :srx}=pϘb%ʽIEU 8Y1 @.ma Pn%DQ;w %-! z RRV&)`n-|V0]§YLdȕ*W jW|Mep fݩ`ZXMc ᖊPs䂫?\h-[, [aYzQhv K4EQ½ -/5iDQ͏`.M ˛7x9S8R"Z#U^[*Z 3(\ہ F`j{bc0! w+lWy:B&#ij$1lXqWD3k|EqO"?2֮5 q('e[zPJK%Y@KKwGWI S,041;_hb0 2o!7T\r Դhn71_،`DJ-HWwfm<88.xv;֘=Ah!u ;xACE"UW7pvSEآwby:m!(rBO]+@k!W*7PԨ*p\MIfʆZ bO>VFgPeŤVNM)-۸b㣋b/njݽm]p78@ iӫ@İ8n kPch @ɒ ]f%jg UYQAϟNm LwDl&w^ՒjPU|g_dIGUt9%5|bR]/HZ?>Vt l iP\W娚d$ԁV{V.up.ÞiTQ$:fᭅK3 Cjzh= Ĺn*RӭK*+J^<#._S?1" #^yD0JG8hD+`ȷ̲Z[ L$p ,AS746è b`H8, f-V"&V9S05"Z#ĴkcH , Pwݣ[RŠ`~Ѵ_5غkYܵՏ$e{PԬףs sSV=+$^a"ƣJ2Zy%S0^ƹܰp2+-0 E*xY^nX; a3U}[+= E >&Ki V7i@JK6w{˹Sn+v l+KQbuT׬j%ջvLe\(Xba,BS_ Z~ ixH,%2u1Յ*„j3nh'FA-P1) RX)Mcl`17i |%S(hX:6J8K{ 療״@nPAiv,8 oI  ^S,:cP$V_DO=|ǒYcN`5੥m{}w?Ku%ud],=Fj_s[[WFZԲ>-52Sjqdgёq6_sx ԅ&\֨W wQ`ݻB~HB۲.)qP0X>ÊÁZNj pG -(%pXJE KPW'nT}lb-D334YYKVQUx#D9#|JVT1lRC]̤JaN7s-O3#>*P H:fn4USKjȝ6p8VZ6ms{@ XHJX kqFSl`{!F)VV`2Y̼#[s)"YLQ: pFC-٣ګݵUg+ lĦ\ PxAh;7t`*E.bn}1Tyw N,PMōc bۑevp@*Qp6u4. EAvL=1N#T M7AG.o @ ezXVQ\IJc=5qУ+lITNK̂0f!-PTR2Y(8y+7ݝ1ҳ2AqEV'oЂcգ/w ">D%VbA5:a7gƣGŖçpW=C|n]x3㙔TS5S-~,8=PJEeQx:0)mA0gGW.Xeڸ.YD|D*|1| حE 8/w_Jg8ZzZ7Q[ fUR񈲜̸Xҕ}E&es"S\@2.S]lrp#7Uo^}~iLQaT7^v  Zn幠9,R82T*'QZĉV`ntÈ_q=lzf^UB+˵ݾeT^Yk*Q84׆#+vyE;AE*53~FĥUrY.vnP-blomp/m^"@82F9m.PwqWYo|=G܃Y*R}4 F%7n,pr^>Ɩ7p{{q Q\ҤR.JQafUјsOHQJ//7\]ܵfh?BqV~"ecQ ypnY~*+fxnw.x*"ܪ67rn+,rT`n{E},_ZkE/?-UTlb P fYC*ۦF=ƪ kkVc|L,߱=B:طܨ fL6L iK3y 8f+\Ye4j%maڥLeh\cj3p_9eqeb;5qR UZbj2A쀉zJUUTQ(y@\^pQƬNcJ²CwRR.1"ʠjт[pˬ+E7``\<\%X%Y)T垎e)G88vz7~E(-\Xj>a,^28VW(wgH <7}j jWeJvFZW`a,G *W\A!l}rd̷J >缺l 5ATL6f,LQz sib{FM'WF'GFgbUJZ#z8`w)ij8 s4-Xo2[nIWjjШ-yj9]5q,WWw"4Zy?!7bQ:%Dk2IBxCu6X ʷO^rCX8(|~%ZGJ|'a>a/ ,TPK*)dZbf2JXԹ'UnR[8&%ט bW@)L,IAs㨖ѻ;MYrie/P1RةZא4L.nT#WSV`X4IJWmzQ* `f=EGA/_ Q;u*](F9J((n% l[P*-`"+2ub:> iX5c+C_f ^(s\h9*&5&Ëy" >4m||0C쪂._5Ī9=6$_BҢiw]əܾaijdTH0AЩ@Y}n.baJq*+~%][%P201.9jV]PU~(O)udQ4YR5Oe\`d #`R Q[wψRj әFʰlWP"#lXQ܎eH,M*d'<U(X ps FEnZmMaI(o1^#Rd;}pnwo9*FTlEYUXōAF/ ۹p4"t17 &]9rs8na$Qcn:2,# e^S`:UJ\%M[_q.BrE8TpSW_`&I!#Fa[MAnbP)pL<ƀ^o8U~To {j5TS i]D .jm ȬZ&6E3%NK4Zv02ɞTR^?iB8@aJyJ4Q\281g cj3_0# ߙCP|H(|LJT,k@t)2" đU>ng_/ǟ.@}bvF !Kr*T]ec=$]mW1ŌGZo9 JTkVO_AUXʪ%7~@^k6FoK(!~|@l*4E4.#cv?rZs,p\/rHR^ <;({KT.!m ['9V -EJ}%zJ@, +*q [nzřv3-@/qWMhj0)E#R-vӅy9'<@n5Sݮ\25w`C밶Z!dPn S4"mn&kuZ⫠q84U1`/{Ff"B]NI%4'˗ĀށWeކU.% ~]J(# Y rNJ3Ē]h|LxG>4,l]QD墿Cl{} )YO^or*өB[ ѻ),CU9KW ͵ VF9!ӀYn3g%MG=DiT\vX:~L f.-J/8*PZ^\h\b$J-oWM8WQ"r 4M\8u*ʺЎ=L=̡B4`lQUCL7 lR\|T̰^kqvٷKmx%"nL@ yE"4qEsڱXJt*\R1eׇĹ0ǂՙ_P\?QZV 2Œ]sFV5;Uiİ~8;K 3!˘9*AeJEj"23Y.1A0`1Ia?YB< {rRM7֐Yfe(<uR@ٌi}]VBh%C|%q&yHUFk4{x*x5 !E. S peY0xi.؈]^k![H٥R6Hcj-i,ʮʣq1ٲQfh {GC"x,{E]l/9bI4EGZZ&rȂiK+9eӞkr5_RļBS!ȄGԵp%Q/1­B uw t?ؘBM9jD!g;F$?[^8%הJdh3 A\$<9Q#X" ՟Ԩ*h Ɛ?Y,_gUhX ^rX .loo6t= J%Dx%1} ~W4ZS\PP$8D+V٤ , MT "ы]mD Spo÷е*1Ŀ .yA Giu.BAGǥקXq^EK ~}n  \xyiB %,54A5 [a}088,k¶JiP980lŋ0bu+N0xAOy% V/rı7X 32!ƝYo_;톊GQ]xD  AWSƼKCJFOAs8J/ -k b@B~bYe t01P=1yrXKױ`^ J,/1 ξ ^!QR; )ueCZ]ȭUĪVʨds47E0;T+3w60f!km^:f; ELjf&Iyz ADc=y|R6=uY@ٴT51bm~o,0TuE9H; č%Mֳ.71 W/ ̴NFB7a[U߱F44.9N=ȶWUn"nh0E՛7Z?#:kQn<\R= -`X69LBc,T.)0X"!YSlWS\iP[xD!ƅ&娼ou~ct(>e8DŽvLg t2QǾvQNWMckWfK'6W ._p٩R!NB\jpkS#O P+kv6?LTsv)kS#j3,uPЖ.bnR9/ .UDO ',;P9d*+Է6-x41BLs&(9̹ןgi"d P"~eyE˭ D `k{[F"_yH8Jj2|K0匤 _Ĺ/˛4UW>AEǒ7G^ ^o٨HUEk8G4/O2 v+ ]an_%g Q'B? 0֝/f|EC 6#3 + Z0D@g\{Aé%*,ABtbI8a᥿ˠhM[>_ .\gʹ^[xSTZ#*veA6euC3V5_+-E07.h>7dp~[rq_!4A INSfżR~Z?0M{QoXe2yruV-M?[twIv;ec O]Ҵ~9%W׈7by-EU e` pZܔ?6kqr;R`.;tAX,AR 61 ;ђd + Vu/ =C#{P&~R0JѽS ?f} 2pGXx;W g`y)-lBU2E.yUcw . 2ͱo75tKP]WXilot)M殃\.ZrۖoyW0G`\3Xt 9c;C5F̵D.Q3]Xð`@S:Gߴ) iݮғEؙ7{~׋rAL.Hu@_bIb#=S'nx ܙ*-sʬNJ pyYHT*F-_ 7(Tvo3hr|•2◊dg3;PŒָW%vŌ/^^C yja- jg1)/|EDb-Z`~oul;e/ ڏ 0HQ E!M_L7XGI&0tR{RT<,]WM.HsJ9|Aۅ!#ٟ 9җŘ%s5*fK@u',ڈє A0A@LdE V臀# y `A#Gf:H:jQHkWʯL ʖa8X-1aazn+hi*^kF u1L@9 ..Xb duDjtUryp {&m>!sf:]T(@P( 5͜`eusĸnr}n󋚀 f`GFUL9Z` @.OR̀eǬlj!A?xU vs>ҜKU{*u*XAȔ[UAEC̳R^HET ܾs]@lQe%yl{,~`Y3 *@[MAW% qV@ 0dE9@;FQJ(SK쇴3(w0bJ;nVO- ʕQ^D1*yER7(RDQ(/5G-c)ϴlq̢SU Hd+(2˲\p:e^0Uʄ Kpn 9<$^k>5y֠Z n; ^Q=WesShbd.ԨZOyLY5*@Mʃ$&^1(&Pj f̦ 7+=óA\u4{C-fXo1.%0tGq+<0̴BaMQt$KxLTCzrDe{̥i٩CD̤2L[pbyE>^ 8\Wfadh=إRny J!2I0M. 3+*7,s `֖\`ZdmV!a~%u˗m& &=$)T3 Y#&^uh a\"o 6` vH)`tZ~ kexNos="lc89Ke$67jKp$tP?ӕ4KBC_ a/PF\}!ĝ9MV!M <  qbΒl+Uy?(vdYTc"t*x,\&yҪ>7fˊvd <3yF9ĊaQ ֡+oCP‡VFæ;~XM@|7-8ݭx]*,1r($ `@(Z0LQA?g% `!!$^fWOW[үX`5vZ4CuŇ'H0U+eKX P~QeYG5LJs%$+mWq^bq|"TH4(ieΞbHfۻLoaLjÐaŃ1Q/oW|0UJ'kfSĹ54:yjn-FqV8jwlzܼovj9/S!|[\Rr(ɞ:!ᣖy͐ %Zr/ Yݟ5.!U_p09R7PhB)c l)笂y u_+W[Xk1Ukܤ^ksjz )⬊ L`-x&D##;J@crH9+\ҙ_F`.( ezc[ q̫"N>cSEp q*?zV[)S* A;|PÏܺHmĂ!7E']n!Ac2P&&dF(c{.ap. 6Q|(Ɠ+yC 77%ыcwkbCi L*>20< )fJ,>Z{Σ ~_`=l;|TH _*KO+*MDVA]4 /UG>sUR#)s~y9BdK|A v_X_$ sn//F\6_c~do qHsżK;bX,%CP>"ʶS-}ۊA\@2CW([L&N>fzb ͡/@ &cm7{G+1, q qB!6.DϨ8kW͐;{Ldf-<G6̺ee(THǼ|łGPV1ä 5inYVާ;ei|0Q2:\oYg3n .ekvV#LؼW\LTg6ʶSĨ4 \9U> Il0q(G^6@/ԢgV3®E~%Z&^aV%ԤP'UԵ_5K@K7[Ű ɈFk( φ?̼>b&*1Z\R{2-`Ni;(½JN7-{ ոKPA8IL<\*:\8wWW jb%]X;ާyJ{m :eE (XO qZZf$[3ƿ*Uv/ؙ2+{"r=o~,_dL̹쭻Vn.8bo\9]%x:sQQVO?2h0ql+Ƒ{9}= FƳ-2zIu^4-/l*^)ͱWtt*&e`UmڗY hA3yQ}nh)IX 9!l uf88/УtWTmXLlbsp*v\ҿM,|uJ\%v`X4@~BBZbYo2A35gG.%WvTkJ6UqX$nH_5p_Z#?LBQY3$%g#$%;j\Žيwӕ%0G-]Z^s9LqhOKM^K2n+=R/_~*f-=XVrƼ JrE6&tnꍇL2%:F#Јvb m769{2 ch ahC " TUR,ۼ !X-0片zP뜮Q}k˽ ħ/E֫4,K#UnyÄr[IǼTg@R==_.7}3Iϭgҳ+|%xJn8Y&#jLcD>m-\@j[Ԅ}>"EÈ).jIt,YBޕSЕ6l%N%]1q^~WǧdA#Ef$6\ƷҮ9To $BbIa?1(ԳyM%J P@bs* 5bq1BCXWL]ΠL Uw58'+R1 HZ, Xl[V;٬-zaG-sQ@bXcW8Ә]W(wMB]q@3v(Aee(_/_y&߉YJR vX_CLB Eb[W2Ա^U<.LՈÛ->Q{&w\T6peXRQEZ:"VMOvU*!IWSF/pkdz(!e3!t0]#јK)kLis1-hK9zI[l8OUܸln`w-H+Ǽ_UJ+4O+e-)/[q\U߈)jqV x,FK}p‹n)0J-Tj5bg=G؇^VஹXaJ%^S״ sq#TUow>SGs,(@V&N20_ 3]3[ws\`%uyU=|]1M ;j{3ub D kaN_ܠ3v˃E>wlw(FynpM(*1uw45MBչK{;v.*|Db%`ڢ 7Sԣνj]z%f%K&ZoN#OCQYM<_;p +!4AM3W.1Bˉ"E5$iaZU- 1S,m} 5EJk]118&FUh;"-75es ]z QQV#Z[*ǿ%ZNE*7gS=q7"p๟`weަGԫXbK>Qj{K7U J4F3\fh3]ԪsUs,ZZ,TJ?h!xΠ[eq %‚81:Ϋ.vTu9G1 ?knQlV/5vYeFZQea0qU_M[VZ"]>%&`pWk%6aGi `7+!9Fa-QKF~)|E a=n0}.3h8-br]FvXte@bql"x5cK9eR،DlR}|=9x=P|DKqJng”I"JJsjb4)8g(uPyȇkCP(/ XMP`yQbG_YA8 3jp:SLh&/:ՙYbҀm c@̠UƑoQ% y8#m6$F(0h{CڎyQ>ҟyYBi슥@HDxf1yYu'?x& [mcJi1.[-a ˠ5c?L,f'4EЪw h]Fl@ RxMU)m*n b̞Ҵ>u2ʭ.((5uMl \VV`d=SmT#]jd*^c"0xkl^LQG7um }UʗcjKp[^,^N7p6u&Cw_%Q|%3È2˂7 X&hosS@يua$ `Pḳ+QSD 1,% QXVpBnb+~%%{yb`y1Pᔺ"ȷ˰sϤ/3ktqO9LҌkScv ^EQYefPe: TF'"2SM㵪kQFo1Um[]Dm6Wu*/z;VkA@`;! HK ~ 7 [7o[vcS.jX3069Al㹷6^@gPE рaG1hBi\^2VРʃVn]i@V yyu39/+"n {Vy.!ilF}Q|KDYF!ew/U]kR`w1\ws %5w|qoUlGnnSZa>%צYM7"5Z"M۝ZH!mvWq)viKxy԰K ϻP3wI*mfh=HIy6 h8mRwֿl+l}U"ݐxb&&J(8n^e>+{+Wl2 W9tޥ.Ӹ-f9f )mc&87@khFJ-r;1Q,JKf8-xL:-Xqrʖ; 1) (Yzu)f>^H( qkXo:w=l Mh&'F+` ZN8}6c%J0qhG>]zĉs$VE1ڠ ^&OG jV\씄\kI~%6M%찄 ĠnUxSwf ]~N{\m;bx)G'#3f49PF.4qyow]ƞNȘǠ`Bxp ׈ѭ0SMyi fC6C ΟJpr K(qYhtn bLL^ 35BcRf!l#\Cx A:Xс |0 TۍKʉKlhx?0Tv!rUG9#-+R =1 1S ~`%" ۿxE yQTvᨿqi=Pǹ>`&\!4J+ىXD"RXs[1aUb+ɐfd|b3K;ݻ:EN<:o@ɗCIry^#7(KvRMS/uE59cE?7>Pw+=iYgO6x@tqGYOg*u0sKxb ̪Ue/@+lC2WԦ  `l e/_:nm[}y:܃EW.([ŻW,"ʥ3T18EL@:M~hdUƯ3i 8:P2+x\y {lO}*PXF _c6j&V/XGe 9'1SeTZJO ty(q#T,<(qOsP:i4+bҳyIm]EH.t/f1vT8kWuJ[O9)i,Vk˾ n X3?7P5)J$੅ AirC 03Y`U Ovz/n-M&uW*UlFp+ac0f5.`vVKMC^ߠTODTJ/\z %b|R-T\gXH)S8u.I誉pA06Ϭ~a# ܥsܢ+ZHRpCV]#a,5Q.P˞K:oؖ`y-yM\4e9PxH6**2S fArw(@+Wཱ G}{ E/~S2ӏxOhP>'V(]TÕ/.= b&%TP+|s٩; uy>`I-d3h/F% T>!)UTz$RK#e~}*}31!ٸK(8F֣B0rrҬJ!Ym.gM2x595 PZse%9n!Z0QĿPWxY18APH?͌? )Rm\1Kn!iܥSDKP'V%j 9ס\WL6 *,k892千`9<*<  YMFn=Q! (NeJ _`*"ޣwe2K8D0mY .5c^3.Ͱ-8q*94"9L`"A(#w0c*UWZ}y/vJA`lLcuKcyDJ}j=X 2|M8@pnlشjY DSn#r?E7xm5,s!2+1P6]HQGCeD-@ Q!% a|_pIK\7\98?Z W5qY+c}A. J0aΥ+%%kf=G~7lNg10"TF j ǫ--W3KfҜOtJskbrf3hɘzRs Ű,3z^fiM\mmV0F,tu)+m3^b+B4 ` "u,b7.g>mVNMA5\j9+(T5#{^cBJeEWQ"+b5TfJ M:iLلiPޖ9|Dc3iΪqxs+qxd6'̷N8 *V{iE;Ua{ۀ OG`~*9POMM3d^H+GѨҫGXL%.by<\|uݍ.׏t[mb3C3^DM1 DžNYGp5*<`4 / g%{M2,kqم$hwkTck%PKXr]5EN^rݬg4mvfZhk]>|K(0#Fůf L>s,r]yf 8?g&E|nM rTDp$ܕA, .J}C+I+ S#幅`lc@1wXBT\yh0_1W>%jaA͢Qt恧g x(K% -RXlH4$Uh; B剠}5iN+E"(v=JڶJ%2u -Зj;m[c1|RԼn 9"YU/ДFJOJ%ߢe |_Iު fE[nsQ̳9t\C|2Rg͐ǿx̲rv9$PzRq:WLUE)RՌ{0vDA[9t <>; lRxs(΢1mܫ>eƗxŮyH"\EaU5hPCSD<9%5R(ӝmmpn8 -&5JqUXLJ٘5@}cgծXa|9**/̵鉣T H_,pW6Z^JJp% xifCM\D7B`> 4yP6"0X4=qti_,Jtf>%̊#3y)l1q27A. P jFe*U;0AA*)&,Nڡ `1`661k#T_6wnw )xoT\=ɩW~LrO)gIXPJbD Y6z ^Dn+#7W~}+uֻu`]g.%5OF^q+L6Ll4r{m䋅a&1j2Du fy KQQKp-5])W.p` -<aIܘAM\-o&кP4 9X!-,8s|Ep dU5 >+W$ZvuwuT e{S2g+v)*Rc, %-kb [ke󪙋(߈1Eb8} i aκA!Wgb1 Y޷1Ƙ؇PWKXsL$(/?y nj b5 pԮ12e%i T-J~JAcY0-*ĵna2֡s͸R%yk" %kKǙ([Z!LZ<4xx+N.6<†Qe O'!ҫe/cퟸ/nD,rFw`:(PEKj'}Ri1j5 RxH}Ɉ8 jJ )HpWVٟ"oMNFjU(90 '喪Ù>X0D5VGnn1dcSaOA@ݐ-YH[c.D,f­lޭȩIP{ULj7ng`Phpn:pgUJlJ%[iv%`$]A? t 2H.|3, RklԠ|]l6+$l{  ٻ)-Po78 R]+"ɸӫF$]^nh fglnEq؏`bZiSuepF]ڲZm /iϘUqݎ hXm} 6i»^C-k Lfl &GYqZS6Ơ`+8lU"4GE+S`F1MD STkfE_u)qWZ6K95,`ز|\},53K%cUikr[*K@iJ 3lv{2n\7b]`ZaM=/* PYy)Y\WB+K,2UDcX(Zǘ>pm=-VNV pGZ^6NpP|9n,ShBfe5/dc_pm% -V*ainŶ[Ӫ9#FF8fJ-[QMɂCS crA미fA6&ᛠT.e_oizw\XfO-CĤj ]2[D. Uq߅GHot_/hYű j(s +Xp$!R`+IJ@_avcƥEIc-cO̴-빨m8~zL4Q]W iFU J+*WuX~-(>xaAqS Xq4nJ[,32W+$kef͚S&RyGa9PaKn6#f󘅰Wܼ~e_14N}ʷuVy9(hќVGP@yU:5wg|X+!FϏxU<?yUBA}ۈfs{Jd%W+lfVܜx1amnX*# |' s<-~eģlZ%@ SK] j)O e:-翨j_m@1*C I`H´:uJ_TI @WRҿpE_03ؖ 8RBE`. m--܁. }?U7WKPU2qAH{hDGlDVk'08[N. DR{m?IU̽%2/w?0+eUh9z%An 8JLm,wŹez/Ѵ 0~= 84oER|fkC6!=..ڔu,n;<]<ZeAi{+icWω%m36Gpq9Lj7c+3uT-P*P1]|^AP>4EY`DER:N',gFV_. x㟉Jw(iyBsR ,o̰X K)cU`8` U%$eʹJ~6N8K+I^f!cv 3KkSZ8J yFyK`Xu`9[gyQGr/6Y߼ |!*XqX/J 5van._a|@Ulb^}Cm]1hd21 o_*5z=S!GɤK#ŒZElAq9V4+(^ùC MT!c^aQw`,qqܫn^MX` Yx:— s5p ً>c=ba 4H Ҳ* Ӹb&QJ[l]yd0ThN?2k݆VXZ5]"x;#[⠯,n @j `/* yTs 1 L &ƟTW|]CTYy#XF2ݘL-[O$Bp򂉫:iPZT[,^Td͈dܴCJ\r8el9'@G:6.n E#MC+o2pLl%`x6("Ew3UɂyV;Urт];}[6_ a+冑Dxh UP0jE9Vk@GB&,PUSGmn,= W6.ulIXtgR6ѫ,zˆUGui~Ȇӌ,EFVU҄JxdDոEkJ*Qqxg4֖u zs )—6`{CQ -(9pY)) *"?iq]e>aeAD*1~wGC+'ԠNX Rb*ɰ΢ؾ=݁"s.:VT@[Wg uA[-9ECWrCfV>5./$Ro&;D@`Qz1%[^ v2d%_2E1+^݅ҕy2xb#1äM |̍s0.r&M ŌS@[- ((Mo, "Z޾cA8b d 6Q`A+9QTxaykz#AH:5K9 8S`~׈DD%DQނ&`d%魟U c`Wk+bĩz5Ȫz sҜ* 6ıq Wc#* Rê)sGK9TVd f,7nX=DTML x9 1Kl'Ɨ } X,劗FmR_-),hzloޓA> K)A[)R&& <߄Gx(U{Rd,P8]=&.D-Q͋N%6ϑ8 * ͫ>f,+P[‹6@ůrCNޗD1~ӧS9CZA74M3Dէ J[`G ֪yN=`9OB* Z}C`/8>zm&XL,\טT`fJz1;q[7_Jܯľ߾a*?C٬TslQFJ`<[=B<,9d[5y:T̐Ɉjj"M,ִG ZX^aY^5fNLu<ٻfnu6Z[UA3'Q̠7QUiH8snUR`l@(wpO\"`_s&e\}*b ^&!kN~0yYUOقGRxbNuXc;˿1{ 5%Gv@V< BiԶK `h2r 2gK~!-0GA0H]TPeu}ׁFSq,KSzTV:؎È=Al%DmcyqRŎ6ܻq7Kgd ]EDzk~fZLG*hW۔:Ro R7+4r6 @|wT`#Rm[pU`c QA^yeؿ6AW&j:qw0Pn1.W] eR؊L*8[1,BlD)pVɜ#.i{ EChElJ4hnmer <ၙvQ_\N*V˥')YX_Pq * ׈96.7~Ȏw۔hbKi;rYkȸ|JX2hDOU2J/r#XZu)Cf\7I9x&dn( )%_@z_`8G4JltWZw+z?r}ģmK?Ptz [0W<a]M&fdM Ġa^SK%Tj#'cjx1PKnPU]!&v<,Vch̿KΨ*tQGoHP&??2.M#eD'î ~#ocN;%0olcN#rQ}'2|A8;Y:͚` ??舆eC. cl1lr׮Gcn >دf X)) "uy(;+2l0޵-.YZ=o3#wH%Y$.U_ĻDM_0#W̱yĦ n3nRDKÈrJ6yg*}|Jܨ~Hb?%[lڴ2n-,zBİOK-@!ʫnx7$p96F-@:"gtJs:W\Au0[a`爕#\Jn&t }BX8)fȪ\UQ,;MT/ZD/77^dTpyPn䅰fG?/9'rRAox:D ;0 C_^ڶ_ +'@Hʄ 9-b~/ N<b=p 03/-6oL1> qK|eK8Kl r%OU<7w QL)F{-pnӌe CA+RnJme <$jĵ/X&1s`0UOΎ.(UGkc%|ABJpWJ̪A~&4-Éx?RReɎ 6_ԢY%h L˶@aQHƦUze+ۣ/oY}V P]j%/0Y,j0ĸp~%\VXATnHD9Fb8q򆸨?ii`xRԩҽΧp}EZ"px[`b݈E7>*%6XRPghf{rRuj6U|V*j-L4P|a2 QSJ6J?B~MD[)ܢ$K삅mQ%eQimش@ h![yYd;b$U ^j:j*U'Sh Jƥ桿Gq0FW%EpjK@X#[|/3Q1Fney+f!K  T׼R۪Hcac~SHc;]:3$ጭt'RR<@x9"ދW;G0[C9{bUV:ߍJ;d4qaQ7V|FɣxSy's sԤV5~%&̭ {KIX1GN^x+@-]yd562@q&E-WcFPSg 0۹X@++ܠVXǴ(K X]BYOj/im."L0*4RR5RAV-q3QHC(UDUV>R~&"~%:*^94yEnP.K`f5+zT=%8_)jNqLbqG5 ̧5)]0U@xWxRUU1׉c17 ee]TM{2'ic1 ~=EY("|ƯVoeB1n[.Y1s<*WY1Q50#z1 {0h8~%%L ᏹ)xT } JY5+u1Ja3 suR20[jiĉ\@ae TBW5S4D&&*!SB{D!e@Ƈ{)n}JtbGBQz H(V&q6_+V70Ǡ%=@A?KLwɲ%z{&Gק: S FHUWWky,\CoJ*<rf'V4nR^fLbWt~b\ 'bS)_>SUo1DdĊ?Pn:avCCy.RhD? !;j^cC`ռ1T0/-ʁCiRgK(ؓ yrCA ,Xq╢}H6Pc,xWiDZν>8o1ZÞੲc'1?vH .*Њ&J ؈J\[R Q;M))_PA3:bƄ q,n0TyƴtSU, ~E%>J`xqwm*/f~%e+_ X RT>ksd ]%^u2!omI[ޖ) 0zR'ONL\cT+@KtXR TQ`0E0DS%2(_*ZdXPdmPF2ƢߌG+frrSϼQ. ov, UUJxqS,&AV?jI$w! prtWhhv~4⍓WG`G)]TpTȔLɑ}b,yWQV͋R ݶ}cj[x(fu<orc#]s"a]tGѰ!,ψ&Hh.k~irG䅒ᒪ|5`ΰ_DkY@ "l5 rM,3z}%>g"~-턨^0h7G9$fYdR g%jtf*Kj!K,5L@iBQ#+0)h/5YykكŬ>zyۃql+g.-|Y4ʽ~##YQ]`"HG6X#I:+_O-u0`l#]?*]XH. ~q+2xyԶiņ㋊jЏWwuOl8]f .Tg r*Ż8Pc4D5wEXreH;"8{Fokj4-G"3F8bbdCm7R>si4ۨ -sdT{A`:?Q@ߦ>jkouAMUU.Y#oM0'(P'Jq4.DbzhKϭ1o-L(Y!s(SAÜi%p쭴t1npG‚.83m ]e*Y}|w*b^U+nlmc>*$m`Չܳw*n ,PW {ʟ3[LYFS#Zq | /iӒWlB4|E(靇yٝ˹}|JT0@LLX1?xf؊$1WJpP1^X^mKd{RcZL)v:lZ6 ݁6R\ynXƵ|-Ue!g2,d g0SR)w؆e|)`4.q{kGmWbuq&SG V7PݧI/R߶a+5|x;u3؜C#(q״Dz0B%jb3L*앨y(D +w=\b7qY6S%70By+S.9.sq`ң;{ɇg1 7Ϙ[n[O7(K_̣͕,Ͳ~e Y͝0Uqp !\"7/-Ǯ#2֪`>b8|1Vr'L۩"Z7c x(VWʝy3Nk P, Ff7RMԱ5;3g2=:؁݉F;j`A˘a]I𲡡d聮,us)e7\#{mհ-_f 9xs8ck,B.y4SS/zJV0 94-79גF^bs+}M kcp`/*])b7)m45B_>􎐗 U^.IYZoDGk>ZY`s~f~ڮH37,q#^JS9B j1jE-yبyT6X"Wvye"e^ۊ(WebrǴ9L?r6- 3 ӻB!VpU5< /62N{[vA 1s,u J?C~"Lb~GpM WސKTߥVSö-"TS+ WaJ+{{-z;#}y- ;r8P${ Lŵ7 Zf6Ll:9\/^"4; G`^*C%+1ǘ+@m–.^ɢY#O$1kcbT1ۙ+5GVZK3l^bS:!oyG-6V"0Գh"m5w՜f.'K,S 'i WN)(ip5y>/ɉ'Λ&N'#cu0uU>f +ᐭwD "WԲ ֬c70,q,,"W4ZPǵ X]um_QiOګ G0S[^z $`ܢ(."cĪi\rT.+ 2kK2'q [/(>%z,EgxģjYwl6{Ա10a7Ήc UW*uC[B-Tff YI(x"Re !TADq<,qps}La8(d,h,d:۶[YDPA0*ɜTO0+14:9"ܹ@/g`JN1<"7EV)ZL XKgg$' =A@\+na[co_@˙f1.RM־e[,*^U:r)u48}˨4M# ňE5`9OKaThc9>%)1M:)+CH*gN7 w e$8RVvAZpix" lۉ.nD{2=䕖u3ܴ2 Qj9orcMRt0/Eq?5){z̖\IJb$6-:k|yn=;! 2tK3Rg>"^R 8bf x yk«E+<Q-)|6dL޳P+SV{Q\.<61fT[<D{H.\2F*rU2G.]䨦 Ye[JD͒逕p(m02ҖטaPs=F6 $ ,1Uw\D=)Cb掲DP q[ͪzHa#^;%N6{߈02%j^nrgl^ cLj8 ´Il"#̩5/&L _-|1g L%P0g#K"Uk:a!l|Û TJ)F^G '26S ́i+x9+ 09$ ^{f5+.kQ^\i5C엎.%K}9Y75%j ]er|M[|J@hBTl @71i씰%u7`x!QeT^ P8lZt/U72uc\UNL/0 VM& w`z\QİHdX,DKXZ3 jRY4A<ߨ%KG_ר/bYy=戟r=Doe8!,1 O:ԥKB%B>bG]bV%g[Wd$\(Zw35V|ŹNV pqO`u(505oAgM0H(VKbÙ\30hߙ|h`V ų)qG ;#L ĜlS p43Ө aN NlBޢzVEFU稐935,e/&QIQu0K%+Ml%/: Ԡ'x? o5#Uf%*{D.e>AbJJ\^21vnGzYNs7oQ*菂k=BuZ$]u3`x@kkG+lV!)sRm[< v[JJ|(#GW^ceGz\#hh J[eTw 256*i+RRWzA{,7uVFvqNˬVag5,53>"+:<Ʊ1<a[SO(%Z?iH;wAGxex yij*bZ)׉ShdRL\^~mw(.K " X(> |@uP4aGy*s-FNY f?(4Pg5]- ^"[ -afV-,Iww@ۢ;VQ6s k\@>+& ?_u  R-s!C\|A,XKDiqFM\$)M Kr0 "oi,[i܃ƒ NB/ ¹;*Df5 YH`tuUUE/*_2BŎȷy9`IM8İh&e:5T|]/lan*~b~p\y]2Req"F`<2cJߺ1~fc.\Q :+{qX?ssO{Wnm*1~EdTc5 Ǧ<ΐՍJr%Z1erJe|(m1x/]6RpoӷYf,\*B¾e[%5U32ngSZ<*=K 8Q3-Tr]ƅjf=(n4K&$210"47 R?:DMq%A԰eK%A/2l楙6 AXt`s~e-~8k'17R ewS "1˫ שX w-̠:@. zӊ.Fmp4u |VSagu%J&Rߘ;ψ_0KqiOD*U\7&pZy꿄Q|S2,Vk]ޑ,˰ [f&*Y12"&DҹszA\8 \b` @K 0lX3,Jm^"TRi4t]`嘓geU4 a!<al1\y LE|w0k Jh0 5RWSN ?ޥY{ AH CK( tӃWT.UVa'P3;5655,@O(wt|1Prex1tj9 . N/lS Tɖ.Χ=7Ϟ#E9lױ_oN,W erp;koKyDx /GW+5\*Ø;ŝ!NZ]5/(p P ˟0ZsVW:q ˭exuGaA 0c9*P0Z xJ 47qo-k_?QqWj_jO*ݗͭ,npو`۱{#ZnW̼Tmܵ!clq̝^B8QAb [D*+Png MzH)9Tb%o5aP~}CPߠ+:g0e/Е; uq<1,S`)}GYk-Ps~<`NVepECahCP/A;WyOicz ˉJ%EW\WUb-/v6T|>n;R`K C o3# 5;q8hfG MVtVyNs(ݽG7(wbVybZi+9 y#Kk@i6qԸS&l'O.R MwVS;wԬ\V$ u:%:g&[LD9*7DB^ԬBse1).GYn%n/gj; ,R#QHB2gÒS ^*!C5)mdj6p|Üŧz ],W. ܡ̛1lƢ@Ø;Mw%x3eVW>eqrJYEwGUKƿ13iA8qYf@[7DNaSDaYnV{\ fNXpx p0d﨏S{ ru.$rAI0˻ o67fN]Q^м]խW+.3 OEZ}\ @Vx8mc"g@VVFL!-J}WVT3m76{1*.XÌ&W -$Eg N̬*G7D 6@ c'm+\Ѻ4I^`֜ŲVNspd^W^GcD`,}F7JSpj!"hC{3hB 1p9Ux_l̼D̙@Acu&@̽Ȩu2kYB^㙡0IQ]jR;H]S)3/&VuFe!nc7(knJSf%Yk2Kmw[.[xl%c(\{1G%)3MD%4/)n *zvyeGyIK)yTH1Z%1H+*{r*-Lp௙Or܍ӯ&㵕o#h" :ëW}Dx n)u"/[߉E-‫h9?x hGcQPm:|.pW&lxYJ6`W"JJrTDQ5 Ry8&kqPØےlo}JÜVR0+5+WQ Īٙ/RѵB[ϻ-N> b0-  L2X\=jzN%ϰ,u9 ʮ)@9;l^a\-Ele8:FeYT p>`~cF>ׂb^n#°U7䗳eҚQ!ZElчT ^&D^9{ȱeLP1!e EO| ~zZF~"\ͧ2kz|įJ : ~!ݑirC0$5JüTAXpeXa#D;-iJH2 G$0j BQGq^lt\Jgx;7DSRhnu*Wrn)`Ws5(BP͉BF>ɛW*Ῠ;uk6fj*(5ij"Kj"gyIR3.*\iktu&Z#el7HF@rrE&wD`` 4GEbY%ZlG^Y n}W]zbHF6C\1R9D/-,Y~#ɿN7*Zy}{bsEG 9aM%җRn6p/1mz #ʦK6֪/yo@vQ$m[+8zֳ !1~!AE^( ZN&+xMNRXi* J8p^Nm` m<ŒS\n歸1JT\Uѧ9WbB@Y7O& `K{:j|JN;}D4c%5v" kb옥!}:_A;:oODL8''!;)+;$lwpqHs ZRK/0u֤[ʰJclp,,|#6!\J4MɳL[Ÿw>x\c䘘{|LN%B $E%-@*Q 0\.<)K=6JK3bSB,b;E2hFm_fKFʛ}a "VyN尥aEu?# ۈbr^<4ڇs AB @RķJn 1`}(f=LO w+q\ k(rn B6h0Bڿyi{J,P՞N2!@DQЪr+ocJBF|G)3 9 1+W`e hxPؗ>cc!T2jfJoJ([PՃs &+kJŹ%jQ0k5 3눉52d .+9R[2YW0Q˗57 J_#ՙOh] M ߘR[0-XZav:`q6(v hLj|'1`Sn-{柹/E:-c&E!fq(w|;=_֧|Ǝu2f(cfRb龜穎-Qx6LW4Y*v |79gW)QrWA94Zڿ@e0e͞ =-B0ߋ9uxj ψexS̪̥ӹwyp|s.}+q*U8Oö]siQ([ULf"qB,)#QuMMaf]M"rѹġw7nX@ct YX-}ү\x Z u,̰M{e|LCƴ Ij0ɗ)q_ ^>O{ؗHuL(]sXf]Vk8("AM ]AF"Z,e\bRLu.|awc-:VR4VrݹL655-$Z-Q.Y]-˂Φ̓ľ0 %o8?*@8O7˜ 4I C1m$m(:E>G(UvR!-p˙@:SqJ j6Ѩt’`U.lPf v0 huJQi!"o Hi8LwAHt..TYVu1- V6+],/iK'G,nnfCi о9De\,^ʢT%/W.mك,liUs([|uWlun!N20Z%zbJF7)\X,D چahe M;jn'CX qyة 7.#ŜAzRnPq1wQ/۩tR`FwD2TiW^ۜ[.b3vaX 2b/nS=)0/xUbf AW^ PƣUȺ*8TSn:+y̤ުj1X\laF"{(U`wQZUw nD-Khk^7Nƻ' gn%\oh, ,\ZpYmQJufyx_AEzZX깖h}dcT"8sd49̻]6K 얡B&qIѨ,OU{G9" s*f $) iqfgRh2įJo+peԠ>؅(Wp\} KD+u kǡcE7f \J3tۀwuӛWyM@*eE@:ĭ qظdL&AO KҼbu8]ˠJR 5Tˤ8GD\Gz7.5.̒[n[U߮_Ss1cS8^ԩܲ]eMqFQCaTǔ-;AF^,47\S$kl6"μfQlq ԬTsq)QlG"=ͥGU]3X%63,,DfZl<ۘUW ^+GS}c^>mvPĢru/x{kegm#\L]=R9O|]_={N}ՍXs ŀC3Dr@4QPn-tT}^]SA`Ew21 [KW`s_Z1Kp dij 7DpF/+9*:F#-8ho\C469+CnڻoUG6ܶZ-RRh EW>}ljm٘` TوB|4E ԫO3O&k,ÍG\.|&Kb,_AUPV(8dQ%@MDD jb6Ah^ZZ%^WJ1Nh}@M hŧlږ a ı2UPoO}1"6N(\oAĠ"i(Qܰ*|=˨"){BU}7xdu/"R%Cqǘ94raf[ٗ^"J[;mnn0OY[eՍTpT 0&8 g2dd)[8 @3R]T4B#X“"05cR˷z”Am OVEcv0uA,]xj5dfΖ%ɑ 1Tzx(ppLA@:mZ B V!V!*?T(! 7}FSE} e[.l +OWSoRZ(Rʫ(0VeA@煋j%hqpihP%72.iNPz6سqga`r@QM}&5Lpq%gn%0:mQYHRw3Q6UeB,QiBPʚ!Qi,XSl˲U+)C jRdb l_scJh r |.0~kT9 E\~ $׏%խ7a-oCr|@KKѡ]D 7dҾ*i( bn(:SCmr "ی}gw%LnS`K4áQXdfA\TID"esxEw[ZԻU;jꢧnXCZi88YKDT 5s˽DR6Uy Vj_ r\B&gpP#$3WA OcsI՛5~f~p0遵IE2C\O^ [To3cOˤM.gDr֔T)9Qin<%cnυ.dsp\sC"([c<Lz J^8NIЂ[,8f|r|'E^Sh; ]Ir*v!X /$/ 3zQJ{#)|EX ,+5qT0bI(/AAEuã4e"WcpZ9j+nR[k?5KYX&eJk.7WU, @ҥA:"tBfĺ.^r嬦 A#GU_l^dR.938n"2J8{ed.!,F7ac"bH%5sS,kVs.' ܝ,W( Z7;VM4>%(UE4|QaWeuQFBՁFx&L.QNw`6b\q; G3OEji3̨hV gK=(M0" ń| ~\@c#lRmSsu&52aaˁM0 s)UmGn l,Z\a /.z]ͱ3{k*{0qoj3m{7eX};gqL ecnHmo5WAeʴ/ĽY ANXwi:Ռ0b.ځ~["ĻL s1❑PU45]JB4׊Kk0F[b5_1 ht:3*XTu%ⶀ Eev~&T[\o:ߘ$Qs2(YE4=R19"+({/j0{ĆjVL3_m_TOBB(1 LrԾGP ~hVxfxRKoxq5^b$=ZPI!vyy~R6!:Qڷ1F- 4X6Q@:H+o{s!]:(*Y؆n&ԲVor*;؉j0`MAA)qܨ2 h2f4@RK[N%E`톄u/ej̀fqw櫈ͧ^FU`K6Z YkU}*َ0(t, mm}ƫћ )_$Uu EY mier%.oRplZ'\JYZ~f%m-\ٲ\G+ ,RU Y&Ŋ7[/%/[^ZP7HC5P4erR3+o*lQ L/Pc5^ XhiEWLb1˷lLXM|&9ޠWBXnW-.8Rn.0XG.f+w3xb۞Q(%sj(r b)_K$M %XĽuf"Ŷ#zԹ: Tٺ2(*]L! 1 eCuɻE ۈp᫗dTRXN%\96wk%/ 9_]SF0\P3 [_b2B @Aj6l")|whPr^*;hm0`ҪUR;P*…70A x_"n 0n;B`lnPj,.]QPw0re70 `5"Ub!}j4&P0) !į /ZvTpe_# ku|BM~ڙb~,_IPWO̬aJ{fQ>sj>d3.Sl󨫓?T(r,"^%W 7P,/w%R#K r˸R-r`WT,♺XgήX8Q&"^D\5>Ѡ]ڔ(n%GZqSR6cN C aVWq !Z]1Mƥt2=RsY<0M<@6oeFWQ1VC]57ia,RB_̬Ƈt J@ ~~ -=͜es4ZAb05f`+UA\q )Mc2]TF]B )+)!<~+ )\ y(rFWZJ}"cdh_g 6n d&>"ĩjf MLQ *Qԫ ҁԷ~VQ@G5-c@ %RE_F̎1[a{"9ń84Td[c3-ÉWM(*GU2f7}*j  s|cR,s3*~x K6J%//#dbĥ φ 1E0Ja‡cb݀(G`BchfUZɋ([>!fw(J[GZ] F-^!`w)2 h"phΦ2*?' qmQ"/Yc$w*d{Wm R8㒾\!FRpk LcKv^XVF"9 SbCv4 [_3<59*![{_t }am Yן 1 dۨd[  VA+d>%E?qdBTEIV12C@wob`.j86NP ꊶ+&A&1^G$+o7^Ziw,d`)`uǙҩHR+v IXw|b_m\ѲQ|͆:'еy4TfYH ʃtNd> ]PǼk5pq;eū0 ߘT8eN4S)ӳZ&1hޥ^Aϴ`nf*X⚾w2)KJP=1EU.gWSw4|963|@ 4D-_8ERJE"x ʰ"ۅ=DbeqFb)f3&L;9 '5Cd(0j(BmӔ4eyq27 e1.Rv_9!Hhef_0M)̡=d^ [O;9kptƝJ|qp\-^؎ YJ>3bRdq\7Y?!Zj!y֣PE5]ŪK^K[hSyUTW(r"@Xh N<ȼGZ b6z*),R!f]HXi*j3qFp"~(ME9g8Z0.m߂ T%Ou(Y)$")nuJlj$PzQLO51X N>&2իf2+:*WWRh_iI1!Q ] Wwh , ق?8HAߪ>zΣBk XU*ػ< 3ىDՎ L222gwrRz:z1.bnnBuMmXUJɉY.0(覾ao]-"\@+Qs)Q]KA-˽KBn 3H%4,%~IaD-)nAGk/ >Ž P Ùܱ)uZ4* +[ `v|D V1UC9Y0269e[1x Z`IQHė ]&ߚao.]L f(0]ZyhX]qF%:7g&uH"q)KpC y.3TspnX5̯(o R. 0Fd:8'γXN{CU@^!pw}6lo,Ve p7ii5fGb`Y:+"B(#5(/!Ly%]1*h1n⸳̠uUFU @﨧q̹ %)ZAM/C\[8uNF6U.] ZDjVu- 3 k+"upz U])bp˭y4;$Vv:K,K%sw[ w2c|@eMr2 t=͎y &8f`< )+<\Y^BMCjse^y5Ds.e9u*ᨺ r%לr6,zrqd1ƃ_eKFL9Dt;n041,,+JfYDP0Kzk;Me)KNe bfeTj5Mr8o mU;$Ц- ؋x̥0N+ /#0==.3g/I W\KWw11[YsX :p^ @|%Ɓ= B@V8 Ep EU5I\{kܻ1Ivd+5˜ENY w( _sgu(.G o1t w8`%IUas! 75h*lk[mW aӊMx,^ `TZ9:ߘkh>`(fUh`U^twxRpbÜ,1lUnțYګ06cXk[bԁs\O+RU.PW܏2"Uέ`-1PQĴ}h*K$ZguK{Z}4 !0gPJp)Ü.ó8rjk:aB9_Cpv z5eҘe,avΥ"op'cpPr43r~skg-w<SrpBm6dr MhWRk-1Bu!QUN Tq-,8^׫*91nc-{+SekTH!Y,(wUsi +a 0f@@uE &桘ʸ(sEs;M*1UK Ra3ejYK!p1B\J;0)j01 $sMˊDZb#R%VnjPr}9+/$)4TJz)2@%5 2yNz16gԺbjSѻ), /+r[` FV P:rP(Q ]?\{/3?SC;slU{@'V1[P*Ų %9ls-/+E.Te^kHfw+PÅيoQqF$Sٍ=OH|?yJ wg-T "< YC8ЮpΏ ),1Ym0@(pf]AdX, |=wux2Wܮ>Μ%7# vf|Rk=[>s _A; CM7@gj@Va{$؏H N%xf){\1}l(|0M{\}%_šKxeMR)Ysb@c[?1AL%>en(]@_cL"ү]$ *t(3 \$cx dk<3؜KVpA"QqE3~5vqO9?xk{/\u\wyEA1p8Hq+j`AXW R#oTlv[PM9<:4`pD ^>hvX6-z< l0.V3W bh.6mDs7 ^'evY!9\—)sEx"^r,MBAXlh(*n#+k~Ym~-!,BWA}{O2vj_!L9Y/J hTH[em!D5NVAR+5+%clCPN,zS!u6.TdKSApEV#qk@EGR5 !."0kx^%FuLZQXhQ+|Ef%łdxioO D` |ŀ y*nh⏈&8DaRn *> EᯨU)Hcī}֮!=3BjU%%:>P9=!) WC'@SmJ8Xmx ^ʜʦj' Tr vLqs3TRD>Lb64T/(B\uUshbYīaz6HP4ez-_B8(z_4fh" A,RJ8quQRB `ߣRkS#:~*Gu)3`fWu**/Uj!dpcV @}^3kH}T hn0qlɪх.eWHHv]/Q9꽡䍿&<1Z/!LjWP7.*|8SdsL#'Z*Y (Klj2l;#M1i+5KAAԏ2F..c[LTԢaJ 0pIva Qbmx%V׈*{1j4ībn弽G.LԺ iq Ul5^;*JnĔĦΙFlRфL.Sija ĴXYݸ΢D xb*I LU+AZxdƱ*6K%.2VtL'"ʌ VLjܵPdhis/G(zT8Q&> Jl,jaciEnd3f^'#fLF Lgi)4ߨ{j'Pj#jVʫ=LxKg{%bQr ͺ&+)R + Y^+RJJV{IĎ`F95e=M'o+_M%z%z'zk)>2FSyRa= ܢ!SFġfY0'5s쇌͕%+R~%z퉔hKGP~%3WRn%:̣ĬĬT38J2zJDJLkQ7+(@_Bl*"^8ss(R((Kf@ef L*S5AUŘ@1*ں̷CaGz7pe\5yhKͶWS wzĦ%b*$3G/A+KK>C߅5}f٧q J6?)jQ/=`ܟe{J_ o"1ȿ4x|GI]he0=Xr1.B%uԄ[6(+= t~O0܆51d}^$Yqpt*>g{-WE&Hֽ~ˍ~FpoOs5Qx߼]>7-[5Z ~r~.:ˮr8־<ݏEÐ<~x`!0i-o xqg_!S_+x(e}2O4i4ql]rpo,/d-(W~/ߙ>bxOί4N 1!fc0I_@ߦ|zlH\F%džRq32]DQl¨p-qb/DV{>=f&0Z{%@A LPSWG0+ YMH6G$9-FŸ=&1Ԃ&f$776uI/kْb@xWgQ McYܫd~acleaYU3p9~ӎ6 P43q4İhAuW.vuQ\@Ut6gP4aQ̰ RŠG¿xS7 ^kj5*A6 k`35(6+dYBP5hSiU]T4cE]\'pGmEr<014ra[䉜feE]Dl\ ~#R匦AJtu]joP+&@G'2ڢ}Eb8e1 m2#0N!KXH;Яr`9ly] U@C[e\47^q`a@fk_{bE^%.di<ǃ'#;.q~`M״KYDgQ,i+,Yw =fM }֕6sq~ F}X S&^eQgl6Sdϱ4SLj6R*BY&5)_ gp#2,QLɚOyVJ43b /B '}3ߊİs*]K󉋩ke߉7u_08Zᒪ ?W}UPnaq:'WQZ)u#ȷWDoTjc9xeu.bg5,(Ƣϼp^"g4@VxPS<% WN{=@hSАZ\%Dis攩jG?_hAfQ+>z7_>D=(qߡ‘Q b}&}y'q ͨ=Kbc !c"v̶wƼ*Ez/xsՠ;pdL14 Xqc0U+K {>Hu,@k!_ NDrs. m%uFZY<^`3%h[`X40r/INb*Sl']0qC~5 Φ  X{)}7AsKXj"GPǣQb,jo)WP5/0urQq[-PKKq>uh΢vۆO>bL0>!R7OPj̳#+2 ŧ|Fɖ9g211y #X TA4l,j` .9f)(cT`JYS)Xw(BPߣBަ ؁Mgǧœ\AfLA$ S`.@iUb'^JǙVAO+>G.&11d1o\@(*'Ҩc耭µ B4\,WQXo2Į9M ㈔,jd7ƾ=y8aWK ӄ 5^\mb/{U̸ګZ95v]X`k,uNyFEIF; Sj)YV;Q R.`'tmWFS ajDVYVp9`2,e%Z{C"G#\^VdBqByJ s*ÉEV0\Qwz4tA clw\W%iZuu2<&7P0.p}ʾ*d^7ߘIxx5b5jB`q&sp -S0;|\P.(i"s9:kwܫ}c^uio˛doUpp\߈1hun/%4!YU6㸸(MZA 3*WYۙj:udsYvg "ע+)0FKyiRPqbb.3:ljE{"P Wƽ.JWsB1(Fe6b2cdzp] *9LAϊ@9>i!kꢣǥ1u|q\3ts)4s S>&afx {M׼zd^eQL Q/dJ C n!g Y-15ıESn.Io^&{Ka~b*yxjsU\7rxng,'<*ssQ YSiɹk |\4-pnۙu2ŮQ}/8,94h#,]b:abao%]oE_rʽQ򏴥@i=pqA &>+~%L7ȂQ` Q ^ıZ?Ceu*KJCDT "k*.-_0{aJ!S+Ue25lLc "VZӀqANey"Ha\ʘ)= ӸpMX/`ϙ}By]#ҵ'AQ`1W%@ N[ >je5ZiUέ6|()yn8GC ? Um<3xfqIsTcۼJS̱^-s&}^l΢R9=Z0m-{h_U3ȵuOJY;کXQ*Ә gbl-ph }ܠa3]M ω%>ˬoT @R9Yd\}3 j.1㑳-BwC?a0nx+9rX}+vxPRLViUUCu+6}\ \S6枧\#biImH1,`,>!*]9Q\,<Ļ9c 5d1\˷:!]EjXPA@fu-Syz'K {;ѿxo=K.]ߩ>!\}Y~aMfp90T\:8y`9zYr^%A Lezz|29mMFqcp3c/p1+)%re츘smt2 n MŽ^%/3bNhϴ7/K,9#IG^ucڜy?%iN{7k719OUZbspxu2Ly:+<K󘻮*e[v2ƢR]6׼Z\JYE8Zh._08-X9oiiM_?sbIJ̼c-uR|T\ν4&b{m@@=TF쇓6kC+:AO ,/.%MTꉨPjf.0rEl@Wq. JvC^C_^[( [d zQ<@YX1м\L:1|(kb3OicxJqEϘp2u)cw|ԯz/6ˍ1ük$7eUn]>1rYefq˯22ǀ,2pAUrW_ؙőE9.P)=ImVxP/'hѝ>X*ٹ9d`!XI}1W5H[gE䤦!ZȠCaT}%/ȴ]x)}(MߵNoG*LF$ :M4EӒMajo'P[PhiPf/!z&CP$.-²7vk94?KZ~!5yM͛Wg;*@ʗ2V6 /'8S™$NQiQuq%[ͪ|1 "YU2Xӧ2M895ˮp "s*@oW0%]dK/a_*&Cl"/=ϧLÆ b[5|Ź\-q-~Y]oۖUܾb3n)fUXhΣxړpH },|Ã^ jů(*7:A:bp{Z(@usW/ҰzjpoPW"Wе-rE6ͿUƑ"_AqU =&^}x$Q AeQ&[jPk$@]mA CeJ-[܃k l+[^JHt0Ch*vJ#qqĶ*:ZM\Ƅe ~%8@bTզsЌLDXq bT0K8  .v UJ`K{Dcdsܡc,0̹.uBAŽ4K:!r32vz;_xHC+! -i{ t;7,b*K%m| ":mԪ4{$@Yn967dJbUnYU?J!dbQj <\b3'i- LK]ę?<@\4FE-PG*!.:KEy{1dIZ+VZ`B0RYJէTAx1\iQt\Ĥ2RUQD%KFy]O*_5wp"2P_8M=^2zהK zqpah~eKSaZ=+>W Q*G_ x)2b8(o _\ˡފ!ɤLܫx!pB)yՄ ;]M!6A"K [G ϙ$$Aj f qW<;bZeRƎ)vVh/b|@,Fi4az[W[:.׆=E-|AVHQKql噑X_EQej:+bRO+fuF2uXkqY*yi1YN#󉱆zk]œXx-;k^*.C1]}u3\_;[[\uV _,6UQW<ÚԨTMySt+Q)B|Ky.92y*khv 򒅈wU-5eghtωCIgw Tk;]P} ,7C=,qB8"[4Aˈj]92EA*%@"#A &CMD8@kZZDWpJx\Ŝ߈ȭX-w, B,wU:-7Y@EAvXuFO1([W (538^ژp̛ZuN*Sd0QXP^'0 ʣi|QDw9ǭB`Rqpl`;^+utQ_uP+kTeCv0sn[[0tPZ;(4)w*֞s3kꊋc>Ӥg":<@o\>l ;kT6Yaa["ai<_ʪ~ek OD !-RKaDXr3ɖVll'  _Ìf ^'2g0 Ynجƃgpr9(\:z/je\rnlLNVj]˯h4nߙNsyO+6}3+.Q ʭzi.Kǣ>gY)K/c3U:`M@h&,K ]c;ayFa=U`U:3 ^=Kq_Sec{9ixLJTS-aɍr-KX`y`eE1RJ-ܣKQ{V]]J5 ^9µeXJ;AK;9-BnE$].a` V| eYq$0X(ieey\*i5*WRō0ΡBs\>˦b ڱ9w*EFi*%-N!CRX%Jk[|E›CܡK=fpʾGJ\l䊋jb(Z) #F[QR۾.%L"_* ژ8Ee+(k1T#B~O߉`ץf-M?UYŢcCUW "T!58M&ަ&DAZDm0\bO2OiYUmp OuGqTUJeT6X`*3Qlj.$]U3k;84N1fF\b @~&[TzU?/umrJ;& ?WV[ڙ# AMo5?hm iވ_]e_)AstįR7A.\3(ø .9,eiUbm-_ 3yL(P a@M3ZT{L-1ZU2l‹X(_%#jcUU`YjbT[2”iJK%4敷* WX Qr < jPlb*4WW㤵fn `ƁU,1'LpV3Sv-]1ecQKWh7\(5nS-AV:fe08Vqs{E"Dk0R/@Je:Wzq)lsS`u3J`tVcUcPT (sac7yUb6XJU%l8r­!LFlx#c: #\d S` |KAUSi>L;Z >560AK:"Z9 Bw-dp­; HpX hEE@F7r 0<~_0cg*{H7̼~gw1e]8bk:|͏LK85c;LvNpͮ6a\j>B4RV59[Lj3R^`ix!HH>eHS3A[8&3N%'T/kLvc@|3-L _~jS~&Q:f!ШGKnǥzz1Q&}LY̭-`\5b^̱2[8z,H1 e8U dspg G`!_刅{ EqZ5u}X4_0*[pID&\̛_Nw왋85(0u^ngqv^L2'R{KSG|(d񹲅C%{biXrGKƛp =s&35sfW' @1Ms6Wo +,AGKq+hDEٓ g2>5,{ikʁȣUsNW`C?01 27571O1[w)"y'M0Kz1Vw A:Xm2L"o~`42UYz24}C%Y1H -,+S,W q UAm&e֙iyMeij&F%YMnQ&737(;S21 g{,BV0!I#Ԩ@--TL[k)fPy/`]ALbAx.lə_LTUoͥ׉AVmD7V^v(-Gx* L7cw-22bP$A(0oeKO&+"Z &k)h*b78cH[Jh sifJ2"SeM@\% @f#HCJ2ϊUyAY7[U& y5h4傾Rɧp U|aT&]@ uXuLMG,gT"jX8|GPRYR ya]KS2ŖL%{.d?[rv: fʈ."P,嘶Q)J'w*,p~j<|@qweY÷P]ێX^n- |cWFc V`6ろq:?Naiu80Y,;MBJÓC -Ǡץ׭b/[~~.E, J S5aj&t&j56|M<\xpɈE`{[?G6NK4CpHpFZeH5s}23.* 2B!t$+>9ljL1n.aS=EQ͞Im鯨n^ʂns`9qwa]0~cJx؞nEpoPoJ \n<#eJ[P70 [8|mo fQ]Mt&J׼[ucFqQ .&,ݟ\e7\V flu=Ȫ)Q |CQW) "PevT(mb9UWij%pL9A)K1k(2wL JYo\;Pˇ޿ sy:,\_TtE1PR3`,y=rNp 7r]o+C6?8[\Ā0Íf8W*7pls7^Hk{EA?2ɺL-Ʊ ;hRA20X6 NA2AN8"T`:rA#3M=0V T{mJ ũӘ13Iu_]7LyJ[10nSV9`wPjjɊrDQʨ5p۲7XR\%E ),`gd{΂-K/0h F&eDKȜ(sr3.b |q, ܥhq% cB+Q7ܶU49-N3rġyy֌}K4,^"PVs}CLA,ҌAJUOʗߛS@, E,kO2̵ WbP @dg&ݐx-2~e+1m9{*+WOV.}L-Jl7sle!P`&ķ\Gᅮf ]%V9/9KԴ]m#WMAqοYV5ic~ 혲r0Aq+lTU-XC+!2!kUߺ6䫫n]9JxI]u(0KXԸbF˻īT?@]{Ub8Fl[%lk e1 >n..Ⱦ?@"G/3OA"B+2# B.\EF{#7^f7ǡk4xErMs_C_ȵ^eՔZ pjetkM| V8%ДERbQ] S ˃֜"NY&QEkHȤ(LSqPKxԸYe\Tb|-_a]WFn"x\m+y 04HZ|ܡ /;7TM5(/R&krc^mgg]3JbKӮY4B"m:mEUn! +=tT问{ᨉK3EdY_f ٿ0rLc04/?E¯n/KQf:uX|6aQ*+x!f.V<4¼Dx>Y%}1*[ TrBVP̜kvDBRWA4.Y)+ &WTZm26)ʠUSPpV[vQK4y GP0!GeF*mT( M7 *W Mxc%2[,Ԫ74 pr 1K&Kڈx?{D B1 &o1 sAڜkf-fdQyw6CuԾ]̕a٘/KD#D2eO` 7n- % ;/% DϊJ@ \Z4V-W$nSqqLKFd+ͦagf:8\(kt#`ĢdSnZ`GG%{ JoRMY}}kWq,7vbe!b,fx0>R7`ۘ+[c1,81ΞauF]3 dyDs+-baLtr_SلR栠Su6o@/A`WPt1X&;{W]ܫ=MK3̥VNnشA4٘"-\b_v͢f LjQѴ1䉁~Y)#k-0-ECqx ŝh3-zouT])*2EH^Ezb118\e6_3*#u.kqNhX(‡$"N0isf&^3-[rq3w׆aok;[ơMKaA^%UL z*Dxm@s"-1k9z0oP} 8-mˏlUn{DEu`* K%uM%i̪(0=l)FFXЬU\D@RSĩ*=ZkEXwLF%h0oQ/p=: ɣİH(XD>ۉa+`gs rtJR굨"NxjXJ PJ!tNIkCЭxV"/eBT\㥈Π0<@@ X @Kh)xHo1/ +oHeMPvmp阪5MEC'@h5EsF! ޠJed6ܢ̩͞XZad° Edv4 p%*o"ױr<2= &\84 zi=Ko+ U~e`Q{8)oKmU`so׸Z%T^^҇'Yb[^b8ĪJX83 ȻswܭXhsܰv`f4(Sl 7{e1[~ab,#w U0S%V =rTowl|1%HRsB%-gE tѥ7nP^lʼnF3pJ#Gc`GZ>Q6G..RkBgA{IKD}P "&Dactk!oQ)kP 7nQ" \l  6!rIHbj`Z0h؎e4dcuĦ/0ڞd5T91ہ%.Mr7QS;H^Dб5Jº`Zaj^SUXU*qȳ+ h`ce(ƒ[2杵qx9eNuJ  pnU.yub!j/G+Yn +D&g9\j h)9a-Jjdr-+F L>e ͼG5Y"X_bQCt0Grr[7hq -r)j!JN '[ +gW6+EAú6ǿ3 S Fj\FՁ 4YyjB2{lPvn XboPo~sǮJ}CE)/1`׈hd"m ^ʂzLO͢`ɧ%#ԸqKSBPPսYkhK3pV3،f ҰwWW.`X1,f jpSuaĸY~T扂y?x?3-`g^W2%`hp%̜fzTlW ^ QH ~ u0D_Vba7,l 9c2¢onr!12[g* t[͌+M4UhcgwT CUF%Eˠ+E^ךQQVDkwp?;2.Ӹ\. -V XoR)-my9KoR!{)jo2sAsP lRmǼ=ŔkO1BTR. XpAy](JBTF 040 *^j|o,0Pݢ)ZmX 89$,JErmʙam3AIEXYH9a2.1!BaoQ%]++iCeZеb Vq.D(e]Q?1 #N/=ph]^oeFTj6,;TcWrɌpO%Tr\GABts5)naz9V5RÑF;YrjR &J*MXe E!ыTeHX-+>Ѫ!,_mL.jo d-OhVߙ u0!c#(RV¥ .Cq@-x4(l4[pvn0`ɃXZ72uRSi.>f_ĨWģ|A.9G3.po[rWq6_!x%&@zWQԿ?SS 03, ?x{DqPTiIKwqM\9&,4S?0` XVԡ\m)oQA9*vbTNIC_rlJTE`=J"I[1do׃m;7 zmpfNR\(0]GF>`.98gJיMy#ܹzЃn[Q34q41̻}7"Qy|bPQ+0)o芆GU)R Z;HpԪ%}crŭқ>% d(ViIU,-Tkc)o%^PI|lZcqCL G/fr0k].}=H8V$Ŝ8@"ߴ `)a*&bיXP>*\Bxwk~!@H?_WJNa)w}EJsS;nPx1*mPi;b7Q KK-l J[z˙m8W4 ۫h]\Pqk\̻D栊: &2n  i(DRIkB1l6Apx}Ц vHEUrn&j'NU3ZdG/ _{2pi/..h  W"1P)ꢩ,7PV ?EO5-}b}%A/3&$ȕ)DIl9 =ޚeHt71^T ̻QAyUʴ6栂劐J]GK(UX|J3+ey 2V+ l2跗75K#v %h삲kBF rȨ7[\hwFwt @g0ʋC "uVo/.&D˨)4(-]s `Whm~w \AlEF8a`VI-4J8"yphf%" ae ԰'QuJGM`f@UMdP[.r ] Qtcm;^%R;6b@h.OQF7C  R 6g/.3f4͒6^l.-U ;u5ԴwqT&w)uLSځNӂ5L Mc1W{]ϋ/D-"9n:4&}h`-i)a|P|KgW6 fA|y!^e }0͋tN,R-g +oS!wF/Ճh h9p S"_!O  8!Yf.*|<玠lBX^ FȶZ,viJ㻃qV5%2Khpy\2Eǡcp M4qnX9r"\ AD] qܪ'=GL;B+>18 -!pn`A04 x{s0sQ337PG[([,>]E} ڗBy@r,V)|Aujtu\)Z>GRYyQ^3}iF-G\3/^7m깙qdF(gSd=q(1^1ZbXkiQ5[x*^ =NwV<]>kxؠ7bSeWENZ;UK((V*K}-UE`0@nh n khE{cRǽ(Qi XMqWQV8ZI/rh_3!U<@9{UYc2hN0@^N[,$o2Z6:f8R[x+Mn:Bʑ¥YKιUƳ. O,ak\KӃیKpB8G2?)t՘f8˨ܡ\q218;EYyKmc&wO0wxKba>&A i~~9W/Fd{Hph:>%U.*1x>&ھ#o3j4wYM˾*,ac8[5_Y^cAV/nRy2{wf7*L1K"9KX#wJO5/kR'L:2A!Ve1jX -p j2uuPA-H5ߥ7<; \QTA&9DW/*A⚃J9I[hΦߣcYx+QP=1sLjH(z:8ҳShQAљ+Sl2\$^ij=($.Xf*%eZyWPb`[0%"rU5 HUlD)gK˺xHZVex1U87+vj6_q8;d+gyV|]dž]YQig(+9q3{G/d"SX4`RLV5mf)A0%{jz>"`ieX>%Ċ K~ȴESbb({cmv%*٧)e9y/qJf*~&@J]Ze:?KVK AxP^bC'q.W;@,qOqaaeD]BZ- ˹p";xhU{XSXCUhMUATZ~.Sn\pfD/YAB(c/vIAU\.R9X%$8YjJ(Zigpz 7U0QP\rV9 U{EFx\B[4* .dBPn.LeWDаY~qn -i=C1-eU ʣ&CU:<2\C\A3  # 6T2{9ʮXRs!]РI_pxVnuIb^Opb¦Bf!6DU48B KMɊ%$l,gMVXk*kc%Z}EyhR:= y u`XP`{QKR<|ų!c1Qn+ےn\hkI-`8eT8^Prtq|"vXlܞ`$h\F*|(,9?.RaK(ax򚔁غ3/d 'jyTaĦxk#,0L-> n%& qS)NZ0)$W0V"߄@g 1 <0ljƎ 1-X ͝ YbPU^K Nx9&ޢ'0eNV-@urdnj /[Cݏd]/8PeK+TD6^`FE @eچ*cm<S<ɚ7EMi]0YgS{ǩ[bsH7*ɦ` Ids}'[{J\dvD S6qnJZxTmpR,>u7ĺ{;Bm2& 90f*"` Kz 3!Y5r)Jifi;p!a]®Ă^k q5* PZY@R%6n"Z R-/*Y yj6*{Կ+L Ts7]r AX)~&W4l1(4QV4a̠Q!̇87 "6 @ޕ`Z\piZc`][wq- ˺vA-9︗Aqnck2<¦i{b j62i?)Nbb ʦ~#Kjϒ!iNsx)URUĿhK3,`/# g= ,wĤE4e_Q(LX \ 8-"*w Ö+J*WˈM21ԧ) aY0vUTQW^y-w 1caIdD4aS*hCGq6ۖ\4RS`ʞ݄▕FX8AEd*퀥_/lȧ8& rWob 74rJJhJ/GQe.R0Fb ۴wtv:%&b)sQT`\|Ee Gdh"r2rR&_̠^ͳ8*5)aC.+zTs¯~ӐJv]HFYS H9(s%+`P}\cl@"r 5CTK5|KTeJcj&ΥnbC.V61D|ElX|y1+fh+iv$H'w)8mh"ܫDp=;VA4)lwXMe@eeV&n)Ns&YAb+ªo_S,Cg>AiƼdX s6 6 {FNx PɟbXsbZ{^j5n$..ש[Dų3Ay=F׼XfĹMVF|)F(q6pi\1RV ^簾kG`n2'+p;?b,3k*؍ -4]+TDAA`p1R"b X rzeuEf Ej\ A+P7kGG!eq1З*o.ə gSh` r[eDu80;2]~թw0+ҳp.xSb\IuW)Բ``^pn5) q\ Js%Ϭ70u6 c`n<Ҝl\4h^"5ڠȽfHurmՑ4bZK[LG8Z drGȺ-{;; ށb}b78',@T@)^%((]0u5:-j.FX\QeNjSJ@ß S'z^Oy@Kb‰"ah!|S>qxW2PWQJbt0&d46'L^僛 Q}7('"[l j<6bݭ""ٗxl}: TFrϘX.C P;VR4 Wfexs)N{ԭt/7.0-od_`Ej[21E*0Qm"rvKS ,QٶXl[TC\PٵvKJoraqĬTb42Eê%1 b 'h cLeS yn;bҶʯ~Z!c] (e hQg!쥫k#<*'mYJcA#*{ :lsJ 1a{ͷICiA +=IgazeSò!X"Y)/s)VqBޕZR`/213+7nY::%<̠c𓈆 h90 Ru!~b;ܹdL:YMb3OX~!y8M fleG 2! s5+ek Kn$_K4QWwV7tA5e RŒM^6q3OeQ*hR= &M`/ly3|w)ZR9S.[3ZU^a?-n*VCDS B,C52(V2~bT, L0<=@*s4 N .񸢲(!|*(Ƙe1P5u NR6rJA9CȑnCSV);9"n^Lf#//>צ&MGFb!ϙ⁷8#p*6qP 5p*Ӆ8K6\7d.ndn/B.e >Shz2Uɜc$ġSRɄ*6#HT1TVe>B[ DU1Y u4ޡ<'haTe.~8 *iqK`Ъh-xH $!R[R˅Y?GFN ^"4W+U[_WN,4!td4hR@ )14{1.M.#C*Fb!pJlS7n,K(aB{man1Q(*'ZU CEg` ^R+DeKGp^ (Q.x {wٍsBJܠ 0ɮKqYp?0R {;6?%5Ѷ^n xH!bdT;.kk|ݓ0\/XڪܬWi|s*ws|]vQ+b^)U`P<%Q19o!8WluufUK${sZ(v1ẨĪ Pjh|.Ө%i4:ċh '0JR#F1QFob-ͭT8NMAI|&+,:'A,kNEv:#/^QTQ9[e!Td9+bdHn-3U#*l{`D8&BShLL%r*`يE98 R^28>G)Xy#whWWEtu/swl5q }6K,Lj#0[7AUtw`Cw74rj4殇2z=1pP@fcQǴѫj>5V<cedx!lZL:QMhdՆ'`cxĥ*w2sd-F\Cp<&]H yf7OH-tyF.pTphjd0ѡL_SkZYc_ܣ%dS)i^uc"e߭Wpo<HlXҫ1hE)Sy!E(%Y./^,2è71. qpt%.` F AT\oYX` 2 21J :Bҝ&(b-/ONhq/ryWu0fʌU1 4P U^8fYk22Mhˤ\qv/dp۞"s=Ux %^-Vv":( -gӇ/z5YK=hPg<1{%W_L\2Bs-X) 6̲;\,9̢!CpZ/ kU{tj62ױPjayAqƥ0;Qrslk'q/NjmS0#PE)tIJ-N|.:iqk0QYJk0ؤU(Eq/,%_12Ņd]e4ׄPPԷ^ddE)Ƈk|Ae47ر 5EnPAm T^em,|eB"-7pq4ⳣ،鶴ՖbPpWΐ].hs/|dnpK@m"@q YPwd"Ϋemni핓 ]0Ej0fٗ:[ǼqK4-E+Px2yx]w,w9\PJ' 4uZ2{0eEKqb)Wh7E];kwqh]e2]9)c'+F Ø36Og|Fj!4%k 4PwL mj=Qv7ut ^F醨nj,+H?g 薆K(չjEts)]*p?kN,gm_GFXc(D% ]yu'F*\BqRT97wJr)e@0u(-C艷з-Rⅼħ*mR5 F"L_Zӭ{&iڮUZƐ;BljaL W.#p zɣMm([&o6 8uZ*ʷyp`ž}K)*<+f6vhcIC9n*6G 2ƿx_.vێ+7Ѻy/oҾE[0ט_C,?QJiwRL fO "!_39L!iaWub6mW - K*PiRoP8lH K,0 afdXf b`!8㥟Rp נ8pܴ%,H%ߡ,C6`TeP ]}-PFŌIb b@3MB捯?Pn@6Ep.ou ݼ:N"6pL| Y-geiY ˡ2>aL&LQVu+ej=jbJ7-"N=Ī*oqG8JZ W.GR@S<39p)LZai&njn&FΙqJ-s3]T%B[0ƴ⁂XWuL@=?7>`s&Jӈb9>;66j+ CZqq>1.)WQE昣|1Uy]<0\+t&-e#\BL 6.dK:hABy6O18t^Z6@ 0 b8넡^=Kź]!6Tr1ECa`V8_҇a7kp0lbGm!@Z/{8k:d9N mTX !Xlv#a/I-e?8^Xi hu &(~[@ -^(fDMq_ q6\<jӪe=cqLjCF*p 3 ⥈;j*D;cPxZA԰X⥴wD`P[S*8*2ԴJw6mX*"U7h0ɒPLgk0kTw,/(90f4@n!ʬQ|->K CLF+ Z}Wr{r !X+!o&:,B#Y*p-N8!ŲKs0ܨ-w(HxΕb/@F: '%Ǣ͓Y9|'L*4ݐ@7 ?H{"Ze$e\E]^tK$VfaW@UG,TXiP*xB%t nd(|-ηԿ^.8k 1|E!͙`ȮuʣbHJF 81E u/2)uKkP'W/Y%UǿMf_McqSD=K `X1~e?`)e-TOCclw` B,23QG^k5I׈}(nҫ -Oen"@ݺtccj%҇S\yV<;w TKaJc LU]cQFTZj Zg|Jv9h7 \%v&)14EEx3 "̥YVEl|&7O'TDcH,gh%Bxo<*g6ՠ M^n?GOde31 3%[Ofu_n$PDndG@06T1+v榲4`jX?+Em-];)ME^hsqNq SnbLO,HġK︜Ri*RJeOm drJqQ@AIF}G+]__#Dso09dx&wYudRSc-f]vXI ( ص2hy&E `*Ռ઄oq00r!?pKE}DU1Pʶ؁'2jFhyNfYAݺHҗK@\sM/Z/MA@ 1ӸZxe[Ex@ݟ(,F,VRűM4t3a !]|ůBgܵ ѪB~QܥA5Xg=l` rToy.c)\,r ~}CS{ *8jafob da:ijÈIHأ%AJ)7Cr.3z.#W$H 6Ŀp'#pY螋^9ʄNsz& ϼ%Ɔi|/}L5sĸaQ~"Х\F含W.tlEӞ+ 80oc,#KmA DA@lu Y]1]j)),ɽ% k 9\oW&B ੸z(r="67kʴt,)Z,wT0m0a7/8(szw04q5ׇKºh0gB]Rgu,8y9PioNO):ֽ{Tu# D._j.YhyshY(kbqp4wIn96԰+JCb8U[(xUv䅈+ueIE1d=Jb1\ZseJ[Îɋgs-q~V+a|8f±2S˷80Я.<[AGeקMqj<YE/}*rfkVtn% u5Ka- 9f  euqxTŷQPu/B&PAȸ3XYc(#F <`˨8`iVmSs"=彽IL (0PCXޢRy%8>P4jp Э!4nKLEXRan_`iv'".Ze:NgfI栟XIJ&y6 UIy"ڪ%_J`E1 EJ\l`ɿ2cLTah%ox %2̄dyjgA|B9jfΞ#fq3nZ%]̺V*iTs96\F47{ )Gwa4 hlC(&/a2[|.S5ZJڠZ2{aCB`$h:wņ ];\|Gw9Gu)^(TZeXVX7-YJAbzlq/ J([RNWuv˶lWRdYDjm FPE QfQշP'P ǥ\ r.aI8W*pJMDPW:,TGRn5cs+{Rù\) ;u !m0)\ YR4" S5h8say[p4a(w 5{K b*:V4]] N86#pb\kYH R6Ub& ռԴ^]̲'& q4pՅ~M.t:[Im1r -5Q7 xQJ*l-s2-h[|bf8@mĠZ4D) W-;Z߈t+lAwbtpsq bCI]K\00\TO_cp.YvN 65A"#|K4JWk G~cx-S\lm7VDU>+VRpl % K\CG&:F"\RH+hh^.hhSW&ɮ rDm:+* ȷ/V>b /EUs0wpum :ᰢWdwqa_dQ "Ho 5⣂aAEW"e-lHVR'لfu AJ{j)] ucC<@_cu%81 t=ȶr*fڴufsA1uScwኵNkU9wkv!jU7~M'^:`K,E5 JMLuS˸b>./r"=ُZ/8c#EnCC]8bX+8#jU_55kLhV(7tDfi"֯-2*ǢؔEs} HJ>( i}@KTuZ<&V 0_@(! D 0LM?{`yR?87)p 1sM-mQ]e -AnV} A9A0&ap\}53ؘbT*n"&7;Eg5SѶc,NCCWR9)/ *cdG1!1b* %a骖[1[]*Ŧ*!x/D(Q5 ӽ@])+mb. vgPfڻ;.*8:f,EUcpg2 @hR^IYfw8wn49'&teмť UpXh.5>"{_nY [#Cbsv)&PafCmsy,A[:hl›l{F( Kš&|0d3v3Qa[OQr[$A|p@6BY eْUUOa1\x4t3'ƯY.l/z q=%V`!&f+Z̡e2b [:ʳ\f[QuP7w1Btx@)=-q"Q8.fNXJ&!b*-p4sf El{\xB3,n E~=B*Pqc ^k߼Ř_7ܢ& z )FƎ.4?;B,KT*L^b V",KRcdA,̫5rİ$7p(6 `IL@f],roq#T xpDOfCea"uZkKu*l81  j͖kĺ^xhpmPo @+G#;q)t(jݒӠ}+AֵhD ev65(jjf$cGb eA8msUxY R=w-xD꾑dp:v-ސ\0҇}Bb@LӘJ(o/&jn7b!U2`hvL|L |LŁZĤ2Ciq>X('*_v" a_22?as-gW Q{K3m*P92ũ\-a[K`hZ0gpYn5D n!p#j`qC$[eazPR r^fœhv†@k5Uex6~K@T*TZp&z8,Y{/թCwb"pJ&-%AtFqFeGb `>1 6R-dcWsH w *˧7[-Ǽ|W{ AdSoqPѤ]QŪߊ6YJmQ(_~zlKFZ\:B@W^ M pĮrs+F%Oԗ+Ǫ\KZ%fT:!og!*b]Eҙė ʷ͖ ~1+Ѩ迢QעyǤ帋y7皁GiiG\7!EVLBC93(9tܶUԩ l Lei ȼ !C`neb+0̡gIKJjrMgk@U@0p x8DXp*PU.RP#2\U1"{DԭڑM!ZKMmYP.nFfղJM' U ]"`g!qZ)lb_6pZlZ*3q5t9 J`@1ؑĺMy yG4PPFabi䖧)WU [H"ZP*\b!e-^rmj2ځw^``?Ԯ]9eMwkvyCi{5&(5s-xudu.-u6n+m fG[ ^#n$u8Z -7Zk2G 8a7m R1s@4S̥ەo0o 01Yf CU5`utޮ : mTQ @5GqĻڢ8L1K {qSv]cP]fKd0 Z[̩(X@H>z2D)Ljhj,ryj3*jTXlXsC!|OżD8u m2y7ĴU98*I黟F.`5UmJ Ĥ(ohm {bbNxb/£2#)YXW] qK2uJÄ[ +Qij-p CT9dY72ŃYQxl#1}18/82vu0:CRQ UqomW5uc55xF~&}ѦR8~l,C%cPW[#MjN%){&cU'2f܍{k(VheCU3ɘ(9V5q7ͶqkY.}*$ ƥaO!OiagrHy+@zH5 p1N%^\+F f8mqh{8rղؚ*)C '!XSo D pa_1-h)ץ*]DQ9 )K3P 5k3tf6`+~%Q5X>qeo& Զ[1%:5pX‰]+fޛ%WbԺ3)ߥ5#%#u^b6wqqEܤ8c7N&fs0(xb*tqU ԰[|*\D[C\qIRxi/J<}D`{F @=P@b&mf!Jxl -D#Qr> qygWt]K(Qs205-ye)a%^RxJYñ—P?)b)ZRMu/ r$ g<u2Vd1@wQj  v:P`V&lZ+,e\8.,̶~nf?U(x=P]V'AQZ( Xms S^8qXvg0e:F^j ` #nWc.<}ŧGvţ9?yrZq)ܻéXר-2|T4ʨ5H)ĘoxxcImA8]g-wL2J~%V4ɽ)PW!(`v(gȔE %PUq,TOqad5^.Ȟq]8qABfJ4_&o1|u S\ Юݑ2rU<1T@6rZZϒhElP8NQqrLW(YyZ[7{%"ȟpLUC`1([,qQs +; M_e*U:BgL*6%jt`1)ib%_̯c*%4o.J4qP / .l]RZ,Dc8V1b9KPq֪np?N@V X}*Y g^bmT,?q0@F%f%0 9.C Zsd0(WT5P,[ bVgqnP(BxH[2wb@-o Bt UxiP*h ғ~.mQ"8vF ܫu\JU YlrbSFBT[>ci^L|C0{͢TuEfu F 3V̥^9C*SIpr>z(ei4j̿7_.Kq/,4305nꚉhL2xa<4R%./C梦/ fkYw@a:f(ޢR5W.pmov\#$,THS`C0Û6kY ^1.Zr"Y_)h@$ЮǼF^bPG,۲. Tee"AIw+FalL\iL+ְbZ7w,A#Y\ ) pp wˎ`okBʡݹY!ʶ1&_}fQV7[mTuCxX~VxWˊ/QP釒-Ug:O0dA"Cc .kP<)#P2YfK *HXLBcMgdJl*¦EkAϔoX5<”#G|FY*NW)A2U7-bZھ +Yդh/V59bq % [26m͋5r:"" )BaG(+/R o`2ZܦDP癏䈥h˨~KN/ U[c4vaYyo&f,djNJUH4qj;2׶Yͽ@(M0:0Lc]b4daUPhU7,+6V#QiVA֣xR=` nZp%-dߴʶ*CWl9zhĪ㹍eonQ(yp39YbUgEbj &iHn{:Ԕ=[ADǡ8`P*1bG(1sW3O/1]v)!(eWcHqQLAܶ\c5sQemfaM<-[Ά%nF;<1]D1`!A|U{Ŏ o?0gFƘ}-& u eBSUeRAЪ \\v +Ī jC&rA*$XR NRui^vwd Ɵ), ԼJ~b8%uXȑseb2eIJs|ܽpPj`h)JmtI@r `U.h6+AJfw1V 7 Ud:ZrsykOAU qj]-C:Î}-g9HXWu,ڔV^~a#яDj:z% FR+уUu[bיq O82nD+a XQǘ- #sG ]Ǹu+N(v{j8/A*2["Rs1h(ɰ` ;&'k+;ဢ+dN"y2`B6] *;7M•K)SMx/[vZsQ e<;Ry(`1\A& N؊!wn*4Ҕan߃%k([rHxĭwwِ{2֢Bk9OQ5z%Ήu:Yn)Z 'y %=Aߓ(LK(ЖYӪ nFwxaI-PԢ`cCU6nJ ,b^9\`Ax`X P8Ű&(6LZ,*i 3őGEFn \ƹ\CHj @4 w bTBICVZ|״ëdR6F% r,Y%`i~ۊe D<0l4kQjm?W)+zZC]ǴPneF^8q,sDnkyAfX32g=jg!:^eQ疢'8K_ru 7 VҳcRRn8 =(%^*r|A.Y"]Z3OK.luJ V?3K=mz"Pآ3XK kDbbQKtӨ\iSM泈/bSN@1ZMwPrZؕ huDbbP٬[.`u-Q%=ق%kcG⛊ڬVPBwPץ0*VegElCS qLA`J, [-T N枎 :@C~9z SJ "}%Du=̱ľD W !j!0ddS᪂5s4ppAv(Z\5KeQs6*Sd[q).cOxs@k3;ir".UJۋTRƇQS:ĢN`X " 2kA uV(nV ܇Ζg8 MtV- mEy 81K-5Dv=\%w n5M͕-kT￉BHy4QJf22[• g Q_Her^ A+@ F!729 !Uw,&MKfTc7WM-|j"L vtߦ. 1e 37ac`%N$g)5# 2`_BNQD0AXAM?J2lun_մ0K=WRB3#xy#?6jL3,@B`p=n ڪ8JDB5z偧jV!X1l0WL̤% E*Y;k8^6P!J蒇6AP'3U{DD׍5&FKn'ctp]Eo\rd9` js9,8>>n!o0ks1y-*-b0r +f>'2QZfjba-r9ikoe ߎ)]!kCQi.Xd3Ĵ?J.a8xr_d]U BV2ʹzuPh# 4Zh,lK}~j< BEwF.ܬXcKxM@D^JB(f9Qխo1imCi`F_'R;] 8uT8Lĩrs#({cQQI"o;[#xO)ɝۨJ&c>aT3!VMb]LzLo quE>%0`Yt1I u2Y"1Q Z(f3o}j9ey b×2_Xl^+oL9 ;8Xt{-SRP|B67lFk(Ɠ^GQ+-`{Pfψ ҽ AĀKs:06fC(m$JypY -˥~f˨[èQ:;7h01@p]G!*KE-PhFI)vJ.%~}*QLA_>r`f!1pX1 ] Ѩe,/FQ_xW*C K qǧ})]+ǥ_2)BZAaӓ0jႽ*7m9ʝk ̵Eܭ1T8o?QB#2l{e$ڨZ#7(7iEO|̩6p.]Ⱥ?%{8˦u \%K"),(B![qNSV@(6\KbFE[p.=TE,X?dBSW-Y`bރ!1X@"R9AG(&'XrEjg" [V2UC@㑩B%x,"Wnb̛^b&O6ĠF`q*CP<"=Ouq|%ٸEoBR\X@Zq7^r `13Ik&d=J{.X~`ȇ>`Mhc,Z,+X׼V:GS 49.PBk ^l0*[Œ iyJ6l!|FZ`M4hm.QtHm88ܩmSP T5Qd ,@Ea͑QBLZԵ2y̴).FNK牐*7)JkP6cCV3p ß@R[Ylin ch3"BX.)Qe]3hV_vq lɄ-SASDJ`xmP`FO< ǘ(:QBx^bݪ& q)bDoJ|v,_ W%l&MKqqh;C wCZ&JW[ZFLVs$j(B pC`.t ]p؇yPY [4(hE"6İ1y#t6, b.fC[(KNq(Mׯ xxq S:x JV @*5DB]7W3T (A0Ur`at|Dh3C$%k )| I9)C(AB-4CDK8t sA+/. US#ю"]kRhG@ZewZ-G TAp]'fZ& Q_ElhbY) Fsxd gLh.,N86LL caG;Π`KfxEɷjR73j4n1cGf茮G%$W*+'Q"G✰] kij_tD3x Ȼ¥\1Ak'n̜\Zvlh㘚O2λ K,9 qb60&E 4f!vʩPղ#_pDtpUH 4y 2~zi)sįJw9%]@:#PWW?Y1y4⻕w.XΠ^!s-aUPjn-*QԷ _YTsVp[{I+SG#?"J2u1i<nlʗ*]䙘γlh+خ^KQXaBqB:b+ܪ jڗThU`=̖=W3[UU0,5p[qUX6ÀEZz һ$/xdtzWpP%J+*(h6hMfCRF-KN&IB!HD\'"(.`&xyvNk7(CQUnhkWG=@sx(߽Klz([Δ$w "Ӫ3t435MCp-M;a҆-rlqYc[y)-ͼD)߸VnmH>d+ ])X't<(X7{Um(*i[E,n 7vV ke.uu[6²fίa myqQ56YP TlF֣XQk#Y.xg5-wђ"P f 68jZ|L4 YPp(˜ċ: J,4Y @A2n2! b!&Yi*x@ Z"ͧK KCZECK b[Q/p0R!.-x퀹 f ቚ;/X{<!Y =W/aL_#G9&PG,*q P;,Sh;AZFTs,-W b*H!^2[g4wA1cVi|dxMj6,Q[W[ iGgFT?Wf(JrsmjA40ū É"( e稆 ELS. faY*xֺ칑Yz|͓!}~fIR[0,&2ef͞3#JU}Ekc@]P4dR*c(&RψF@^ Ug)p \o50r<T8@yCv_&m'|.Tr 3Z0r ahFKZV.* (q!n3\Z n"(/ Qc٨'[4X1%ntXWZII_H- u4#`8!fP5d9BO5]*屍UN͓A\L 2)vIwO\p#uĭ !ݣ@ :W9ZZ3@ɋ>@Y> YpWpԴ(bm$` ޙUya}:Y-+c lFWĶ`|LcYF׺CO).fTM#up -m-ӥcL:i[}CqILӨ7fZ0CXSL[Te,x\C;eU{m@s%v@%trF+4zơo$0R0B"(۵R7V UHd ~HM#*m- j%W y6unZ@<}rO 5"Ddg֩RVZDj@ tp..؀H؊X1jw," PfqU:G$"ʆ/+5 똅(׹7A9VSj}7rޒ+Vg U WS b׭Lz ՐxSg8fTu#Y*ddJ`PX!?BFQTiksSsc `bH lE1@ ^:َNUڥaӏh8e{`&7Եq;TWdU,@0))㨬_KhGRg)x^nax^C%[/1(]"~~xf(Z lΐ2=h'B\.  4WlJNc0 b/8d$EN Kzi|C:j%iE9[̢qԸ rMZeq*gY/n ܴ9F:&\F( ˿C~% ת_>},=6ALnaX[)@{DǙ`B@5c2q^Wmp\Y}ȪN m"Cr0 S'y pT]A8@ˇudKpcn_a-dZىSM iӘ7USy{*Vb˳ "ڜ^(J[*кـ4"Tv w7M3Z`*Un8RqS]Kv:V3)1rxJŲARjϕ.iS8rGZnkA Ѯ _ JPۤ*Qs q 8Ӽ =\+&^+"5iʵuR r>ˈx49&@<,ZjcY/="iCtኂ5MCDb듨m(`4XL6#F_(o` iAUf@8P⳴h`BD';lpa[J-O8W/0h-SXKTIjbXqĕ N[,e6a0nb4l˛',1)Lbp,xf \F&kx-XQ~]V{[$&R ׈0;F(b`-b{dq"nx{LU1t;qD>ɢX-D)fN%#[ppwЦ F¢o ͦYPTz2 廈}[<˴ۄ]y*1)CQj].`K"JfYUv2 4U^I!fP̭+&'V X1S M}u7hɖ^' Ze[~W]DR@U* +AQDU0"&>QSh,i2j,(bD*%-Wۢ|"Z?`4n[[ V,)t[̩tѴ2( ཅG"QW+-eO!4K `'.L"ݰFVZє% ^dNǟf1w4Ӹ؁lxB/U孈iC&n FrԠuf%+vW1ZPjÿEktFBZH*v{ҲP9q+!d5]b FfV ͉o'?mu$Ord6՘Gi/]nhnrҐ8Swf$z@#cT-_0.Sw]LP{ PUP/\ B+/cN 2lP'.Mjv0Pa7nKq}~ab*sr%h Qw VP"|a0@J€Y~BhH#u(WS{,6H`0arB{ZX 홡#9@ND ̭]qM5HXp-EPh`^Eß20a٬KN !WDo\Pg#[U*5T%LKpk-Z3"U 7+q/Rf0ZʾI(SLor<]ViFoYV{uI*Z3wQwW9@Yn1 9y j t mOem=kJ +^U4YɦR߽qJf*'.*Yms6ysQZ3p9%M*׸bLe 6d)iC{pԢUf Nw`(|U+:b4N*# j]Z1abϰ^Bk+GMR;XjP+2PT %Yx/g؊jJD,j^e,*%UҗUr~b 4=߼E Sk܋f+DW{g%;Vܠ0ˬؐNL4]VBYEsV^jQ@ӺLcשzd|&wd[ =ELp<16 7.`(qu4 x&a˴|4؇ **Ŷ4n1M%v0]8ug̵cbV{ݭ%emk HDkuWc8nܲ q0ͶJU瑄{(,0CV/!+gd_&?Fנ%5{&WwLb+}Em;#枢胩JgK:y>Uҹ*UJ54'كRˋ Gtj+OhEE M˪.MֽDuI3 =D/Vbe_2,&nÜS4W*mx7@qRs*Bˬ%[`Ff ޥ"ZƚBҍ[x`O7Qɹ`Ղlؾ!Gw m (ʃN@(\mp#Q!B0. D M8K zkئD 8MSyN8ȃט!]@O.sR4Q5 [*uģHC&BwxCkj& %8zjުaHޥ8(K8FڜA v|{A冃~1E}\U)obm R`jsSF${/1 M$5ƀ K 9*'E%xOFadN̟bR\-HAդi6UTv @Ã1قd~$sWQ d[7IJ^W2urS>aF+7|ja][C&9R 8b^BMG b99CV!A%Ŋ9"qFQJ]\Mlmvas«R9`8 rיj QaZ\!h6%Y`%++zLð*WprX+F \hXKC! c9iqQ%zAZmtG@3" qM/;fy^RڇFF$jH!>xt%=C+@ܵ vD#5Ye\Ew :{ (U^&0#h4jf`\,fjâ<,QAZ瘩|FG385 {TVmL þgh8 5h.ҡùtԥ3PdԥP_8%tQ@OvLJa(1T8{%½ϲW9<|Equ$^&)傪e1gQ5Jq;8qs)Լ`cI K")癱G//Rp=EUF}Fj;ys@.؂#kv/t+5(KWX;x=@"]IFy/\28 穃(9#h7V9bJBT*r~a+xhtiQ ]IXk5YG &|0CUKpFh @8F%(͐3`XSN:Uqph%\T0?",sHGXѸ52xMEyPdۉ`9],]UǏ^aZÆoM.^ OR-./Q4hPe8q ؖH\-z07&Y(nH- ]ы VHj2q.ҡFv!bP4nyE?t:GE"^[=JZ x[ fx1-[?8,3qlPces8>q@@IBU;U* B"vd5MGzV(0cFKFu7/V4/Y`M̹ee. ep$Jۑ37(Ui(K\DAF{ VeA`U` @߿ "(LÒZ]"Ĵ8H^$rJo,eĥ nR u(cE9&ࢺ69 0C-< 6q,  m BQHqFG&̍l/dъm8^뗹)Y)AU(8-Eܼҷť#\mM(jFŽs+)LU£Ln#\"DXѺ)o|&`jS!Us[\q+"mMT (K'@:Q6jk+{x60 ?ܧc,*m [pCdz+;Xp2 8Vh$,&lf , o pF QJ2V7JTF.xB徢 p#i,N+U W0hpa ,0j"4ՙeW}E ; $ݻUWa.+`dx`x G rٱ#EtdVx>Yģ],i\G&F@">]7Pf0ш/F5TKxyXETn~PT1'q2RҙsW+jYV}/R [ E`֝KK&xdLA@d .{f-V Wؠ[ZYx,:ކlXX=0s6@K!&q"DX̺w/,*-ُ3X~ 5OBg1Tqa!S+;0giĴ㩝,P6jL.2y0\*F=q-ip2j\-- mC%İ<(Sd C!t.*+fcQ3W%̦r{"]b̋|֊  v5s)58L]*@ )wF@v` &O`3Ե VpaۨիnJPbM6E1]E eT 9WZpNaJjc.Y@7-Uc2 7G\ecD(oɯjIZ4ƍ8Y*ileWBIDŽ@ E@eYkw*& iLNR$x0q2+wFZAvBt-Dwxn1-@BP"ֺ8m;ls2)2kfb ,cQ4U+Zw)]Q"N2FP,po w-bbpؙQWXsE]Tnj(RVRQsqХ Li%A0Ve*X<=Y%bi)=Rxcx2!3à2K. Ԁͷ3l#:Hc84uU1s]hD4T<*Z\8\'gLcYA#EqB["%u_?Ev x%A]IJ@1D:pM%,^=Qc_0Q{)owUڒÁ:c<40>bYҲ(<̆8 QyVDa( OR9rBZ 7e$BvqܾeJW7my[X"9iC5kB_SAɩUl pJ3 zYCM=i[iQnPs\DD4C3>'~!sآ;!v )V=#%X/I=ԸiAKs]!GV,YyTc>mubXHJV?hm>ZX~ CLJ(̫pqڵI,g2;/T|DrۏhDka鎵a~UuP 89DtAa+t֠9C[XƕcrlA,5Z:`eN+ e&ܲYK*e*e*65~%J,Nt,2Qfh–?+l ..s2 cN9ftanȉ^Tix ݲ0NG4(8_9!l\c2Ĭ RoXqnnHJ瘦VejU[Z [y^b6v&ZvedA=CrJ38dF*(h.PwVV‘3BQtv@d:cE5lfAx+Bn[oQ #od4n7)#UYf Z)nJM5(x:r ٹY&O (&T_h+ZG%` bh"^&i < ˥xj3eSĴ+=`]GR'Ï[8r uz%>h#K"1yTһEzVZeH2hG˴{Qn 9e?֖eSB몍ų075/\ p52KSm1@>,v1*R+wj  [Y 9plqRޡ36;``lv,f\KAAd &Vr1P0^ V),Wx"SM)Qq!.ɧ7|"_bf(X(KA*La0>w3cqC4L4r-{tLM$̂<2uљQTb+DT6oD9x7Hf*ͱg=l(NrX)jUqpU) .N4Rn_n J-d\*R^G5L7UE izɜŪe"𲊵nSwUY\ʱMuN8,&4kA9n) ,N*BVVӣ^n r[Q+&{@X|( \=pP]\-(`Vi!da^Dn*yTXu%Xv͚<@uD~LB D#vi`.s6RprvExJq:]gf3!9FN6Y]WɜO{ZvFh 5[DU:퇞l,w6~LQi1x@r*n^pi(ıƑ4! EaFU1 5zeg9yhUqR7an\VM ST#a%*Hq^.H "\q*Kݲk/z/=z>"[PCL}Nke+BZ2A-WK.2npҊDx8kG6l5!ac~#w]z;SIMK6P2g Ի Y9Y g2 !Zf%8*vK=3H Tg =Ŕk|% ݘ nD"R.nVꑦ)vmRg%V*`[A՜Bt(,b\JDrj-2% 54.Qp]L8fBe)v[Rn[4/+9f)ϝNS[sW6_K6mK5G QUaqT4AVgXxxFSM̹^ٕe^ixoLɘMUP$KIVJ)"t\u(b?`dR|-r a 5tVe\._a( E60)tf:@<#Pߖţw'K\FKV DC-V2 C(4E%!&nG<9W7B Vۋd\8d(s(jiPw^I`AȶsϐUl2 _]*p [3'FXhXŹ"bm!iT8Į7%62.Цv4Cl!,ܱC%˥A7"yDڪFoL'bzs 7P( ycܨ/ uXd5o"QlUBK `*yLk2e\pw67L`&|E`VJ1)QnA9p‰KV*t ^ixC]buD".xbzPmv`]R&N{G *XlmeqW/p0**IG~*dZ/S%JtaCly p,(4k'Py_7r5!t^x_ģbʓه*ey0j)J2,y*8QKtop%{b;+ {l q 8(Se% E]K0&Mӎ0IiIa\'4 *dD( "1Tcf,8LDd9>vIFVӂ(:x.4!JiJ7Uwnu(?`Ç֝ld(jq.٧P-PmƇ7'/u!}2Ljrg*(< 8(n#t35\,õԣq˜FY]JAg9/LQ+juE$mz!QE*-{ igs1}F[ /4qS駸wwм˹x?~?,YAlEYD"z\zy娕Z-sk  s>~qz`e]7r 2jIF$mDHDyE6~_pś1sg2׈8q,C gQa RUd.AB* ic =RͰi_DJ_ -sf:#AJŏ/QO!T6Sk5]ġjxslߙ\o)%n3(K]R_#is)Tx`Q7 0(#_;M\tiK{]TǻxS[j( y)eg3,&@ʨ8i2 @oP\XVLś "М!ihnL};Z6cQEq^ 3-P,l˰246AIrUveZdWX bl%] tbXC05YOA{dj5Q4Շp"\0{AU˦Zm)!. .1p.M۵S(1NXQgkpJE( rƣm b1F(af\Qʃc2hXJ++&DWW/fۓ1`ZeԲq2W%,˻W!٠H Ƶ9E@4 W<2 6p@tM_RJwP$icZ]Ĭ"YAxW6qWl C #H51j(Bf8dBV[co.ܻW "XDe!R -ȼ>H|2f#Tت1ye 9CF8CS#ej4Q2c,B5foq'41?/ wOL`f0j 6ZU-0%KZuK[?+Qs=MCC^ Zq_̻F,7gP * Tb5a^ ~b@9&%Ot8s1|V{D nQa70VHrS[zZTK.:;"4i.{\8r e^uRB)eW07I*jTK7#7Mq{x^EVVqڣs*ݐ[*k2# d,L 7 \6GDTH_EPWqPAM\U7fcXxK r,X JfPVh*C?a`cf9])P +EZ'(2=UW--l)Y*Ŝ1 UuU)a0)PpyNv,Ҽ%y@qBd-wIxRXo QבUQ`Fo*nQlW 07Z7nDtM `qIrsfJȹ@af; ܹ6 hkσpT[nc!BASD/qJanwEФ5z/d\ZҾN.X%6`BefANK^%yjЭU\Φ0*1E8Yd3P1,fDY("pKlv264e*9U ubD84k n1إ953Y$;ž. Bo$YEwcQ4COjj/a-S0DUsH]|TzYWlT/q R@'<)8-"Fi5K]+A‚)Y2R1F`Ra#@Q.:A<˱ vHLTC]DY֦$5|CvP2lhvA@eX9+p)v@*m -nGzbK?QLġBqjuB cewg$^~7:N 00SG YaɖZqh,&.-IJjPp"K(c̲խPm_1 1E|2hF]DkEonEn-$; Q9>C,MQ-騡2^bQ{eջ g~i"ۉc9N8ÖV6\F9pM<3 LX5I@sw "v."Ox8U- ӿyUo2P4 Z-N=Qh@!RVB(d;繸t8Yw n$p3hEK2VQj WPoXD= sH$GQÌL}>YA+qܩ5I#h$-2S.֝iψ%5)j 3 D3zXhQ"P`9: P-A Q0,LrR-3o{7/@9= ud-Sˌ(O %TN3.rBԼN TZ£, 屖rFʄ bwv#vGYpJ2vP9q3P#f=eCF1akUp6&GP; F0YU]X~WZǨxS4؃  IkQ o&x{BZsVCUbV9L+.`P2*8L f.d 5C $E+E@5tAqzuT4KPEVG>`5f`Q b0TKl`œq*Rr.ipIT]p*Q ݌p*3=d-`.K+`DD-`5E8J*JdtE[ẖ[XpxZf?B[xYʔ@du;wmf$ZLRk%;Bopv x`gވU b# W+T$T3,\;_3&5Gd4]" %&F`Rc i^[ `Vy"qgj0,[/0obb̲4Pɛѧ,]1/2ȩP`B4^f[&U&n ػh"Aޮ]aHU \ Nj6&q:1(Vԣ7|mlAZpb, 2ja%/;U4 ڃ Lse^qvLbF4IW k9Uɶ->mQ-gaQ林dUSx5esZ =3$jEH7L=~j%Rs0תa.уUxSrŗkifyqjUR嚜U~34R1/֮sLw *90*Bb0d aKuQ R=QpġZ2r;:H\+pXcB1֩g 5P 4Tj]Ebݿ'GȎXC bhM5*lG Q~[)6j1b8#d_*š,M@(:>2Afp-Zw1e|U0k(8d5t԰cD^*XAcI`+jRag*u{AcU \u +TP(% ]/n{ MDN r/?VơXi%\W8ud !NF Y:# u.- L_Qpܡl+uZ 8G)7͈))W Bla[,TQs-k Ĥټ6%&40L\+2 t[je\jafiXp^hPEѸC+]P了 ZpĒm 32,+B/8ܺl~. @:pɢu,!CFkr60u3972ݮVz(ؼ 3yb-PJP:J-`[((PzfLAf9۲^PgϴNМPηU 3w 6gY}B"o#%[BE 5Pmd^kIDZLv-cNv<#'7ˌLMW0/:W5żDI71ZXAJ©s[yxs)0Jp6wG#mdZ]bDg0w߈+WNmx&S ebW(r`&\r^|3m 3;GSܬLbp aH]LYNGzybɠ[\i%΢N0QfC1r{/x`5uLTR*+ %F9 5Q'%- apYJP2p8T둙TDh9l"ˬƨv91@Yԫ12;XxOPE[iXZ2a/%, A Y97*TSz]s#TKe3w6F˩dL+}TEQT.ϴEdN0Q.@YUA#bb;cZ!L n `7K{ y  *`MKi?L'r+\Ui(22<.P^2a*R.u.8XbQPZN`|snE7"XLQe:V!sZ clo BWe71T Y n3`5N`mVuĤ@^HE ~ +JY8egkʷ԰AnՈ^2ǰQ @Zq,]Wԫ=B^# e7Ob }4*}穓7INXR&&s|LeEpC-EQ: D!vDyiorP jĖ"2` Nn$qhwXyt":8Ʊ*ıW!i2Ά%fɆ`ts--ن%xk< TC`%B9zds q|D0o,5DF`7j`!Q^Xd>|ɜrZ^#b(Zifv4.U`"e˕p GCg )f/|\40^%סw0gmSӂ-!ĵIz5&UT"QUДFwMyw* nxQ(%5K'`-q6&w#c >*Ũc5;%)\6}avO$-:l 8ý3JH`# 3.!PkRk(76)zm2o8 '=Ʀ ؠeW%|(q (6ݗTUYueYU E-[)UD : E-  Nάs2MGAyS`-hlS?" }xc*P7)G'qaŮwP  ܠk,-տ0/5F+Gĝ 8{3 UahX%1L0ќ̐N4PW{V87\QP5u`ij9T8YTlĪpq,Pc0es S@bL\P{E61-:3!m[[y_5S CqG>P,囦,-PmܠMAx YX m0D 0f,TN6 SaMb uY_!VAvspT-/%wy~`؉ Xmr0EKA\D,%U[FK:88Gqjtf%Aj=#% DVu;ėv04gB,Fj+]1PeZdKzh ̬d[¶Uf#⺈j2@XYp6GeL: Ag',`Ԭ*``qj0IB0@6rз[Dr4L!Ȩy] >qs9 ^z% 1 7*q_H J T@N}&^|#ApDU: x=T>('`nRR,T-`9'h/Z5eDjM}͙fM3LNRw)q_hkJ{ Ke+Ax&th)x[fP}v.1_t #-Yۂ(|Š0b*6%UVF+p09|}n rbU-)N84aaCUQ@j`Y:lkfBɁbqGQ3~Iz D'CORťLDjr.+&EaiD:-Յo(,ZW]VmudAeJ pYLZܦ ePYvtbqKM˫C3Zd-4dBh\7W-#v]B+fP)CORA@^$X8kb]nG7-\^;QҰ {"eV%W"G}qFjpUbRo,)&e6 b2Ue#'<0Z75 tūEh eP;hX ˂z3,ZM kdq +nq<_1(U0E$Fk0/AA$ @["֘R V70E@]m_&j ܴb< ] dy3<~"AFl@S`Ԣq--LOcs"9f۹ܔ;d425EŦP^꺸fы;|BQh@STABl]ѶQZUdȯ~efn8@ ŝs  ӞLV rBy:; e7lVJ a"h, Dġ 81x4@oo n`VKQs2׺-+);uʒ< - Dq,hKW+G!L뻽@K[~ U{W`w,TqGtqۇ%c̦&O3f j@Z E2b͜L1 br&AQq9gvh_l/S_A-{ p)Hap/w|TVX 7>s?"Pq9G;t%1bYwe7;]07EwUZ+˥_-3!Jk9`DQQU4P8keb0-4uJ']mpy&动M`;yns%-,)wfET1uĨ*l"%`!H.aP)؉A}P19U^+P9Hp(WXZ0BH[7KAw?hE6y~6xź%J$./#/TP/>W\֏ǟM'.^ !H@T !FPX&WYqvmHa"WX85Nj@,W ̰b>PG#e9G9W~G#frALbkPxQAGkRhJcTe,]b*>% + /] ľ*%2L bfӞA f*K.H @M w9e3 BrU „0Qms-Գ -J:4ȂxK<jUY3\pfBR}z.\ÄPo,q(3.eqܪ !A0Kj 7, mh).*, K!fonPZPԶӪآ\*s*r0UU`Cb6-;rJKr*W:uK/ZT^29]@0Uf1Xb+fX& 1AY= Yk0^|MP#TC()ṩoE= AcI)X`1 cmTCFnPrE}Wk 3ı_q|EP4r: 0#\>ޠ̋@j!M0r1a-5HLZ[>к,uRh!KģNָ)aGlN=380uqγKy#)T^P6 - S^"YX5T:YQnlf#܎VpU22[#C&gP&|ODNiU+tnYV%f൥KF[Rau>5ԻbLLyIY} ˜H(myU\U4H9w{FQ!k$pwx?uƛ/=+Spi{ؾMK5q(VI0(9+9ܼWDpa{x{`*3! JKRꫂ5nx]( 4]­U̅lVTjmeg8@-70_ʭ7b[3hcj7qU 2KZ` ".C@.f%xIQābZ ) U00;eͭy,"Mf13kpB2<˷9fF  EJ {X/J\Rh]4Q[*P;ל{kD€ n1D}!..+z`_ӁnJ0:UKDkjv`,fcv4f#5Zø Fy3p`Xr$T+QD KUĨvw̬7Eh5,P_DXGћ ; eI iA /p_LrZV}/X R!ڳ#G|´9xZ1.Ɠ1l;q)k,r*PS lg̰S84:MсPE.LHW&W¥F\\&1cEԻi&ȑpC2yUi[+֬[)/> #=6rĚJ $ŹR,5QhEۗ,±nEh(2eBμNQqYUUڭp<@re2Z]AJq-(ԨQ:4 4ŐY)^QXe{A wPM}DEXf+*`M;/Yf̲_Z4TS3*sg%vF n`B2/8!A[8eY5l#c=;a.X+%Ϻ`A vck~];Q\&%UQf4X[PA@=w,@!A^CǶ(*q2`+(]Tu⛗G4-_%+{ERvw)/DD1  9/[ LLDJso3輕`ZKj4SyyBYBq o@ Ad1(V̱ 8n! bi 1 3!<Cx2XCgh*1SpI/|,TF'.|P"C d#L=^U&wDʻM3rdi}BMϧauM S/Dw<6 䊳wP m#t S7 ^bۙEgB|%ACwcc3Lh[*pjb,ͷl*RPNݰOf{j**aJP-k9 2}ELj,-+Sv 7,:,FmP×F?:tW ]y+Lť5Pv"+Y^zEђjD{Qväf̓jp?X0l.##%š+U{3:.8m#xPpѭXNSoɘ8^4\@exejBE„.,QGLTvôd2L ;3U=搵2 PQUx e=r$>,j,9"r?+W ej]2tMKP,0r 7s!f* (j `Ey\9/8XsYƕk!j06q@US :\PRZ UZor|M S VXʿ-Sj#DeVJyj0,IpUT-`ѐZ,|\J+D VRs,e U1AKj A; ӪXѥ{Kp;;@<ŽJ 9  iI41ʻ7N+VygȔqc=K,6|.S&j>""'| o'pp9a6&PHrfou^/bg8ֲ U9 WX"[V93Mѩu9,ܲ[L98r"Kdj+`JNێk3Y@Г\ǔ7CZYV( 8 #]V%bɤQDsy.~Ԫ]:3>G+t 4TG1TM א&-3Eq⛉VbX:j$M JómPҩG. x#kyF":K5HZUf,Y6f@O5Pj&S)EaLUwyڡ\=AN޽3|\(K& XtyP`/phS~s( Dh ,]ט^h@1G!^pǬ:fڅ7K{t7HQE]bt}*|WVE=(!Fmwɛk/mjBbbYE@qX)l͜i,-Z F6ct2 \& ,pC7h1 V-S.0CkO ԡEt!J%F.-^A1.UP&.:B#!SfUuc3F4b6Ģ\1`꺭◛H0Qe^ch '-\ӽ~ -S0leTB_GKKS%+(#q ]P,%9XK[2䘆An^EnfݠJocp#Ŷ/ :|0tZrP--R y(V1c|\a._f[*}`.MEg,BfQn,P:n|eۨi=ΈW[j=!k c%PH^s!U 4;b uY3P ]wFԡ}͵Tj#R,p c:-1gn"D97&c)Mv`ybup׈֊+PԨ,J8k8GԵ1ҡ⻔B#9k%P52jo  ^I@CU52&@5#r!6^;ˏ'ghWR4fP,‹;t^i^gW_{a|\WP@*eeb2PKZTW. a]Fl2^:PU.TjՍtxdy6X(iF‚D+Pi*&!5,3^,ς^ b*C`Ljrz+;ֻCaW^.q1qmin42يBWux5+'x 5EP9PPNGA`yu/hs-pu 0P )x8E6@5= 4]vT-پ sn@Mv.`)xf q ViMw# b1Dv^ޚLfdK j2bnY= nE4:H1M)lP(Q*,t:.W,Ճr%1![Ptpİyaje+CU , n+5T ld`zifF̬:'37̕b#||j՜L&{̱ZQf-+?1݁)C-%>di" P@n0p]k8\ a?at[ŢPY]&*}.qm:A҂ 'm iEJWp%.0kK go蕰+^eTCQpo#P%T jyqFD3ؤψ*eJl Υ4weEaez\!0;W-S aKYSUp1$E5ZwiesUQZs:?%63@(K;l'}1vaъt(b(;^mj\3z7F\wAX{*S qYaCn5yGV*vbR̻1A,81 F+hOGkj9b[QZy\%sFe,z:5v,Ф[kȐ)[,!RUĽQGS;vE*&UYҚYo*0aR5DvlZ(*4P2qڪˀ@b!`ql Y~uF?XeqvLҐ\2-smeTkF^ C%B9ep1#lPRgPVSLg)TL><\o,rMn)ST(kMeRVqsÈ^!_hּ04@[vZ8͂(]Q㡉7gKIoy3X=9F; ZoP+\0u!^Q@Y\|@bt!}QKZa\E[y& WTE/ 8):j,;@ʶl8^Nkd&^ŚO1h3p#qú{p)DlUnjb+ ob Px̱puD8g1Zv|GYz)G&Se3w 5U Smn+KejQ!M ~u9=bnDN(0: AmTcƦ%n f8!|.73,j|%&!3blyT+ \U"{th٣ a(9"gLƃhbPg`OjՑxE [6asܡs:4K[aT7dYZyԄ]i^V-bP22eu)6ef,pM(PѨC o \ig%\5@T+.빁Gx`0hǴ#Zacs0:y2`brrߋbXn]ձ;c _rmq(Fpr7s=&MXL=; ̘B zc"MZ Y@*QJ{S LJj57%.wӊTWJcD:mPdz#j.%}Y P1d3]äfE1L&U+V G&܅zp3y0%0#@ "#}6h_.Y@^0ưa@ ΥgހB16{BhD1 gpaW^e'Kp> DQws4@Y( 7#>`]bk܄uU!CE5W'.jG{@&>Fɲ I4,tV J㸜\*/!- ,U{&* JZF `Y@m1P9pPʱ7\jݵ8ӂTQ 9#e9;&L ? 322 lnc ax&UqMjqX [fn-^D̡Z˱{`NܱTʉBa=?0I<, `ڧ hy!^;9Gg,h0b3v%!ur92:T WZeGc|J7f؀Xy :-F~D'L+3(7o3h)<\v өr0K-` RvY@ Qwp`(nɇd$U(׬@*KgлCa-X2JQ=`9 _5ā B>a%C_pL*^j:C⻂df%b. xraZKsȗl8SF= g@_"%H!\z)RzJϢ\ut=̠TJrDpӇDfYnSE*Ne]LT6˝BH< Зm*kti)U_̠g5!OGPX)%6FUU5jɐUuvYl@Jڑ bA9݇oqf.ౠՍApEP jȋ=(9uU84:GYxZ%ȱZPUD T\r7\nJdR%[J;W1h##6hnW@owL-`0Š/]q 8΢g\2)ZoSI@wKVHuJ@. g}6U`ܵWq+Jn ;;Ki&" ^jdUhjnhd{*V˱ݙkw ab,-'AC]:3 HY,l8`2HX&Bi:-#|c*4-c,:"\`_J0~ T̠wVK'/9*f ,TvP-G{J+e.fd"]k[h [?+cX"+:lTl"朰x/kE*B(/{"ScÈj / |"W1吘Po8f BZaő0m`gXEBk /z@ή8MTK5YyG'rD7Ѩm)9yR*K_1KJ`GRWlnzZVZJUՎZz7a֮"d}WnW+ˈ֥iMٹbVWQL̫ ,,@`ej!PEܯ2eth*V%z1 _2[%)Svchbvۘ56Hb 1"+\%s)*bsPh 1 d@PcQFmeÅY(n,Vq(RU@glgP]J),`CĤO3:u*vil HҐm2rHpCfHQW̭0@@$qa+"/ı-ژ3!3vH2F`Pj搄a.=Fb\muxUa-첳hQcJ̬_Vb\F fg*%f*Π)48ҪTo$p|BbJT!* <48b!S:g1TC-o{g WMnW!ILVꎁrbl)ˌp,VKՂPY{T!edvԠt<PJn\E>eיl5tXĨ$7f Pe1p,d ƶr`@(X*S 7Ef.5aYzsJ2"@\Q `TsĂD0[paZ-8Tq.(.z>` oP)`qcBPح֡Ggg3'#.XeNh4@ NA.G˼f[^apP,NR;@&uŹZh2ؤ?18ϖ,Ö|ŹEN_H]\]sX]\H/_V%LdGC1+kyn,+TK2eh SYV8Ak1HUkxh"3hj RsQEg&btn1`TPr˛Rb3!# bօ4+ݸdPZg X9;Cq[ƣUndQ@Y{A_CA.zZ}#`[kl V(湘GJ,瘛/`3qBBpUMaut[/"Fr&`qzX"VN`K"! \3D0^lܩQwbRVGoPM[5(h+j (kq%%ule- ʢ(43ԡ# !x%VZTj%qL^r֔}fLJA"E3DPP˪k@1to'1Fznh0|nXG!gNp ,IV.aś& }7,fi^ˍf]u:-ElbQ0Le(vfhpaNΰIJgGqbʹASqb1\Km(QPT[%኶R= `Kፌ9J1tj]HhB7lB֛AaIa;=r[; bI5p1u$LAӔ B;`3U "s(̥3*(ոIp 0'P,gPAav*\Jb;a-L0q0j E 9# ADA}j fa k%M}8n[7\^z4XC @ǡ+oф:u Ut0.jPW/+ %LY3+ % G1(3KB`[P:h!2k[<4T{b9+Q\LTq.j1l@ kc,Wsy-]7HJ jZe3$x!47vhEA9f6,xsu!h)ה&z" SR(v y(pb a1Cf0S4\2ٙ~G@KjVXQ7zJވPfQ|؋ !ѪXM\TFy3~4XTm^""voݍbXЄʔḼtZdp3+e`JrSxHo,蜃f$h 8.W4cSV1We^BQW*ɯJ*-v,eeQqvH%|s :DZ"Caݕ/"VPĠY}ʹen ,,#\En(WV*,Nj]A%ګfIG⤶0m/4!vAY;< bQ&![lc@+y7((YbREݛI)nVܪYhcV]K mVP.Z[ !xWK6 v-o$$[2p mLz:sǴ.̈ɷ:KelƦͦ&Rn2S;Ic6)؏d c_k \JaNqANHvV,_1Ze^3Cc+,K*#\-|3Ap3|_$bۏd5w,q<8;b+xR΄+)\vνrYX3w o:yLl͇Pz@[b`&!,aƦKX4{npsFpiljrV*5F-*VN`f6@ }uPP"+pVpQ,\KL]J 6@}Cx lp<\;P4~fd>` B4H`0,+HS6JMq%mFʊDv!AjSxڡlciG/ނﺀaءkT \FTm3 xmh}@ĶK(eb*WK| PHfM״|G$k|mQe4AR-PnEx/:Am Af%r誉U^l;Yc.jhR8v1JU̠,FjfKgE?qpJX{93hOמTrmw$4ag{DLVkr36@"TpLآ^ncV>Z>jjfu%Ķ8l@7jƢX HȻ]vXE^+=2Ktg ̢A*EgWgqlXA@deU U]a( C6̬,3VUU*LQ!h(w,.)WJ) eBP ZYħxmZ1wO&E)rJPӜn- :%3*C+Ԭ-}`NpW8PnQql]ciinVPT n1!++N_Q)ldF.69PnXnPzZ Pn`5E)f"^B m*e8VTt|"@%%qKѵ jn\N2MQ~` >+LFA^<ʔhiPXbP&V"g8b,Sx\p~RgKUo0_Wy嗆htMhc ߘ&(*(:nqE\$b^C8Y3-Uei\;bEʇC@rs4VeLHMX(ܺnW.(sFb$AvKfڔ+*|5*ٙ.6Dߊ›۟!f58MhyT_2[;jZl'8IeAc 0߂ {Y) " w"$RA԰WjB" 8eD QߘPcPHJP~b6);qq ],S"3a{;6,~/? `(,+namgY.xsqxp?8`RGa(8DG .BKk!5L"%~ kb"~*`9n1錌q.#4 S:'oW_) 1 %3/ 蕭Ƈhq؂|.퇓9Qe[Xh n@XH2Sbi)v̼TϬQ%ߊwR%ʞfzT"C^{_-ŘOzb}$3oј*.b 63Alc3ؙ`𣅫RmJ๗BeeSR\~`e1 #w3mܯY &)2>qRƃ3MT{y pUm+ϼBo^.#5Pm(2@tK5xu4qy*=0][$*ǜivU'p) / .XAf4!RX`]THZ`EJx,]X*)$PȰGj4J{а8a)Sfڏ)n7ԧ@;ˠ & Mf`vlLahU[ vvn '"ثu*J-05*l9Y` W;1ݮxlޠ( J@]3GQVn0V"|EE `\4(X fߺ8Pƥd U@sz_xʃgRbfbBXZj̎R&_'B_F,hrbixA>Yfmp0(stAMU,pRe`!D#apPscCTȟᘃZ`w,=㐺2¿#E?(*( iɸa@_kLQql4R`x`*lv -yly@P/ŵ3LM')rGscӷjeigG=De!@;8]mjx9Q2w0ѵ1.(W<^_u.qeLSGB nl\[*ߘQf.T9VVu/1ʇ3BY2hS1+š4`e`u6ޙ6q\QPr]\T6J֢ͳs(V":s @(QPޫVoG0eƥ'+8hd;)Mu r _(S#iS*nGG>jvf 9%WljĺCH'ultMհ4Sw QEwFnAҕ-7 ¢`MB/n5;:oC+ߴ gp2M IEfYE![׼?cw[yU-7R Ulh*{"A/]XCj*5qa,{K/5qJgi c=i( Nf3 |aehsDRK}Wv c$ZPF&RYW^'@b_ 0@-Fb,K# )=E(iJ36*Ht/mG[1*#_w ֥m)Dp*Ï~ bF(ȚaKVUDˀɖz:` FK K D*-!]\Fpp jYӿCDW54+un*T(pؿc^ؘXFQFʕKx*TD A`f_jcBn?Q,\!_@$Bx# 8 \%FeND$HVch 20+p`SddԿ0kma|aH'}Mͣ(xrDk,-rRb+o! R 8&rc?O`[h)t׎_Wu~0Akh/0Oy Zr, . ,_ꊧ\"L X]YPͺ=1 Yl_of0%o,a+WK ]~K@{}VH+]"(EAw{\|Wvapk3.M*cat Z.+cky$pޣF7e!bֶLeL4,⸆F`znjp1 qGLE֋qcb{5Ci~,p3\ 4|@Ts!8Y(#|{b)ojUhGK 2EܱjP %K;\ћͯ %[g Th-& oy |s."jЙ1T^z0L0܋syBQ(f=2:@ج-HJkUW+ru 3Aed\,˰$ pF^emb`7̪#UT$63^ V E+i6!qH 4(0,(&ANPP]wPqf!s`ej^Zb(UFoOfZf.r]2VA6u)[]U\rAqňwu dfq@Uw0(LЇ!(!kP./X4e{̰@ "4Kn!V+]EPѨY Tm6=Jwa_bQNR*i}A,;!U,ty[edA]dZ5fĿ<[u0 `1a+YeMZ)T ] kSPBY-0,w\PEZE,ӸicfdgC`- 䥕k*uNZ7պ$ϑإa|L]b<y*mJS!W].ȹou.巃w% AW=wDBc K61&/F9"*-YlTVx,hA|%\ֻ7, [,滏`:e"WXy% [(4bS:^c }E}b7h9MʤjJt @+1a)wu~P\dJ |Gon*<b%es<ˀ48JxD_]\̸ 0.xv~&Pd7,k(T.m0ߖ(SEߒ kVa'@#%,qN1u -@oi"Vh8&(6+h%K]q~a:˯3i8r0|J8~kP^,*c fbWfݫ0(9[3"K vu11 AG,fPpc a6ҥ\+p+f"">`SnxT_cR ״ʵ%S K0F#l2{t G”lam2틪 -gP 8GuQA=.-j[a0K_+P 쯨(/v< <`uRl0;:x9mvhQh=8u*U5_L3j*[1mo8![=jZJ Q O7Jz~ ѫi45C .dj⡑ANUDGu Mŷw˹K #ȗaO@,}-1$*,R*RbVi<(WDU},h ]ji~"eQ-rx1^e*8umudq )Hb,j_(-n/CgD A[ Q%)+]PKK_oj-t%.!\'DB픙RnFY bW5G>_ auDn(^FDn ,Q2gM j{xx%Jr]ƹsq4L TdmY @xk`QqcGFZ"r.`O; _ TEؕxԸHؾ #Itm8:r;6ɪݸ[ULj\cN"+F((\(JJ UU>#& $Ty9\h\R²f@_8oϴ1uDAvXƙR b +_4 w Z䍍;3[VFʿQg!XD1MN"o`eSf[fGEn*՛ ,F9WGI*ˋLL yrZ#0j2G.1 SNYF#/ln#@b(]_)yq:uԷ%J-(LmuZ%o:Iŗ<] >n`}Vus0~&H(XPyxl.?0N<:q^l|n VΥ %eZ7|B~` k7)E@mG^sk1PظFtdZ\ڹ̻CRA`#Ar\r[--ECST A8%S@BTld/Ʊ[%<3g'C+e~פ:xΒ0SM<"cX9uL%nYPE Z{DeQGBihs 2hkηlq.՝J]m2)Qq 7 3ܲfi-')Mk ښ &w2r6)n1hj$b'?;*.x0;O{x8VⳐb^dR]k2 JGTy9E \ϴ 5^_hrZ؜X<r< T*UKH fi຀V:߼i@6|gCA@.wd1n-WeE-(?ؗ\8.eL<,@S30Qb0ƂY&2%*ŨS`VDi⠛=K" (Wj6j8p7hQpPF6ck݌R]o46߼igX8 ٧zYHJ#`rx` HY0xbZ2Um9+WHu4UbU>%JWen6njRSVeaS&X8ܥr?%b.*-\Wv3\ٞ\ \f9 qE1b 2P#j]x㤢 4/ȥ-\]xT-YB;s2afU>yCIlATX qԡ @Wۃχ*%5]J`ym1; Qr.~$I3%7\MTk\ l_}ÒERɳ>e2-S$7, $)XO`kjNfKQR2iixH]od91Qں&M w p S 4&*逅|n fUQp[.P|)S ) p[(S.sfX.|w1rSVE?ypp(0mOK# "98]j_ o)L6|B!^b@q]މDNb4e^%nr7̈́N"c2Dd)Jc5`!Cp;Ne oGg!\P] AhwdK0YQa(.L\H+cXH)gp"SsW652r0iTĴۭf990+ebu *к8x\R3h A7 9'=JĽ?0WG^ ܪ^֏CE`䮐BJC0b`30-,q.uyHjpJFTVVd\"p˜R[prS ;aUVde?kҙ& Q ^b[K%S/VAJ8,#* )S&" Wa(f+_Oe'!_E'.i"ř@ӻ,*wU(͖]-͒V㘶+V>suĵbkYV{nS(5+ۈ]}z&{ ӹSBJT!  (Xڿ @G: ~ j( idB.Jkc;7s1TZL@\^7(5bX " iP\sUSLc CHL쨀"(r*p4ڕS$%\jg@!g肇MĂ}Vϙ -~b.8XEg뫚@8>` W,x;|A5gE=4ȅl*0Ӊf 8X)Wh. Z[b!ju //9 osXkLl1`wf;(SVYOipvGc\/X,.5- bP[l*>2V( 731SۇIƠKUDv o l˦K(qQc&Kuo H-kf`s2̢ciр@S1,xᙋ Gh>J )tsQ`rPp2V^; /,$P<[5%ҳyʢ#ByG7JWBVQL[T9AjY{[D-FF<Ŭn- [vJIVf{ YR4F'`󈥣b,)Ҙ( Y]<@Dt(h]\S#bxn|ElԳK5v"ZU#ķٍ5} d>eqF-cup2P->#Y-4uPVV*@\ߩT0(eK*;nrd킝vWDA'1 ExʽKTob6*f*f6BɆ`V#+AD*cC2+ gA!gFY"{,ZTs67 reP˘CEU]lxFmyU-f@TU35@S7-OxSx/DW4yJ9#B)lhU9}n4⳨ZE3vEL+),Yt\DRصa@i+7AW4 ^G1ZX؀M*ZVqDLBP,`(xn`bP'&΀j}Yse!ykGO5 J!Lpx" q%K*``P@[a-̾EoķiY+HfB Kuؔ%z?D\ۨ&fR[Y5ᙺ33bM-?iS [&z@a Wl+y UA(# p1&Ki ݣlϴ`:QܳgM留ЎµՐ+UpfW};na\rP81d:Rb/A, ~"<wπY>Ajr@b.rrqYe)g BG7Xؔ` i(%iF{ BPm8?\ӉV%Tfb:7B69^o9[aTU[$01N؜`.ɔ-eVO(T VJ5eFXc*¸̰fcn?QZ}r3wKn*bs(2}/ |U6q|j (8eaSn@W~ƖŬߘ F׿dS@0a\ٵ-<8SQМqR]8""KK V̹<-M9:Uw)zs @W']0"WhJ2ٚhڷ ]E2H }],ja9K91D^+6_7f ڄNSl( 7BV+xgLQ\ (|Day2$xP-`VqgJjCt!YF܋b)"oLWᇆ[a%kDYJujTEZqlZugK܌Me-Hg71&ۢdҴĆ`4'36O ȼb0&wV]K.[.]Ri GFI1G%ix@=c}J#@o!3sȱŊۏ "B, D2Ze4@MIc( :vO!8Kh* 9&Ro9% n5~Uw[BʼeK٩08Adr,;P@ vb#RBM "P|Y+ X{Y@]0#MyřF[?hc%Z,3-W7H:nrBhATiIJ]y]ŢpC!Ze#ZUTLM .\F&qdm7,l*U8̽D6~`[ Udj54.͹ XkeM`\9,8\/ :aklcĻ^q/¢iq`, }Jz@Q/ 25wJk%-!-=PjsVbT"f4]͠jxE 1&T"2Yv7P`ܻs(\B87/UpzảhM@%3yAqDrn/0e|J+!JAB̶֝Aİ7 ! R;0*Qsh1|ElLTq{FamjE|M\dpܶv8Z׼Ru i{c.>Cɸ\yvFXXWo-DKd(4+P7E 䫚m{Ɇ90L5ho' 6_<ҵ 3B|A0aDl>eV?0>m0ʵn" ّ*Bd_a8cҞW5|,7T#2ee[ ;/e3k4  VcSZ_&`saubo\ScQҮ#.o%@x- n}iLX%k /];M .'fjpP2eRjWi(Z'T7L[ VY9'8 ARUk-V0k2l<䯘ʭဪll(ǞF-۟izSA*ʲi2יWAueS*3.WRP[c6A.P wQ1%Wzn "95Bɻ6L(O`_^`VFuRk^9N.dxZLZ[M2_cn0_rcq U2uRG*-Q w|I/4KH%}M=(=-bQ/.|Dv;Wo/ kʮ7P ,|TZbwe$ǒWǃQ_*% S{(6.h8P2%м^7)j_4h {d"A(n!yR) j=F,Y+cZpGQ_+BVP Z59UCX]RTPEdzAScdT0\mxef)EL}"9W7/JeNF`+jD3RKh+g1rFo]ʬh*oI jysr2a|9 ^|Nsh/ի5o@}KE5qwm|EL0ĩk̻W`x%[Q%|3'VrQrHM\@[\_ٱ lʹsJ'5Wk !eM\L@ o i˸ R1[8"3!ef m0Zf]nPe*cCP i TЇ1}q*FH!F47-YbʆQ7*q;P!Ũ-Km ?BŨpVRiPfb;3-9.RUĴ 4W?0xiF$yA(@'[+̎+0 &+w&PjeHfVᣚ`Ks[5 2]{K ug1F]) [OodR)VS{6^c]ܭY(=@4Vn%QQ;6Bpu|y hEnTg@05Pc2.*ǦCT i7Eh.za%ʙDuVKWDpJJ,J`1|Fؔ0bjtr`ҡdvpV>Z ܨE/Y50ڗ,Skc*3P `\VQG#%uaZJe2ְ<f0r919 )epBxbgG(Z8 gcȻp،7MeQbfEd5fmVcΈ]˩-1B጖N!;Ҡ{xm&wjcu A>UZ}h_̡ U0DN2%[hƄlm䨓j8?OB=C4xN҆m,0 y9V2ʬ;4u%,);+b]aK*`4Q‘PcW+X$ݚAs@`qyԢ,V\@)B3 X;hPd9RNe7UyK,~#_K B"̥nL "Oe&]*`uv?ylI@ec K:*C(Fq "`AFb{?]H[+ԇ=Gxq~Ik1h@Ǥeχ !aCxIr8_%Al0mi,Y8n#8%v*KQ- &_(픭WWʪi(Ӄ MƈEݾXFi"x ;bƦv[f2PҏFUܱFb \X_aɂIj8h=֑=d5S@$",Wlh|3N= 1S.h.1;x85z#A|Űʀ!(r6@`^8[`"f8ғ9g1Mx]@00nF0l1LB19VKPfݫq*6SqK>;8} UpÆdr&VqqED*{Ծ',"mQV^ޠ8w(Rc9Hf;^&\ʨ#.N/p9kR..7pqq0ca0dݽ/`5W9InvG-5p=<)/w Q*#xqw4rEJk`*vh$& m fC|,J %xuw+. ]b+vϼĺ[k~2:\U*ptҼP@Qc ̼.gt/טVy9мk@<[1_$lj/@ tp_N. $T r*؝x,=JSu(in.l'*W-gO|ߘ*U3k,Wb&X*cVmeWW,\7Lh> \&"]0]K3QV~ Hf]bevvf ڱXәb. 䉘]_GisJZMgՃn5HUq$Q֣EbЀլq '̭eʚbRb [e@X@AvJLqQ7iwĠ)ͳ- 8%_@ԱE @nJ.ڥ^" ihR%jAE\q]\Pd!}^3g dyZ({wz\Y7<fy+b-ܵCպNŮks*Z1iU_PE\F\x8kVF:bߚT~(K6.#6 TX_p@Bt*qYs߻#o} KtZ2H>. e @D? 8jX h-. :pAPxe3gŪb)JѶ}u.^QbwUV]f&^bC%8JdA)L.$ TRexo1LP(p>{%p0oqglcA{e^Or_pr;&zʰS[ <0-pX?-_4#qafb+q1gA@o,/~^.QM̵!i![9 T%[iϳQZgܡV2nZ>eHS B n__YL7^Rb*4_bdYi ^yUWdLFAtu)UzT( ݸmÕTq̢EQ3ׄ]DQy h e1+kL+,%𑫺&d ndUx 7V<ܠ\3 E T9##Ƙ 2JkPUCZ N7-DRթUK՜KΠqڋ7-D2 CЪ)2p̝ѵ&gADÂ)Xؖ=]Q]spG>`&2#,dKj.)$R Nx 622zұelpA|ԧY.=",85{ (ŚZĬ m?PY!wR/8iQ7,GD $)aN TDϪw?0l=(93,ɚvXdF4`V70V]:AQ  +P/Y*1k;AΙo@x"PnSW?c+no`Z'nK/!Jc'Av1JwuO & sG(f_m8xHW1SyO^ aI״Lkpu`WncDb$.pY ^1fw9WoxU~PRZDXLzgSesr'sDR"mY[4c6[4 xM_p.?(RϹ4cO @y࠯+\#m]52гZCAvJrUV0̾\;jωV (q5hCUZf&&(C""c/3rZPlPR65>eJ6= H9cVVADPܡD (g*mHvbWVX !J. Hò\u7.ZS2-KigTb#<7)[-ž9%%s;Y0_(fY*3kWMg{@2ck3b\\Cr2iDLjnREfJ2("|ʊ K*4j1{(68 ʏCLvAEe}KU2w'qѓpQpE5I@E3^اJyB`Q mP82@{ e@+@1cS7ĥr1D(g0m7 Zŧ+7ٺS]L W&N@KAe v)K=dbsRt@% pwh9gohE%ts:B9ZUqQ}î.ZJgRCGg-qƋ#h8?lf.yUgr"2k$LB[%WƐ sdˈ -7C;tsXl%%%"ԱBA/6L"[傛qXhr(&u557ųYMT(<2-YīuEqMꪦ,K|{:6䂪+ԣ!NsFyQUx&>1( r8"+c&b)`Z]ʁ)K牚yQ̻*U7Ҽf2h rAfQ{ f-q̲i0fK, ur.wV,_{ KV:QM*+VT&%n ѼKgiG|Jn)aM2'RxZ@9]q) R*:4;^}ج-h'c"OB6M`FY#aTi¨JRBn bn^@[`ZZ!R q`c%@z*\j08H¥Jo( JklZ,v4zҦkb]ӋLQx$*`1KHR#ĩh1VǢeVeEJm`fVe&eZ楬I[%\T1|j:ŨY2"'rIXu6"梲q,TDS1o#k("31 Aǘ7PY6P\B7 ԵV@]湆(u5eol u2LUc/fUX˻jꛁm̢J0g=^aUuOiR~yMnJr[ @ %}˼>7-ۡ77o_SX_T>!c&"g^7g08]̷f_M~mmy斝w{F 隳#s5A|bBs}i 3n @p~`İB>V2jʩre.u`qeEUR^9Q莎q:oa Y[>ṕg<ş]G1;0h|ˇaJt:o Uƒ 1Inc0GJ>.T"dlw )VU^{5fZn~#0~f|9Ql.q._1*G`rǠݣ Ygu|To`@-9b.^0 }E(ſ ߶">je bƄsQyU*OޠUR1uO*9W:XV xN 橎u msQR*-6JqGp"kL\Y08/]j#Gg`#sKK`X_a ٯUyMz8^f"ũb' Ep]~iL|%5q u BˡG.SR P.=Oi*h,ܬM5wunQMV%Nj}j^BiW|Lݏ;ծRRk`] ɣȦṠH֫ ]CXFFgyOERj j`(}iN1vv("Ƌf_S%FL`A F4\r\4B+oY]Byy PAQ} aҭ3([gn8 <7,PшI h~Il ,-U,ZF$e_*qjWB\ڍ)l[41( yy$U i?Hؕk0aX]⽥9:,1Ơj-ډPs31,K5 //^`  GSuƒ+ |h,n5Q^Bʤ߼| ߶b.Mm~e yTg%>X?[api İnX򅄢 Sb(Lj*ky3!)em> #m5˻e^|!tx\~ *qehȴ< ){I#$l w !1Z.nم(g-" !Pe2mrDp'Wv80϶!^XEX%;XhRa "]f/ĕ* X4o2%5.JAi!C_/@#0%G2 _PMڋ) 0x/|ptZ OjPaj"S9gZ@rj(IJ1̗ܒ^G "`M>tc;kَ]B=K ,½2yK:jUK5P[Vf'u0 |,fޜKEy8.odY34ǫz 4w.9|PS[p:NR)\&J_Ph4yc9cL Mux[b)W_k\Ǣ|urԦy ⭔eb~"| )mS [ g4 8r D.AΦ R4r)#cÐ:vۉ4&Rjs(e.uqqUIYjamG!H2pō :sU{p9 |kpZ`]>pia] SEұr4QW+tj KQ|b:ȷA@6grJ1tm6= [b=6חCxǷ)bۅ8 Ik(mom㍸u4͘,_6 ƛL73sA6&{P|Ϙ 8qTҼ~ %Xmޥn~auy*o7oQs6{6㉳g&/-UZǶ Mަ{|È0dR"6✙Su\J*dLQ'\P\WBdPlbh z79=/ ׼X,kOmжWS#a4 e*ilۛ]D(BUJNp?,m]ӏ>&D ~2/eKLpGkz;p 4 \PH>5*"8%!OjUu\kbP*a-^ܹta\D( (:W47w Y?,w~ %Po&`fXHyR܌{&H΢{Y,ɵd/ewQ-&eil]0l#) ʁsWN~"7kL[gQGp*8+B~8S.s|&+ub]}bXWy8P؊SS?3ORLm%W4(UQϖ\][84ŕUkLޛp:(!wBp#w 7JTlGXۚbl1w \ɠ~ߴ] f{GĬ}ƅx: {TS9{Vوo!̊xH=|[d Ҵpdao5~#ayiKE8j"lF'Q䖘E`(T.f~hl?ߴgDKOkyE| f'J#)C4.ڢK\v![^XV* X+Q6E( +Aqt/˅迼n0HK fѾ>bX.կ`q_a=@mj#M{ ]1\|@g],^Q.0V hn`L.K bg|5ٛu6. e׿B.e0t²ʆlVAnK` ퟘui_ XbĢQk ?xdbߊrAV˧,7H/g 8e(ؽCIUM8JUJʾ{dN|C`hRCE  'qeؐ%B0LC[w wIJ^I2U┏?ZFXj[R: fq%0 5$ 1f*0z` |@y rX]D5vs)6hfJl |@Eʳܥ+/-`KBdo1g (:k FrD0g01lYL`(L Gk(M b.*\u .k@i*`c{DEk36 w8ni2X^"QrIZz#λaiJrksZv/! ,T~ f׶<ߦ@_D2%.C 2@D#!iR*>c~fWm -50.}hhwPtSƢsT eKE{b1GZ~"QBRYKqѠn^]Pf "YTy*Sy^S_B@s@1L 0/ kDYZ*s7"²b/!] 8D!f"rq J]/JGAD($ԪkIW~c (XnX˫|K%@?w,`DU>0VU:h){axyPJoArhߴ-,i#'oo$^?BqYPRcYq4ϝ@etR)~@Lx3#ҷ4qkVjXVpD=Mb$R1e3/%ۈoܢ,u S0(4ĭ5MrZG%-mĨ\[f2&W 5(X/n )1Jz>bơ6 I)")qv903l@:J9x$5\ gea3!ehab_Q-9/<50}̘4dwzȠ/!. rF5w٩S_\ K cjr2-IXT0` 5XiGdqlm4?~% T\n>MkA5,(x|c-X/ˤAĤ<@°Ũ2T_f oZ4zh3 5؂WF3^з?hFטVZMTrR#+@ȳf/U_[TKk)mr^7F/pm;!Tf3l8a U5"d|ߘZ/I(]8p.hx JbZ .J4Sj%[֢Z8+zVU,2^' ?G&ãTJ*͛) n ",@i9 ?4-)r0)g/.펡H5kȰe2̨(Țr摦Ie`KA[R0IU]3Uw+,t}:;u8W_l',7x.X+fmO`QPNXl`K7 RVC?QFJٔ*p V$1q,%6o_juZn mi`"F@f\3(. $ZJ|J[ SBךc^AFD>f1BywGfּ` $ [?$8CYK@Z)3|WPĥC*!Ze ffP-Z6˫E* Tq C4Ξ"LtLl)K%;_1%  7G3q #"Ҩݺz57+9&J]`7FV#u|t/ W J>O4 7 N՚ܮGBXwMX(āj9J h, bi: rk.XhRtد(10_N:T'Iy"*+$ٚ9c#SPx{W&A@Zu,`)Ħۯ l4n=t@ V9Vaɤ9z&r=և&T֍l{ɢVub;tQVVZh冭IX DX~R-u_UO,k3,&W'δiI8|`*ouS 4MW_̽ݻο9#yC+v0 S^+̤ Ǐ.L@}-qcDau]YlDБ]F(vjVU0srHJ{E`)b텂EdZUy7G0HA~!gǼ6}Ge^l1mQ\ M8AipKj^!YW22{EZ\9'9U.AOjTH.GgIv)X ub0g,Z/b a`B.[*ҬDNLjj-զ5uİX?İK}pz|EgV&9pқ  Bx`R5DrkPڥd1au2.(w]٨)PQ P5U=Za} DAt6(;y AuxZr6sP-\_Q*BQD@o!]* ]A7x*.e]Sp!aW4)wZjR=\aAkc5E`/@-j\ņ7VtIZSdvq NS`%B{ m,7tFXMI_Mt j^?؊XXO]mj^dUg0 /!QBT89.u+^ָcC5P&1ܪ$j(B.ak%ƭv9ۥԴZ\[^-P`Nou,wq'  P@j o:kR3gam> >~`Uu^E o>EӶͱut= hh&ic.sVW2WMTvCG+چH[7Kxe-Za[b,Pi/\8(i0fxWVb5*Hmv2tEK@x:>&hmò0@^0- C`!@ bTzd4&3hPKPRfD%٪lu{-P`ȝ#8;ʊfv+QdePVРB:;/e7dYa%PLAK;qw)J`bihl2kR%-cшH.$2e1Vn\%nbu I++, -k:) YgȀV4,VWr]F6E$aZ ztq,W,' =D/4xuDP ^~wRȪϴkfO}@g Լen < cUO3QMx.{L2Ș1J1J*L;R:C: 7lb`rQDHaG{/JocQ++rak^*0-!e_|?W8 )rhڷ1`ܣY[(^+4*D xbwLU8 iUJb1-{sPI_bApO08)徫kķnel60EhلV&~exچqjNKX TBmԦh՟19VfwgPr[xR*{51* :h722^j].JWK(r?de5zi0K Vr_]h(כ#bXl{l#UkG¯#KfUS7=Cs%PPPQ LtX=ka DZa1l(AdÞmk#jh@*ۊ at2IJVAeecZJ*\1'} _Ew, *8Ů*t㵐g#-Csx PY51VD2s)n2PٝNdrm7+(k|+[r5kS 0W%>Q:KZgS 'Ľ02 6ɊepP(@Vm-oj(Ak`FYu C h17QJ1̼/lO]KZh=JqJ*t{~bP@|oq}Fr6n3 U5YؗP*dg4R+ѷNnyT޵.ShS e*N(MQ$ Jtqyq gu}}DYĦcB%_3ZX~8ƥcC 9 (ε*U_kX```5Jj!ff2({f-,enE֢S:&Ya}B#Ab|m3b̩-ҫÏеDBihh@UcIe+"u`@+K/}#lʭj 5z $W m2f@VxqC½L!`vn͙Arx̳y {wqҵ#!06C~bJqA@9A1e QپXaq(T_>.$IFqɈM? QĨ[6+WRf(TKsNyF^.G5Ǵ),ʕHRP(#2^?ZQi]|?# Pbf1GLPfbkP“W*1KQǖ$%C3Z! L8l mұ+LɒX? *9l^güƒf*( xk*ߧ=Y%zF.Dat1oYl;8nj+`7TUW[$z}n5rMR^ ̠4)fSwlv,䈩B= p& S \hNcjGenोb-UޱJTi2%K7JKVষ*ܦP-ԾTbaV ΁*%jXx`1EơziOAJ3ɂ5"aRiQ1k8Dc@^1g(ɏ1y(&fс-N: yH ; A@W_ܢ]04,x]y#^akb Ƈe6.!QMDdX3-7u ȦyB1mYoګLxiOX=f[7*C&4Z4w1#GĴ+u3/OKq ;ɧRp U)%Tu%+S4Vg5\hsQGsZs0Iut c"c6\VYů{[&U,ĸ2Hܧ2$y g>aLOt,˅[^(dul=W#EP>~pʻdxb >N1 p[ j7vI:~sh>_U,±PEߴPbX2mD^5@*"HsXP _T%9sdgH4]56C~y}TG1S7QQxQ[ pjr1ڼQE,#4ʚle٘ḗj: 2f8)b^[]\BKe&l%%6e-7f¥^c[4Tڳ5:,' a2K+sĸ3e-U9Ԧՙ3!Ik V W!ga1-|]0->cަ `ɂ5kE]эG1Z. kFV1p:f}L żڼj[ ;hK 6臘gUZs0ȶ6jŒVcMb,U *sR\ܶdn<1H5xRYz.U1|*R87]LՀqlJk,1zNpPv\EUdLL% R DTJPH^ VóY}qrK\UF5T` Vp 5%!- 2zڛ?U?+1*_P摗CR{T>8.85*-~a݈,}QE^ 85N')R:Q*wx(4Ɠ˘t^ie\0fDR9x 1* :NJ$͋Z(BXV*!a9dcܥ,4rF֙Nq8x.S|A&96^5)*8'knXC 稭ӉUT(7yS;.QX׈Ҷ@ s9}m_@1(Hj28%mnK1j x&.PpF'05f_0VCodc*,9NxJ̧JGk+ɘ%Q5⸍[5e|惣 ]Z^V*[Rb8hMQuEJxIx𐇂4UQzɅ -E},T_ߖae2u /XESxbsxE!m9W*ӏ>Kߩ* ~u,YV^7y`XNt @W홛E̳LP aʪ{kE^@<ycAe׏iE./eCVgY`0Y5 z4\q#!ΧIU5w3$=\To Zt̰/B4#nH,;QqTp;J8Csu6qN TA._e0Yfj'ҫea.%+&{f-}ʃ-G9e}Q22݃Z8)uF8˨Up@^%-;!yY(W =C 8^a4 &m+v,g;rJ hņ.(J*Rb]C;:>%vqpmLclW?v ~ET1PY>sҜ 1';\ymb v*Tlq3VycXiKS&xe?˨t: Z(aG/1}* ŰxUsa^->#:W.&8 ^U#D]Eˡ!J+1d Yjc}CAO'Su3=M@v9KΆqUb5,-[5ap?3+ Lj=D7ĢgQcL\/t~ !G=EWC"'Sne*%pf]e^ 8%9605D ۄ& ̦셼61dGJ1 [ƥ"Wp+/%}MYgTaOJkG3xnѢڀP\ DKԢ[hB9R[=GT5&ކ`Ժic] 5~.&қ"Mv~*pq.xB|@w0bȦ1F-QZ *8E<y_:A/,%YGr K6~=9nki}*boLxexPէO[)sɁXcEŇ|9 qG * X_2d]EQQm 0='HTOIchB)E<px^!] ܗpP#!I!jWv%+ |ŵh*"zKyq^ '+WrqBlĩ%Sbx_/ED&Ð%4C&}Ko_Z+7@J=JPg8آ SG E#!İm04]& UJN.!ǖRk+݀{pk {UqTrCWC56vd-ļT . uU51on"F]pę/w1,4fRyfͨGqWN (W ~`a%(9“@KT,K䉈"TZ5[ܴx6yG Q[jl)'AòJrp}@s&YI앞Z9Y(&‹}*Y5b3n?Oo1,dp\{H׊EK|B ??BZ&W%QguJ]f0M ɞe4/5-7b-l qiCL Rz~,NSTYbeV&=}n#'pC2g &mw0ኡAi DqRb#Ub16Ԯ'WP:.5S(QB݌S8? xi}xfYSo^AECW(W7b$#V-ۊ/]uj_ SRnjOf%?A[QMKLj4}+%CJ8O <=Jq2Q]¾&3XP֏̺S.9=T ,;.gØvYwrhux"S|Cw*Lx3®|f%]%o6dVbꙓ:+vjV.[%V8,w1qd:`]B!@y0"IX. ߺSa x&yNp+6|c &s<+U+% yqDcՌyfgƢS9{ek7q|3pw1;ߎޱBpUNs|J#Mt.iuZYud8}Gr}T7 HRiFS9>`p۞&قZ !b̲]mHƋ X44SV`taLxYYX> ߢ1.& ydAD / !B 2%b,}dD}߈1t);~}UQMFbw(Aӏ qz|\\n/d@}'%% M 0lVR\-YK7,D6jeJ5 vKKFd́oqE(w.M6WD27EYh-;,#|@u8>!n86̉ac( {06⯙P|}cR`v|JWPug=JqRkJgǤC7g1q+&~UZyYw/R0unl1p;tpl I9eMFeטjJIDϼ) o/G.,L0Qt"LTk0r9n_3J -NJXp>&c(*:iC|k>%VkmgamCQex[o2-` SG 5S2 qlej5U^\dǴT[3C1Cbٚ$}m0 .ZF`b}uUwaП`Ril06g/َ[52.x7FX\}fZk=%8.%.X/Ixx3qF/$Kp&p4M nBJ,)7A thކ-,QF 7^+S)Ez_1qG4jז %K){b^QeVbLAdJ!87+[̢Q)3u sEk0P+x#q%E+8@nyncU+KIfhUHFiye3̈<{Jt\~sį\ʿoK Ka{7:!7@.rԺ"}VF$>WL&`B.[ nj#,_DVsJE̔yUA(SXuiGX-0O(W00nEBӔk.aFgdr!2?s g+'ql Y{Z,ʀ)-qsVLN/1UA!^rJ F2*|&^5ј;qP~&:# 2G x7]|̊!:=z~2536ginư)㙐̬՚"JKeP^!CSB&Xs+QZ5ޥ07] 4Dl"Q=a .iOV1C׉yGYZ#bz{)2e>_.RjG9|l*]S6u(,ܫ/_ȁ;Jhؠi^bp_*BF?jaṮ ,je{G0^]wEuءUZquCqҭ?)];BȈ68L({[1'U!cX*Yfa W,5ܡ0j W-1^%=\Kj"p\s1K15hCafQDJ^, eAUwrYfSⳲJ0TxPN(_Tľ{f7 PcSQiL%If4pf@5]@D 7W3iۉ3lw&gj5Q8x}FhAQVDK!/J?NckLYHxMG3}oN!L~..12=Hټ<;f\Wm)0jg 6cU=w.#3u-<9՘tq*Q?5iX#8%j%/2̛]cBw)Jd 2k\y,1F>߈֢Pr.}C/3x&OeFO2RQ q#eFNGeT!{fs|Jj@)R00) VWg*b7c ЙWG6!m`C?-3a0MqL̦Ytax3l]{cxa10+؍)5f b!q|+ ZqqiC+:g[qZSoXgh10 yfgxY8ꊹڠqD P|vLEjQO5.qK*^<[?*tCjqx!/^e*LC@Eq0db-u]U5GeԿ %_H9@ -k PGaN 2M.XSZ*%CR2޽b'9=Й&5,zi9|˻K"JM1~#TT髹XZXEY9gY@@5*1+qTݝD]镊V֡+%0y,.%eQ: Q>c.^ZNP Pa aX_g2=c#)LZ&/+mץ[U618p"ѣO9-42%*2Q$BERK$?4`f e1LKDp=7eᖪ{"˸R71G<&3wm_u21Iii~jz7e'g^;(ui^"W#n9> p(n,jz_31`M{,^kk.eŖ=Ĵ{D|+ ud6 Z@]J )LS>^")gǥ"\;JFϦ7Uwu+m|7*ٸ#T?(50^d?*{ <!@A+&N>oPn/`aL<flnK!"' D.8b?X"ZB7*s7s+f))+Ųdz %%rʟ׼N(e h.j a纜e#&uX%_i %ĺ):}DˊͼJ賙ic_,j^cj/.W K^҆snpL1G.]26P%3y#=VfX~Q+"j_mvdKIjK09!m"W 0uӆb0<2Plkx+r0Jc`(:/PQ0:J=JϢu24.8kь\Kʖ!raF&S6ɾQ4]ˇNQ11 3 Oل z&ET8F{/TA4jTH/*dKxMXr=U0`%K+UeXoKR]]#de|1?ʠX2x7A][%`  k$M@] QW<[ZgLO2 +~`ؖsẹWa\E޸Anw CN`jɅaȱesGF2+Ģ:q-%LGMJ5)9LC^?V ~oTF z%J`Tԯ\+uy!`\7 E8:Q$XZ)AlԻJpEYPsmA}A";|ӹXRbAŬ*&+5Q,0Msƺ̽>Ѷ-akS2pJn#b+S!1ƼŒ~j1q+#K`7Jxn6*QYEG92R[z"/Lqob-NxeuNr1mkVEOVrB.@-*Ĭs,f&(ǼKw)Ae@=Fm,bJ)? eTøۋ87__MעWpmD3/XiAj-K_N'7JǏMfcϭ/XקBYODH+:*%C (!-Q= q'2 yܢ)5gXԼZQ\E,䚕!kҁŒZuBd^ ,;5(b>̢-0'UzUX._dr@EHԾؔreV@ə*%w|D+U*&ÆY 500bhqqSP,qA`7<@HHxq%]_>ĸ%EwJ%fwEh_kKQ>%!IcO2ѴQp@ܰPU3`x*hV+9PJPV@+ 1aO.0T c(:iS9]AV*Q(BWW.V/"xP\gQԭGh`sVĩ9X3QcdLju)%1Kn撳/LD2\̺PXɍ~A`5#D \-1sjvnqpsZp6JLݔs rf 1M?rNdDi_S"4Ki"8 O=CUK^̱Yyɕ ?ܱ5 kW2J-j̮@Yd.1"U^bin w\^@JB `,eeX|ns~^昻)P穴|H5νF^=,!U@\סLsLw.5ߙUSqCp @pE/ؗ~JiW0wL,r\@) En`g`K"]NصoG P* Czw7 zdG3:J0JnzX73Pn D܁5Q990:#ٸN&9Ec7RJ s~C%nm+'A-e+Y//S]oh%WoD6eYDϢV`\qDa+.~bEK^W(e\=jMS%MيYΠUFjls-{!RQ|"\Q}-5I)Vi]M7qҡlCP3"_(_$ףd jUAl+^K>.UBS! Z1v8Y& W**F%y&2CqLZ,(W38טsl5qvKMQR*X=0;r׬MUBq zJ uZG$ TKc{W4ůD%^Q=XLTXmB\SBvQ,$Yh!)B4S(AD F?@Dd8AchД̯Mby!Jv-T&9.y{TdvWHufEbHh6M#o*"v@Y5- Fs,kS$ɁP,%cұ}+7Q"_[*>- P *J}U~Q? D*LRI5,1@BG8F%PBZc0$Ń*a/23& 9_Fۧ9uw, 3ʸjt%q3Ng&vZB)&-,e/@KZRs xQ}^(@XخVV?wf %ORB K̲5An-zjqZbſAi ~81~Ĭ#J\%M*e&_0:w*FZ-/rfGL6XjQՕe3_CD-h]ZTK*Y+C&h-J2\RlnR5+U R%S0$ע[+Rܭ*/V*wVQcO(@8!.76nD]0n@(b^1 j=.>bd39S Yסa%\i0u;wqoC~L +5+:[oе6F{>}W%JЬTHGކnƲÁQ+p*%D Q(E0j7 D,O2DB /8 _Y1s>eT z#2/1 KD@Ž ER$ 1HXfX 9S .0bP]ͼ9&"Tul蔯 aE1lbf70bc?p9s=Tiǒl5J;[hv$`V5..{80ܲ=1)D/@.e*+Qw321%l)Rk?0cs!\a(eЇ%_I8 vhsg&h d- ]\L}D\ ==/pŃ5*-4m91^ XSdK{e2r-p+Tkҍ&)vLvi\~X S4ҕ 4KET  T̩.비Ρ0u{xUiS~롫ػVhXX&**\~g LP:EW W[KILϴLش)GeX u[8 DjrfA-qhUe`Ʀ[m2Vl}LHωFxb bDb\W4{ag7Evf^kn-Ya3߮+o֏Tqj4fA+bb2Gi*W3q(E\)#gH/=KȮ#Ġ0]۸_c5(d.F}Te ڶE u5# k,Lv a`q{sWR`qjPj(%rw*VڙIYb\~`bێ)p]D*J5v(qd?2TixI%8[ĪVrע.zZ[E^"RS*0_]3,iQn]BA74oD}:pVm>\T3q01@6³(5k%Q#b!ޡXq J,pAG]1[Cn^&dL2ʿ XJJYA R˗gȇ^'PҢ%JK@HlGj)SFƥ[{:":8BdmndCTlV~-{o Dӆ#X Q5̏6T;>^P.-C Qj+ Y%nLļ]h5|Mej.IsYLdj9VJ#N#b Tw(0WyQ,*qЁAE \ [gW_+(pM :~:ceb1LeUu 8"U jWET =Ap}9lUq*șq 8ΠA\3j.`ľ#P˦;  QRiy`Amc1' @ 9TPLWDdǦ&Osh솼KJI\%WL 4ĹVD4is9|3[Xʄ/Zs*Qơj!n&Ml[“\򘃘~cD eS*Q/7Q,S1uz\@Զܼ'ߢp;} 6C^6Pfm951nY&;E$ = +Qs+ᰲX)gd f:uFmL`7Duԕ_GU/UUzbGQAXFϠxC\K)Sφ $t-J&eeK󹤻qXf& 饌624 ./Ĩ4eDOPC Pmƌí XEnm+DFF%F=GI2MjUHra*pɐnS1G#R=̃ˈz2L)&^qLx ʚf$vnM6;uuNP().Еܮ]hm$"ߠc~4BNp*V!uFL1+ JdILW=mAħח,zYnUsIJ<̉.SGx{F=?(E7LYXP@3Z𕀁W7 @M`.UbT{o.%D 1*y.qVid'qԣ:vR˨N&EI b f`Q69D"4 5]fY؆1+ s.w*R-C Ff!Dtr>Q'LBe<)N%FLrİN\PD Vp`z<@sƁP `*[1lcوèl\fEk AO2f`$b+p=B@>:_o^x^A(Lk'~g9?jtL/:0yܼV~j--D80bψ_ pE\`,}ܰ j*!U&=GP j%s/i\B1lׂJ$l zbDLK8iDK4e#)e=ك̫Чrqy>f г Tpm^ԫϢ?_3I`WEGJ=!0xdǠү>^YwE>edSjጨ3.o@8L_l55^KDefX*&C\ J ĩpܡaҗGLnPE)@̣5Bz ߠתʋ s?]b^cK@QEUnk$O;B=,#xd @CF"Ґ%qS8+! Rئ%߫#0%1"TZheKq/,53#He-ɨ`aV#KM|ؑ"<5RơV⹭K7 3b4A% efH8!XfbһK%ho4sq>dz f#0 L 6ԨܱW.>`"H|B[=Ph1$pzq70C60U{na_0ϡ7+i3c3i(zs(Jڔ*KĿr䃄j m6~GRٙxl^~匿 6(%\0\wTR l- EEV AqP;r[s\KP.vļ6C w) "N;J?>e5@DVE&f"jgZ#Hf`e ߫Ǚs.%c_32)H 3 z.(BTpY U@w"..8q늸OS2;_}8.Oq3)%ͱ-Q*f.T-4)ж:)Mb;`@>ewB`0% 4e%B&%c\J5긔b 3)rͰ3-Kn nXVu2cQ(BZpx#o̬=7'~0xw ĤGk <+ZԳBz}Q>q/JoQ,ؖc@;'qk˛Cr4ܼ}^, ĥ)8>a;Y}]o;EWq-ghC(DYgSfS0x@AA9=iIϢ>sDpT ;0RT]U _Q=8F>TLsDKq.e씃?2YѬ| %ޅ+{y LQ^tz˻re.pVz,U5w.Tgѷwͱp"@jsnUtmj ~H%|^܈)!7ؔ/; 09j ƒ .&7P0YG0Q& qs8 Q+((sA@0Bup49\K?LqXP 0BR KX h*i蹗zi4ոd\&%PQс&ۍLMQT/(72A1e&#7&[,0Y3Da3 |AZZDљWUh|D0$`jS0!+d&:/0bq~p W\JbHR >ߠ0)]_L"0ņqAwiq9y6bV I9p[r5;=НJckѷio7Q3 @=0AUy -y+XC{r+.w0g{1U9]jnd-x\0G heŘk'XCR+h4Db*X|-YWĻ-!UѾ+6F:VaCquzߓyË G­UL/xa( Èo,]F#Yݿ3BP,*(1l)p F .*_ ; j[`TϠ. w븕 P-1*m3  Z*YqxTӔH٨52E+DXs1]0 /gqU `S +Eb Ai\3IBѸ+ נסu[d)n\B(rZBI3.ujK8}1 zZKaq#UK %쁺]f"qxP%õ]X@̧8,ԞR c)fdc!A!f&&Y&CD'aanmG^oˈ;qJ&,'Wԩ\mSg֑ܦ7$Luh̩cVþB~Er> PBY@xF,Z&:g &ƶrʜ"|~k]T[QR!J%`_7FHJ_( p rץVe_"$9caĚ+OXcJsq$2PO7,\_VI2t*Qn{̽Kng]V  ѓgQ RRżB}j%^‚nk½W'hN#exrI{jZ./ ro%"WBc+Uc֯Ǣ_(bBŞe@TB% HdD ĸ..k{f31h#^Y䀾qy=}{1RN%umj> l'wgI8$0];ji9^rYX3,ټKPFpix{ܲ'kz N#cѧT|Ĵzxҷ5j Pp5+S ".XKqG?s3GA&M,7=s7*]\kWkҚUfP Fsf̺OCK<"=Ǡ+aqRs]AE1.OT9Xxj Lj1H ҧ LY(~x^o|@Uf&-FBKP |CKT!+5M^+0Q_S~KX@ߪǙV`ǼPAu,ǡG^V&|QSղ|KAϮiP+Ҏ1ѳ?ԕn')DJ(!tEqnAK1D1X8ͽWGq|rsWLZGM^(y ަfܚf\zBf*r6,FjlU%M# Nn=u63rz*R)YHfY+e )Ju":TϮѻHh0_$Krj:4C<(̿HdqS U竅Z3*AGM0T`/bԲ,^bx5az09̲ט=o-i%Fap3Mg_o]>Ϣ\H%>e L<"3k[ĩ@U%id(0pDP05!:`}RD mdLղ<ǸT(S @FU Vx8?Bn1 >ޝV1ߡqWifVm ;r,]7iZPF #Ui,fԢrf9UQ-p 2M$״wzeHPT7a@XŨn.R{z-ǘeU2~YfC5^Ňz8?P73~\ Efk>s^wߢ\(A~fIJ:Fkw/|4Ԣj\D zv@U0fسĤEP"Yeat;mxP{kڠ PL $%#cpKPǡrep=h Q%(9sZ%AC~Z`Rs ըWOT :"J2vDA-{7]G1{D?W˃eWv Rw=ܝʿu+ܨX皰6OzN{ QnA Ԭ&j|ҪD#0;TC)@%,GX-=[-s.^}fj%}>nUf/2"PsJ$İ@7UQC Tͽ8bT^F:qQu#Z%lJJ%q*lGǼc> i*n M %Ju-&32QeW D̥C^73kE:QQ+w:ApꦽĩSBbKa1꘴1+Ԣa(a|#k˘cTJ0b׈v7N:]6fT&zEb01`ZuDʍi@]z-ETElm# "C}P0'!pRQC(oҼJǫ~Dj-?[~ޣlҷL!15 @"@ωhXw?UD h@ǫU)@e)7Q/J_rÅQ-b F g$HmLvb` ڝE[M9Tl\oyTA,lĔn+w]B 5lazfKJҙqQע!]_a;XV߈*EhaCQ  ATqI]S7g۫Cl}.bѨB ԠA^%\zЉ ګRpʑq ^AP*Vk1;[U,ԼW6K `̼Eys/JdƟRsܗQu1X< \ȝQQ׆g*a! }&tnY*餶%ALEVi`|**k#SUaxfZ /R2Vf5<* y+X߄;)j7gǢ\jd[X; V%Ay\K^ B |=v&yCM~1ߥwqB`Ṣ RY {FyWAݽ0@OL~x"T_Ao İZcgP~#9(ħH6_k+k ,[P+2;EIJ`D+Q*s9:pKq Ήf`0סouIoE> B\dJƚh,Z2쮳0,Q}0.9k3" A!3Y FByPDdAYMCl9DzՏ7s=EDzJ$՘"8b30Sj%.(a 7_*sܷڳ.'lz*nPluiH ):12b !ȏ]"gW1<7mj%aA51oQ.bS+1Ԫ1i}ICaN1-cPlux/nnV,^ѹd8!su*.#0f;U%ow/%ʧXs֎ p*F@TGzC^-5.hq,gҼ<RO gz%M/1ɋdEE1eY` ʐ9LèYQM]@8@jC^Fԙ AR #:=^ qgMLJXcj Q(|Dn駢i#A*XJ8lԮ$K;mQf7)f18}Cg tVeTw6p.<ķү?.yDE.-ʶCRD65<\GRÜfV%/JDb7;Q %pF+nv"DAQ7nʙbit]4 % ѓ87)-Jպ= 0ni-.#}%טe1yPn&LbXXQpI )q71LߦH2.py"84įV3z Y pj#rLxK^~1U:Q#3 +6㉒D0gh,s=GL?zKb`>1]OJZ [Eqo>+2US_^&] ]#f`"TS5C-Eq 'T5;[J˩w q1:TQ~VPKS^+ ܢ x`ĨܣK>5uHU0Bq/U/* ߰p3I{äŰЖI%+ڝARШjhB~eKR[OEszKxa"Q(9N~%"Tg1º sPA HaM9S@5ksfV -twqP+IY Je -K*P3JP5Pz]>&D5p bMGGyOv5$U-)p@nn\u+P̯Qq TbA : A0/0S)GP+!Q6Dyp E\*)mq{A9Yc5)WP%2*rwH!zRѥ3=#kHy r^"J^55qZzn;T5b }JYq7JevAD1\kM-F[WYi0WV_]\G*SR+9N ?@iPVs[%ꦕl?@!@.%woXWTf  aX*(xBB8|ĥ7W=jSM0(T HIX=0g@+U(#6P]3RVD.kt {D1W*s~!uW\Dy^+S i1"]&$XSD㨩a Or *~рt\ (L]jXeb]IJ?1Cu)jK2T.{fP:%dR@_>v)Z[<~L52h{p V>b¿j&X=,sP̡eiVMKA\ER*`jmgn^;pڊ׉ %"G0XLL$5ƠLJ{[CMJa, Yv8W۶H؛X C^pC=oS̷h Z2-d\4A;4T&)b880KV qa”fٜJͳW-. *K>U0 b=)h'Tˑ "YP|~tpWyGRn, WP85?(7xO3q#*= Yai(f`  d(+GdBW Na&ܪ5)C|w?#.٘؜9Y >#m9s d45[ܩeB#W>qWC,1ĪUp-fP( &QQ e*у ̺ir7?0\ ETR$J}-GK8h}e Qrބ7BRCsБ/ n[~zȵ//r6צp2AŨb%E>=skqje̱)p˲=͟v=?xkp 1d f5Mx`@dD^|Vf/E1l>i(o— A*U{BV1GRPsS EE'En*CGq FPQ.LfL1-<ÈQĹ!K+ץ?w؇ 1m!+ne꛸ژ^*:TD7pg0vCoP+"*F0TѽD)J[P|K=Fq-QnV`4SGp͠VBP ̠#Z_P/C } 誙Gm%]GF!@2şpY%X )@L&$g[gK{RcF6q(;>\1j&+HkUn}+5;ZǣHX rK d*.@@[K~VK3y9{[RQ0/Gjh "5G/кxa~%k*.N~et;uR%Eϯ.%$-2YE}$X8& 9u­b(p@GQT6 w=;bcDG0N7=V "TK Qᆷ+l!wc9%%ҶzV1z PoϢ_y`Ƣ bx&6D1/& s L EA+,eb1+qs5aSA\01h 5*ecZBxi.nIrڡ@l::G̿Rv,* vu)Uܢ~`#(bX f2h-lK[E*-NchNH1xEuDT` Dqi(7D0+8$Q)aEZ %ˈ!cFS %=j=ǘ˸7Uai \/D~IDs A&0RaM0ΪPA8z-K ASqen-1F>FV`%Ax}P3.O1*%C/u*Me/\*w yOhOڙ Nc̈P`%xw )P F8W S:{qަQ@g 5US AEwm^8QJEgkvʹw.ȗ*,!R]r\ %,9 8UFۄ ORYM "E*{267qE‡f=Y"`_k]D!Ռ/$-&ESpJAC)T /I>zv(&RH hk8b "^^(X@VcįM"TKQ -ewIJTu DjCkSfw)S/D2@JťEt*Ruvz[D/R w.fLzDn+(3)yb(z ܥj:UVNSD>9 2)~ +5n b^!s0eQX`aSqÈ^N=DLD^"\J$}GLj8-ፌϣkŸ'.*i*+/dLc1&sH<*_3]G^"m`AqH,R.fS 7Ǽinf7eW`n([R&)X[P Z޴6 ~~%6[ N0d d¸mm4N@I6@,[H:i"9ޠ˖2q&ς(R7LY0x T ,BdJD*žCVz #t7^0F 7+Xnc.!G>&USoAl-, F6ޮVUfRuJ-A5WW۝cy%e[F OQp T1 תzhGG~& 4̄UtFYRm"&Lm<̵|TNZ9f-Enr{-+Hyarz2D!K9QfW1~RC}=@7@TK#]ġM^e4R ͼQ)t(Tb/ Шؐ)<¦%_}DK[נ5 P3O^_ -zU9`;OJ-A~mJ2s vFK9`pDXܠA|TǞy"-*fdij+yGl_W+G; O|0] I[Ih9Jgp(5.K՝JDS]%Ws.Ұcv% :mfJDiY^̦lHQ [ LBT6 ~z1ߡrKDC'y(%HDD\o -2Ɍhrע5 e.1Bojd̩)) D 5:X ։A[Jܪ#bjQ4F-*ylE$ 7r/j*jA4qE0u frZ-F3PRc5*!B ĿZF1.Q1QԬD>0eZ0|`R-qJT%BJ`Z5|Mx05[-Eǡfaj 0AK}B)abqn^=.Bjآ{RC;=K.(dvL&v;Uh#)X(y2!;#ƦPE8-ܿɵ7t0_pXP^b.4Rj5DW R ̫6Ԙ+ڵf XASk^h \H,Id̥faD?lt=Ef9D0GJ}GX@`Y:p@Xy^fOhQAU2CL?0ֵZ(pTbVú q1R~m2ea'ԏ& M6L~g2X=Z*2-X5 ;>&ƞw˘H gHg2M,V[͎+R ВO"R+O\ɰ;Pw3Xf4n Yq ¦WW];BF*oqxqql @)!#U4( ZJB @! ĹhYJe0SQH(;PS+QP[sroǥgOԜQIQ7, d"al-B[QU kZ[Ns 1Ǩ[(g<~c!6s\gl+8[`|_qøc}DS+0=׸?T-֥˻g>"NAKe,oKa^ɦ%h)^гbCzM1 F9JE\p̥80,h ڱ.Ҭì)h ;us/2Ub",1a+b҄gQS5(orZ9ȵ.EʪT8"|0JGFҫ f*Q458Jii|DeHU"&oʵ/' q h$ @GTL `AzAdqEqأ#8,X, 9;:Kt@pKy0ɉd[P Ya oU n~WVsrS LŠeWHj|zy_( ~^rO[| 0N;:$8¿q>(s,\;M!M=̲ -GlOy-jpQ+I٩Ubf)QC0y`)+zE~:~4IcKF]APZJ2! $VkACr s*S->`jAm׼O@قgܪ,P s%m 䙨QV.Ik71M['P1+(&E+P.w2 핢~ɴ}T ;= G(ٹb`RSjLf/n2 7 5ߥ.\10D|L@pܪ%J]J+2u \IJZbm)1N e(M(jp̂wAgKoUf`u 2t:j*Xn/ACn\G{,*q\?B!Špz\]Q%L B;ڛ00T]Ve[v6^9ei/S)rY,eqPq@z,hJTz^$ZXF.fNx`\ى` !(xBԷȵ|A&BMK$<&Boe-RV*Փ00pА2zr~y6spQ'8״#톌Z3 H.@蠍qy^$)B p25JG~鸕1. %a]'-WE-G'dEqzIJ0a@ e`ԻsxYHJe1A1[{ _u6Cy13bRh-%\J lbCEThTh:qR9ee;> \8l#C\J|VaWYIs@\VaPVĖ߈4~oWJ_nuS.Y @p}*,>abX@Q) ILܥe/xۈ\-`bOGqfS#qOl5)K֣!7;\CO~uZvN0(iu3*.P[bXfZL#p$Nn^'*Y,z&gD_.JajQ5j-,2+Bܹcv '!1zi/0%.l[ MidC N":2qT̳0LpXCM!Nza7;fe Yo* ˦]@^@TC}%ЭAZzi LK%˘:%ʒLV87 DЀvܰ1}Yz-S"s:MH0b0R\R_Rۥ+6KuEE+Ae*tfPPCY."]8\; G/PIu[&I1Nr \0Ŏ!xk$m"J0@1{cRׄ&2>pЂW-zkݩ"먆rYXԪ"\ Ɔ %=1S1$Om/0bܻQq+N`;,e$\e 5VǴ7ʟ`55 U^5=SIRm`tp*Q&Rbb;N_+2$#u.10fuUndS`~˦&JT1肖^"9X,\Od~^`_=+/CiyAl-4(M`_}ـKk!Ք²L 7S9XB_3`I}9e 鸩gZQ@ ,hc2L GCBPXc%xm24F}/5n;rLWP1 @zWoRSs٤W z ElyTj9a8@;09l% \Ԭ-ح^`50)L#$I( B*RZ,Y3kǦTK pǠ3r zW߼D\TVF.J#,ϸ2X=/`:WtD0ENG~= V [s з栓OɄ u+Ki,jba\12D Qks QMVi665r O[^^u3j8**dߴ ]F״ĺ-/O2?Hg]1 HKPf,NfX&I5ULU8(д+6**V_rXjpVY h~!%JϘ;Z00Ft rJ kL.F Yx&`meoC,ĿŨpn lrٕoC~^ DQT/[ ~$P.qj h/Eb03ҳzl&!dpt xdXjdIi|@lS=CP/CkcqJCIw;qq(` I[%P2B[ d,/ 9,u-~eU=6@ק19e3mf ĸIµ%ˎ-;¸c"kxyExG\NUJZZbDC2HGDf&bp6,SRfHBp3K 'Q!N9%@ߣL"o?Vf,,qJwd]./0Yi7Zeɸ,y%EUmT 2R0S sU67H-խiVX=aU+\hWGDCԠԫ**?F%WPR4EyPS ZjR`ǣL9y(4t;Eu2qtʜ@"i~"M_R6zp5dZL_LXH!ΡjEh9/$HZU-1u+ B\Xcr[w G#G_P/SxMmK"ߢߡD׈M%U,0hWSK%(xx/P討T gVCM1%<ÃS$(ϳm0\G&Gp5\GjPqS%'V zp:}VXꪁ*΢S W9P1)F^Q5Ly9 o^7w ;a `XU9=FK.ܼ;Jc;z6z-z4v@ WUl-e6)jfc3d&M׏OUGpz4cVw/CLQU  A f%> Xj՞X@#6\+PF [EB.J{IDH-6/5ٹ@m4Gg j0u ^aט -UqGRԚd?b7_S2*!^#3eVa pͷ]4w3](Q. 6ܫQ~ akpjx#RzDzP0K7*Tz "%@?JbB]zUSh*'~:D*gL$MC#3R N\׏W[w,=X/2وEpV๴2'-aZc''7q*93.tl+)Ř9at=*#10МMz!lƠ id~q;R%=LK D]z%ߟoFuYu7}GSYQ=]5/HlMx@|Bh[fR(J̢h[Pf psQ`&b=: eBV6S*/sDBBArļLS(˱)Ep!UǠkӌ.b;c*EnM(fWrV+Ѳ@k 7̼Kl3Kp C 㨪eU~^dkLq@ ǘ 昼˨Y -bkz$1'RSJPT*lf-MRbAdTeQŏ Xfbu[ŷ=gġ7!aVrd fP !V%RXv1#FJ:g2~cԠA ziAw*KfN w?Ի1Q^9`qn'fܒb 2P:L0 !n9C8D V* La+W+5}1>$0#(eV ,JDCRą-,Q}QK[zAvzlEG*ݬt@p3<C<"jnj3&_Rm\! Wtbk-D&^AK7b.)^DJS\ 3˙0'1K 6<C^yS^Y1o:%/0>ґO]J;Qb"4r*=T Y*G *[ٖژBUeh!|`T;CƓES(Q/"dD%E #DQ r_<e1)Rzb8a @ y b,fda/Ч;W+>SG[m)SID:\oe]CKL} h~ A(K x"2ƠabF%z|z%z`z;Def zN7/,s/s>#GKb3ī"~e[8;0jgi6/aD=)DU1G:œǓ5@8κ;iG"<Pנ2GK%bV`c֘vuX@7D)Z50@66{̱^5,)  cĬ17fQ/ģx@.!wYѶe,0HnV)gϣnSXֳb1*m㙏u(x>+ NŨ)80*Ww1Q.t`CKo70y, {\~ ?)U>0y`>9ZJFH6i]Q̡ļE!MU.ieREDJ`WeucCŊ>2FdEFԨ4)Xs]J37s%bswx!]RxZ㨴FӤ/vXjy}~?C7=5.}55^ &gxŋ!RTu^Yāy:rX+]CSH.l") ☳Pxvyu*%bs8-y2UCt+'0*i!* )k AXbP5lʗX`p 9@rV/.-\J"̀rN&ܺT`tVeP15Ĉ. +%^0%@ܢ@@7Sp,X7TO1)5/АDw&j GrطB\Lj5i9ᇠWh$~~ZQ`E_6[S/|EUQ\-'1"RS [ȮfrP)pb`LC OnBLA%ޓxy^fqke*]n(NEp xRW*4Oe.k*c{YT jJ2c|EQ, ![h˹Jce"wSqY R.+U;iTf jslOoV@+jU7̩l0rpbօ~=SD!˻PalTT[KMtKE .vMXM=z1*Ķ Ahh-T}.Rڸ@Rba,J.2SbV4Iމȸ]X kAD+}k3hp0AZ;%r -E1#m {"58>&.S˳.㨗RRhzne;L8QӎV;bDcmeKj^a.*p&X<Ø0DEJC0W :c8j$G+Xx :vƠAU!X\VKCP1 Y}H/-EkYC8Kưjh:4s Zuk0V@dNHz.?/xSd̢%z6KQ q(Aj6Cz.):ls>1ȩwSph5k+m^kQu`>XEwe kYj]QS]_R!ML &l5D a[İ^c ED*V7-ix:E9~;Qa:KݬD%dM|F jq|QMF|Ex1#gqHUWZk2{xS"W߰ƍ븵;J̪L}88ԫ=4E@?psX4\&tD!kH2ʳUH{@x>z&KeRkPUk#͏iN.ZnZĸ;Ѐل![ľiLuLY`TG~S(hbVl=6QTU{~M He U_F TéTP+ivLJ'd \Ys/7rc}LJ&0cEN`\K@zzb[Dc 7z8%-0Q#Pbg֠:%MZ/* ĉRUω0qVXo 9A0n&a(2k>4z%%rTUEfS8 K.,mB2%qy+hcF)e85 EV1J%oz4vo8Xo 6"h CQ;n'ܥn8%7O?D×#W\t0.ג^1[TVP~sx"ʼs@+v=zV.#DJ=XE}ZWf q ߩF~EVb_`Xq*Xq^̼%WZm0UjɢPA\K7 za8/.Uҳ~dv9vU*mtr@s\ܭ >W3gAQ LD.RBj%"*0'Yl9vqOYP L`0ٓ\]\(Rc! l&7ܩANVFB[oS>Dw ]A5 ə0oZE{-be0€Ak} Z1e |c Β^ck z3\𥰑 ~[b2KϢZ&O0E L}h9f8:%c멄k5枉~įV型=\aVq (,W\6kڮ%L1j*/v@aؑV愄C}1-= mS}=j[mט˨؈؍^sF GϘܶdn's·Rfa~j լCOȅo 䄔n4R25)Y 0fD+4#o n6Jp3ջb#,9傑j3FN 1s 9Y_Ka5gz 7 ce1U L=: s~&ODz&CQL =;G f1v\Uwڷǥo2 Gx UCq@CW4Sڛ} 뤾sJJ^c7*,NBy(qi0~/y%徻} ϭ]#tunY8# YbQY{ m*p6M1ڍQZ@T 9E3q1Fج8s 3*ɟ-nJ3G~/Fԟ4 8G.i^)fC{oevFe5 Y¹UJjGS62e34 ʉ!cօ(seA[ƐwcST' w%8sQ0b]\X*$[X 0`>EB7p.'棓ۨXH<̭Tq+9efCeje"u:3x6\K> ^-l<{AKh:m<<״JѦ: Q%ÚpAY?R̻^A~7J2QVs*Ji?)IOgMkPaxs!땏U\b87Q m+ЕY,[ m D270hBqd쒩/q{ 2Mw,#N',f=*q {%?સ{|\Dx_ޡ|fU4`WJ @(xҷ/19)>r*KE6Oh4R/e*&HQԺ"* zMn{E"SQuUQW #̺+= m7}M1ni17oTpN(7+P chBq39HZP D59UJT8K1)wRp 0dnb7y%_@_A`;MP*9etq(MLj'xx[zp$8x`\H4AqaZIPQ2uk.Jq;%2e+ b'( 31 3[n59]MzFiHaя]`HCGrzi0hJǥXfWiY rA>3149e3wxM|Z!e~W(|,88ՄP>])A*gn3(IFz'xi$mEҥ9 S,ψHr)3pM8,.R۟h(lYkJ5?c-Pr{ JbLۉ f,;{D +A8bŚ V{e((*ܸ&A̫m54IsjD}JD9ԠuPQ&1A].QTN}WPoW_q]b^s(W>Xk1( в\JA0T CM/lXy $Z)]J3E,dޡc08Fu#-A&_1*jT7b^e'EJn7^P`"PXCS%QL#Vb@xS$y+hd0&yZWrP8(s@u*?9(g=0QsLU7w2E٫'~`uz-ZXr;n "dtVӸm+1(s;71w_RV' WJ6]G֛cKT3OEZ1R3` aFC;YBAC{q L`Źu6ehXVxף.M^}]bowq\kSq*: 4i&P:":7 RHD/P$d p-9#1+5 Oi=u4DE9&,W k. L'Y9 @IrJ 0 `PZCu{qLH<~@ Uz Odd (cN"u1X$ Fp.Y  `[1M26 I *e%L/0n z]E(VCRnV C$@RA1A;]L#DߣM׭k-M1[z E@ZwlZ= 053(`*Ĭ0gQ}b|!5Wd1cBtaXoyH{%(1dVs dL3q߼7AǡDO1n`cѤo< >?,ק8Jρws3D-a cq]]+W>fR^!`5wW7DMV> stream xXK7ϯ0cnvg|9%qNY_#UIZ^[w}P NJ+1jvpկN_?'P׿'?]?r3\tH};7ȝXPvn[>W3]+\^)_ᾞ]&{Yϑ#|AtGtnzb3"j iIߐ"th̶H34ʋbM 2LbK$&xO2 dI'tE%ѡs(LI% F=V8|[(\z¸IA 㟏ϧe)D<0[D+9ܙT|$3#}lk9]lk\nKNt GӝfEӔX"`)3la1`WPDDf"aMFyBfFtfR(Odu,L,bDU6KJ{n 2NBlH՘=4 2 / l-A L^*"6jBURdJ6e<&K9C!l=ȚHfNDNxcDnвL^QRnI>|Jlqhlx+TW`v=8Ch Dr&˶NLX&<=Od.=gz^)(b -TH?qCVuS^)'0dEuSSYΉ B,DGm ۱ᰚ er[z]%n8~Rr#,ettJ.}@9x!ս16 %86=bc&Aq^D8n[ 'So\}ZQo&]W^1NN*1|-]'@ʖ->MM/6F9g<,˜C(=Tf]g(L&*Sx&,5y>Ԑ\r*hq6yxW٨WA$ UƈCKػ8̒@7 [7Q-:Uasu7FO1ňWdycA5٩/;R}y$qy:;(o9d ķ -+.cr;~K[s[n15WCEMDTk*Fz| "^;h_ԫ߼zկfx6ck}9wj݉( endstream endobj 16 0 obj 1275 endobj 18 0 obj <> stream xZO+ S9F#`a`hniMx"Ex`5H?"q'~?|[Ltw0})M3)O o?M?'os!,Ì 4e'_RsYVHnRH?ߟ8t|>“K9'3?Ï+HG[<X.fz+}Lof}{LF+ 2$#8LPJM"Bp?D̬g9H,7A1Hy OGp'τ| q(~>ϰPt~M.y-lt>jDb]z]q2͔6Y;_-&}ןU<T`* z=Vxxk֯bq> p)Adϵ@TQ&O6@"Aެ<kZSFCV%m^2)K;u-\rnV yܕ,Dl4i>ڱUߋ ``uG,(6ciQ(ɒY$ yY4kZF>Wو h²!a kYaO55"1nDLV?&8=oűخ}b3)ub̗zݰ2Ը!e`^l1GHzͲougܗ{ۚ+u*bT`և׼nض71(9 wCK>*-vsfwE4}DckŬf]Wrgmv =I5Y3Wzi;]᳝Z S%|Ay *j! _/ZLo ۄ379.an:Q+-^w "x{6>wtd:n`]o˚U<kH+1e/}Gm_]HkK͹0 &k'j)7Yͧ[4D߰Yv\ձT`oV3Yc9lO{?\ UNDRq7m AtI;%~]NӥJk[iE]IŎeR [#;PܤCzYkWכ#TK.ּR`ntl>ٱe[KeMNSzgz-nKH5m]z_I`RmZ h::Z.q lfC_B _tp/B~c9 {څʘ#rVnSEVqb(zy: c|gWKrfv] ew{F:tQٷ?JLS,E+ l ]ݘz6ZO)*ԻqpД6hQ?L'ؗ|k5)M>iy6n?E4-OuV֛^?9 1onBK:[]xW#X?֗Wwiשׯ:ǩ՘Jnba@ٛO'v;%y֟ҋ BB y<<ٗ>O #!,>O2u29MִQHF7Fͣ)S<[jݠ5MVl`X-zr ?VҪw|;uGVZJkvwFcFôwôwFôwôwFwGh?nM..1\!KuVE} 8JO 4㨒r({>-Ѝ#!<9$Ӎ#GCKŅn | `qY>N)Hg[ @)7> stream JFIFC     C   X" p˭.8FWO:geMJkdu2YpʲYuׄfv+~RxdKr->yBԾ/ӶHY5Os4|rZZ]wz]JܟN'kk;jή{YK򸚞sҐ7]λFVQŹ+SXo%N#6t<|Jk֠͌VP')0,Tޖz42_yH_:6\"'BgsC=͢ȶsT/:B}q]?F +"|;S'{'^3BQqiNK(B,F)lgG:&ؓa圶^;S!ä{ =Yt:aqt0;{) H)H^=-=>"š;s,gsV{^#w|:sEnOk7%[&6?}]oC윣'ѷL#A-m榈yy,*mQO(f gqƘ~~,cc æ}XD+"3rTc2˚$tM,زw`Gs8'|zuEq >om|=m6caz.3мS]C.zM5F_Zϭ0;&d_>z-о_A%O_h>z?ȍk4{*+*pFF8)CCibLƗYU?;!s|?!omS:gӡF}il~5{tkwȫo^Ľ5vYo[7ϝ>HC-#3ӣ@r$ʿZ7r8zok6v=w- zHāxҠ%aAY=ydIMt9'#Lq2k¸.t'%pc/vyfb3uJy2suέ3}pBg`vIUwzq2 :Srrw@mr&'"TVb,ira3=tptKpW˹*trBrˌ ^ 6WV&Wzy%5-1 sf1'3 [0h/=FogzFKV+ԳR.l QdDjɢG)d+ mP*coQ"zRM~C*Ժ9A XB Q 4(СA&40ӭj200Qd;.] @ȉh62q4[qR:䓭p6CLjKjR0@Q@H 1F NVq d`25FL\!` @ 6S:#=2 m8f7I~|*TԍBFv6Sjo,B5A!i53I&)I1Fh1a' 6 Np*@@0A¶)4fDA6 BB3DM%\6`PJ q4 $֬T99p/@BIf33HiP`5!RH1f Rch40@3! `2L0@0+,lФ' l2"@Sv!FYUfxҡNЗ:(vH560FiP@4(RT8K6tPfp3% ΐV&,InA@HMBh!HD0zl3n=ik%l79v;fVV3Y?%Q^[l3%Fd8wgzlwcY<,P QrC,zm{e 㶍dw69CyK & Dſn- L3D OfrJ5ϙ I֮m܌\kTgzb7 sJ1]ZmIp:ZN2lJ̢7 04XaY ɍ('}  (F!b@ SnVlDD)˱&UrŮhn*V`Ѻc-e)unS*dR,[+xIrN:ȱ-Z+_.Ml4 kXs_Њh!Q%HHwQi%Izd8m~&s^Qrٶ$qBatIQ2jɶǤEzWm0&Xj>C\IBbKv0\m ;u[F[V5 7σұ[cOL0SM>Hӹ4Q#/Rj3k\gt da,MBNX+DmLvx뮙ќI2{5)YDi,a "Uo6Ćs_(X~m6lu5ݽ̣Q H&~^ ˪1M*`[Om_g`Qҁ9&T[jEg"CuhViF)Qe.,֛hR D۔p@ DʙM *hfʚ"H H`k:2 VS< %x+%D}?cF`b "0CStvet `F{ERRPsslHV[,(6IWIHJ"Q4i4RkfGi$R,=j-qc!y|olM@C!Jf` "0 ЁG 2a`S#q 0*al@X h@J@ jTUz7zX u *86! 1"02346@5A%7#$&B'殡iT4I#|put })m?NH,6bܬ"e,[T}NWkN٬>yյH3vP@je)tZD0{)TjUZtoZUڵ5)Vf jܡh/T-fU ܨŘ)vYj^*S=*t jKMVHUN.eBjv>niz 9s.%A]n[223c_!HVb Q%5<$o{95)mNoYd'}%PCh?_!_q1@ZvuaԸ`~'n%RYB3Y(z2(?NeF:cMؒ2ÅZD;D?f>]ڞh_]N숋t~c S-k46QnP84%eihNVeSz H!LnH&uhRݻ9P/ӋJM>Tw2q D(o%\h'EhݢUAQ+,1_6QqڴFNBƎh~SY9ȗÄ*Z5-Um ;B&2dlޑ C{oնHժjKrn=Q3pBJ-y((I}$:g7d7ںur*Bj˶%EWLWvذ ~U+.][pv] ?zP%Ս6}q ][p fU"/k-kYroveYҳjkJ& l*亪[dVYԚEbcV, ]VԚ> .ֶ%Q~ê7+T(cH{<g e 2$Ak4w<T 54Nd-D )9>N=P PxJ^HŒ*ΟN{DLA)83xLtL]4DRˉ ̣Cx#Qn*m&t),in" %zvȨ*|Ҿ(2TEN]K;֊B?i]OԮ=>i_q.#>LK !0(74ENS.&T¢+Lvpͬ^0uhGqߐ=9%q) ћHd{Ck%#.EusPpTQ *DMq8pkQiV?JwvJfR⋯*tJL içBtcZߒ>/{4cX~Ms7 J ~*%Ƀ%e9Qz$u$]_we]:/lсs~+nC8Uu quȒf8tתhTiųL [mdT bM]FmX2hЁ{# bR LKD>vddgk#H#,H!N~HuK 1Qm(3j|ZE<5/g o×BOMA|5R ڴP%TVjٺu-:*xnjͨŽ&b&Tf(k[T+jbߑQ=bUq55!UUUUfѦFti@;R%2T8xi#]<Ѧ)DFbؤKf:K6 ;3\m7_|⼸e {Mͺe!H?Jkzy@ZSKzuxY i^ꏜfj1 uR .Q΢ i(4(aF$[rieܛguĆU߽K6P-.[w_.ٞa"[;Y&o0004 #H4`iFc'N%[Ecøi: g,Gf)[BC5*?PJkmv+cv\Kpʄ"?GCFO"VA>FaNSCg%]22Ve/Bi*_Je٪.ʯ8Zu[RPNعsmyߵ5d,^6Ժ+×!m×)mḼ Z VkCաhxF<#ZVǃCհvelx.)M\7>7.PT; 7*ʛmKnfׯR3R-j[ۨT^VnKC;:F*򞿨3*͝Y+QsQk$f=L(hrY{ZRMޔ]}I{ye'%47|o7xo Ѻ7FtnѸ7pn 77pn 7;AA)# cɀd6a(BӢ1ĥouԲ!d;ȇy"Cw!)))) Ҝf 3(/na#>xopY: &M7~(F 3~EePϟ ĸ !5&--:#~7J?Q>e,]ʵddg3 A}A ^~9~3 0Rl*lŮ$3`VJ]L# \P~oқQ?(!-6HFGȌϞ9,II7T>~ ((\tȋȳ sKJJ& ((/>D~U?U >~5ߥR& ((,g-ld9)[ ;Ht3e"O_KJ 0G//q85BK,򲈟K~G>"Tc<Ykj3d}A!s^P3tә D9U 0B8ӆhyI pdRX"漒!5ʖnSfr}ڤ6M՚|%߳OPQ %Q${KxOdtm X4FyMgyD#ƌol|}mi(gsO0,އ-ݵK7"ȗR[076wq<:Sa,5!{ONz? Cm& (VF7%h,y1W9l71]"/~r2, suqC ΧR$Yf٬e.NH3µIY% /iY(IFh.f :;'͍culn n$n$n$n$k!jdd82@tHRCw :x113 RJXge.t}"ByrR T#Ar=AS`cT$ E؞؞ ^ O\gPn+DAxv'{Ra< 4 $ T]IDdJ&^˭#O3,%ce<`3ЂT[Ej KBF3Ƚ~ R(N -]mQ7HJI|P&I GeqVHK-Ց 7rI ?#g\$g*.ai4ChT6P j0Ж Մ^Y-ĠjPqhӑƳ$ӚGh]X{(F21Lڀ܈hԘdDI2"\@jAEg=OunY6Ԋ1h9lvc5k H{yFlPN%`)eFAć؃vom".B.F ̂MTKS[eE0ߔޕn%MVI.`̃Q"j #%dx Np@,F(1!ԔAI)fY< ֬Z&eL3._<4N%!ʘq0ӥsmHy2yH%J(x#!F,/P?A$}}R+c%sw?FBA2~W_Az'^(! 01AQa"@BP`pq? bR[H&H؎D#ؔk%Df$, txGcYcR!8.E- I$Ē"le-=p|yBUKE7-sXct4QQF QQTb$14U4Hq=^2v%}/>̒F| G,R4)-!1jغIv*"`TNDɂSEцLbkZ&|"-~HeEj Yih-R:{A.!?$PYt*HIDMA8D$DZRҠtEj Qj-EZȐ#TtD17H DQ$Ir/Ig`"j \ŤI$I$I$I$I$I$ʃY?H(3kٷm6~Ϳf߳oٷy-^KWٷm6ͳl6ͳmOΙDY,&j&&H Xb_Q}E̺nlpGkAAcӔ.C $! 10A@Qa"q?N/քUl2&~$[R5Rȁ$hlm7[N WW6@4{?ȴNj)U)j68eLʂLuPЪFHd7zDT'vl]=ض9Vܐ),JFb1~BB5m^SP:U?6:]YhJFR:t2*YJ#w%}I#&ID I&LfL%̟I$*}dj]$A5(C'Eʑycbd,Y,I$C<&yE+x"A?ɣG!*AAbA&&&&&$Z-cw >tMsGuW#H sVv!+.3[pgthzh~~~ _*7oїᑑI"dd+4#$ddf*uA(Plj4cItO '#դIB22BfCI;M+uttM I&tO%D #\_^ϋ"HĈŊ#-%G?R  !1A "2Qaq#035@rs4BRb$Cct6PSD?]SN4Rik׹q0R_x}Iפl6ۑpkZ>T\9?^6OWulg|=n.}9G\c_H6eS F).YaCW_=bLv-=SZ~+Cn#8 2؛-/vUtes7\$Z$yVо'L݋Ke;}ƾvw3wǑs< PKޤ{/skdLH/5 eRAfk!u}8W }jm8bmW C2^.Ov>B N}n T5'I-){/ w 'LdFIAgZH`5>'w=]w{uʊj 칂m}.]o +kiA֋"y">^H݄bx-9zY[i!ͽ(]qĭey]l)c(I? ayp&*"!H ?x'Eluaq f"4Oİ,ߚi]uMK8y,a¥9S0pփR?Wڂu[gƪ/H-?mV,j+Jc26e金44?uQ3lh%x^RRiw%<E%nͻv$?!s]ysJUZ}_)H'>r;ۃ<[ ܕα*+$xӽS"4xb Ĝ ږHZpޞ3U|Al/0^+Tr9OjSZ)DPkQAl?w(,s<T[Њ4e%h#(uE״$ )-;S9muG%iU;Կq\2_7CATSuN8R|9|Li8M6y}ߚikRF)`$DdD+UE!Rه8ͭ m1g._ G `u.=7kQL9(EՒFm8(Ox;"s˚\~HW@J f E`$zU<sqaFdW4q:l/2<#&Jܗ^8--yFgLu"NḓN\c rZ6E nJU,sdᅧ%hu* ;sl5"' 'YZ] a24d-*XTR<67Eq'V;d*U, 'iZGqhpO| 1*j^Q{v(ڽKlhh[#˔{=K["15/19?EE"*@7{AǮ [#n؏w9Vo+U;&+q\Š-% fhe]F]wSa+d9BMm(|_ G_\QA{CMƶFƶ?օh14KeB^S=Wַ~!yӜ^MO_4P.nZXu mfNA qn-FHH9PV׊+nԭ7aׂᷥpҧޝn_ ޕjq+]vH +ޔCK׽^1*teV_7jZEu\6`xWUYҼk:U὞\d %3xt V5^9*ţ:JwPUJ^׽^>>fjwZҼ{:UE)%MAHf7t6]+]MX& dZ8 pmTP2ZkNflv5cnmki5S"kM<B)t.w$t}0V"wbg;:W¼޸X'FV@?uw< iV7OlEzƒ?G3/&왱FhT%6Wy؝?u:'؀HԶa,n ȍOb3/IaKlMކ.fB;z;l6K4cЪ6obÄ;VXbSFښ"?»h6F+Iv k;v-hX]@FzFòPcV):JQ{QЦc6 5=y&1)%͒9.m T}o5wCEZk{䦲tX9cмN)/0ܦ5)Ҽ[ 9汱W~k } -!6 nGgx-cI#<-/AlVsځxҮho0녿>Sdӵ=8Ӿ ҏ9fpq7w|Bz?ҵxq;qu3a QYnCJ];S d8̇vlCgkL ԄOp2~*^Nzml_i51w(9J?[O080P;qcXRT'FY ,7qpOke{]x_9n4O{D\wXn2R~g nuE.EwdK`J/[G˗-[yUGyc"m1 VVxh`|@[ GL qNkIꩽC![}{)kFIn_I*_a#I?ޥEJh]%^6?TSxU0B拷?_RCc]'kb(n$5ױ߄बMKEʲNCn7mOlZ?/<8eZ2n ۑ+_JMkH#źΜ=Lҍq~n^\YڼK;WOj1-kT{+:SW68KI6XrGjci*]}MUw4cU(9bӭDFMOhdǥTl#kliTHAwF*y"}XGjm+pdxMW-^Tzڻ6&Z: \y"}s;Tu4== ^^Yt:V)QGj츦h?g2!{7ݙgwelk o&W7+)k:FU;rp`oJ|ܭ/jVBך;ah`sI|߉<[+?b-5`= ұ+dud3E9Ytr.uC~z2AAracAG?xc%m3ZpqS]J}Ps;{UMMMw{V^ϊn[1n荨=0>S4Wn]TR^$,u YGw7"&\` EuxAUh7,V$B32h/'}^OiEy>&O Ӵ~#ڮcUPlVjXm-AWIh4Y2âm Wp1 F׊W] +Vb,6<#-Ch,nwޡ}dlB襜H085mWܬ԰%FZxObe'[˹a[! :)%Nʌ0ww+#ƶVH@V{LVimhtgB&nbBu@]8&8퍶^nBHB? Q]Kw# Ihs"#؛${nkۑsc-t;5`Ni;huqc*+ɶvjkɶel}уZ3dcτ!`Jm\N+qhd#,9Ugobmh²ZKM^[UW vZZiu涜]fX1@YA4̮=V94iE?5?5{^XnWDxI|/Bc=u?5>ʾȟ @IY`@ ˧J'(q*э`E/]m+Й4|8 +.k_qKFF(qȪ=ܖZP4bEwo:?F?{vR~-V VC!u~-- f2ߎuΟ%^cbAX+H?xţ]-pU][׷P=.V}OU( k0X`ѣT73R3ۋ\QiE{쓣xo:14\"Eq?)zazBzj{;]./tG`sl V*U@Ƽ5eW'?\Ĭ@bL$F^eƜ_Ks%J W;.O`9D5O*d1AzZ)!}PX?X[-uAFlEG5ߠ,ikΟ{# rV+7M8P5+)̧%B \]5#G;Q$:_2,Kߐc%b?w, #% ^5y"egjo stXlP%w Ey}{SͿwT#՗:!ss˃ $A8'f^BnX#M%ҸS,Ӂ3xą,ӊSZ?tїJChh[@YZJ='Iojtf'Iojtf'Kojlf&ojlf&ojdf&ojlf'Iojtf'Ioj,6@= - K\ƫ薌넍_DeO$>מjCcFַxcJRdW͖{ܒ|ՒKT(k%t7GpǕy5v&nWG׶?pˡ/rsˍ5`i|+C,b㫣x9'|n*G6|zmN~*<5׍5S^>f9~>iȪ8xcc󏏚ij1{ Iu5ƁkjxO?ϏH5fZ(oWmfdօ2;cyy՟>nZ;U 8*4ܚf3|<) {30A8n(kD9{5~5s%2 UM [1nm滺kڮ#p"+;8ѭEHu8f/sݭ9h@pv>5JȜZ ?0!xE n#1-IqM@]֘C[_IƩ,bAʈeM7*Tf;rM0Uou)3yeMmL %Szp<$0 Kxo]LtxTv~J^t.ƚpjN~|7TrˊnGR-xV\oEQNtqN;m]NeǹYX>Mݔngt: 0d oj"Z{_;WS8f>=Xӥ^ `Hj#Qjbt.u2F<ꚶ8ޡқYuUU4ho"t0ԛM7-$YWRaOXyĹܻf:״jr֫]R66P.C < 7/@H)CE'y| i3':㈥sni=i#B0N{c)IAǖs"sq̢QP,sYźTI̝̅ΏXnU%-N~*k¸Xb'xRNFZvZ(n\ WzjBhN$D?QR.թ̎"qM<['/@B<̃Mک^>r0^cXS] t$dwP]MEE+n3s#CMa]8vh+_?q5k!^ܑ u0KK\CxQC7IJqa):!EO/ F*kL;ЯC34 ⮬wu4 k*!J$|;b  #trDix[$Bq. }Z` dv%04_n߷r< =|o3w9*Oo7mX=4ݣ)!1AQaq 0@?!XE^`@~eS,ђaBqm[ˠtaF?]PT Xn^?H#05s۲kL(z]V3à/r ~/ Qe#:Z:enMh*= ogHnoQn94**p#"qn$tƭt})CF4Rk}& +H$ `Q[zK|VRތiTyU/EV3ɽK >W׊×^@yS&sJV ʭ1-fc Ylhv gZf<5s{ѻ6Yݗ%^6y| 0a ~M= ≬GαX~ TuÙ1,Ex [lh/W(2㋟J_+p뢕Em@6F&mw 0[mN79o#Cީ#Hv|ϒTU: a};[_p=Ydh_Y!7V%7=&t=ك닥Q[Ʈ #l#鹪zl=vGlSkmk1$S&GxD 8WH] 5&ktyH0ugH2T W9Nw$J\%wb,%,;|=ɲUj/n?2:J@hzFәKjm邳Pk}ę-ٵHs1|V_݆͂.EܩY]4#Uz,9j15+0\YZ٪įH@i2Pf|h\?vzg+ sU|6KL-l߮YZfp6ReNjT;rI?%%^k娳\TӴy.ӟpಥ^oV*6-wFZ"mP- ŵ7`.ҩט>pl6GhoNnV /  7 mM.i ϾMQZ4-v*c0%J,y!pec3/6(`K0|&6Cy86ͯyr5-Qɾ߼s\5x.\G]&xw~Q)doƭϕ.RmPxO=R`Gx3%mt6Z? K}W rezcB)bw=!2[.ɹN/c+T/fRT+L2 HzJ )eFT5702V욾KJRYӄ- 7e@#@ӮpZǡe''?/Em~ҫcX除Ej1',V/]UIHLR$MHkfo174<kuĩ*8hI@g;~*'6zJU TF' IF1-+OGc @4vb-ƣUc%rkvֻ/aL< + x"7vpDf &0>:%ˋ*NCZfBTA@tݦoRRbC,ݖ{n?Toj4rs[+,bSt!PeA2|rRLor+Ah< S#~7~:}O5EZj\S"*Q((i ~9( YC vb5FuA5E? ^h֚X{T9媙CMFTxI3x'+xE_Ҍ P PhU4TPi4S|loPج¤UYA\Iýdr G{bo 5z/ c t6Ch(hb01zu1@I|1R㘾6w%5ZA2aR%([=rb|;ˬc⛪JXC 2v驈-jZb,}f^.!h"ˬ_1[ .'Y8n4O%5p+snU֗QY9\852s%# nTe1Ev2iGSRe9xZiD>iL.{bz7"IoMK Lκ^cYӗ oE|7’ܪߦ4S7v~}a E~w2*Թ!V9%n ,LfcDVx="!+i1{UGD~rr9u[tUCCģB6i_nr'O0GVALT FVxpYimKĠST=a_1< Dsm{jn;;b3a+(Gői b` &̘'0%jf0a(g5[† Y2&6ky,7^,5ҕD3E7uy5N~Ȇ4[F9+o6V' @V}ZH LckVR ee^&EŹK!@G ~H7okan.#ZMESqXa> |r>@!t„ìxWeC֕tX~9( nh:\5icQzaN>+t;@]=%%JPqUZv?(TN<.Gyy$o#_wk6_5\ zʏU4/D|_r;"/YOc]/Pn0.vSKNkre]9 iX] mp _RQEóX|Z?9Zxhgu\Xp@B#)g'3heK'mFk/G P"tJͥEA^s =R!6Cߣj4e,~ucJ+X*]ͭU}L=>eZ5h:$]ֶϾ-類H_y.9C4ztѭix C#F@5{Kp@]8A_smU' [/ w'F`F, s`a~S0+nSLkJ s=0T61l6#u꼢Pv8 ~lzS#9݆PYg V6!WO(ڊtRp93vv'I:[>@#J i|.`YfN>7iH-U|FtS<όi ^yG*W"@m|ٮE6Κ iDkpƳ+,YnEG-Y>OhɭZyp%-hʾQ`\Nkp E= ʳabCM"̻Vu2pErLңY%"ܻJ{tDW>֋yg! 5[\n缸 (gEkTs$zoˆ zzO:Յ]B#Ng 9K>Ad!l8a"X H~ҜJ8Rב~^~iW:]zXC / 5y-Pئ;-\ͅ#0!%}4bD]Ux_07t+ߌ֋o%?0I,5 gu4%J*TďE]-P֛p ~?,Vh\ԭ;=Hyޟؚ$EN7Vft*EVF㴠  g3  *\m\.O̢QVy*ܳĈVRVB4ȞWD#}*0'C,=:Hmvlױqѵ[8;!k &^OOl÷Ye/S|S)(u߱0BZ~!R~RnVW^k2ج0fh%Yz1K(4z1, /1.7]N#X*BZ ykֱ-a;C%͜0OQ~&^jQQU _L5v:ϋ &jW $V牮Zs &*R`0o,嶦ci~YitF!qs΀Q#za 1FC9l=r͠ g=R9PmA ))9XS+@ B+_=nÓ .cx1xU(%5d2r^1[ Ek 7ih+1f]((7eS.(ݓ=kZ|ä 8zu {MOSaOS?_W_W?OW_W&ԧ)RԤR)E"ԯ~g&2˨#iNIp:Ԏ=uY`l]'rښ\Az#X)}%o&sV~_?O:1n(\n,Xt!5`ٮaҸ QVX3CToaf# rrg*T讇]jqo}AMɖ9x^  4X_}%0֥kHh JOԗ.\r˗o*k #Ŋ/٘OY3Èm$8I a\e :Ŋ/zTiR摕V` VߙfҜ\#YU(k\vC Ӧ>trˇCE?r,QIUZ3U3p"™U)tcѴJ՟q%\I0&ŗ(]*D: 2}ls%AԾ'_}=qcCRN](eN%S5fr=}}t:T:_K|\,c \M!Y-{r",yk̲S%LYe\>З_]K0社~ҊCH_BaȗiV!((ê?%A /rQB??5.^.\=\ ^G@4%Ō'3͠GH*XMknbsx#U΢se_KF.\:?.?ܹ.]πK}.1zդa*iahp]dM: e3g)} "Ryz?|v-c0A}e%f Չ^oSS;\Fqiy._K餾Aw/깟;/WǠvw0(jkMG6EWk rZe+PcYկP Ʀ$|à8*\|(4t97. t'E}s9E~c=y6W.;M*"c;&90.j:O5Q{Xi U+X RZV)鼹M1.\y/6eçqʣ@yv!?y h8_8+B4%$#qʡ]5.cMhC4(-A:kB.\BK[ٍ{}}e<0\>>>ܽ{K[S#ǹ<_t(.AF1cbva"DzE z}GFDe]" YI+91̡)HN)T&2T&U\ONU~'0BKO9 O:Xy??L0S dt=r{Jx}<2&z\,XK3L˽il&6W."xߠK@-A z1V#C9~ ̦t] R+<hWi;sA.cy H9r1qYQ(:zS^%nTk.؛qli,j%@>F\œ{lHS8Zxc?x~ÖntAy؎x]=7>7> ωcdTAIq=.fܑ&j1\J/}gn˶~TR8he6Z"гUxf_1`NqQ 蜵sA_hpŖ/yi#`Fլ-)wtr= `*6*0+vxFn ǁ2ՃDp ?*h*PF4#gx)[p4d5bH9&>1^¿&AK#>(UʂyVbH;7\|S0%xi Hn .WPE1MCHJ+BHN :h015bY.YlL1)GiIWv,`T=_pI9bxV\4Q%K)Ncy8k YBxU ƐcnP`FpDr+0dlO2R=&'hlMUVC( FBfsL7a`S w#.gq=/e"g7TAi[P_' ~17 trR6hte0xi*$c-:D"P1q.VU ۲WCT3 홂᳡cQG R }l--9ܠG74x)^_۶]DM'Vl˚.N`7x3),у #t7{Ja҉labl\N%kF¾ĦɬLĕ :=yIhv?0-rޠ+&*1l^A8D_%8?ӣhs 󋁙.Ix,Q* 0?.`ɀJDi1JZrJٗEl<RJ\[6b&>a%K摅3,pT87mcADJ,Le^\7̠'*fGF-COwƣD PfЭ`HӵarBfԹBc Ia0i4~(݁/h:G+H3.PR7יK<Õ+0ttqaW(R iifNq4 hs3X*2JК@q0Ʒ(HGQ0B^>ҡݾRs83C`%.M }XeT T07*BT~=fn丌èZ 3Jxe򇃖nRXXx:F *_Jj̽*n!m47)נqMY|6t,* !/GvkiaۙVB*6)w%[I8U5&q *cc 웲fi8SJ踆mMⵂ%w,س\WPub\ es@GMfaWzѩ`V66i]EqT,Hw2f.B20Ăa % :xn:kNRa4FlRRglQ͊񚮫-Ĩ.Qjzbu.P5f LL3}Q2Uuc:X|K7L`#g9b?ˎ0 } DsMY۫4}, ])-;pKJn&fX*֭s2[XTFi$j'`n KFWEG1OZ%Htc +mRa@^wT֨XJC}eM|Qh2M]_Y@y D3hJ*N揪zFLEST&kX^tWJR ԗYKeQw7Az*"`0CIg(w%Ν7fss*ZSi/qe Wv/(t<^O52a4sث%v .hI9MσhOD>iSgt:'y3_Lh\MiWFu1i:>'ٓG >n~jy: @/s{RT7klSeX7!Am~&:ylOv0ϸ}^Ћ./<  G>q_k lsMd~B>^p5\ |Od5FÀNM/*,(r3ᇈ]tߒXݸ qr9NJ557d# ^ܶ#D^׾C,1 |.Oݎvyg_7;BCqd#S UW^bsm֓<qYa3lcRW;uNwe=v<@d1ook5ֈsqh~_q&<\C2ʒ/Oل3Pw)aG2B)r pbOЎXLeh#Dz~IZ kUUa Ac}f;xDOQC,^ Sb!A'mMmK$YvB".AB,4FPFPeQ4QT’$|Hvv`Q-fCJ_挮=}eW5+e;\s_)D؉W&V~K_wy,1-7"Xh0$$ot8mRTdz]KSB q!#D='zOA$BSJ X<;MlнЪR͚{rOrIG|bD4h!Z0kC_*uм8\QER(%/}((y{ =/CЃRFŒlcQ\zW o\ފ^Ӯ_SJ"( m[ecc `\#dи\i0.HJ% X!NuQ"pNn c$%d $@\&u+^K_'lB#5TF1.7ld=3{1#9cػ KY\ch4I -$!mY# 1cIZQo "1 Bro6$I燖6Н'n/,Ĉ+fD _q{-;G6NCEq.XJ1)$-r]$nloD+}%+;w;tw Kvh&!1A Qqa0?Q\PB,ØW}dkn[ރӿ߫}/nG<emۓe2.F!ďY 0=۶5|>)ů0sGF24`lrٌ2+Tbģıhޢ8݃?psmY`k:ml[̕2vT4N0L-vlCy9# `^3sqk0soA2}{`+?t7~F9-93@s~8~/\8D/'㾾HAd4$6xGqpc&d/,Γ-oKG Z\Rz~<t7KL垲@F\ۗ3HR2#ơ#ti.CieŰw'Iv/>Y:c>%m< "ɄjBd >^39>GZsqu]uK9n7NϑHq n˲ǝ՟?V>@=_o^YG߄:䌌IŇ!NOQ/ψ\lmٙGX@iW^:~]ّo;9;Z.Vo -[?CI#p%  |cr rKl |,C+ 3\#3rs g&9+8dwԙmeߎ̳- uw/ap@]LW.V#E@KevyfZ;.|aG,c>Ml leÄ!Yvlq`L-;ryzo>K13^zuOoG'!1AQaq ?>Y9.8;I)qnP@#uP)]@RgmJ9EC 2DVʻb\*)+PE ~&* ,]H-ؠZa7!ˋVwt1ڻettlZE(%VvAeT.CDPw4u2=CeFk4t8֩Yh*q2x@fu\/4YIr.`o@[y- j{XJ 6ŷSv{JTqBc#Srk+ag,iD32% t@x%2!(<P̱qsQ( B{qA8jƨ)H7ÎhdZQ@4d+MOC3tY5H$(MkĽ* *#mTVQɈiҵ 6FCi\VكO v5\  zi +^s<7 9pk]4AZȨ(ڂpb&Kx j *JU]=1rCYE[vE2D+Iq`ݵ 2Z8QbQ?3jEQL6XTEϸR,q7vfn ?ͶU-UV -0ܣLU];eX' D* EE^wqpE $,5c%4_a~f-WF,~֪  :~R)JAYl7S%C^0APq̮S2$%j(Rα h?h5,Ֆ*y"IRH r}\@A R4eeP7+a@kk{XMfŒpAw͞; Y" ,ng٪ȠM(襰/ >"Z"9PO%9ymZ mA?:tjXpeo2JptjkGQ LMȰŔW r9?qZA^8% ­3CIUܧuO, S(PE~eD0rj#؈̩0DXN+Y 5{ΰ@Ҹ(/. qWTfބ] B5͋5cP `Z S9KHZJIt*[AP+9 )L 52/ ??HBrAŗV`M 6A5f(HJ[3YBKcĠUQ?5(KZOr1 iOB1c"ЇX+j{z)Ѱle|  Vyհ"bIDZNgktYn3n[H~2f9"L&(TkVψn/NiTPL&%]Ki@/h@ ݅~ {,*Ŗbf*ypluL )Q۶εR>#~+q]& Y%vm@C' D̴H k_QSkD `?x^r|l1j' 7@yEMff2%kl#H"WZ)`4{H+ABM آQ-W* &i ]KטX!쪬5t*[)TɅ, %Î$EjMu z`u-yW1-e:1{FVŦjcBXA),p 5@ƃr"1L 5bLBꋫ>+m+V{ѐ&ARv$mlv1Qуw]0$O+Pnp6ד.…F؈hyJYMU(hv SWW^5,3~`&<# &/k%\&qbC n} (FBնUnj3L\AQfUt K\L%gWy[9V&} BuqVs:yeyv:7ߢ dn7 a 9q.imP &78kq#Xs[t9rd{+@ZƜoVvH\.6wD$mYWTmWE_&ļ>y(EI`q`y/Xvq)Jo@W7rP&{7MdR^PXdI= ͫEd2äMP>|2A  h f{lB W))rƳf * 7eǩ[v_wz?mt=KB<:Mb]X$V 0i_ FLfGb!AXZUb TFQjg sP+E do5`B)my[Z@.alujsXTY$`6Y8)9ae[nX+ha0;$)^.%v p֍jЁxMũ ߹Cp!0B[A㏈̥:9bJFR<'4Ÿ^1CPt*ψY؁4jaj^"쪦mpXcXP TGh ,Zkt?^ Aq5IJS?7eK+w6@AajA rXv&KLm4SDb1PQ5rx_ԥ[nr9{g X;u M8*Fl`ZywşF6yӍiS-6[ϳ=WskRUT=UɋYd@mêqwC`S\o* Ju +*`Τk3E8Z3pa$lY@;>cC3K2Qq5XC(1VQ\8ݴZrrsZ #fXCU(#2U0Q8pla0aLUK?EʀF4 t1m{+!nR(@U@s*%hdSmceJ.ôH*㠄lUuZS^Bg~bW!!m0WrH/[#"] [h4RzfU%q4XtcY0Ab|s 1V`P f9ab CCB7_o F\'C A:fXYf`V4H) 3Q_sX@!DYH 43ti$D|JoۢlA3@$f4n1YbG56wtrҡyvЦ*ZKKQ6EH\4‡=ӝt)U)M  ͤRJ0*k:M#_h0+Es 7D7W_T22;Pϋ?p?c,ţy l-l: h!07SK@kcʴ/sIaS<+4sbU΢0s,H~ aX^B%D`j4A1\Ƚ08V*1Xm_QWô2/U\\Q6 C3 Jچkq&`sx,!PRJT2lmF:LK8Y_׹3%IՋjU`&`eKh BcU?Fl6-Ea&gxG:[*k,;RT 2U-֖Uűiv(>icL}29?0DޱKV7 z$Tiʥ[+:W*ܶ`ʫZ։@̩nRBF)U{"a,Kf.Vy5?8eUs {jp.u91 Rwd `%X83DJgP*](ʦ;q7MCx[b# 9_rJJYCt"P Q103AQQb9Fnn3ע @ B1!SE{z"S v X)%mVs,60\9Rd0D%Q @+eM=C8ݼSY[E.)AAw(-5& Cee|w m\g.%خ\b"O\PQ]^x%a[԰K7@/L֩!uEt98aW*ւR&T5 TE{;-H-^A~.+S`.P07ruuMF0d\tKxusy16s cw2a\ /zslˡH"䅫żW'%߉X. T' @ 1P3hlJ1- cPI:TodvGA١cuC˖L FxJ23 ]ХF7ձlQ ezrڮcI {}l uwn̫l )˲谺.;"PZm9JEû)dtPq>EWM SF`lp}Iq"C`hsx2ϼ27?O;lAi9yԿQEn]gc"a=h ,t}݌ &&R,E`[:XD=XBe,6ZQN <d?,`kXRo :% g)B`R Ebw*%nʯ(Q*0.,? >w&ªS]Q m V7Ԫ(,"j~kgZdLT_L1y33/qC;T?!-Z rz#n?N+n,8/Qfb22\F1*(adҹ\Q)~uhxGR|DOS&ۨ2֫ć`Mt`S(6"PXXVy= {@)5* ЛY /8F6,UnNX a.cIM7%tk$o|A{|^* uNBjƑ"DY CWo vB3! $Wm`}BmyĻ&A)H)Du84i3@1uX"oY>e3g vr⛻`(vXC|4 ˆ¡qF@jPfTu(Sv)J_nkIeRfq|R>#N_T2%(YPF\,㏣F%MgrnA6L35p娷*Fž(̓Ta@gfP]P"V ĵ% O x-?v!*, [X1!cz,.'%c2n1{,{@tVNqQ0}!@?Afm+T6i㧈 }FUo5mk`k_lڻJN8 EQp> -h ܡn@Q)"H$`¥P_:e@ șQ;]KKu,E PR V BSaT; jV o wjf fgR4 XƸAXh/ a/zjq/rKF*T)b/8/5]cp*~*P; Ȏ-Q\u2$dzUP3. Qk(ZpDބ4I 26lpа589@#(guZ ܴ(F%,Yȫz"(0- 獑B>%t8>4bb'N73Ye }A ct\]Eq)hepဆ Q.JFSʨ*.55(7\Da GE4ceD):e_gxPZ$ۦl4xY*h ǘ3NDlky9 8nT \G.YA~gL{ 鉶9d5OR? l>=yDざ%Q.o%޿@g}ؑai)ж2v֡0>$elH#u $dWJ/ TK77Tg'pD#PdHڴC\ʬ⥅*YXgO!ωrvV6+*)]f_A[X9 (cݟurQ!(PĢLDoҥx@r~`_S WJJkn\kX li >YM O_dPŨP_13O"Pd3 `g;4g0];-Ip.BVL=BP FBڍs0L&RRwWF%ߙG&\kĬu pk#tZ-rcW1 /̤*È u-TVVmZ# \Jx1pT ^z c.LEʀg̪r + =4♦JAklTo dʪ =f._fgQu;+Uu?@V3uk2s,=4_U"ů|>qsqze}:x-ˁ@+0{B4Bl*N.oU}mLRl-b |@0CPJ&o0?/ =..e0MVIu9%uqdOPZ?˼s-u{f/`ۿR'W7Z9̾s3ܩu\~-3s0}Ϩ­sX|$92ًO^F:H!ds,e?JMYS /*MrUccdw~%;aNf`U bEqj3WPoR r5w0.o7/\z5yI̱)hkos3|׽~ị((|F-nM2jXsN"-)V-r5_Qhȿ[r?<tܽDc,_2̼f ՃGGq/ 83\W.ZquĶ3^0͒ÞqP?b%[Quy(w,=JKZTvDws."wiM#)r؀ VQ<h0E^ ]K6/y?"A.Ye{Tn%- t=>f% +gs̼ }.i5/9kKGf;}gW mNMᚦQpd[ [VSrn1IJϹ[ȋ@Z&Wpձ:Je!!<'yM<0l'Vl̼2  ju Wu[5%MmCPti¾w`iT/D>~G_޲\Q(yyg59;Q[ObfNQ[7W}ũlSQlq~""Kx\I>#5c A_@d`pܱ_LyЪ](z"ÀǺy4{i?&&s,nh063%ubGY_  ԾiZýS V\B%ƪ-n NuRe#%ì30j%j6{,wCMr,/,RyVO%u _wP*uf1RWKyLn~!V@W#jL? o=nU_K=m۹e`\;-K<"(bsWҒYtܻ6)(-M&>݀q20sWkhy",qL"16t4ULD LPhs%R=W(j[[cE]F ]Q0!ex`R=T`m\\ۈb`3ϔqv*N2]>i^s/It|B)+\M߸y,-;Կ8^>qe|@%btVFABdarjX7 8tJIb"w\m5 f9b.Ũy8R~!qTчi:U>Oe-B/՞|$V}V5%Tuak@QX @ƅ9Q3S cEu/N?iv}r/9+ϛ8oP,-q̽sΜ/Q#aDY>ȎpCūt 5/6S^P6 W,|[qxe}D(ײѾE=l[DV`悏x"Ц__f, gbg:h0 H2]4ewN[&f)NYcb g``F7dSERQq[:Nv4AvʒZ Z{i$GT.&ݬLlĮG7$[ᎂ12<3!"/zxʯ7sХT?/f3}]Ц< 7wH&o iB_ Oπ<~/}??DO;6}_ܟ [)WRD^/Ly>iF+y #?Y stYFEݥT&@0pZ9{N C[!$.‘T92`nՠ^00jX|K=1 +ӾD;*./;AW\+.@,#EA$Fl IxrFت u9}Fg#.* n:B,zR P"gb5k {/ K]Sr#<(~E1`:iJqxB:+a q^ 2!31x#©Di-j`WOEN*U59UM+8,M&? 5t ^v-w鯏RJ^ "";QkQ+ġC!pBE<f^Yʌ>"hy#48_ARV&!3*Eh!A3*CRX*[?5S2}N^ >gE2}L.D  j۱]% ~#K. Ĺ& -} RGz 87 !Kr9Gh7"QA`8 p T2D[iS8CIA%EzheH8 19Fk|NX2XzXodSjW>/įQOX=[-zhohhU TYb5r~"x)SM+ĩG8=@(3iD Uͣ.JrRfX\XGHɌLy 3b0BbPv)[XIXJIcѢL+}n_M b"V(ϊ|D# G@C똙!x^%$lvTɨD]%Cu&r&i@S]Z *C~#m"67bV~߈ ųS 15ZBjk6FH)Գ+qگ0֨PxLrSd/19K,bv׸Zbn0Ѧ1)o IW (ELDc  *GT\ƺ-6 r; ((SN6f'u* TĉDw _J}DEJY@,EgdNKƀ2pbc[}IT f\C B9aeX6u*+gU*[BÅЭ{@) ˜ao킣w@Aיl Բ,4!|&!cEo7 5{_kjJJYaxZ|)n\Z§2O)K60RXeI:x8ZՔgN*tŭa/͗lPَH(0N]of/F1Fm#i A`ex7P/c!U|y[TxCS&n J*Py~9=a|{]^pRdFW SJн4r!*X-4¡6^ႆqRR D"%F0E҄(Aq|z\" j櫼٫kEE%Z1zZ\)qY2 =mE8*²*_fAQS%.e{lxiSXC2uaX6$Ȭ< i `m%~#؂G.i&*&zA0 /.`K/.ވz!]uR bT QdFG8NrJB eV*/ jU mKMֱ!.krȅR0(C>"`3OV}Kj5x;C*_B VvobMLգʹ 5ØyX @aʲdfқ4&*;+h#`R~dlU"9+; DT vCm3jAXc@(.Ta0;aX(W/:g팝pL.j`C2"!pvTA3Mqh)и6Ts@lQ"Wb}ap3Am 0ƂUr\qD4 QNZjUsdO}OEB}`rĨl!0+m {%ZX q>Qj\0WVf/1PCcmTʌ4`v3%E % % 9HPӢ1FKʛM5ļkzh2!Q;|ԨQ㿢G@֙bqgfQi}%P7g3lİJfojRYJuүV x7hMs -;{ )K}N%Z7XXwr†|K,|,f F0<.Df jaL}]Dp%8jvfnZ)})S1^ \xG)>QSr P\q{(yCmXj+oڽck%~w7~6՟_M36? }$? ~5MM!i?gsOwΟG_ɤ?57M endstream endobj 22 0 obj <> stream x\Koc W0 $ϢtNNM~N<%I!QO4oavڼyަ_=)Oay^M~39n_2/v{}::=/_?|cr99ct~qX>%v 3_OG<8&bl\F{Z|mBaA[b '4q5vm}׃|q1L8/aB>rD?_N+wOk)bG~S,}U|5_.Oikr,3/h/yOCMnZ<]+8u?5e&'Ecʿ44 E'Ί37e,5iBqH ȇ 7u6?YE-+BcBےElm׌.+ϊAy!r`Zv{k}4wj&$2ԸFEW{^`<0Uʎ? -;[]-]Be-ٰ=$ÚX1C\ HCyVv«5m.yFTK5ע.kϺ'lkr!&HCvL38&j1OFU[ M!ش8+̘nOɫzP6Yqinv1rӈ%UNJh F/Z֬8k\eˏ ;.ndT>k|li8x,ՠceeۺy’qȓNy?-!̱R+[H{SY lalo L̓aڊʛc&Ub1| :elYI6XؑamRyŬ$L5vَ-cb|]֘LKbk4%3}ooj Ͼ2 '[z9uاc^L{:#8ڛrKw"ڰ!ۉ/lWaYҀc>yT: e< ZVl2zKf۸:HlEVû歄B#bxKdX_6 5F=H";BI[p*Cce)rFkWrU6͹#Qa #:!x%Cq55M~T"L:Aid%;lV#h"~Qr&"4G p7Jsټ\kUTK?Q\0j6 >Pݚ׊/@:B8Ypb S.&Ɍ{fNU a3PG_S`$9,C&eM#z&hmDCoLjMGdϋ j:me{Zml?ϛk;\y[rv#`@U:/On]m#h$ika' %2i0B&ZKw' Ȥq 8 y?,M"j7j2ؤD @K m-[mHhD`mE" 8D @ $Bm+q1* ݨvH%(TemH(43h[D*)$R a0&Jq DY@@F-{dH!0HD @ a0vHaR" 0m;$R0N!yJvڍZ*c)a'U1B)B0mZ )d> U0N]WX4E@F-M ؄D*@jVcB" X-0mZ dj1hժdO DMtSN0I# budLeՃOJƙ*T88w:px"SA >#Nq">r:zcUu33/yOɣH,Ձ YXȖT&]Py"a? gB@-3O.PDR,fu28tpj`$2=~'T5>HmT:H=| *,cyx]a|i6SxVӽkWGIHIK $-s{rn:q+%*Cx.ܾݣeƅ%OMu< 2mi,kw/zϠ׷qePawSFo+^Hӊ]Iּ|tf;59Yge8mT&9q kN{Tl_F׶\h SrT Tмyy5Mz>QKz&ޣBLP;kLJﲢ2*by({!_xivP*[[MX\*uf("JBă,v+#blvĖGs t fln%^8E(I`xKG(PpFaTO:c5{7`aUi2? uf:YIL+2~ 5gI3HU |øњO:9-h$~xZjk`l"2Ua3[vvvhnIm`ԏ?%W%@a`Uʛ #8[XE.3 {=pjO}BFy>j> stream x[Ko Wyv- 0! 䖝9^o=ɏZW=a_uZ%n0^֫- &倲FuL^dOBI+?i-,Y8'? dexkPxY8riFTJk]!ែd#QOlו1*# [`tBXS|tF¹J`Ö幎MuȗV`.#Wl h'/%yE{uЙO\YGi%"˷*l!V51[W҈1= h_aZcS_(Ɏ zqt\v(P6ҍta]Uj Vúi&Fba!(討[seKaHYѦLk,yvk:?U|>>B0ml7]K dAJnX$7/_IEHUU)cwW6՗s׷J6] ͛sU~=FezcsؒY@&tBh&mwG7 W &'Oh;}_>kLXGYa])z C#䧰HmZX/K ,Hw$Ӟmef^ ,۲SR)AWOq3c_wKLa)f$^H^,˴(ܦu }1 ϒU 9^4M>8&t*}$Ka m2^GMh$ZqڡwS 2ޟ^AւCS +i7}ѡosrE%% G-?)[YTM,_}BK_]hYC𴭮v->J`- ʀҚzb^{Bd(|aV6^gAR?sm+nGRl'R&eyhC^ >8hآgH@~-Y©砤Q#=D-'G֌'q\ !_o@("&= 8h6Ƣaܤ/H}zWBI<NְcߎRQŦIz';QEftkh/ԣ^+HJ9<ՐTB~ ܎CΣH]f(7@"c NK;s (ck麼 <z :p!w,Rx:.1fd5P\T(5i{_[EnVm\\R Va4. ܲ4slFqۺf69amYV1+7jD2pmY;~AkkR[oE*!KF)a[x",S\2>Pf;* 1]K"<%ݹq9,8g7Ea< |J,v alHl%L" @ΆۇA+j[; Ivvx^%f]K7~݊V, .ɡgC5FJ bp0(!XXjm>ZLŠqhgBU qm> Mw(=1+$niZlP=[ H"(CiT;M[A?FvXѹI4!rRAHmAd8 4 pMEl4F.",xPS,ڑIMNNk&8 nw S[ YlnƉIFC6>/duȸQil׭?,j by>}#_.Ҙ.X OĦ$Rth(}YeLgjLP<̡죒Hu:k{(eju >8f't&Jh!m{ORpo6d|n4:tބဓ{K$tWyKBoQe[^[0hh +(neL3p`X0M^š^oźOD<㖁y3~ +?r iٯxrZr㐡eXOiƇrn̕AGQ8K!8 z vu1JudT6Z}Y[i]o(B`Q_x|/0>(C46lkE uAl7'xl`ģT^wT1)MeF/FUO2)CAܕvf 3#`g̳㇇URZYYD8_,pVL##gc/- ϵ؟_?h*829.2ʏzďz29,Qn#IK'2zZ$ <-tfh%-i68 &kAjCh&o$Go˗]Υܞ~)1*,# #1 n8}ft(/3:s4PpbM@5xS X0lX;hpI8,elJ ËOQ]c-K;U^,lhGw (~d5 P ^\r̉?=v=pFB;2:*{CY~ɉxKG ĻK0#u_-3l@k9z'<@`34SEA}Ag0{ ތd0nKFw <F .e}6v[ࢡGDW}" 2_OGg:2P^QYǷ N6F[f^g"LhB8?1߶ζ54; s!UỲ"̮iSGRYobq= 9MCeq0K=㏊wulܑP-9Ʒ{>0ӕ6 GZWPjRr&(u^:w+2 ;%d2-|ƷH0jܡn3_o_t}_S^XQ8QVqkew; Hh6EJpfT':MYou{0 WۇQύ_lj auGi[BC/3cAq^vFW@BOjE endstream endobj 26 0 obj 3646 endobj 28 0 obj <> stream xZKo#7 W̹Hbho -v/H4bQב(>?RNߦy7EOUeNjyrLvQ8i/?LXʄIVY$Y*}HMSiY-_Բ6[5zùg5?zOH?k8z6HIy ﺞmZ- P EBu e"4^3*Os25_g5ݱ0jQ]ݎT1Iz+B/flDh`"^ 60sU>dj1⢲5se9q+鑙':E4_:&u&@k\G N3řdU(')Q5זNC_MʒousrP)Êieڿ|}v"c/á_1٪RB+f< ¦&(a|& &qM7#0EfVL{)PF4,hS֦N>a-e!1?4E3f,eUr.B/Rdt3Z ;T,Km.<_<3Doj\da%n@$*G_KXڍЮSᮚQȵ$B;$ίu5,Kwc=D re&t2V0va^Aqf)¨4^.dXX{"Cec#!+^MYYҗ8ȈRfXn33{%ظ-ybSms+iHmOm,YЊ S.h("#RY 7|犙UzlNj`oQ4d(oBfh+ pBeM[0:3iofьJjoDth3H7a)+.,/l&[6Y|$kyb]XQbn5hgs%q|(A=}UT K5*qav^ˑYLEv|Es`yGo*=vDh#Mm,/^ U' $<صrd˝ TTwɶHDGlKx$NȀ a6졋yX6v[U j*FxP䗉Xty (wx .͵>\?-ʯesY +~b`A>=>+h~K`ԑ}ZBzX)s,3saN!wG@ڻ)&6FqgrD-Pc|d B?@,;C(Q[P0}\޴q|-W [\c@}YGAQw1|8 uRт=gVWv}GO율PNuV)513dh5|ʥ2Cyȷ2,or{u!q^PA')Q-G)\qZ!- ]P{CR[0%\aGF/td䞧ŏ.!EmGܭ65;ɵz Yy6*!~ p}=}2)? endstream endobj 29 0 obj 1967 endobj 31 0 obj <> stream xXK7ϯ0cnvg|9%qNY_#UIZ^[w}P NJ+1jvpկN_?'P׿'?]?r3\tH};7ȝXPvn[>W3]+\^)_ᾞ]&{Yϑ#|AtGtnzb3"j iIߐ"th̶H34ʋbM 2LbK$&xO2 dI'tE%ѡs(LI% F=V8|[(\z¸IA 㟏ϧe)D<0[D+9ܙT|$3#}lk9]lk\nKNt GӝfEӔX"`)3la1`WPDDf"aMFyBfFtfR(Odu,L,bDU6KJ{n 2NBlH՘=4 2 / l-A L^*"6jBURdJ6e<&K9C!l=ȚHfNDNxcDnвL^QRnI>|Jlqhlx+TW`v=8Ch Dr&˶NLX&<=Od.=gz^)(b -TH?qCVuS^)'0dEuSSYΉ B,DGm ۱ᰚ er[z]%n8~Rr#,ettJ.}@9x!ս16 %86=bc&Aq^D8n[ 'So\}ZQo&]W^1NN*1|-]'@ʖ->MM/6F9g<,˜C(=Tf]g(L&*Sx&,5y>Ԑ\r*hq6yxW٨WA$ UƈCKػ8̒@7 [7Q-:Uasu7FO1ňWdycA5٩/;R}y$qy:;(o9d ķ -+.cr;~K[s[n15WCEMDTk*Fz| "^;h_ԫ߼zկfx6ck}9wj݉( endstream endobj 32 0 obj 1275 endobj 34 0 obj <> stream xM,7޿΁䏲ah虞 ݁=͆撿Ye^w^˲$[2g82OStp9'=p''ᜦ7 Mf7O?Ox  k&\?^Iʏq~pvD5DCUq(lbc^+z6ъѻ ` Њэ^vf!g,bhDZ~^fW>_ >~nUt  GD1U+ 艗`=^ ?@, GFL?xT2ɐ¶6 Z-5 x6\ َve2%4:, ,Y<9D%g>qpHV!a3RI,-C_5 L& cA]?+KLaۍLlM(kܗ3J5H1a5Bca@f9 )R4tؐ2R_ 8g57b >xfLB?W<̥Xv5e+a%[FETBQYaok\f4`}yTLgUG(E #~mb, G%Z!ʂBhKOCx{;[%J-Zd$]Y*_/H9k($.JIS*U2h ]#Bi0*bߪz92èZjvB*ԅ_=ah A"f*"߫hnřV$!I4TM=UgE7o[6f:jvP 6:fQDA%)G䴋+>텭٪"'uFo_d1Rw/raN=;]mO}H~ Saw܎G UdVg8)aE?؊=Y ZTyH;(*\ *G5bHYځ{GrqFzVUcC/,}3L%=o.z76,Vk&-vj#vӼUQRJJ((ZpoA^UrݫGjY*= M㵫mj-t8ĵ1Ȅ~\CfjR;FwV>e Kyf+, *lQL0U;H'wKq7m6VZLUz:'<HaZR@ƓwDR$D"MO0[BXf2C}a;frirDୢbU6YA:9tgm<ۇF]f6+Cyv*5t½'G`њl:b ZZ)5:`]Tn_ ۥEȐaaGaaa *K\r{;B9z$ņU&teLHҠhϩm.ci)DH:X-q_j郉DI{hd҉8;?iIb@e@5D0.2ͷB3%$L-F1r<"f.Mgɲgη`B܀zCw2K!˥#v?m!_'> stream x\Mo$ WyvJ_ c!rL`'@UI{N%IQ*jGwÿOiʿ-=zowi~\Nz{N_SvKa^NK_OOn]߿{riz/ 9۲]zX>%wI;}tt9d=PX>K'O4{C Zw5wHz)>3&ҦKhEv9goI,OUF5ǣK~nSlm1zu_.O*r,,h.yßbv7Iy n ezwGcǦ}ytOgUe,7hq+ȇ!W7Y-RBeI"VY[ajUfE%4;E[lK웎%!$+ԅFlzmJ_? 4/u}kVw\кSʼnY-Zd_ C ezF|*Tm]цd4Tf'hwI  ZJ%Vwp{{ÈU^SΧbRd4KSN[3oh\Q0Dڬ*CK:{t ;?dʪԍ@Qt4WL9iԒoԼMgRnG2g^Kq%)`#9~̿Ic:\DJ/F:"3s$.bH:V6B-&n KQ&n\R޺)<9Cix&4(JL="x<HV<5ݬ}0goQ127LD/M8A= O֡cy4h<"dKw (ԯ|^JSmI#,S &|S! (Y+r\#}qZq.A A;6Z #F5$^u^T_7Z$P(\Qejc|UpBj^zt\fWaCh.(t tBfq-u|*49&4ͺŁ0$P~:"0xSV#8[):Q*rsENMpnqz51^F4!(ς WS󵝮T§CD| +z[tH;5'>ȆiƷo@YK⤛6C_&=O9?O⎡W&}R9I0M4[s~Z/iοyJ49\׽%6/m|y&7mbYX!2`9b9?@̧_:֍5yy9#"C+BLAA1*#H2@͵{P yV1)l$\Ea0]>bgSbDhݨ+s@% ZċImU$w$^^ lIGX AgHeK& d}l4*0 E`!vU+1 S`̀`uB_ks6&~•jPHsg4/4…Db0FÅ+ՀThAާpJ5|ژuz\Xcy^7y zyvKI\\n|X}WXG.ɯsqk/_RZp}|, ʏO}W7gAn?c/[O~3|U}#_Kȷʊ>:Rg&"+j>CEc:bXj+{z q66* k΋Տwi`^zlAz=3't5^ƽ[/FS}o^çG)A?2@TZMXD')[[(t!׸]jieP)UEZ*le(xh'0e` ɻ{ QMRUVJ-d7&lS3t.y=uO;&R`+!Z?za=髢Xb9=AOb| CX>}Jss#O>W1ؼrZ9X7 1( T(,~1mCPoUd$[)A+Nem ; i7o~f6lxGHRQu dP"­eёYֆݞ8+Fɲr(/lz]qL)6bOZ&x}1 nV)@exi^'JH9w٣iC8yϜսLA|9iQIu &~PO+%[XEEwP] JfC~ s /1,x̐ǚRq}V7$^OQ^h.Z}Ga%jn*Y.a-ۑ5!`p낤]>] A `^]yQ}orǫͶra>RXú k,ׅ5nxR$CWc풲:4/Ad6֥Y,Cͫoxt/_& endstream endobj 38 0 obj 3646 endobj 40 0 obj <> stream x[Ko8Wy$vz=,s?2%eFĖ(⛴y?^1~s|u)o/{Y8cɾΗM1-%HҲ1E0/J7c3`F3 h>&߾`q֦7!IOovpF37֙ayHaLDhB::iV"z4.񆶺5|̸\Ûy_ҏ:Mososq#-U=. \Ǚ6iKo\}^v5.Shaֿ3_{gn]A%Ûf^3/zpWeeұn͊Du򚧄+۩·@f?^'GP_n c! 0`4BB=fDKHP$:|~۴ޭ\tiV2q m>dK!hx/]gMth|˅tG0?J{Go!C[8{SŌo{_X#Ң }i% LyaP$&L6 rrVbJۡ@.XF*Pէo5PK8MG"Y4"3: o͕ ^"H@ e.ƺ"2ծ@|zd㛳;ZdPM(vFl}H H 9a>8%7/_H^UڑMαī+y8W[!:Юjޙ͹BEbzE-GȠ\Y6or[V> [I]AWVU*acpGLj"RM(JG=c b#ldiO6)[i~;_P h[Jii5bK\gct3e~6T^01>mx2(֓C{d@_塼~Z+\^^+?E['Q/e'N@&v+cX A೦A7V͖G>ڠb4|:ldšHSk {FnMP{j6͢jM: ]ecd>gayUK6RwX89"v Jn; Hp!:Ff9Xu@6;8ϯ~+O2!w< rl{l7{TOKX(ܱ"h=FZRuEk$ Ni{^|:tލcxL:??ϩTIًr6QD$׍LN[^AZM+,_}dBs_\hYBu[YjPE>`="@r"ap/!r0.7 hʣМ#|`n}PL%OLNdzh^ >$XX@~mcAIF#TM]%Gq\ !_k@8b.=*:8X1Ƣad/h}vzWGBfK7j'6 ^**GLQljj'pkd&ALRF=ʵ+ۉD@Q 9MUJ 0:>ơdy}[*66d\k$?] 6/>ҊY?]W$U}\MK\*5^o[8]8S5 `MyZDޖ5T6"7W6EJ+Rm0G`Oen{\_>؇Ûu0mrt~=^ pߡW5IK5h=uD:d:r(M'Oh<6|!𽇒>^1^)u!<À!9 Iq=,h}(˪-1ۂ5-|l-Bzӳ)47:'\& d,8-% Πb*qhn E(Rá W%ThW7AܑN MZV^qJGWC Phŵ)ǜ`þskg,@kL?|}ur*lb$pRѺ/ݖf1 k8r'>\@`*Ccy$Xf`7Ȁ׏øh<0PJvgop|X9T?Vp$KTP(|y_v/÷: =6|ɲqF/,_Kb6_Ğ Mh6Enٶ_"}ppXMY\fӴ3کZzʬ71錸H_℄L8T8(KݝFweCTmܡPy uFǟO!\iu3Iukzn0xxWritǑ;9frFVvʍq}( 6ZwhHC;ܤ -F>Y >췪.n-̴nJJ 䂷Tb)*E)/:Z4ϽpP%@G딗b24Z_cAsVvW@dFOͅF endstream endobj 41 0 obj 3640 endobj 43 0 obj <> stream xZK6 W\ Ym3 ӶؽW"%eOb4cS?QR拚9}iN\t=-V]:)K2E]dpIlަY*}+Н% /`xߧOoI޿U?ͷ٪93g;| OM#䏸J< ~z6EHIyЬﺞmZ- H y׍7Peʌ`GLt>wASD{q]Ԅw^SS ە) 6$äzRs6 ' hr1le4H׫B <9*?>j125ke 9i+ّ '9E2`_:=Msק6 sp4H;SN6%Byys;niWsre)R9\`aCGȲM>>v"?'zATL9+[5U~Ŋ߂=5Jؤ&BR9-As!Je(jR\-V6ecUHD֔!$)vȪrފk/%(-t*IS1QWˡf-ŝ˩%x*h PC]2U='U0(JJٚJj uP}Ba%X2H1(Fa<ԋ)U.l:/FT,F].@ OZbj ŴCL.b2+8i@L<}Z ɇњ Z.hNls}r=/i\錭ە޲zwcy552հR @*g_),gF~hיpgq-*z] ϥܱ!I2ksn]3pfRPB&2Yl@ƞ^ C"&C4߅ kZ$;v{aoBL_ 3:({C`G($fa|4$@G૶#H-|qshŒ).x("%ɇ ̘*]tvNX(r!u,#!uQܨB5iEoc[d6maE&Mu_%nbDQ@R- }MfS0fM~dL. :L w{*CHY_Oa$Z9@^/klѮIˎfDyLХPS SF$e}[ݕY"z؜LlԵH~Wzb}Q7cn;5hMg{dp[6z}n*3hwc,Cݵ];Sm$D3Vڻ,P2>-l/ $<8i˝ e*\1BK,ӑȎXp $\ӨO`64졣y\6v[M jXFPrD,gI\t[bBhA-D,܊*|k`AB=^-hO`̑w⎠z!UBܦzX)sx5SOՋB|wK5i-d!?k\͍Y׃'5P'%c^1&nS%31:9Dz3P\Z]+G6[+Ah,0x<պ}mZuK%ћ,vBB{]s4`*F4oݣDNH(:ؕZ뇘at^\ 2UM(dFMp<4$M ! Ty$#FldJ _tZl-O"3ouEEȿg|g wHfӏ_OY׿;$ endstream endobj 44 0 obj 1967 endobj 46 0 obj <> stream xXK7ϯ0cnvg|9%qNY_#UIZ^[w}P NJ+1jvpկN_?'P׿'?]?r3\tH};7ȝXPvn[>W3]+\^)_ᾞ]&{Yϑ#|AtGtnzb3"j iIߐ"th̶H34ʋbM 2LbK$&xO2 dI'tE%ѡs(LI% F=V8|[(\z¸IA 㟏ϧe)D<0[D+9ܙT|$3#}lk9]lk\nKNt GӝfEӔX"`)3la1`WPDDf"aMFyBfFtfR(Odu,L,bDU6KJ{n 2NBlH՘=4 2 / l-A L^*"6jBURdJ6e<&K9C!l=ȚHfNDNxcDnвL^QRnI>|Jlqhlx+TW`v=8Ch Dr&˶NLX&<=Od.=gz^)(b -TH?qCVuS^)'0dEuSSYΉ B,DGm ۱ᰚ er[z]%n8~Rr#,ettJ.}@9x!ս16 %86=bc&Aq^D8n[ 'So\}ZQo&]W^1NN*1|-]'@ʖ->MM/6F9g<,˜C(=Tf]g(L&*Sx&,5y>Ԑ\r*hq6yxW٨WA$ UƈCKػ8̒@7 [7Q-:Uasu7FO1ňWdycA5٩/;R}y$qy:;(o9d ķ -+.cr;~K[s[n15WCEMDTk*Fz| "^;h_ԫ߼zկfx6ck}9wj݉( endstream endobj 47 0 obj 1275 endobj 49 0 obj <> stream xZO+ S9F#`a`hniMx"Ex`5H?"q'~?|[Ltw0})M3)O o?M?'os!,Ì 4e'_RsYVHnRH?ߟ8t|>“K9'3?Ï+HG[<X.fz+}Lof}{LF+ 2$#8LPJM"Bp?D̬g9H,7A1Hy OGp'τ| q(~>ϰPt~M.y-lt>jDb]z]q2͔6Y;_-&}ןU<T`* z=Vxxk֯bq> p)Adϵ@TQ&O6@"Aެ<kZSFCV%m^2)K;u-\rnV yܕ,Dl4i>ڱUߋ ``uG,(6ciQ(ɒY$ yY4kZF>Wو h²!a kYaO55"1nDLV?&8=oűخ}b3)ub̗zݰ2Ը!e`^l1GHzͲougܗ{ۚ+u*bT`և׼nض71(9 wCK>*-vsfwE4}DckŬf]Wrgmv =I5Y3Wzi;]᳝Z S%|Ay *j! _/ZLo ۄ379.an:Q+-^w "x{6>wtd:n`]o˚U<kH+1e/}Gm_]HkK͹0 &k'j)7Yͧ[4D߰Yv\ձT`oV3Yc9lO{?\ UNDRq7m AtI;%~]NӥJk[iE]IŎeR [#;PܤCzYkWכ#TK.ּR`ntl>ٱe[KeMNSzgz-nKH5m]z_I`RmZ h::Z.q lfC_B _tp/B~c9 {څʘ#rVnSEVqb(zy: c|gWKrfv] ew{F:tQٷ?JLS,E+ l ]ݘz6ZO)*ԻqpД6hQ?L'ؗ|k5)M>iy6n?E4-OuV֛^?9 1onBK:[]xW#X?֗Wwiשׯ:ǩ՘Jnba@ٛO'v;%y֟ҋ BB y<<ٗ>O #!,>O2u29MִQHF7Fͣ)S<[jݠ5MVl`X-zr ?VҪw|;uGVZJkvwFcFôwôwFôwôwFwGh?nM..1\!KuVE} 8JO 4㨒r({>-Ѝ#!<9$Ӎ#GCKŅn | `qY>N)Hg[ @)7> stream x\Koc W0 $ϢtNNM~N<%I!QO4oavڼyަ_=)Oay^M~39n_2/v{}::=/_?|cr99ct~qX>%v 3_OG<8&bl\F{Z|mBaA[b '4q5vm}׃|q1L8/aB>rD?_N+wOk)bG~S,}U|5_.Oikr,3/h/yOCMnZ<]+8u?5e&'Ecʿ44 E'Ί37e,5iBqH ȇ 7u6?YE-+BcBےElm׌.+ϊAy!r`Zv{k}4wj&$2ԸFEW{^`<0Uʎ? -;[]-]Be-ٰ=$ÚX1C\ HCyVv«5m.yFTK5ע.kϺ'lkr!&HCvL38&j1OFU[ M!ش8+̘nOɫzP6Yqinv1rӈ%UNJh F/Z֬8k\eˏ ;.ndT>k|li8x,ՠceeۺy’qȓNy?-!̱R+[H{SY lalo L̓aڊʛc&Ub1| :elYI6XؑamRyŬ$L5vَ-cb|]֘LKbk4%3}ooj Ͼ2 '[z9uاc^L{:#8ڛrKw"ڰ!ۉ/lWaYҀc>yT: e< ZVl2zKf۸:HlEVû歄B#bxKdX_6 5F=H";BI[p*Cce)rFkWrU6͹#Qa #:!x%Cq55M~T"L:Aid%;lV#h"~Qr&"4G p7Jsټ\kUTK?Q\0j6 >Pݚ׊/@:B8Ypb S.&Ɍ{fNU a3PG_S`$9,C&eM#z&hmDCoLjMGdϋ j:me{Zml?ϛk;\y[rv#`@U:/On]m#h$ika' %2i0B&ZKw' Ȥq 8 y?,M"j7j2ؤD @K m-[mHhD`mE" 8D @ $Bm+q1* ݨvH%(TemH(43h[D*)$R a0&Jq DY@@F-{dH!0HD @ a0vHaR" 0m;$R0N!yJvڍZ*c)a'U1B)B0mZ )d> U0N]WX4E@F-M ؄D*@jVcB" X-0mZ dj1hժdO DMtSN0I# budLeՃOJƙ*T88w:px"SA >#Nq">r:zcUu33/yOɣH,Ձ YXȖT&]Py"a? gB@-3O.PDR,fu28tpj`$2=~'T5>HmT:H=| *,cyx]a|i6SxVӽkWGIHIK $-s{rn:q+%*Cx.ܾݣeƅ%OMu< 2mi,kw/zϠ׷qePawSFo+^Hӊ]Iּ|tf;59Yge8mT&9q kN{Tl_F׶\h SrT Tмyy5Mz>QKz&ޣBLP;kLJﲢ2*by({!_xivP*[[MX\*uf("JBă,v+#blvĖGs t fln%^8E(I`xKG(PpFaTO:c5{7`aUi2? uf:YIL+2~ 5gI3HU |øњO:9-h$~xZjk`l"2Ua3[vvvhnIm`ԏ?%W%@a`Uʛ #8[XE.3 {=pjO}BFy>j> stream x[Ko Wyv- 0! 䖝9^o=ɏZW=a_uZ%n0^֫- &倲FuL^dOBI+?i-,Y8'? dexkPxY8riFTJk]!ែd#QOlו1*# [`tBXS|tF¹J`Ö幎MuȗV`.#Wl h'/%yE{uЙO\YGi%"˷*l!V51[W҈1= h_aZcS_(Ɏ zqt\v(P6ҍta]Uj Vúi&Fba!(討[seKaHYѦLk,yvk:?U|>>B0ml7]K dAJnX$7/_IEHUU)cwW6՗s׷J6] ͛sU~=FezcsؒY@&tBh&mwG7 W &'Oh;}_>kLXGYa])z C#䧰HmZX/K ,Hw$Ӟmef^ ,۲SR)AWOq3c_wKLa)f$^H^,˴(ܦu }1 ϒU 9^4M>8&t*}$Ka m2^GMh$ZqڡwS 2ޟ^AւCS +i7}ѡosrE%% G-?)[YTM,_}BK_]hYC𴭮v->J`- ʀҚzb^{Bd(|aV6^gAR?sm+nGRl'R&eyhC^ >8hآgH@~-Y©砤Q#=D-'G֌'q\ !_o@("&= 8h6Ƣaܤ/H}zWBI<NְcߎRQŦIz';QEftkh/ԣ^+HJ9<ՐTB~ ܎CΣH]f(7@"c NK;s (ck麼 <z :p!w,Rx:.1fd5P\T(5i{_[EnVm\\R Va4. ܲ4slFqۺf69amYV1+7jD2pmY;~AkkR[oE*!KF)a[x",S\2>Pf;* 1]K"<%ݹq9,8g7Ea< |J,v alHl%L" @ΆۇA+j[; Ivvx^%f]K7~݊V, .ɡgC5FJ bp0(!XXjm>ZLŠqhgBU qm> Mw(=1+$niZlP=[ H"(CiT;M[A?FvXѹI4!rRAHmAd8 4 pMEl4F.",xPS,ڑIMNNk&8 nw S[ YlnƉIFC6>/duȸQil׭?,j by>}#_.Ҙ.X OĦ$Rth(}YeLgjLP<̡죒Hu:k{(eju >8f't&Jh!m{ORpo6d|n4:tބဓ{K$tWyKBoQe[^[0hh +(neL3p`X0M^š^oźOD<㖁y3~ +?r iٯxrZr㐡eXOiƇrn̕AGQ8K!8 z vu1JudT6Z}Y[i]o(B`Q_x|/0>(C46lkE uAl7'xl`ģT^wT1)MeF/FUO2)CAܕvf 3#`g̳㇇URZYYD8_,pVL##gc/- ϵ؟_?h*829.2ʏzďz29,Qn#IK'2zZ$ <-tfh%-i68 &kAjCh&o$Go˗]Υܞ~)1*,# #1 n8}ft(/3:s4PpbM@5xS X0lX;hpI8,elJ ËOQ]c-K;U^,lhGw (~d5 P ^\r̉?=v=pFB;2:*{CY~ɉxKG ĻK0#u_-3l@k9z'<@`34SEA}Ag0{ ތd0nKFw <F .e}6v[ࢡGDW}" 2_OGg:2P^QYǷ N6F[f^g"LhB8?1߶ζ54; s!UỲ"̮iSGRYobq= 9MCeq0K=㏊wulܑP-9Ʒ{>0ӕ6 GZWPjRr&(u^:w+2 ;%d2-|ƷH0jܡn3_o_t}_S^XQ8QVqkew; Hh6EJpfT':MYou{0 WۇQύ_lj auGi[BC/3cAq^vFW@BOjE endstream endobj 56 0 obj 3646 endobj 58 0 obj <> stream xZKo#7 W̹Hbho -v/H4bQב(>?RNߦy7EOUeNjyrLvQ8i/?LXʄIVY$Y*}HMSiY-_Բ6[5zùg5?zOH?k8z6HIy ﺞmZ- P EBu e"4^3*Os25_g5ݱ0jQ]ݎT1Iz+B/flDh`"^ 60sU>dj1⢲5se9q+鑙':E4_:&u&@k\G N3řdU(')Q5זNC_MʒousrP)Êieڿ|}v"c/á_1٪RB+f< ¦&(a|& &qM7#0EfVL{)PF4,hS֦N>a-e!1?4E3f,eUr.B/Rdt3Z ;T,Km.<_<3Doj\da%n@$*G_KXڍЮSᮚQȵ$B;$ίu5,Kwc=D re&t2V0va^Aqf)¨4^.dXX{"Cec#!+^MYYҗ8ȈRfXn33{%ظ-ybSms+iHmOm,YЊ S.h("#RY 7|犙UzlNj`oQ4d(oBfh+ pBeM[0:3iofьJjoDth3H7a)+.,/l&[6Y|$kyb]XQbn5hgs%q|(A=}UT K5*qav^ˑYLEv|Es`yGo*=vDh#Mm,/^ U' $<صrd˝ TTwɶHDGlKx$NȀ a6졋yX6v[U j*FxP䗉Xty (wx .͵>\?-ʯesY +~b`A>=>+h~K`ԑ}ZBzX)s,3saN!wG@ڻ)&6FqgrD-Pc|d B?@,;C(Q[P0}\޴q|-W [\c@}YGAQw1|8 uRт=gVWv}GO율PNuV)513dh5|ʥ2Cyȷ2,or{u!q^PA')Q-G)\qZ!- ]P{CR[0%\aGF/td䞧ŏ.!EmGܭ65;ɵz Yy6*!~ p}=}2)? endstream endobj 59 0 obj 1967 endobj 61 0 obj <> stream xXK7ϯ0cnvg|9%qNY_#UIZ^[w}P NJ+1jvpկN_?'P׿'?]?r3\tH};7ȝXPvn[>W3]+\^)_ᾞ]&{Yϑ#|AtGtnzb3"j iIߐ"th̶H34ʋbM 2LbK$&xO2 dI'tE%ѡs(LI% F=V8|[(\z¸IA 㟏ϧe)D<0[D+9ܙT|$3#}lk9]lk\nKNt GӝfEӔX"`)3la1`WPDDf"aMFyBfFtfR(Odu,L,bDU6KJ{n 2NBlH՘=4 2 / l-A L^*"6jBURdJ6e<&K9C!l=ȚHfNDNxcDnвL^QRnI>|Jlqhlx+TW`v=8Ch Dr&˶NLX&<=Od.=gz^)(b -TH?qCVuS^)'0dEuSSYΉ B,DGm ۱ᰚ er[z]%n8~Rr#,ettJ.}@9x!ս16 %86=bc&Aq^D8n[ 'So\}ZQo&]W^1NN*1|-]'@ʖ->MM/6F9g<,˜C(=Tf]g(L&*Sx&,5y>Ԑ\r*hq6yxW٨WA$ UƈCKػ8̒@7 [7Q-:Uasu7FO1ňWdycA5٩/;R}y$qy:;(o9d ķ -+.cr;~K[s[n15WCEMDTk*Fz| "^;h_ԫ߼zկfx6ck}9wj݉( endstream endobj 62 0 obj 1275 endobj 64 0 obj <> stream xM,7޿΁䏲ah虞 ݁=͆撿Ye^w^˲$[2g82OStp9'=p''ᜦ7 Mf7O?Ox  k&\?^Iʏq~pvD5DCUq(lbc^+z6ъѻ ` Њэ^vf!g,bhDZ~^fW>_ >~nUt  GD1U+ 艗`=^ ?@, GFL?xT2ɐ¶6 Z-5 x6\ َve2%4:, ,Y<9D%g>qpHV!a3RI,-C_5 L& cA]?+KLaۍLlM(kܗ3J5H1a5Bca@f9 )R4tؐ2R_ 8g57b >xfLB?W<̥Xv5e+a%[FETBQYaok\f4`}yTLgUG(E #~mb, G%Z!ʂBhKOCx{;[%J-Zd$]Y*_/H9k($.JIS*U2h ]#Bi0*bߪz92èZjvB*ԅ_=ah A"f*"߫hnřV$!I4TM=UgE7o[6f:jvP 6:fQDA%)G䴋+>텭٪"'uFo_d1Rw/raN=;]mO}H~ Saw܎G UdVg8)aE?؊=Y ZTyH;(*\ *G5bHYځ{GrqFzVUcC/,}3L%=o.z76,Vk&-vj#vӼUQRJJ((ZpoA^UrݫGjY*= M㵫mj-t8ĵ1Ȅ~\CfjR;FwV>e Kyf+, *lQL0U;H'wKq7m6VZLUz:'<HaZR@ƓwDR$D"MO0[BXf2C}a;frirDୢbU6YA:9tgm<ۇF]f6+Cyv*5t½'G`њl:b ZZ)5:`]Tn_ ۥEȐaaGaaa *K\r{;B9z$ņU&teLHҠhϩm.ci)DH:X-q_j郉DI{hd҉8;?iIb@e@5D0.2ͷB3%$L-F1r<"f.Mgɲgη`B܀zCw2K!˥#v?m!_'> stream x\Mo$ WyvJ_ c!rL`'@UI{N%IQ*jGwÿOiʿ-=zowi~\Nz{N_SvKa^NK_OOn]߿{riz/ 9۲]zX>%wI;}tt9d=PX>K'O4{C Zw5wHz)>3&ҦKhEv9goI,OUF5ǣK~nSlm1zu_.O*r,,h.yßbv7Iy n ezwGcǦ}ytOgUe,7hq+ȇ!W7Y-RBeI"VY[ajUfE%4;E[lK웎%!$+ԅFlzmJ_? 4/u}kVw\кSʼnY-Zd_ C ezF|*Tm]цd4Tf'hwI  ZJ%Vwp{{ÈU^SΧbRd4KSN[3oh\Q0Dڬ*CK:{t ;?dʪԍ@Qt4WL9iԒoԼMgRnG2g^Kq%)`#9~̿Ic:\DJ/F:"3s$.bH:V6B-&n KQ&n\R޺)<9Cix&4(JL="x<HV<5ݬ}0goQ127LD/M8A= O֡cy4h<"dKw (ԯ|^JSmI#,S &|S! (Y+r\#}qZq.A A;6Z #F5$^u^T_7Z$P(\Qejc|UpBj^zt\fWaCh.(t tBfq-u|*49&4ͺŁ0$P~:"0xSV#8[):Q*rsENMpnqz51^F4!(ς WS󵝮T§CD| +z[tH;5'>ȆiƷo@YK⤛6C_&=O9?O⎡W&}R9I0M4[s~Z/iοyJ49\׽%6/m|y&7mbYX!2`9b9?@̧_:֍5yy9#"C+BLAA1*#H2@͵{P yV1)l$\Ea0]>bgSbDhݨ+s@% ZċImU$w$^^ lIGX AgHeK& d}l4*0 E`!vU+1 S`̀`uB_ks6&~•jPHsg4/4…Db0FÅ+ՀThAާpJ5|ژuz\Xcy^7y zyvKI\\n|X}WXG.ɯsqk/_RZp}|, ʏO}W7gAn?c/[O~3|U}#_Kȷʊ>:Rg&"+j>CEc:bXj+{z q66* k΋Տwi`^zlAz=3't5^ƽ[/FS}o^çG)A?2@TZMXD')[[(t!׸]jieP)UEZ*le(xh'0e` ɻ{ QMRUVJ-d7&lS3t.y=uO;&R`+!Z?za=髢Xb9=AOb| CX>}Jss#O>W1ؼrZ9X7 1( T(,~1mCPoUd$[)A+Nem ; i7o~f6lxGHRQu dP"­eёYֆݞ8+Fɲr(/lz]qL)6bOZ&x}1 nV)@exi^'JH9w٣iC8yϜսLA|9iQIu &~PO+%[XEEwP] JfC~ s /1,x̐ǚRq}V7$^OQ^h.Z}Ga%jn*Y.a-ۑ5!`p낤]>] A `^]yQ}orǫͶra>RXú k,ׅ5nxR$CWc풲:4/Ad6֥Y,Cͫoxt/_& endstream endobj 68 0 obj 3646 endobj 70 0 obj <> stream x[Ko8Wy$vz=,s?2%eFĖ(⛴y?^1~s|u)o/{Y8cɾΗM1-%HҲ1E0/J7c3`F3 h>&߾`q֦7!IOovpF37֙ayHaLDhB::iV"z4.񆶺5|̸\Ûy_ҏ:Mososq#-U=. \Ǚ6iKo\}^v5.Shaֿ3_{gn]A%Ûf^3/zpWeeұn͊Du򚧄+۩·@f?^'GP_n c! 0`4BB=fDKHP$:|~۴ޭ\tiV2q m>dK!hx/]gMth|˅tG0?J{Go!C[8{SŌo{_X#Ң }i% LyaP$&L6 rrVbJۡ@.XF*Pէo5PK8MG"Y4"3: o͕ ^"H@ e.ƺ"2ծ@|zd㛳;ZdPM(vFl}H H 9a>8%7/_H^UڑMαī+y8W[!:Юjޙ͹BEbzE-GȠ\Y6or[V> [I]AWVU*acpGLj"RM(JG=c b#ldiO6)[i~;_P h[Jii5bK\gct3e~6T^01>mx2(֓C{d@_塼~Z+\^^+?E['Q/e'N@&v+cX A೦A7V͖G>ڠb4|:ldšHSk {FnMP{j6͢jM: ]ecd>gayUK6RwX89"v Jn; Hp!:Ff9Xu@6;8ϯ~+O2!w< rl{l7{TOKX(ܱ"h=FZRuEk$ Ni{^|:tލcxL:??ϩTIًr6QD$׍LN[^AZM+,_}dBs_\hYBu[YjPE>`="@r"ap/!r0.7 hʣМ#|`n}PL%OLNdzh^ >$XX@~mcAIF#TM]%Gq\ !_k@8b.=*:8X1Ƣad/h}vzWGBfK7j'6 ^**GLQljj'pkd&ALRF=ʵ+ۉD@Q 9MUJ 0:>ơdy}[*66d\k$?] 6/>ҊY?]W$U}\MK\*5^o[8]8S5 `MyZDޖ5T6"7W6EJ+Rm0G`Oen{\_>؇Ûu0mrt~=^ pߡW5IK5h=uD:d:r(M'Oh<6|!𽇒>^1^)u!<À!9 Iq=,h}(˪-1ۂ5-|l-Bzӳ)47:'\& d,8-% Πb*qhn E(Rá W%ThW7AܑN MZV^qJGWC Phŵ)ǜ`þskg,@kL?|}ur*lb$pRѺ/ݖf1 k8r'>\@`*Ccy$Xf`7Ȁ׏øh<0PJvgop|X9T?Vp$KTP(|y_v/÷: =6|ɲqF/,_Kb6_Ğ Mh6Enٶ_"}ppXMY\fӴ3کZzʬ71錸H_℄L8T8(KݝFweCTmܡPy uFǟO!\iu3Iukzn0xxWritǑ;9frFVvʍq}( 6ZwhHC;ܤ -F>Y >췪.n-̴nJJ 䂷Tb)*E)/:Z4ϽpP%@G딗b24Z_cAsVvW@dFOͅF endstream endobj 71 0 obj 3640 endobj 73 0 obj <> stream xZK6 W\ Ym3 ӶؽW"%eOb4cS?QR拚9}iN\t=-V]:)K2E]dpIlަY*}+Н% /`xߧOoI޿U?ͷ٪93g;| OM#䏸J< ~z6EHIyЬﺞmZ- H y׍7Peʌ`GLt>wASD{q]Ԅw^SS ە) 6$äzRs6 ' hr1le4H׫B <9*?>j125ke 9i+ّ '9E2`_:=Msק6 sp4H;SN6%Byys;niWsre)R9\`aCGȲM>>v"?'zATL9+[5U~Ŋ߂=5Jؤ&BR9-As!Je(jR\-V6ecUHD֔!$)vȪrފk/%(-t*IS1QWˡf-ŝ˩%x*h PC]2U='U0(JJٚJj uP}Ba%X2H1(Fa<ԋ)U.l:/FT,F].@ OZbj ŴCL.b2+8i@L<}Z ɇњ Z.hNls}r=/i\錭ە޲zwcy552հR @*g_),gF~hיpgq-*z] ϥܱ!I2ksn]3pfRPB&2Yl@ƞ^ C"&C4߅ kZ$;v{aoBL_ 3:({C`G($fa|4$@G૶#H-|qshŒ).x("%ɇ ̘*]tvNX(r!u,#!uQܨB5iEoc[d6maE&Mu_%nbDQ@R- }MfS0fM~dL. :L w{*CHY_Oa$Z9@^/klѮIˎfDyLХPS SF$e}[ݕY"z؜LlԵH~Wzb}Q7cn;5hMg{dp[6z}n*3hwc,Cݵ];Sm$D3Vڻ,P2>-l/ $<8i˝ e*\1BK,ӑȎXp $\ӨO`64졣y\6v[M jXFPrD,gI\t[bBhA-D,܊*|k`AB=^-hO`̑w⎠z!UBܦzX)sx5SOՋB|wK5i-d!?k\͍Y׃'5P'%c^1&nS%31:9Dz3P\Z]+G6[+Ah,0x<պ}mZuK%ћ,vBB{]s4`*F4oݣDNH(:ؕZ뇘at^\ 2UM(dFMp<4$M ! Ty$#FldJ _tZl-O"3ouEEȿg|g wHfӏ_OY׿;$ endstream endobj 74 0 obj 1967 endobj 76 0 obj <> stream xXK7ϯ0cnvg|9%qNY_#UIZ^[w}P NJ+1jvpկN_?'P׿'?]?r3\tH};7ȝXPvn[>W3]+\^)_ᾞ]&{Yϑ#|AtGtnzb3"j iIߐ"th̶H34ʋbM 2LbK$&xO2 dI'tE%ѡs(LI% F=V8|[(\z¸IA 㟏ϧe)D<0[D+9ܙT|$3#}lk9]lk\nKNt GӝfEӔX"`)3la1`WPDDf"aMFyBfFtfR(Odu,L,bDU6KJ{n 2NBlH՘=4 2 / l-A L^*"6jBURdJ6e<&K9C!l=ȚHfNDNxcDnвL^QRnI>|Jlqhlx+TW`v=8Ch Dr&˶NLX&<=Od.=gz^)(b -TH?qCVuS^)'0dEuSSYΉ B,DGm ۱ᰚ er[z]%n8~Rr#,ettJ.}@9x!ս16 %86=bc&Aq^D8n[ 'So\}ZQo&]W^1NN*1|-]'@ʖ->MM/6F9g<,˜C(=Tf]g(L&*Sx&,5y>Ԑ\r*hq6yxW٨WA$ UƈCKػ8̒@7 [7Q-:Uasu7FO1ňWdycA5٩/;R}y$qy:;(o9d ķ -+.cr;~K[s[n15WCEMDTk*Fz| "^;h_ԫ߼zկfx6ck}9wj݉( endstream endobj 77 0 obj 1275 endobj 79 0 obj <> stream xM,7޿΁䏲ah虞 ݁=͆撿Ye^w^˲$[2g82OStp9'=p''ᜦ7 Mf7O?Ox  k&\?^Iʏq~pvD5DCUq(lbc^+z6ъѻ ` Њэ^vf!g,bhDZ~^fW>_ >~nUt  GD1U+ 艗`=^ ?@, GFL?xT2ɐ¶6 Z-5 x6\ َve2%4:, ,Y<9D%g>qpHV!a3RI,-C_5 L& cA]?+KLaۍLlM(kܗ3J5H1a5Bca@f9 )R4tؐ2R_ 8g57b >xfLB?W<̥Xv5e+a%[FETBQYaok\f4`}yTLgUG(E #~mb, G%Z!ʂBhKOCx{;[%J-Zd$]Y*_/H9k($.JIS*U2h ]#Bi0*bߪz92èZjvB*ԅ_=ah A"f*"߫hnřV$!I4TM=UgE7o[6f:jvP 6:fQDA%)G䴋+>텭٪"'uFo_d1Rw/raN=;]mO}H~ Saw܎G UdVg8)aE?؊=Y ZTyH;(*\ *G5bHYځ{GrqFzVUcC/,}3L%=o.z76,Vk&-vj#vӼUQRJJ((ZpoA^UrݫGjY*= M㵫mj-t8ĵ1Ȅ~\CfjR;FwV>e Kyf+, *lQL0U;H'wKq7m6VZLUz:'<HaZR@ƓwDR$D"MO0[BXf2C}a;frirDୢbU6YA:9tgm<ۇF]f6+Cyv*5t½'G`њl:b ZZ)5:`]Tn_ ۥEȐaaGaaa *K\r{;B9z$ņU&teLHҠhϩm.ci)DH:X-q_j郉DI{hd҉8;?iIb@e@5D0.2ͷB3%$L-F1r<"f.Mgɲgη`B܀zCw2K!˥#v?m!_'> stream x\Mo$ WyvJ_ c!rL`'@UI{N%IQ*jGwÿOiʿ-=zowi~\Nz{N_SvKa^NK_OOn]߿{riz/ 9۲]zX>%wI;}tt9d=PX>K'O4{C Zw5wHz)>3&ҦKhEv9goI,OUF5ǣK~nSlm1zu_.O*r,,h.yßbv7Iy n ezwGcǦ}ytOgUe,7hq+ȇ!W7Y-RBeI"VY[ajUfE%4;E[lK웎%!$+ԅFlzmJ_? 4/u}kVw\кSʼnY-Zd_ C ezF|*Tm]цd4Tf'hwI  ZJ%Vwp{{ÈU^SΧbRd4KSN[3oh\Q0Dڬ*CK:{t ;?dʪԍ@Qt4WL9iԒoԼMgRnG2g^Kq%)`#9~̿Ic:\DJ/F:"3s$.bH:V6B-&n KQ&n\R޺)<9Cix&4(JL="x<HV<5ݬ}0goQ127LD/M8A= O֡cy4h<"dKw (ԯ|^JSmI#,S &|S! (Y+r\#}qZq.A A;6Z #F5$^u^T_7Z$P(\Qejc|UpBj^zt\fWaCh.(t tBfq-u|*49&4ͺŁ0$P~:"0xSV#8[):Q*rsENMpnqz51^F4!(ς WS󵝮T§CD| +z[tH;5'>ȆiƷo@YK⤛6C_&=O9?O⎡W&}R9I0M4[s~Z/iοyJ49\׽%6/m|y&7mbYX!2`9b9?@̧_:֍5yy9#"C+BLAA1*#H2@͵{P yV1)l$\Ea0]>bgSbDhݨ+s@% ZċImU$w$^^ lIGX AgHeK& d}l4*0 E`!vU+1 S`̀`uB_ks6&~•jPHsg4/4…Db0FÅ+ՀThAާpJ5|ژuz\Xcy^7y zyvKI\\n|X}WXG.ɯsqk/_RZp}|, ʏO}W7gAn?c/[O~3|U}#_Kȷʊ>:Rg&"+j>CEc:bXj+{z q66* k΋Տwi`^zlAz=3't5^ƽ[/FS}o^çG)A?2@TZMXD')[[(t!׸]jieP)UEZ*le(xh'0e` ɻ{ QMRUVJ-d7&lS3t.y=uO;&R`+!Z?za=髢Xb9=AOb| CX>}Jss#O>W1ؼrZ9X7 1( T(,~1mCPoUd$[)A+Nem ; i7o~f6lxGHRQu dP"­eёYֆݞ8+Fɲr(/lz]qL)6bOZ&x}1 nV)@exi^'JH9w٣iC8yϜսLA|9iQIu &~PO+%[XEEwP] JfC~ s /1,x̐ǚRq}V7$^OQ^h.Z}Ga%jn*Y.a-ۑ5!`p낤]>] A `^]yQ}orǫͶra>RXú k,ׅ5nxR$CWc풲:4/Ad6֥Y,Cͫoxt/_& endstream endobj 83 0 obj 3646 endobj 85 0 obj <> stream x[Ko8Wy$vz=,s?2%eFĖ(⛴y?^1~s|u)o/{Y8cɾΗM1-%HҲ1E0/J7c3`F3 h>&߾`q֦7!IOovpF37֙ayHaLDhB::iV"z4.񆶺5|̸\Ûy_ҏ:Mososq#-U=. \Ǚ6iKo\}^v5.Shaֿ3_{gn]A%Ûf^3/zpWeeұn͊Du򚧄+۩·@f?^'GP_n c! 0`4BB=fDKHP$:|~۴ޭ\tiV2q m>dK!hx/]gMth|˅tG0?J{Go!C[8{SŌo{_X#Ң }i% LyaP$&L6 rrVbJۡ@.XF*Pէo5PK8MG"Y4"3: o͕ ^"H@ e.ƺ"2ծ@|zd㛳;ZdPM(vFl}H H 9a>8%7/_H^UڑMαī+y8W[!:Юjޙ͹BEbzE-GȠ\Y6or[V> [I]AWVU*acpGLj"RM(JG=c b#ldiO6)[i~;_P h[Jii5bK\gct3e~6T^01>mx2(֓C{d@_塼~Z+\^^+?E['Q/e'N@&v+cX A೦A7V͖G>ڠb4|:ldšHSk {FnMP{j6͢jM: ]ecd>gayUK6RwX89"v Jn; Hp!:Ff9Xu@6;8ϯ~+O2!w< rl{l7{TOKX(ܱ"h=FZRuEk$ Ni{^|:tލcxL:??ϩTIًr6QD$׍LN[^AZM+,_}dBs_\hYBu[YjPE>`="@r"ap/!r0.7 hʣМ#|`n}PL%OLNdzh^ >$XX@~mcAIF#TM]%Gq\ !_k@8b.=*:8X1Ƣad/h}vzWGBfK7j'6 ^**GLQljj'pkd&ALRF=ʵ+ۉD@Q 9MUJ 0:>ơdy}[*66d\k$?] 6/>ҊY?]W$U}\MK\*5^o[8]8S5 `MyZDޖ5T6"7W6EJ+Rm0G`Oen{\_>؇Ûu0mrt~=^ pߡW5IK5h=uD:d:r(M'Oh<6|!𽇒>^1^)u!<À!9 Iq=,h}(˪-1ۂ5-|l-Bzӳ)47:'\& d,8-% Πb*qhn E(Rá W%ThW7AܑN MZV^qJGWC Phŵ)ǜ`þskg,@kL?|}ur*lb$pRѺ/ݖf1 k8r'>\@`*Ccy$Xf`7Ȁ׏øh<0PJvgop|X9T?Vp$KTP(|y_v/÷: =6|ɲqF/,_Kb6_Ğ Mh6Enٶ_"}ppXMY\fӴ3کZzʬ71錸H_℄L8T8(KݝFweCTmܡPy uFǟO!\iu3Iukzn0xxWritǑ;9frFVvʍq}( 6ZwhHC;ܤ -F>Y >췪.n-̴nJJ 䂷Tb)*E)/:Z4ϽpP%@G딗b24Z_cAsVvW@dFOͅF endstream endobj 86 0 obj 3640 endobj 88 0 obj <> stream xZK6 W\ Ym3 ӶؽW"%eOb4cS?QR拚9}iN\t=-V]:)K2E]dpIlަY*}+Н% /`xߧOoI޿U?ͷ٪93g;| OM#䏸J< ~z6EHIyЬﺞmZ- H y׍7Peʌ`GLt>wASD{q]Ԅw^SS ە) 6$äzRs6 ' hr1le4H׫B <9*?>j125ke 9i+ّ '9E2`_:=Msק6 sp4H;SN6%Byys;niWsre)R9\`aCGȲM>>v"?'zATL9+[5U~Ŋ߂=5Jؤ&BR9-As!Je(jR\-V6ecUHD֔!$)vȪrފk/%(-t*IS1QWˡf-ŝ˩%x*h PC]2U='U0(JJٚJj uP}Ba%X2H1(Fa<ԋ)U.l:/FT,F].@ OZbj ŴCL.b2+8i@L<}Z ɇњ Z.hNls}r=/i\錭ە޲zwcy552հR @*g_),gF~hיpgq-*z] ϥܱ!I2ksn]3pfRPB&2Yl@ƞ^ C"&C4߅ kZ$;v{aoBL_ 3:({C`G($fa|4$@G૶#H-|qshŒ).x("%ɇ ̘*]tvNX(r!u,#!uQܨB5iEoc[d6maE&Mu_%nbDQ@R- }MfS0fM~dL. :L w{*CHY_Oa$Z9@^/klѮIˎfDyLХPS SF$e}[ݕY"z؜LlԵH~Wzb}Q7cn;5hMg{dp[6z}n*3hwc,Cݵ];Sm$D3Vڻ,P2>-l/ $<8i˝ e*\1BK,ӑȎXp $\ӨO`64졣y\6v[M jXFPrD,gI\t[bBhA-D,܊*|k`AB=^-hO`̑w⎠z!UBܦzX)sx5SOՋB|wK5i-d!?k\͍Y׃'5P'%c^1&nS%31:9Dz3P\Z]+G6[+Ah,0x<պ}mZuK%ћ,vBB{]s4`*F4oݣDNH(:ؕZ뇘at^\ 2UM(dFMp<4$M ! Ty$#FldJ _tZl-O"3ouEEȿg|g wHfӏ_OY׿;$ endstream endobj 89 0 obj 1967 endobj 91 0 obj <> stream xXK7ϯ0cnvg|9%qNY_#UIZ^[w}P NJ+1jvpկN_?'P׿'?]?r3\tH};7ȝXPvn[>W3]+\^)_ᾞ]&{Yϑ#|AtGtnzb3"j iIߐ"th̶H34ʋbM 2LbK$&xO2 dI'tE%ѡs(LI% F=V8|[(\z¸IA 㟏ϧe)D<0[D+9ܙT|$3#}lk9]lk\nKNt GӝfEӔX"`)3la1`WPDDf"aMFyBfFtfR(Odu,L,bDU6KJ{n 2NBlH՘=4 2 / l-A L^*"6jBURdJ6e<&K9C!l=ȚHfNDNxcDnвL^QRnI>|Jlqhlx+TW`v=8Ch Dr&˶NLX&<=Od.=gz^)(b -TH?qCVuS^)'0dEuSSYΉ B,DGm ۱ᰚ er[z]%n8~Rr#,ettJ.}@9x!ս16 %86=bc&Aq^D8n[ 'So\}ZQo&]W^1NN*1|-]'@ʖ->MM/6F9g<,˜C(=Tf]g(L&*Sx&,5y>Ԑ\r*hq6yxW٨WA$ UƈCKػ8̒@7 [7Q-:Uasu7FO1ňWdycA5٩/;R}y$qy:;(o9d ķ -+.cr;~K[s[n15WCEMDTk*Fz| "^;h_ԫ߼zկfx6ck}9wj݉( endstream endobj 92 0 obj 1275 endobj 100 0 obj <> stream xzkT[跿OIt@lȀdalv m 1Ic+NMRשm4qZ8fRNWzۦn{ډNf$i;~%]]#s{{!c.MGl{z-9~@CyOCnrH ];x2 3;dGK"1}=?vN7MY8=L{`vrCJcd-(*!6{G{[kKsScÖ͛79785mUҺkV,y9Bѐicc"#4jRQ ]pt"Wp: . 6BwGN -wJP.I[HvSt:PEv: oaE b4oOۃFE5BFF!P0_JoDÚ r9`cS6h(낱B"5ɠ&Mn:yGtdg9O빷=n`P,|;#wE437۩$9:|D0a;)=a*Ga8-Mx 1ä斾Hzq#p`| q=Ho`\nw,4-6D`:0F# 9ىHp=dgb;q9m3̹-`oַ>9~'8iu ~nqz!U]*s1-uV S d$k!av삽;LA|tC]"` }؂=EZa8 TO.Ղ 5A Z쵬e l M허u*>E+YE:jpR U=4tHӍA[vp`*Eo־&H)rwCf䂚 Nӹ!w TgP[ TNэ`owՆ~Q%+5CN3a ]"E6n54,e3(I,)v%t|bc鑳Np_ށݖ,L1"{a :'7Aoλul>[̸6H a%l[OG?ς1hy<fm66ٰ u}},3W'Y[q[ E̪g84k-/?~F^% J3YjFD#˧_2%s2A{42 H t44Efi^JmX tw'IBJ̎P9 T\(ѫ*DW1+P G)l}6I,˺ jfdV Erd jUg.[F}QolƍAe_UB,⃸ғ׶b dx&eNg;QB7m <Xr8Ұ}ݰnJ7Zu}:Z*: :]jܶ(܄6}~FZ4ϥrۈB)8BG,]cTҥ$TT-ĕ[:JNkIzήN3{0H~0Q/%+hbB,U J8zu]:0M7/ۜC/*X=xbG̓ɽgQeXKsgO.Tl!$mk'#& Zf-˞XK"<#Aƙ}"f$T!D^cy\EM6f8 0Պ"-[n1 YہpL$IqH1@-㖉fϠzDc7⇢bJNk~*S/>K}9y?g:(_U"P"DQ-]jX,53 8Vȏ; w@&dk^89FkGxZIe5r5zMn;>Omղ5ָrbwekt/rTXPsXW@rRrJX'8s&2""RpƯ.\TǪMF_ShZ ~zch)s >'/7`Kc15:3K@&^exZ\_"GLEЈ(@ac8A.TMl@钜Veӛ1RiORp2,Z-$[-J,+F4ħaƏ`3pIź=OSNC11"Zyo6)`,m:N:ʁ9P;5s !@j"OSU:ezl[i1MSqx\#< +rD87nw8.2.-RԐjUX\9X̝Vke#洅\B+JeixFWUrR_֩ڮ\DۻgFYۆ'ٚB{o]Ұb]ZC3;6Lw'?-bN֒n$tbYt2mc11t2'CwiZ$j`PUA]zRW%F+fğf!^\H,Kͅ{sw"sri:Q@QTRrֹ;s$:̜eAWǞN|-,gȩd\2ze l,]+dnK/U'U/z_}yLXwZ=N?k/G5񳻭YN `wӯ:-]qO'Q7Ф73p[+ul`z\-d2fk|R 'UpXlQ:HI8%)mщN]"H xU lFJ>MRDTWetYCQ+Bk2[;GXɩaRi$b95A:7f.!Qs e|B >?+m*W&-=M4b XG$ ;r9aj7r[iG;*`\HO'bШhs8kf&tM(ZaӪ+> 4{43!s!aK?Ґkঋ[F  uF?77ބgjjoo>{uz[][`xd5伭h"L&hڭ۸!nZiB֬,W¾N@ #c '4͞6HJDoBo9Cgr bʒE4M/)8Dfx~U/5d"MԪWScd[cfr?c!40BVh2a9Q!syK];77)|uy25mp_^bsȱɚ_?T~ =a,t479]co)\?i}mO54iHl;8?OnjF*C23Xt L$@Qh5JzU A弒R°rJIJ+Y2ҳr JX Wה(h7U,:w܎$|㯫XM/ǽg2a h2VXu,^cQ)p\;MI.8l&;7w% 潟G`8c1TU!)>1hّd`Fwd7n >Rt dGG*WU_J,PriCmβah}zaWsY5jX64SwEQjb>oW%8)# nNVHvKP@*h]S#ISIͷ$BFXSfC_[K$Ak].K;Q<ؖh<3D0gvIAdˢMc퓠^*fA!}#ҫ+뇚zUbpr hC)]gq }q"s>d K8T%*YI+AHTNKg*ظPs-aŒOR+niF:-K lX/&1_*VİS",+K>`yNw쮻O_p2IItl//C0ϺKiR5I qBBѰ‹Pʚ۾c\ʺ>[M\˝Cjzo3߲hhq1C\"Y8Q))"y̓|"1$FSs-}6y e8{v/x`yʿlPCA?xݬkҮBKKtVBԕ7[?kGj?!*|d'Kw>4xoۂw#ރxw V%y'"Tp[ QFaMMhde rzҌ+N诒:踫TWmo^Zװw?3ߩɽ5w{=|K3FB.ЭQW.b%p=pQ$=|[QKwy|ou^רAshls|E7ݯ{].~sy=~pŲwޢp/zXv|^4߳{:cr)sx=1C2"sxfއ]}w/Xw-!{nπf%L/&d$~“|ҋ!ֱFH%DZOvㇿ͂O\v{\ePzҊpقT,߃{PE{BiPQރQ>]9bލmn~oPƆI4W̝{vdhJqd ytɽ</mΐGwb oUC~F;찞eo/N(kb]rdЍfٗ;6 @A~0,Bdç 5>pdxa6t]?pj7\~ʨ?tI?\s~y85k#/ն$*pm 7 oRaYJ?~ؘk.A6Ksq%Ū \wO?mh| ' 84|xcǗsŏSctr6:=r,NMV*#z؇S2gHu3 B>aOC6>DcgÁGv 1Po9d-hi}KفuĒ endstream endobj 101 0 obj 7317 endobj 102 0 obj <> endobj 103 0 obj <> stream x]Kn0Oe0$!R$}{H-cg3yI՜}&ق6<ݜUf\i+z˱, v=`{-|uTS,yu 6Wڰno~sʒ+;ҍPjݨP~Y߆bgNQ`י+Bu]20_-cg4lb9~ޒ3ޑ7):>z>Doяє=F"o){gxO>SNUrys..~NS|쐮 endstream endobj 104 0 obj <> endobj 105 0 obj <> stream x{ \Te眹a`fDE h9(^7d ifVͲm̌5(sks33տlvu}[< 3 ݶ3s<{mu0do{!FG/K[n,nnks D=iYC/^ FY%r\)*2f\&w=w!fMO8'µbov΄M5x3p!0O 5#!a2GFYâ'$&I)#3Ff_P:N0~`Fuf^GFn =Πcr:Bv@0лZhL{As-ڮXZI:܋:1r-~:ڬXԡ@AGӀR:}iS^4j@ݦ1N:$wQ >͠5Bn2- AcH; aGf|'wtLC? FzQxa%:2Gyқd LZUuI].TB|'Iui]GcfV͓:T\<+ie)oY]T(n=v` 5|HsUrɍ2'Ӓ g?4`$Xk:~0z('ob.H5 kM:-|>?eOㅶE"|Ln%Z r:^b  P:N2L!M#D8O&hb\̕eBf>i\KX2~ЪYmd.D qcnFwN?;7ltOUg<Ȋu ֧_)>:4(79hM'40k˜IHU™a pSS,m.G%9_x#^ɉ8?ok8Nل6h=aG_\rhQnʇpfX=qۃu ;v,|%q*pphv_2)dcnIJ9XX$x+Z7֍û|wZyDѐEqpΒR<{$h?&Ӧ/NGF =Q>(9^7ćeYII$ä0$<)4ʱXG 82)SL^\oXI,iD# 'DbH,#|>#'sJ2Wgޕّy&s8FleFZ%DlhRS31ێ;*v,ظq}S>?]zS؞hO=rdm,9|ޗz`ܸ̬Je] \(t-&\Em `eL%S7{'y 6dk2l̩͸ ʉ6l?ױG6)o*44ӣmh?'`Jt'ɵLzJ_0*ߜjη?|3J%@π6;,B=ܺ)+nk ,Ŗ$@<{}u; h36]>_ VP |==sgƕKZnۣ޷C?}o_{J`08Qc~YIASFEN_hM /ɫmqJ t26OALLϱ";,-@T _ypd]]ٷaӰ^쎝;c@>$C>=7K^Nrpj$oaͬ {X~S?P5_||X%"a_!FS;vLAPLX`H/Qg9g)І&YX>giVKSZro\{;|NJy 巄_޳a=O~9*0y[p pf4EA?pku+N\xcۏsKAr[mtIZq,gL^ҍCp)su%2D9x>WPvXhZ +VafZ7Fa܃~~afSCOu|us|]o-u|$䃏h-Q Yi,akx~6" Ī2 0mbܕ\5hWsZSb5f.qbff]b1b3|&|oAxH?:7|_!/LJFnA B'# р(uʢ¯U}qW0َqgV{\D;6LǙ HID+q<*7T^&"HjO x_Rf("~$wv? *(m=srGPGڱpIm [ N)) ڏT c]]Aw( >5`c$Mm,1kuZ:E8fQr:tA(jE<y,ԋ'E9YQb:$Վ]Jnv[]+ 㱁D8Hn~,L띺%e5N?D,,R8`oƫ˫ȫ b{us^ģs| >Bm hkfEiV/r Vx J gc)`$KP)M"YجU+YH4 >)'$ M/;gݝ_XjܛC~g@.kppx[3~DMaCZL4fCOKT )!pj$&֞jXgx=Yɇʇ7GJ BP9烉CMl3G {8[‰s19؁|И')ϝOҷBr]xOÞ (M8- ? =oҙMP=GzX7gH6s1+hjo3ݗvw2%.&|6ƒ2Ah'Yq7ax^i XF %edVf䋏GЊ9OK@>/WxݿpN W/w&Ō~w߻UqmL>g8>aL[f mAkgu?DP E!눇ɂ>W a<}g߄;zR?+ XȆ)d]p10ξXPDޫ)} 1~ P- I&rBG9>pgsҸ 1Z1|,'RMk/Y6nm# ) 7M!}7rYOMˏ# F>s6|wAֲo_ǟ]'A3a/?yl%':Ի5gB.%IɤfVYLpVC4$H!H$S~\-}'v۲kx}%냿~E^ny[1"}$zghs3`Dkl H  &x 7?baT= Ȁ? =L:ʀFh>r6t;ãw4+peXqnx9YJ'=hd']'~B;pH9'w :[*57%vh-F1nnO"h$zqgLT5gL4gI2b(I^/, "HmtQt?#mB>OQ ,C&ȏFn&pk$Dt=^. sXw m|P>E7{1tߏzaF=לB_k&^xޏ&iM&۳ÁN+|GC=kGCmC_܃z.'. ^ג]|X(v =[4hSm.[\OtC`1~e86*y=:hլ9fc9ꘇq:pԱY>uC) ؀ OccZu'%c6i:6#ݤ=˼ō~id}/-Z&>ioΒZ곥¦&INӻ/:n/m^nY,/qs}NThoYIvSrHEMznZkj-"ːrHyc %~OAN时fܭzgۻؙeTEH)-r62 qTڴ\tH ^wTu.UE `jU,FA3ofqeċ}) v;v a0Qrz]>f~Ojtzkgl$[,<0^vaS}#P+;[|`fL >e~]lTW8i$6H5?%IuznGkq@1עV ؐnojuPI\Fwiv(bJ T,IYBxdQ9ns` DUĚ d=~Q1cuꆆVo 0tsgIEK~:Ckp7AQ-W @ξȽ4P -n?Rxܓ|&qSYb+5!Ֆ<;0Vxپ ;\ .h&? `+ j\MvH9>&b%WaP{=y|9Q"0`7 M@#H kiZ&B\x_'tࣆ~ be^OJ;pCLiL)We2RmP,us䇌}QPtt hKvPt `t;*pPT hx9M4ۨR+{ Ša[j+(X 5X*j*KjVKe5RUu圲iӤNɒN]+ŠReTX1_YV1-K*WU]\S#VVKeˊabjieR쫨fJU%UV\C*:. jg%e@ ڲ U5@c((.ųA 4j~uY,T YbmuY3$ V *WKlI6H 49tsrp]KSZQ9X,]1B**U ʋ@ei K:&tN"PZ\Q\]X%TO-cYuZl(gN)f6L,qb(ߩL2~KVV28K*.)q?+KX{RURѹVݪӊ ˁ` &k!owz4VJ#+JbQH\e -AfT`æ8K)|@tC'RJc*WtbLVz73EZiom~1$h y]~(&fjmi 5\AyK:eZ/eLW `fUufzA*ŌeK׷N_~78HTpMpA7A8H-/3A"~$D2$* b%1o`%RXIXI J;.A?"]%QKҷKqsw !B&QL7L`$}$ 9fTR #t$56H #۠#1IC#۠#>>偏Ȁ@?@ _ٗ=isqA)Qؠ34-C^BQ## D(P.aVH^DvԌ` l&xI]9 {§V_~i)EVS9q%ojΨ9;H*-5 I `:5$rKzvt݌kșX=4Cկz P>ֹ 8^/;h8aOI Vr6XKؘR*;`M#{&ZvT`=1: .tW|fQ.f/+ewC; lg34ʚo97xB5b`LSk1Ңz=K-cJ&W=챫, !B?OL/c+T~?įc&tò4"gwe8d\2*MX 4W-B5 ̆Y!ޡfObHKկg BۥZu/ur3QԨ٣+qdC-Iydoj%SGIl3&Keg*(Ye -pq%h~5|r%hOb:ۙ"cMKu9I}3֏ ?DsUXr{MENmdt?HJmyh:uDMJ$j }Uv=Jx RT5FK0`{ %[&uj.{꬝Օ ?"2{8:dZ81l0_;D)!QL;DV5qܢf^JwE8d4 /o*Eҥ$P놪 ZC5U˅檏U@f[ (rh^u@7bcJ?Q%WeVRQ1S*򩄫Z4pd5Wsj34BvOa8Ɣb%h)4Ҟ3ĮLX_b4(j5 Y%)Y0[:c*̆k:.E**`W-ʢHZ A*c͂j?][=*Gt\ʩXQ6)ͩ Q9 0{2i+%p_ѥIxBh*|WoeVjՕY̏Til?:R$TLA*٪-9saKbL-M! N)@5f3 *"Zڳ?C2ًJ>q*dRP b?R_1T9[]v,e3J<1]Vh*qDyu2gjL2 Ԃi.?Bsj͂ޯPq*sY.U59Rw* րj|VK6о< *C=ЃX<[CY!^RճعCQcάZ*\6ZU| nC=a,@An(:>W0t#6v7=ى{slg?Wi)šPEO.mlW կU]Kz\iHt 9Q\Oft(\ r< Sj!;EQ"W3w}.t$8uAA|$~H>DYG`W;AE+I+\)\)x\Ia}JOk JJAs%2ϹRNR0+]^tIy>W针. }.bS&h?eOALgILv$~S&_v$23 wv$ّxّo;;/yv<ן_rtgGzr N|BOi[\N|Đ˝;|'4PAd|U7W9.7;`)W Dc7g,+ ŭ]8/s4evpL_ə4 t;#>G$ )!|zF'k'#琏d{|G|Nޗ{w7w_&o`on&' 'n&Kɱwc2y'Z&/e m'oIޖɑDV9,7֙7χ2y]&?k2yU&?"2yY&/I4[&]/,t} _&/$Mطv?I#{eB;#e)dA 'Iv93;#gH49C&OI<I…cQ%lqM&?#?#< =h!"_&[ۍVشwopHro8er]/V&l^H6;elaL&rl ,>3ֵfV552Y-U2]曅[drd +r,M-,IL#7#e"drL%5%ƛbhS&dL.,d@&sdH92 gZ+&f8kfD Ȥ@*eR1$Tdd&ܙ)e&aF)K0 e&2HJeRN4LSϑILb\}Up\59B*Ld&.DIFR 2rE0$L xg cI]adr\#11z!DIV=*M 2#Qi$3HF4f#HILR"H2I$I:GADI0x`LΑ"12 `h MbU&D$D f 21 2 aÈA&ee:h-D > endobj 108 0 obj <> stream x]n0z C 08v $%S-0-|anQ][:3m0Kɷtsޟ*Ļa=LG-<*U;:߻\CcL>;ed)wޅΟ]*Vv6s~ZO.PCFpS-ق+Їś(}x3CԡRGY aƿFSXʏ Iӌ,Y Ӯ endstream endobj 109 0 obj <> endobj 110 0 obj <> stream xzkX[ו!/爃C #ƀ9<$p pmd#d'v$v|6vg2i<ؙGi;m:4g鴓PΗt:-f#a4ctY{^{ڈN?JA3F_pߵ!6B8mxW?pU_AkkptG Ĕ#dG'vo2u*2N}#lf;B{f ۢ)S]4#|ߴz m'&> ߄J6Ovm]?E(a$ʖ*?xZoо!׀!̇M P&$&%mZz.3+;'طٷHv;.f5@" i~F+E9؈>Cx}EŕԸ4U\d2zz+(]|-??5 ʆ#B#b&\;{oy{Ja rOQ";;xwַ4E5|JlF@DëuvF{6y"ɏ&Jo,tU7E:@Y mFdkh4%={tgfpt{f#qEf?m#4oa6MYdZZ;2GXFBj귱ע&0i:>weY K,AOS/4dzY r{x#hx48 pL '8r@ǐށYq /`a hGX3)EJ$IDo"^AJ +jgdפPZ\ :5ٳPPJgJLo f4=Pb.jީN!\L}>x}#^yYґvdF*=)˥p.6X)͇6B U][e hׇߙ}5k|-Twp>~N=qaϛ/El47!9z2.9Kz%1 uKy):;Q8l冸9Nw^a@rQIyK@rS4J5ԯMMMvl\v҆vm*6:.ff,,D{ S(]Y]w}Ne4nַץ;f!Q"qsXz,@Z37iRkt4Pz-ҶޢL7Iك498'E4Vg%5VP9B UtR,k [g^ ܼ+fC]dĴo̝~6a2qǞ~.Hh_Ԓeoq>pLg@u蹳d隬naO ΉPjO˩T_аcǨG/_OcoL=9)NʧR/2DE`T;`5Tb6Qljʲ/`:r`)5.jAM"1@,)*tZ bbTٶ=HSMf=/+l]'s=C_'DM<j,p7@wwVTKRPl)fOl9'1p4Eb%`IS_V`E؝HJ$R7sj"K ft S&L+OMdU9ŋif`ѻcL*B5h,*%u4cE8PhUp:/x𐷔ґV5j^t%/ "ԢdP,ʉMO `pr뷶ۆ6U`P{Yk)靣'|@*҉5lIu}N"eI*}_se؎mGoGOE~%%%u ]ja\#u\)WszqFlk4RR#6dgc=E?.2̕Y˨"$Tr"9x9ﴺ:`l0ҴZ͚E (ͼBi4K5Y YJ ϨY45}%4tR]ms7VI Y62R9Ȕ7{1cXU4UrYjuYG Cb{AXSk̝dmS6KN[F $p ʯ񚑽jyp*$TA-QtgfRi. ?s65dP΢OeXV 8U4~YfWx`Y}HX(\R)5GWQTƱFe6Ej~^uIE`a*sek*]lb_]syWV l){$ 3 /'! YE,T ~?Q-(/Jw]e}- U5M}'.'2-b&λ/G݆GNj6<"QaEnqH6>ej()0c~J(\uZ:·el%RA*RD'[ d%v-X2RX5:$l(Alhi\/{{K%P-؇GRƩV띮@%$j9dLdMaLlŀD,{`Y-l,/n10lsCLcySrM8}REp`OУϢ$"L.C Z|;qc&JIpjDr޻3gNŶm˦L*8uz時=lƴb{~O7{ߘZ3a[$FۻNH_<{ar|RwR-ss ͌l 'n tzYK=^apXAr01f#ɏBa 4(=zt~:=<={ :]J_ףAǎ)' '_=~lC{[*v̼,}";"m^E|U/uRě""DEÏE|YO }Lĝ@C5>Hņ.Oω~oUj*b1ODܷ<#'cS+d"|,ƜLŘE2u1!6_1H K K]4 5'ɬlYĜL2/D(Ř>ʘx??hayeE2 =2 9n M"uE&RDOȋ E)dKW 8'qg0^L޾bkqS딱0x(iXmgMmM&%-T|+a̬5tmAit iZ"(i44!R*RRRCDEj/oNKj ]Wٶx}'Ʋ*̤s Gi4.eCMUb\i+IR,^ɕU'YXƌz#B(6ΣK b!L+Dv Y4z^۰bA"Ȑ{3 M)8Fͳ6-lFSH\ oh8~{G3~}g|z3ɜ)Dף?w~B= jc~MLu+M6P:>G9k0ib}C/}]C =>9^0ϱ]}[rł=<0/IIݲz䌯X؇٣6`[kZ'#7SA8L#02Уq:;qX5+qXp"R8 2 o SQUh G܇Iyjcƨ΍JWa8̠kqEyw?J)8JD2q8b868>d_*Whk {z|t,̗ VG__;9l'&x OC] ߵֵܷ>><Mo[5? &yo2i 'G|^tn:D[~T|a"bt<Or|ﷸ-|/ @֭~98B[i Lq]~~/cppzZ|qa vXP_xw?N-c] d)ҬԠ30 Fє!'CD NÖQm6K|hGD4Gh !іax$BZzh,5 +8~x.^ SL; >F@[]0~<IuCk@{F6< T2iCc[O64[ џΜ_cC2MC1[\2̻(2U<(,6)S}Ɍ.q+'M9,C;9X\N?B@I--3mONW-".yu2>,v#+WXNq1AY~ޣ@EФDC;W1AE|eɛ]>#jw㚇Q@p] /X1v8J LvpN9~Wu|0;$rCgcYmxYɥi,XM7ԐKr.(GzqqpJ]ԀkE-4i:skE J_QOaԏH%=z>g8uzHOͨVKPS_V/1U53 |l\P.vFݛ"`&OgcDq07nc=Z;#tFF0&> ¡Pp BˈPh'yDFBpņ!d;@r- ̊„LdHF63!~ڍ endstream endobj 111 0 obj 7524 endobj 112 0 obj <> endobj 113 0 obj <> stream x]n0 _۫(77`; 0Ѕ>O{njS&%/DBb+z40Vo\BUUD`Ϳ$K?[RUH2- qV!o ry|BΈgGf)rLNCu\^$S쌼#t3dO'Ox.b+73:oއCyz oɍ) endstream endobj 114 0 obj <> endobj 115 0 obj <> stream x| `sf67n!$$$WyɆ$@Hb h6 YL "V'~VD"V*Ҫ(+GկEC;M?2̝{=sm(!qd%ltͫEyк4Z&'$Djl/<#s !ښ~;o㺽rtk'+;uʖ*|oƍ\'}\:*2 .W'&|G` riB?_߯#$ax8b1[Xlow$$&%WhK^wO#Id 6ϭz B$B)o"(DKZ7q/o{n6@\!C=e44ɝ4y|!6& !y9ij|t6#kr yZ?SQr!V#1 }&w}H>6^Oӿ"kȋФ0Im/9|M'A;(5y,3(^-d j-*)GMI19r7KhJ٨ͫ,?#WQ;rc!f*kLmtk9~-B$\H]JZ.%ӟ1!П1-rYkZ+7\7E NvrRl"A5mp|i ٦Ύ%!0,'I%PfS&Y-wIZ]FJ/.r7EQ'bL %}_漼I}va%^U{m.Sw>]$g( )÷Ayߺ`N](7V4dCN|EXT)OU6]~[ɗpOVm&p1${/T95+ߗdRH~Hoq#Ǒi d|[[ci&pH92eE2AH&Ě*Rdc?NAhKIQoɤb#Ʈc7?坅Y@$J$9!AdUs%`\6-Mpٱ,J̱z;x'嚾1zLn[)I>i3NvI n[9:[Y-~JLC\[iO֛ׄ QfKut;?\3]XrJ6iJZZѯ}xMqW9 LN&t8 G-O $$g fu[V{}=Z9\i6&TBkZ|Lut/o0'8/Hzv,}!:aBp#yfXce5i'zKJ&q%;qDx|R$G¤bgui괜#ssrԮr#Py@} 1_I.*.rf7def5gߒ2Ugoږm_D\SrRؒ h#g?Ǎ58.cUY敗{n^Y;oh<}BTs}멛6-z~ov W[XH3><wN9:#9"z8dCVӬ$k¬ k2g[|c/H&q~cɩ'{OrMrN=vb^^3>{dȺ+GȘE/IIFGqI=hk(Y擴5~|mvy/KÇ\eݩWY}S7]~(&t2řƽt8uH0I$1ꨔ> ME8VrX/u挕2$IR 9UvH,vtySR{֞[F~cˢ㜟%-5'b'degJGل|="'|$mC^n9*fgĐz[RL]Q/Ccڏ[jp F(y‰$Е7nD-DOFN ᚶ94I#Ίk yTuNw߉ӴǎSk[莝yy>nIEVsoߚc* 'pfn m a$*9͈[Zchg;k̴߮a'vuA`3m}HC˖˖^ލW* xU-:ܖ˵hsgf5ϰ#r 휻X;vl0{CK dRJCi޹)|yYxjmjY=E0u!oQoLǓ kd&&c:&j&.ot9>hXE&i;i7N=M[:ץIftg^d} Eן#yZ|x+v/֝{,9&Ęc T2HX YY ~,V 3]*K X[gC.iHxKmζ$1tiT+UT˜]=**#ҶmlL1Rh:m|LC-uByyNǼl|^sZ3uHP{?h#ߵj[Nh['߂1rW!vf*6yX&r$}]B&ǖy^!aDڜ Ӕo@JK?^ |qXqIÝ;RA˚ w{lң^֫w^mS*Z -uI5Nv6^n=7 Y YbZǨ'wd9~M&XWDUbZ'>*2.Gˮ15N/*?}}{\?m-}?yfēsکeF䃤t)aғd2d+m"ؐe7'QA+%9O@lA5?s9a+T6Ńıv Ny aĨ. 숙X( "FOpx[r GXe9]sY5Zɥv쁱Zu-F0|s0a!6Ԭ ufmMP;qAi}Zՠί*(WsK=@]P8v^5 ڙjiBuNUMyZTW_jժuU8WUSV=RjjꪹUVl5PUU4pds+fk錪ƅlfUc DRl^uiZ7q#ښHbn jWUj,M8YK+)PY-\ BqYꌪƆҹkvnY;FQΨyCQʪKsK+8a"L'7TVTԗV ueU|z(k{D`y8pal A(gBiogeAUCEZZ_-2)<`ۈϝwWV#N]׵z!FpQQ=wՓpe>'x,adSGnzO"=z0y*'oPD:>S$x (̕gs@@axqKOdq6q cJHF$T"A? .t,+D?'.: хZCåBH],}!]ʘ>stS1R/b:H:YIU` ό! H>Kk%n/Vbz~Z}JJl@]p;[ĢjP.yI*Q.\bߍwĺ|g.Z21dR/dbK&BJ&6dɤ~5Ο;]:낪#TG,\:bՑzA:R?Kuĝu@>쬅) vG=‡g` P)VsvzzA+46Fʈ2 ^Q(JF㳄5G-"AH')*҅8*%xWPy=KFHvT'SmDJKӅМtq'ъ.#vD*b»aZTC.6OD|>˪zA cɸ».9 6WDȰLLrK| 7 R8*qe´^\ C\ǺtVv6i^Cɮ[υh mw;vh{Gd ^)t}-.Y)E+xjkCłJatj\ w~#t >2|kxK5 !`jpua Z]e\r$WXugPՊ{\|LDA+zh+aÈQ6D8F@ؑS.ssYae>`)4t?r/U߈36?7; m = 3"guGYLۍs^.( 4 )I[uORW!4U-P_?cеS{'[&d䖽V>U*t7P nJ{Y"֯1QPnB+ D,VRa)w~ys6P8 ÝOqi`jÆ~mxUZ 'wtF΂\] YRv_K։݆|Tzֿ^0_DLzjL߀ώL3.â:>;|$i`+ U˵+ET(W? _};} Wi%A448~Az÷<\c.W1PUg=xBJ@3p ab&]=US=&bMo| 4Q= \o| J8gsV4bP P!*X+w',1$N{ Djr). (Ib;q^/M('54JQ%> Nh?{} in^A/^`?vW7 o/oioh>x4u7R^u+W^?_W/!Iy9pԮ! K^#Eb>ar8.I94~w0A]LpLx>  ֠wBXWg9O=~ws܅z+V|7 ~ܮm|_ݚ|O[ܢwJm n`e: Vhp[}L nP A`|S@@WtAG\ )%X[[- \Ɋk54SҜ 0ZE\Wipʕ\oWdB `yz4ĥ$<]qtMWơԦCfA$:v(s`vM*ʙIJe2L>(/)Pfʌ>(E85~M`T2-Za&k0) D0~2!ƏKRq̪Kq+%q$Kʥ#PpId>(HW &7\JedP!? .C( KJnCSV AVdOW2ax|2<œqaasa+ ρ4 RR ! H nٕD'`[)[qq%Ŧ@JY9ef b40)L1i0P wIf/B@@wQ-?c#_5__&¹ endstream endobj 116 0 obj 9503 endobj 117 0 obj <> endobj 118 0 obj <> stream x]n0 yCEB)Bh841Q~q6ig닝ϵՍw+zdu5 5EYtc͟M[+##g;CX I H򏔏K L\"_Cj?rǁw?͐&I(q?+_Oxa/Ū}ͣa endstream endobj 119 0 obj <> endobj 120 0 obj <> stream xTJwH7  t#tCwwII#)) RҢt J߽z˵ϮPU]"coyښ'#:bԄ89ChH:AL\oE |0''?NqgW+=@AENJ Q ji"O -zc1d!v'濡ߑ;YBڔ`rqq-""g ; - RP'KORacgnjgautKOa,!.n07/xYoK ؙz;;,L`_ pqrz_a@s b cC,^+8A=zbv0?&?JH{Av N}O&9Ǧ=@o_ `_m*ۻ@ _mȸ`wW Tт &NGA5:uz@U.fV ;s jQw>O\+TAgbg~[9 ;x;W?v..] .0c87Ll @W;Z<@;W[?:2dG@@[61 ]ͬ\L(rs൶A,k/ v:ؚX0W?knL<@[] Ct818Y GX9ApruZsg t<@gOy<z~x0;_ y9 8)/h!\_m/;vpSc^8Oyy;k>ϒ(ݕ w5[J>0P>.57p@^gTtE~<խ\@5&6j'ހ'~>;Fw|:Y puz$:@\2\9ATѽ8CA\a&EpBs6gdW5A4^M9*4S|''^l8@{_q- g6mu 6 SHNw :zSz:iV]LntoF@r'  Nw`8u wg@ 4 0Z!|WMҮw'; 7w@@\}")=) jA]<@  jzCMlL/ &Nw‚| NX1mnjg@z?wp O,lAy,?@pNs Nl#No0N,d`;'ۀ9 r'8Cݠw'?QP78wؙUӇ !09w"S_ _k\P΍V{fp W;i%;7K=u]c gPSw]M_K  '?05Wn 翢wZܑK"̃[ؚ8W;&sW{p3`<@s{{P0a݁ނpzlt_ǚw(  g0_="Yft8U'&vw|w~_'%I}u\9`.\\w=,qߕܳsWrwzп{n  }௝~=, u=3|w 3%pxtWtA9;{#{i3W'. x% fX.!D'fbݜKպǜOt4VN )6ajȎߪE@NfDjP?>d 1`N#ƶ#S^=S0J!B.'&v5)qАȴ>}Mf:KF2j8CxJ+OzlQ4 @1evȗ3ׯlBo*?4B) 6s<;EiI5g쇸n2E@ͷkP/ .&粸,!5z5ք)>ȡ̜5eiM iN̳P8Eͭy"@,6Kۇ$ ژO"NBa'RͥYϴBxL<W B 3ʤWUٌ8}VaaKlY#zjh~Nfk|LJ:"S^xx1hn`0fF x7:B_~M7)) $H >QV\(&~AI}aSɊ/) Zސ\B4 0U[bjѴim N&vًQg5rm5o{Nsj[\H?^7z:Wz6-X&BbyN<UΉuI ꪳ/]t-<j󶴰׃oU,&{0 Cnmb,&q'G:H =zG[jHM lHB^n/-,BSӖx f{Cx;iT_L?)!@u !{"*M}#Ss 䴢(^,ԭXTsZhLǤ@|&wzBwh/Rțty$xYzMYھEKd s|gCg+{6I]+k?=9f^mKsy;^R:9o;_3a\hi7!e 84סD"K%Jzɣ/,U](Hoƨ<*2£/ _Ê oHlO,mIc9x@DYأWgH1lb\5kY̥DX0L/0lG|d- %hXhHAŦ͐P0a a;ZD55jF[Yh+ X.FTH|hꊩN誊q[ 5ErA^X7O{kkP%Sb[=s 2J. ;ȿ {@Vĵv̫҂ ⥖qdB.jy0Iޣq>2,f̟F}:kCì09ku@hQȹǣm_ždlSÖB2X]x%V4X䏙R-/r}%u.=|TCLϤ[AEYC.ɔG g iZ=\+9o"`USk՛"L73eX?Ec.RrmRh'[-/Z0s~qNNlQY<`ȡ2̜~"= JQW}caxqm/96@NH4H,V*kS黍aq}qu/gio6O{JU/D3{ϒhY;mŢL[1|ybp!! `T5qIdc&%3ѱ nW.xͩSz3F\WNȮ:A.OzE ٌ}p~ UwwU&p׼]ƣ+ӯ}>BOl]v_ɕ[BUӸ@]΍F8g#OWuI550+i6;=WZ %of1Z]r5-C/襠tVf PN.ǧ.TכQzcX(<=i ̧zcWu0aRr"HW}=Gl43J y5Z=thA9)lA_yQ ~* `ٓn5Mm.~͈P|RX*ԓ#Sq)@Q&1Zi!GFFꂽ9~{7͝5V{UEG"q6<<ن.|J,QO7=';S^;`vr6MKe.r-i.xqB3ݖkC'g>g=!XPaW `YXp "(dFEЊ MaTҙp]8¯ 9zvt!Sωiu; 34t-@lm6\z*FNbzkΛc ᧶] ,Wv*~&h9}j1h8Q!d7PLh#a;8,%>'iJiW{ڇ/b:4[I[CC}ݬ4x5b^^K:.'c3?F7y32x=Bp^6 $R;$pROG:n|sHSA(^?*%.' ^<9оAX[J2X,{?HOXyYVs%}#*5eV앋S[D!wAE~N8j[TYA2j3raڽuV@L(@F!=^p(.;z(ijYՉ2rUS4+P6`֟I8+Cpۼ({ݭms%s&uS" V^Ny̮t0ay"RJsxۃQhӴ ZqccRZ:"ov",M"s'TŔi27eب`YV!~ΈU5tfMDRUxu7T0p2C-/]Q2OUև#V >Q=Hl.?iL 58:ԠAIH¢3!e١ڄ2-8ΚF&n$g%d&[_zs5h4\+? 9Zrg\ 8̻!֛/hI,~ f}!}xXnTWly+Z.Rt'1,yPuN±5+\aED# ȮzMMD*FXIr6-:$? qP_c@et(h`!9uuaa56$MjEvX&ʢ )mTF:mWCPąp͌u)M4!@D uS# QH/w*xbܢYqb=]!ܒw-îZQAҔ㗭hT&r\Ic*0ufs y6Cua㕨 O4>n(f7fgUKH) uu0CzaTyK M5kX"n`v&YYKWH^= eH,?񏮼C~@GZm.}. o԰SRڶ}4hvY˙5\^ p[ pǟ*7wl"?/`Bi sYJX%\9'+J\G#ܨxKu*i; oTIY "kܢsoi;XWAjGn?:>Zn9#?P!3P<G^>(di=1hs?QӀE1Š~0ǂoB3,@Ak{AjkWڊW-lM(hOz8"_{s<CVpE"ic:0mHR6.yJ<$@ -'cy{_]"66yH9)8d~i[' u-mx땮5kOlJ>u)StOsZ{X&&uVsԞbe%{u\G o^xҎZ}n.R+ft"˘|ehhDQ NSJ($Iq7A `3%<8!\JCVaڡcl5ۧ)bZ$gS7ÈIf} f#u `5s[g|Y"dji'ud!",15ѻكr6IƗj%!˷7zţ":,ϮhJ( }/ 䛥F*ioұNzefSdtdk%}"--j;;@׹Qi)vYCoVc/vq3;yʬdsKq2,ek?CCW*ș,}2{^FM`q_>.' CQկڑdNv 2o0\DR孷.D[.&$u ܓcePqWuY 3l:\U:z/imkdΠ6.ctY'wgII85I AT[bay*T㊍ZGf륯0g|7}]//! +qY9#UQLU'D_[!/#n.I2FW_[t-(uªm-.&/-}L$Ey-kZULI[\'WU N+kH^t~k01D]*'`k} )(gWQ(KX;adaOCW)b:XY7Ml0,ChU8g]/Z sɘ_v,Ӏ>+Rg1]>Ɔ2eQQ^,72FϤjQNܫ!H^fzڷlִHqA8콨PG9PtkV:hb!w@24=wm2\feb+AsH@emŐ:jJL"qJޒmQRw3UGB Ux/;w872)7~@ [WYkZ]9EDTdz2sF>աQM{}7HSa=DIyQmz꿼3ʱ/Wq6JUCUf̞\ꓐxR\ctJMՆ>-ۍ4 in,᮲>i>1vR_Xџv0T7K8Om=֪<jwB/0lj~ʕ XG0ľ6=|oQ7Ha䭥y,rJ٥7ivuEH^'$씌B\ .MB#-FTŤu7˱ K=[] J=>69Lo=JqB7a.^"[ Z2ej i\xStq)4"AC:ɑy^M¡=f $0P#u[SκǦ'ƂX3_LpObO2ݭŨu:'5eUM>Y#lL~9g{q>m?H*dI|ItzZ潖4̚uҝ:'(<I_Xq"|3cYppߐA -ׅs9)]vlĵO7_};uiGM;I+BtHDdn1l+wվјP9ڲ!+aw덅aMUfYO~AM PC0O'Yk "%/=#Fu%{I<ܾCukQ+{Ek6"06TIYӂ[i$QF+XTݳ (CHumuC&=_C"[2烎P4ģO0ݩ/ J7Uj|8ê Df6YT$  O:U?'lw*nT*E/EE׈!F>25hG2"NB#4iџ,`֣̓&Ƈ' o "sCp;||#}ĦH.5ɬAiDRۖ52=ڎhս]Y2E^[F2O'W~v$mG@?~qi2~\B#i+T$SL.&GT;e?-4 ϫP/kdo~f+͸h=WA4"+zM|uRB^􃦆̈j3zgo统bk".p==I>];yn%ۈ jo npux*x(գ9c a!om$#%FqD/1,`o?_Tƚ=}VdK]>l(ֆ'%2,F]#5u Aq829*\ʔw쯁IY|VE ^"Ncin71ol)7WlƖLqH,o{gEdy`g ]< a$_dV;+1u0_B%Sl_ڌ:q}5QXSܴo9SaZGg:80}UժoIM$F -W( G4[i9EKeXH}+x]>Mn!)U$tXP15rPLo(wiYɓyr-1ު-!au=LP\AvDM=c;$كa5\ލ8E*gإIU[M"Ci5LwiJiM:  i3XS]ɨ1'mydger-1;$B%~Y6+ԡOt>BaE${Ԓn[~-H/g(nt\0N[njo|"UQOV^RHL%٧WY|+qo:[ '"IvA7ƉSu |yn˗#xfiin:?ًpA܍ss$R)|iqD)Q$ŮV to]vz7xDfLcJRin$;DW=>:p`-LKsg0[֩ q u{=OU1WWh^]k (|:Q%XicL1Ća(W#qx R,? R`uw?.[-p6D,lJLYO UIPz'o"ARj|GLͩV4|Ϊ'a]܉ŮM]=7I52܃ X螮&ÍFj*9/"O:pݧ\2'$#,rꝧ>-KCy\>-F*,+_\,d6urz2:>,ay2" L Vfn D՗#QD<Ѩg]퍩ć~ꡁ BՠьQ=8+9*D7\ (%g+k7Dij Xi1~T~lmD;}|dMi;>0Už(A@;s]{FD Z)۸_y< R#~*ǯh~Pꕥ.8(\fs-+JCè"0o^ݨMk<!?z_8M|W3xU/0=¡O}J}`D-A&Me/C)tV}N y+ڍ(tTiHOz[Z)I q Lk?ͮcTCwJ&&uafe\5_,P4@*YDmR?}k+{0j7+e%YW~ h7V jio*H/]AL& Q\tdJN+WUBnRmpyք,9Gnˀ] KǠӃ8q͛GaC +H[($r-ۈ $Z0Xn&6V1̓'nڪUyPsL&x3KWTBGFՏMy<绍 ]ƈ7O>6ʕ%[, "'\=5OK*yv \%uecA%D&N0._?۟ %`&տKu|Vlݿr1u쓲ceǫ;9;oН:Tkrd~b)Q[=U}8M++E|#6H*ChT }hy| k rGm/=_[d}Ԥ՗skFGc}Tؐ٤ra7!`g4Ĵ~H(IlzF2kU zUr3k@O(#@U ED_u:Iٜ\YBUD`^dO8gJ 3^Anu sBB+6 RtSaX~5X\\C&b Mю$F .z~:HO.8 t,(b ̙%4Zۣ矹jPM;aYWM@d|eQYM aNG|B@rxz*՚+INhk g@KL9Csnԙkln̔3M #{QOɛ̌rxrRLOZ17FQSYcf视Gl6~Ebed 1!6-0YvժKla ~gΫ xf!ѯQw6M@ҹN=t@VJ]B`m} .=ԌS٬e(uz lj}E;DN0k(/I߉D䙘Y4~_ z:h2*Q'0E^/;F 7}.ftr{`$!~~R º򁋁U}th ltϭvq+I:1)#YDݹɍ<p ̘לْ56(c/2\Zߎ)I7j9Ts89vDRmvʡN:Q_Fhl*p<܉Nd-U|L$Q3Yx$6l_Կ-tJE~& KygYo7/ol[Dh95.>[ †YѬ g*@s=YJO|`5tPq?UN$9ڵ5hb! ̮Azj%O_aHd"[:*Kt4⇆Pb.>xG;>`N3WTL:BWg8aT*v|r= )>c9G! 'V њT0Rc/6ҟR(Ao e2wP_sf>g F盽:C!ЋǔP}̝%.2ҥq6ee35a&_ }ڼ']gpmM{VGyC7Nzl2bƴ4 䯩{ TIk)Bjϟœ\^E:أObpe&- #YT~uuWǣ=,1qmFYA@7߄XqŷvTtޢ\[$7y/ʼo0R +"%s~:2 awEcU>W/IO7gdFk!l>l&~>̎܁ ?ϱQhR敕;=o{t?#5 ڛ!%jٹAR-0[ @ZS>Ĉ#Ujq w&E!£a,JcU?"';%SS W7v`_b)+T)3W|))D@&ơ)Tl OӘtgxOL4uuR>m,-R6ЍYHJgڠ}ȡ!^u$NU ö!iZ&w^=AGi)|௢D4RaH5uuaމÈCR)'r;*VXaփEy!ElQ -U.>*2+a*2{ >gh=IT 4H? D0j~e3VaȶUx4ɕXn&S8/ѼdND(0-yw|]3q9$=Tg3_m4,_94蟃nTJ/0J"FwV&<;aP'ߋdLx>=iAL5𚁻l!nc GDs$F"^|z3U?@&[;nX\Vj)(D8I7gn6gv] 1k7-1qr 2Kq -9VHX|ھOj\R*K ݻ[Ӑ<=q5Uj 9:1LP+!Km#+)4)IàiJ^&0kOݜf~g6g "|Y䈸}w4%5unO4V31R2_)zO:9X^:dVZ"`;H.$ag{0m*-r#^ΌP<Tc8+e?@zO/y,t oOjkPXyHn:5L^}= :}VL*X?Dr]ԈshDkk|\W!5/5K5܊2 jkpAAUSUsA PeO>ꖕ]t0BA%Piha'fAJeHiFaΤ^{IԴu(Pb&QYqDٚ{6r&P9n>EjѮBca㙙 Ўs%0tY$Q"$Lt1Ԓck;,>dGT'N_gs!R8y:%1 ѳ~7y.9k2רw OU@0.BN#p[~9{ׯ`]]CY##u³ZԉhW)zLЋC't p17C>\HJ%8=J'cfs^ȶ%J)]їQuG6dIydI}a%o#˻d.KodnqܝJؼ"qif9FRķloxlEF"A_ᴜckSMh!L0qf&"SY1YG9Grmu5y⸈l[B%r'%؊` u2H`(Ux4(AoI݁z>drrlL 6Bw<3o>İW>'TfE_bcrHcR18<Xx4{lk}i^$N_U!S~@E35<2i9?L 1OFr t1՞O4Ee8/ku!ݼT;4ekDp\k9ՐC! ߒͤ`[:U4ҁ?B<-'ץTLbdV z{ς$c?U]$2s,a;wMrE1K :{ k qtH%k,0-t:p)tOrZ9S M+YKTe;A(gZx/ =[>$ zNo]q">uz\2wqAF*ԍN蠓d!ae9C{ֲR/uq#ɞ7VgNqiQz7Lf$URa2 A<>fΊIRKwMj4cl0m3WS=K% L ELJ0N]S1#QȲzNF y=zmgVLWܒ~RTrgpG؋f3QZKSI }c|-F/jD^a*jI|Zvp& [#հů[ NS$j! S r7ҵ)7P4C4nF3t&C+QrD>لxպ?'GAJhy6:U-bl9Lehx4 :of!ݸDm~Y~a6йgP(5ƆM:AdJDv M 4s $! [/XIiP>u<`jɯՅDSfTk9ȋH#6O 3r@Jx!ɡ"0v~_!r>ᓺ6Fu}ʊoWbrdUuFAXq]|ԍzDkDzg`Wj2]ýIvV&~?y?cF|si>jo5Ft] 5 3S5XI?] p0C`ͫ=vd+ˌAIO.zϷSck2#',DD,c,ɕA28A6BE@|a- Ёx 'gH3eP+ufX0K[`2 kLcke/Wy|XΆ>HF+w<5  BOZ n5H@Ĩo lݜ_j釗.N?'c8 ?!~'v@J9}[P*j.$#촨uc < 'rۗ;ϻG- I>-7FzrlkY/K%@R{wn-ᫎnG&6A,mUYf0&KM"mlCR(~Tvt!. ԋKR7P._t1gG$m$/L&cAP!j(';_'"=wZ 4;"3y7U;#pv \e2t@V !-N)wO=$VAx3g]4;+~o ͤxT@Rc,LF͉ioq#OhX{(Z+#Լ~eW Ib,F*Cَ.lpjBVh0h 08n߀u ^ &_|]3o]8f g|gCt1;:V.+v8;F4mn 1\2^#n@MK2 ^vt4Bo"NPwkrn%\]3L|6N= Y?VW%]L*Wr)׻in~fbEYIW{ n ?~@]L"ʡ7j$&S7mPm)0`|.PMzZHy,A. v Aeo}(3\V.h.&8'99e#B&D,JŖĀ| ̧l]2l9AƊ஗/-Zw[Ttrq- 2H') LRHUczϰBF #3 3JDеqYlrK1ޥ 3 A-؎Ix\r/k0^YR)t.D,{኎)wҼ2fJvSqIIkt"+BR1D yҷfd}N"rx,w棺A)gͻk Oɟl{5k:be„B ? N6u NG1̛ZG?O\5s)5@T~F!&S;I&d$Őp6G"^RbϦpO*NJH;aPPopBq0n.";r.9qAp]* ^1~i!"Z ԁDYֳfCaFjJPY] 3"RyAlEC`AB.aq1}4 2JP&VIPPh/ELJ3hjS59PF;DhtEU C0:K\_%e#j(uZG(*"Ղ M̽"^qK?pP8֨caAVٱbu#W"r':HG(EetG(ł:$*}R/D;i}( k͜Ō"[N_nB'"sp~ji4^%znL0{^4 ~8Q',~۹Jo]$E)Ey|WTvJ# =eb;׉}ގ6}r+C2? -di ĝ,y8AKrs&zhjI%FVB>yJ9Jm'ɽUoZG=L-& d&t}Q_o"s.(gǫ$1 g"7;L{{nW)n($㉶,N:8\ӸPL 2 8гcy1mOOmp=!/gG~+ OLH10w j]-R9.<5 t?`B8ieBkꞈD:;^Պ{`VHoq5XcHU pz)i3P-X4&(&QSNِTl ."i~ n_$'کq}l(Nɖt 3/V}i/.im-9a~pR4 p VhS\P ɊkKR&PjkY`Sch#&Ш]Xmd]RD|9đO +y lIc JC* Si2RREL@('rbTyQ#2{s|Bɹ}Tb]Lq:n4S{, Z<=&C01Rl Z"7Jf>JF_hBvC\߶8HᑃKBgj--97cyιoakE )@KV$nr 2|БZb{;r[s咗M w0L mO,Ү~-ƙEL"aa8 /J:ɰ~O(%N=еY [`4Ftnoגv<[&G} r%21H`!v)lL} #pT$ Lc{}"eMZl{Wn + 4%rVN绥oΒ_ ng/~*&_YPPqc`FH\Q,Qt$eT^kGgq*Rc"{9*%:Eɍ%b) Fom?{6$@ ;PZMm1]Da+v#D*ԲdO-3 *u. ͥ k1. d5RDAIAV- Mo/ᤏÿ9tSX~ Z\jdqbe~b8QQ飼%2L:O9lJ+ PbQ;hHT«`r}h~7Yf!0OK.J$ڔ{>>lęK#\E֢+<EkpM5,5yvT˗!WzA"Jʙ30̅2=Iٿ {OơNV~//tdt;"Gx59Tx#䭉."it_+2v`. BHQ0\' 3-)~hJws%l/6_GԘq1xb)[t]ަ0:yKǖ ^G{R>\oAm6YDL `?&s G*]ѐO̸ E~}v1],ٶFEcBa':MXWp0Ԯ~N w b|VSaנY$dq|%0nL`'a(vcYtIm.-8mbV,sGz?"ډhVS~PPHHm u>%o|;a/hysCʆ ](o5]nGL\ЇQY#9 nʕWg==1LoJI ay=5QXƪJi׸0Yܽl_Ӝ(e[լD-D/1}?Nj!nS! 9_E6/# &=[\Yp1!Rp{i>F>3Ag [e6ѺWX=b\R|Q  ~?"BZ,bEx|Cz8&pL\aHkq|ԧf7n W&"痁obV]!5E>EWJߙ{ OJjAE-Ogtr\6?pS7mH]4 BTYWO`X_VBьXeJ5 W? [)|bbβhHSqf Nm`!/RO 0wj.Y/>n2&s?oys :CKIhX(PfƌWXNBn?ho#5|m^z`CܽʕeE\bysG }QAxvf,@'? auW4zWGW(b,Q 8ߦ 0eRPppP 빨)% ǃK .]BD kbh!@j ^,~Cl#Նr )]E{7,nwlݍ,Lmiv,%8}xu/U%BR#^|wʻ4Cz5d? bV#sveQx RP,̕=( <=ҁϵwfq?-ݹ0uX+h̞= s~vꤗGq~[#jTɀI7Xo?UO5d]"iWbqXV)'ϐD=T6v\h!z.JG YlkJ~ ?NS)u+[Elΐ#eSe5̿ .ZYϣEɭJ-FOճ9\?yy yz%]U_31y<F 9 c2|F,t.B' &,P]MoΝh` RQg;d '0L9>8HS쨜>}ʳtCܙXՖFy" =N/+lxgwi \@!*6 XN˒E1$AqgnAi6$faz=:;jR Tت1pYpų)¼D^PgSO\ Nst mp|yXQ…`Ә50L` s/AS1Ԥ3bjF/MGjI"V tü˝X:D*TfusϑU׿a]z@F%R`h725qHi W>rfkFeLiZ:P^E^`{Cmr gXrT3WN1qOLx⏁vj8Xh!8!<Ǜid >kN`~Lf",#^YKl=M$vemj)HSo0UAx_7“z.&fe|8>$a!̖'b-8LTnhճW2 %a'0f8d?M2J/hťh"bOuéogCe̡.NV;ǔXג\g\Sd֋[N{~˦gIH GYs)L=vi#Y,!R/sSmt𻂅"FQ(v{kh%crQ0}K+r׮TE پ՞E@aIuSX,GL瀁u' .5N@3tw)u(i ’vjKf-8rȔ>zTy5O Dpk },5c3aפ!3j<3Pi{Y% nVʋ% 7M+Q9pĄ7ΟXGwVJL(3WH)~rAL+ vdeT1Sj醨C6 ز,dZTTg2[l#Me-f*6FUg!b C.Aօǎ џ}uVˤ%CR[p`WM,ϙlބ}/ϬvF($VoŴ ̕{d6]t|:@@Q1`6+CN}{U᠐^Ol^)U N> '=3h_j爿v&?bZ4AG[ mlxJ2q"Gn9ŝ7jmcRS0 KF S6ƏNEEp$$gTG9bboL5(X`kE&`*/1~p\%P&28 K bÍ-JA7-Ew[j0z2$b]*GFyt(܊^k~DD)WZ21O&њc,6+~f+C . IT2Ā⪽jփ@q8u~Q)nؐC$}VԢ5u0z:ܧYwcM!w7PkPk^ϲMRP;6(.D*ތyxVagJMwQ"w[<@=/i'~K.^0){U4 H"BR1شPgmdo8e[#:A_ӾҷnCk.-LCcuEC'zԜEGAA9ʢkoDjj‹`soYOU4'H^S鹂9UPa$ܙ#= @2g +m˔q4KmŜHق{֟MG-d& \(;S:̈' -kwW 7=I,_1・e~fϼ- t~#Y>q¶w+፺.>N֑N0~uӗ +F?̷%Lkܞ?o8>F ނ;kpWoPmj U 82A}u!Ssjc@3>/ȆC*VijFϑAX+&67v]v6$Ή|r`X\]Z-6( pD73kc4, l|r=K[ 2 *]L4涡? VbҜ }pj\^=MrOLجȖC ]tj^i Vla~Bmm˂a=$N|_ agy]~%k vѻ11Z;Yy.\I={̜-U@ =+ۥ} U8hU\q+]-K}ihRWu~YyW( ε'bީPÊv#JuBIpQnc/{8U*-47MZ6q{/u}G~ dM壇:gݫu/PzȢ>3X( H~8go'qtR#FaB(;|kg(]4-u[b99n13RҧȚHymxj TRzy9>gJ mpO%`4yveiNj) '䵤c-۽`Ou4  B9%h;No(KKJ<+%X=7C&Ţ&#{,΁Um.eaeu'%mY4?V*t[7>+е׃/ q_5blBHSX[U۝ F=oknUfl%O`YE !lWdٱ("ONur)jVvH3rک_8 grvPuY%/:9U /,w?iLe?,fS.,=r:Gq8 ?8WsꭀEG_7Z͛sэ YtdXO l6tŒ-I+[, ?\iPqQِj hbLs5غt=s`ܡ:in 9-6w SnSD-k`JLzj5=Ntӭ m]YniRq9YɶLRٗ6|av__63o89-܍keG}u4{Vv4`‘$ }*Vë;N* ^RS'|\X`k$ ][c"gG* U ~|̥~_//WU_> endobj 125 0 obj <> endobj 126 0 obj <> /ProcSet[/PDF/Text/ImageC/ImageI/ImageB] >> endobj 1 0 obj <>/Contents 2 0 R>> endobj 4 0 obj <>/Contents 5 0 R>> endobj 7 0 obj <>/Contents 8 0 R>> endobj 10 0 obj <>/Contents 11 0 R>> endobj 14 0 obj <>/Contents 15 0 R>> endobj 17 0 obj <>/Contents 18 0 R>> endobj 21 0 obj <>/Contents 22 0 R>> endobj 24 0 obj <>/Contents 25 0 R>> endobj 27 0 obj <>/Contents 28 0 R>> endobj 30 0 obj <>/Contents 31 0 R>> endobj 33 0 obj <>/Contents 34 0 R>> endobj 36 0 obj <>/Contents 37 0 R>> endobj 39 0 obj <>/Contents 40 0 R>> endobj 42 0 obj <>/Contents 43 0 R>> endobj 45 0 obj <>/Contents 46 0 R>> endobj 48 0 obj <>/Contents 49 0 R>> endobj 51 0 obj <>/Contents 52 0 R>> endobj 54 0 obj <>/Contents 55 0 R>> endobj 57 0 obj <>/Contents 58 0 R>> endobj 60 0 obj <>/Contents 61 0 R>> endobj 63 0 obj <>/Contents 64 0 R>> endobj 66 0 obj <>/Contents 67 0 R>> endobj 69 0 obj <>/Contents 70 0 R>> endobj 72 0 obj <>/Contents 73 0 R>> endobj 75 0 obj <>/Contents 76 0 R>> endobj 78 0 obj <>/Contents 79 0 R>> endobj 81 0 obj <>/Contents 82 0 R>> endobj 84 0 obj <>/Contents 85 0 R>> endobj 87 0 obj <>/Contents 88 0 R>> endobj 90 0 obj <>/Contents 91 0 R>> endobj 127 0 obj <> endobj 128 0 obj < /Dest[1 0 R/XYZ 74.7 697.1 0]/Parent 127 0 R/Next 129 0 R>> endobj 129 0 obj < /Dest[4 0 R/XYZ 74.7 537.2 0]/Parent 127 0 R/Prev 128 0 R/Next 130 0 R>> endobj 130 0 obj < /Dest[7 0 R/XYZ 74.7 785.3 0]/Parent 127 0 R/Prev 129 0 R/Next 135 0 R>> endobj 131 0 obj < /Dest[7 0 R/XYZ 74.7 558.4 0]/Parent 130 0 R/Next 132 0 R>> endobj 132 0 obj < /Dest[7 0 R/XYZ 74.7 321.4 0]/Parent 130 0 R/Prev 131 0 R/Next 133 0 R>> endobj 133 0 obj < /Dest[10 0 R/XYZ 74.7 427.9 0]/Parent 130 0 R/Prev 132 0 R/Next 134 0 R>> endobj 134 0 obj < /Dest[10 0 R/XYZ 74.7 190.9 0]/Parent 130 0 R/Prev 133 0 R>> endobj 135 0 obj < /Dest[17 0 R/XYZ 74.7 697.1 0]/Parent 127 0 R/Prev 130 0 R/Next 136 0 R>> endobj 136 0 obj < /Dest[21 0 R/XYZ 74.7 511.2 0]/Parent 127 0 R/Prev 135 0 R/Next 137 0 R>> endobj 137 0 obj < /Dest[24 0 R/XYZ 74.7 747.3 0]/Parent 127 0 R/Prev 136 0 R/Next 142 0 R>> endobj 138 0 obj < /Dest[24 0 R/XYZ 74.7 520.4 0]/Parent 137 0 R/Next 139 0 R>> endobj 139 0 obj < /Dest[24 0 R/XYZ 74.7 283.4 0]/Parent 137 0 R/Prev 138 0 R/Next 140 0 R>> endobj 140 0 obj < /Dest[27 0 R/XYZ 74.7 402 0]/Parent 137 0 R/Prev 139 0 R/Next 141 0 R>> endobj 141 0 obj < /Dest[27 0 R/XYZ 74.7 165 0]/Parent 137 0 R/Prev 140 0 R>> endobj 142 0 obj < /Dest[33 0 R/XYZ 74.7 697.1 0]/Parent 127 0 R/Prev 137 0 R/Next 143 0 R>> endobj 143 0 obj < /Dest[36 0 R/XYZ 74.7 537.2 0]/Parent 127 0 R/Prev 142 0 R/Next 144 0 R>> endobj 144 0 obj < /Dest[39 0 R/XYZ 74.7 785.3 0]/Parent 127 0 R/Prev 143 0 R/Next 149 0 R>> endobj 145 0 obj < /Dest[39 0 R/XYZ 74.7 558.4 0]/Parent 144 0 R/Next 146 0 R>> endobj 146 0 obj < /Dest[39 0 R/XYZ 74.7 321.4 0]/Parent 144 0 R/Prev 145 0 R/Next 147 0 R>> endobj 147 0 obj < /Dest[42 0 R/XYZ 74.7 427.9 0]/Parent 144 0 R/Prev 146 0 R/Next 148 0 R>> endobj 148 0 obj < /Dest[42 0 R/XYZ 74.7 190.9 0]/Parent 144 0 R/Prev 147 0 R>> endobj 149 0 obj < /Dest[48 0 R/XYZ 74.7 697.1 0]/Parent 127 0 R/Prev 144 0 R/Next 150 0 R>> endobj 150 0 obj < /Dest[51 0 R/XYZ 74.7 511.2 0]/Parent 127 0 R/Prev 149 0 R/Next 151 0 R>> endobj 151 0 obj < /Dest[54 0 R/XYZ 74.7 747.3 0]/Parent 127 0 R/Prev 150 0 R/Next 156 0 R>> endobj 152 0 obj < /Dest[54 0 R/XYZ 74.7 520.4 0]/Parent 151 0 R/Next 153 0 R>> endobj 153 0 obj < /Dest[54 0 R/XYZ 74.7 283.4 0]/Parent 151 0 R/Prev 152 0 R/Next 154 0 R>> endobj 154 0 obj < /Dest[57 0 R/XYZ 74.7 402 0]/Parent 151 0 R/Prev 153 0 R/Next 155 0 R>> endobj 155 0 obj < /Dest[57 0 R/XYZ 74.7 165 0]/Parent 151 0 R/Prev 154 0 R>> endobj 156 0 obj < /Dest[63 0 R/XYZ 74.7 697.1 0]/Parent 127 0 R/Prev 151 0 R/Next 157 0 R>> endobj 157 0 obj < /Dest[66 0 R/XYZ 74.7 537.2 0]/Parent 127 0 R/Prev 156 0 R/Next 158 0 R>> endobj 158 0 obj < /Dest[69 0 R/XYZ 74.7 785.3 0]/Parent 127 0 R/Prev 157 0 R/Next 163 0 R>> endobj 159 0 obj < /Dest[69 0 R/XYZ 74.7 558.4 0]/Parent 158 0 R/Next 160 0 R>> endobj 160 0 obj < /Dest[69 0 R/XYZ 74.7 321.4 0]/Parent 158 0 R/Prev 159 0 R/Next 161 0 R>> endobj 161 0 obj < /Dest[72 0 R/XYZ 74.7 427.9 0]/Parent 158 0 R/Prev 160 0 R/Next 162 0 R>> endobj 162 0 obj < /Dest[72 0 R/XYZ 74.7 190.9 0]/Parent 158 0 R/Prev 161 0 R>> endobj 163 0 obj < /Dest[78 0 R/XYZ 74.7 697.1 0]/Parent 127 0 R/Prev 158 0 R/Next 164 0 R>> endobj 164 0 obj < /Dest[81 0 R/XYZ 74.7 537.2 0]/Parent 127 0 R/Prev 163 0 R/Next 165 0 R>> endobj 165 0 obj < /Dest[84 0 R/XYZ 74.7 785.3 0]/Parent 127 0 R/Prev 164 0 R>> endobj 166 0 obj < /Dest[84 0 R/XYZ 74.7 558.4 0]/Parent 165 0 R/Next 167 0 R>> endobj 167 0 obj < /Dest[84 0 R/XYZ 74.7 321.4 0]/Parent 165 0 R/Prev 166 0 R/Next 168 0 R>> endobj 168 0 obj < /Dest[87 0 R/XYZ 74.7 427.9 0]/Parent 165 0 R/Prev 167 0 R/Next 169 0 R>> endobj 169 0 obj < /Dest[87 0 R/XYZ 74.7 190.9 0]/Parent 165 0 R/Prev 168 0 R>> endobj 99 0 obj <> endobj 93 0 obj <> >> endobj 94 0 obj <> >> endobj 95 0 obj <> >> endobj 96 0 obj <> >> endobj 97 0 obj <> >> endobj 98 0 obj <> >> endobj 170 0 obj <> endobj 171 0 obj < /Producer /CreationDate(D:20170811232209+02'00')>> endobj xref 0 172 0000000000 65535 f 0001017088 00000 n 0000000019 00000 n 0000002806 00000 n 0001017251 00000 n 0000002827 00000 n 0000006544 00000 n 0001017396 00000 n 0000006565 00000 n 0000010276 00000 n 0001017541 00000 n 0000010297 00000 n 0000012337 00000 n 0000012359 00000 n 0001017688 00000 n 0000818759 00000 n 0000820107 00000 n 0001017835 00000 n 0000820129 00000 n 0000823041 00000 n 0000823063 00000 n 0001018000 00000 n 0000873985 00000 n 0000877706 00000 n 0001018147 00000 n 0000877728 00000 n 0000881447 00000 n 0001018294 00000 n 0000881469 00000 n 0000883509 00000 n 0001018441 00000 n 0000883531 00000 n 0000884879 00000 n 0001018588 00000 n 0000884901 00000 n 0000887690 00000 n 0001018753 00000 n 0000887712 00000 n 0000891431 00000 n 0001018900 00000 n 0000891453 00000 n 0000895166 00000 n 0001019047 00000 n 0000895188 00000 n 0000897228 00000 n 0001019194 00000 n 0000897250 00000 n 0000898598 00000 n 0001019341 00000 n 0000898620 00000 n 0000901532 00000 n 0001019506 00000 n 0000901554 00000 n 0000905275 00000 n 0001019653 00000 n 0000905297 00000 n 0000909016 00000 n 0001019800 00000 n 0000909038 00000 n 0000911078 00000 n 0001019947 00000 n 0000911100 00000 n 0000912448 00000 n 0001020094 00000 n 0000912470 00000 n 0000915259 00000 n 0001020259 00000 n 0000915281 00000 n 0000919000 00000 n 0001020406 00000 n 0000919022 00000 n 0000922735 00000 n 0001020553 00000 n 0000922757 00000 n 0000924797 00000 n 0001020700 00000 n 0000924819 00000 n 0000926167 00000 n 0001020847 00000 n 0000926189 00000 n 0000928978 00000 n 0001021012 00000 n 0000929000 00000 n 0000932719 00000 n 0001021159 00000 n 0000932741 00000 n 0000936454 00000 n 0001021306 00000 n 0000936476 00000 n 0000938516 00000 n 0001021453 00000 n 0000938538 00000 n 0000939886 00000 n 0001037278 00000 n 0001037434 00000 n 0001037590 00000 n 0001037746 00000 n 0001037902 00000 n 0001038058 00000 n 0001036975 00000 n 0000939908 00000 n 0000947314 00000 n 0000947337 00000 n 0000947535 00000 n 0000947902 00000 n 0000948131 00000 n 0000959742 00000 n 0000959766 00000 n 0000959963 00000 n 0000960461 00000 n 0000960798 00000 n 0000968411 00000 n 0000968434 00000 n 0000968639 00000 n 0000969050 00000 n 0000969324 00000 n 0000978916 00000 n 0000978939 00000 n 0000979141 00000 n 0000979544 00000 n 0000979807 00000 n 0001014641 00000 n 0001014665 00000 n 0001014850 00000 n 0001015916 00000 n 0001016894 00000 n 0001016973 00000 n 0001021600 00000 n 0001021660 00000 n 0001022077 00000 n 0001022411 00000 n 0001022777 00000 n 0001023166 00000 n 0001023488 00000 n 0001023891 00000 n 0001024201 00000 n 0001024632 00000 n 0001024967 00000 n 0001025334 00000 n 0001025724 00000 n 0001026047 00000 n 0001026448 00000 n 0001026756 00000 n 0001027187 00000 n 0001027522 00000 n 0001027889 00000 n 0001028279 00000 n 0001028602 00000 n 0001029005 00000 n 0001029315 00000 n 0001029746 00000 n 0001030081 00000 n 0001030448 00000 n 0001030838 00000 n 0001031161 00000 n 0001031562 00000 n 0001031870 00000 n 0001032301 00000 n 0001032636 00000 n 0001033003 00000 n 0001033393 00000 n 0001033716 00000 n 0001034119 00000 n 0001034429 00000 n 0001034860 00000 n 0001035195 00000 n 0001035549 00000 n 0001035939 00000 n 0001036262 00000 n 0001036665 00000 n 0001038214 00000 n 0001038331 00000 n trailer < <79A92E1D34F84EE145CEEC510418B227> ] /DocChecksum /BE7087345AA5039AF5A4689409BE444B >> startxref 1038507 %%EOF zip4j-2.2.8/src/test/resources/test-files/sample.pdf000066400000000000000000000054161357650161600224160ustar00rootroot00000000000000%PDF-1.3 % 1 0 obj << /Type /Catalog /Outlines 2 0 R /Pages 3 0 R >> endobj 2 0 obj << /Type /Outlines /Count 0 >> endobj 3 0 obj << /Type /Pages /Count 2 /Kids [ 4 0 R 6 0 R ] >> endobj 4 0 obj << /Type /Page /Parent 3 0 R /Resources << /Font << /F1 9 0 R >> /ProcSet 8 0 R >> /MediaBox [0 0 612.0000 792.0000] /Contents 5 0 R >> endobj 5 0 obj << /Length 1074 >> stream 2 J BT 0 0 0 rg /F1 0027 Tf 57.3750 722.2800 Td ( A Simple PDF File ) Tj ET BT /F1 0010 Tf 69.2500 688.6080 Td ( This is a small demonstration .pdf file - ) Tj ET BT /F1 0010 Tf 69.2500 664.7040 Td ( just for use in the Virtual Mechanics tutorials. More text. And more ) Tj ET BT /F1 0010 Tf 69.2500 652.7520 Td ( text. And more text. And more text. And more text. ) Tj ET BT /F1 0010 Tf 69.2500 628.8480 Td ( And more text. And more text. And more text. And more text. And more ) Tj ET BT /F1 0010 Tf 69.2500 616.8960 Td ( text. And more text. Boring, zzzzz. And more text. And more text. And ) Tj ET BT /F1 0010 Tf 69.2500 604.9440 Td ( more text. And more text. And more text. And more text. And more text. ) Tj ET BT /F1 0010 Tf 69.2500 592.9920 Td ( And more text. And more text. ) Tj ET BT /F1 0010 Tf 69.2500 569.0880 Td ( And more text. And more text. And more text. And more text. And more ) Tj ET BT /F1 0010 Tf 69.2500 557.1360 Td ( text. And more text. And more text. Even more. Continued on page 2 ...) Tj ET endstream endobj 6 0 obj << /Type /Page /Parent 3 0 R /Resources << /Font << /F1 9 0 R >> /ProcSet 8 0 R >> /MediaBox [0 0 612.0000 792.0000] /Contents 7 0 R >> endobj 7 0 obj << /Length 676 >> stream 2 J BT 0 0 0 rg /F1 0027 Tf 57.3750 722.2800 Td ( Simple PDF File 2 ) Tj ET BT /F1 0010 Tf 69.2500 688.6080 Td ( ...continued from page 1. Yet more text. And more text. And more text. ) Tj ET BT /F1 0010 Tf 69.2500 676.6560 Td ( And more text. And more text. And more text. And more text. And more ) Tj ET BT /F1 0010 Tf 69.2500 664.7040 Td ( text. Oh, how boring typing this stuff. But not as boring as watching ) Tj ET BT /F1 0010 Tf 69.2500 652.7520 Td ( paint dry. And more text. And more text. And more text. And more text. ) Tj ET BT /F1 0010 Tf 69.2500 640.8000 Td ( Boring. More, a little more text. The end, and just as well. ) Tj ET endstream endobj 8 0 obj [/PDF /Text] endobj 9 0 obj << /Type /Font /Subtype /Type1 /Name /F1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >> endobj 10 0 obj << /Creator (Rave \(http://www.nevrona.com/rave\)) /Producer (Nevrona Designs) /CreationDate (D:20060301072826) >> endobj xref 0 11 0000000000 65535 f 0000000019 00000 n 0000000093 00000 n 0000000147 00000 n 0000000222 00000 n 0000000390 00000 n 0000001522 00000 n 0000001690 00000 n 0000002423 00000 n 0000002456 00000 n 0000002574 00000 n trailer << /Size 11 /Root 1 0 R /Info 10 0 R >> startxref 2714 %%EOF zip4j-2.2.8/src/test/resources/test-files/sample_directory/000077500000000000000000000000001357650161600240015ustar00rootroot00000000000000zip4j-2.2.8/src/test/resources/test-files/sample_directory/favicon.ico000066400000000000000000000021761357650161600261300ustar00rootroot00000000000000 h(  H""" """絵L### ###  NNN===OOOPPPPPPQQQPPPQQQ###SSScccTTTVVVVVV777777------纺JIIIIIIBzip4j-2.2.8/src/test/resources/test-files/sample_text1.txt000066400000000000000000000007011357650161600236010ustar00rootroot00000000000000"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." zip4j-2.2.8/src/test/resources/test-files/sample_text_large.txt000066400000000000000000003044751357650161600247110ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at ligula. Fusce fermentum. Nullam cursus lacinia erat. Praesent blandit laoreet nibh. Fusce convallis metus id felis luctus adipiscing. Pellentesque egestas, neque sit amet convallis pulvinar, justo nulla eleifend augue, ac auctor orci leo non est. Quisque id mi. Ut tincidunt tincidunt erat. Etiam feugiat lorem non metus. Vestibulum dapibus nunc ac augue. Curabitur vestibulum aliquam leo. Praesent egestas neque eu enim. In hac habitasse platea dictumst. Fusce a quam. Etiam ut purus mattis mauris sodales aliquam. Curabitur nisi. Quisque malesuada placerat nisl. Nam ipsum risus, rutrum vitae, vestibulum eu, molestie vel, lacus. Sed augue ipsum, egestas nec, vestibulum et, malesuada adipiscing, dui. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, vitae euismod ligula urna in dolor. Mauris sollicitudin fermentum libero. Praesent nonummy mi in odio. Nunc interdum lacus sit amet orci. Vestibulum rutrum, mi nec elementum vehicula, eros quam gravida nisl, id fringilla neque ante vel mi. Morbi mollis tellus ac sapien. Phasellus volutpat, metus eget egestas mollis, lacus lacus blandit dui, id egestas quam mauris ut lacus. Fusce vel dui. Sed in libero ut nibh placerat accumsan. Proin faucibus arcu quis ante. In consectetuer turpis ut velit. Nulla sit amet est. Praesent metus tellus, elementum eu, semper a, adipiscing nec, purus. Cras risus ipsum, faucibus ut, ullamcorper id, varius ac, leo. Suspendisse feugiat. Suspendisse enim turpis, dictum sed, iaculis a, condimentum nec, nisi. Praesent nec nisl a purus blandit viverra. Praesent ac massa at ligula laoreet iaculis. Nulla neque dolor, sagittis eget, iaculis quis, molestie non, velit. Mauris turpis nunc, blandit et, volutpat molestie, porta ut, ligula. Fusce pharetra convallis urna. Quisque ut nisi. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Suspendisse non nisl sit amet velit hendrerit rutrum. Ut leo. Ut a nisl id ante tempus hendrerit. Proin pretium, leo ac pellentesque mollis, felis nunc ultrices eros, sed gravida augue augue mollis justo. Suspendisse eu ligula. Nulla facilisi. Donec id justo. Praesent porttitor, nulla vitae posuere iaculis, arcu nisl dignissim dolor, a pretium mi sem ut ipsum. Curabitur suscipit suscipit tellus. Praesent vestibulum dapibus nibh. Etiam iaculis nunc ac metus. Ut id nisl quis enim dignissim sagittis. Etiam sollicitudin, ipsum eu pulvinar rutrum, tellus ipsum laoreet sapien, quis venenatis ante odio sit amet eros. Proin magna. Duis vel nibh at velit scelerisque suscipit. Curabitur turpis. Vestibulum suscipit nulla quis orci. Fusce ac felis sit amet ligula pharetra condimentum. Maecenas egestas arcu quis ligula mattis placerat. Duis lobortis massa imperdiet quam. Suspendisse potenti. Pellentesque commodo eros a enim. Vestibulum turpis sem, aliquet eget, lobortis pellentesque, rutrum eu, nisl. Sed libero. Aliquam erat volutpat. Etiam vitae tortor. Morbi vestibulum volutpat enim. Aliquam eu nunc. Nunc sed turpis. Sed mollis, eros et ultrices tempus, mauris ipsum aliquam libero, non adipiscing dolor urna a orci. Nulla porta dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Pellentesque dapibus hendrerit tortor. Praesent egestas tristique nibh. Sed a libero. Cras varius. Donec vitae orci sed dolor rutrum auctor. Fusce egestas elit eget lorem. Suspendisse nisl elit, rhoncus eget, elementum ac, condimentum eget, diam. Nam at tortor in tellus interdum sagittis. Aliquam lobortis. Donec orci lectus, aliquam ut, faucibus non, euismod id, nulla. Curabitur blandit mollis lacus. Nam adipiscing. Vestibulum eu odio. Vivamus laoreet. Nullam tincidunt adipiscing enim. Phasellus tempus. Proin viverra, ligula sit amet ultrices semper, ligula arcu tristique sapien, a accumsan nisi mauris ac eros. Fusce neque. Suspendisse faucibus, nunc et pellentesque egestas, lacus ante convallis tellus, vitae iaculis lacus elit id tortor. Vivamus aliquet elit ac nisl. Fusce fermentum odio nec arcu. Vivamus euismod mauris. In ut quam vitae odio lacinia tincidunt. Praesent ut ligula non mi varius sagittis. Cras sagittis. Praesent ac sem eget est egestas volutpat. Vivamus consectetuer hendrerit lacus. Cras non dolor. Vivamus in erat ut urna cursus vestibulum. Fusce commodo aliquam arcu. Nam commodo suscipit quam. Quisque id odio. Praesent venenatis metus at tortor pulvinar varius.Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at ligula. Fusce fermentum. Nullam cursus lacinia erat. Praesent blandit laoreet nibh. Fusce convallis metus id felis luctus adipiscing. Pellentesque egestas, neque sit amet convallis pulvinar, justo nulla eleifend augue, ac auctor orci leo non est. Quisque id mi. Ut tincidunt tincidunt erat. Etiam feugiat lorem non metus. Vestibulum dapibus nunc ac augue. Curabitur vestibulum aliquam leo. Praesent egestas neque eu enim. In hac habitasse platea dictumst. Fusce a quam. Etiam ut purus mattis mauris sodales aliquam. Curabitur nisi. Quisque malesuada placerat nisl. Nam ipsum risus, rutrum vitae, vestibulum eu, molestie vel, lacus. Sed augue ipsum, egestas nec, vestibulum et, malesuada adipiscing, dui. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, vitae euismod ligula urna in dolor. Mauris sollicitudin fermentum libero. Praesent nonummy mi in odio. Nunc interdum lacus sit amet orci. Vestibulum rutrum, mi nec elementum vehicula, eros quam gravida nisl, id fringilla neque ante vel mi. Morbi mollis tellus ac sapien. Phasellus volutpat, metus eget egestas mollis, lacus lacus blandit dui, id egestas quam mauris ut lacus. Fusce vel dui. Sed in libero ut nibh placerat accumsan. Proin faucibus arcu quis ante. In consectetuer turpis ut velit. Nulla sit amet est. Praesent metus tellus, elementum eu, semper a, adipiscing nec, purus. Cras risus ipsum, faucibus ut, ullamcorper id, varius ac, leo. Suspendisse feugiat. Suspendisse enim turpis, dictum sed, iaculis a, condimentum nec, nisi. Praesent nec nisl a purus blandit viverra. Praesent ac massa at ligula laoreet iaculis. Nulla neque dolor, sagittis eget, iaculis quis, molestie non, velit. Mauris turpis nunc, blandit et, volutpat molestie, porta ut, ligula. Fusce pharetra convallis urna. Quisque ut nisi. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Suspendisse non nisl sit amet velit hendrerit rutrum. Ut leo. Ut a nisl id ante tempus hendrerit. Proin pretium, leo ac pellentesque mollis, felis nunc ultrices eros, sed gravida augue augue mollis justo. Suspendisse eu ligula. Nulla facilisi. Donec id justo. Praesent porttitor, nulla vitae posuere iaculis, arcu nisl dignissim dolor, a pretium mi sem ut ipsum. Curabitur suscipit suscipit tellus. Praesent vestibulum dapibus nibh. Etiam iaculis nunc ac metus. Ut id nisl quis enim dignissim sagittis. Etiam sollicitudin, ipsum eu pulvinar rutrum, tellus ipsum laoreet sapien, quis venenatis ante odio sit amet eros. Proin magna. Duis vel nibh at velit scelerisque suscipit. Curabitur turpis. Vestibulum suscipit nulla quis orci. Fusce ac felis sit amet ligula pharetra condimentum. Maecenas egestas arcu quis ligula mattis placerat. Duis lobortis massa imperdiet quam. Suspendisse potenti. Pellentesque commodo eros a enim. Vestibulum turpis sem, aliquet eget, lobortis pellentesque, rutrum eu, nisl. Sed libero. Aliquam erat volutpat. Etiam vitae tortor. Morbi vestibulum volutpat enim. Aliquam eu nunc. Nunc sed turpis. Sed mollis, eros et ultrices tempus, mauris ipsum aliquam libero, non adipiscing dolor urna a orci. Nulla porta dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Pellentesque dapibus hendrerit tortor. Praesent egestas tristique nibh. Sed a libero. Cras varius. Donec vitae orci sed dolor rutrum auctor. Fusce egestas elit eget lorem. Suspendisse nisl elit, rhoncus eget, elementum ac, condimentum eget, diam. Nam at tortor in tellus interdum sagittis. Aliquam lobortis. Donec orci lectus, aliquam ut, faucibus non, euismod id, nulla. Curabitur blandit mollis lacus. Nam adipiscing. Vestibulum eu odio. Vivamus laoreet. Nullam tincidunt adipiscing enim. Phasellus tempus. Proin viverra, ligula sit amet ultrices semper, ligula arcu tristique sapien, a accumsan nisi mauris ac eros. Fusce neque. Suspendisse faucibus, nunc et pellentesque egestas, lacus ante convallis tellus, vitae iaculis lacus elit id tortor. Vivamus aliquet elit ac nisl. Fusce fermentum odio nec arcu. Vivamus euismod mauris. In ut quam vitae odio lacinia tincidunt. Praesent ut ligula non mi varius sagittis. Cras sagittis. Praesent ac sem eget est egestas volutpat. Vivamus consectetuer hendrerit lacus. Cras non dolor. Vivamus in erat ut urna cursus vestibulum. Fusce commodo aliquam arcu. Nam commodo suscipit quam. Quisque id odio. Praesent venenatis metus at tortor pulvinar varius.Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at ligula. Fusce fermentum. Nullam cursus lacinia erat. Praesent blandit laoreet nibh. Fusce convallis metus id felis luctus adipiscing. Pellentesque egestas, neque sit amet convallis pulvinar, justo nulla eleifend augue, ac auctor orci leo non est. Quisque id mi. Ut tincidunt tincidunt erat. Etiam feugiat lorem non metus. Vestibulum dapibus nunc ac augue. Curabitur vestibulum aliquam leo. Praesent egestas neque eu enim. In hac habitasse platea dictumst. Fusce a quam. Etiam ut purus mattis mauris sodales aliquam. Curabitur nisi. Quisque malesuada placerat nisl. Nam ipsum risus, rutrum vitae, vestibulum eu, molestie vel, lacus. Sed augue ipsum, egestas nec, vestibulum et, malesuada adipiscing, dui. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, vitae euismod ligula urna in dolor. Mauris sollicitudin fermentum libero. Praesent nonummy mi in odio. Nunc interdum lacus sit amet orci. Vestibulum rutrum, mi nec elementum vehicula, eros quam gravida nisl, id fringilla neque ante vel mi. Morbi mollis tellus ac sapien. Phasellus volutpat, metus eget egestas mollis, lacus lacus blandit dui, id egestas quam mauris ut lacus. Fusce vel dui. Sed in libero ut nibh placerat accumsan. Proin faucibus arcu quis ante. In consectetuer turpis ut velit. Nulla sit amet est. Praesent metus tellus, elementum eu, semper a, adipiscing nec, purus. Cras risus ipsum, faucibus ut, ullamcorper id, varius ac, leo. Suspendisse feugiat. Suspendisse enim turpis, dictum sed, iaculis a, condimentum nec, nisi. Praesent nec nisl a purus blandit viverra. Praesent ac massa at ligula laoreet iaculis. Nulla neque dolor, sagittis eget, iaculis quis, molestie non, velit. Mauris turpis nunc, blandit et, volutpat molestie, porta ut, ligula. Fusce pharetra convallis urna. Quisque ut nisi. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Suspendisse non nisl sit amet velit hendrerit rutrum. Ut leo. Ut a nisl id ante tempus hendrerit. Proin pretium, leo ac pellentesque mollis, felis nunc ultrices eros, sed gravida augue augue mollis justo. Suspendisse eu ligula. Nulla facilisi. Donec id justo. Praesent porttitor, nulla vitae posuere iaculis, arcu nisl dignissim dolor, a pretium mi sem ut ipsum. Curabitur suscipit suscipit tellus. Praesent vestibulum dapibus nibh. Etiam iaculis nunc ac metus. Ut id nisl quis enim dignissim sagittis. Etiam sollicitudin, ipsum eu pulvinar rutrum, tellus ipsum laoreet sapien, quis venenatis ante odio sit amet eros. Proin magna. Duis vel nibh at velit scelerisque suscipit. Curabitur turpis. Vestibulum suscipit nulla quis orci. Fusce ac felis sit amet ligula pharetra condimentum. Maecenas egestas arcu quis ligula mattis placerat. Duis lobortis massa imperdiet quam. Suspendisse potenti. Pellentesque commodo eros a enim. Vestibulum turpis sem, aliquet eget, lobortis pellentesque, rutrum eu, nisl. Sed libero. Aliquam erat volutpat. Etiam vitae tortor. Morbi vestibulum volutpat enim. Aliquam eu nunc. Nunc sed turpis. Sed mollis, eros et ultrices tempus, mauris ipsum aliquam libero, non adipiscing dolor urna a orci. Nulla porta dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Pellentesque dapibus hendrerit tortor. Praesent egestas tristique nibh. Sed a libero. Cras varius. Donec vitae orci sed dolor rutrum auctor. Fusce egestas elit eget lorem. Suspendisse nisl elit, rhoncus eget, elementum ac, condimentum eget, diam. Nam at tortor in tellus interdum sagittis. Aliquam lobortis. Donec orci lectus, aliquam ut, faucibus non, euismod id, nulla. Curabitur blandit mollis lacus. Nam adipiscing. Vestibulum eu odio. Vivamus laoreet. Nullam tincidunt adipiscing enim. Phasellus tempus. Proin viverra, ligula sit amet ultrices semper, ligula arcu tristique sapien, a accumsan nisi mauris ac eros. Fusce neque. Suspendisse faucibus, nunc et pellentesque egestas, lacus ante convallis tellus, vitae iaculis lacus elit id tortor. Vivamus aliquet elit ac nisl. Fusce fermentum odio nec arcu. Vivamus euismod mauris. In ut quam vitae odio lacinia tincidunt. Praesent ut ligula non mi varius sagittis. Cras sagittis. Praesent ac sem eget est egestas volutpat. Vivamus consectetuer hendrerit lacus. Cras non dolor. Vivamus in erat ut urna cursus vestibulum. Fusce commodo aliquam arcu. Nam commodo suscipit quam. Quisque id odio. Praesent venenatis metus at tortor pulvinar varius.Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at ligula. Fusce fermentum. Nullam cursus lacinia erat. Praesent blandit laoreet nibh. Fusce convallis metus id felis luctus adipiscing. Pellentesque egestas, neque sit amet convallis pulvinar, justo nulla eleifend augue, ac auctor orci leo non est. Quisque id mi. Ut tincidunt tincidunt erat. Etiam feugiat lorem non metus. Vestibulum dapibus nunc ac augue. Curabitur vestibulum aliquam leo. Praesent egestas neque eu enim. In hac habitasse platea dictumst. Fusce a quam. Etiam ut purus mattis mauris sodales aliquam. Curabitur nisi. Quisque malesuada placerat nisl. Nam ipsum risus, rutrum vitae, vestibulum eu, molestie vel, lacus. Sed augue ipsum, egestas nec, vestibulum et, malesuada adipiscing, dui. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, vitae euismod ligula urna in dolor. Mauris sollicitudin fermentum libero. Praesent nonummy mi in odio. Nunc interdum lacus sit amet orci. Vestibulum rutrum, mi nec elementum vehicula, eros quam gravida nisl, id fringilla neque ante vel mi. Morbi mollis tellus ac sapien. Phasellus volutpat, metus eget egestas mollis, lacus lacus blandit dui, id egestas quam mauris ut lacus. Fusce vel dui. Sed in libero ut nibh placerat accumsan. Proin faucibus arcu quis ante. In consectetuer turpis ut velit. Nulla sit amet est. Praesent metus tellus, elementum eu, semper a, adipiscing nec, purus. Cras risus ipsum, faucibus ut, ullamcorper id, varius ac, leo. Suspendisse feugiat. Suspendisse enim turpis, dictum sed, iaculis a, condimentum nec, nisi. Praesent nec nisl a purus blandit viverra. Praesent ac massa at ligula laoreet iaculis. Nulla neque dolor, sagittis eget, iaculis quis, molestie non, velit. Mauris turpis nunc, blandit et, volutpat molestie, porta ut, ligula. Fusce pharetra convallis urna. Quisque ut nisi. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Suspendisse non nisl sit amet velit hendrerit rutrum. Ut leo. Ut a nisl id ante tempus hendrerit. Proin pretium, leo ac pellentesque mollis, felis nunc ultrices eros, sed gravida augue augue mollis justo. Suspendisse eu ligula. Nulla facilisi. Donec id justo. Praesent porttitor, nulla vitae posuere iaculis, arcu nisl dignissim dolor, a pretium mi sem ut ipsum. Curabitur suscipit suscipit tellus. Praesent vestibulum dapibus nibh. Etiam iaculis nunc ac metus. Ut id nisl quis enim dignissim sagittis. Etiam sollicitudin, ipsum eu pulvinar rutrum, tellus ipsum laoreet sapien, quis venenatis ante odio sit amet eros. Proin magna. Duis vel nibh at velit scelerisque suscipit. Curabitur turpis. Vestibulum suscipit nulla quis orci. Fusce ac felis sit amet ligula pharetra condimentum. Maecenas egestas arcu quis ligula mattis placerat. Duis lobortis massa imperdiet quam. Suspendisse potenti. Pellentesque commodo eros a enim. Vestibulum turpis sem, aliquet eget, lobortis pellentesque, rutrum eu, nisl. Sed libero. Aliquam erat volutpat. Etiam vitae tortor. Morbi vestibulum volutpat enim. Aliquam eu nunc. Nunc sed turpis. Sed mollis, eros et ultrices tempus, mauris ipsum aliquam libero, non adipiscing dolor urna a orci. Nulla porta dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Pellentesque dapibus hendrerit tortor. Praesent egestas tristique nibh. Sed a libero. Cras varius. Donec vitae orci sed dolor rutrum auctor. Fusce egestas elit eget lorem. Suspendisse nisl elit, rhoncus eget, elementum ac, condimentum eget, diam. Nam at tortor in tellus interdum sagittis. Aliquam lobortis. Donec orci lectus, aliquam ut, faucibus non, euismod id, nulla. Curabitur blandit mollis lacus. Nam adipiscing. Vestibulum eu odio. Vivamus laoreet. Nullam tincidunt adipiscing enim. Phasellus tempus. Proin viverra, ligula sit amet ultrices semper, ligula arcu tristique sapien, a accumsan nisi mauris ac eros. Fusce neque. Suspendisse faucibus, nunc et pellentesque egestas, lacus ante convallis tellus, vitae iaculis lacus elit id tortor. Vivamus aliquet elit ac nisl. Fusce fermentum odio nec arcu. Vivamus euismod mauris. In ut quam vitae odio lacinia tincidunt. Praesent ut ligula non mi varius sagittis. Cras sagittis. Praesent ac sem eget est egestas volutpat. Vivamus consectetuer hendrerit lacus. Cras non dolor. Vivamus in erat ut urna cursus vestibulum. Fusce commodo aliquam arcu. Nam commodo suscipit quam. Quisque id odio. Praesent venenatis metus at tortor pulvinar varius.Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at ligula. Fusce fermentum. Nullam cursus lacinia erat. Praesent blandit laoreet nibh. Fusce convallis metus id felis luctus adipiscing. Pellentesque egestas, neque sit amet convallis pulvinar, justo nulla eleifend augue, ac auctor orci leo non est. Quisque id mi. Ut tincidunt tincidunt erat. Etiam feugiat lorem non metus. Vestibulum dapibus nunc ac augue. Curabitur vestibulum aliquam leo. Praesent egestas neque eu enim. In hac habitasse platea dictumst. Fusce a quam. Etiam ut purus mattis mauris sodales aliquam. Curabitur nisi. Quisque malesuada placerat nisl. Nam ipsum risus, rutrum vitae, vestibulum eu, molestie vel, lacus. Sed augue ipsum, egestas nec, vestibulum et, malesuada adipiscing, dui. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, vitae euismod ligula urna in dolor. Mauris sollicitudin fermentum libero. Praesent nonummy mi in odio. Nunc interdum lacus sit amet orci. Vestibulum rutrum, mi nec elementum vehicula, eros quam gravida nisl, id fringilla neque ante vel mi. Morbi mollis tellus ac sapien. Phasellus volutpat, metus eget egestas mollis, lacus lacus blandit dui, id egestas quam mauris ut lacus. Fusce vel dui. Sed in libero ut nibh placerat accumsan. Proin faucibus arcu quis ante. In consectetuer turpis ut velit. Nulla sit amet est. Praesent metus tellus, elementum eu, semper a, adipiscing nec, purus. Cras risus ipsum, faucibus ut, ullamcorper id, varius ac, leo. Suspendisse feugiat. Suspendisse enim turpis, dictum sed, iaculis a, condimentum nec, nisi. Praesent nec nisl a purus blandit viverra. Praesent ac massa at ligula laoreet iaculis. Nulla neque dolor, sagittis eget, iaculis quis, molestie non, velit. Mauris turpis nunc, blandit et, volutpat molestie, porta ut, ligula. Fusce pharetra convallis urna. Quisque ut nisi. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Suspendisse non nisl sit amet velit hendrerit rutrum. Ut leo. Ut a nisl id ante tempus hendrerit. Proin pretium, leo ac pellentesque mollis, felis nunc ultrices eros, sed gravida augue augue mollis justo. Suspendisse eu ligula. Nulla facilisi. Donec id justo. Praesent porttitor, nulla vitae posuere iaculis, arcu nisl dignissim dolor, a pretium mi sem ut ipsum. Curabitur suscipit suscipit tellus. Praesent vestibulum dapibus nibh. Etiam iaculis nunc ac metus. Ut id nisl quis enim dignissim sagittis. Etiam sollicitudin, ipsum eu pulvinar rutrum, tellus ipsum laoreet sapien, quis venenatis ante odio sit amet eros. Proin magna. Duis vel nibh at velit scelerisque suscipit. Curabitur turpis. Vestibulum suscipit nulla quis orci. Fusce ac felis sit amet ligula pharetra condimentum. Maecenas egestas arcu quis ligula mattis placerat. Duis lobortis massa imperdiet quam. Suspendisse potenti. Pellentesque commodo eros a enim. Vestibulum turpis sem, aliquet eget, lobortis pellentesque, rutrum eu, nisl. Sed libero. Aliquam erat volutpat. Etiam vitae tortor. Morbi vestibulum volutpat enim. Aliquam eu nunc. Nunc sed turpis. Sed mollis, eros et ultrices tempus, mauris ipsum aliquam libero, non adipiscing dolor urna a orci. Nulla porta dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Pellentesque dapibus hendrerit tortor. Praesent egestas tristique nibh. Sed a libero. Cras varius. Donec vitae orci sed dolor rutrum auctor. Fusce egestas elit eget lorem. Suspendisse nisl elit, rhoncus eget, elementum ac, condimentum eget, diam. Nam at tortor in tellus interdum sagittis. Aliquam lobortis. Donec orci lectus, aliquam ut, faucibus non, euismod id, nulla. Curabitur blandit mollis lacus. Nam adipiscing. Vestibulum eu odio. Vivamus laoreet. Nullam tincidunt adipiscing enim. Phasellus tempus. Proin viverra, ligula sit amet ultrices semper, ligula arcu tristique sapien, a accumsan nisi mauris ac eros. Fusce neque. Suspendisse faucibus, nunc et pellentesque egestas, lacus ante convallis tellus, vitae iaculis lacus elit id tortor. Vivamus aliquet elit ac nisl. Fusce fermentum odio nec arcu. Vivamus euismod mauris. In ut quam vitae odio lacinia tincidunt. Praesent ut ligula non mi varius sagittis. Cras sagittis. Praesent ac sem eget est egestas volutpat. Vivamus consectetuer hendrerit lacus. Cras non dolor. Vivamus in erat ut urna cursus vestibulum. Fusce commodo aliquam arcu. Nam commodo suscipit quam. Quisque id odio. Praesent venenatis metus at tortor pulvinar varius.Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at ligula. Fusce fermentum. Nullam cursus lacinia erat. Praesent blandit laoreet nibh. Fusce convallis metus id felis luctus adipiscing. Pellentesque egestas, neque sit amet convallis pulvinar, justo nulla eleifend augue, ac auctor orci leo non est. Quisque id mi. Ut tincidunt tincidunt erat. Etiam feugiat lorem non metus. Vestibulum dapibus nunc ac augue. Curabitur vestibulum aliquam leo. Praesent egestas neque eu enim. In hac habitasse platea dictumst. Fusce a quam. Etiam ut purus mattis mauris sodales aliquam. Curabitur nisi. Quisque malesuada placerat nisl. Nam ipsum risus, rutrum vitae, vestibulum eu, molestie vel, lacus. Sed augue ipsum, egestas nec, vestibulum et, malesuada adipiscing, dui. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, vitae euismod ligula urna in dolor. Mauris sollicitudin fermentum libero. Praesent nonummy mi in odio. Nunc interdum lacus sit amet orci. Vestibulum rutrum, mi nec elementum vehicula, eros quam gravida nisl, id fringilla neque ante vel mi. Morbi mollis tellus ac sapien. Phasellus volutpat, metus eget egestas mollis, lacus lacus blandit dui, id egestas quam mauris ut lacus. Fusce vel dui. Sed in libero ut nibh placerat accumsan. Proin faucibus arcu quis ante. In consectetuer turpis ut velit. Nulla sit amet est. Praesent metus tellus, elementum eu, semper a, adipiscing nec, purus. Cras risus ipsum, faucibus ut, ullamcorper id, varius ac, leo. Suspendisse feugiat. Suspendisse enim turpis, dictum sed, iaculis a, condimentum nec, nisi. Praesent nec nisl a purus blandit viverra. Praesent ac massa at ligula laoreet iaculis. Nulla neque dolor, sagittis eget, iaculis quis, molestie non, velit. Mauris turpis nunc, blandit et, volutpat molestie, porta ut, ligula. Fusce pharetra convallis urna. Quisque ut nisi. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Suspendisse non nisl sit amet velit hendrerit rutrum. Ut leo. Ut a nisl id ante tempus hendrerit. Proin pretium, leo ac pellentesque mollis, felis nunc ultrices eros, sed gravida augue augue mollis justo. Suspendisse eu ligula. Nulla facilisi. Donec id justo. Praesent porttitor, nulla vitae posuere iaculis, arcu nisl dignissim dolor, a pretium mi sem ut ipsum. Curabitur suscipit suscipit tellus. Praesent vestibulum dapibus nibh. Etiam iaculis nunc ac metus. Ut id nisl quis enim dignissim sagittis. Etiam sollicitudin, ipsum eu pulvinar rutrum, tellus ipsum laoreet sapien, quis venenatis ante odio sit amet eros. Proin magna. Duis vel nibh at velit scelerisque suscipit. Curabitur turpis. Vestibulum suscipit nulla quis orci. Fusce ac felis sit amet ligula pharetra condimentum. Maecenas egestas arcu quis ligula mattis placerat. Duis lobortis massa imperdiet quam. Suspendisse potenti. Pellentesque commodo eros a enim. Vestibulum turpis sem, aliquet eget, lobortis pellentesque, rutrum eu, nisl. Sed libero. Aliquam erat volutpat. Etiam vitae tortor. Morbi vestibulum volutpat enim. Aliquam eu nunc. Nunc sed turpis. Sed mollis, eros et ultrices tempus, mauris ipsum aliquam libero, non adipiscing dolor urna a orci. Nulla porta dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Pellentesque dapibus hendrerit tortor. Praesent egestas tristique nibh. Sed a libero. Cras varius. Donec vitae orci sed dolor rutrum auctor. Fusce egestas elit eget lorem. Suspendisse nisl elit, rhoncus eget, elementum ac, condimentum eget, diam. Nam at tortor in tellus interdum sagittis. Aliquam lobortis. Donec orci lectus, aliquam ut, faucibus non, euismod id, nulla. Curabitur blandit mollis lacus. Nam adipiscing. Vestibulum eu odio. Vivamus laoreet. Nullam tincidunt adipiscing enim. Phasellus tempus. Proin viverra, ligula sit amet ultrices semper, ligula arcu tristique sapien, a accumsan nisi mauris ac eros. Fusce neque. Suspendisse faucibus, nunc et pellentesque egestas, lacus ante convallis tellus, vitae iaculis lacus elit id tortor. Vivamus aliquet elit ac nisl. Fusce fermentum odio nec arcu. Vivamus euismod mauris. In ut quam vitae odio lacinia tincidunt. Praesent ut ligula non mi varius sagittis. Cras sagittis. Praesent ac sem eget est egestas volutpat. Vivamus consectetuer hendrerit lacus. Cras non dolor. Vivamus in erat ut urna cursus vestibulum. Fusce commodo aliquam arcu. Nam commodo suscipit quam. Quisque id odio. Praesent venenatis metus at tortor pulvinar varius.Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at ligula. Fusce fermentum. Nullam cursus lacinia erat. Praesent blandit laoreet nibh. Fusce convallis metus id felis luctus adipiscing. Pellentesque egestas, neque sit amet convallis pulvinar, justo nulla eleifend augue, ac auctor orci leo non est. Quisque id mi. Ut tincidunt tincidunt erat. Etiam feugiat lorem non metus. Vestibulum dapibus nunc ac augue. Curabitur vestibulum aliquam leo. Praesent egestas neque eu enim. In hac habitasse platea dictumst. Fusce a quam. Etiam ut purus mattis mauris sodales aliquam. Curabitur nisi. Quisque malesuada placerat nisl. Nam ipsum risus, rutrum vitae, vestibulum eu, molestie vel, lacus. Sed augue ipsum, egestas nec, vestibulum et, malesuada adipiscing, dui. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, vitae euismod ligula urna in dolor. Mauris sollicitudin fermentum libero. Praesent nonummy mi in odio. Nunc interdum lacus sit amet orci. Vestibulum rutrum, mi nec elementum vehicula, eros quam gravida nisl, id fringilla neque ante vel mi. Morbi mollis tellus ac sapien. Phasellus volutpat, metus eget egestas mollis, lacus lacus blandit dui, id egestas quam mauris ut lacus. Fusce vel dui. Sed in libero ut nibh placerat accumsan. Proin faucibus arcu quis ante. In consectetuer turpis ut velit. Nulla sit amet est. Praesent metus tellus, elementum eu, semper a, adipiscing nec, purus. Cras risus ipsum, faucibus ut, ullamcorper id, varius ac, leo. Suspendisse feugiat. Suspendisse enim turpis, dictum sed, iaculis a, condimentum nec, nisi. Praesent nec nisl a purus blandit viverra. Praesent ac massa at ligula laoreet iaculis. Nulla neque dolor, sagittis eget, iaculis quis, molestie non, velit. Mauris turpis nunc, blandit et, volutpat molestie, porta ut, ligula. Fusce pharetra convallis urna. Quisque ut nisi. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Suspendisse non nisl sit amet velit hendrerit rutrum. Ut leo. Ut a nisl id ante tempus hendrerit. Proin pretium, leo ac pellentesque mollis, felis nunc ultrices eros, sed gravida augue augue mollis justo. Suspendisse eu ligula. Nulla facilisi. Donec id justo. Praesent porttitor, nulla vitae posuere iaculis, arcu nisl dignissim dolor, a pretium mi sem ut ipsum. Curabitur suscipit suscipit tellus. Praesent vestibulum dapibus nibh. Etiam iaculis nunc ac metus. Ut id nisl quis enim dignissim sagittis. Etiam sollicitudin, ipsum eu pulvinar rutrum, tellus ipsum laoreet sapien, quis venenatis ante odio sit amet eros. Proin magna. Duis vel nibh at velit scelerisque suscipit. Curabitur turpis. Vestibulum suscipit nulla quis orci. Fusce ac felis sit amet ligula pharetra condimentum. Maecenas egestas arcu quis ligula mattis placerat. Duis lobortis massa imperdiet quam. Suspendisse potenti. Pellentesque commodo eros a enim. Vestibulum turpis sem, aliquet eget, lobortis pellentesque, rutrum eu, nisl. Sed libero. Aliquam erat volutpat. Etiam vitae tortor. Morbi vestibulum volutpat enim. Aliquam eu nunc. Nunc sed turpis. Sed mollis, eros et ultrices tempus, mauris ipsum aliquam libero, non adipiscing dolor urna a orci. Nulla porta dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Pellentesque dapibus hendrerit tortor. Praesent egestas tristique nibh. Sed a libero. Cras varius. Donec vitae orci sed dolor rutrum auctor. Fusce egestas elit eget lorem. Suspendisse nisl elit, rhoncus eget, elementum ac, condimentum eget, diam. Nam at tortor in tellus interdum sagittis. Aliquam lobortis. Donec orci lectus, aliquam ut, faucibus non, euismod id, nulla. Curabitur blandit mollis lacus. Nam adipiscing. Vestibulum eu odio. Vivamus laoreet. Nullam tincidunt adipiscing enim. Phasellus tempus. Proin viverra, ligula sit amet ultrices semper, ligula arcu tristique sapien, a accumsan nisi mauris ac eros. Fusce neque. Suspendisse faucibus, nunc et pellentesque egestas, lacus ante convallis tellus, vitae iaculis lacus elit id tortor. Vivamus aliquet elit ac nisl. Fusce fermentum odio nec arcu. Vivamus euismod mauris. In ut quam vitae odio lacinia tincidunt. Praesent ut ligula non mi varius sagittis. Cras sagittis. Praesent ac sem eget est egestas volutpat. Vivamus consectetuer hendrerit lacus. Cras non dolor. Vivamus in erat ut urna cursus vestibulum. Fusce commodo aliquam arcu. Nam commodo suscipit quam. Quisque id odio. Praesent venenatis metus at tortor pulvinar varius.Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac felis quis tortor malesuada pretium. Pellentesque auctor neque nec urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna. In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis vulputate lorem. Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, dui et placerat feugiat, eros pede varius nisi, condimentum viverra felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. In auctor lobortis lacus. Quisque libero metus, condimentum nec, tempor a, commodo mollis, magna. Vestibulum ullamcorper mauris at 1234zip4j-2.2.8/src/test/resources/test-files/zero_byte_file.txt000066400000000000000000000000001357650161600241640ustar00rootroot00000000000000zip4j-2.2.8/src/test/resources/test-files/öüäöäö/000077500000000000000000000000001357650161600250035ustar00rootroot00000000000000zip4j-2.2.8/src/test/resources/test-files/öüäöäö/asöäööl000066400000000000000000000052671357650161600304410ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc,zip4j-2.2.8/src/test/resources/test-files/бореиская.txt000066400000000000000000000222551357650161600277330ustar00rootroot00000000000000Лорем ипсум долор сит амет, аетерно нострум цотидиеяуе яуо ат, еиус интегре еррорибус еу цум, еи хас ребум нонумес. Иусто цетерос нец цу. Ан постулант цонвенире елаборарет вис, меи тота иллуд апериам ан. Ат пер атяуи ностро темпорибус, солута тритани луцилиус меи но. Но мел солум ессент. Яуи цу феугаит доценди перципит. Но ест лаореет цонцептам. Партем сплендиде цонцептам меи ин. Ех алиа постеа мел. Ат при дицо абхорреант хонестатис. Не дуо пауло игнота легимус, веро яуодси инвенире ан яуо, яуи нулла харум диссентиунт ад. Нец диам меис омниум еи, усу фабулас легендос ан. Дицтас пондерум сцрипторем ан еам, ут мел чоро платонем ассуеверит. Сапиентем хендрерит яуи ин, еум ет сонет лабитур интерессет. Яуи ан алиа ессе интеллегебат. Еос ин ребум перицула. Но меис ностро луцилиус еам, ех яуалисяуе импердиет усу. Но пер салутатус десеруиссе, усу муциус легендос цонсеяуунтур но, но сеа цибо виси. Дуо еа дицо апериам инвенире. Нолуиссе адолесценс вим ан, те сонет нолуиссе хас, пер видиссе алияуип нецесситатибус но. Еос фацер хендрерит но. Неморе инсоленс сцрибентур хас ид, ессе яуодси вих ид, ет демоцритум либерависсе вел. При алии видерер ат, цум не ерат нихил волутпат. Яуи омнис интегре иудицабит еи, ех диам убияуе абхорреант сит. Ид маиорум минимум инвидунт нец, цум цу ферри маиестатис, еу лаборе аццумсан елаборарет иус. Вих алиенум цопиосае десеруиссе еу, ет нулла репудиаре еос, ех хас унум аргументум. Хис антиопам ассентиор сентентиае ан, усу еу нобис лаборес волутпат. Тота яуаестио адверсариум но дуо, еи меа ессент орнатус вивендум. Еу про еним дицам, вел ид санцтус симилияуе цонцлудатуряуе. Тритани аперири ирацундиа сит но, цасе идяуе ет про. Ан нец менандри волутпат, вис еи ностер партиендо. Вери путант но вих. Омнес алияуид номинави сед не, латине сплендиде ех пер. Юсто молестие усу ет. Лорем трацтатос ех цум, про тимеам ерипуит ад, еу хис оптион феугаит. Вереар феугиат вел те. Яуис децоре молестие сит ех, дуо ут модус зрил, хас оратио долорем алиенум ид. Ан яуо облияуе алияуандо, сит ад сенсерит салутанди сцрипторем. Меа еу натум фугит, детрацто луптатум персеяуерис ут дуо, пер еи омнес иудицо дебитис. Сед яуидам цоммодо ассентиор ех, еос еи легере импердиет ассентиор. Ад тимеам аппетере вел. Нец ех яуас мелиус. Еу пер натум репудиаре. Либрис цетерос делицата меи ан, ат алии долоре тамяуам сит. Либер сигниферумяуе ин вис, натум дигниссим те ест. Еа фугит дицам лаборес про. Ин яуис интеллегат персеяуерис дуо. Ех еам вениам детрацто, ат мел поссе нусяуам. Пхаедрум сенсерит ин сед. Аппареат праесент ин усу, юсто хендрерит но нец, солум утамур нам не. Яуо но луптатум еффициантур, еу ест мовет путент, меа но харум минимум цомплецтитур. Но еум стет цлита, те хис нулла облияуе волумус. Дицта фацете платонем при те, еним салутанди не сит. Порро пондерум репримияуе ад пер. Яуо омнис дицит те, иус еа сцрипта оцурререт. Ат хас аппетере инвенире оцурререт. Но хис алияуип десерунт, дицта лаборе неглегентур ад хис, еи сонет яуаеяуе перицула про. Мовет ехпетендис интерпретарис цу сед, еос но мазим ассентиор дефиниебас. Цибо фацилисис сед не. Меи лаборамус инцидеринт интерпретарис не. Яуи амет лаборес пертинах еу. Цум ех доминг персецути. Меи ат новум импетус луцилиус. Еи яуо форенсибус улламцорпер, фалли постеа нусяуам вел ан, ад меи аетерно нумяуам хонестатис. Ест дицунт фацилиси постулант еи, алии тибияуе реформиданс ат вих. Не вел доцтус сусципит адолесценс, про ан легере веритус партиендо. Еирмод аперири деленит сит ат, те апеириан детрахит репудиаре при. Ностер видиссе омиттам ех цум, вим еи ерудити аппетере форенсибус. Но еррор вирис анимал хас, яуаеяуе патриояуе те вим, хис те солет нуллам моллис. Ад веро постеа легендос еос, легере фабеллас вих ех, цум ин цоррумпит импердиет. Партем диспутатиони но при, еам цу ассум омиттам, ан модус вертерем нец. Ин риденс нецесситатибус хас. Ад еум видиссе яуаерендум, вис алтера цонвенире персецути цу. Уллум аппеллантур еум еа, сеа тритани сентентиае яуаерендум ан, сед ан мунди партиендо. Еос еу солеат епицури, партем еуисмод меа ад. Вим ан яуалисяуе диспутандо, ут вих яуас дебитис фацилис. Тхеопхрастус репрехендунт ест ад, ин тритани хонестатис про. Синт яуас фабеллас еу при. Еум ессент сцрипторем еа, легендос рецтеяуе торяуатос не сит. Вел малис децоре ириуре еа, иллуд темпор оптион дуо еу. Еа виси хабео нострум еам, мутат евертитур ин меа. Тамяуам инцоррупте вим но, хинц дефинитионем мел ут. Еум цу модус пондерум мнесарчум, иус ут дебет чоро плацерат. Интегре репудиаре дигниссим ин сеа, ад регионе оффициис глориатур меа. Ет меа еррем салутатус, нец рецтеяуе либерависсе ут. Иллуд ностро утрояуе ин хис, саперет симилияуе репудиандае не яуи, цорпора маиестатис диссентиет ат вел. Про одио фиерент репудиаре еи, вис ут тимеам фиерент пондерум. Натум еирмод персецути но еум, цум ид алии утинам тибияуе. Вим санцтус ехпетенда еу. Ет праесент еурипидис дуо, ат унум волуптатум хас, елеифенд делицата праесент вис не. Бонорум волуптатибус ин сед. Ин саперет продессет сцрибентур хас. Иус еа елитр яуалисяуе, хас ет прима ехплицари ирацундиа. Еос дицант дебитис ан. Яуо сале анимал ет. Дицам воцибус при ид, сеа дицит постеа инцидеринт ад. Фуиссет витуператорибус мел ет.zip4j-2.2.8/src/test/resources/test-files/가나다.abc000066400000000000000000000007261357650161600242600ustar00rootroot00000000000000세계를 향한 대화, 유니코드로 하십시오. 제10회 유니코드 국제 회의가 1997년 3월 10일부터 12일까지 독일의 마인즈에서 열립니다. 지금 등록하십시오. 이 회의에서는 업계 전반의 전문가들이 함께 모여 다음과 같은 분야를 다룹니다. - 인터넷과 유니코드, 국제화와 지역화, 운영 체제와 응용 프로그램에서 유니코드의 구현, 글꼴, 문자 배열, 다국어 컴퓨팅.