From ddd8b6bfd0f1ced07940df6b3f8126efff3f1fcf Mon Sep 17 00:00:00 2001 From: vladdd Date: Mon, 7 Apr 2014 15:28:46 -0400 Subject: [PATCH 01/10] Add test_repository_tool.py Add 'test_repository_tool.py' (initial incomplete commit) Add test cases for the top-level metadata objects (Root(), Targets(), etc.) Add test case for Repository() Add test case for get_target_hash() Minor update to 'repository_tool.py': add default rolename for Targets() and call tuf.util.get_target_hash() in the repository tool's get_target_hash() --- tests/unit/test_repository_tool.py | 308 +++++++++++++++++++++++++++++ tuf/repository_tool.py | 32 +-- 2 files changed, 313 insertions(+), 27 deletions(-) create mode 100755 tests/unit/test_repository_tool.py diff --git a/tests/unit/test_repository_tool.py b/tests/unit/test_repository_tool.py new file mode 100755 index 00000000..cb6f61a5 --- /dev/null +++ b/tests/unit/test_repository_tool.py @@ -0,0 +1,308 @@ +""" + + test_repository_tool.py + + + Vladimir Diaz + + + April 7, 2014. + + + See LICENSE for licensing information. + + + Unit test for 'repository_tool.py'. +""" + +import unittest +import logging + +import tuf +import tuf.log +import tuf.formats +import tuf.roledb +import tuf.keydb +import tuf.repository_tool as repo_tool + +logger = logging.getLogger('tuf.test_repository_tool') + + + +class TestRepository(unittest.TestCase): + def setUp(self): + pass + + + + def tearDown(self): + tuf.roledb.clear_roledb() + tuf.keydb.clear_keydb() + + + def test_init(self): + + # Test normal case. + repository = repo_tool.Repository('repository_directory/', + 'metadata_directory/', + 'targets_directory/') + self.assertTrue(isinstance(repository.root, repo_tool.Root)) + self.assertTrue(isinstance(repository.snapshot, repo_tool.Snapshot)) + self.assertTrue(isinstance(repository.timestamp, repo_tool.Timestamp)) + self.assertTrue(isinstance(repository.targets, repo_tool.Targets)) + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, repo_tool.Repository, 3, + 'metadata_directory/', 'targets_directory') + self.assertRaises(tuf.FormatError, repo_tool.Repository, + 'repository_directory', 3, 'targets_directory') + self.assertRaises(tuf.FormatError, repo_tool.Repository, + 'repository_directory', 'metadata_directory', 3) + + + +class TestMetadata(unittest.TestCase): + def setUp(self): + pass + + + + def tearDown(self): + pass + + + + + +class TestRoot(unittest.TestCase): + def setUp(self): + pass + + + + def tearDown(self): + tuf.roledb.clear_roledb() + tuf.keydb.clear_keydb() + + + + def test_init(self): + + # Test normal case. + # Root() subclasses Metadata(), and creates a 'root' role in 'tuf.roledb'. + root_object = repo_tool.Root() + self.assertTrue(isinstance(root_object, repo_tool.Metadata)) + self.assertTrue(tuf.roledb.role_exists('root')) + + + +class TestTimestamp(unittest.TestCase): + def setUp(self): + pass + + + + def tearDown(self): + tuf.roledb.clear_roledb() + tuf.keydb.clear_keydb() + + + + def test_init(self): + + # Test normal case. + # Timestamp() subclasses Metadata(), and creates a 'timestamp' role in + # 'tuf.roledb'. + timestamp_object = repo_tool.Timestamp() + self.assertTrue(isinstance(timestamp_object, repo_tool.Metadata)) + self.assertTrue(tuf.roledb.role_exists('timestamp')) + + + + + +class TestSnapshot(unittest.TestCase): + def setUp(self): + pass + + + + def tearDown(self): + tuf.roledb.clear_roledb() + tuf.keydb.clear_keydb() + + + + def test_init(self): + + # Test normal case. + # Snapshot() subclasses Metadata(), and creates a 'snapshot' role in + # 'tuf.roledb'. + snapshot_object = repo_tool.Snapshot() + self.assertTrue(isinstance(snapshot_object, repo_tool.Metadata)) + self.assertTrue(tuf.roledb.role_exists('snapshot')) + + + + + +class TestTargets(unittest.TestCase): + def setUp(self): + pass + + + + def tearDown(self): + tuf.roledb.clear_roledb() + tuf.keydb.clear_keydb() + + + + def test_init(self): + + # Test normal case. + # Snapshot() subclasses Metadata(), and creates a 'snapshot' role in + # 'tuf.roledb'. + targets_object = repo_tool.Targets('targets_directory/') + self.assertTrue(isinstance(targets_object, repo_tool.Metadata)) + self.assertTrue(tuf.roledb.role_exists('targets')) + + # Custom Targets object rolename. + targets_object = repo_tool.Targets('targets_directory/', 'project') + self.assertTrue(isinstance(targets_object, repo_tool.Metadata)) + self.assertTrue(tuf.roledb.role_exists('project')) + + # Custom roleinfo object (i.e., tuf.formats.ROLEDB_SCHEMA). 'keyids' and + # 'threshold' are required, the rest are optional. + roleinfo = {'keyids': + ['66c4cb5fef5e4d62b7013ef1cab4b8a827a36c14056d5603c3a970e21eb30e6f'], + 'threshold': 8} + self.assertTrue(tuf.formats.ROLEDB_SCHEMA.matches(roleinfo)) + + targets_object = repo_tool.Targets('targets_directory/', 'package', roleinfo) + self.assertTrue(isinstance(targets_object, repo_tool.Metadata)) + self.assertTrue(tuf.roledb.role_exists('package')) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, repo_tool.Targets, 3) + self.assertRaises(tuf.FormatError, repo_tool.Targets, 'targets_directory/', 3) + self.assertRaises(tuf.FormatError, repo_tool.Targets, 'targets_directory/', + 'targets', 3) + + + +class TestRepositoryToolFunctions(unittest.TestCase): + def setUp(self): + pass + + + def tearDown(self): + pass + + + + def test_create_new_repository(self): + pass + + + + def test_load_repository(self): + pass + + + + def test_generate_and_write_rsa_keypair(self): + pass + + + + def test_import_rsa_privatekey_from_file(self): + pass + + + + def test_import_rsa_publickey_from_file(self): + pass + + + + def test_generate_and_write_ed25519_keypair(self): + pass + + + + def test_import_ed25519_publickey_from_file(self): + pass + + + + def test_import_ed25519_privatekey_from_file(self): + pass + + + + def test_get_metadata_filenames(self): + pass + + + + def test_get_metadata_file_info(self): + pass + + + + def test_get_target_hash(self): + # Test normal case. + expected_target_hashes = { + '/file1.txt': 'e3a3d89eb3b70ce3fbce6017d7b8c12d4abd5635427a0e8a238f53157df85b3d', + '/README.txt': '8faee106f1bb69f34aaf1df1e3c2e87d763c4d878cb96b91db13495e32ceb0b0', + '/packages/file2.txt': 'c9c4a5cdd84858dd6a23d98d7e6e6b2aec45034946c16b2200bc317c75415e92' + } + for filepath, target_hash in expected_target_hashes.items(): + self.assertTrue(tuf.formats.RELPATH_SCHEMA.matches(filepath)) + self.assertTrue(tuf.formats.HASH_SCHEMA.matches(target_hash)) + self.assertEqual(repo_tool.get_target_hash(filepath), target_hash) + + # Test for improperly formatted argument. + self.assertRaises(tuf.FormatError, repo_tool.get_target_hash, 8) + + + + def test_generate_root_metadata(self): + pass + + + + def test_generate_targets_metadata(self): + pass + + + + def test_generate_snapshot_metadata(self): + pass + + + + def test_generate_timestamp_metadata(self): + pass + + + + def test_sign_metadata(self): + pass + + + + def test_write_metadata_file(self): + pass + + + + def test_create_tuf_client_directory(self): + pass + + +# Run the test cases. +if __name__ == '__main__': + unittest.main() diff --git a/tuf/repository_tool.py b/tuf/repository_tool.py index 69211c4e..32408475 100755 --- a/tuf/repository_tool.py +++ b/tuf/repository_tool.py @@ -188,7 +188,7 @@ def write(self, write_partial=False, consistent_snapshot=False): private keys, etc. - write_partial: + mrite_partial: A boolean indicating whether partial metadata should be written to disk. Partial metadata may be written to allow multiple maintainters to independently sign and update role metadata. write() raises an @@ -1479,13 +1479,14 @@ class Targets(Metadata): tuf.FormatError, if the arguments are improperly formatted. - Modifies the roleinfo of the targets role in 'tuf.roledb'. + Modifies the roleinfo of the targets role in 'tuf.roledb', or creates + a default one named 'targets'. None. """ - def __init__(self, targets_directory, rolename, roleinfo=None): + def __init__(self, targets_directory, rolename='targets', roleinfo=None): # Do the arguments have the correct format? # Ensure the arguments have the appropriate number of objects and object @@ -3831,30 +3832,7 @@ def get_target_hash(target_filepath): The hash of 'target_filepath'. """ - # Does 'target_filepath' have the correct format? - # Ensure the arguments have the appropriate number of objects and object - # types, and that all dict keys are properly named. - # Raise 'tuf.FormatError' if there is a mismatch. - tuf.formats.RELPATH_SCHEMA.check_match(target_filepath) - - # Calculate the hash of the filepath to determine which bin to find the - # target. The client currently assumes the repository uses - # 'HASH_FUNCTION' to generate hashes. - digest_object = tuf.hash.digest(HASH_FUNCTION) - - try: - digest_object.update(target_filepath) - - except UnicodeEncodeError: - # Sometimes, there are Unicode characters in target paths. We assume a - # UTF-8 encoding and try to hash that. - digest_object = tuf.hash.digest(HASH_FUNCTION) - encoded_target_filepath = target_filepath.encode('utf-8') - digest_object.update(encoded_target_filepath) - - target_filepath_hash = digest_object.hexdigest() - - return target_filepath_hash + return tuf.util.get_target_hash(target_filepath) From 0f3cdd9f61bbf612da2a8d200f2367a351de3b09 Mon Sep 17 00:00:00 2001 From: vladdd Date: Mon, 7 Apr 2014 20:21:39 -0400 Subject: [PATCH 02/10] Continue updating test_repository_tool.py. Add test cases for the crypto funtions (e.g., generate_and_write_ed25519_keypair()). Add test case for get_metadata_filenames(). Add test case for get_metadata_fileinfo(). Add setUpClass() and tearDownClass(). Update affected functions that failed testing, and any that needed modifying (such as util.py). --- tests/unit/test_repository_tool.py | 272 ++++++++++++++++++++++++++++- tests/unit/test_updater.py | 2 +- tuf/pycrypto_keys.py | 10 +- tuf/repository_tool.py | 48 ++--- tuf/util.py | 13 +- 5 files changed, 308 insertions(+), 37 deletions(-) diff --git a/tests/unit/test_repository_tool.py b/tests/unit/test_repository_tool.py index cb6f61a5..4cd58723 100755 --- a/tests/unit/test_repository_tool.py +++ b/tests/unit/test_repository_tool.py @@ -15,14 +15,18 @@ Unit test for 'repository_tool.py'. """ +import os import unittest import logging +import tempfile +import shutil import tuf import tuf.log import tuf.formats import tuf.roledb import tuf.keydb +import tuf.hash import tuf.repository_tool as repo_tool logger = logging.getLogger('tuf.test_repository_tool') @@ -193,6 +197,30 @@ def test_init(self): class TestRepositoryToolFunctions(unittest.TestCase): + @classmethod + def setUpClass(cls): + + # setUpClass() is called before tests in an individual class are executed. + + # Create a temporary directory to store the repository, metadata, and target + # files. 'temporary_directory' must be deleted in TearDownClass() so that + # temporary files are always removed, even when exceptions occur. + cls.temporary_directory = tempfile.mkdtemp(dir=os.getcwd()) + + + + @classmethod + def tearDownClass(cls): + + # tearDownModule() is called after all the tests have run. + # http://docs.python.org/2/library/unittest.html#class-and-module-fixtures + + # Remove the temporary repository directory, which should contain all the + # metadata, targets, and key files generated for the test cases. + shutil.rmtree(cls.temporary_directory) + + + def setUp(self): pass @@ -213,42 +241,268 @@ def test_load_repository(self): def test_generate_and_write_rsa_keypair(self): - pass + + # Test normal case. + temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) + test_keypath = os.path.join(temporary_directory, 'rsa_key') + + repo_tool.generate_and_write_rsa_keypair(test_keypath, password='pw') + self.assertTrue(os.path.exists(test_keypath)) + self.assertTrue(os.path.exists(test_keypath + '.pub')) + + # Ensure the generated key files are importable. + imported_pubkey = \ + repo_tool.import_rsa_publickey_from_file(test_keypath + '.pub') + self.assertTrue(tuf.formats.RSAKEY_SCHEMA.matches(imported_pubkey)) + + imported_privkey = \ + repo_tool.import_rsa_privatekey_from_file(test_keypath, 'pw') + self.assertTrue(tuf.formats.RSAKEY_SCHEMA.matches(imported_privkey)) + + # Custom 'bits' argument. + os.remove(test_keypath) + os.remove(test_keypath + '.pub') + repo_tool.generate_and_write_rsa_keypair(test_keypath, bits=2048, + password='pw') + self.assertTrue(os.path.exists(test_keypath)) + self.assertTrue(os.path.exists(test_keypath + '.pub')) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, repo_tool.generate_and_write_rsa_keypair, + 3, bits=2048, password='pw') + self.assertRaises(tuf.FormatError, repo_tool.generate_and_write_rsa_keypair, + test_keypath, bits='bad', password='pw') + self.assertRaises(tuf.FormatError, repo_tool.generate_and_write_rsa_keypair, + test_keypath, bits=2048, password=3) + + + # Test invalid 'bits' argument. + self.assertRaises(tuf.FormatError, repo_tool.generate_and_write_rsa_keypair, + test_keypath, bits=1024, password='pw') def test_import_rsa_privatekey_from_file(self): - pass + # Test normal case. + temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) + + # Load one of the pre-generated key files from 'tuf/tests/repository_data'. + # 'password' unlocks the pre-generated key files. + key_filepath = os.path.join(os.pardir, 'repository_data', 'keystore', + 'root_key') + self.assertTrue(os.path.exists(key_filepath)) + + imported_rsa_key = repo_tool.import_rsa_privatekey_from_file(key_filepath, + 'password') + self.assertTrue(tuf.formats.RSAKEY_SCHEMA.matches(imported_rsa_key)) + + + # Test improperly formatted argument. + self.assertRaises(tuf.FormatError, + repo_tool.import_rsa_privatekey_from_file, 3, 'pw') + + + # Test invalid argument. + # Non-existent key file. + nonexistent_keypath = os.path.join(temporary_directory, + 'nonexistent_keypath') + self.assertRaises(IOError, repo_tool.import_rsa_privatekey_from_file, + nonexistent_keypath, 'pw') + + # Invalid key file argument. + invalid_keyfile = os.path.join(temporary_directory, 'invalid_keyfile') + with open(invalid_keyfile, 'wb') as file_object: + file_object.write('bad keyfile') + self.assertRaises(tuf.CryptoError, repo_tool.import_rsa_privatekey_from_file, + invalid_keyfile, 'pw') def test_import_rsa_publickey_from_file(self): - pass + # Test normal case. + temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) + + # Load one of the pre-generated key files from 'tuf/tests/repository_data'. + key_filepath = os.path.join(os.pardir, 'repository_data', 'keystore', + 'root_key.pub') + self.assertTrue(os.path.exists(key_filepath)) + + imported_rsa_key = repo_tool.import_rsa_publickey_from_file(key_filepath) + self.assertTrue(tuf.formats.RSAKEY_SCHEMA.matches(imported_rsa_key)) + + + # Test improperly formatted argument. + self.assertRaises(tuf.FormatError, + repo_tool.import_rsa_privatekey_from_file, 3) + + + # Test invalid argument. + # Non-existent key file. + nonexistent_keypath = os.path.join(temporary_directory, + 'nonexistent_keypath') + self.assertRaises(IOError, repo_tool.import_rsa_publickey_from_file, + nonexistent_keypath) + + # Invalid key file argument. + invalid_keyfile = os.path.join(temporary_directory, 'invalid_keyfile') + with open(invalid_keyfile, 'wb') as file_object: + file_object.write('bad keyfile') + self.assertRaises(tuf.Error, repo_tool.import_rsa_publickey_from_file, + invalid_keyfile) def test_generate_and_write_ed25519_keypair(self): - pass + + # Test normal case. + temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) + test_keypath = os.path.join(temporary_directory, 'ed25519_key') + + repo_tool.generate_and_write_ed25519_keypair(test_keypath, password='pw') + self.assertTrue(os.path.exists(test_keypath)) + self.assertTrue(os.path.exists(test_keypath + '.pub')) + + # Ensure the generated key files are importable. + imported_pubkey = \ + repo_tool.import_ed25519_publickey_from_file(test_keypath + '.pub') + self.assertTrue(tuf.formats.ED25519KEY_SCHEMA.matches(imported_pubkey)) + + imported_privkey = \ + repo_tool.import_ed25519_privatekey_from_file(test_keypath, 'pw') + self.assertTrue(tuf.formats.ED25519KEY_SCHEMA.matches(imported_privkey)) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, + repo_tool.generate_and_write_ed25519_keypair, + 3, password='pw') + self.assertRaises(tuf.FormatError, repo_tool.generate_and_write_rsa_keypair, + test_keypath, password=3) def test_import_ed25519_publickey_from_file(self): - pass + # Test normal case. + # Generate ed25519 keys that can be imported. + temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) + ed25519_keypath = os.path.join(temporary_directory, 'ed25519_key') + repo_tool.generate_and_write_ed25519_keypair(ed25519_keypath, password='pw') + + imported_ed25519_key = \ + repo_tool.import_ed25519_publickey_from_file(ed25519_keypath + '.pub') + self.assertTrue(tuf.formats.ED25519KEY_SCHEMA.matches(imported_ed25519_key)) + + + # Test improperly formatted argument. + self.assertRaises(tuf.FormatError, + repo_tool.import_ed25519_publickey_from_file, 3) + + + # Test invalid argument. + # Non-existent key file. + nonexistent_keypath = os.path.join(temporary_directory, + 'nonexistent_keypath') + self.assertRaises(IOError, repo_tool.import_ed25519_publickey_from_file, + nonexistent_keypath) + + # Invalid key file argument. + invalid_keyfile = os.path.join(temporary_directory, 'invalid_keyfile') + with open(invalid_keyfile, 'wb') as file_object: + file_object.write('bad keyfile') + + self.assertRaises(tuf.Error, repo_tool.import_ed25519_publickey_from_file, + invalid_keyfile) def test_import_ed25519_privatekey_from_file(self): - pass + # Test normal case. + # Generate ed25519 keys that can be imported. + temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) + ed25519_keypath = os.path.join(temporary_directory, 'ed25519_key') + repo_tool.generate_and_write_ed25519_keypair(ed25519_keypath, password='pw') + + imported_ed25519_key = \ + repo_tool.import_ed25519_privatekey_from_file(ed25519_keypath, 'pw') + self.assertTrue(tuf.formats.ED25519KEY_SCHEMA.matches(imported_ed25519_key)) + + + # Test improperly formatted argument. + self.assertRaises(tuf.FormatError, + repo_tool.import_ed25519_privatekey_from_file, 3, 'pw') + + + # Test invalid argument. + # Non-existent key file. + nonexistent_keypath = os.path.join(temporary_directory, + 'nonexistent_keypath') + self.assertRaises(IOError, repo_tool.import_ed25519_privatekey_from_file, + nonexistent_keypath, 'pw') + + # Invalid key file argument. + invalid_keyfile = os.path.join(temporary_directory, 'invalid_keyfile') + with open(invalid_keyfile, 'wb') as file_object: + file_object.write('bad keyfile') + + self.assertRaises(tuf.Error, repo_tool.import_ed25519_privatekey_from_file, + invalid_keyfile, 'pw') def test_get_metadata_filenames(self): - pass + + # Test normal case. + metadata_directory = os.path.join('metadata/') + filenames = {'root.json': metadata_directory + 'root.json', + 'targets.json': metadata_directory + 'targets.json', + 'snapshot.json': metadata_directory + 'snapshot.json', + 'timestamp.json': metadata_directory + 'timestamp.json'} + + self.assertEqual(filenames, repo_tool.get_metadata_filenames('metadata/')) + + # If a directory argument is not specified, the current working directory + # is used. + metadata_directory = os.getcwd() + filenames = {'root.json': os.path.join(metadata_directory, 'root.json'), + 'targets.json': os.path.join(metadata_directory, 'targets.json'), + 'snapshot.json': os.path.join(metadata_directory, 'snapshot.json'), + 'timestamp.json': os.path.join(metadata_directory, 'timestamp.json')} + self.assertEqual(filenames, repo_tool.get_metadata_filenames()) + + + # Test improperly formatted argument. + self.assertRaises(tuf.FormatError, repo_tool.get_metadata_filenames, 3) - def test_get_metadata_file_info(self): - pass + def test_get_metadata_fileinfo(self): + # Test normal case. + temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) + test_filepath = os.path.join(temporary_directory, 'file.txt') + + with open(test_filepath, 'wb') as file_object: + file_object.write('test file') + + # Generate test fileinfo object. It is assumed SHA256 hashes are computed + # by get_metadata_fileinfo(). + file_length = os.path.getsize(test_filepath) + digest_object = tuf.hash.digest_filename(test_filepath) + file_hashes = {'sha256': digest_object.hexdigest()} + fileinfo = {'length': file_length, 'hashes': file_hashes} + self.assertTrue(tuf.formats.FILEINFO_SCHEMA.matches(fileinfo)) + + self.assertEqual(fileinfo, repo_tool.get_metadata_fileinfo(test_filepath)) + + + # Test improperly formatted argument. + self.assertRaises(tuf.FormatError, repo_tool.get_metadata_fileinfo, 3) + + + # Test non-existent file. + nonexistent_filepath = os.path.join(temporary_directory, 'oops.txt') + self.assertRaises(tuf.Error, repo_tool.get_metadata_fileinfo, + nonexistent_filepath) diff --git a/tests/unit/test_updater.py b/tests/unit/test_updater.py index 95476a34..d8e1a5b4 100755 --- a/tests/unit/test_updater.py +++ b/tests/unit/test_updater.py @@ -5,7 +5,7 @@ test_updater.py - Konstantin Andrianov + Konstantin Andrianov. October 15, 2012. diff --git a/tuf/pycrypto_keys.py b/tuf/pycrypto_keys.py index a5b59f03..def95632 100755 --- a/tuf/pycrypto_keys.py +++ b/tuf/pycrypto_keys.py @@ -894,9 +894,15 @@ def _decrypt(file_contents, password): # from 'file_contents'. These five values are delimited by # '_ENCRYPTION_DELIMITER'. This delimiter is arbitrarily chosen and should # not occur in the hexadecimal representations of the fields it is separating. - salt, iterations, hmac, iv, ciphertext = \ - file_contents.split(_ENCRYPTION_DELIMITER) + # Raise 'tuf.CryptoError', if 'file_contents' does not contains the expected + # data layout. + try: + salt, iterations, hmac, iv, ciphertext = \ + file_contents.split(_ENCRYPTION_DELIMITER) + except ValueError: + raise tuf.CryptoError('Invalid encrypted file.') + # Ensure we have the expected raw data for the delimited cryptographic data. salt = binascii.unhexlify(salt) iterations = int(binascii.unhexlify(iterations)) diff --git a/tuf/repository_tool.py b/tuf/repository_tool.py index 32408475..0db27b22 100755 --- a/tuf/repository_tool.py +++ b/tuf/repository_tool.py @@ -3380,6 +3380,8 @@ def import_rsa_privatekey_from_file(filepath, password=None): tuf.FormatError, if the arguments are improperly formatted. + tuf.CryptoError, if 'filepath' is not a valid encrypted key file. + The contents of 'filepath' is read, decrypted, and the key stored. @@ -3409,7 +3411,8 @@ def import_rsa_privatekey_from_file(filepath, password=None): with open(filepath, 'rb') as file_object: encrypted_pem = file_object.read() - # Convert 'encrypted_pem' to 'tuf.formats.RSAKEY_SCHEMA' format. + # Convert 'encrypted_pem' to 'tuf.formats.RSAKEY_SCHEMA' format. Raise + # 'tuf.CryptoError' if 'encrypted_pem' is invalid. rsa_key = tuf.keys.import_rsakey_from_encrypted_pem(encrypted_pem, password) return rsa_key @@ -3692,13 +3695,13 @@ def get_metadata_filenames(metadata_directory=None): If 'metadata_directory' is set to 'metadata', the dictionary returned would contain: - filenames = {'root': 'metadata/root.json', - 'targets': 'metadata/targets.json', - 'snapshot': 'metadata/snapshot.json', - 'timestamp': 'metadata/timestamp.json'} + filenames = {'root.json': 'metadata/root.json', + 'targets.json': 'metadata/targets.json', + 'snapshot.json': 'metadata/snapshot.json', + 'timestamp.json': 'metadata/timestamp.json'} - If the metadata directory is not set by the caller, the current - directory is used. + If 'metadata_directory' is not set by the caller, the current directory is + used. metadata_directory: @@ -3715,19 +3718,15 @@ def get_metadata_filenames(metadata_directory=None): metadata files, such as 'root.json' and 'snapshot.json'. """ + if metadata_directory is None: + metadata_directory = os.getcwd() + # Does 'metadata_directory' have the correct format? # Ensure the arguments have the appropriate number of objects and object # types, and that all dict keys are properly named. # Raise 'tuf.FormatError' if there is a mismatch. tuf.formats.PATH_SCHEMA.check_match(metadata_directory) - if metadata_directory is None: - metadata_directory = '.' - - # Does 'metadata_directory' have the correct format? - # Raise 'tuf.FormatError' if there is a mismatch. - tuf.formats.PATH_SCHEMA.check_match(metadata_directory) - # Store the filepaths of the top-level roles, including the # 'metadata_directory' for each one. filenames = {} @@ -3750,13 +3749,14 @@ def get_metadata_filenames(metadata_directory=None): -def get_metadata_file_info(filename): +def get_metadata_fileinfo(filename): """ Retrieve the file information of 'filename'. The object returned conforms to 'tuf.formats.FILEINFO_SCHEMA'. The information generated for 'filename' is stored in metadata files like 'targets.json'. The fileinfo object returned has the form: + fileinfo = {'length': 1024, 'hashes': {'sha256': 1233dfba312, ...}, 'custom': {...}} @@ -3777,7 +3777,7 @@ def get_metadata_file_info(filename): A dictionary conformant to 'tuf.formats.FILEINFO_SCHEMA'. This dictionary contains the length, hashes, and custom data about the - 'filename' metadata file. + 'filename' metadata file. SHA256 hashes are generated by default. """ # Does 'filename' have the correct format? @@ -4034,7 +4034,7 @@ def generate_targets_metadata(targets_directory, target_files, version, 'targets metadata.' raise tuf.Error(message) - filedict[relative_targetpath] = get_metadata_file_info(target_path) + filedict[relative_targetpath] = get_metadata_fileinfo(target_path) if write_consistent_targets: for target_digest in filedict[relative_targetpath]['hashes'].values(): @@ -4123,8 +4123,8 @@ def generate_snapshot_metadata(metadata_directory, version, expiration_date, # Retrieve the fileinfo of 'root.json' and 'targets.json'. This file # information includes data such as file length, hashes of the file, etc. filedict = {} - filedict[ROOT_FILENAME] = get_metadata_file_info(root_filename) - filedict[TARGETS_FILENAME] = get_metadata_file_info(targets_filename) + filedict[ROOT_FILENAME] = get_metadata_fileinfo(root_filename) + filedict[TARGETS_FILENAME] = get_metadata_fileinfo(targets_filename) # Add compressed versions of the 'targets.json' and 'root.json' metadata, # if they exist. @@ -4136,10 +4136,10 @@ def generate_snapshot_metadata(metadata_directory, version, expiration_date, # add their file attributes to 'filedict'. if os.path.exists(compressed_root_filename): filedict[ROOT_FILENAME+extension] = \ - get_metadata_file_info(compressed_root_filename) + get_metadata_fileinfo(compressed_root_filename) if os.path.exists(compressed_targets_filename): filedict[TARGETS_FILENAME+extension] = \ - get_metadata_file_info(compressed_targets_filename) + get_metadata_fileinfo(compressed_targets_filename) # Walk the 'targets/' directory and generate the fileinfo of all the role # files found. This information is stored in the 'meta' field of the snapshot @@ -4169,7 +4169,7 @@ def generate_snapshot_metadata(metadata_directory, version, expiration_date, # Obsolete role files may still be found. Ensure only roles loaded # in the roledb are included in the snapshot metadata. if tuf.roledb.role_exists(rolename): - filedict[metadata_name] = get_metadata_file_info(metadata_path) + filedict[metadata_name] = get_metadata_fileinfo(metadata_path) # Generate the snapshot metadata object. snapshot_metadata = tuf.formats.SnapshotFile.make_metadata(version, @@ -4232,7 +4232,7 @@ def generate_timestamp_metadata(snapshot_filename, version, # Retrieve the fileinfo of the snapshot metadata file. # This file information contains hashes, file length, custom data, etc. fileinfo = {} - fileinfo[SNAPSHOT_FILENAME] = get_metadata_file_info(snapshot_filename) + fileinfo[SNAPSHOT_FILENAME] = get_metadata_fileinfo(snapshot_filename) # Save the fileinfo of the compressed versions of 'timestamp.json' # in 'fileinfo'. Log the files included in 'fileinfo'. @@ -4242,7 +4242,7 @@ def generate_timestamp_metadata(snapshot_filename, version, compressed_filename = snapshot_filename + '.' + file_extension try: - compressed_fileinfo = get_metadata_file_info(compressed_filename) + compressed_fileinfo = get_metadata_fileinfo(compressed_filename) except: logger.warn('Cannot get fileinfo about '+str(compressed_filename)) diff --git a/tuf/util.py b/tuf/util.py index a825175f..7c93668d 100755 --- a/tuf/util.py +++ b/tuf/util.py @@ -879,6 +879,8 @@ def load_json_file(filepath): tuf.FormatError: If 'filepath' is improperly formatted. + tuf.Error: If 'filepath' cannot be deserialized to a Python object. + IOError in case of runtime IO exceptions. @@ -892,6 +894,8 @@ def load_json_file(filepath): # tuf.FormatError is raised on incorrect format. tuf.formats.PATH_SCHEMA.check_match(filepath) + deserialized_object = None + # The file is mostly likely gzipped. if filepath.endswith('.gz'): logger.debug('gzip.open('+str(filepath)+')') @@ -902,7 +906,14 @@ def load_json_file(filepath): fileobject = open(filepath) try: - return json.load(fileobject) + deserialized_object = json.load(fileobject) + + except ValueError, TypeError: + message = 'Cannot deserialize to a Python object: '+repr(filepath) + raise tuf.Error(message) + + else: + return deserialized_object finally: fileobject.close() From 4ad679062fae90e5572c5fc8aa0c7e0128b6d4cf Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Tue, 8 Apr 2014 12:31:06 -0400 Subject: [PATCH 03/10] Continue updating test_repository_tool.py. Add test cases for the generate_*_metadata() functions. --- tests/unit/test_repository_tool.py | 224 ++++++++++++++++++++++++++++- tuf/repository_tool.py | 10 +- 2 files changed, 226 insertions(+), 8 deletions(-) diff --git a/tests/unit/test_repository_tool.py b/tests/unit/test_repository_tool.py index 4cd58723..1297c566 100755 --- a/tests/unit/test_repository_tool.py +++ b/tests/unit/test_repository_tool.py @@ -31,6 +31,7 @@ logger = logging.getLogger('tuf.test_repository_tool') +repo_tool.disable_console_log_messages() class TestRepository(unittest.TestCase): @@ -231,7 +232,44 @@ def tearDown(self): def test_create_new_repository(self): - pass + # Test normal case. + # Setup the temporary repository directories needed by + # create_new_repository(). + temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) + repository_directory = os.path.join(temporary_directory, 'repository') + metadata_directory = os.path.join(repository_directory, + repo_tool.METADATA_STAGED_DIRECTORY_NAME) + targets_directory = os.path.join(repository_directory, + repo_tool.TARGETS_DIRECTORY_NAME) + + repository = repo_tool.create_new_repository(repository_directory) + self.assertTrue(isinstance(repository, repo_tool.Repository)) + + # Verify that the 'repository/', 'repository/metadata', and + # 'repository/targets' directories were created. + self.assertTrue(os.path.exists(repository_directory)) + self.assertTrue(os.path.exists(metadata_directory)) + self.assertTrue(os.path.exists(targets_directory)) + + + # Test that the 'repository' directory is created (along with the other + # sub-directories) when it does not exist yet. The repository tool creates + # the non-existent directory. + shutil.rmtree(repository_directory) + + repository = repo_tool.create_new_repository(repository_directory) + repository = repo_tool.create_new_repository(repository_directory) + self.assertTrue(isinstance(repository, repo_tool.Repository)) + + # Verify that the 'repository/', 'repository/metadata', and + # 'repository/targets' directories were created. + self.assertTrue(os.path.exists(repository_directory)) + self.assertTrue(os.path.exists(metadata_directory)) + self.assertTrue(os.path.exists(targets_directory)) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, repo_tool.create_new_repository, 3) @@ -524,22 +562,198 @@ def test_get_target_hash(self): def test_generate_root_metadata(self): - pass + # Test normal case. + # Load the root metadata provided in 'tuf/tests/repository_data/'. + root_filepath = os.path.join(os.pardir, 'repository_data', 'repository', + 'metadata', 'root.json') + root_signable = tuf.util.load_json_file(root_filepath) + + # generate_root_metadata() expects the top-level roles and keys to be + # available in 'tuf.keydb' and 'tuf.roledb'. + tuf.roledb.create_roledb_from_root_metadata(root_signable['signed']) + tuf.keydb.create_keydb_from_root_metadata(root_signable['signed']) + + root_metadata = repo_tool.generate_root_metadata(1, '2088-01-01 12:00:00 UTC', + consistent_snapshot=False) + self.assertTrue(tuf.formats.ROOT_SCHEMA.matches(root_metadata)) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, repo_tool.generate_root_metadata, + '3', '2088-01-01 12:00:00 UTC', False) + self.assertRaises(tuf.FormatError, repo_tool.generate_root_metadata, + 1, 3, False) + self.assertRaises(tuf.FormatError, repo_tool.generate_root_metadata, + 1, '2088-01-01 12:00:00 UTC', 3) + + # Test for missing required roles and keys. + tuf.roledb.clear_roledb() + tuf.keydb.clear_keydb() + self.assertRaises(tuf.Error, repo_tool.generate_root_metadata, + 1, '2088-01-01 12:00:00 UTC', False) def test_generate_targets_metadata(self): - pass + # Test normal case. + temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) + targets_directory = os.path.join(temporary_directory, 'targets') + file1_path = os.path.join(targets_directory, 'file.txt') + tuf.util.ensure_parent_dir(file1_path) + + with open(file1_path, 'wb') as file_object: + file_object.write('test file.') + + # Set valid generate_targets_metadata() arguments. + version = 1 + expiration_date = '2088-01-01 12:00:00 UTC' + target_files = ['file.txt'] + delegations = {"keys": { + "a394c28384648328b16731f81440d72243c77bb44c07c040be99347f0df7d7bf": { + "keytype": "ed25519", + "keyval": { + "public": "3eb81026ded5af2c61fb3d4b272ac53cd1049a810ee88f4df1fc35cdaf918157" + } + } + }, + "roles": [ + { + "keyids": [ + "a394c28384648328b16731f81440d72243c77bb44c07c040be99347f0df7d7bf" + ], + "name": "targets/warehouse", + "paths": [ + "/file1.txt", "/README.txt", '/warehouse/' + ], + "threshold": 1 + } + ] + } + + targets_metadata = \ + repo_tool.generate_targets_metadata(targets_directory, target_files, + version, expiration_date, delegations, + False) + self.assertTrue(tuf.formats.TARGETS_SCHEMA.matches(targets_metadata)) + + # Verify that 'digest.filename' file is saved to 'targets_directory' if + # the 'write_consistent_targets' argument is True. + list_targets_directory = os.listdir(targets_directory) + targets_metadata = \ + repo_tool.generate_targets_metadata(targets_directory, target_files, + version, expiration_date, delegations, + write_consistent_targets=True) + new_list_targets_directory = os.listdir(targets_directory) + + # Verify that 'targets_directory' contains only one extra item. + self.assertTrue(len(list_targets_directory) + 1, + len(new_list_targets_directory)) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, repo_tool.generate_targets_metadata, + 3, target_files, version, expiration_date) + self.assertRaises(tuf.FormatError, repo_tool.generate_targets_metadata, + targets_directory, 3, version, expiration_date) + self.assertRaises(tuf.FormatError, repo_tool.generate_targets_metadata, + targets_directory, target_files, '3', expiration_date) + self.assertRaises(tuf.FormatError, repo_tool.generate_targets_metadata, + targets_directory, target_files, version, 3) + + # Improperly formatted 'delegations' and 'write_consistent_targets' + self.assertRaises(tuf.FormatError, repo_tool.generate_targets_metadata, + targets_directory, target_files, version, expiration_date, + 3, False) + self.assertRaises(tuf.FormatError, repo_tool.generate_targets_metadata, + targets_directory, target_files, version, expiration_date, + delegations, 3) + + + # Test invalid 'target_files' argument. + self.assertRaises(tuf.Error, repo_tool.generate_targets_metadata, + targets_directory, ['nonexistent_file.txt'], version, + expiration_date) + def test_generate_snapshot_metadata(self): - pass + # Test normal case. + temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) + original_repository_path = os.path.join(os.pardir, 'repository_data', + 'repository') + repository_directory = os.path.join(temporary_directory, 'repository') + shutil.copytree(original_repository_path, repository_directory) + metadata_directory = os.path.join(repository_directory, + repo_tool.METADATA_STAGED_DIRECTORY_NAME) + root_filename = os.path.join(metadata_directory, repo_tool.ROOT_FILENAME) + targets_filename = os.path.join(metadata_directory, + repo_tool.TARGETS_FILENAME) + version = 1 + expiration_date = '2088-01-01 12:00:00 UTC' + + snapshot_metadata = \ + repo_tool.generate_snapshot_metadata(metadata_directory, version, + expiration_date, root_filename, + targets_filename, + consistent_snapshot=False) + self.assertTrue(tuf.formats.SNAPSHOT_SCHEMA.matches(snapshot_metadata)) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, repo_tool.generate_snapshot_metadata, + 3, version, expiration_date, + root_filename, targets_filename, consistent_snapshot=False) + self.assertRaises(tuf.FormatError, repo_tool.generate_snapshot_metadata, + metadata_directory, '3', expiration_date, + root_filename, targets_filename, consistent_snapshot=False) + self.assertRaises(tuf.FormatError, repo_tool.generate_snapshot_metadata, + metadata_directory, version, 3, + root_filename, targets_filename, consistent_snapshot=False) + self.assertRaises(tuf.FormatError, repo_tool.generate_snapshot_metadata, + metadata_directory, version, expiration_date, + 3, targets_filename, consistent_snapshot=False) + self.assertRaises(tuf.FormatError, repo_tool.generate_snapshot_metadata, + metadata_directory, version, expiration_date, + root_filename, 3, consistent_snapshot=False) + self.assertRaises(tuf.FormatError, repo_tool.generate_snapshot_metadata, + metadata_directory, version, expiration_date, + root_filename, targets_filename, 3) def test_generate_timestamp_metadata(self): - pass + # Test normal case. + temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) + original_repository_path = os.path.join(os.pardir, 'repository_data', + 'repository') + repository_directory = os.path.join(temporary_directory, 'repository') + shutil.copytree(original_repository_path, repository_directory) + metadata_directory = os.path.join(repository_directory, + repo_tool.METADATA_STAGED_DIRECTORY_NAME) + snapshot_filename = os.path.join(metadata_directory, + repo_tool.SNAPSHOT_FILENAME) + + # Set valid generate_timestamp_metadata() arguments. + version = 1 + expiration_date = '2088-01-01 12:00:00 UTC' + compressions = ['gz'] + + snapshot_metadata = \ + repo_tool.generate_timestamp_metadata(snapshot_filename, version, + expiration_date, compressions) + self.assertTrue(tuf.formats.TIMESTAMP_SCHEMA.matches(snapshot_metadata)) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, repo_tool.generate_timestamp_metadata, + 3, version, expiration_date, compressions) + self.assertRaises(tuf.FormatError, repo_tool.generate_timestamp_metadata, + snapshot_filename, '3', expiration_date, compressions) + self.assertRaises(tuf.FormatError, repo_tool.generate_timestamp_metadata, + snapshot_filename, version, 3, compressions) + self.assertRaises(tuf.FormatError, repo_tool.generate_timestamp_metadata, + snapshot_filename, version, expiration_date, 3) diff --git a/tuf/repository_tool.py b/tuf/repository_tool.py index 0db27b22..a6ca0fda 100755 --- a/tuf/repository_tool.py +++ b/tuf/repository_tool.py @@ -3856,13 +3856,16 @@ def generate_root_metadata(version, expiration_date, consistent_snapshot): 'tuf.formats.TIME_SCHEMA'. consistent_snapshot: + Boolean. If True, a file digest is expected to be prepended to the + filename of any target file located in the targets directory. Each digest + is stripped from the target filename and listed in the snapshot metadata. tuf.FormatError, if the generated root metadata object could not be generated with the correct format. tuf.Error, if an error is encountered while generating the root - metadata object. + metadata object (e.g., a required top-level role not found in 'tuf.roledb'.) The contents of 'tuf.keydb.py' and 'tuf.roledb.py' are read. @@ -4060,7 +4063,7 @@ def generate_targets_metadata(targets_directory, target_files, version, def generate_snapshot_metadata(metadata_directory, version, expiration_date, root_filename, targets_filename, - consistent_snapshot): + consistent_snapshot=False): """ Create the snapshot metadata. The minimum metadata must exist @@ -4096,7 +4099,7 @@ def generate_snapshot_metadata(metadata_directory, version, expiration_date, is stripped from the target filename and listed in the snapshot metadata. - tuf.FormatError, if 'metadata_directory' is improperly formatted. + tuf.FormatError, if the arguments are improperly formatted. tuf.Error, if an error occurred trying to generate the snapshot metadata object. @@ -4117,6 +4120,7 @@ def generate_snapshot_metadata(metadata_directory, version, expiration_date, tuf.formats.TIME_SCHEMA.check_match(expiration_date) tuf.formats.PATH_SCHEMA.check_match(root_filename) tuf.formats.PATH_SCHEMA.check_match(targets_filename) + tuf.formats.BOOLEAN_SCHEMA.check_match(consistent_snapshot) metadata_directory = _check_directory(metadata_directory) From af8d2fb98e2a072cdd4be42afbe60c6d76e76b0f Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Tue, 8 Apr 2014 14:56:39 -0400 Subject: [PATCH 04/10] Finish remaining test cases for repository_tool.py functions. --- tests/unit/test_repository_tool.py | 140 ++++++++++++++++++++++++++++- tuf/repository_tool.py | 14 +-- 2 files changed, 144 insertions(+), 10 deletions(-) diff --git a/tests/unit/test_repository_tool.py b/tests/unit/test_repository_tool.py index 1297c566..c2813dd6 100755 --- a/tests/unit/test_repository_tool.py +++ b/tests/unit/test_repository_tool.py @@ -274,7 +274,47 @@ def test_create_new_repository(self): def test_load_repository(self): - pass + # Test normal case. + temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) + original_repository_directory = os.path.join(os.pardir, 'repository_data', + 'repository') + repository_directory = os.path.join(temporary_directory, 'repository') + shutil.copytree(original_repository_directory, repository_directory) + + repository = repo_tool.load_repository(repository_directory) + self.assertTrue(isinstance(repository, repo_tool.Repository)) + + # Verify the expected roles have been loaded. See + # 'tuf/tests/repository_data/repository/'. + expected_roles = \ + ['root', 'targets', 'snapshot', 'timestamp', 'targets/role1'] + for role in tuf.roledb.get_rolenames(): + self.assertTrue(role in expected_roles) + + self.assertTrue(len(repository.root.keys)) + self.assertTrue(len(repository.targets.keys)) + self.assertTrue(len(repository.snapshot.keys)) + self.assertTrue(len(repository.timestamp.keys)) + self.assertTrue(len(repository.targets('role1').keys)) + + # Assumed the targets (tuf/tests/repository_data/) role contains 'file1.txt' + # and 'file2.txt'. + self.assertTrue('/file1.txt' in repository.targets.target_files) + self.assertTrue('/file2.txt' in repository.targets.target_files) + self.assertTrue('/file3.txt' in repository.targets('role1').target_files) + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, repo_tool.load_repository, 3) + + + # Test for invalid 'repository_directory' (i.e., does not contain the + # minimum required metadata. + root_filepath = \ + os.path.join(repository_directory, + repo_tool.METADATA_STAGED_DIRECTORY_NAME, 'root.json') + os.remove(root_filepath) + self.assertRaises(tuf.RepositoryError, repo_tool.load_repository, + repository_directory) @@ -608,6 +648,7 @@ def test_generate_targets_metadata(self): version = 1 expiration_date = '2088-01-01 12:00:00 UTC' target_files = ['file.txt'] + delegations = {"keys": { "a394c28384648328b16731f81440d72243c77bb44c07c040be99347f0df7d7bf": { "keytype": "ed25519", @@ -754,21 +795,112 @@ def test_generate_timestamp_metadata(self): snapshot_filename, version, 3, compressions) self.assertRaises(tuf.FormatError, repo_tool.generate_timestamp_metadata, snapshot_filename, version, expiration_date, 3) + self.assertRaises(tuf.FormatError, repo_tool.generate_timestamp_metadata, + snapshot_filename, version, expiration_date, ['compress']) + def test_sign_metadata(self): - pass + # Test normal case. + temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) + metadata_path = os.path.join(os.pardir, 'repository_data', + 'repository', 'metadata') + keystore_path = os.path.join(os.pardir, 'repository_data', + 'keystore') + root_filename = os.path.join(metadata_path, 'root.json') + root_metadata = tuf.util.load_json_file(root_filename)['signed'] + + tuf.keydb.create_keydb_from_root_metadata(root_metadata) + tuf.roledb.create_roledb_from_root_metadata(root_metadata) + root_keyids = tuf.roledb.get_role_keyids('root') + + root_private_keypath = os.path.join(keystore_path, 'root_key') + root_private_key = \ + repo_tool.import_rsa_privatekey_from_file(root_private_keypath, + 'password') + + # sign_metadata() expects the private key 'root_metadata' to be in + # 'tuf.keydb'. Remove any public keys that may be loaded before + # adding private key, otherwise a 'tuf.KeyAlreadyExists' exception is + # raised. + tuf.keydb.remove_key(root_private_key['keyid']) + tuf.keydb.add_key(root_private_key) + + root_signable = repo_tool.sign_metadata(root_metadata, root_keyids, + root_filename) + self.assertTrue(tuf.formats.SIGNABLE_SCHEMA.matches(root_signable)) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, repo_tool.sign_metadata, 3, root_keyids, + 'root.json') + self.assertRaises(tuf.FormatError, repo_tool.sign_metadata, root_metadata, + 3, 'root.json') + self.assertRaises(tuf.FormatError, repo_tool.sign_metadata, root_metadata, + root_keyids, 3) def test_write_metadata_file(self): - pass + # Test normal case. + temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) + metadata_directory = os.path.join(os.pardir, 'repository_data', + 'repository', 'metadata') + root_filename = os.path.join(metadata_directory, 'root.json') + root_signable = tuf.util.load_json_file(root_filename) + + output_filename = os.path.join(temporary_directory, 'root.json') + compressions = ['gz'] + + self.assertFalse(os.path.exists(output_filename)) + repo_tool.write_metadata_file(root_signable, output_filename, compressions, + consistent_snapshot=False) + self.assertTrue(os.path.exists(output_filename)) + self.assertTrue(os.path.exists(output_filename + '.gz')) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, repo_tool.write_metadata_file, + 3, output_filename, compressions, False) + self.assertRaises(tuf.FormatError, repo_tool.write_metadata_file, + root_signable, 3, compressions, False) + self.assertRaises(tuf.FormatError, repo_tool.write_metadata_file, + root_signable, output_filename, 3, False) + self.assertRaises(tuf.FormatError, repo_tool.write_metadata_file, + root_signable, output_filename, compressions, 3) def test_create_tuf_client_directory(self): - pass + # Test normal case. + temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) + repository_directory = os.path.join(os.pardir, 'repository_data', + 'repository') + client_directory = os.path.join(temporary_directory, 'client') + + repo_tool.create_tuf_client_directory(repository_directory, client_directory) + + self.assertTrue(os.path.exists(client_directory)) + metadata_directory = os.path.join(client_directory, 'metadata') + current_directory = os.path.join(metadata_directory, 'current') + previous_directory = os.path.join(metadata_directory, 'previous') + self.assertTrue(os.path.exists(client_directory)) + self.assertTrue(os.path.exists(metadata_directory)) + self.assertTrue(os.path.exists(current_directory)) + self.assertTrue(os.path.exists(previous_directory)) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, repo_tool.create_tuf_client_directory, + 3, client_directory) + self.assertRaises(tuf.FormatError, repo_tool.create_tuf_client_directory, + repository_directory, 3) + + + # Test invalid argument (i.e., client directory already exists.) + self.assertRaises(tuf.RepositoryError, repo_tool.create_tuf_client_directory, + repository_directory, client_directory) # Run the test cases. diff --git a/tuf/repository_tool.py b/tuf/repository_tool.py index a6ca0fda..4d47fea8 100755 --- a/tuf/repository_tool.py +++ b/tuf/repository_tool.py @@ -878,7 +878,7 @@ def keys(self): A getter method that returns the role's keyids of the keys. The role is expected to eventually contain a threshold of signatures generated - by the private keys of each of the role's keys (returned here as a keyid). + by the private keys of each of the role's keys (returned here as a keyid.) None. @@ -2957,9 +2957,11 @@ def load_repository(repository_directory): tuf.FormatError, if 'repository_directory' or any of the metadata files - are improperly formatted. Also raised if, at a minimum, the Root role - cannot be found. + are improperly formatted. + tuf.RepositoryError, if the Root role cannot be found. At a minimum, + a repository must contain 'root.json' + All the metadata files found in the repository are loaded and their contents stored in a repository_tool.Repository object. @@ -4249,11 +4251,11 @@ def generate_timestamp_metadata(snapshot_filename, version, compressed_fileinfo = get_metadata_fileinfo(compressed_filename) except: - logger.warn('Cannot get fileinfo about '+str(compressed_filename)) + logger.warn('Cannot get fileinfo about '+repr(compressed_filename)) else: - logger.info('Including fileinfo about '+str(compressed_filename)) - fileinfo[SNAPSHOT_FILENAME+'.'+file_extension] = compressed_fileinfo + logger.info('Including fileinfo about '+repr(compressed_filename)) + fileinfo[SNAPSHOT_FILENAME + '.' + file_extension] = compressed_fileinfo # Generate the timestamp metadata object. timestamp_metadata = tuf.formats.TimestampFile.make_metadata(version, From 81c274885902847ff60eadf62843636c9078c1f9 Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Tue, 8 Apr 2014 15:19:40 -0400 Subject: [PATCH 05/10] Add test cases (skeleton) for remaining methods of test_repository_tool.py. --- tests/unit/test_repository_tool.py | 142 ++++++++++++++++++++++++++++- 1 file changed, 141 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_repository_tool.py b/tests/unit/test_repository_tool.py index c2813dd6..9a9edaab 100755 --- a/tests/unit/test_repository_tool.py +++ b/tests/unit/test_repository_tool.py @@ -64,6 +64,23 @@ def test_init(self): self.assertRaises(tuf.FormatError, repo_tool.Repository, 'repository_directory', 'metadata_directory', 3) + + + def test_write(self): + pass + + + + def test_write_partial(self): + pass + + + + def test_get_filepaths_in_directory(self): + pass + + + class TestMetadata(unittest.TestCase): @@ -75,6 +92,72 @@ def setUp(self): def tearDown(self): pass + + + def test_rolename(self): + pass + + + + def test_version(self): + pass + + + + def test_threshold(self): + pass + + + + def test_expiration(self): + pass + + + + def test_keys(self): + pass + + + + def test_signing_keys(self): + pass + + + + def test_compressions(self): + pass + + + + def test_add_verification_keys(self): + pass + + + + def test_remove_verification_keys(self): + pass + + + + def test_load_signing_key(self): + pass + + + + def test_unload_signing_key(self): + pass + + + + def test_add_signature(self): + pass + + + + + def test_remove_signature(self): + pass + @@ -197,6 +280,63 @@ def test_init(self): + def test_get_delegated_rolenames(self): + pass + + + + def test_target_files(self): + pass + + + + def test_delegations(self): + pass + + + + def test_add_target(self): + pass + + + + def test_add_targets(self): + pass + + + + def test_remove_target(self): + pass + + + + def test_clear_targets(self): + pass + + + + def test_delegate(self): + pass + + + + def test_delegate_hashed_bins(self): + pass + + + + def test_add_restricted_paths(self): + pass + + + + def test_revoke(self): + pass + + + + + class TestRepositoryToolFunctions(unittest.TestCase): @classmethod def setUpClass(cls): @@ -545,7 +685,7 @@ def test_get_metadata_filenames(self): filenames = {'root.json': os.path.join(metadata_directory, 'root.json'), 'targets.json': os.path.join(metadata_directory, 'targets.json'), 'snapshot.json': os.path.join(metadata_directory, 'snapshot.json'), - 'timestamp.json': os.path.join(metadata_directory, 'timestamp.json')} + 'timestamp.json': os.path.join(metadata_directory, 'timestamp.json')} self.assertEqual(filenames, repo_tool.get_metadata_filenames()) From 666bb7ba9e5d572e06c3e9c47f536730cd31ae6a Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Wed, 9 Apr 2014 12:43:31 -0400 Subject: [PATCH 06/10] Update repository_tool.py. Add remaining test cases for the methods of repository_tool.Metadata(). Add test case for repository_tool.Repository.get_filepaths_in_directory(). --- tests/unit/test_repository_tool.py | 316 +++++++++++++++++++++++++++-- tuf/repository_tool.py | 5 +- 2 files changed, 300 insertions(+), 21 deletions(-) diff --git a/tests/unit/test_repository_tool.py b/tests/unit/test_repository_tool.py index 9a9edaab..1ddbd12b 100755 --- a/tests/unit/test_repository_tool.py +++ b/tests/unit/test_repository_tool.py @@ -16,6 +16,7 @@ """ import os +import time import unittest import logging import tempfile @@ -35,6 +36,30 @@ class TestRepository(unittest.TestCase): + @classmethod + def setUpClass(cls): + + # setUpClass() is called before tests in an individual class are executed. + + # Create a temporary directory to store the repository, metadata, and target + # files. 'temporary_directory' must be deleted in TearDownClass() so that + # temporary files are always removed, even when exceptions occur. + cls.temporary_directory = tempfile.mkdtemp(dir=os.getcwd()) + + + + @classmethod + def tearDownClass(cls): + + # tearDownModule() is called after all the tests have run. + # http://docs.python.org/2/library/unittest.html#class-and-module-fixtures + + # Remove the temporary repository directory, which should contain all the + # metadata, targets, and key files generated for the test cases. + shutil.rmtree(cls.temporary_directory) + + + def setUp(self): pass @@ -77,7 +102,27 @@ def test_write_partial(self): def test_get_filepaths_in_directory(self): - pass + # Test normal case. + # Use the pre-generated metadata directory for testing. + metadata_directory = os.path.join(os.pardir, 'repository_data', + 'repository', 'metadata') + + + # Test improperly formatted arguments. + repo = repo_tool.Repository + self.assertRaises(tuf.FormatError, repo.get_filepaths_in_directory, + 3, recursive_walk=False, followlinks=False) + self.assertRaises(tuf.FormatError, repo.get_filepaths_in_directory, + metadata_directory, 3, followlinks=False) + self.assertRaises(tuf.FormatError, repo.get_filepaths_in_directory, + metadata_directory, recursive_walk=False, followlinks=3) + + # Test invalid directory argument. + temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) + nonexistent_directory = os.path.join(temporary_directory, 'nonexistent/') + self.assertRaises(tuf.Error, repo.get_filepaths_in_directory, + nonexistent_directory, recursive_walk=False, + followlinks=False) @@ -85,80 +130,313 @@ def test_get_filepaths_in_directory(self): class TestMetadata(unittest.TestCase): def setUp(self): - pass + # Inherit from the repo_tool.Metadata() base class. All of the methods + # to be tested in TestMetadata require at least 1 role, so create it here + # and set its roleinfo. + class MetadataRole(repo_tool.Metadata): + + def __init__(self): + super(MetadataRole, self).__init__() + + self._rolename = 'metadata_role' + + # Expire in 86400 seconds (1 day). + expiration = tuf.formats.format_time(time.time() + 86400) + + roleinfo = {'keyids': [], 'signing_keyids': [], 'threshold': 1, + 'signatures': [], 'version': 0, + 'consistent_snapshot': False, + 'compressions': [''], 'expires': expiration, + 'partial_loaded': False} + + tuf.roledb.add_role(self._rolename, roleinfo) + + self.metadata = MetadataRole() def tearDown(self): - pass + tuf.roledb.clear_roledb() + tuf.keydb.clear_keydb() + self.metadata = None def test_rolename(self): - pass - + base_metadata = repo_tool.Metadata() + + self.assertEqual(base_metadata.rolename, None) + + # Test the sub-classed MetadataRole(). + self.assertEqual(self.metadata.rolename, 'metadata_role') + def test_version(self): - pass + # Test version getter, and the default version number. + self.assertEqual(self.metadata.version, 0) + + # Test version setter, and verify updated version number. + self.metadata.version = 8 + self.assertEqual(self.metadata.version, 8) def test_threshold(self): - pass + # Test threshold getter, and the default threshold number. + self.assertEqual(self.metadata.threshold, 1) + + # Test threshold setter, and verify updated threshold number. + self.metadata.threshold = 3 + self.assertEqual(self.metadata.threshold, 3) def test_expiration(self): - pass + # Test expiration getter. + expiration = self.metadata.expiration + self.assertTrue(tuf.formats.TIME_SCHEMA.matches(expiration)) + + # Test expiration setter. + self.metadata.expiration = '2088-01-01 12:00:00' + expiration = self.metadata.expiration + self.assertTrue(tuf.formats.TIME_SCHEMA.matches(expiration)) + + + # Test improperly formatted datetime. + try: + self.metadata.expiration = '3' + except tuf.FormatError: + pass + else: + self.fail('Setter failed to detect improperly formatted datetime.') + + + # Test invalid argument (i.e., expiration has already expired.) + expired_datetime = tuf.formats.format_time(time.time() - 1) + try: + self.metadata.expiration = expired_datetime + except tuf.FormatError: + pass + else: + self.fail('Setter failted to detect an expired datetime.') def test_keys(self): - pass + # Test default case, where a verification key has not been added. + self.assertEqual(self.metadata.keys, []) + + + # Test keys() getter after a verification key has been loaded. + key_path = os.path.join(os.pardir, 'repository_data', + 'keystore', 'root_key.pub') + key_object = repo_tool.import_rsa_publickey_from_file(key_path) + self.metadata.add_verification_key(key_object) + + keyid = key_object['keyid'] + self.assertEqual([keyid], self.metadata.keys) def test_signing_keys(self): - pass + # Test default case, where a signing key has not been added. + self.assertEqual(self.metadata.signing_keys, []) + + + # Test signing_keys() getter after a signing key has been loaded. + key_path = os.path.join(os.pardir, 'repository_data', + 'keystore', 'root_key') + key_object = repo_tool.import_rsa_privatekey_from_file(key_path, 'password') + self.metadata.load_signing_key(key_object) + + keyid = key_object['keyid'] + self.assertEqual([keyid], self.metadata.signing_keys) def test_compressions(self): - pass + # Test default case, where only uncompressed metadata is supported. + self.assertEqual(self.metadata.compressions, ['']) + + # Test compressions getter after a compressions algorithm is added. + self.metadata.compressions = ['gz'] + + self.assertEqual(self.metadata.compressions, ['', 'gz']) + + + # Test improperly formatted argument. + try: + self.metadata.compressions = 3 + except tuf.FormatError: + pass + else: + self.fail('Setter failed to detect improperly formatted compressions') - def test_add_verification_keys(self): - pass + def test_add_verification_key(self): + # Add verification key and verify with keys() that it was added. + key_path = os.path.join(os.pardir, 'repository_data', + 'keystore', 'root_key.pub') + key_object = repo_tool.import_rsa_publickey_from_file(key_path) + self.metadata.add_verification_key(key_object) + + keyid = key_object['keyid'] + self.assertEqual([keyid], self.metadata.keys) + + + # Test improperly formatted key argument. + self.assertRaises(tuf.FormatError, self.metadata.add_verification_key, 3) - def test_remove_verification_keys(self): - pass + def test_remove_verification_key(self): + # Add verification key so that remove_verifiation_key() can be tested. + key_path = os.path.join(os.pardir, 'repository_data', + 'keystore', 'root_key.pub') + key_object = repo_tool.import_rsa_publickey_from_file(key_path) + self.metadata.add_verification_key(key_object) + + keyid = key_object['keyid'] + self.assertEqual([keyid], self.metadata.keys) + + + # Test successful removal of verification key added above. + self.metadata.remove_verification_key(key_object) + self.assertEqual(self.metadata.keys, []) + + + # Test improperly formatted argument + self.assertRaises(tuf.FormatError, self.metadata.remove_verification_key, 3) + + + # Test non-existent public key argument. + key_path = os.path.join(os.pardir, 'repository_data', + 'keystore', 'targets_key.pub') + unused_key_object = repo_tool.import_rsa_publickey_from_file(key_path) + + self.assertRaises(tuf.Error, self.metadata.remove_verification_key, + unused_key_object) def test_load_signing_key(self): - pass + # Test normal case. + key_path = os.path.join(os.pardir, 'repository_data', + 'keystore', 'root_key') + key_object = repo_tool.import_rsa_privatekey_from_file(key_path, 'password') + self.metadata.load_signing_key(key_object) + + keyid = key_object['keyid'] + self.assertEqual([keyid], self.metadata.signing_keys) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, self.metadata.load_signing_key, 3) + + + # Test non-private key. + key_path = os.path.join(os.pardir, 'repository_data', + 'keystore', 'root_key.pub') + key_object = repo_tool.import_rsa_publickey_from_file(key_path) + self.assertRaises(tuf.Error, self.metadata.load_signing_key, key_object) def test_unload_signing_key(self): - pass + # Load a signing key so that unload_signing_key() can have a key to unload. + key_path = os.path.join(os.pardir, 'repository_data', + 'keystore', 'root_key') + key_object = repo_tool.import_rsa_privatekey_from_file(key_path, 'password') + self.metadata.load_signing_key(key_object) + + keyid = key_object['keyid'] + self.assertEqual([keyid], self.metadata.signing_keys) + + self.metadata.unload_signing_key(key_object) + + self.assertEqual(self.metadata.signing_keys, []) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, self.metadata.unload_signing_key, 3) + + + # Test non-existent key argument. + key_path = os.path.join(os.pardir, 'repository_data', + 'keystore', 'targets_key') + unused_key_object = repo_tool.import_rsa_privatekey_from_file(key_path, + 'password') + + self.assertRaises(tuf.Error, self.metadata.unload_signing_key, + unused_key_object) def test_add_signature(self): - pass + # Test normal case. + # Load signature list from any of pre-generated metadata; needed for + # testing. + metadata_directory = os.path.join(os.pardir, 'repository_data', + 'repository', 'metadata') + root_filepath = os.path.join(metadata_directory, 'root.json') + root_signable = tuf.util.load_json_file(root_filepath) + signatures = root_signable['signatures'] + # Add the first signature from the list, as only need one is needed. + self.metadata.add_signature(signatures[0]) + self.assertEqual(signatures, self.metadata.signatures) + + + # Test improperly formatted signature argument. + self.assertRaises(tuf.FormatError, self.metadata.add_signature, 3) def test_remove_signature(self): - pass + # Test normal case. + # Add a signature so remove_signature() has some signature to remove. + metadata_directory = os.path.join(os.pardir, 'repository_data', + 'repository', 'metadata') + root_filepath = os.path.join(metadata_directory, 'root.json') + root_signable = tuf.util.load_json_file(root_filepath) + signatures = root_signable['signatures'] + self.metadata.add_signature(signatures[0]) + + self.metadata.remove_signature(signatures[0]) + self.assertEqual(self.metadata.signatures, []) + # Test improperly formatted signature argument. + self.assertRaises(tuf.FormatError, self.metadata.remove_signature, 3) + + + # Test invalid signature argument (i.e., signature that has not been added.) + # Load an unused signature to be tested. + targets_filepath = os.path.join(metadata_directory, 'targets.json') + targets_signable = tuf.util.load_json_file(targets_filepath) + signatures = targets_signable['signatures'] + + self.assertRaises(tuf.Error, self.metadata.remove_signature, signatures[0]) + + + + def test_signatures(self): + # Test default case, where no signatures have been added yet. + self.assertEqual(self.metadata.signatures, []) + + + # Test getter after adding an example signature. + metadata_directory = os.path.join(os.pardir, 'repository_data', + 'repository', 'metadata') + root_filepath = os.path.join(metadata_directory, 'root.json') + root_signable = tuf.util.load_json_file(root_filepath) + signatures = root_signable['signatures'] + + # Add the first signature from the list, as only need one is needed. + self.metadata.add_signature(signatures[0]) + self.assertEqual(signatures, self.metadata.signatures) diff --git a/tuf/repository_tool.py b/tuf/repository_tool.py index 4d47fea8..292e6d02 100755 --- a/tuf/repository_tool.py +++ b/tuf/repository_tool.py @@ -1117,7 +1117,8 @@ def expiration(self, expiration_datetime_utc): 'tuf.formats.DATETIME_SCHEMA'. - tuf.FormatError, if 'expiration_datetime_utc' is improperly formatted. + tuf.FormatError, if 'expiration_datetime_utc' is improperly formatted, + or invalid (e.g., already expired). Modifies the expiration attribute of the Repository object. @@ -1135,7 +1136,7 @@ def expiration(self, expiration_datetime_utc): # Further validate the datetime, such as a correct date, time, expiration. # Convert 'expiration_datetime_utc' to a unix timestamp so that it can be # compared with time.time(). - expiration_datetime_utc = expiration_datetime_utc+' UTC' + expiration_datetime_utc = expiration_datetime_utc + ' UTC' try: unix_timestamp = tuf.formats.parse_time(expiration_datetime_utc) From 4ddd5a417c26ddfd1219144af834116244d4ea74 Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Wed, 9 Apr 2014 12:52:20 -0400 Subject: [PATCH 07/10] Update repository tool diagram. Add missing 'repository_tool.Metadata.signatures' to the diagram. --- docs/images/repository_tool-diagram.png | Bin 90045 -> 91913 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/images/repository_tool-diagram.png b/docs/images/repository_tool-diagram.png index 36d444f944625f44099f9f643a1ad1a1ed3d16c2..e8e6f2898b4788f535e3151c8d7ba18466e937e4 100644 GIT binary patch literal 91913 zcmce8c{o&U{P!6KBVkY>VaA%WBxT?nJ`J)*g>*Wp8zjii# zAliuThc1zXR!7r@XEz?>UZ`08H&mgqT?A_Ju-MN&lFnVg@_1xd*g1ObNzD?O` zI2=pUa2fJ;8EU9j;x&t~W*jf7f0^aaC=(AUTrV8v{xj@Dz@Ib2 zUIK_ezlV1m2X=alHSD_|LXxu`h9m~Zss88{+N!D8uU#(b0uwtLJ2vQ74$0YR^eO>i z{~rG(kOBVT&vEE5*$&Bcm|vqY0Zf3eXN*jNf5&zJ01x}$HgHS#pOO6fuRq5)1b|!4 zFaW$ zZ&ffUr2B5Q7E{ralOg`JHVrkOIpKa$?&R#h=f#N5Q0!M~Plj}t?T)BvpdT`RiAFEg zFnKp@Tw|gw+NiqE{M4wW3KpzCgCVNGOM__s8u4+!>0Qy@n$3vN9=qzsN#Y%2HLBU1 z^VfhBU^OCoZ-_D=gWh0ndz|fX0QmRgf!OjvCT!H!R<`5^&guRv7^lUBjPHGCm(M2n zO6vGf!q6o*fPuTlfh#RU>ib8wkH%wmhiN~@`}%jraC@^^70uiYo0*$RG>XPfj)!Rc z{lA~hAwZ1vO5KS<+BH}sNg>pmOCs8E@1x-Mmol}vD(?~Hl_5jxda?dZ0T_Mr(Kqw% ztotfLAF4jrOG15bTRB$sL=ncWzB3R*-TS-}us%`9 zZB-+s;S#W_#)MuCX-Js(@9c#M06g~K%TC$F^!`!w-9Eu9^(SPRmTQTREA~DYpt&Ic z$!5jSdSM*Oc8nQF)uGH3AK(_;-RbY2J57u}-dN1tFg4~`WwV>j6cr9|9{W3anyU^2 z-xGkv6M!vrIif>#M!iLjo|fQUN9((&Qn+#Kqmf3*Dt0ZkCiw>4DNev$O(fkM6(y^} zu0=q-Xj_?`uEtNHNqOV-o~qVBbimH%f;+w78tvDpk?zRE{S1cc|DB9(Wl&6g2r#@c z8_+d%!;AmNGF6hx)MXu|RzS;c)rQts<8pF5Or1RAw7pOj&UI|=MtkOe zYost0_c31uw^QiT9vO+#R&jr{2 zxrELEwqEG7H{u$MArr+d4W}i5W}REZBqW{d7sv6Pa$z@y>tV9c0Z+|X@QlCLVWle_ z09ke4p*+utaw_sZ2Ep86KGeDlY>u!8P&yogqfV`^&^plL7XVvgy~M3aZ`u!n{U}9& z8Ss&Mcjo11dD}jX;)8zt;3fQC5aBGsfTpWQs^>Z$Q<6~6*fV?$c;f~MT>EBY!SyH@ zQa>>?TJefZMqqS*%+=ycqr38Aw$CSS9M@;ng~g0t2Mm&)3Y!o_e~v%6%^UPzRj7p1 z0Z`S@fZmbOq$-Ul0E2D3xzlTFwEI?fq2+`xdok_;dk|ubK{)RlrGf*Xpw8Fy8OfCV zcK}6iIzVm#esl4T4dt`*v7%X>+QwOp?dR}S z!v+emq8#4y-zPJ7eZSQG|0`%_-RXOi~_d~X|Ye= z=~NbM*!?1SOet_W$-c-^FjF9KJJZ3NR#bsgQ zpCaFnP5ULW-v4i9ZJZx6qy3a&E3BKDWnxFqD99Kk9cNj(IM{hJ93j}SNkzL82lxa7 z2+6wE=VHl(y-&j2)dx$3X9vEUSFIGRC|1(A1=NMgcR$w9zBLI8HcV3W|CYy8^p#BD z9e!_u30>DopC!|RKpz0O`%Ry01uP6U+*ip-EAu8$Q9XYbe z<-d=W5G{@6nj50-!c;PCwxNjmtn^q)=VM)0>4%>@mK(85ox?M)jQvve1(caZS4_zR zBEZClS`nP#bAQ*O*P9zIv}v>jPDTH_X`;lq`2k?YyYmzJ5|B}_w~78~>^M!MwzBWO z9G5{QD`%qSD2MJ1>Qh}#0$I_Ed92;h-wnv+D39)S@_YXk6`#!%0iN`TU$q@veZSYA zq6|1GJ@d!aE>7=PNR^+BISm1Px9q+~J9>A1tc)V6Y==PAHkyq2!RIdxU*O@^L^wKm z0TZ~Kj?-`_1PaWAPgioS^U>z&vC6P_=km8)>5G?ub{rU4VMG$&U*9z zh$E=1_)W(6tAiE&m^q(-)>I~rMoB*2@3CK%qT%MS;o))er0~6&WG2BtCr1cIc{-U% zfb!2U3BPu!@4ilysd1@UJ00?#O!44qNlyjb5@t!!ViZ}}lzdivvUT*qm&U%_cWp?k|K7#@NcA-EX$ zJq??c-~hrn)v!?-cA%sK`xG031sGTH8?vimx_22Bv!?fp8w|i6+?!Plr6V<#A@3uP zq%uuTsqTqoSi-m*uF`#|nu$}@QZ4CArMja#Tl{Wf-(jz}@^0Vq;&hKMg{!V=XRsNB z5Cv&N-ri!bu|B#7NtgJ;6z^@Ri$t8h%-C+^mrEd9(-sT-_qvMqC+U31X+Id{{2Lul5OR{9Z;NjibBYB;A9%^%L{IiWh zw@p4<5tB>bxGZapX9a3w&WBK5qH>Eayy+%XMXlebRTgwh*Z7EEh1fpZF}0t0JonT~ z9G%5-Ih!Ch8ty#mA9UJXRf}6V3ws0=^=O)38{O`S4T#ce_x0*lm}kkG`k)~Sn266L zO>UTf9^4Enw32koBM$vr(&! zx9FTV>`L&n#~7@hr^Dvf-p{mr`PHSH*feLR?H)e4n$ZH~&BVdg znH;W%g(3J)sPx*N27du>4gk(wDNSeCb+P8&lJ|mIIty|&Tp}eH*b)Uc9%BiY0i_}T z;j*do6@=KFfI8Y`w&t*fYW17h@LL%x58gcoG^wt%OM7wBcdLtvqj?f3Uxp}q__f;J zKrToI3j=oJCcCP*stihA{ez}%tpo%JdetEQMk9(K<#xmB{Q%$ZwrWcI001xSeaT18 zKD*8V)pN|1)8YRB1=pR+ftxK#y}}uUD>|_!AiNyAxtm|`H~UNV=rrx>^q1QtT%ymu zi@0o$d~F5rd!-r%PA)y&_GcC*9*z$0kw^bdjyDvi>r z{Q|)-`YAvJaH_Ux$JRy$Kb7@ojk$M(R~GqjZB#ArY9HQ*FevRmsH2JWfmh@k83$$? zTo=EV+khS6aAVUN7sVVSNBb*o9xY-ZrI@>iAAdMoB}S)-W3Uzi?#lahF1!x|FX~(1 zW_)f7uFfS5KZAThlblPyApH{|j3*S8)P2WbnT5%*$uiVgTU$i}#hT zE?|#Q`+Zr7Q~1QT{gy10zN_WzjM`n#H~>Q>#h#Wb%YXWP3@&5$0EtR^K6(w{VGvb~ z0)UuM;7SSVi7F4fJ*kba!Q!8m`I-)S0U2sar`dNg zy48535*$Xpn&6W6CEpjj<6?eF09nogO@H-}wg>Flr5 zr^tPF=Shz%H81*@OYN=X$}$|+T}*lgc^3gR%B9YpFcrc-ct;HVT@~Otl7Pr5p5_e3 z6CwGh-V})-h;OMk$DmVp#7>FW=K}Coy%lgB6NE7wrvyR7>=KXId3pyWUoStIM~1D< zFL&c9a(D1fM0q;Ra(g1NxJqC3cx?Y`a3P^iKD{X5%}V!zdPRRQ4Ux`fey6jpM$rRo zMOCwn{L}3CJ`0q(m_ag_8uEs%3fbZ%?sLoIAp(g6ueXY%;eXJ1QKx9GHYGZbu? z7G~OP0daXc3VS!L7hWhrkA;OA7fjEo4&60_0FyyyvKNE}*gf1}e>xaNQ3%j=VDfwq z%I^HOipU$ztARHBo$}h*2r>L|FV6a_fbHjFbQgx;A~2XPBsRp~uW#I#8K_kBa4zS- zdxcoy)cSOuaRxm1K%O3v6LIk5iGRLL0cZx?Un8HT%08LFHLRQQyi@AUn%7l$#(n^z-Yc{0=oY^E^v)^rl z2prmwSU60_Dxn8V^7tZGdK~F)6pAHSIh_Q0X632`n>8$0V`WHT@R<4$dSg=R-L$}H z36p6ySEM0!ci)S?7JvD~vJxlLaoG=8`)Pw)J$Yt!_IU|AU_~YAk?T_v4=> zM~;RdHqH{0q-H9Psz$G?Tz+!Ze&y#S#>#TW+rrMRHg=21LTs zB>H)~?=g-K_S#fLo|aAtb>=J2oXk>ydUBJvy}d{{gzE_R6w6a#+|#`%7RuEmy-oc< z;c`tzn1NUe^`N3$x9}}-a&8AhtIX_@8?@D9NhKeI$_Tt5AX z+UU3UbVE4hiz=FxNxOx&nYX_zdWXVcavwO!qw>dZHK(YD5OxvcGvX!WV;@aLn$vfX z2^Y)mH79e#d{-oA3yQxJyf;q}b@)t0c(S+6@y_h{kT>d+=3Gj9#4Nln^&X!03_Ab2 z0}Yqs1Q5QNyfgWNoe8cWv5}^=n$tH_)T!96uKoGAi_a*6EmBwdHyeTM)C6oN#`+WG z43h@Cm!t9>hURZtlJZn&zMKH3!YlvC2`dcK#c1=4KV_Pi88Fv>4r@GWBPqRS8ajP~ zR{)-yXw(K$zR;SKuE1GCiF^zxbNJ%*H;W@*i6P;`RTS2u*Oyf83s%eq#xO|?gvGcH_!ac8{cSDrtIxwQcvAIfycP@Y&yEF zi~!39=T*%v|5?W|*VSSf)SaJHE@L#B|9*6Vssz2ma`mi$bBo&jj=ZglCJp;-V?lvQ z)!s|?zzcs7gXLA~f*|cvQ(0ndK%K(&JqfORFViH3&EpEM4Kt;;ZrazK9qk(BS}37o zuHA1z1#9*($j{d{yPKX$KdJldL#Qp++TLna?H9knd4JQnsG*f44eGEbKS^a%MX11u zz4f~PKC4lE%v*J3+fmjwaTIpHCbv|2T~5nt)VH4{I-XB9w0bEx;cq)ti8vAr;uv(u znYgeEQAtV-;dDSKS~qmuf477?S9!lro*32Cv&AfnM4ff!UHGTEK>4cMK$@etbk+%Gg zjKs;d?`*7k%;+l|4FJbFqDwq2&SrP3lO9ESYT;HVuAA8@5ax>7Shs1Iy)AHOt2h?X z%2Co%7HJtmB$v|xo}bUcKc(A8(WWvRyk-=gTiq6(%D;9-UTkCXePal8UGLRCVam4f zs*eTbWcKYfAt7h?P5-51E40U9(Wzk(Xo|;@qj0|qC9tqfmLiqlckU4HQ%J7NGK=~5 zS_uWz!i%kF#2KMkjzGPcGlZpb$8CLwwVerv#@O)u+?=!@TxG2DiKUK1*jZ*@YyFkv z85swmyFH?uyXNPq&VHTxuWnGp=EVpyHRO6>YyRGdSLz|WcRK$hA^%=0`@L5O<5M=S;IMJa375vI>c6{{g)jmAUGkeZ3?9Lp(>Uyx{97eF zA5SW?&I=R3?k0@UxxMZuy7i7-X;qZFEr;y!X=yF~?!Z#tA(`L{qD@Fz7MHu#;~XQ0 z$C6#fBvsAhxwuj(J?jU<_pYm>UUA-7sAbB_`#^{X8rK!hk#Z7Ffse|_RSV&*XAZ7?)ah-{f3dcYWdC*)}Z(!OTUf#`TXRlFjSN z`5dk=)i7Sa`kPvqy99)HczD;`duPc2*j+UXF;`h)YeYC8#I!vf=wR0B(t0wB^YkS(SGE?%L)$P`pX9IXtJ3GKv^8@q$8w;U zV=J>rm!$I`r3NP%n?FdIsmiy87pr#oe&_I|s369~Is9YopxHOsDC~sGs7`&9IKh{) z2;NxV%*ulvayuM1x5`o;|1NimrVvci?!wpS{@uyjFgjuHK|Ju;vzl#eF1XL_(kARj zNe_PLnoxB!zk=fm^6=S$7yxU&5<~TEuQAl!j2T85cyj}Ka^pQVttaogU9(H~2TH7< z7;OKD95HP=eV4Cc{mZ7C-G%L%qQq!d5Cg)C@11dcPHr&>AH2Jk!1Lv+U0<4dy8H74 z&ka#vv{DR0exSCFHVBk4ZhesOCTnr@WQMxJ=qReKdPmaYR(Y!G6>n8JRnry@;7vt* z1q)Yh%fgarMR5KUJgbTYY2|YvIkTQ$CM(x-&y=t|!io7NcZ&PanO*m*)_>O4BotHC z=z|>HE+{e;G>0K(ifUd&GtQ+fC!Q9OL81KYSdegR6TGN*62B-0LCB4Hly@u&h8`>q zP8!Fb64wB+V=t62Qk$bmsr@Jpc?}(XgF7Wlv(dOss!QaZW&N>vZCCWyU zaPD8smk8lEuEDYdI3)a%J&F&K_)uJKx4J#lLBM?`Ve!Ceag#q)mqi?Sr-$mbn|}~b zjsY4me(h7zwa)=S;ziAzLFAhmL51DkTPCXElKogRw%3SGa|;;1n*I8Sc3T~?3wy-i zzMX;hVL-2>AH1`x0<9Fg&}UBk++DcWOl2+`;oCJZ0RP0ece9*Yr~^$bwV{j};FCCk zDXXS~>*XlKX7Sx4>L~C2)}6r^;q()R6=JCuB1m1hH)i>MjadR~mN&YdtWh#(E&KQj z#|2CU;wm`}aqFl9z*;2lQu&{bMmiM$+EQ+I^O5@xVs$3;NYEdCaXH<{qWyXKe8=_b;D8?0{v{} zC3yV8iig~y1ATk)mIWzQ<}e`S+R1>!mkmoB{h>JLt^fv)9x={)o|Fo|5uEfm<8xUh z(^Px1JtGr0_@UApO0(yF3>$a^&IWydnz*#f*~pMU6#7bh@pyA|j8br=RP zyE8HqiT_M%#neh41}YN*R=q`AXWaS`m?|01c-U6bEStpam{qD$6rd^4on3dSBpo%3 z8r3mSkqz~Ur9q#EKI+myPMvio#TQIg6Ma>MvL&-hTL>8fxn}9iT|&EykG?(5u3R-< zI;$ZLm?Z9+XGPl^);m)_9xs4NayY*+TW~y7dFtN0;WeKlKlClT@A!y(k$&+_I-F-- z<68WJi271R0f4nSMk!H#_U)Z>!{17L9l(@oU*hz{B~@c{E}6)+hVf_)@nXe4`10@H zGlVWY1bc*$cyJ3j4#!l=g76Iv6n>Ep9(;%;avhWkAwyMXEL-`>p-_=1!!zs4%CgJ(z5A!neErLYEgi1r{8k#t$b+f$UdNBgOXQJr&V|#MF)k` z!!Hi;ccq`6+=Z{*Yd_KUdS!J)S2yDe@}JC2vZ}B{L%hGKYt=@~xA)8o5nHVuI=*nB z^5JB5W_J0zE1wG8cv?pu*zcK3-r5*>YcY)a|f zmu2=pl;H>qfPcHWIeRcNDadZ3K0>89+u^2Xn;^%0$q``ca476+h}hJvuXiN>mQxwv zZZ-=Cz#yL+mrFJC1)2=W!Cai|8D(>pO1$-SNcfs(n2H{Av^vmZo3fx-D|idF6#_ zAHPB82LoKKUE&j{(x|ucpTS$g_SN>Gwm%x+|5D_4b*t7UC@BlKeG29Hl3KpI2_51A0F@=HxT~eOBHpDv~VWU`2bQoHey>!AaU`>y9dK%#b=L}8~WK-6%5DjaIB^6 z&TDMFcCmc3b=Ogi6n$!aUzB5Npz5OoEo#;jgc=JSzMA=DS9(XOQJ%`=Q$JiigZ=|_ zE#VN1)Fgt2kFqX>cs8@NecEHd?8C1zE3jyH8ShEI?Qh)&n9g7;@sQ|a2?5-)w&)k< z1^2!*S;6{^<>Z7%)cC-NvfByJcE*Cy=%)i+luCm6OLbFClO#wJeCcsb=>4 z`kxPq%-w5PMuCONs@=51;3n8BpJ+NdD>1hecVnvDro+z?g7JD)=Vx(=889UEF$6m^ z01HumEtl?iDbY1vMP~)-IaijV?Odprag$>$QsO16DXqI85x6DpV%mYaaiwuHQ&gB8 zwQ5Ry4YbTh9{~Q$g-8ZWJrp6r{GJr-=xzVi&vnj9#xhRdfKC_Dk`$BQ42va!ayi;r ziD@4yS3dW7cRiIz^u=pxUws?i=tlY^DMCYcdaT@-%dxIl%C~P_xD$=Ohtk-iwfEOf zUgw{=u~_C3I2^O1GxMeY7=bnSD%jUblGT; zYU}4Pw7Tuf)+JuPgzH&TE2^Ox)WjG=A?@kYJq(1e(kRcKaWj?#z_MYjr@&tPvwQHK zDyL0`KhM=~IepLn7)+!=hPmlG4SSw(7k;!=a3-cfiS3pJf8qJU<`@s8T9W7!m|H$) zT>jZ7uA1Qn8oCUzP`9^GmS9+ukTFy#WJgO~$0re5%@=c`OFKV968i-o%qba_WYf&r zY9%91n;QT%&SLfG9nLsRz?COfAiCC3l3@4LS67UJy~_hM)w(-pxU>a=UNRDgMSuX% zdti-Q2j#<>vwW8Zs}z<%mxA`5gmK!o*iY*&VdDWAb&8{Yd)v0ddQ1xC(B(9X;q>|_ zY>e_N={DirGM)J~V>r)%7S<6hP)-X@05h9pmDyn0TFCd#>5`$zjeaWy`m&AcgK9HPUYuM#O_+TXt}DZ$KwA z;Ng6#?7G_KSE7Qz5s&NLNcJA|-sjhWW(pe;kc|F&?J7c@p4%;!qZb);RgEA((|uE` zxslwtz0*st{CfyJKCID<3lN>)BhDkA;WxA+ zm5ay1fW~SPWw@NK@%Rnvz7ddpzRTR4t^E4o4La>QPZ>;BN51-8bpC-O5?n2=-8QVX z@|P|EFF4<<9;`H=0~(BbJ*PA!Ie@MTDoNmPk-h~%eB00$NoaJ#-nR?S5hBZFrfd4; zNCBD;AGp@yx5TAJdP=PQN+ppcA3^O#_;RSS#XlLS9t+E_`dRfXfce~LzehLYAgAjk z0Mj@Feh%p|Jo^oI!*PGQrpt!lj;n>vaxTDDyt&e zl%~o~rtV|Y=agj z04J`?s5n5gUax8VJr_{&wW^%Z=V4UK-~~3_^)3oLS|ZH~o@Ny`cwb=%rn<-zDtobq z@>&ZJ=IJ>zT@cfV67EVt__uM*&IC-UZu|rWdtdrXV$roSZ$dUA;tl~(_^5B*afM=09q5*!%8a89t|kJ zI;}W2dAqah7&~)17r+URqK35|GXpTNdsGEjAnBYs*SwlYLI6pHY@3k2IY02ALv6uP zsW|IPz9eMsy7LhYk@$6lQx>b17#h5WT2p;|tEn+okz_sW4hnSl8&Q=}8=F;dq30Eh2BrnF&l06;uCeFKWlJ0=r>F9C-j3|NcgBB#2UAs?;q=;H=pn4I3eW?gECJ_y`qSf~P84eyOuGqc$wL$>43R?J7e3!3|wK73A)WJ^iAe#2e zO{t*%Gu<;DMWXDRTnb;+=GZ(`j-GgwN=_1QDA`L1#E%a%`dKEAne)!xj!+lcxU=s< zlO>5qKhp0wWAE0nP>|6Z_S0mfkaI*pGy3^viA*FMEJ;Jdp`Ys zhgto1)(U9wr8kv=bPU?(HYq>*X_FcS(YUPy59$%%M@vsrc}e}86@VEK8nD!rNee%X zrJcCqrW)Ms#(>#y3@!89d79JEP%lIKEv1K_lmVQQtL{uhPFh=7IU^%MZOQApqAiBd z7#QabdAO6>f(TqzZKa!=9IH3(N(gv3M66BBVFA3yHa~|E%Bs?VHGFJJ z0>iS&84a@|B{|&7_R)PaFr79n%uS+>QKB}>S~x;}@MWrlLNsDY{Q=H7T$qfs*%)1( zf9GtYt%_VSA~e=V241J$Eb?gYNnFd4YMm3)y1QVWlb)bT1nIgk4N^+P;EvGw~IVdsYPu=#}2 zRaXPP0=9lXvJYb(GkK>}hakSjb$?|f?gc{`TGE_Ovu;YjdgUYSB$A&Qz~3ku!k-_Eal_2t>Pa{8-r4 z$2YD*Fdj(JRC?y%EZ6+lM}ZM1m32clbP!38JCDx6)ABEi8^ce-E`$%n4pP5$F}3U5 z#P;BqOK)UugBG-KI0i3Io^}LAl-sfu?LEo;-_4MAqo{shq@C~}V zWj=agIUD4ghc2%+OJ2J%<$Ovg*IZjOrCZ=B+Jxbs{a%%yz&ONI#6V%%jDHWBN3PJ0 zWscnF>+x8L0Zs|g@R8j`4(x>?PCDsv1#5z{iA>Msy16UH^zF}I7v-cL(>XI; zWstOfu-pDe%Z7QVNLMymKY&M#_%u(e;g}k6xG+cDc}QxLPcne#Wid?HP~B(+hTX zxo8;M5tq;l-?I(C$8RjoTA)2`km}rjOT3`eyiQ!?eF6$(Mxk|n&s|gfEQEm2N8a47 z3(qN?vCoGR5zCqOh$b%o-Ba}ZLh1hN3dAk_4w>@9d3uJxl}i*8&j@{qim7EQ>8`d~J{!H;9~%t=CJ6ZYbS<9fW5YzC=`a6G zdK^-zbwm4szHe}?rqc;uo+>g(&SCP63Yx->YrcB1#_DjCfrEH&h4I@n1zl0`aABAb zUVuDkO@cbG+>`?;^&ccMC%8xB7w=5vG zrQcI1%x>c%z+Eq&tJw>(F&l83>tFI-`vtEKA1~J`G*+2r{??}Mc`U}(hE5axL}>NV zq{wdGfwR7S)Y!z=1j_jXfbX)O<`+L_)dyYDDGNm`r+>GJ+K>tCp}X0E+z?yc)f2LO z=NJTIEMP;HKru)zdlpdQW41<;eFIa3baVA#<30G95%(Zzwnr%`$TTisA)S=>J`3|G zX^p=O^q1IP)nD6bbperrsV7TJhYNT|@!wrem|Eqf&q}NHXfSN3xyE_0mr%*zyJwYLRE2o<-vAFmF-H%%oN-K*8oi_qU?yVv zp(1>|B?NPBkc34L%xyaHGgo^^5-D$KRg{tIz#GRdWInR`y#KJR!+FQj2p#BiP{YY} zURih_X9SW|+Nva=Rnm_jK0ozRw%`8kfKDV6#axvRex!Uf-4&cv9S8N~=D05d8LFn9 z{3{1YWeyR#f9JfbHXYEMlM`1qeDH_cOFPz;t22S&*BLj&MFFgS0&nC%h8X#o9d_5d3577 z=xTa?Y%|G6wApb+l?C89t%o{6(4*+xzU@T%$?N=Beg(UG&j?%H~V&LW2HwGB*&wDc7)2%|4#5VSuQ+|EnMN9U4&{ z0`$Ixvp$x&pqcI9P8C}GSxTh&I9^%jpn>4+^qqcQ+9HppGCn=-dhq7!MeOFH8JojP z>uS;Hv(5=TSg+;tsGdeQ^N*d2qH^t7xTVbpsD~v`Owyv$V9Ux#*rT@Ya>FAOc}&k` zDRyUb>P5=sg;-USXy%JI2*SoZV~0lK&?RYi4JKoq0XZ(?x;1p<#sih;+Vh-TNb=hm zE&`;hxddUM9Yx1#rv&qqCCLv~04BrqFCXu$dDn73aQ(P03G~eIX-T3qR${9Ij@LZ6 zDCIr4@t0aZ4Q3G{wbbO&lhVGg2yE8l%$P5YewV5NG%b;8GxWjDK%nkH*Rm%wZUVa4 zBwBS;@Wk=irX1%6#hzH{{?IHV^E4&BQyKa3 zzMhriYO?ftB`>mAlT;Szi8L1>IlO$__xpWH#jICWb_}ikzScN1U@m+;&TcJ$%7r+@ zUGK_v(M;`Ld4SG*i2Na;((hXP?9fK{&CZQPbwgYl+m$eEd-WytUH?*HU6cS~4JLLr zT!ehI>a7{!**EDU!#tjssXwnOot&QyplmF-@)?aR5S=J)xXst{eoy>qi%U1Cfz9vZ zL#U66fT24Np5W|L_bMI#_KJymz(9qOGyNMY>j>c8(lW%XY3kh9#OC|dv$ZVn@MhwZkjXUY7Tr}R3AGtY8w7R$VV$O*!sW1&vF8oP$mq` zik3UqV)E>U`(D#EE*V!cpd|~&CAeuNXACyGcBUb2H@Aa#N)BawzL~+F z?bZI@30uy zmPBP5T!WYfiF;u53Y~@yW~yJ0Au^F^A3j!ePOeNZe_h*mP+93axwVdutbit|_PLb+ z7@z?r4Zj#s$HfiyXdXX@*(C^2#(dk z1i86Y@&Zq~)}pm0Rrt*8fD3w^iRd?NgKLsI3|RMt(`Yw#M;B==4~J4*hBQ{?PVD=A zUjpw0dUOH@w27}+-w0b9Nbk`Gx8s})!v!}{pq-^%cMs9UUA&aED^A|tRMp>ZL8O=k2}xbVI92{*sz8#DY?tl z#Z)|WM)*0O#@z0*;O_RKZzaEnfUY+VwCg@4^_-49GApL08(XD1TZ@v`x7904@LaA% zy*5MQD}<%8GvF=~B%I@yD~vy=SO zbIPFpy*i(ecHn~NaHr-I{QKn;Y6075jwzGU5&xx>aW4@X9KBG8)8Edmv9fP97dBRR z*BHqg2OVgF$i(==cl6}v+^nqV0OmPbs@yrR>F=G}`=U)n0dKaHHmuwD?KO^I528!y z$=!RGfDHAO!1=sBVxME0)jq>s9#yy2L>_sIrtr1=xFf6;Iz9&a-6DwU&6P(z<0(5| z=h7`J=xKyxKhoDCVZuz^mGK=jw1;P^ zn|=A1ag=c9xkAQY(g!r5Wz5mwNQ2W*m=-`Hj32WvcqDobshY}CJbesCU-INwo=Dob zBI9M>XIwrKF5o+<7|P1Y`Yptg z>`tO#?{^9}R7S@cx9{`wfyc#XUoLAOcvpdQMgF{UHljd##{|UW;gGQc0%P%m0+Wj*`nWniTtX;9Xs4&mr10j&w2bUJ%Ipw0?_^?q-L%aDEFSvd1AoD6~~eZ!Q=R zJ9R9pkW(-G=u*-wcUD`On(==A#qpc{L+}{S)bCu}Zu3~EH~^T=2nwqWRrf=7qlg^5 z53ElNW}{t=7aRpatbjvZPX{#^hvg0}13^QZFY6{$omx3R-1oq|qU-cNjCtv^ldwn`XNxj@ zy)k&PPGR>?-|3(wa{7iJ(>-vdYzWDTZdY<2G)~u6)S00;If4Y_LAd$bR!ERQOuu?w zWY{^oEYKARqxRrWmHfnu^pj*w)2LokN$MXiQja%IzV(5B4f`Sd&!Sv}`h)K20|3fd z1tmOtl%YLO_r+Y$y1w)1ryL_U*p?Jvf>w3Rq$2lbK)FR5;R*G<1b zLGV^e@&;^_E#zj7Wt#`e+F!GLwObO8+_eM60VPOAg{bG5bE)QqCdB(9;YPpY0gSxO zkXKuODuDIS)1oW$5C;YXlE2sJgU=GW#(B_CE-O$V{@weRO3?e5|1?w3rLQ`6g~-I2 z2+@OiAIlc-H{=WnkzOMo$r2zuRcY8%#tG+FC6Kf!2>dmlHb1W@VKyu+e|AOgGw6rk z{NQ5Rm#AoB++#7WAy5>Dk2D^QAVnI8(=mOZoBC4k1$hBh&ip{hVyN^pKvTu(L5(P# zrWf5rl3QcJzPWk7Z4Eyu0*EBbuQsyq4mK(;EzIe@c znP1A#z_k&KbHp5v-Ry1am!|`6Gd!f=m%~!8ccnS%eb9OJk`pyU7g{TY%&r)o?i!y8uJ2$w2qrheHB8eP*96Wxg{*^dWSE6d#l>Fzg!>|5 znzwE7YQ|5$fhFyCBzo!@49L?u^$z;JV{=~)|M+N%QGl~j7Z@m|QNUqrYp4Ful~aM7 z>P~W0armC>!vZFpT~Xr?PX!ym=D>(WaNlDmauN}`mS?eb?X)r{@L*M|R`7br1Qx5` z6)I^QGd5FEE;R=2(PC$NK_Q$DuUF&u)K*(k)*R9U!m(Xcks8QX?*|t{!8TYo|Hm5Q z2TR_2dMJT=c53j&1r05Ae@iA%iuoOWPP7C~3R+VLM{JUl|MkpVorL%Q9Bolz=WP0| zAZROBqQ#$pG7KJJ@2~w!K}eGN@;-DXZoG=AWCI_1M_9Y=OW^W(KJNwmhyJAAeR*4% zyzE#y{l%mrH&gAnOFQQz5_&k;5Iv#jcJyZorBOJTk|7-~*Qh20eQqG*c|NW0HIPLZ zw`8xH3mj3fFx(o2z^$&xcWSUgFz)uuFcIZ@*&3ShdE6g-j{rWO-|vLnLuMKBlNP(Y zdQC&^ogVrdHV0dkH#~wD{+%bLpaR(RSmf?!zTnpJw8pLVw;ld=z9%Px3^b>-@9R5; zAhicIR7=L;Z~qk&4p~O78?ba;TYuM#sL6D?N;c0E3t?wLPd7xBBN{7{w`#sLwzpJ?*oqA3r=v|z`(Bb!C@35OQ zJ;t?yttz4K{DQLh{X=OzW@%R6l?#peBNrO{6Z5?W{M_QBmHE`oa4?Se$9`|_6=o@Q zFi^I9Qx*dtkZ1 zypnf;@p-CfI@6x?(+f4-kNE7kl@1a{UL5*9C-@;wnPhIcJT+P=GgX7tstiMCpzglu7nAx8lz>pId z6AxKkM*#5gAbx=a9t$n&c+kbC?e+9=lh>POf*=~+sd5} z1!}$%_x<-S!qqG8zXti|ofML-GEJ83D^=g5FTf38(r!9Y9?@ayKXW~k1`lU0Ph5Ha z;>>39en!@LZZTHASOG2&yH&qf5lAzBIO}NU7F;P=!w86dXLisat?|{L{H1`TmV!AU zRrR4v%?I2~^5Ve5;F1^XTa|2b})x^~y3Lg4E2rm>zX z5BhV#Wk$}?Etr+(?DcaK%Zc?5()#r^)*j2Hp?G^|edkZ&L$B!X2f|Z_d5!Euo_;2H z5H1d#^Lv6c+)t}|j^WlvRQjNyl)@%u>UhmBuYbYP0pIcc$V0yt>tW#Y=k?Y?cG)2@ zu!Csv!KIM{Ki9md6|_%0N@3k>To#sCHvlG3Lqrl7+p>mw*Zmlx4DB=Ia~FV#!3zxo zoBWNNPIAdW=C2Zc97x3iz|&d-ImKS})p?mBrrY-asMEJ8Puy~a&5IlVG6^Lm!}&mQ zB&BuTw8SB8K<;?5vUj|U<(2*SK!`DU^zb#vz6h1ne#X-i+h5{7yX5JcPC4LdfRZ&( zk0xl@aStBhxa}(7efEW%7bk+Yp_8731zDB+sH$^fLTsqo`nfkJ)6Fca4+G`vu*z_e zaB>HzO5m@q2Yx9&0qBanH0~$DQxrQ@?)NL_&!;Lt5U6RzmDPRGgXo|7Ng9lmf{>~Z zG)m{w(MLp7Zb?sl^5pFf-`Cc{#}K?I<8xDpp1f^N#SA_OP3f^DrL`7`ag`lWmr_- z_Xm20Mn+O)=x!xNx{**x18D>Vkp^jo5TqNVqy!WY1f{#AyBh|n0Y(_QhPVg){+|DR zckja+Q5puqzDH8|Uur}&2L?b*VAZc&5+kmJ7;CPZwN?oBO+0r{Gv&~~RLt50N_?wa@wwN*ir5$n0MU%As zZ4>^eVo0eH+~OTQ{EcyLrubLeYGl`kcf}=|MrVSP(TT0sZJkCpzAH8T?N9aYoUj)`|f zJ)K|S#=dW{npf2J)6v1Qt}+_FLOa^QI-3J8i{a&a!lm5Q+`2KpMNCX4SrH4pIHshl7jPVtqYbiA?o1KjV+s~>DKq^UqCDz}R z&LFk~s>E265?T=O%TUFPK{S?OZ=8@Wuz|!uaQT^KlM*vl09Em;3AWDs+3L_Zl43eC zwEGQy*stozF=|!3(v@)WUcaA2fU~)PZPS+CaW3xgH*BRd)o9CRM-@sTRVKWIf-tT= z^Id&={D>ACIz}gTHZp;UISU7>Nraa1hDQgf##>{MQEGd^h<2IA+3K|MHstBQDwr&{ zO*DRHA0v%jsdiDqp7=r2Vy3oOO5GTA*kwut7K*PEPs?$BD@#mBVZ%R|)I*malE=Os zv-g}`NkNo@Ni5?C>~cd&mNzVCvz#PMbe)ks#lTLyKHB>XYmja0WdNeRF25uNDfJS2 zc9?Q>GPxUhe)soAo5X(nN={1d)+IEM7%LS{K?W2Mt#usg&;-X0q2$KI-FJqM1NB3QQgJH7U zDl+B~Eh-i$!Rj7rm%X+kD0VQkD6wxLI#F7ae&oH1xoc(%$LTk0ijm*20JUpUjLn*U zIXm6YJ2zcUi!E==gM|L0?rBl{fFCq_)KapLpC611aC<6rSNGcwp4eV;teLc2>TX^q_*2c!uBQquk0f5>=+?^|a~b=KV5RDF;i4Ko7-$f9T!c&{z`1^if*gQr`lX2$9c8%;ej!# zi_CrZ%6<9}pjjZpzIa(|RdP>5VrU|*^Kgu+$+qrK01EU7<=G|@04ch;f2<@3w0#F- zlrp=wl&CPNAV#`{3uI89dBffIyaNrsUU0wK-d;GY;yqcJf1cT1FXIuUEO)$)bQS57JFsV}7UCE5H}L5(KK=KM$^+}zYcb!Xb9#Yd zp92=)o!e{(F4Yr$(M`FUCGE79mvjdX2x$c$P&`Mqb09|%z=~(X^d^Pr4|ZJ4AZW5s zVPEb4SyaVJx7UJz5(~+GXO_iN9FxI;_Wrr0=-y-K`v_3+@$}$y!%LeT2x9+B)UA9= zlv>>mKsBbl-y4zPZc%UeFtyKjTtd(C@!&a@z$VX_ZTfH2ieTBnb3mmiQ2lawRlweSTF z`i?tC*y|LnRd`{IH+D$X;*$=tzsG_XH0B9l-tsSiVP9|7{+?4YPJ4aIh$pR6uH4SN{gac0Cq=#@Ch^vR?qfh&^O!4fF`E+VTi@u8l-Bu*6NKY)!DQGcymHmgxe0TslC3ffMtIZ6M zaEV3AT6`9aF;crbx$ctU@7Pat`a!eLmZII>F%%^3Mi`XkNQ9o=iTkuCprG8Oc(9q> zGFSScNREW?CRe_BCdBnN;Sm$?PG{9U|-wbo!+;maF2?E?gB%A)R|@E z(3(qOVV>)X9g=mKz6#xq+Q4cao4I_hpZGw>zP@BrkS@bZ(yX+P+v>UFm%6EFSIDBo z^CXBi-U6M~AV`#=`}9h@EjOKNc{RC#zj}001xOZ2GMKrf#DEshAH2$&b5gLQni_)v zpoDVmJF-bvwTtw-VXAs$SXS}R!#hcH<{F^7t?gB%p1P5nj;z4g!HN1qlR{I4s|n73*Qk00 zOZH}%NUHFFs|g1a&~_og`-#OYQN+3$^dhZ@p){D7W|fpP|3{z}rwG5jEN11kip=g- z9-0Cv)v~1(p|W+Cp!BPK2_bDz`GkeN*|!Ml5323HUBtgIT#8XaO1*b6SY#r4dR*wp zE%*lM!vt~Em*E0EOE>t^dKm#tmaI=9`tT7J*owLoN@buVg%0L7 z4KJ^a1fV~L1Rq{J->3UcJVIOI2kt%odDCJ_ z3UES?&ZIv5D?-F-&cN}bk142i-P;Wo6HvG)RXA3}ribiG^viF`kqy{s&ap@kZI60BsEOP4$M0%nHq8dY)@nQD&Y z@iEHNTJqF_yl!A@!50xpXGam~LGYW1;k}!i$(2Mk-TM+8`44}5($~ude&_-OHGKn= zG1Z&$8M-@a*3ymM9H>otgp)ta3y`UTdfkg|g^3;=`yA>v630MEuq@<{6<4xZ*JoL4(Cro( z^qN?me0h^g;XC%`N9#gMg^K~5DxVQ0oNo)X+FWA}tY+ zHi=y(@Zkq}*0R1QBy~_eg4^>`AV@t-h-37Lt&6W)xhnhcD;p%viw%Rl9}w z2BL|By0=9}22a(;4lh-O23Nk_<0^bFH&W0-5YLXTG~%ldFH?+0?HPVsQn`)4IdJp& zlMoT*+a~*4=oVw)>!Vx~s0OfP=Sr!52qY6gzS)KMg<1Tgy|IhGZ-+XA|CV2KRmmIB zS7|YHOT;0>W6#))em&=Ix4@RjP7^t#%t&Zf2>8 z%lB6qIF}bZJ=;@jZ!um3w%E^b!+lq*G>=u#@26EkEjsDgO}N{$ zwZ8Wt(a&g|S0-$gXqYP_t3ASA&a`#E;#tss^`KReSmQdl=d+!)r-|UAZRa-2D7_WO z4V2+#r(R>mr7!C3nRG12z38j)$a}?dW%e9y6wswplw{sz@{fpG_&_GN>8#cJ6t{r3 z5WR0xDDq8VFTOVCE=%;SHmozB0aZgbJ>RH!w}JF;mJ8TvM2;oa`(9M!l>W|SqtzTp z=kT^{Y7H*e)eDj*0 zq$Z??;kap7+|^)hd4`b7pK}~Su;Dm3eDo%bl5(Fu*(y{(<^4G;qe-P3&CP+$2XLl{ zG1ZaP)R$P1ONFE|bn*$5D3_TDU&p&%fn zEm@*)(Xw0~aZ@(p*1hUI?WCHE%uwqQJ$y36eoO}RT5g6u?Pq$!Siym##FcqMVxW8DlE~1cne8* zxQC)tBZqKe^VoyLyjKJsWMK33rx$~#H>?b#>vn&DZw1YW}!t}?t2FWSjj2>0}2kdYHw7UA;em0>s z?L$5&P_*PFNbNQ`%u8Hd%lG|ipPk-j)J^HJoO)04_pKHZ-AkdFpNf!0Z&YelfB-VN`$^R+E#+VTnAzy0B#qoum9;?P=$ zF-$5OV5Z-z|8^{DtY**`g^)_*i-jLXUoBO_%Ve&>wW`+=DBYx55y>+9Nuxa{x6q+m zV7A`%m-=fiC?ajx9eEw-aS^$K`72}smjDPhm}}LY1kmCMxb)VR(cda%Pdva(e1O&_ zN48kERDx-jK2AGrqX-%I_?6?s0a;r&YO0n->k&AFk}+v>Gz z6@%^h)wRr7f0D&YQ0Qz)XRfaD8X&C7K&q!Pjde>POK~mJt?!W>ZRX-Lq!&3d`XyF@orGb;BR`-#aPdlpJD)2Yuo9^qlUU zGW)JLAJ{ERrzz{lY!`##t;5_WV`7J0FH)_Zo^@P@C?47fcU;w3POcj^<(6o=7Jn%b zc&tv7doMpcWAZw3cf!Lge0+>dKXO9+Nz>|u`h_F!>6-XJj&uz>JiOv$o+aWk{=0!{ z@a4nwl*`DH|VRrg)$KyU`Z&+f@AhTm#;$ry)oF_`c5G z2l~d#UBRjE>?HU9d~X@lhWH6P=C(AOz1u2Ily+@I7Ynwis#j6S3&?6Ff2c=2 zoSYcM8N8Q^Gtvg%`|iiOE9n^F%d{Gk88aP|50aMkBwa)vOP*b1zGX(WL;&fdC^@W;U&Jk9i>Aeq2(_ zlPIPeV91P0(sGR6$ZwH7WoeYxDJW-g%I#Z;KC0a>rrc%)+F1OZV(C}2p}KiS7(5SF zx~Os2E%e!pPJT{7Bl(~}6qvO5zB#6JOq6gVubjohzdEl!4zVJ3%!*jKw?uH|Bxx?S z^^{QbA_&z*k`38#@Eyo$0>Oer&zy#?r<-5HfP~;n!HPc-m$K=9fkv$61zcdv8V?$5 zHFpLJ@_tR)j2{KLbv39QN=@?+Te?B=hz>Gn$Q|E|vJWH|yTUvtIic5U%u= z-KRO888?)`_BV3fMsyqRE%bJIaIr8r6@NFU7VP$T$(9eD0t0pCCL$?%{cf0bhzki& zC3M1)$6A!|p7nW~J3O5&%HZLHpkHk`?5Yw9Pa8&Y753Ni+FTso}3W%9{$vULjMKJiZSyV7&nkT|mAP zgApC%M#sUc=k{-|3w>R=hfeV3o4V=ehK)`{~fHVLs11b}+mltcN8lt+a%Vbh_sfhrY3~vrxipv*!W2%EWt#iRM0uN0UBl z@~|Qr7~JIr?r!m%+Qq0}MQPbBoQf$)uNNBj4ODSWa@^c(n9yp&lDs>jbpBYk+WvOb zY2q_T=zevS*7)_c!pKS{om^Y^4C92RKo6~4Mg%|Q`Rk{o@TfDmEKE9qj=Lg>p) zMn4h;2Ju^4-u3&A!8^OLt%ATeY!(%cPFRs_^NHqCN76Jc9-nu8{GA@RbO`(WTzoX& z2CMCM11Q=^>5{;Hgy))&vOL}%gHSL5(}ug1d!FL!x=0jMe=&7=oSB=x=nQH>S16qx zo#m=|#d1U`_^4)@o%b$&a9-X^h1@<=gOA|7#2a^!O+3n`>*9F52AdRY?#oK8{SC8g znG3Jy&$<0xnjBbnPT9ydS|9XD7j#(Pv^>?Pg6jGfaJL+RA+2%-aN93aD^OHU>v1x;$%sPz;*RX-Si zoSGR@`J8dR)z;#VN3e^dh|yaz1}BXLHjlwb$tW6%ey%%xX?d-D15p1OHm zNVC`NSBY+%2*N!wlCNXSDgYy&uHf*!1#1HaEzk0E>&axEZjHmm{0vym>iY|+xlyAT zosoqc;hj*rp`>-Dy*QmSl|(vd>vbd23Sgf?E^&bg}FR))_I|-m2i`m{W0e z)l51M#NnBYor&JR(^}Njn^Bqfjd#9$@*dfy&=ylbPRa;dk8q%HnE<%6TevC9ULS{wzz@Hc*PJos?7WI0*U*Eo|$w5T*_~50|!T=Hrg8o5KBb;+1o1BZB!qk#s&<>J*R61k15rzQ61&NeHKz@K4 zob8y3Y=sxt+3Fqv+%*d-t}Onz=$iemx>(l8&Yz>Kx;NK%l;{N&|I z4#!QnUzVo+udWz~1e$~Iwf)o&(ZLg+4kIk>>l6>@13^57g@SiqC!Eob0h0-9ICi@9+=Jp<5oUdY-S4q%b+b#Kye7t}Z@rk?xu6uv)qb9zF@4@4%(fByxljrw@m z+1m|f<;YcJijxgSEiuZcAfbK>utU5uD?bl)({YiCJ|2BTMV`%pK6i(H7}}=BX3VYg zqJDkTZGnLdOW8aUeaEA#w2D`SbTRQ*O(URmSKq<|^x_NA7$O*SA89TB8e?<@aU(oV zI*;ygdo1%*uZM5edG`s|szJlFktvIP#EQt_2;%T=i{6M%A{csGwjw;y2a>ez@sVvr zjUQKnU2|v;Mq@(~=7|$MQ(tTUuHJp~A0YBT@cvnr-1^DOU9wDS9P z)2mJX>e)2ZX#&7cIYf9Ho@9FpzBU=BQd@&;@9H6IOe&AbNJZwu;;ELAKE_OF`C zUK`58^fy=OIAgELoP{Dk1q!Iu{h0+Ofc)tP_jA8dOya~xGWK{e2Ik?BKUox48Le!! z?Gip#^QNjVViun;G-E{N{zBImmL7Xw-EjfKMIo)qidcKyZl!SoJ@`L6RFdq;5aEB` zQ9u|y)*7BAv4639E47y1Za>C_ARbxE$c=ZSUwu3~mQ0YVDiOmlvSj$e&1yOch^ZFH zeVFpJX*%Ug6xUgukCm}5H^90EnA$hCk7;z=d>#Sj&1z=iCsMZ6;h-sIf*w5bjbaSS z%6Py*0<@>-rQcB7h*M=|az2UrxacWl4dv z2>8rBtpuU0CHjiH)A|zFv9%XE4DRN#339^4#W^Pa2_j^rp!1-1RU2>ArS~VddhP;9 zPLS~n5}=OFvc$r^31ywK|>$R^|*Rp6a)O{9hsLV;UA01)VDr*;we_ zROFvGts?AYC*#a*6K)=XVX))JXBcwe>&T;;w--@MBzHG>X4`s&(eN3Jq(1a8lavOL?)o-IRJlCzzA<8x-yp-mC^1(Bikb{rA?1VMO#>0 zRPF8*fzVzfpm)*#nh^dVPfe7sSBm)rJl2YsR%>xC&47og2?F3>X+Gbzj0dyf$n=%! zL~^C-kn#wZEPIZ8D;+f;-$+1`gh_=gq^`Q7E|SVVLkp*l>(w2tD&EYQ*J>__JiES) z0xq*tXS`j}Kq1|rv&nfEC;4=h-lM7nZ+3FryNxG&U(85~iM&65chfPOFBrw#&AA1hDz@UA3b~`Rci}e*H+HYa zL{`_0yf!p;t6#>s&G1}AS5m`Ay%5V+hVKSgd)^d0VwaAp!_B(Z3vvQah>c)HZIR@H zq0AZ4w;A+Qrn|;v0*mn86Zsc1NOZj*@EY*X70iEc{sIk1=X0)-LAf0Z%UI5R%asqc zb+q^RsZS24G1VzYf)nDKvFn(-5)95!bR4CUlj;xCm2kQqJWnC?KcA2@oqxe)w@3C| zsh0<)xTkN?I%1JydsfQs{*K-Q<W7+K_>qDIO$kN%&oC*!KqWpf_)oltzo=0ASb8Z*d{Qjif9;A~4nUxf!r?cbC`{6mjr&Cw1pX@mbN*i#`LSz}&Z&v0pK5fL?>#0Sj+ho`M z%KslEi4p{jZ=f!9l{zMT?#457>K|1N*8ce;Br~&b_-{JMe_jziz$9GI^le(_y{%fQ zqZ1wCh10IX^kO!528e6{P4*duKOK`_)6=6rFCEA><^TOxh{*^+2pGJS`Sp7GPSL1^ z#p*Zj5H6nAU;iAQ=-Sh?XABBENw@3_ELZBMt}&h;fE?lbL9dJLvjJ0lvSMHK;oyR} z(vN2&Wlgh=JNVsMY)o3T&HSQviwH;ULO}kOK8j*C>)J^DLARC9d6d1&+|L@$LNMi*SRv z%~}U$dyq*eMPFPS0d~OH8~vublceK^k|F#PL;LXGaJMTy z$@<30>p2zrO~xRE)^Z&teCC-*^@Gl`QtA8YV#C1@z6=E(->;9& zWAVn*FCS|Dtzl2tzS=f1u;L>$!bu&^2UO`AbRJSwzlvEYLYn{3-EV6y9#DVnaH9sS zWz5*qQZm)!sMaUW_kE+rsc5$~rmbt|q8Fd#faXc9lDg{O&c=9Oh1Kv{pp1nlvKS_M z@5NDLY3&!6@C$5o2J0d8g|$@fcI~kG*v|k?HD^Mg1C!N3RPZ=OGzqN?uiEhmBM1)( z&FXWs=tp7VsdnpsIYqd^&<&T7DtdRixDX|2<|JUwh;41rF`gy3SrYn36Pra1j3plByvFlMb=MfaoIEC%DD;j z*(zGIlR*1aq9tP|LrCY6&K1_VD9ZR#Vh8>B6f+>)r_oumK)$r5*-*FGOS~-Tr4zj= zfd?~5Zm+E9aj$Fz$l!ixDzLtL`hBalDOb5JW>EdlX?m#^u|zFS+z%RoeIm9``?^PQc@Mg8HyWq7{_LAE6u zu(ORs#|Be*tX=dNEtDHEMsCBFM<1*O>oOt63R`pCTD2h{Q3x$kw z_LCjKc~qh|u%?m#^B|l10fT;3H*Q!p)k;?>RctRn-6h7T;5bBWacaXqQK|ZBqs6YI z&7}GkTV!+|d2RXyM8Kh8aJzz4g3&?&8THaVCF$B-{rQ%)a$EpsHFrAFp0R0{7pbo! zHSUgZDk&_-{L9^10@=wvyx?=!-e%jk7hV%1Iv}Dy@U3X_PYW^%G_`woG&JC)ezV@t z3AWZn`Ju0an^zbUrl}KopR5X)OWR=*XtdrR=<*8aT{S5Jvd>a_^l?JREN4nOtr%?s zBgAFZXUK3#B_7;Nbyl0Fe>_@V$OA0Wwby-E_U2<;Cy3t-n*l8WM$KQ*S_@M3G&L6H z!miz5uF(q7uo<_#OSt|*XT2K*$*bpy*8T1A!~gM5qFN9@1YINdjDgfqdOIEZc2nF_ z#eu+@3#&3D12VPSI@lEn%W{zcH$z|LJnap zJAXWq)@!^?!AFR<4wV*K{BxXoRG^!93agilXsma#Pb*3b5+LP65?WWEo*k4Dc0W9vN(vDN!&nS|gaI z!MNF*PNff=5LiI&C&+wPsjDz{qgMHPkr%QG`U2S{iX2;pM(85-u(2ixW0pPZ7tw~cYC zo(ALU5mFXHIxe(Pj_c>mp_HN5_lJdaXMqim7}2x+MHoq!_fOlcZc7MQx9ZM{ic`w> zLY0+sFTu!IGWOPx zUTteNBS`HN@$cRSO7EK{RE4lpoq%yyx`}?_yr;BWj1cbf&}x^>C+gytZG&CG(%+mw1<#T=1O6Ac*{bSi8!R=wCO zUQVyD9U3vj0WQ^==LmYo9yUgj1yS?@=pc8q->R{U?{oaZKuQKw?k$bs_*F+9>qd&# z8I_>?BN&qle_f~*d&&Mn*5Zpw zA-+IF9k-AK8H0GrT0QqSu-c}-AM44ihSx3CUmf{%0knLR@pt*!##7&q760@N`Dc+o zb#>(LXPX|0vSzIPNU)#LFYdnZN^0{8j00RXb0Gr9ko-HhWYZpr5C=I_u% zM3qY+;vt}NF}7+i&=~JxD}(-Rgm?MXMw;fIWd?L?5_oq45pr|^6nIL|b-~eCs_kk= z`R$Yj{}X5FNk^jw4k6`GqUI;$xpAm!WFr`g9aznmOH->1-bIQqY~2*z-%Q*>5afEqT3hl;__w-nx_WPa8)IwM8A zIiQyfZ-&bKXsf!EJ_(VBrFB>hj-oWA0|5|DG)DYsWOM54=k=q^&-SiD;_JO0^micB zY@$0c#9B_}TMTHWkzYb@%RkV2EJ`G7SyyqH?P;MOQ;8*-6wj4i&buk9z~AXy>EZ2l zh$Xx(hiCnZycBhW0D`)fnL@DlC*vxEF|*S7rY~5`Wy)6iLS$doSk5C;u7aB_g6On+ zJ;o!Q3moSYP~zA_)w}WZt67pRjMR{`s^nW$l!1vcdO6x62e=51#e}7;@Soc- z8YLxfz`)6w_b0(7{p|l2Is1PWq`&h+iCg)=reFUVek0DxC_rYIN z(urPi3>lwOlm5<_JoedpujvI^b{tpWk;>$)G^d}vcQxf%{~&PdolnvQ)jn<^u9B`A z8>7xlXY!JUs!C19@zP7K1h6TB+ViJqRlRx~l~;yFU& z_Gpg3p?Z>LIO(ERPr9Up%ZyKbn|$w!!!G9#!R0ljr?7Y8LETF6bQKG+`;aY7)31qt4xR^%Zj*F<1vtRwfAIa?%B^6 zqyYLi?ec6h;sTy#xfUCZfZYIMkXqD|*fGwHBp&qgabyeMrjpzgw!8NS5Tu9nn1JPa zH)JC44jk_g*Tmp;&ZE4otZQA%MKkCC7hy7pL)Y7Lav%?3ob{APOx)K0brm+OHc)kk zy1*xbA+NH2G2+ENCuGg#5aNbc;*k0I69_NIiXpc-Q5OpbYwD4f8v0QS0?(K+Dp(nS% z=VbYKSVh)Qe!0>kiO%3TwpR26aA2e2^zIL)GtNi@S@%<<)jag; z@-*`UjqgBWj+G*g!|EmWQjV#bI%AJh5u}J{0zb+99P1t4UNnT<42(J#J67bk}%yD;4T|* zqy||x`kBFyfwE@?su^Gf7Ypq>Pys=gE6o_4yjP{cDv-q#sz}xCU3M%#+wnS)V{cQ% zGMV44-o&3rJMSAc{h7p2@^OTD`vy4;M3`;U*lv1sOQHfX6IDbDd<$~a9Muhr+rv))IvXLYYsX&^Q3=dVvK(s zovwxhG;NJPIn(wTJ?}u?pgnSPD~4$In%p0dR{=P~vQ+@uG@x0Q>fWnhc?x?)XxFw? z=AO4Jx_{2e1}5=|07zh|jFcSN{br{hffgk6*WywK#I-F2n~KeA89d>7@x06f_G*tGGTJR}Ep6B*(F+3tn0&Ojpj$3U*hOTL|odl1=j zEHdcs!oJ7{!73)8Hdy=LGj))XF%y?|7$JS><9Tm17$<%=CF2bSf1d;#Sc2?p{= zNpBy9`TRl21n0Gjnf(pt@3z)JF6>qG)cXX#ll6zvB4mJK->xsmi#H`?vhrB6TDKmG zxncS3{E9{5{i6GFbJe=XFXGr@g-M>QPtZY&RXI#;gkX)pZ{^d``Ob>3ot~$IF&40F zC!9~+!~=T$YD6HQW%pX!e`V&5yj1 zPWW*roE_Mk)lL6Ovg@@X5?lV=GouLV7fU~qgn+*gSDoqWQYo&vEQpp<1kiDMefM#( zpBBX~$n7|WMxjeHF|X_qK} zTY>c=uXprt$vv&iIM{kL$PhYVm!RZdIEm*TOri=X#(pnkeWoBat3wk8C^>H1hUiFG z_t+}{D~)KujDpS~=an3D@TQM3>o>^WdF0lhLx{aS__E$d*U4J3(FC^m8&y*^F8Wd) zct*nFTPJcqqep8iSg+6KS+p#;9`GPT`x`oWHJ2$Oo{pn;DQI@=iqSQ(v3_+%v?R~B8Hq!_^s~y|gd?y@JwDZ#z z9G59?OfE8frx;9^2d(rlH^nch;LGf6A@{uj+yW!b& zM35LH#i|5+MNEyMkclCO^*GA%o>}To*dgD-<88+gT~nMN#t|RXaAa>r(vQols2}FP zS$j4Qd?xov!I3p|ND%eKpY1 z=m(d_`+BK#q$=^o`9+Ru9S>pWgQIRVIkVlE$U^U*q_G@&m&yYB6tlOEBjseQ)Uy9p z%&GyQA1uyYZ~1q#NozP6F0}o1LfOm6SJ0--nigxl+oJV30#juqL9~x<8>Ma5v*tyDQ#%Rkj4-pqrU}3FB{uqzIy~w_TB?$}q{alwUv#{e8%6L7hpgLB z=%WLnA3AqPwoh&f;=|`$p&3X@4uoZMg8BC$&X89fx#1D@3Bsn>0bi8KNCkwx-RFJQ z$_6wl#F4SZr(KL&MM6egD(_+O1fuD|XYe7`Vwc<{YuF+=ZEcU>tJTLP@F%znl~kjj z4638%UcFV$rT_JnjceDW)HfSY!Dm`!Y{&cXs2ZnPk}QS*xarIEcMXFW;omP_@3bxr zcBqG{mI~D81|$0oKTpUQH~+CQn*o@3{P%-_$1B*6fUf}Zhkv(5ELk=U0`O&6f&cp; z*WdHLvdiKJ(K5Sl;SiP3&lw$RK1%u6dnd{=b(dEnS34S$hw|rJ|Lr6I;kEUEuxSqm z5EiJWXKnrp_g&0jFqrI^I-(vMa6bsuC{9|e-yCetyF4jTH#$8NDZYy82xC*+r>l^E zDR_YN-EzCQ_}{*?&jIYTWks~J?CTq!gdZZ=!setWm&HFFy>3#V+^Z`d-#|%8lI%#{ zZLnXe-fhu5}dFSoFi`YcI5W#*M8w40{wM?82R8 zx7=d3v+<;7cMi<54!_(0J&3`Rj~T_4CX;~0s*50zru~%6OQTRORNuwEojqa769_8E>YLgH4oyR&;=j>}v= zMWKN!%hUcdo3dL-4A}X))S=}c%&x5$n_!gyzQMe6X>zUX$|mkyRr zN9ZHwo{_i<&7)c-Ekd9%=bqnkA~|qK&6yLt=ba5zulQo@rkc-wVTs@p{BX%f_4KH> zMhVkDuTb3Y=zIa9j9F?ngF?bOoDmm0oDA9xdNBqEK)#kJ;5I<|2m5TV4AdF@SU8$`npwlP z2jqb9fk*7$J{;>vI~$#~Su(%!yiePXnd*QI$*5q0Z!i180r^ZZWku>;b1br2qT zB4>7;rlf*v)$rckP7pipsK5ASmK><^B^_ z`$dY(%guYooVJ_GgK*T~{o@8{`|gwyj0s~~9ZYg>A#VU05155)%}*6<Gjna*3}p~;RHydT?MYq-$@2f!g8B<=8pgFiF)DuZKT*IOMI zBWWz)zEAxA<+4x)C_-7lcm&fi>1{Ej&!ap8z6Wb*r)Q2sOjgK*J@oogCM18 za~T7&2)=aOgRb^GQsx3q;c^pd{STEI2>&Zo0>HVDvI0(;?=*)r@0*f7Ap>^LKcD0P zpNB~8LYbf4&fpBdkX|%UdGFa{AMJlMk(;C`I5oP4=FhUK3{BV%fAv?+B94k?07kjxc!L zt3j!7%aos%G7&#Hf3RTrRXW|2G~J*K2Wn(NAR_Ijd(79u=}`vH>U3O2lS*z9i$# zH%?pOmJHjFCZtcM5{?3H)5DHA(pA($z@%?)fsX2H3dgi(oZfU!PnqXdLeDrY$khvVu#+xZ8Ifs(J&Rx1$(4h%4fsB z`j%CTS6ax2*rN(BLL49KFR89xZPOJ7Dnd68zI!>q>O9l`OwRV6@=vT=hspaF^D9kv zR5OqP-JSi%x99TSLeY-^hwW{`2>Eo^(O}Lk;fU((DiX+$^EgGvT*>WrsRH~M7S0Q) zbY^p%j(Z&%3_ROr^&zJX2kW;xaB~>(AU!fYa2XwuH21_80sM>Q_wdA#zmFw7c@7SBTp~whec_J()o#nLDNh(w$7n zN?daF%Or+4F{u5!`?IT5QB_S8K>A`&oyW6-JKDuQNnp5x1`W6T-ddFaGeCY%cv88; zJ1UUkzw_24HZIsy+b}j|ruJix!b=3h)wqN_%2uZ0SqZ!>PNQ1P1q?FR2D2M_R z89kJCl$0paB`7EYN>AyK0fMA74C(Ip9(>>T_viPg2-_3)ea>~R>pJJQC`9~#^NqpX z;MSx{v5qh&?<75zdY zR6`e1(Pd@pcQy%-F&{q+yo|xsl^x!E#0e~z*LUo0*&HS9#l=samlCRy@g6DA{tKi0 z1$+gRubOzx5M`D?&M5 zKwC_EBx=Qn2RPbRtb|#)`^Uaj-z{%LUFNV67j@;PJVjGjCCihb&iy~BuLt?j5(*THGl+WIxOnV)O;9ZHh^TE`%PPkju1%wy9Qh2nsFBVFO25W3WtRs&Y z+m^8oO7RC+b>@%^Un1ZQ>lS}9Lm!hJ>J=dwZXD`3LfHlv<$5lju`CC^=a_TtMa>}@ zgzge(|8Sy?+msZr(?Y0u5a`FohKBTytK~)I1c}eV7V48 zJN$)YN|{i#?ucuL1T=&z=D`7pasNdnEl3+pa{Pu9CLcwZD4B3Xs1dYL$NQbvJ)C9A z%2F5TK4tTZCUyU2Kck3X^Sjka3$?G%Ma$YkIU0w~YNrnuO&a`%gj_O~uRWg&_IF5u!rE-4XMHM`_lk$4y9ce)7{CF{6#ZVKxUdXy&#SdHs z%xUaf{^9A}>tD~b)GasHI*wCj)iLvOr7gnSLGz4yy5B^>x0f<$oKmOHynGzyqSPW? z@=Zk($U7IP>ReY|?>)+x_8~G>%~Yf9$5E$w2mMgElp{9h5~Pyr6MXYG;}7ZoxU`2K z<{T=rR)#)0W1jOnn}Q+v{UiNf;_Lijfdag+0+hM}q_>{M%JJPV0n18Gw1DUkAQ^$eic|l5FD_o4d>Aw9d@XtVGV=SY6}mNtT#~M-=AsUd|ff6^o%1PLZk6 z4eoj=PoNytr@u)GKUAcVg!xS#ST}_{a5CW#eF+q0*9D$9k%T^p`tmtK!s6N8;`??c zZYY_b6KCn04_lt6xm*ieY&KMT>x7-F=4L9K?9yQFm1$Ke(R;EL;)bM3@nbI}dlo%g z28kyT%?P>Gg+Gk+yp!t~318H6+M9RY4vImrZM+$kCdvx$N?EJ^LkNUIjSu?|RN3`6}yNk4(La)27&0BwtCLNgsqyU!!)q8?7!kEy@fQgYX=frjsjDiUXd%tzWMhNn!D|NJgnUY!71t2wD4uRrmJqkNI|#tEdl(2JmNyuQ z1Y4G5#acBjBuC03WRceH{LM&(T+^oZ7V>vS(tLB|$g+6pWk+TrViU>*nc&1xAh>U3s2}G4HA<6$>ec_1>YL~)^ z@a;Y*wm<6Es8OPB2En64!Bp`NeOtFr)F=k<*J${2Z!<-4FA9l8E~^9Mw>FO1@^A0X zt5b!*tXE2MxKA3zN(BGS2WZ_4S-~lqW*t_VtJF|4dHh(8?XiKcK&o@7IhQ!)lDE?-~Vg^>gy1$c%2`(Q$} z?Crd-)^-p`oD8x1hklU;(>vMP65J|q!!uvo%=@HkdSzR&-}>F)B&6uT_@uWy!El$F_ZB*PtI=tKeDTCPYP_I zt;+aWq^lOU{>1Sf>ntD^5N*^ynb#Tbh-4$afqTqrmiLPrYm}=rCd&GbRutl)KlLS< z1igLscJq#|E98$(guWE@Ultt(o+<#4qV;$HaX|q!odfw+9u45IQhI;#_^A-7Y9LUT zfnlfwHo70zKAA1W0y%rCIRFQzE6)=I$nE?ivRrT;YX1*60v=SLMv}ad%MDnrKF=3Q z9f-=p&PQaPMdLc3UHu@;s&^A=E|gVN?8No&EvFxV9|)c5UkJao6G1p!u}(F<{anFp z*H9!OGidIU&^d+CWaubsWlu*H5h)|z(HnOv<&(wg(r}J|eNg(iAdxP(mr_CqTB4pw zSiq0|n;=t#w*_?SL*qt6Vy)c{ms@|Xmeoh5HYdZt*2%QL_mKd2%+RTU5gb&D6j7A1Gy_u}Gh0=1J=9i&XJI2_L+vQp6*V-hL=L$ZvWAB_^ zFey5~2BTBv_NVl@$AQygPdNeQv-!s#;_ysxY&`H#&lPBu?80oZP6fMC4j_9M>`r0| z4JXdQ!%D|~c;(HFY_nGH!iWBtrU=Z-uh`+#PobmVy@M7f*lh~A?JMy9DL3ybxZiCQ z&V3CFPiSM=&Tkz!HF_UrFn>b%JVtR0j! z?4K(sX@Rpnd1gm&K|SZ5`h-~g*%tdREuPk3N)dc;-9S2@F#vB12s+8ltk1W%p_rfs z*pFwf;V{4b3^>V#gNk5iKIPNFT^Y<~jhI%TPKc}J=NOq#aHx(3aX2amvc;;Q&I3f+ z+N<yuOjRkVNv`uM-fkWZsAe9F+b{}4lCz%1w+f@jaIVK>|X zYj?FEOlwpTL4%%x79lVca!Y4Ut+k;lqT#*FfCAxDa9~fQ`O{i{iXj6R1`uRuBLWBbZ%XCj^Sx9JIzG7J|H{4F*REs=WtV5DH9>l-ZiNKIPX5A zfy6J**X{_cvWHv^sWt=nQ%FMMnTQ@BbD(NKH{(9IJO8CjLE_dkb~p;a-oI;yIb+ID z)&=ce_At@R^mA%}G>YAUqI$w4-J4;rWNVM(a>@csvZp=6P5?e+@b_@u9#n*VmY-e z{o!+hD<))Vb0sjH#a0d%P7?gL$u8ChDcXuUr9pmIt&oE?;~0(|j6dD@X(uQ3-f6{4 z@q)RLrD~NKljEJ(%Y%Vm-Hb&{vaJWWJ4tssJ^WnHbONHwBSS($JkM)H#-T1?@8R_R zXCZ3w58of+l9`q=LLgjGOX=8CcnV39@*axaRr6Af$Mg$BkJ!Io2n=`c_|83n(x`RM z()?XK^Wc1FE3&v#iASa);2ic=cgh=ljvTwtXYg+6XfSh7YywdGAsAXXBo*0rIWh`% z@*?Es*t=oFcI8!!OK>#M?iDbgY?;g%_(x%v#x=8C(VmD1>Q+Ob6-37GWn1;B+y2KX zm8JPdE%uGH8o)G+NiN(mf~gLC$s;WXKJ~VUl-UHKqVW&&U|3pf+&=?%^&s|h#_s#` zKf@$wcoBSq<8U5Tx*5ICgWota_0A03eC5*Ez`}0(NO5JQ%}#kX+wfDJdjb!+)l+d} zQtLPK+aNBAx~lU77)>(r#cZJ_wWkyxFc00RAUIudpQ;OduO&@>-;k4IX4>#hVQkpL zY0$$?WBkdBR2AIFD^ej?ENuaID4gpsjA1chk_U>70=fG~;j}@I2(dSbXh6t>)}Rp& zr)FUoVIv{Vq0n;A`Jf?#ngmx?`8J{C?L7?=Pyb4r3$`A_xe){zzKVbJKDsqyY4c*S zcY>z_-QpDO*Z4v+d(XPy%sGTQc11Mw7y|eHV=*wpfq%vg)LDQj3gU4Ksh%=&=b4Qg zv9OH|y7Q=G>_<)tFMzxD*csUE>sF|WgsrZ5~wPuVf=gLY8Sr% z{wFN}{@q2!Z1!|0w#BLhZ`oy@%sf`CKg%4XpV*1rx4eV(iR1j6@WD?4Gyk);jH7P9 zci57Np2Ehx)x_iAt?AFuXY}wBVX*_SM`0Vojhfb5`^mQPJNH5=`3n(rfJi^PyLY{JzoTm15!4y|^gnAiu;Etj`WU$=aIwi*{WH`lUXe>8V#?eJUTMT+!h z^Hn;VwLAMYif;aTa%gw_d$+--aZ%e~Vq?K4<76W9bhqPgirE&JUPwPLWcPg-IN^7B zFPHT%wO_Wq#D4xRkt%3O?~4tA`cv3mk563i&S#gg`{YA-KlHhF>pJgT4Tdjp=6v#` zNkxe~+ok)y&}845MZ;OlmlzyQ`D~7frEPdBDtP9nolWrmaq!u-X^XKbQEqd>;&Ey zH-ckNk)9U0%%hWQ_dxaNnOr@4dBpnUZph@`wUt`!zl(aRh8oac;rHdril6?b>bq#0 z=A^dD#g=`<8f~5=a{;@)CIj}tnC?2UL*}M^i(q`*qZe>{f5ibX6$ON+~mfZFeDRpU$Ax^U+gfagPuBgOm<*VxEjpwHxIUOLd` z6WM1S6|W&_KV|V);nc-$lwI=q*>!`D^;)9hmm;{MGGZaPgOBw+eb(f(S+;ITP*n{z z);5~^Qhw-0hyV7^P$Y)QSB!(22Nbr`EU4UQ= zFhdP0TKxFJ(EuL6wcS@|T2Kl+aqHS|y6c zDtwb+@ZHByzzg!cCh-TOA;`Y5ImT`Fq>sfF2AUZbJ`P~h6r=VvLLO@qsz!!as|ClC z#B-g62k!q95vD_kTJ)I~T z$uqbEsPg-KLxzn;u*1?Xe8<_%(>IGj{YeET)lf*ulDJxZ?=i9=*Zr*ELQyRyRNdcc zE5iAJ+#K451T7bL+Wv-3OnKdIqB?#^`iT<0z#Mz4wy?C45`wKnegFrhiHi^Q7Z?Bo zxMV=*wrZwAafZS9OI?XD4QY_DN1i4E|Aw|la7es(CtGw>h#sWyHLt2AzhR;OH_V_M)BCx-C$WJf=y+T%^hsT=ugZ|Pua4ychaz6C z>KA{zqQ~NEvA2MI<@1LnmBa-mfIKArD%ldtjvGyn-R_g48iCN@QexHC+Ap7Ro|U4{ z|6cqG5$plnpzbk}t-*<~^%C8<_wrCSz*SHF6D%1UKt7}cRRC!XmUgAnjoeC({tjQF zh6otkLW-7zV4$DmKM>pOx1bFpdGSBa4n>DLDouW*?S*k;bDR7L%S@}xT0HJRIdtiD zWZihUmp+djGl)MOF)z9OoSJXJj12TzJS0^8zK+oFA7BXQpX9*@vqRB~{1e%^OJ`QK z4MSHrV9F(wQ|@KPHt`=!GrS|+bjEDjL(XCi*=vkrZQB*gqkwjnZ{t5Hx94`X4RNx+ z5kt$Dy|~HCr?IqYx> z`bD!B;=aliD98F^wchQsmZ=uYGWp*iWh`QHN#Ko-M4R)UhSKN1k}4Cj4UbXhGx<0c z%_KmBAn3I-1dwb~ zXC47OhOd~V)Af-zXB8R}Q(N~ZJ@6T&mWM<8omXekbap%}o>Ee6^}G^+?dg-8WB_ zmn4v<>ffTFsR2Eq=lh+b7tBS87TBeiNlw*A zV3+i$hk#Duc5d?NVeHu_cCp63uC!FXq1l>rDYNEdZ_|?(=XB+*=hEZRQ87l9em~B( zdL-{vq)FJ@4f|BlbjrNGYX3uf@@9yevj9tLg+(PeY2z402*>q>ehbxNUnF16V|{C7 zrv}VfO;2pH#2mRY*2Rt^(j#PHv5@G{ruuM6)&v)7s_r-AAB);LF#WLcJW$#I6 z%(lSiTZi@rp5{<2?3>*HNHF}Rpt|-uH`AlTOh8{+8yJX$Dmqm@`X`cYi9X0>9sZCVe4%3#Go`++YzQ2g7XqH?TQ-6w?;k4&*68_ zKEWNz(6dj^hd#sEVQ$#!hd$h7RA=E)Xz5%!{GL)SMoe<-pc;-Y(!4Fc^ive0wbv!R z{DNjdT1yg7o^8C{=SK+~b@<8!WAN~5mQ?EZem`6}n$h1>JI}1&eEJji$TcM0-Rtm= zQzUDvkV7yWhamns883N=Nw8xK=t=M=O872YFo{_Q^`s2u(eH;x^<9=OYAeaV=tj*` zAuoa9NAzf6i6m>>L%ecxd+C1WBptrUHj7EbNa@CSW+>Of_~60izM-*~ZZjQI`=*(m zrFQBE+svckKoR|sLblGDbHg%W8w?UJ{W;MfLAl$^<_efO^Z;u`+D{(|kr~H<3BH)> zlx1oNx-_u{h4vQpA5hH6o-a34?9fo@=Od>2 zJi%JM*TkNOLMWqQ&ehD2FA*Zvf?6LAzdkCI!#i|M(@skh@FxSr%qXl%?!hU zeGMCe11Anj*Sgl>oIt<87+so)8*~6)sYMq0WK;v_y+q-cAw})@Y?3c&JqgT~3Gx zf)EhP?$mW0Xic2hkKmZPEkV33x$c(OmJ5IEh-on{GQ}LDR%a8|h`twVKPH9r-gq7j zlhsS!!1!FdXKGx>1hbNN>0qkYBLV78PT6iX@wWV?Wt5Qo>XZb^YCMEk{vk^B&sUPU zI74vK&Tk|bHMK#;jt}r#3*_;1MPOsk#`$cru5c9FZXP`}6Z5~+eCI;}?(wIp4=?F8 z7P~({`J7zhxhG8wz; z#bauFgvZyolZ?aqCz27z-Es#Rr(0t#lYSGUZA(hus12vT3C|9=S>;s$liPLsy@6Gp z6Ko;4_KuSlTTtO>4k?sEOBk2D_qf+^h~hRq8+0c)&?uKiIjKXI!56P=*j2xwGgLaXri- z%rk8!xa6B8*ruEM`KZM=LeJJa6u48JQU3RlUsT=mptEIDhaI$v=-3tOv}JuDTCCY; zDs%omq@8SL+U&OE$G?xtn|)`k3+T_U^PLng4B==1D_6{RTQ6Ha{faI?U8Pj%%&0+r zJZpB-;j3-khk~ozF^%1Hz`o{j}X$y!6$6IQ8xsd0_7Cb$K}nD9cE0ncij7%s6`)F-|@+ z7;umertD`Lv^&T!mPm`>SO5@snbLdEM;if#=((Y`k82J;#-C#xzHexE$%nJ(MDBvo z&5nn2zf@apYSr%T6`}NER*s7toUo`z7n^c}9=slQDTwME*Y+;|k)id3EAiq)-&k*i zR;>7Rt+sI$C*TO%zQ*#tE1dUFMPv?C!sZaq2PD z0Pt-e4Q$VOp&Z^cvZ4+fU?YH4+gq8<)CL-&co=D`qS?5cfvl4{`)9EG`XFQ0O(cQ_ zf|W){%0rCSS!b`ytMO%{imzTamC9u}L)@JVuL z;UmH6LN8os9lP(}DNPex2&EWu1N|Yus&#Vy=+h6fcj{9RK3{zvFjwzMbo&=W2(OQR z`#Q(?r)3B<7wywV03;Ho>o%N<@-I6*Clc%ap^MP72*3}vVAjG)Fp1}T5&dlqT=iijgvf`9!3|f@Y%0m)aR4ij<>aXP8|CCUER2o{rcUmGFs+Xqq zzr%kYo5OMCkrR3GyHquYn!${s&wLSuovbUp+ zF6_A=Nk@qF<^>jAcF%lH+vTr=oI|p`imV)bYEP=iM$z{{R7)4(gD-2k#~Rn z<)+37p+FPc?`JRt*Y)^ti;g4En^^s(NEh>nli+ykJ;Z>!=f)h@v6sGK8s~gml3;09 zHC6_(Rgti$mCk))XG-$9nC`Tz|7ZP&iiBAYtzPB5o41))=YnHrQFSZu5!vs4H}m=| zfAPGjHpo%C@nHV7m0!x~aONyrXmmW)-#{eKnEO7NR@{;S>0e5L5K9x1szS#|N1MI* zcI4(9meyK&FGv7_ozrm!Iw4LTG`KQC>>x2tBgZ20*1H7Z3s&yq`ycRe_zzEUaokI2 zhxHqI9WE$`5TQf!%knn-`=ncx%dpkEDrbd@Vp>i0hc%bP%Q9u z&0Z31*6=6UCMp0v$P^FAT4x$ZrU?I*(`?QzT2bc^CWwzhH2aWf!RBi zi8S#9@yuyb+F{xLkAlo^G0_0s57Oh7_x((Ft-Ym-KSXb zkl=x2)KqlQZWiXo7_I1n3vqA!GQ|mxG3vJ^?~YRT9xLkG$d@ZLts&nk+iP7iL#M!= zy1*2Zw}MceNTqP;!z0uhLZJNnEfDVQvw1XVs681ldtd&<`}>j5OEv18yBfep5qC~n zpiJcf#XG8a{#uP2`Y{kB$MKHY$Nz}rNM`WVH>>NUOG2JgBax2RA90=g;4bUZv|bB; zy5F0X zPPCYkKvd;bZFd2Uks>S#2Xy`{QB&fs7soh^8WY{ zZ0JRsV%V0SD;&=qHdRN8h8eGg%dBEj1L?s2vhS`&x5Tz@lAuE;;#yxW|12F~XlJps zkVEIe=lAwMMNq$r)+t)8S<`bO2L&gnYGfBDz$SOvFiVyBtuc_;&X+j&kfPy~;w*J= zhm7+4aA5mf>CRaOgCT{@Cu}XctxoV>Ju%S27sUb>phX22=SUYrJ|+RLBv)<-akx}m zIahx$4m)~9HgFN+#HsoVhBJNKizl64m5T_No!S^&y)p+Nfo%=U5&Wb>B@S}A`pqrz z1-5>mz!g}LI-_o_c+pP0kff?*7Pl;wTNL zhUe%u55wDpee`LmhYea*)G-O4p4m?iGr3=9mm3;@hkks;*$Gye;7yWzQh;nszP}>c zC>wIV&KR)ESoa`qayzHNxtc49u5Q+J`I49bG!pvUBy7fqAIcF{WaQPJkX{U}=U#cz zNdTS$P78b?P^VX~z!}3|OE_w;dx7lm&mfjoZwkv;!y*+eA*9wq&U=^4XQdGwfF#wR zFNc;KvBHqyd=N>T*}Egq#>U7cnl>|TpVvNIDNh= zbx{4&*YUyIS0BbMZ+0p$KAC+f$DOfc+nXG}&2sH_7?0lOZlE|y&c&5rj_G)UfP|>j zH0B<~%EUE{cWLUL_z>o3_{EOr1T_~%iun9~&yQUWT`l|^3x<_Y!$u-x8*gc{uGK{) z$q-k3B9$VP3haQf8kDW9)0hDGzL;_h%fbB5lVGy*)65UVSSps+K#?wK^z~oTww3wM zYBn+D&vO#yGf40n3F?qP)hWlcfN^9bF$V?)wfDX)El)#kO+^(2BA=Pa_fk%bE0h;mKjgJ6!t;+3g@ujNB~$!U%4NO%Tes6L==5; zm)RHslG@pMY?oiBpVB~#gm@BhjjmmsG~mB``NiJxzqRgN`eCo2Yw`QBYg!}Byf};? zjCv+oV7gH`K~g4FbZ@_~EpYuNCs2jZ?fFT~=T$nSz#(WaNOjJMt<%l3UQl09;+!)H zzJ9O<>fr$55kJI;(pLu{W0*$he9CrBH5wA+h_1Dbqn?_$Mc&ch>`q%UXZUJUpAnWT? zOxqaya~81xwdE4Y5DNk>WzA6WR=HR=hb&WLIYhh$d7h)n5VoB!A0SX6qwS;}iVndk zQxmW-P!=H|HXl zXC}}lksxkNSXKk9`KW)W4m(jHLs`({;^)RBo;Q;>kXFP-Bw!6xeYVwAa{5b@VWwa@ z73{&f{L;KPmx04w%Pb)b#73$Yzmq@+7rV|C`jx&hAf!=`6dh*o9der)eH9|eIpjzD z7YW)0`PvnGlk)4fdETI^73H`Yvm>>@8hLMNKvm~>{31^OZJJ9D+0xAZ4kKvZD3s0C z1;s?@lsK}<@Z__$!BaV^!2=76qF=C82hzqsb5i@B?5dltv*=9Up)yNvJ8-rC>$J+g zFqoNvi+SgF1OU%}#j}MObP3oC`|H8J5RLzq!rBHvb8p$iITMlnD&t>2%|+(Bp!E8v zfw|*E^A^eC_h|KmK^lnaR|TODAE}PZn}ssn;3qK1#->=XTYWTnS0}B(IoRoiwB(!m zQx@p|)(#g0aJ@nHXY2>hkbkL|zvzD(nm(-v%XhLO+k2jQ)IXvu9IRIlA5}j;uy&)b zJN*=wE)saWHKZi|-WMyuVpx0RQ<1s;^j$K?KeiE&<|$Up84fvFpA)@c>)FA%scFe( z8_rG-o$lk2N>uBP80V@@27+^Gs=B+UjGP?nOEi#;=E6q5tqXzF3vVN4Ipj7i4_~n^ zWZGrv@@_n4SMvFNIeKj~E#GcguG&0hsbw+vqK7Jx9JMr(V&+&t0;I%*g-#tk#MkYF zoN}-)9e60dVrq=Pq0T52XM) zukI_)x?hLrnw{Awou|)&;^chF-BcrDmA#h7^54iozTvVyu+dZ6>aSUMdPpJn0-+M! z>1Z%@4B|KmC2vY|kLlVrm7@`9QtUBX+}Xuwhv@_OI(#wEE{9_2WB?q>=)Lf|#*l}3 z_!{B01o8mhL3>vU1Y#IpBjV5hpU3aZ+Axnu$B!Z{ z*wuYHKYj>&4q*K!jDHfzLsT=AGCKy>+dhdLD<4*u8D62v3&Jicv^#K8-*0i|q#(Oz73_ao0GAwy}HJPaYbAEY4qF&qe>#Qf_Ym2JA%0h3|KKYZGd6sS}Yi z1UrfJGVayMo~EZ?YKi5CC?4g?XjM0LNp%{|*D}!xUS#`YIsiH64zjdB>+avd2kKwX z2Rz}!QCe^s0MttL;+qO*KgpaN>iemU%4!00@=?yr2pRwsCRVs093YAXyR3z-14i(u z)8aN|M11HS-u?1$m4;o037;3=>DMnGf95v13DH3m6i1rPDzy=#Y&ZyAnGw`}cLYOJ zWBKWG-_C`B7ZSk*(B96>@y!i52)Ne+*L>hE`HS?_F1hZgy1jfbi^Pty1t<4YW}#U5 zv3M$|bv|H6&K5MNx;_TGaHc^z=YLny{M?ovzO~{MKa_rqlH91$zM=Mqiy=u|8}`!z+DE=^;8YwtaxH8hd}(tGnQ44l)7$aiZ(O$zLSJ3e3XEJymug04(s_LVTQ)ux zV;ALjV%9m}OOnB9$68sc_Ix8@e+`|+qA>t=%+SEeY{Tf0c+E)ce*MC9F3gN4_O#~Z z;0qx3yN>Y{1eKqz>XsPNCaGXBT(Ikk)dgvmiIYT!)|MHR9B-Gl@y8n9!6ha`#QV2J zd0mC|AO%Jf_ZhLTmor@t<9~KvhmTV?Cx7JaM=wj*aksqBNIZN`HwHhi5NX>#d`CMF zr@YzmnTJqH+u;Oh&C|+AHQGKd`$h0_;Km`t-(H>0WDa2FdYl70?lK!a=HUHf7EL~f z1mhVk+c%)fN5&S-*$Kn{$r2GLPYUR6s2JdH^MV!pT~f7lfKq0V_v>YL$E?N|QLV+t zTcv!kU_l~|ArjJ$t^!VPhfiB3QAa6m)ClYe_8h)8Xqs8|tm65ycxu>vovTd8c5aT6#< zTMvY&j26&~7XGpN7tD^)0g!OXb;||f1;UBh+`6Y2+jh&n{BTt<$rsZct)uaUiU4BfjidRqk6rTpNMy8%hi=-Z-a51le!Z>5l2t-`b9-X%h=_UV$o%eH^= z`kz->3X*>_K`3KK{oDiR5gx%4x_egjydW~(X#VIP8tNDhp!4DZ>I?eqGT4c}F>`FI zB;1pl6P!nAtyLSD)YSA)Od;tmKUBy!?>HyI5W*H+W z6anb;74&%@$#?zs|9f*o;7>lOs2!#;14x4Gqk-h=_fMx!*NLbTZ)ffQ_lYk7>~UPV z+Jv*nNh;ZK$rambN=W=kw=Up(7p(63iU~Kjs10;$wI=9Ggvk-NzCXSU!3L;?G`@a= zJMZSpz3V7OTan#k+8tsg^539`OEH_wFRXmiS02S+|ut;G`6m zCca0y*%n`DPdm1}1ZePi^i*%?^{jG>U07xs2vvEYv=EPx@L+d3?rRCicgU&!ihmPI zIiYIGVUtH089gHA;j$SaCe*kpJ+Dg0VN1 zn1Zx#D3y4nb~K(7%<8lN_*gD@hriQitetq`Tz}*t2J-WsX-^yv$h%@Ug8!0n7qH?Y zi9x5B04fz4s8tw80uhYwecxZG0tOWDV!>HG34pAl{XwE2x~)<4+y_jfU+*fpTtsu( z^Xln~PI;f(e#tQL*xCn!oA1Jqj6#DRHXbxXIl;BoFLsnOqKseLT{^aDmZde*acEm) zpsGJsUmj%pFIg~nLA(0N3@Q5kT=}XelE_(%paP#<7Cd{j?rU4f z&{ogy@w=L{CW7#EZ2mTUSCLMSglDfd(lY=&RBOPhZ>A!_>}BjA7z+QLaBu;eTIqm_ z=X>DLol6kx=_t>ZGxJ9l2_e{SB%wJ=j>o5erxg54Pl5!c938*_p$IgWVyZ-HP+Y)d z&A-++HEU+DT~sI~DuzZcV?Pq$P+GQOKUkGg-?;E{ouyZRF>mU^#pcI2%+N5`uqqp%66%pRc7K|-8r4z*L)FSgdR%8J=S zSm}CI&K_9sxU%R`>q;M=a`l=}n@3=4-ufjr|0OAxW6$3gw&;3N^o6B;2&cIwvR^Gm z-W^!FA9alL%sm_Ai2*I$!^ohX-3y;!Rp9+D{>e;F`$0tMA5a3gSO?U2VptZx%}SqOHyu@*JhUD0phQ>sxvOl(maoMKWT~2y+@vjfI7~_g5=2uyz+XixAD-m`dOvr zW&3u|x2^+7;h`3iT-NtMlZ)495O`f_uyJ98h zL=l88WpPcWzZ#EsRSps>%W3kd=HYjZoOD4O*EUFOkx5`tD)X%1G3Mc>AO;juwpf2C zz2=zo6bh)jJ)zVwq|nDqP-jW#MCr5#!I$HScGKgD16q5Rx2eYvybC4(?Qf%wh`QMk z+gy?=1Yj=G{WhrYDaG%gRkl;?*K$^zLguE&cD3KfYCG#oj@fo!CK&d}N#o@(_eqtJ z6MnzpJ&bo+#ksX&taXm}5C8}D5wi7Zt8**>-=$K|TvcR$I4k93GuA!)iE7)mYEw9; zNxxEhHt1oA<$saneks?)6&MTe_Hs@;AA?8U)kprN{Bla%GJ5RhVH9_X!(?@{E_QGo z3A*l0v9?^~7nRJ-8b`%lkG*cv02L6Xcq!Eougf(wk+fJYyj9>-{cP70)drtcYPBN@ zb$y!&dZWbGjT@9f3eh;|;H57O!I#HvKW)n597dud{KV8?ZBuL6CrT?yATVoaevw%L zrEp$I={tRHghiDOt!~S;NmPNtktvRGpDU1s2G~8GXoN4pL6^Py@iJ=u%*(J~lu2!F zc)|W(;E1HMDiaQZyVy}p##R5IT!)_$%(?q@K>?OlHB%=OZq1R$A$w~BX-6Q{4_QR zMXgF?WNi|@Ta0mL-JAp4;kj#`RV!#19i_jfM&gEfzuW%J zQ*un8-7aE~|0W6d!$W?>*c=tJ>1ZGHsWEkNPNJH=8BIw`zSl$a%yZ)gx25Y#R0Bwp z2h|pat*BV-d?--?Ly6kA;#G{-mAo>pmdH&9;pH<7os(>#kp`l;y0pFrO7>YR87ztM zAc0rWV3AUmF%qz#6oIH&1uF3xF;6yPL?x~^l`lK9`WAzO=fy$gD z`t;}>DHb*0aWc_%dBJwgf{Z-;#MYHVid{6#v^~}Crtq8gx(X`D#<$A;uE|w#uZFd7uCBq7+KHDukbq3#(uiZH|LskFGs@|u|oA+9^q-$ z7vdS`&>hM+o6*Sc$j_2aH6Ip5KOfeCN;NwAc$CgHblvmrGnYGR3qM`RuG0u$D~(c2 z=H(7k|8T9|U^nJ>%iLIMlj)w7P|~-yZN_fTFjv+%OrlvJ@QfVKE_qDZ=5t4jRukK-49djCXRz% z!WMpT?*UaPu+Qr(H$)cFKn20>B5_NVQavhjtd;QLA2$2gZuab$@P)u3tOX&ZD`74n} z@;`P#v5vAjEd4nSt ze14Z|u*(~oU8ot*aLI5cBmok-o@E>)-Kt2;f4p_J{KpzT!E#M^`};6n7Hcha`=uz} z-VUpL<4y^U@k-*;-UU?i&$Xp*cbSi$;FQ}wD&gTGLSKIz#Qcgi^momJk1gu8A|@rY z_iMzh{s6s`#?E6!r9Jmqyv{kGUgrh<-Ef};<2}84=+PHFO7>Pc_iUR#xJ5>XM)}yW z9kCLjxsyfdQ^ux^2o!w&fKP1`&b9ux=H~{&-I!-JN$GH*>G7x}$amqAp*e7|Wx%Uq zKE9y+=ABqLoZK?_peI;FK5Wylh9FQYTc=?;n*@zI0tMdS(w~%PV8XF6`P;(MCL4r$ zrJd&zeddsLm>w1QZT!yi<$((2op1q1-TwvRL4S$Sv;)6k6$f8^KWSJH$j5n1yo+u^ z-G0ggXl0{GQ~C0w4tGN%_MnEFoUG4)sc{qyiQG!3S~UMn8X)a`VGiSxL`N44tW&H( zWwzyv9xD8* zQTTVD#utb>Ee~1KSHENlwM05gz6N)2)}f0zG|Rk?oxXjHr$WhL&%57X^ZOEOw8KOb zVxJ1fzPfOrMD zQaOvv4nC;7M=@rDIR}qL$x}=5|E?Ngz1Yb$grMhWCT#Vo)4SoV!>;YHH5@tKTX2ASJ*_7Zs!@q-?8_%T(8=ZmYm@(O z;B}9|w?IbR_kw;p)yIK7p1;m&eZi`zzhoi0ucAB^7UPj8HOtBXzy{co4TM$&!LHj|Efr73 z4rQhpJ)gfr52gWfbSz@NBm!Z$$`_Lv3C)o{MKeynzB^{Tim`Ozyyruxw*SgA>CC|^ zH6W)Ar|hNnd+do)xnm`EMZ8H)ddC?awtSzpHUD*^@q^{%^Wh-)WCU=<8;SaY zMGjbSyrhofGn|l5us2a~=LreH`nwXTw7AR&9H}gb*niywx%#hlq)lu72=@NNXHsqQ z>&q=VQp1sV8!mfL2TZ_zs`rMzDLuWbcZ6>H+nj8tRWOHbUNaBVf;uVM6)L{5+FriA zZ~VI3VdTeF*>4_>+bK~yUSL0SBW4nP^vF7l2w@%&XH2ny)lbJZdxm`_?C7+xDy`*L zROgWx%w=5Z5ZsXtFg3-|-<8Nqil+${St{Q(j7w+y4XsarGwsdr?}J*L1(UT#MktA} z@g5#%aoqXX9eKld^f}q71aD)wWv;Ph%Z4kZhEw)6nAJ>N* zGd(-sAcf2oNX)h--vE0tP2L10XYDCi!585NoKf+OhRX-_Txkror?uIA9B!@}trzCF z*C<}Se7s%=yczlvghEHd^3lMFl2c8Yl2OnVF8yv0<2?)2w2od=yx zWAR5?YQBoEKG~8T3vyLc0)Lo6cAb_s8gv_=Hu^o9d(!Oi1+F z(6N-7)+|;QLaBrqjl{jXAYK>bpR4H+8KwtVuvmTtOZ7ZTu6>B2G_LF-IYHC*Hs@0V z7w}5Sw=xa(N>+22X**N+nB0)j9X{YQzf{k6!kMccjN5oTP)O*r39ugv;^YOsd^QQH z%H9*~nAu|sVU-Zt>xAu&ho{-sQRvS86^5b98eJOAS(M*D9KQJ?OC}q5&miZ4*DgXc zxj;}-1qj<}TV}Z9dlSI^BXp*B^)7 z4HFI)0a>YQJW!iq+p^}P7(?grWl4zk&2*Lg$NrV;ihvnI1lhd?bRysNuMffvTk8tn zu62jJhT)khE%n2$+cVHDB)qoFQwidiA>r*#ys%*xgmYx*pI5eQ^U)-}IA%LV#@{Oe|JHVbkoS+5qnk zzC-KM?UNVDA6zYJGB=v8HExbl0x7Rud+L(X4W$WELgCIbvCTUkw>bR};x_xnP8YYx zvNV+2^6}+Hn#yzZ@Ptl{*$*Q?c=;e1y_5L#P5$TMxre)nMqrJDFl{;eg3&MKHG+07 zL;<15;N;;Hj9$8t&Tg$m)u>DOt)1g~sr{SI$+~%?+?%wA(pF+93hKd*yEsmq(N6Mg z(<#J!f`7rbPbB2016$zcA|hpdOH1|TV61!v$j3&$XUw<2$QpQa{rdPs6anq%JF?bw z&j?`13M5PDq)(09zmv(HBX_Mjx7KTFAGD)ayK3Us-E-%RSg-rp;Inb=Pf_B_jh9Hp z(;ikavFNdF*N?#m(kad_>~IdiY$;^DvU>y}*BcC1rOEWA>nPt9lY7P+kO zWu=fzWtdIs_IPG=!Ii{JbSQyx^GjrsgD4U2h zX69rrxyNi5duls0Xv1+FRi-hjjE2{wRF#l;rRnW?aQ#6kf5xxB&joB06l*EYNYV|4 zYx?M)C@YtnElHZ+x_!-Ila&N%Y&R-#gZD^9sd4G{6?;>VIcX(J71B)iHrj1-(Yt23 zKF4(dBYB~bd&>f}9Ti-!hrjYl$@G-sLyWhJGra*8^Vy@sdn7@|i?mna@H_sAmaHLQ z*(7M(h(!RwpI-?EPuI^fLi4nKX?$^46o?f()tMX;i}B(z>7ts80;$lWdCeBzGnjP> z!nS2wz!Y+ekMI3+0M>T=)uMoqUt-h`Mi?kOq#kY(e`cHmv(#U?$!b669>GTUOYD zME~#Y(doza>!m8Kv$ZMkzPWMbXu%*3K=0(mPQ(l&*hk3?SdV^K^M&S(`ul1y3Oi0$ z7?KHZ+e`-&g> zs=)j|w!`eggYxS*?|wgHM|YkfMrus|z8v=1kh@Q#TX07*rps!CJP%0SoOj4)Y?D|%86;6{(b zjwDCw@b%k$oOM8QbBy5{9a_6YROXwn8vma{hKw5c_`RSt5zx5KJqm58xqetx#a+8` zALZ7s7=L{{i~ees%QpkDrzF>%Qote}eIMaOW_O3CGLwnTchnk97hAa7udm~GHLky2 zWql+Vp;*rZ(sd0^*Vb1P_z$%@i1u$Ml3EHlKM$WSP?mz@4_-L@+6QL@i5l2=jq)F) zv%T8PwseHQ*t2|rWXLb9c3iD-uv4N#Wb%8MI-~U5%yAy7@QW{1CXz>T1=}+oCFr}Y zp+_J4+?Frq)gj@bVD~EXd{Vt_zGew@P zZ};hx=z@c$Gr<7JB}#tJJ4ew1U0>|KXqXJVO4}OC@B7|kaElP0{?!bSKqp6fz5(ROBu?Zs-9;gOJ~5Jci+Zp z*6H;PFtumbO)n(#{v&q8J^f6+H4wHfpaS}z{s^Amr{+gU)%DjeJ(CxFUTr=Y??LY5 zxJZqxx_ygmu5Qd&lZ4cLz3-5vgL(dODLDjWbBQ*%QlqZ_IuNy)P;ysXcKag7Guf*` zITevIyQc~toqvO8aKv!kUi(QFxnRR`8J^MGyejHv8ar}bKSV0jAd{G}-meG7?98+( zBgt+@eZufz^&fZ7kT4o8AWUI1hpazsR91n)7sZ2Tc26>cJq=S;v0L*1gf}yF?!c6K zP04?{Nu&2j06#4Wo9@u zO5s*_+z5B^&pigo`|MomTc0TwCdEet*=j3|h6DB=6ibVU_1kdn3k5v3^XjD;X&IVh zy2YqcgB^nwH%{oL617sz|Dt-2u0G}JH;+D#$bJ3r=E&Bjs^6~My>^7u6& zo7e^WD|pz!I;L34x|W$&PA#rdt!=RN<~cCt7|Rj+{-Y@~o7oG-nGP1<5}}tW9t%Nf z>CV6yW|Z0l1zH-s*f31NY*m*uJMV$YBV7v*Wm^*mJ-#?ffG3Z>`4V7|3Q&sCz_(lO z6Tu1n5s^n07cGwk_tefEJu?SReskw63%Fo`Wew*#{EHG}8bJ*kXuSBh(tZ%?=90qS zWCgc^KF^cXU?s1D(1CTd}MBfS5+=N8oX|cclI)UnA zUNm9cdrhm#;tm-4iMO7-ZE zH=BsfPYzVFx&c|wx8wpcEBj)LI*YPAh0h_fYx2Oo61T1GOL~b<1}tj&Eu3AZm5kI0 z_GSf}kt0v&svmo1#pAvtmMb$+xDkg7w3g#;_Ej!_{3-g!(MnS(k1F`An(~?fQpq*f({;A`5nTvgWmeY($#uX14 zd_xsOd1!RUI@S|clLICn3E~5nIh3hbo!oDd*ff9Dj83zy`C<42`bJ)vHII^@JkulY zM0&ZWH;tMzvo`AqFdV^3q84?%JH&#RRSr8uL=a32-D_NY^iCvD z|0H77IA#@0SSIrwN#$H{W*n&ujh|)%c~0q$+6@IyEr7qP)2>|?dC5t9}(w4W)Pt5Vn!*(g3l5vEI zksUJmRDRidDYY4*syg>4Y36slrRCd}4fS#>GS9H8rOj|B6;$b|&@ouS=j|m`ZnU0+ z$ny_d0QiLmOWxosGY4`GDz#0Xtn`N0?|g{hTi_^VTb-BM@uh)Ad+Clgo678a#R`6$ zsj;8B*~&=fng(soF}3R(>_HTQdewBDnb5V?*Q0KTX)6SUkYyuzOQ@s9LrkR#8R@KD zIQ;p`@0}9e@9H=Ct>H;;h2})EYO8Occ>V|U0BKFmEKB%_d~O`{PI5=t4-Ny+8T{Vy4a?+%GC^G-yonpx*% z|Bk_@0&r-jG{9%2p{KI<-ZNatH-E_wDJQy>*>HPs#3iJm!?45s&%x@$e3)e^zF3vB zv+Xt5l4hjga>Zqxl3&`tG z_)YO3z}%?oTN97RL3a5hXA6?+_lF$T&kWa_T}-Vn>%6QOq&63!j~kqoKw`a!PeGt9 zl`ami$OV??(CD0O6cJaqlUd>2e2nd`mc6|q+u=`7@NZJPVrdBF3@pInF!X!)R9JM^ z0<+hK{qhkDZAVHnJ>Bj>v>Rii@BO$$O#j4T)^62W%`yQujvMbAi)nF-8!p;rE?bWs zrg)u(c`Dfi*n6&RuNkfzv_UUmK2O{$mJPUnn3e>{G=lYMXY^U%noK{u46LNTIoQl- zp)6_kU?VIr*Y*d}^T#KomuY9*S3(HgaS%qy{ZPA7##KkQ-e@c71t{0DEq!AW}{OyD6BCA%Mw{Un;CMznYwbG81^NK5n? z;N4tk8oKuRW=GvyQGq8`F}`RwBWZU0dXPM@edHjBWs%SM(;M>RD7|j9%0tM6{H+bx zA3}W$bGa#w*$77g48nuKc51*5!vS?m<93AnxO^Ul)YN%m__U9Z!Qamra!T1T57Y_g%5MV?kGC(@GmE0(WKvpWz-<6ujAijnRBAlaWt|0rxwB2VE zZ1Rvh9BJW4JvuX#jxC>%KK3cllKZ)k%VP%8(_Njc$*0n>18TN*9gNpwf?va;l;xyf z<*$3)eSOa07|W*)c%!$mn^=Tr82Gm8%^69w`_JL5q^@(e7ux^rOb8Uk93{MTl4!6c`-4D4|gREUp!=VprkScaV(H8iOH{ z-Q^@#`)8@nJh4b1IF}KMxSz?ev8H`9F^E-`jR+ugf3tsIj_2Ntw-P`ZAlE0aNq}_B z7?6`?{sR%QW$huN1+y??=7H!>9I1a3sz6Z%Nn!JKUp`FkKB5xn z;$;~s+}l@$bP;^r>w|rh*V=)M9wyrx6G3GcX2!tMY3iDJzsj+S)R_9i;TS<5wjNK zgKTOx*wp`k;V_!>ek3o#Ktts}`Bi`EEf)w`|H+C15nm1vApaL^3|7A#m>LXNQBF5c z9)i6Kv3bgT|D4#wvkCvB7tUdKa5-@lPJd7KKZpA~W8j~ZCLHA$__=>Rientb{Q~~O zX--@eUG{pZm|paeqEX=g$V+*x<*6^5odnoVZYoRiJTc>Mw?a(AC$!4vxXzDnd**|1?~{%Ri9?WfuSbXK@0#H}`q#k0n(xk73>q^hJ64Lu(TaM39AiWK zbxWtan`f+BLihjzljw$NZ#!e#+J2e`t}hI-QMrq_<^Og3AZ=k{Q?~GYQ8X?AlOVRY zd$qCF7v#3pDS^KOJ&?DSxS)d=64>y;W$T~4wqfc?cdhKV)8heC_le{0Hz0$RJ;^RVncJtV=6OA2NFCJ}# zv9OA-w_0KgQQIoZ7;%d9Tw)-C`y)1bU#5IKsP1Hd=5%`H80|f?KCqD2M7V0WVQr-}kc<*4{$}$YNS8mr?w6>-{zp*rC@Bj~u+f?iApq4p!E9w;?L; zkklNt_%6*(oc!>m9iYr>mQa>hU_wx^>W14F_xDmAcw@C?ysv>(8Zx#t?l^wbaUx6K z9Ykx0@98YcBv)x?omKH84eliVV{3p>EL@VB__1uDSo5v)&2~4iDD+rb_N26I1cGZI zp~d9BxG-?I^Sn{OM%H-+&a{DJ?2@6Ga2kIeMC=YG&`WrL(Fd;z@^wV;D`P%!A?_3o=9O=r|@U3hB_ExF(0=OO71EQos_nZvS|$RhGO}y|Z!-edWcNMX#++|T zt%>Np_dMdO($S~E5xK?S(%u}@gj;>w_ZMt|lMdhg^+8swlAL4sbL}q>CFK7dvwoou zQty5`+>$P}9X8kxSj3iTM{r6pThjM+dj|V+-T?%USm^|Y3Uj3g8)(s+IF3^XnCP<$ zSu&SDAXU;;U*q`YLP?MpRLL^ggYojHpx3XSv;CcktMeByKzq>t>;-~s-haytOgHG$ z57w2w1L$xAkv_ZAmM36TbeBXKA1}~DtbKD*k*ts5^au;4(+~$GxkgohoGky<5wTg^ zfNdUv%(~$ZNHlh{E=XQlp@VP!V)uv_QHS;z@I6}VoF5ph8ML9xl2ts}okJ4ug?nyenA z^in;T_-kk0H24W~-zX{4-la;W>#;tOEvsCG?j(P@9XawFSfohl{Q4oj3#_5%4fKDW z2vQc5UyxfXSg58wrkyU-YvS+6Tz(>#CtbC7raeOAr3YWB zswRP0hc1Z{=_mU7f+9RQ$p`i@1>!fj2$g-4+)Z!Q>R~Nr+iN`cz`6hu^9!JfabA}L zN_v$jzTD4z?Ne+p0JzT>Tt3A1>zjse-8XVUet;gks`?p|TD%XO+h4?&v1vo?KrE$y zknynyE3oyw!}m;+oyF_H!#rfY{@`*x@xNcB_yz>28!m4e?itt7A!>_fB}src{Gf5^ z@cRne1`W{xhCymfoLWBD z74Nul8X9D@+wk(;8?@=DcI_MCF(Ao)E>&VCPKuI0SD%tkidXZvS@s=QpH2=gnnhEOSCm$;%1U2M

cwj#cDKFI>5*i_6c@E!DZ3XvdZBF#X5w03 zzAVDa(cv1wOouiy3VdRvNE!7ls^4U(U9i^PI9W9O=sWgZf?1W3Oqs@uaN`UI25tF= zrzI2n^n^R)j=yoKVQl2DovaCxM?`C|MyFRx1HlHKb=icU!YyG$3nddo#+EMME<~W7 z{L3T;>$c6h=e@!(wj1M^OlG=Un{T2=LSESpxj7(~NSk7$K~^pabs0-JuxQ-VJCjJj zwXA6GLq+%%yCV@+DuH6V-+mEMmnjLeIG+qZXnAG#c}Zg;@`Jkyetq6!N8{ zz4B^zqoh<)*9iBzxn&r?xhg{aPmL7|=vE|wv8*pw-197lEM7M2v=nO8xrdDnN^QiI z8_0~x6B>dVkR)SnxnsjKcQmYo)1Hj-BL_YS#o)RHnL$>Hz>#-zwFxOp1lekTARF=z|HPmZ8LaerKCylW z^&UMCMeR*Nhpc4g_hk;!1R148F+m!6R6r`wS{9W@`p-dk%)A1+R$!se2F86=&}vh8 z(DIVCtPpiI#58t|8>HQ}O@=f%Dl6Yr0IlfA8*nKtAC>*=unKgOlEaBG+bTT)VDnmH%%x0a6Tc)UT za!rl|?c*UB>!bvS5Uf$F;J03DG@UD2bO2hGSxiX?C&~sfe zJi8vQ9W9dyQC2>B`rMWt`W~|r@VQ_{1Z)Yyg`-3SM`YlIAomI9Adz@eA?|#(#cXdF znV6|5>Qv-G=r?21Nv1(b*WcF1Cbo>n=>%Oz?OE_6=(^5B;`>(2T_IcjjoMXQlfGSz zJ}%EVM&uqXQY;(p&n(Lr2Z_#k<23q)DaZsmT$4m&0v{;UMITFg%+jW(ymqq_iBeHV z7v1ft=fy*^xfb?%;)C_U4{tc!S6Hjo?HY?x4U$2bBNa`uM^33BItF5S+or$M>^JKy z^9B)itPNTgIyXL7giiZ`?9oM$I$75e231BS!K+iq=bxjaq5)(uWH|P522A_O{ZV-T z@LjhYHCrq$lTur$fsaHzEEhtM85pWO)zG@u!SUgZ(@n#Bqi>B{zRl`VKZwnwA2-@x zzEkv6pA)jQb^nVb>$@;*ZUIn|b8bO{H%%r+dGT8%Z;$GmGg-o#c_-mrF}6E}y%t`y z+5K{XPg|?wVfmK`X8iogNJzt)kT?Y7Ap};lMwRxa$Uh6W1;l->&wN{>iQRh>x?bu zjSV2@duR;B0|=m5^qdz;#M^;((otPvp61v$%{Tt^y?d<%C{f8z$-~seIR_`bhpz^N zuDZX`+1lt+VGY|4Ud~j_B<8~NL(GsDTuI9Ml*tnCJflcAj^(O2vwg7h)qp_zdlt;O@s$Qh}F+J#>nxRoU~o20Qya>nHS@I*?~N?7!TxZlwwKBK~#C zQQ^eMZw?~$D)<2W4(F}wxc-gf3K!g*6AvR{Pj6pznV{rWQtg|g;G-Kq)*%6_{BP=n z_;r?*cUy20UEkD0k(!(8Q!On4Sr5t2y)T#$9ugsVvb;xoBzRir#>@`u5O=`?0!Z#; z)6dCMNBhH-rwYb1{MQVg5IGR6(tRMLtkr>E%QD>kga85YrcCPla{~6-$F{0g$kHIa z`|&oH6oQvE0J+jFrnP`Sw$}mtXM6e0tbw8vn`_NOO&K419%7_u%iT{sf@XeWvBO@2 zI@0K^)ouAv>o1I36`0FheZECSe%+6oUpy_m#8vdr?i%rL%BMG<72;dC4bVSNmHYIt z=%&?*X{hKI4f)TicY59{yw{=nPT@nzu<--@=}%)#y8T2(rWZt)+{fXMtJdC?qaX62 zpchNiyxi>9xX}pa78jF+AM1{o+F~@(9{lIG8j~h?k=@(l?-EadnOF8Mwvpr?`_ElZ{HDjqJR8JzWK-H!3;fLFQQ0O^~J*IjKA$9J=6c3Ww00P|!VZc*a{&8u(jfvF=z%OV2yI0%(@rPLF zLV6%M#G@`ux^i|a5dz7iU!m~z0&T13RL~5@p9wcUjcNOPDYJ92ciavaY?+rRI_wq( zctWYtIu$#g;-^1?W{BPWUSZ;8ix{EoVj5K?;r-ADh;LgdNmkjC>6=Pl>TiL^$@`i7 zGS0^#n-($jz^ZG*!6;dy2-F$TwnPW`Q&=0lkE_wS@dHqm!BdjTI-MnCxnTDELcKFN znAm%HSm1$ME}+UQ0*G>LpFE5I71LqkJSl#(?hpyWOgCQul(#0j5-HSf--{u;PnEjT zQ2XH(xInv7Cx~jo-oG0s2;S-=~XffbW*dfLvOOQW1Ru5`66Fu=KDb=(% ztP@*8g7|=v26baa9z^a}#<=R;I32db?xE{|m1Z+bo&x=6t<3joZEL7|(2OhdM%Lc(wFPqX>eG zQWjv(sUqZ3h|?>GhN!3F)r-)50Gk5maBn2~YbTJmnSZ(j1%zD8g(`2ex;$alC5+Xk zM)F_QyGsu6p3hJ81EhXS`982O#3$?=iEIH_YDyUi7EWCMNRizLFK@-Q~+|9*+fW4)(xpyGN;Ry@g z_iVj?4^-_D`_6R)`fZ$f4>hyg>S#H& zbl2G|p|I!u@b9k=WfInBp;4lk%2ce($+F_3yMNTb5+ZSPC1$-1fP47A<3r8J$&l;V zY3*KzlSPBx^qS*H{XNWNr7z>&rb9)l_j-CSw~4!@=l+yUj_JwBe$K?``S5tL z!$EUh()oGbj5_M1j34*XNbDP|klV-Dts9dS*!dG|%ivSoG9(0y!5I{)e(zVk4hljf z;8P9JtPR1QkNRvzfNX#E*G|XXCcAfbWl!u}nOIK-S8FW~#3-2Y4-HI@hF^{bbur77 z@h?ocqS;C3frI%-UtzxeSF1FdvK!ywfhbu_!0X&tuU?yr8YMMO>or8oEy?Vx=)WKz_v75v_6#{W%Ai;+DPp7k}0c|#=a>mX72POULoEz7(YB-p*@{H zdGrG1HeSW;gNgSrKPrNJ!oJT9MEhea=h$|5xqAsNIdy9>*K?Rv>qD}^BY8H!^hZ&k zZnwF*F_d?Et1Mi{vtc*p8nWl4&6r1)ee)edM_p%!O0e*+i8%Q1nslQREuj(_gD)nv z_ZC!DTvngJGi#rgh#~n3l!vCGrRNszv=f3}H8^+iWpW_cY+^GylH8a5i0XIeXY{H1 ztqH^khcHQxiodYLg!*;WR^Ryz#rZwcJpxqO%Dssb+C#oqcA|^{ zC4835mIMu~C81pKcR5wr_lKRUxi{u__LZb{p&I;)kGXfP?nBPM|929E^=zsZGRl~? z6YU`cUv>y#LaYGRl@k&--|^Bf4T3!2T9GBeFQk)uCBsJMNhsHD4_Y!_`?3NnmKR>1 z%LYz5&Lk_k#(d(jqN)t*5eKI5IGAwKa~3bSJBS$022GIBi2*7I0TMzOGyHwm0yhau zGgKdnd*JWwd~}y&o{OakA!=P5{IY8)${nVUs zm^m3Q>ccVZ>VmiOKFRvR2?zdja^%BXT+jvcdn~7kGCJ(g+W>A4|L!6^5X!>7(%Df` z2-(rVD?Jt7(lP(`|?*YNlB6e>p`)nd*JH) zM~|K9aev&p{a5$mCJL%M+>7Is8g*LHA^=!_Ra26`hf{|C)lif;P4_RAboQL1OU2+^ z&cuNn`3^OUSja`CGf5uqt!&3fVp=xp51J!=%2}CA_%T~(MZ`t#Kg)%#KRey+aL zF?FiAVYAO?IaEVfzd1OO=rj3xf$WtbR1Vb9|Gj*!iU5@L426)4v*Dujjz+=qaf?uT zfOz>h{bX;$@hd3cHWpvs%v?sDcIrNgF%1ug4V1K0N>@&hdF-z*Q z$#thezCV*qP zMx2)Gw=)U@J{zMbEoM;EYxd5U?VtJg5dv(UYu|()IMujep}lbQsHlUi!q+#?DiBAc zM7g6fFHcu!I{^)#Vms09Im*3dV?QF&`}xuDbgYxBOv~wLmoM|4QzuUO@GWq!pIW9t z9C3<{f9x~d1R)+|H|tq$oFi)d36nYH&{f#1fClP$R3-IH+s|(-RbI?0l>+hSd~#&3 zCPa=*cqAgvIvew?){ShGj@5zb#_PrThr>9vY!gi&NY*Nb{G_~LGYay!(<@;s$KBny zCPO$TjZcdRftuAcmB4R~4D2UVfAT4g;l^b6{=S_AZ1kRl!05B}u1FtmYaX+E8%E$B zd5F>jC%3k*!J1FfZpCw?&q*9&Cc-qbjo2neWNfq0;%hHkQq=U~`$BEjJ>$`3iQhw{ zP-j}`Dey7tObgC4hO$I_EvC4tJZLv0i&_P7Zk}{7H zSqST&RPU!aOLqnE#_M8Ds1g1$Wfb!U+MCmwl|Ex=*~_y+OT% z#gd;#$9MZ^cUxLtXl_Q?XiF0*ev`JYQb=AfOnX+C3b!eJRU@hK{n46qQST+p?{1uS zCr7%70wXjuN}kHc%dbDwo!tAJnulzs1vL04htLz)cT>Xrui6y5BLZgwvgs*y3EsgE zlcn$&T1b^jio!Nbo%(klLno7x9T@layDaJb&a_pAcG%cJ)9)ohVD*Rhi}0BW2lwXG z7fZjL$$+#ctK07Ew1AYa;52tfitSqnez9)uzS_BMVMqkch-XFF0AKi7V7{zuUtw_q zvL;cs7a^z3EG(=Oe;z>cKy6lJ|IAc$)UsFE#H_4j?n8C)0B$|{3FWFIb+N>gCOdr( z!~j)=Wa6(~h_pHM)S;7v{u3BseYOT_RFzQ9c|+qtlpR|yxmOPAuS|nF(JD3_c2c9U za{$^1MUv}LJV@}KkWNY0IsE}zzB{ktW+AN|hNH^u7TG8_CVsFpD$Y3AR4OgnY+n~t zh_Kt>e^nKJiB=d8@J)>S;reyG{ypo*nwzpM<~$3AWOJ@}@v_>bBKN$P%F1Ra<7>yD zu)X$c`xjd_z3mKg+Jv*X^|(3BUz|oHhEJvTY)UOWgd%NNJqUhrIge5Pj>AYv7PihG z_63D$?7I|Gq2NqsRbYDDsab2w%+=1{(!JkLZQ<*D*mQe3Aw~OImG7VQ z0b_FkGxj-^4V-!FPgoxesSh2=_CGXud;3lNh53Q)E%~o3{Ct-D2I_*q*7PsFc%23{ zHQJ$yXqwKpu?vK&q?h=gpTF&4&D@?>HT-v(=UsIr>nrc2p~#8Nj}3Zl>yC&A8#n$i z(oE4d0`lor#kF=1oWdy?B`dUU&U}6DI~^eQjS#SQ%>)D#mv+Pxl&Zk81dkXSQ}qZ3 zl|{a7vfQ!Ht<^^#b3WQCXtckK7yu^IZr*0(c(UN5mGtiQTl}xeN193f-b-)m%6*qH z(YPDuJ>=6HLZIu^mJWz&(y1}-*CjDw+pG2LM?dxJETB{O!ySNRpy zPpTiptzlOeDibHXe9*kye61SoW5*w_HdblqPkJ=0Bif7AEVol8lTunXNmO!ALue-t zMd(N-N-*za+J%K+4#7%=J(sJLMF?oab2Vv4?vt^{E>~4ub(xX)W3!}*`GeZwJ$6pF zHTsflS><*=dm%A-@f+>6wuP7{UcEsH9DTWI%y5={%Fo(Zr*Qf<%9w2B6-@~kbvDkp z0Cmd-CqMg4&Blg8Q(n6*EkO5UD&RE@RRhPig--mcdLDDjmO_gYou4WXR*ww4z0D(Q zW@kO=*hAeWd5vMsj`-NLo@Z_9n!vuo0(s5ZMBpuIVN%=rsu~SSn=~OBvWM!q9cso$VGS(SFyJ3ws1a#bmA3?BQvC zQla1(c4n(v7A{R2d}N5Ve*Vqq=RJr|8wYios zAdiBOaPZlLtcCkdCJ@7~dgh}L)?cri-PEzc2iP(^NM=~aAB}{pxV+n*4xV&YREfKF)P_xZ&Wg-)KAcEC16`wlo6T?spQG#;8f#YN zMdjJFEPFY6u5)d-xZ8ofqKgCEFmN@s$rs2Wp~GE z>hLH^U~jyNWVB>Drf1h!`2A{av{M5*WWIq%Rho6w?v+f=g`IY5t6D9Zn%DO6@1+A~ z4?+%F#8qzv1jI3f!6tV=vkzzRotb>54d@IAKot@5_BZOv3*ybBu`5(!O_G>EsXG!4EigpzdVaF?6c_K zhUUcM3sezOwpHE2>iLf7=XTE-H*Ah(m&XYG%zRwL`b1ZmEQR)35z;|3ENPIn&6CCw z2%B?d*)*%D>|GIZd2cg&hW7lOA9%&y2528gn5D*km|Rkoc)!p|SCY6wl}}oPr_4v6e)+^@}c{qQtwRGP^v|u`qWo*sHL3hl=y(5qxEHza}hTe5Dlo=iu0;74%Fb~ zqZ1(AfqORXN@(Ee^4-I7*#dX1iC&+jBv;M5*1l_M_PyCRTpnc+EUJfv#9m?Nu+Oum zfH{4AQAD$lYvSSp2_x+cN@|L>1lf}cC(=dnSoC2_TcLk_aGb6DD}F=R43KNsDPtCl zkAnL@O${=+^DO+rA{`L?BTUPHjhMHji`Su}-ci-sGcNw8stY4sNp_b@o8eO2Ec{)p zeti2}5>=kP#i$zhYoF8?lx6R20IQ3>CB-6anOtliqY3&@UHjU<3wcU*f8GnMNEwU& z0*T3bv%B-S$S#w35$$Ozq?uW)q@x+pS807*(#!hr^__FkK;mo+Ti~M~q~6$G`IDD= z!@$NGL+`D^mb}|+Z=>y%e~!Y3n>`XQo2a>qgS66v`GduD%XDhR>htX zcbd4%WULiL*hf6)zNP3dj90#2vk&%cFJ8z_EvI;UP*P;`vqJmuWc2d)FYgWyU|f77 zjz!<3cU6sC+M35KkLYqz`YJ2@PHkq>mM=k&(el{cHR(aCC4!H9Hyt0hn@jGCS)^|l zQYpeNUk3g)@1sj~K7>dHn;eKh!|!=W6rW|LfPnc5#s8dV5ui(4_)cbobH_WuLp*^8 zwrq-Lu0M$QopADRGkd*%$H%IcBt%gj_^j9!hC^Q*U1R2;RD~{A@T-Kl4CFN3dCnXi zs2M3O(5cDMqx0D3=?4wM+3S04hBYH|pvyyu3?uoZ6M zoDG{DT!^@mE~QN(#nHE2y<*Oa_C-go)CerjJZUCoI041$@X-N>;Qd82Ec9ssm!I<= zvBy)S6%@-i26a3ugk?+KILG~&8VdUcm!b1E8jW{(3zr_SDpr>+&)U7gcRTUXYxm-! zUUj#}(kqJI`h=qurZtuoIYMf$oPT~ZaTyVegCeT}st5;XOl1aL(qJ;Hn%B6VJf0aZ zz5g$y>$5pI&?M4gQn?>7%p*SSHdro3o+GC`l^7YsV)LkJeH=qUw%{CY%x{sHtxCsh zqs5$=h@cB$1`RxN%4w_nT$hl<(56GsQtzDK)TdEig@OIj+-`~!WQYJ?xzeARW?p?N zwyU`#CjvZOx8Ii%;<%bU_E9^mZ7MY2UklJn3wAYZ@*vMG!J~~%w@-jj%@9A?TbFcv zI_?qd9r3JjmOX|2Qwtf;PYN3Wa!qgU;j<_8Sz0!9*c0f<| z0Y?VHp0O-bUgX9(g7Fs^eNF!WI~!ufH%> zNP$VPGf!F}L3e-8Sb209ysgZ`0l?)i5yf5gV_K(7it|yDqCE~fhxY?a z@A8Ue|3y9jLO5qQ*gt^w8G5Jy9^?;IF$MfH=g(( z+_;nD0EA@eDiGT(DA zEV@xpCC%&pXB^aZh7RCtEJHph5Y4oy{nSS~Fv>UL5CoPa=e(Pw=H-;v_(yAP#%)v+ zeA_35d!S1AeakglT@yjUuU#Md;a2+T`C5ZsN+tx!vS2Dx*{W~ z>Kp5b-Pni@G>VZas2O(VYhkavyQp8|eD4IP(iPexesD5|@itAqX_61_dR%opz9V_6 zZJGsU-a#j+K^)xIUjkb}a!9$9>s8Nj$jp8iwqZ%Ok?0Mq$iCe9;h}$lN6Z7CMHiYb z>F%z2G0zaEJw6T#HypE`M`gNPp_3b%206H30TFca*JA_pwPNjOR{ZFg~Y8Flo_=~e*U9JOD7zV{#$m=^Hv@Alv zlnFcGLGleO$nSf750)MbwCCwUpM$XcTjHboxc@T)QGiA5a0Jv=Tah}5xRq+qWhdIC zD7$U;B%+1*>E)Bp&smNIh`(O##TSnd^5P>0Mfbn(C%E5J>f?>*G+GTnVSCxN^sp?I z!$;&xx}#4RWDAdFyzGmqdL~gGR9Oy0{nGmZ+D)4Ppxrix0u;f2KW1o>-(!{_OK8`$-OAD zdbV@Ea&u1{FXS9ujD8wF2QL}*YS|FOjE|FE+8ls;wtURKxVmf5{t$%=K`on|=%IK=w+%&#mGzVk0iI1vBF8=JUK6dm+7@4T61oNU4WCf=Uf!AxVd^XwSFqvmpH?UCSO8t zKBKYcdXB0Hjb4t`^wo73QTDizJ37xjgo_!uSkudCOAQ_?qDs^2R#!^~=V+I-(|efX z*S>ik{U|{=ByfHAS$cq;6PK|mfY@i&(Ngp=Z;6e1?nle0nd;uk@kIj}_A9x+$A}^p z>U1hS`QatK&GeA(^?0M=l|IZ{n2-A@m_9_=T)iz@CugO)7QU+yKW6-~@UUuJ%=aXD>d69li6HLgF%IU0aZIZvo{+{wU7y>Tf4Qa5 z&_Tr?Xk1b|4@N*tOum{RsQkYK_+&kwe>8Bk>D&sd%{J^yM3nO{@*M@)^$hfDedWNZ zSyB*wg=V}^q}EE(dD&!r#gUb*=#-1ClPh%$>e6&|Gg8M&5<9TG1w*qcnR%t23~Wov z(6zzqcelJc%XmhG8%(N}N0Ps-7VK*JT3GLlw44Szrcn)>q?hyqa*j5!=?l)Qz7=Jt zT3@LS&)tvvEc(DB>$@z?KMd3RkHsH6_MW!`*f#W zbtQE>!sq6cJrvMQ>!D=&>fu*!gziw`Cju6J+($!GXrxRCZ4EgoEKLH17D`Jc4(%@@ zV?!vlFkDYgpB-;lTOkn)>et-sw@XEgluf|Uo19k_7rz&8@@d=2-F6Mx?nn={M!;<1 zo!HuH!~>@jP!A^11ggS34&O^NbWwI2#tVi=wKhPuR9EgrDZ4+( zHSk%F_S%mVoLwE#4Td4#-+CFL#K7C$Wqe<3Q0vv5^OZG%B5C3{oZ3rOsk#!kC+Gnw zRS(0hNv)@x{3ITfm4tF8?LxnP!UV4gNq~OC23~#WHGa~*oBWm{ossdh5us7@;tHDF z9i6Jb=>aNijq1G_R{ndj@8X3X!`7SK0 zI;Z*F*Z6-5d+&HE-#>o*K1fFqhss{(m{An3$`DII`X^mM49EMt61@`H-2RuIt9C z4>hzo$eg_K<%1+N7_}?=WA46{6I23*pM->qq~+aZ{vwHx`m?k+d_ciPO|#q0VZvRH z4z{i@F{PotFUpC@BuCQdGo32U++Mojr%JBl)vR>54TV!>RrfCVht4p1Zc#5j;?Nip z5avR?#>QRFC>$K=x(iqoA0p+C6ob*bw764(KFj6=5FV%=e8SF6u|}~>3UYkqKc|5F zKqe5bh8yMjJ<`I*1;~T{aS2eYW(cqc&${FsD>0k`G*_qb#-pDfxTOxbqN!`GzT5gJ z|GYl>OP>QUP-3ta#1&U1x&;Inu=;6`;jTdwO+ zs_Ju?xO92IGZ*j?Xn1OLFnQJ9R34^uyj+@^xSMlzA7uD=0(yLWsD_2x#jm8GpN~1N#^gvr-eR&qYBcsFTkt2z>4g2|j7m(e zep7LGcC(g52HW66Ex4HmWAf1q>;Z!8|JE*0_z$U#D{hF9nbu_Zj#v(|AN+{ zomv0fTAApE+lMo>#;6HL_LxVwExKNwr-lcZr?1xS#6H5UJ{oErGzX78+jMP@7H(yO zdYdO*W4{K!vs0@KmX2i&v zXVPx)3aMbMa+Hgo#^Sh$0UfQh&2{C~%oX*)s9Oxv7x#-;5r_`2uY^k9xZ!l2c#-Q0 zC68^{I>Xi;!wdHIT)CA)B@{`_FpV74&{ayFbdR~Fll1OfT{G7po8a7(OU60Jo}HxO z5yM3Ky3;P}+I@>kK&r7UN?bavIH6rg*SuqLNr!xs{?aVaoRDN-QO9m! z>t=7gJN{g~WKAb4O+iiX#gjjuy*v51lB3l{v8HiuT8iZ*_~iHopB=EDW?+&t><~ED z0cr*rc)el{>bhd=3pVwX-I3v!Ea)Xfr$y?Si~80q`Wo1GSvo&*QQL_SK>1T=_|KxS zx!WD?$vy@NTlwAYQqR)n_jP+nMI-wsdbo%_dS41xBi0 z7h`c`a(Sn3!^)xmg;1!?UBv1&byLO{Y_uM%H7o~NRL~4hgSap4ItlG^9Bob zrj85SBaQfNn$mf!Q+fZ0v_Hy+^fJ<(vGYLv4KW&Q#JjlmhYQw$5p!Gd)<~i?Lz=T@ zqQIB?0eObB^Tpl~*v|FR`N30Ev)0ovkNNH4MvKDECpH6}@~fqErPbI^-m4MzC;Z~a znSA^-mQ?3i%1}!)D{&8$p82Vce3(buo*x;c9t;tme0}+PqSNH--mcMy751g=!uhwv zoRr5D9Yh46|LErvUb0AeL?PZzqsS?dmqJ1NOR0OWm+OnjXeO@VWb=K>nFsM7%XA9f zd(i~BDCe|!=Dp=3wpKQ5xT(t`JsQVzuCo@i_*j|>Nd&$iWf@i)7gd#oS_?;ZQ)!4Y z%vJJ)(|E2}QxKU`R<2YAyB?2`=`OSuvp#>;ysp0$q~NyRHG)kcP(BheQYz}VJ(6sx zN$^v@8v={G`f$y}vJU^c!~H#{a}FiKstk78c87#0mMTJYQ@Xxp6k^?u%fYzR@h zVJPx<0N(U}-*^6%4uOdnUjJ;;8K7mq@Ie4y0m-BI%hZ>Cyx?vAdz1fItv|ogQ3-SX z(jSndv^5*Hw|OP28FLCN7gr4<?2VH5M1_M?n15S-6S__VFmAxF^CQA)n zXUPJkS#Oh|nU~vP%)=}Ztrf~^bLKfCYu$w**42wWBYNaso2TEob@Ww&|N7(|GD<|u zH=7{?fBKDY=#?dBzdoFkI`I?(+%}Muz>N>JIh^lyhSc{jfF;*-zMF4k9zncUnM#ofNSHw9JKJe4xyv3d04erthq z@5;!sLo+#H1&_c+^8kBUyOBHU?|~r;r6k5 zPJOa-BLt|nyINuTn|DYR9Q)d~}k!q&Upo|?KJRLhsFOi{@^(Ses!lb^JP7AivC zpp2OI>35!f^^)_)RXQAegzagk2byo1G90nYcC2&6IrAJ=5jA0bm}k&uPNUNPX4*Aa za*r-uZBeZ(t0a^wXs6EJ5J|(pYfguAx?a5;w_^3m)l`WoEL!W1H^;=j)jSQ+Uphqc zyeG>!#5XJ~vb@u+8P@UR!%7(8{-&F-VEM+?7T)CQl*Tj!5Tfz)krtV-QQbf$qf)TG ze?ZsBzrd80k_US(3tHws28$j!;0c7B1L%{v3K`sz*6FrT-iXb0@4Cd%z%$)W0sPBn zH*+s#!%e)t4!_N+)meD20T#jEGYu$8_$YC%Yj-152@%KLnY%k`p1UfV%2`uG&5MJJ zr~&JfO9h(e-=0a*9I>8m5K^eC>8P?Na;BTBPqWU=j~{1gg%w5Rnzs~Vm8~>*cAOLw zL-cb_|LgH%h^dyupULw=Yh}x0FgH=0qr#=roVS_y6_eH}?6vGZ$SCilD z=#AL`xfFwAPln{TKFdw+aQ}y@6WT7$)0j~yW_~3+9VwbG80GzNFhFpqI%2Re1Xg?g z*4+9S$|6{)dCj|Dd(F?~Eal>uu%H($i(KL2+B?Dv_v0In1!SEwxtb7NY*6rwq%Ce^ zCEg&5E(uv|ObKP)u^w!K>x8|>xRNhoPpJ%`d}kZQ%d3*dA{cZ-)`R;{&t}g127h(e zIkE-ro8g8&6UjiP%&@=boatld>9_~`)F#ICS{&pow^=xPiI1`HTU5U74I^y(&K%OJ zS&D(q7nbQ+#s~`_MuA0OOw1wkKn#bg=-^*%^hwB$@1PPmAwci80#YG;!NcE+KoK$S z(gp=fv(7uIz3vrQ$rD0Ny=Got$}k9Qvo&%KV?&#;IA49#HpV|%Ka z0v46+E0Td5*?Mx3@+Hw!*iGhX&FKhU^>vd-|8%nxFd;y|>=i09sgmuZdi~O2Aod3r zHyV)qW+!cAQgMC}r6di&baEvkGc0a1tk9d0$z8>ywC&AZzqH+%P2YLHoejU8&iTcF zhdvP{Tim{F2gvM-1HkvSgvTe0?{C)lr0G?6?vDHI%*Q@Ceq@3O+oNCTaLfG7RES-v zJOJ1_LZSzEzxnN~#_k5(+`Y5vHRb1TZAFfpAj{G~S#3FP`~Tu*_DbV#89eX`EQE7! zJF44G25i&sKID?$$jDIpqmn-f8TCN2of!tUA&iVo zDJ?Q@tO_cTb+~&S*vCKDR9%NFkX ztsp-%;o5ZEOi@)ld-YPQGJT^#`9@NWaHvEzz&P6@CSL*A>*cpkp=qqs>i|uvzN%NS z2xzzvZwaYDZxOd&YbUr~t9NV={wKY8k>72(D%Zx2UnT^BHl?~Z36!|S69fL;MSh#_ z6+!~U04lpgR%~V;BzA9*xut`o!g}Xij|!kI&f#ABEcZ^SAo}a86Lyr1#5oXwAuCHV zYo$X>afDhFrXkiU8UQ@Txne0Wwo!s5I~jI{-|XJ;5};~;mV@O|Yd#iqMgL~m@O0N8 zo&rQM>svc2j<>Drdzcdb29-qJ?%K>eEeU@}WlFQMe01ZIiO05cNvgY*G-g2gM;-gS z8|6d(^GjrX@IXV&<{=KYvP(BUJ)$VOK#ALs4D57JwxeEttXm?CpjbLhplA!j2=T0} zlC_n&6odX`lfa7fSv4}Tu(uA?hloQA&skyg0c&q++7AW*_H^Y3Yxi(!Xzn~Kgl*d0 z2~B-?_XkicGbz?eP2UKf)N9e&1saT?QSFH%_9#kcElOOJ2i5h79leRVugU?tm+aEl zfT2SFsDH3GkbwnzbwVmS))<`@j@=aQ?X`z2(DNTrPABS<-#G;MY>`#h-k`W2;QV{3 ziNLflV6U_VicKni+5M^ObkIc2Vue7%!H?w}l(=5lnhqC?fCFxeSB3SE#rGXRoky@+ zv;V+(sVRu-$A}jRoZahx_ojC-FD*nR+WNWFibWC^d>cUoda(N2n&$pa!(MQkjQ#P} zkSj1UWm9C%iJ&3IL4ZL)U#%sWCq~SIZ17`n3x8#8Hn(ukkIH4`%m4GTIKDY+V$}f` z0xX`I&mDm17Rsn$GUzT4ET_!6ncxKJ+huds>uW^>9(x8dc9sV?66)_>ekyLru4@lr zYI`QUP2CXsVzh`st+@KEX9pN!3?G-9{v8jjGSIYxlKa4!SD|=&;ZsKYwK6LEpr%~e z#(I^{92klI%MmaJ%T$}-F`my?L<==-Ac`8g8G9eX9Z?kCxaOPW{*XNQ9EE>>H=Vj3~Dkr?|1$R_vuyDqT5ZLgg!`kxULQ(`OZp`?~lcGDEya zlUu2?L)!d{KP3gLO{*gfyr_!Bw)W&Odox730v6@9;IYK{BW=OE4Q9T!XCM1D4&5lB zVmy}*`-H9lj^<$GgstP4r(_bUQ2x&Qug5m@qf z*_eH9vt;@%#8)-Pj2Jmin?EqL-W@qP?q|HQFygthSzCIN;}BWDD!p;*c&o{QmqTBZ zUPUgo1Ic(Qm+$P_!uo?-J4(+~egJk~NCMQ#y;?-p?HO-1Ngy}LdmK{3vHisLG9Rua zrHw78p81`v$R6^d>H2*7?fH9i+t#Ej@fDY8apaBCC%_q<)2m+X{3!jspS-uR!>Jkm z6&%pvY|$PCf$q=GnCnU}9FCB*g|>i`EhW`wxXdsE^-6{$HjEZ?Q~$M_+-r&J=C_ts zgNoEJem|T>9<>*L4=V~yQ$!Q$x%}3j8&Kl(=g9S8^c9)dw7lslM-BC_122jcv9LUC zePoVffoz>qXBT>8uxV7;LtW79Ijtot*FPyq$}``oCM!|o7iBm^UdC&4ue6+tdmBAz z#u<@5L?*dQipxS7R^!gp!${4lMTrstKffza%;k(!9NO*k(R|}nAp(S=X%*Ph7tn_(unY4u&+w{&@j8Xu#fa+GwW|rod;GclEP8iF3 zs`8#aZCLY=?@q~s*S#Fj`WybqaydvSL6`#UN=t{}YxoN*FC!P-&uttVo$*kpKlyDO z%C+s=eCvCg>Q;oh%1gyB6=sk8O?3x+!HSaLOFz9tGnl%fUl>;dye%(%lAJ7qOU5m# zT>)_3xF|DDU{y8QAEaLVyQ~CLtAql^$MH70YQCvBNl3kxY>0Tb0=L&FK)CYNTNC9n zJI_R2`c)I)roo;Xck+!*pTcUjy!60180{+LtR`MTnud{ znLvbUaIkc!4|+U5=!O_J;l9ynA49xBYj-+gbcE&TMcLJCsDxVOJG+(qoYl#cP1OoC zmlfoy>>#q55#QRh1$YPa54{v|m8n&XuD5P7X6r8(n8M^2tMVvn;=48ObzQIa zxp(gGjMSjT4d))q+%`cWl>#NL-f((_!*Ie~OgG^tHIfsop%Th0x z#2QDg*RG7iCmf-yfGd_A_GOZv(i6VoHtg?Vl=>I8lhg8q)`$Fek5#O)9QybAj-okn za)bCX!gK?qQpspEq7d0N%K{}U2y8J7#o7u)s^=O-vy2kZ2I27R<2EO}Stxm|WT+v5 zMlh$4C;|6;DpCGI7-|W#k52-bz5~dC4}FA;atsv{jXoVt`o{427?}uGXgxywZkD3J zrh~PQ@`s$T6LP*=zzMTE;foX$m;EW%BXxA+dsf@{gml#JWy$#(C8Qc2^~}AK72E0H z2MXi(fcz4^6y6*>BRKnp`9%$=uj1Rs7@4A@ocOIQd9x$X(4hQ@#y$6^dyAZP*7mo4 zaYLbaD+oi3Y`-6F=xuvwtU6e&mv3#314U$`V{;D8s%28{}Z690aYl5>m*!w2I3%@+* zT4*-GK>hR4UgP;#2=go=yrJBSB9Re?z}=`lwH{XiuGnd%<*8l!6*kD%JU75iop_P{ zm0m@^^|MCF(=gN>pE#Sa3UhI}gWz#2a89sXqKNvh*Z>dk*pri}!m4^7LPXBJw{Wj za_wDDk2w$lGBg}41%?VmQ*BC~RrJV%hlaeoo8K0tYp5oPzVNWDT8{LTxspH@A?sGd zJ`;+62aSf=*{cYAZCe};-Uz=KeECRm(mS)lw+`YU5s!a?;Wm8y0q{*=bFNn{f6k-) zQXZ~kakHY#Rtlu34BuJtCWgE2K06l)>nbpe^upcN$Wlm<<}MKxQpKx7acgGaSbu$tQjToGjc65uP8=t@N#NVyJK2C4vq~L zqDV4uka(vz!Pxiyl`(i|@2E!4RuEbYuPbRbX`Jl2wbkw**Ql?&k*8sqm4+xmQv)t= zEXY^)Y6Z86&xdEVMO@S>ZAoKP_#(Xt|9YhnkwLYq>nBRI<@*f$2R}WeOoS)*znMoL z6=*f-BkyYW+Pxb_ED&6Tiqnb<);#5|(S^W1e{97a7;BAq2p6k&R^aBm(X`GH`pR}J zzRM3|@#gUG`B<16okpV1X3`}+A8~P(J2?gT5w=p5lN9~Ahyyff_6o~w(u{Mx-RnB1 z9TFI_OtKYua(iU3YYL-tOQ)ybm*z41X_`NYIMdctD$azA@Ue9Z_+X?Hab*>5Jw0;p zz5S*!*PXkvp4>f`q>jNT1FLJNYAaQ1t&fWGeQ7kh@YwJ2O0jJlH52sv7J%$R zeeuDaVk>4PJq$+gpv2AV^vLS=)~-haDkpIAPwwO~TK7=@^H=OVLq5e9PCYx$DrY`h zH8sv?EZl63!67!*sGG1}AEnHMvX|w5e zO`iygLusd_vNNv2pjvN#_0n?siJYs)EuL$c@976e$Qr4lo9SW4{*K|O`x9a2yW?@T zD@`{${mAw(&^jzMFwpFII&Yc!^gQeh`bnBUWP3esoTW1h<_dNs0P`>M6StdoJnAed zul~0`s<0|?suHzL!d3&n20ZM-V2f-MMz_hDII?MWTURea$(4Zo(&4X~O5z3cFv5 zPxq!$_V=)cwc7oi-y)W{L9@Cvv{=TvkLRCzXGjXZ#bl>+sbLAUr>rZy3^XowBy!tH z0Y}yiGLy(UHS%tMmMu!(1GBku|5h7GSIdk23W$p}j~JJvP-=9=YFHpVp65Q%B`Rk` zz8+(%;98!inS9^2KD?5?sD~ipHOf$lFR=i#Z{NWG6iSfq$J|I{ch(HfQJ&hB-mfxO zD878{-4ABN6Mjib9aUp48ii{I&1bup%Q*SdOEife_gVf1+Eyg?Ryx%sycGj+1zdp9vwz>$M zpdBJX-fq_kPvKjGddJZh`y^z7{!ZL{D`<)8x<)r?;a2crtI*k{<@fPekDFYGmK7rt zgoN2$$qX9AQQph%8WEPlIh^0<(T9Z;GU5%UT0Q6Go}|FBE&Opl!eVo*-&{y{60@HL zM;eC0zk7PC9f?HDVh`7Y#FmZy#UmlbD9>-~9jB=bN2C*=$r9TW4bbD~MaN@j+#|)Q zpThJr`F?U{=rLIqnNae4;YK~N?^yaSwXMs!=+`>QR?XcbXlGb(DUp%LT(ZwgFm%^PR<6 zw-UvCU7o_abXl8k27$63#_-J)HF3o6)A0R7>M+_VR`sELM+e*24QgTg8+W+R{BC`v zlVM#ECgqE7eFy{BPTL&0_k1m%T19W^1m9B$Q=aYvH2j_Zm`~rPD>=GZt$C5?*2U8F z?+4KtRb>HBsB6D%6sEM`WD7l7IM$!AKnKG%o^XJ14q2W5AvSV~f?; zAQM{JXmZ@nF-J^-6IP7Bef`}Qs~4Au49z?MIv*bgy=+Vx0LP?o{%7G^=jovA;Dy!6 z`Q5YS&vd>SJ5k~cyasER_5(b9E=pRzzj)IiRvx(`mpa7UEEV0$;+Qlvc{c+CcIE$V zg9qZMq0w27f}0AF+`JX%DS6b2+PN8GUeVvVRL*-U?~=x5ew2odo<`l)wf!EKzi<5D zD}L10elYFiVh19Yn3q6x;>^6n*(%V&W(7QDMV#l_Tw@+JmmDnrI5V>p4orO|p-$1} z7NX{AgqYHkkQ;YTTIq;eA>)MknHO5$O-=7W#cAGF-%GIf?|jf;TguKCz2)`G2L<4a zdO$J7z{3sJj&(@pRNX5FA2s6U9HS#^7*_JV<}*vSrTEEAGh*QpC)RZA-Bqh*2cP(l zGtc+y4c|HQQeKUYu5;;5uQVi)Qx)I3{pA(t?^FS&!4YI2aV`7w*rqBVW7mPO97Fa}q;A4;o8@aPR5 zli~5^nRBLtFdn47q1eI38C$4JIZ+j8$dO7T7u&eC9u4%hPNek;dyAa!;_Bek=1IyE zZN1#5|GV%OUk6Si`*gW6Jby5~6@vHvZD-9IxwM%Q-c@IpcSV?mLP+f<7a+ zbv1`&sg6{k@djgg#0GwvKoxvUO*;S3v$1_#-5>j^OqEl$s9QLlGz+&&YaEq7+;Ywj zjGHN6oF};)JPM(d50w&gVL0%pN#TaBPbT$|s$R|tt@>rc)toHBM z0W?Tol#Z>%p$#kJm+xD91r2qM`iM2ebD7Ybfy=H2mA^J~<&Q$1T`=K4`iXXRRH;!Cl=eVB1JtoDPQeTyhhG0BE5LH}xG zS@F)M2Kp*Fi^Trs8Tk!l(gqosvb(9yu?iUE+2rqnxu9V&3|J?VQ&~5?o zEL}zx+SMzQ145)veu}+tKs%7kVQ!VnNj#j@>5XW_%W(5K{n?9o&Z5? zP}`&u*f*;Z70JQ{&?eem*ir=8e$QqE2YZAq@s_jc&0yh1GP1?4aTge$i$YHIpRFKP zeBb@vxjRrLO_~q*Nm4hBNb&VCk}1nM=bIg|wN38rY`eIzBywQTV#{Z?iiunP8=L7I zeGfa!;sNjVdE*&+cY}LJ%EWzkZ5%Lb@9jx?!`qF{?Bg5&G)t$?wZ3Y$5wTJ}-X%7x zZSJZxzEX{v-2{ym`hdzG&k8==0b=Oz13G#@*U$bGOGA*BWvN7Xhi7XnHc$7Eh39LJ zuov}_r^u|sJEaJk)$aioAE|4}OhWH{hOMn|WAj{WawyMTV$yc`r^TiP7mcUp8{=uA zon&3WQXQwl;uj?+!8(7@{ZRj7r95-~In&(TwN)T>$WAb+_PN}1z?-_3ObeI&aFNnI z0UGsLv+?R1kyeZgoEGd8n0W^q7gOuH0OZyr=TJqSb3iK&eVUCKgy# zL-pJ6^HMS+^3L{Iil~0PCS%q3tG3Mv+29)~_dBJjj-fiEq9@^B0h0`O?H~bg%{iDY z{fud@VOc5m9mnTI51;wOaI>Ca_~$;TmhbU*xal+j7PH7K0m;&T#UWNq=q)>MkYyKr zIdS)IN2IGLPrL#=tM`{QPYZE<>1g(kA)j-G(ZW;Qm>obKDe`{_awprMgzcJ*s~lQ^ zU!V~F8>Uus=A(p0BNv?1%TQ~GXNXa}ibgTXN#ZpJjQ#BZJFkY8SR;gQ?uyWL=gbx% zutLN;(JO2R=;dR z(qHZPShNwUok~1uz+#!Lpie)2fwhMx8kWfP{IUukd^I5#rX(0+o$iyE$5rLS_j_hD z*w;cT^^r6qsjp5iSsvvxhxnFK3|8O+Mr%eI7AFLO@}18_2_xGdJvCT%7vpEv(cc4o zX0t}t9JkH`+{Ah7^BU8njpOy+g|E`Wf@^O#m*C5E7@1QG%=U~IdjLP4&I)3XS7v49 zFKo0r95p89{)u&;hJ(O`WW-b-#KYw8qp0yvP{gF{kii)$XmlElWHd4-H5{hinRsO; zumhxe;$q@wy|Gr+0Ca>Hzax5Umn)P##si6wO|;Vk)+xy7yaSiJc>=_2zrDq90@nCH zM1fZt4~vSvhOt(wB(2ZWTor_kBrfD5AVj~Sp=HHP-^*v5XC?*O6 z3lmFHnaz(JIy5YIe?u);z zQ8RW&D>)_v>O_Q^1#7lniyHewiNcFj!M@f3bG$Bc0Xwm-H_?~dDRey>;`x%q6tSig zzunfZC+&}s4*M#b+HDb2$9ocy+=_-?UGFOv6y_X9C08h^NqRVwLZ&($PA>BjxzNd3 zUL_t?v^W+SCian+L>9>-MTw=(HpF^jK;KHHh|$dl}UQaDHzwIsa! z-eIa&7{TKqtoEnE$Wl^6nctpwwZC4#%l8p9ZBRz2DJLPNx<}Rwb-`hhF_$WlI~ed609VMrnii*!#5DI>D&IqTv}Sx>Bql!MPF#yAbTdnWYzLCT6C%~^|QH%gh!w-?Of$; z%t_RowbiDM__m~Ur*aRgw}_$z7vj_BRlPq8JURO4D|sz|_p%{7wG>gGoeP`i`ySHd z>k(CvVln8n=tap-=7g;t!6kY*$UBU8h8QZ!%)$thxIB7{{?B-#!yUJSY`)dLcdZy5 zBU})ihkB?P$_Qoe%xr3}yqLK1ShpEgYd>PIsw=xPQfs@`A*2(ASw8 z7_@Vi7WJPGfx4EOr!Lyy$NM6?Li3JX>GT~!396*oEFilt#~zbX*SsF`JBB5v}N78M%^B%u0&`dcD=Sc|<;= z37||4{G-}92_4cE)D0L|0;6#&Zj-F=7+%?nJEP=2WpBLEwTb$Tc~jRi(n57luSV|+ z0C8o9Nb3omWLYFY>g(P$nzQ!#1}v@jXOm&83}69w4QUM0caxxxC#R%klLNU#%CRJXr`=NT0$byX-DVeh_ud|JklQ2J z|8<_s##MpB`>E`P$?+hG3cW4ArSb9*w<*n*Z>4wUh(TXvW%}Esj2t1?kyl)Pz1JtJ z-IM_*`6F(_Ab~h}bL1UaE1*0rnT8UlUmFZ^y>Qh@OlhQzw@$A$_#I`FLB#KCh z$42FN5Mr1^lhx$W9G+ytRE&?(4U~t0^QM;+36S#Dd-Q%t2chxzO2=4o?7RnV&(?i4C5wZyvW+! zv;)MXpXeS%qHFCj{F&dR9`abdI4}Tj@C;_*O^)7tQk|^%tSqw!e<_<)lvw7-K&0{n zRtYx#q@Lk>i>a{MG95vde912yyj+V}RSDpdkUJIrItgW5b3}MGJ5h0T!&e}KBJ(kq z=1urEOAj*pENS>GAu{~M`M}@v(@Go2{|@^*U#RqtFm}=OQ`gAxS*$?ZCy1h?gp zuA34}p}HZ}=9?!r$*QPnjMPG-uJC>;JB}J;ttc1+NuXWYW+43sDw}*_E)GqJX-r%V z%C;vrD=J%&p8~#fZ#Qf^Bwag*SPf(0rQpk^n`Eqto;hyEw!a%yr4;>pX>ov(!Lvpz zp+`wf<$RH-%7F6|ubC9}_c{g99?U=Tc$%cfAsQVIro15nfwT!WKYJPfBE~^(O=s;{%r^Ab5A$&1?so zIKL;r&%&{ONc>#pEqZIrlJO;Zo`<+RHs{Rhbsu4EdPWc6P5rydNYLUQT5=0Se*)4u zf)3Nek<^6sc|;suUgIHY9Ck+Z(mv5Pig*Z?5G{+RJz$U3zk!{<%z!>~(LXYLD0ON;_XKXyb zTR#U8`(36$ zy3+ko;KlvV=R7w-*A!O936?sq(cR0_E{Y&{$w54jJ(jjV4y#$ZcxF=S@Ec>?lJWpjW;0|~Xr&kTildCalVA$$p85cOrvB%%HwqE&~LWi#2} zDn@Z}8W9GsA1)PoB=1VQ%c#_WSC*q<`O*GxwRBkSs3A@(WE}oU=aNb#(z`++1iqY? zIapO@AlULA|Jj$ynJOUrP*-HB@>R46N_=f-#hzV&o42hPJEge3KX?DKdnmbE>S=v?3F68(02iX@V9_h&O zoT`bq2}HJ9?bFmV?}K{jz;*|MvqRxI8hb!$B%;M3&0GDca;;t)%EE*0wppjPIz4SlitfNU1=2NuPC zF9s2mm)l`6E~7U?s0`(E1KN(XQ4Y#TWB&j$=`h!$F*=LE(PHr;!UuV^>*Oc0V?Q$# z`(nMD=f}@BSVC|7CjYk&t@` z$=^i>gctnnXWS%XeW@<9;=IZ@67sI*k5A>kJ1enKc-qKc z;$zx3?;)T&`55B^-z8fE0H=dKo%HwdTDbo*)!zi0Y%D%uo6NGkWAr4mW0F@QMGdw5 zUW9`KBrNrX+Ws_MAg8GV8qmrLFIm?C{l1l&iUHeC{1;9S} zla+cw7TaI?t7Q~{eLWLJdL|hF0t2$iaylZU;_JWsB#o}IIQ!IM7rFWh_T8O)NH%_m zr&WEo|Fi*2A4=n=$~Xa0a{1Y7LQ3scGYu*5sk<(s*Z zn~q6;C2e;Bn^Np>pthQvnG=%2g%OIljc)O!@urjUn6dm@dl#G)(OS6=xfSkIhWp^ zX0ss7!tR3N_~h+^`*+U2_u$8Nti+M-1-12bPLjTR*$i)MA!`)4eSM84$OKrxqRZSt ztpLN_;(FY-gYW-*o?k@8p%vvN;(}F+kh?}AVj&p+QV}&xz@VxTaY+gtHNHR*sn&`} zK)fdCqU`cn%v$bCHmFm1hf}NgUb;g95HUAz3c_s~^9eX2Nvszqjaf3Gw<%6$Wj>u< z^cS6bG?|@i9CAC=454^O$l{7c2g5bhssUX-%FPu{*^~G+j7t}NoOvdj+mLb1hj?_* zU*&q)V_2=wPU2LAakxQL(e5$-d?3B4wbvLX8EgR+RFx<|;Hx=Dvm=5lR%xLY^*E-m zF0=W?)qp3!bG(hC!3jr|A&#r^Q6MX;?2vRH%6cUro41x5`IhODYZh9`*Tq|J^R>pCB>71p6{(JVo3I2rqQ zLOk5HS*%s`Xhz@*bh&n_Kqmfkl)SzS#uLd4SO}g2ne8`fJ&~fh?%FFiK&a)ICCkjE z=?1j*I@jMf1qmQ{e~g6Mtpb82Q6X~~kq;kYGc#`%^1-;N5hcy57}HN(wqsPm1YkUy zI99LdN6#$IGf;nU4w{Z40rKa2{|QA=YDvgyq?nNhT`a_j#zV1G!rmaU{s}qtstJKh-nN> zasl?f6!hYmnafiZ0^rD@MEHHcdCjSC%d8{#wHwH| z`A??lY)Ogh9%~-TmB{RcpTUgoVR>O@R5I>6h#f+KyXZ5kBk8SE3M4WX%CfGx)p)`9 zDNN;sqS`#-BisUmojzBA<^x)kDXQtF8_xD0-(W<*W6oWEoHiv^uYlDKuzfe z*aQ`}GWDj}Bv6@3d9$g!)&rzgS#*exBUeRN?{pH=A4+oc3ot%wpj^#0eXjav_XsQj zI4zAPBodJ$>YfnoiMg&Y*shcT$sYaDB%~ZzNq1j!%w4IG_X4Y;peScFsr7S!Jkl4< z!|)z%SI~SIckFhay26)seA<0`@#cBLMk!#l|mmL#Wbq=dy*$sKbrNo&S`b(l0 zKXynt#!-x080%j`3R%&19qPCnJm*d?6za7pm*BLV%{In1#7w2^_jcQ16Y0K%=JDQ6 zQ8IRT@Hd-&K#jWANWgttQt@$&_7PQo)q`<$I=??^uo3*$ETq`O zFdav!F-`MEa4Z#b=V}!_UQ}hOe+nYNkd%0We&WB7ejI+`y<3GXgJryWdE9r43gg@??pF#$HRT>X=whGcAzHfK^VM=;XimO_L*<(&(L4xmp=ZA&Bym- zMea*1fE+$qvTL8*sM-q$Cie3&WS0ZkttJs+<#8&D>v>NpixDTDhgdi?*G=QV~~j4kE>EP|qi@E}C}{#gnP!*ntG= zbwX1++GGP}ZTU7@?<4+4#jw!xwnbImu< zqz|F2&v%F3zROu+vc33j-w(1kpIrVdH-lQk9~=32t~ie;Uw(rFDGP$okkG)Akq`@+P9!C)(PlUd20DAHbH>U zLmDFS?$lije3sm?Nv~++Gg}2Ol0*0;xu>}6#mJURaVaa$EFD_PU=aA*Bt5NT&O7zd z=(I%h(yL3Q^tlHEQ|5$oWcFDA@V);lm;O-%Em3%a;!CPelGZ^Tn!yKJ!EoOQ_L+Wi zr4>K)?}u~}tWu-q<@SfGLP9i$c>R+`OwFA?8fi`VNjtza^jK^PC`!84j|C&r&BW|XsWRhZF*1B(lRDflLPvFk{*wJGJ}Il(5l zd^G*K4hfAM{5sGeg{-{Sg7etWU$0cMRt-yY)TA{NU1*lGvA*D z!MZx;G;5LBOiE^9#$(9@*~MF?)6eg98BuBIFN%G+t+S<8*X1Nf5VAgCW5fOSYAa8D zfP~ESFwk+-s%3sqJWt&~DX_2q$#B~pa|c$H;6 zvl+)EM#zj!*-%N?SML#;Y7w*({w9k^e)!M;T>HqMrM}}BIOiy@Luf&baIK5!jdjvg zJ}k=GTdl%g*XFfK#u0`ckAUDwH51(xua?t8x(~OgLAdnMmO4E?b?}y)qysky`?D>G z6}wVmZynhntg`Ke0N(6^Wv;XnFRaq7|7qqytlm<+paCGA&Af^j zH0h017Puck8~ED07BE7zQGJh_81JDWtKhDQvJrf0?y6)nm!3UDlxEy#P@ePrnmSU! zalLBm5b@19A+l%jKOcs%J>}z&&#_@#yb%tqF?XbVHo>b=S;TLBHE&VPns66ch+Pkm zh*7|~AwCjg>w`@qFTw=e4e7k2sSif~WNj|&N7dmvO>Q|Kfm%k_5|0q&v}ex6%kAT7 zUF3Zxm%1556O%>gUE&c29$_>8crCuCsMXkwpJLdrBY?fb7`QqUs5X$c<*WBPtKj$| z-SIziZ@dSvk$&XTuG^pF6!H1=8t*h*)n&5V-Csd1!q~>=TwDUescg~ZNGl;1r7s*B zFmpJ_?7UJz_Xz`I$uU$EA`!12eA|fAxZ}P;Kgve2KhqyWec`P_Ut|e)x-UsF5@eVN z`6Ia+HiUaPr}4_DO~Qb+K#`a8`D4yzyh%4GQb9?_>BLlAW#rUpL_hA-F417)TpHllwnC4FB z)%8@FSd-*%FR z#~%hi_P=!eQzdivslhDv-CF_Lw#qQ@%{BGjT+ZbloJHcX7mYc)!{A%wf4ED@FsqX) Vkq@GbJIMb(b6OWupk@*H{{f7yXsG}I literal 90045 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 z zucx4P-Z|@-hAH$h!?OcBMf@DiyOizKnNi2Kh>_`yq!d5T%%KidubU3$065HCdb@pRtb zM9L(R$`T83MySXL!Cw# z=n_$;zLBpD)v_!5wIS5e1LtsBhO+zox4=A6eBzCyv#4foy(weE$jyW-DSqlU5~G zgm;#>Gh#buJX+c-NSVbl&(u}>*R+_p2FfU^cViDwMz~L|$O5=WU=phAWFI+RoR>ZS zv|oOZDMa%+up_495-ACh7#t>tdL@w&(J^FCjCNRKtTe0ac|j7H>jxf*pgZ*7rKgv! zrCyZlVLK~y<9u-co+?RqyMRm2#1rjvRKIpS%tdjCQ?vrpHC%Gb6g6(u=c#I1dYXgV zjK_lKt=(9elho+A9QRZRyKU_tUCmXiN;5poxAKZRLkFOJVT8wAHI(%k6E%$BMSHYX zx){rccTNv~`Pa2YIZ$r8z$8EujWuOja}CBcuNN&Q{~EYyjDeQ2n?Gp*&uu|a?Z(dy zbS&X!Z>2TfeyHkjwiKgNw|$QiZGWxz1@&EoNPn@?BHsF5~k|>n+|8 zg1)mLOr8_*@)P^qr(?BR?oK+X>?~ck{~lRm0B7G=<9Oow>*`{FNiyz@S|-;f4UYU~ z&0c|+MNKWd(3;p%s=>lu7y5Vrre(~I}^S>%M1_U&;o#PH2T-dpH8X{Ro7Cq+miQ|^*3|&zU ztZ`+M7?8UAlEHIajg^5>`7x}bHd0u zS^W%F8STkvC(#$K#Sm}Csu#XJF|K1>|3}%UnNEc%ewQuy++yiS4`?OUq)D>O9sY00 z`O%nMhQ-RL%o2JbPFwA0czzv}*NWS9Zm{N|B#z~>4rwr$v>B}aG#_9>%uD@n)BNWp z2OQOVQpNm{7(8Uz{K>0MLW{EaWqTZy*>1C$D7HO8#3_QV^PtGW|4oc`W_mRl+X{My zhIxgl=G*cFZtb`i0EEk>AC}7hL)!qli(kDCI;>wjoO0RtnncKV%@4&fd+K||_x5jp z2@hj!_uysX)*{ymjWcEKlk%SkIr$5Bz5-$WVFZ(mW4tXd>Og(dM|Ai24?U4}H+`P> zaakUnbxiJ8U*6sFwh*#B^J{Sv%**!a7IFt;9KjbC#q+6;+l1r(Ahep0n*v-5dMQO= zJPSL7<|O@(S}jTmZMta$a&^brK|(QJpdSdDqxrJd><7rQptX^*NXI~tX{SR^Mu?yh z>B`w(ey-gk7?2q0a4FI|<)e~EtIXFL=JqN!$Cc0*3vEUSb*sm& zT@*S`rgST3Jh4a7eK}ls)1;=~aCv-Zd1l&Xck9^UwEcYu^&HaYPod*lNldYm3U735$O&Hq5Ych> zP>X*G>MrB~lf1ch#-eKC+kpH)46sOrUT-(_=&GIzUR&!YXl=6lG8z`@N{7(FRM+N#{q%nD4W8YB@mQ8R7+hFkAJOc@P$C3o*jsMsA@*l zl+7J)-soUB+g#U-Wbs{@Yo0$K&&iR(6?%+g-?K zhHtx&aE;;-3o4PK38p9Pm8})7ac5#4TZhP@5Lu+qHHMIToGN~MOd?P8@b!ke z#PxK1+~^0{OV|1F0Uiqs)xmDPP=KyA2%=bgBdW59 z#H~K@wHa$|Z}(w?c&9QNXby~QQtbCy7L0p_9AjaT`nzU^-m#XM8njhqpyRM_3C85e zCl#R9^UPQ3ra$L>pq1KSFQE>rDM)_8X%1Ad_SYnO)a&l$I2QU>TyxXC9hbEY_Nqp^ z{Dt23=q$?K7AQ5_^@5_aNFko8K05ENE=4$Ee;pV2hFjJOr=$Lo?T@9-qr)vjA|P6T zkwT8eo0?uGM%%%?aRd22YtGHtl|NDuKWKl;JQ|xfGz-au-n0Q~jx}=tV?PTHO_`L;@`yt%l7bYTCR3>7e ze4DsTz88NiLe5*d+|&amB$%7rba7OUYfSdMTFJdFh)C6-CPeO6-rMLEt z+g*?DaH%qcGLijJwNasg!J^t_CfV5J%vu`{qDf9H{1Ed`-7Iob%7ZdJ#nrmM?sec% z!4=CAME>vs|9g%FrM|AV`Ai{oEaLTLmw56W#+wCC|2U&mSniaQbD&7U66X3#SR+|( zeA-b~<6GYWB`@1sXIG8yLd)^IDX<;-SQM29)c8JflHO$SzR`blqy#O_a+>iUa;yK7 zQ+fI+%n1a@ncYNg5)u?8w+IPxL4^V0-+;w%aLWG(Sd1Msn~T3LeB8+13x3c}?`_>f z;3jT9)%uN=+&r{k@t2CQj-CF-?|V2K!~`cqPh#Es-bKv2w2S*2_lI9^FS}x2a?9=9 zc6ZZ~F2Pj03s8b{kaYeLYb{?y-rzS#k5yI(gQ zhYe0B&veC_#T|hxE$>T&aLab)f?bcWFtvfI$^|b%dZj0D7>nCW{XUYt3NgB92k3#G zlXd%hUHWM22`rB?+KiQ>v&9#a1JJ6PfRz|qCxAO0Yl&WjK)LcmIS#`jm=l3K8~A5X zGpQ(Zm=?;;@If5NG;Am6Z~ygl3FLRaJ5Cg1n>drIaAUg?n6ndRSgh6A(voTRcFlGZ z$KMy9C1d2>ICfQ=0gdRBJFmS5v=r{`5aJdJ>h=lEPupA=|65q^O20tcuYG444Wkhb zO~0egl=k-{$-pZOqF(LxhoASTDY^FvhQ-8&O8Deg=p#Ua7*1R)7$@(|$S!%HPMI@N z9Y4oYe<5N{n4e~p94`#A0&p(fx=be83EacRKr*D?0L-Htn7f_-TC0lnBD{sfg&Zus z^UsU|+xew%*d(1tE)ey&>@uEWgn7$9T>))FX&--cR!Z&UEq0Sorx9~X=4=+0UIO{2 zA3pXA?0j$<>||!DsHDjt%-_wDKpZ^kLgHW+9CzX?w0%q?3gz@q-$Fn%>g_HL=?8?) zO^Br);76BS4BQ5%LEekKms~`J4*g4pFJvShj_>@R#v5$|KIIuR6pCOOR_J*b$L4*% z4Cz6XBm7r=y%@6IIacjIB5jb2cv=GT^aiag=>yZxe~s?Rf;^OH@~sN{HG z3#!UXYy0`9DTl3wO|%?B?NIKZ~J3%MMcD*=XISg+EY*kBU# zjk3HwEi5z6E&TUNp(W-ls3JG5nSmE)K6kWfG7?A@Tf8WInp-yJ`Uns@XC`>~5}*X~ zrgw62XMucHkauI;-45b)R0QeC^WU;tOG7Zh$a>R@uK`L=W&czGrl>TAhAV>|C2BK2*qfA;>FJ+Sv_Zg z5`*0xK3nH+YGfkWCcQ7O*H^d;8lM;?5d?SSe)6LL#jA?w?XI{qc#&1>2T60xifpP-7r`1QSZdL zxKLHaz&M_#UtcKv%74f^1yVN<{4jQ+v+zWxBdvlHX4uav>CWsmXJHf>hOP6c=uLR| zRk^0iU`0TA+H8d(jYP9=%bx}->RHwFK&k9FjD4_19hB2m44zSpXZ^n-lF#RCsaxRr z4%{`Ri=0&@p$xrWuFk%t9C(zbXnkVZf)fOGNrH4umCE-q!l`Uc5$HVs>L`>HzYOF0 z$L_t~=k|mWLVoSfzpOP+laFKOv4B4n`^E};(udpiYC9)_c3D{fq70k6uxTzsiX#m{ zlx}I!kaMG;{Ko5~r&>uxf@a0WJ1g>Oi*d{}ojSgc{@U&riE!NznF0-n1W|o`&9pGC zkq((6a_=GJ!nMtmBWPL0nLgf%_a^PW{+wR_rekUKQ-3GDm+Cm;m%RBWEKQxOB{#4; zwVI5gas&tXj|2<`U^kD)b!@$)qHT_d!?zyj<=2|ALppG7YR@TxqwL2p4eSo*MXtB& zc($rl;f*xRYbV_VnwAtNROHkvLzpdE)E~$H{73@4(B!!_>0IQ(J15Vl*xY>EXO%Yb z)XM;J@>p261C!BM_3CMxqd$7n(i*5y1nT)BI!ApWXYr)drO0#C{imb){hUQ3i50K$ z{n2gwiT(dM!~=@p_X!+lmi+()K+7GqzwN{Sjtqae=+Ko+sA9*iiOB`Y&Hn2uUz7%j z`+Y`0rsjg1`cFsz;1Qxti$sHWa82ue1Z~{HAvT|lk%r)3?hD~+kl)I(33i|WF`yci z5j6|A8z z@?`I;jk#Iu_QbcFlprsekWCjX+-6GjgA7nVs$&vehkT z`G7aG+rYqYYJVz)!e@vapd)wP9528&+U)q3x`Nuh$7c$L_Xy?Q9K^ReJUMsrXa9sj z)k7A1oB6~!DeTAUimQY`jn6dqkxnR9=S5Ip6y~Zo3Btj0LikXdocH>ol6NHl1?VHV z_R}-WA{wn7+{;}7#sS{Ljxj4cs&cxjXF+Fhl}*YO^y~K9zl5RnRT6zc zwt&(;5518__;+AK5K`VCgyXIQr3Ocxah(Udz0?u`=#U5b09_gtdj+FQ2ol3#vRF!-OE} zw5-C26-^HS7#~cEjemBxqVFJU2D2i@6O>BvUIX(`03dEp_YopI2h{Li=;ama#Q8Mp z1H~r`OaLS9v9E2qwA;}@-;#R3C|nqWZLl9Lt&zGAfM6s?jC%q(3H77^<OVC546A2N0{MXtYG)V~*R2tv8{~FBoBN1)5PMuS7I_70^!v?&v zOdZ25IEV8kZ`>MMt;G&kmlMrQD=5C3F^=%Q9Nb!%Zx0&g&uOTHovfY;NGx+x}7$8 z@2OOUpL0`5)Yy0S5zygL9u!(4pC!Kr0*YeGJ>!(JH=Ni-Tj0$YyI~DXugi3Khck|1 z7$NROxVX|f^s(}}Z%k#bmXo8q)h25D2DW=3+psY5l{d&H_>7=qDkV32PYYy`=ryik zTJE}R+BA(Zsws)Pw1y>16cp6cXZ?u)aEW(9w!Qak=-t^x$7PMkCCsmKe*%mIWvZLQ zAhtWkXdhS4@2C{ZLz9*JPlA`x%yVv^^tnYQ77{OA^esRnXl)N{O(lWL#K@t_PP3uN z=o?Enfyc}M^!xU8(pQ%6^K^}kV{FW<)nf1ex(s!EOpx4)7I1qwgi;ikXTcP=x2)ST zz3m`<4zBM~Da-CPV<=x_vWH)0tHb%Dzp*=r9mv7Tgzr=f`xh+eITF5_KLNp_{u2_M z%`lZf3{keJBgo|X+X|w2CM=FM>d6Byj40OrvlF1Bf>fsr>D`du>_jR6azVkC!(HJ( zEV@+w`Z2{DfI3W3jW9N?dX}ExJdE*nDw)`~u5x@ubnn20HU9m(u<|!lAqGIDK?B1Z z-$Zv3Uf)3eNSx=$nG$WixHQR$*@;eqqUlnzGNQd>81&OJE@P6M7BchWj+TJi{KSM{l_qE)Zz_V#AE{f%Nb z{JB~|(Mc}V0w2>-nJR^jwJgsiWB^V>iyLnIj z=kUl$g}0Qdst8g}+=W-_WKM~Qbva{>FT=PoJ^GjDzq7QQoc%&HJAYA3lW83UG9Wsc z#f-Q+S=Qp%U3P+gZ>+g6PKh8v%kOHJ>mbZ|d={@2>{XhLJ&l%seEw|}^`FZt4FqrH zIy4GOkM6oO!P0hkJ5?CBaQ;K;&P2KMXy0C83x>zyR;oXL9c1|QBw_S=>MsXpB)tK{ zfpW?u-30-=Wv{sdHy$P#oybpYoF!yJkj^3hPplu5^|OGOo4-yvcf>U}YsBnyDNL+{ zUGsb+P9fx6lZ-wc;aws>6ynFa&Yz|Ihpp{54*Xx$1piw{@pHpHHp%?_>^}(yjEgAL z1{9fo<{#BTVj2A!$Kfc)A+_V3ADx!Ue{3}bNyC5G{$tAjr^*5RKR_7DfX8RT>4)wC z{7)&}(GKMi{si3!o}SVlczgUF+Wu&4Ktle|;{@RMNx~sx54Q;ZAbHoI6ceEn*-q)d zekCblD|ysUs`ki1q+;?+eO2Y9IO3xgro-M7zJ9A5ox~xWKLBxXigl-}fP?7hfl|lO zJmt~gqiyrHif+C$;FX-mA`C89$x8|Q8pCLC5~U38A4U~qbbkKm=iz>x_W$$)tKzXE Z;`3LcY%gyHOhMqEmfBU-Y-Ni_{|{0(yIKGM From e018c76c1edc1579e45a840902ae85602079acca Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Thu, 10 Apr 2014 11:34:40 -0400 Subject: [PATCH 08/10] Finish test cases for the methods of Targets(). --- tests/unit/test_repository_tool.py | 359 ++++++++++++++++++++++++++++- tuf/repository_tool.py | 12 +- 2 files changed, 354 insertions(+), 17 deletions(-) diff --git a/tests/unit/test_repository_tool.py b/tests/unit/test_repository_tool.py index 1ddbd12b..a6b00761 100755 --- a/tests/unit/test_repository_tool.py +++ b/tests/unit/test_repository_tool.py @@ -513,14 +513,45 @@ def test_init(self): class TestTargets(unittest.TestCase): + @classmethod + def setUpClass(cls): + + # setUpClass() is called before tests in an individual class are executed. + + # Create a temporary directory to store the repository, metadata, and target + # files. 'temporary_directory' must be deleted in TearDownClass() so that + # temporary files are always removed, even when exceptions occur. + cls.temporary_directory = tempfile.mkdtemp(dir=os.getcwd()) + + + + @classmethod + def tearDownClass(cls): + + # tearDownModule() is called after all the tests have run. + # http://docs.python.org/2/library/unittest.html#class-and-module-fixtures + + # Remove the temporary repository directory, which should contain all the + # metadata, targets, and key files generated for the test cases. + shutil.rmtree(cls.temporary_directory) + + + def setUp(self): - pass + temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) + self.targets_directory = os.path.join(temporary_directory, 'repository', + 'targets') + original_targets_directory = os.path.join(os.pardir, 'repository_data', + 'repository', 'targets') + shutil.copytree(original_targets_directory, self.targets_directory) + self.targets_object = repo_tool.Targets(self.targets_directory) def tearDown(self): tuf.roledb.clear_roledb() tuf.keydb.clear_keydb() + self.targets_object = None @@ -559,57 +590,361 @@ def test_init(self): def test_get_delegated_rolenames(self): - pass + # Test normal case. + # Perform two delegations so that get_delegated_rolenames() has roles to + # return. + keystore_directory = os.path.join(os.pardir, 'repository_data', 'keystore') + public_keypath = os.path.join(keystore_directory, 'root_key.pub') + public_key = repo_tool.import_rsa_publickey_from_file(public_keypath) + target1_filepath = os.path.join(self.targets_directory, 'file1.txt') + target2_filepath = os.path.join(self.targets_directory, 'file2.txt') + + # Set needed arguments by delegate(). + public_keys = [public_key] + threshold = 1 + + self.targets_object.delegate('tuf', public_keys, [target1_filepath], + threshold, restricted_paths=None, + path_hash_prefixes=None) + self.targets_object.delegate('warehouse', public_keys, [target2_filepath], + threshold, restricted_paths=None, + path_hash_prefixes=None) + + # Test that get_delegated_rolenames returns the expected delegations. + expected_delegated_rolenames = ['targets/tuf/', 'targets/warehouse'] + for delegated_rolename in self.targets_object.get_delegated_rolenames(): + delegated_rolename in expected_delegated_rolenames def test_target_files(self): - pass + # Test normal case. + # Verify the targets object initially contains zero target files. + self.assertEqual(self.targets_object.target_files, []) + + target_filepath = os.path.join(self.targets_directory, 'file1.txt') + self.targets_object.add_target(target_filepath) + + self.assertEqual(len(self.targets_object.target_files), 1) + self.assertTrue('/file1.txt' in self.targets_object.target_files) def test_delegations(self): - pass + # Test normal case. + # Perform a delegation so that delegations() has a Targets() object to + # return. + keystore_directory = os.path.join(os.pardir, 'repository_data', 'keystore') + public_keypath = os.path.join(keystore_directory, 'root_key.pub') + public_key = repo_tool.import_rsa_publickey_from_file(public_keypath) + target1_filepath = os.path.join(self.targets_directory, 'file1.txt') + + # Set needed arguments by delegate(). + public_keys = [public_key] + rolename = 'tuf' + list_of_targets = [target1_filepath] + threshold = 1 + + self.targets_object.delegate(rolename, public_keys, list_of_targets, + threshold, restricted_paths=None, + path_hash_prefixes=None) + + # Test that a valid Targets() object is returned by delegations(). + for delegated_object in self.targets_object.delegations: + self.assertTrue(isinstance(delegated_object, repo_tool.Targets)) def test_add_target(self): - pass + # Test normal case. + # Verify the targets object initially contains zero target files. + self.assertEqual(self.targets_object.target_files, []) + + target_filepath = os.path.join(self.targets_directory, 'file1.txt') + self.targets_object.add_target(target_filepath) + + self.assertEqual(len(self.targets_object.target_files), 1) + self.assertTrue('/file1.txt' in self.targets_object.target_files) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, self.targets_object.add_target, + 3) + + + # Test invalid filepath argument (i.e., non-existent or invalid file.) + self.assertRaises(tuf.Error, self.targets_object.add_target, + 'non-existent.txt') + self.assertRaises(tuf.Error, self.targets_object.add_target, + self.temporary_directory) def test_add_targets(self): - pass + # Test normal case. + # Verify the targets object initially contains zero target files. + self.assertEqual(self.targets_object.target_files, []) + + target1_filepath = os.path.join(self.targets_directory, 'file1.txt') + target2_filepath = os.path.join(self.targets_directory, 'file2.txt') + target3_filepath = os.path.join(self.targets_directory, 'file3.txt') + + target_files = [target1_filepath, target2_filepath, target3_filepath] + self.targets_object.add_targets(target_files) + + self.assertEqual(len(self.targets_object.target_files), 3) + self.assertEqual(self.targets_object.target_files, + ['/file1.txt', '/file2.txt', '/file3.txt']) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, self.targets_object.add_targets, + 3) + + + # Test invalid filepath argument (i.e., non-existent or invalid file.) + self.assertRaises(tuf.Error, self.targets_object.add_target, + ['non-existent.txt']) + self.assertRaises(tuf.Error, self.targets_object.add_target, + [target1_filepath, target2_filepath, 'non-existent.txt']) + self.assertRaises(tuf.Error, self.targets_object.add_target, + self.temporary_directory) def test_remove_target(self): - pass + # Test normal case. + # Verify the targets object initially contains zero target files. + self.assertEqual(self.targets_object.target_files, []) + + # Add a target so that remove_target() has something to remove. + target_filepath = os.path.join(self.targets_directory, 'file1.txt') + self.targets_object.add_target(target_filepath) + + # Test remove_target()'s behavior. + self.targets_object.remove_target(target_filepath) + self.assertEqual(self.targets_object.target_files, []) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, self.targets_object.remove_target, + 3) + + + # Test invalid filepath argument (i.e., non-existent or invalid file.) + self.assertRaises(tuf.Error, self.targets_object.remove_target, + '/non-existent.txt') def test_clear_targets(self): - pass + # Test normal case. + # Verify the targets object initially contains zero target files. + self.assertEqual(self.targets_object.target_files, []) + + # Add targets, to be tested by clear_targets(). + target1_filepath = os.path.join(self.targets_directory, 'file1.txt') + target2_filepath = os.path.join(self.targets_directory, 'file2.txt') + self.targets_object.add_targets([target1_filepath, target2_filepath]) + + self.targets_object.clear_targets() + self.assertEqual(self.targets_object.target_files, []) def test_delegate(self): - pass + # Test normal case. + # Need at least one public key and valid target paths required by + # delegate(). + keystore_directory = os.path.join(os.pardir, 'repository_data', 'keystore') + public_keypath = os.path.join(keystore_directory, 'root_key.pub') + public_key = repo_tool.import_rsa_publickey_from_file(public_keypath) + target1_filepath = os.path.join(self.targets_directory, 'file1.txt') + target2_filepath = os.path.join(self.targets_directory, 'file2.txt') + + + # Set needed arguments by delegate(). + public_keys = [public_key] + rolename = 'tuf' + list_of_targets = [target1_filepath, target2_filepath] + threshold = 1 + restricted_paths = [self.targets_directory] + path_hash_prefixes = ['e3a3', '8fae', 'd543'] + + self.targets_object.delegate(rolename, public_keys, list_of_targets, + threshold, restricted_paths, + path_hash_prefixes) + + self.assertEqual(self.targets_object.get_delegated_rolenames(), + ['targets/tuf']) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, self.targets_object.delegate, + 3, public_keys, list_of_targets, threshold, + restricted_paths, path_hash_prefixes) + self.assertRaises(tuf.FormatError, self.targets_object.delegate, + rolename, 3, list_of_targets, threshold, + restricted_paths, path_hash_prefixes) + self.assertRaises(tuf.FormatError, self.targets_object.delegate, + rolename, public_keys, 3, threshold, + restricted_paths, path_hash_prefixes) + self.assertRaises(tuf.FormatError, self.targets_object.delegate, + rolename, public_keys, list_of_targets, '3', + restricted_paths, path_hash_prefixes) + self.assertRaises(tuf.FormatError, self.targets_object.delegate, + rolename, public_keys, list_of_targets, threshold, + 3, path_hash_prefixes) + self.assertRaises(tuf.FormatError, self.targets_object.delegate, + rolename, public_keys, list_of_targets, threshold, + restricted_paths, 3) + + + # Test invalid arguments (e.g., already delegated 'rolename', non-existent + # files, etc.). + # Test duplicate 'rolename' delegation, which should have been delegated + # in the normal case above. + self.assertRaises(tuf.Error, self.targets_object.delegate, + rolename, public_keys, list_of_targets, threshold, + restricted_paths, path_hash_prefixes) + + # Test non-existent target paths. + self.assertRaises(tuf.Error, self.targets_object.delegate, + rolename, public_keys, ['/non-existent'], threshold, + restricted_paths, path_hash_prefixes) def test_delegate_hashed_bins(self): - pass + # Test normal case. + keystore_directory = os.path.join(os.pardir, 'repository_data', 'keystore') + public_keypath = os.path.join(keystore_directory, 'root_key.pub') + public_key = repo_tool.import_rsa_publickey_from_file(public_keypath) + target1_filepath = os.path.join(self.targets_directory, 'file1.txt') + + # Set needed arguments by delegate_hashed_bins(). + public_keys = [public_key] + list_of_targets = [target1_filepath] + + # Test delegate_hashed_bins() and verify that 16 hashed bins have + # been delegated in the parent's roleinfo. + self.targets_object.delegate_hashed_bins(list_of_targets, public_keys, + number_of_bins=16) + + # The expected child rolenames, since 'number_of_bins' = 16 + delegated_rolenames = ['0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'] + + # Prepend the parent's rolename. + expected_delegated_rolenames = [] + for rolename in delegated_rolenames: + rolename = self.targets_object.rolename + '/' + rolename + expected_delegated_rolenames.append(rolename) + + self.assertEqual(sorted(self.targets_object.get_delegated_rolenames()), + sorted(expected_delegated_rolenames)) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, + self.targets_object.delegate_hashed_bins, 3, public_keys, + number_of_bins=1) + self.assertRaises(tuf.FormatError, + self.targets_object.delegate_hashed_bins, + list_of_targets, 3, number_of_bins=1) + self.assertRaises(tuf.FormatError, + self.targets_object.delegate_hashed_bins, + list_of_targets, public_keys, '1') + + + # Test invalid arguments. + # Invalid number of bins, which must be a power of 2. + self.assertRaises(tuf.Error, + self.targets_object.delegate_hashed_bins, + list_of_targets, public_keys, number_of_bins=3) + + # Invalid 'list_of_targets'. + self.assertRaises(tuf.Error, + self.targets_object.delegate_hashed_bins, + ['/non-existent'], public_keys, number_of_bins=3) def test_add_restricted_paths(self): - pass + # Test normal case. + # Perform a delegation so that add_restricted_paths() has a child role + # to restrict. + keystore_directory = os.path.join(os.pardir, 'repository_data', 'keystore') + public_keypath = os.path.join(keystore_directory, 'root_key.pub') + public_key = repo_tool.import_rsa_publickey_from_file(public_keypath) + + # Set needed arguments by delegate(). + public_keys = [public_key] + rolename = 'tuf' + threshold = 1 + + self.targets_object.delegate(rolename, public_keys, [], + threshold, restricted_paths=None, + path_hash_prefixes=None) + + restricted_path = os.path.join(self.targets_directory, 'tuf_files') + os.mkdir(restricted_path) + restricted_paths = [restricted_path] + self.targets_object.add_restricted_paths(restricted_paths, 'tuf') + + # Retrieve 'targets_object' roleinfo, and verify the roleinfo contains + # the expected restricted paths of the delegated role. Only + # Repository.write() verifies that child target paths are allowed by the + # parent. + targets_object_roleinfo = tuf.roledb.get_roleinfo(self.targets_object.rolename) + + delegated_role = targets_object_roleinfo['delegations']['roles'][0] + self.assertEqual(['/tuf_files/'], delegated_role['paths']) + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, self.targets_object.add_restricted_paths, + 3, 'tuf') + self.assertRaises(tuf.FormatError, self.targets_object.add_restricted_paths, + restricted_paths, 3) + + + # Test invalid arguments. + # A non-delegated child role. + self.assertRaises(tuf.Error, self.targets_object.add_restricted_paths, + restricted_paths, 'non_delegated_rolename') + + # Non-existent 'restricted_paths'. + self.assertRaises(tuf.Error, self.targets_object.add_restricted_paths, + ['/non-existent'], 'tuf') def test_revoke(self): - pass + # Test normal case. + # Perform a delegation so that revoke() has a delegation to revoke. + keystore_directory = os.path.join(os.pardir, 'repository_data', 'keystore') + public_keypath = os.path.join(keystore_directory, 'root_key.pub') + public_key = repo_tool.import_rsa_publickey_from_file(public_keypath) + target1_filepath = os.path.join(self.targets_directory, 'file1.txt') + + # Set needed arguments by delegate(). + public_keys = [public_key] + rolename = 'tuf' + list_of_targets = [target1_filepath] + threshold = 1 + + self.targets_object.delegate(rolename, public_keys, list_of_targets, + threshold, restricted_paths=None, + path_hash_prefixes=None) + + # Test revoke() + self.targets_object.revoke('tuf') + self.assertEqual(self.targets_object.get_delegated_rolenames(), []) + + + # Test improperly formatted rolename argument. + self.assertRaises(tuf.FormatError, self.targets_object.revoke, 3) diff --git a/tuf/repository_tool.py b/tuf/repository_tool.py index 292e6d02..737141b9 100755 --- a/tuf/repository_tool.py +++ b/tuf/repository_tool.py @@ -1611,7 +1611,8 @@ def add_restricted_paths(self, list_of_directory_paths, child_rolename): child_rolename: The child delegation that requires an update to its restricted paths, - as listed in the parent role's delegations. + as listed in the parent role's delegations (e.g., 'Django' in + 'targets/unclaimed/Django'). tuf.Error, if a directory path in 'list_of_directory_paths' is not a @@ -2192,8 +2193,9 @@ def delegate_hashed_bins(self, list_of_targets, keys_of_hashed_bins, considered the hash prefix). - tuf.FormatError, if the arguments are improperly formatted, - 'number_of_bins' is not a power of 2, or one of the targets + tuf.FormatError, if the arguments are improperly formatted. + + tuf.Error, if 'number_of_bins' is not a power of 2, or one of the targets in 'list_of_targets' is not located under the repository's targets directory. @@ -2229,7 +2231,7 @@ def delegate_hashed_bins(self, list_of_targets, keys_of_hashed_bins, # (total_hash_prefixes / number_of_bins) hash prefixes. if total_hash_prefixes % number_of_bins != 0: message = 'The "number_of_bins" argument must be a power of 2.' - raise tuf.FormatError(message) + raise tuf.Error(message) logger.info('Creating hashed bin delegations.') logger.info(repr(len(list_of_targets)) + ' total targets.') @@ -2250,7 +2252,7 @@ def delegate_hashed_bins(self, list_of_targets, keys_of_hashed_bins, if not target_path.startswith(self._targets_directory+os.sep): message = 'A path in the list of targets argument is not '+\ 'under the repository\'s targets directory: '+repr(target_path) - raise tuf.FormatError(message) + raise tuf.Error(message) # Determine the hash prefix of 'target_path' by computing the digest of # its path relative to the targets directory. Example: From 19bc1f326ee8941ee7f60aecd975c1afa8119301 Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Thu, 10 Apr 2014 12:59:42 -0400 Subject: [PATCH 09/10] [WIP] Add test case for repository.write(). --- tests/unit/test_repository_tool.py | 106 +++++++++++++++++++++++++++-- 1 file changed, 102 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_repository_tool.py b/tests/unit/test_repository_tool.py index a6b00761..6b2de58b 100755 --- a/tests/unit/test_repository_tool.py +++ b/tests/unit/test_repository_tool.py @@ -91,13 +91,109 @@ def test_init(self): - def test_write(self): - pass + def test_write_and_write_partial(self): + # Test full example of creating a TUF repository, including delegations. + # 1. Load public and private keys. + # 2. Add verification keys. + # 3. Load signing keys. + # 4. Add target files. + # 5. Perform delegation. + # 5. write() + # + # Copy the target files from 'tuf/tests/repository_data' so that write() + # has target files to include in metadata. + temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) + targets_directory = os.path.join(temporary_directory, 'repository', + repo_tool.TARGETS_DIRECTORY_NAME) + original_targets_directory = os.path.join(os.pardir, 'repository_data', + 'repository', 'targets') + shutil.copytree(original_targets_directory, targets_directory) + + # create_new_repository() creates the 'repository/' sub-directory in + # 'temporary_directory' if it does not exist. + repository_directory = os.path.join(temporary_directory, 'repository') + repository = repo_tool.create_new_repository(repository_directory) + + + # (1) Load the public and private keys of the top-level roles, and one + # delegated. + keystore_directory = os.path.join(os.pardir, 'repository_data', 'keystore') + + # Load the public keys. + root_pubkey_path = os.path.join(keystore_directory, 'root_key.pub') + targets_pubkey_path = os.path.join(keystore_directory, 'targets_key.pub') + snapshot_pubkey_path = os.path.join(keystore_directory, 'snapshot_key.pub') + timestamp_pubkey_path = os.path.join(keystore_directory, 'timestamp_key.pub') + role1_pubkey_path = os.path.join(keystore_directory, 'delegation_key.pub') + + root_pubkey = repo_tool.import_rsa_publickey_from_file(root_pubkey_path) + targets_pubkey = repo_tool.import_rsa_publickey_from_file(targets_pubkey_path) + snapshot_pubkey = repo_tool.import_rsa_publickey_from_file(snapshot_pubkey_path) + timestamp_pubkey = repo_tool.import_rsa_publickey_from_file(timestamp_pubkey_path) + role1_pubkey = repo_tool.import_rsa_publickey_from_file(role1_pubkey_path) + + # Load the private keys. + root_privkey_path = os.path.join(keystore_directory, 'root_key') + targets_privkey_path = os.path.join(keystore_directory, 'targets_key') + snapshot_privkey_path = os.path.join(keystore_directory, 'snapshot_key') + timestamp_privkey_path = os.path.join(keystore_directory, 'timestamp_key') + role1_privkey_path = os.path.join(keystore_directory, 'delegation_key') + + root_privkey = \ + repo_tool.import_rsa_privatekey_from_file(root_privkey_path, 'password') + targets_privkey = \ + repo_tool.import_rsa_privatekey_from_file(targets_privkey_path, + 'password') + snapshot_privkey = \ + repo_tool.import_rsa_privatekey_from_file(snapshot_privkey_path, + 'password') + timestamp_privkey = \ + repo_tool.import_rsa_privatekey_from_file(timestamp_privkey_path, + 'password') + role1_privkey = \ + repo_tool.import_rsa_privatekey_from_file(role1_privkey_path, + 'password') + # (2) Add verification keys. + repository.root.add_verification_key(root_pubkey) + repository.targets.add_verification_key(targets_pubkey) + repository.snapshot.add_verification_key(snapshot_pubkey) + repository.timestamp.add_verification_key(timestamp_pubkey) + + + # (3) Load signing keys. + repository.root.load_signing_key(root_privkey) + repository.targets.load_signing_key(targets_privkey) + repository.snapshot.load_signing_key(snapshot_privkey) + repository.timestamp.load_signing_key(timestamp_privkey) + + + # (4) Add target files. + target1 = os.path.join(targets_directory, 'file1.txt') + target2 = os.path.join(targets_directory, 'file2.txt') + target3 = os.path.join(targets_directory, 'file3.txt') + repository.targets.add_target(target1) + repository.targets.add_target(target2) - def test_write_partial(self): - pass + # (5) Perform delegation. + repository.targets.delegate('role1', [role1_pubkey], [target3]) + repository.targets('role1').load_signing_key(role1_privkey) + + # (6) Write repository. + repository.targets.compressions = ['gz'] + repository.write() + + + # TODO: Test expected exceptions for incomplete write(). + # TODO: Test consistent snapshots. + # TODO: Test write_partial() + # TODO: Test multiple writes, loading repository, and compressed metadata. + + + # Test improperly formatted arguments. + self.assertRaises(tuf.FormatError, repository.write, 3, False) + self.assertRaises(tuf.FormatError, repository.write, False, 3) @@ -109,7 +205,9 @@ def test_get_filepaths_in_directory(self): # Test improperly formatted arguments. + # Set 'repo' to improve readability. repo = repo_tool.Repository + self.assertRaises(tuf.FormatError, repo.get_filepaths_in_directory, 3, recursive_walk=False, followlinks=False) self.assertRaises(tuf.FormatError, repo.get_filepaths_in_directory, From 6a4c82cc8172e8f88865b0751d7d2859d97631f6 Mon Sep 17 00:00:00 2001 From: vladdd Date: Fri, 11 Apr 2014 07:35:02 -0400 Subject: [PATCH 10/10] Finish test case for write(). --- tests/unit/test_repository_tool.py | 91 +++++++++++++++++++++++++----- tuf/repository_tool.py | 27 +++++---- 2 files changed, 91 insertions(+), 27 deletions(-) diff --git a/tests/unit/test_repository_tool.py b/tests/unit/test_repository_tool.py index 6b2de58b..62dc92f4 100755 --- a/tests/unit/test_repository_tool.py +++ b/tests/unit/test_repository_tool.py @@ -92,7 +92,8 @@ def test_init(self): def test_write_and_write_partial(self): - # Test full example of creating a TUF repository, including delegations. + # Test creation of a TUF repository. + # # 1. Load public and private keys. # 2. Add verification keys. # 3. Load signing keys. @@ -101,7 +102,7 @@ def test_write_and_write_partial(self): # 5. write() # # Copy the target files from 'tuf/tests/repository_data' so that write() - # has target files to include in metadata. + # has target fileinfo to include in metadata. temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) targets_directory = os.path.join(temporary_directory, 'repository', repo_tool.TARGETS_DIRECTORY_NAME) @@ -109,14 +110,16 @@ def test_write_and_write_partial(self): 'repository', 'targets') shutil.copytree(original_targets_directory, targets_directory) - # create_new_repository() creates the 'repository/' sub-directory in - # 'temporary_directory' if it does not exist. + # In this case, create_new_repository() creates the 'repository/' + # sub-directory in 'temporary_directory' if it does not exist. repository_directory = os.path.join(temporary_directory, 'repository') + metadata_directory = os.path.join(repository_directory, + repo_tool.METADATA_STAGED_DIRECTORY_NAME) repository = repo_tool.create_new_repository(repository_directory) # (1) Load the public and private keys of the top-level roles, and one - # delegated. + # delegated role. keystore_directory = os.path.join(os.pardir, 'repository_data', 'keystore') # Load the public keys. @@ -127,9 +130,12 @@ def test_write_and_write_partial(self): role1_pubkey_path = os.path.join(keystore_directory, 'delegation_key.pub') root_pubkey = repo_tool.import_rsa_publickey_from_file(root_pubkey_path) - targets_pubkey = repo_tool.import_rsa_publickey_from_file(targets_pubkey_path) - snapshot_pubkey = repo_tool.import_rsa_publickey_from_file(snapshot_pubkey_path) - timestamp_pubkey = repo_tool.import_rsa_publickey_from_file(timestamp_pubkey_path) + targets_pubkey = \ + repo_tool.import_rsa_publickey_from_file(targets_pubkey_path) + snapshot_pubkey = \ + repo_tool.import_rsa_publickey_from_file(snapshot_pubkey_path) + timestamp_pubkey = \ + repo_tool.import_rsa_publickey_from_file(timestamp_pubkey_path) role1_pubkey = repo_tool.import_rsa_publickey_from_file(role1_pubkey_path) # Load the private keys. @@ -155,17 +161,27 @@ def test_write_and_write_partial(self): 'password') - # (2) Add verification keys. + # (2) Add top-level verification keys. repository.root.add_verification_key(root_pubkey) repository.targets.add_verification_key(targets_pubkey) repository.snapshot.add_verification_key(snapshot_pubkey) + + # Verify that repository.write() fails for insufficient threshold + # of signatures (default threshold = 1). + self.assertRaises(tuf.UnsignedMetadataError, repository.write) + repository.timestamp.add_verification_key(timestamp_pubkey) - # (3) Load signing keys. + # (3) Load top-level signing keys. repository.root.load_signing_key(root_privkey) repository.targets.load_signing_key(targets_privkey) repository.snapshot.load_signing_key(snapshot_privkey) + + # Verify that repository.write() fails for insufficient threshold + # of signatures (default threshold = 1). + self.assertRaises(tuf.UnsignedMetadataError, repository.write) + repository.timestamp.load_signing_key(timestamp_privkey) @@ -184,11 +200,56 @@ def test_write_and_write_partial(self): repository.targets.compressions = ['gz'] repository.write() + + # Verify that the expected metadata is written. + for role in ['root.json', 'targets.json', 'snapshot.json', 'timestamp.json']: + role_filepath = os.path.join(metadata_directory, role) + role_signable = tuf.util.load_json_file(role_filepath) + + # Raise 'tuf.FormatError' if 'role_signable' is an invalid signable. + tuf.formats.check_signable_object_format(role_signable) - # TODO: Test expected exceptions for incomplete write(). - # TODO: Test consistent snapshots. - # TODO: Test write_partial() - # TODO: Test multiple writes, loading repository, and compressed metadata. + if role == 'targets.json': + compressed_filepath = role_filepath + '.gz' + self.assertTrue(os.path.exists(compressed_filepath)) + + # Verify the 'role1.json' delegation is also written. + role1_filepath = os.path.join(metadata_directory, 'targets', 'role1.json') + role1_signable = tuf.util.load_json_file(role1_filepath) + tuf.formats.check_signable_object_format(role1_signable) + + # Verify that an exception is *not* raised for multiple repository.write(). + repository.write() + + # Verify that a write() fails if a repository is loaded and a change + # is made to a role. + repo_tool.load_repository(repository_directory) + + repository.timestamp.expiration = '2084-01-01 12:00:00' + self.assertRaises(tuf.UnsignedMetadataError, repository.write) + + # Verify that a write_partial() is allowed. + repository.write_partial() + + # Next, perform a non-partial write() with consistent snapshots enabled. + # Since the timestamp was modified, load its private key. + repository.timestamp.load_signing_key(timestamp_privkey) + + # Test creation of a consistent snapshot repository. Writing a consistent + # snapshot modifies the Root metadata, which specifies whether a repository + # supports consistent snapshots. Verify that an exception is raised due to + # the missing signatures of Root and Snapshot. + self.assertRaises(tuf.UnsignedMetadataError, repository.write, + False, True) + + # Load the private keys of Root and Snapshot (new version required since + # Root has changed.) + repository.root.load_signing_key(root_privkey) + repository.snapshot.load_signing_key(snapshot_privkey) + + # Verify that consistent snapshot can be written and loaded. + repository.write(consistent_snapshot=True) + repo_tool.load_repository(repository_directory) # Test improperly formatted arguments. @@ -205,7 +266,7 @@ def test_get_filepaths_in_directory(self): # Test improperly formatted arguments. - # Set 'repo' to improve readability. + # Set 'repo' reference to improve readability. repo = repo_tool.Repository self.assertRaises(tuf.FormatError, repo.get_filepaths_in_directory, diff --git a/tuf/repository_tool.py b/tuf/repository_tool.py index 737141b9..5e1a3866 100755 --- a/tuf/repository_tool.py +++ b/tuf/repository_tool.py @@ -2771,29 +2771,32 @@ def _delete_obsolete_metadata(metadata_directory, snapshot_metadata, # Strip the digest if 'consistent_snapshot' is True. # Example: 'targets/unclaimed/13df98ab0.django.json' --> - # 'targets/unclaimed/django.json' - metadata_name, embeded_digest = \ - _strip_consistent_snapshot_digest(metadata_name, consistent_snapshot) - + # 'targets/unclaimed/django.json'. Consistent and non-consistent + # metadata might co-exist if write() and write(consistent_snapshot=True) + # are mixed, so ensure only 'digest.filename' metadata is stripped. + embeded_digest = None + if metadata_name not in snapshot_metadata['meta']: + metadata_name, embeded_digest = \ + _strip_consistent_snapshot_digest(metadata_name, consistent_snapshot) + # Strip filename extensions. The role database does not include the # metadata extension. + metadata_name_extension = metadata_name for metadata_extension in METADATA_EXTENSIONS: if metadata_name.endswith(metadata_extension): - metadata_name_without_extension = \ - metadata_name[:-len(metadata_extension)] + metadata_name = metadata_name[:-len(metadata_extension)] # Delete the metadata file if it does not exist in 'tuf.roledb'. - # repository_tool.py might have marked 'metadata_name' as removed, but its - # metadata file is not actually deleted yet. Do it now. - if not tuf.roledb.role_exists(metadata_name_without_extension): + # 'repository_tool.py' might have marked 'metadata_name' as removed, but + # its metadata file is not actually deleted yet. Do it now. + if not tuf.roledb.role_exists(metadata_name): logger.info('Removing outdated metadata: ' + repr(metadata_path)) os.remove(metadata_path) # Delete outdated consistent snapshots. snapshot metadata includes # the file extension of roles. - if consistent_snapshot: - #metadata_name_extension = metadata_name + METADATA_EXTENSION - file_hashes = snapshot_metadata['meta'][metadata_name] \ + if consistent_snapshot and embeded_digest is not None: + file_hashes = snapshot_metadata['meta'][metadata_name_extension] \ ['hashes'].values() if embeded_digest not in file_hashes: logger.info('Removing outdated metadata: ' + repr(metadata_path))