diff -Nru dulwich-0.12.0/appveyor.yml dulwich-0.18.5/appveyor.yml --- dulwich-0.12.0/appveyor.yml 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/appveyor.yml 2017-10-07 15:45:17.000000000 +0000 @@ -1,29 +1,96 @@ environment: + matrix: - - PYTHON: "C:\\Python27" - PYWIN32_URL: "https://downloads.sourceforge.net/project/pywin32/pywin32/Build%20219/pywin32-219.win32-py2.7.exe" - - PYTHON: "C:\\Python34" - PYWIN32_URL: "https://downloads.sourceforge.net/project/pywin32/pywin32/Build%20219/pywin32-219.win32-py3.4.exe" + - PYTHON: "C:\\Python27" + PYTHON_VERSION: "2.7.x" + PYTHON_ARCH: "32" - PYTHON: "C:\\Python27-x64" - PYWIN32_URL: "https://downloads.sourceforge.net/project/pywin32/pywin32/Build%20219/pywin32-219.win-amd64-py2.7.exe" + PYTHON_VERSION: "2.7.x" + PYTHON_ARCH: "64" + + - PYTHON: "C:\\Python33" + PYTHON_VERSION: "3.3.x" + PYTHON_ARCH: "32" + + - PYTHON: "C:\\Python33-x64" + PYTHON_VERSION: "3.3.x" + PYTHON_ARCH: "64" + DISTUTILS_USE_SDK: "1" + + - PYTHON: "C:\\Python34" + PYTHON_VERSION: "3.4.x" + PYTHON_ARCH: "32" - PYTHON: "C:\\Python34-x64" - PYWIN32_URL: "https://downloads.sourceforge.net/project/pywin32/pywin32/Build%20219/pywin32-219.win-amd64-py3.4.exe" + PYTHON_VERSION: "3.4.x" + PYTHON_ARCH: "64" + DISTUTILS_USE_SDK: "1" + + - PYTHON: "C:\\Python35" + PYTHON_VERSION: "3.5.x" + PYTHON_ARCH: "32" + + - PYTHON: "C:\\Python35-x64" + PYTHON_VERSION: "3.5.x" + PYTHON_ARCH: "64" + + - PYTHON: "C:\\Python36" + PYTHON_VERSION: "3.6.x" + PYTHON_ARCH: "32" + + - PYTHON: "C:\\Python36-x64" + PYTHON_VERSION: "3.6.x" + PYTHON_ARCH: "64" install: - - ps: (new-object net.webclient).DownloadFile($env:PYWIN32_URL, 'c:\\pywin32.exe') - - "%PYTHON%/Scripts/easy_install.exe c:\\pywin32.exe" - - "%PYTHON%/Scripts/easy_install.exe wheel" - -build: off + # If there is a newer build queued for the same PR, cancel this one. + # The AppVeyor 'rollout builds' option is supposed to serve the same + # purpose but it is problematic because it tends to cancel builds pushed + # directly to master instead of just PR builds (or the converse). + # credits: JuliaLang developers. + - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` + https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` + Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` + throw "There are newer queued builds for this pull request, failing early." } + - ECHO "Filesystem root:" + - ps: "ls \"C:/\"" + + - ECHO "Installed SDKs:" + - ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\"" + + # Install Python (from the official .msi of http://python.org) and pip when + # not already installed. + - ps: if (-not(Test-Path($env:PYTHON))) { & appveyor\install.ps1 } + + # Prepend newly installed Python to the PATH of this build (this cannot be + # done from inside the powershell script as it would require to restart + # the parent CMD process). + - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" + + # Check that we have the expected version and architecture for Python + - "build.cmd %PYTHON%\\python.exe --version" + - "build.cmd %PYTHON%\\python.exe -c \"import struct; print(struct.calcsize('P') * 8)\"" + + # Install setuptools/wheel so that we can e.g. use bdist_wheel + - "pip install setuptools wheel" + + - "build.cmd %PYTHON%\\python.exe setup.py develop" + +build_script: + # Build the compiled extension + - "build.cmd %PYTHON%\\python.exe setup.py build" test_script: - - "%WITH_COMPILER% %PYTHON%/python setup.py test" + - "build.cmd %PYTHON%\\python.exe setup.py test" after_test: - - "%WITH_COMPILER% %PYTHON%/python setup.py bdist_wheel" + - "build.cmd %PYTHON%\\python.exe setup.py bdist_wheel" + # http://stackoverflow.com/questions/43255455/unicode-character-causing-error-with-bdist-wininst-on-python-3-but-not-python-2 + # - "python setup.py bdist_wininst" + - "build.cmd %PYTHON%\\python.exe setup.py bdist_msi" + - ps: "ls dist" artifacts: - path: dist\* diff -Nru dulwich-0.12.0/AUTHORS dulwich-0.18.5/AUTHORS --- dulwich-0.12.0/AUTHORS 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/AUTHORS 2017-10-07 15:45:17.000000000 +0000 @@ -1,11 +1,134 @@ -Jelmer Vernooij -James Westby -John Carr +Jelmer Vernooij Dave Borowitz -Chris Eberle -"milki" +John Carr Gary van der Merwe +milki +Augie Fackler +Tay Ray Chuan +Risto Kankkunen +Jonas Haag +Fabien Boucher +James Westby +Mike Edgar +Koen Martens +Abderrahim Kitouni +William Grant +Marcin Kuzminski +Ryan Faulkner +Julian Berman +Mark Mikofski +Michael K +Ali Sabil +Damien Tournoud +Hannu Valtonen +Mika Mäenpää +Paul Hummer +Lele Gaifax +Lukasz Balcerzak +Tommy Yu +anatoly techtonik +bmcorser +Brendan Cully +Chow Loong Jin +Chris Eberle +Dmitriy +Hervé Cauwelier +Hugo Osvaldo Barrera +Jameson Nash +Marc Brinkmann +Nicolas Dandrimont +Robert Brown +Siddharth Agarwal +Stefan Zimmermann +Takeshi Kanemoto +Yifan Zhang +Aaron O'Mullan +Adam "Cezar" Jenkins +Alberto Ruiz +Alexander Belchenko +Andreas Kloeckner +André Roth +Benjamin Pollack +Benoit HERVIER +Dan Callaghan +David Keijser +David Ostrovsky +David Pursehouse +Dmitrij D. Czarkoff +Doug Hellmann +Dov Feldstern +Félix Mattrat +Hwee Miin Koh +Jason R. Coombs +Jeremy Whitlock +John Arbash Meinel +Laurent Rineau +Martin Packman +Max Shawabkeh +Michael Hudson +Nick Stenning +Nick Ward +Paul Chen +Roland Mas +Ronald Blaschke +Ronny Pfannschmidt +Ross Light +Ryan McKern +Ted Horst +Thomas Liebetraut +Timo Schmid +Víðir Valberg Guðmundsson +dak180 +Akbar Gumbira +Alex Holmes +Andi McClure +Andres Lowrie +Artem Tikhomirov +Brian Visel +Bruce Duncan +Bruno Renié +Chaiwat Suttipongsakul +Chris Bunney +Chris Reid +Daniele Sluijters +David Bennett +David Blewett +David Carr +Dirk +Elan Ruusamäe +Forrest Hopkins +Hal Wine +Hans Kolek +Jakub Wilk +JonChu +Kostis Anagnostopoulos +Kyle Kelly +Lionel Flandrin +Max Bowsher <_@maxb.eu> +Mike Williams +Mikhail Terekhov +Nix +OnMaster +Pascal Quantin +Ricardo Salveti +Rod Cloutier +Sam Vilain +Stefano Rivera +Steven Myint +Søren Løvborg +Travis Cline +Victor Stinner +Volodymyr Holovko +Yuval Langer +codingtony +jon bain +kwatters +max +Segev Finer +fviolette +dzhuang +Antoine Pietri +Taras Postument +Earl Chew -Hervé Cauwelier wrote the original tutorial. - -See the revision history for a full list of contributors. +If you contributed but are missing from this list, please send me an e-mail. diff -Nru dulwich-0.12.0/bin/dul-receive-pack dulwich-0.18.5/bin/dul-receive-pack --- dulwich-0.12.0/bin/dul-receive-pack 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/bin/dul-receive-pack 2017-07-29 00:14:28.000000000 +0000 @@ -2,20 +2,22 @@ # dul-receive-pack - git-receive-pack in python # Copyright (C) 2008 John Carr # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. from dulwich.porcelain import receive_pack import os diff -Nru dulwich-0.12.0/bin/dul-upload-pack dulwich-0.18.5/bin/dul-upload-pack --- dulwich-0.12.0/bin/dul-upload-pack 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/bin/dul-upload-pack 2017-07-29 00:14:28.000000000 +0000 @@ -2,20 +2,22 @@ # dul-upload-pack - git-upload-pack in python # Copyright (C) 2008 John Carr # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. from dulwich.porcelain import upload_pack import os diff -Nru dulwich-0.12.0/bin/dulwich dulwich-0.18.5/bin/dulwich --- dulwich-0.12.0/bin/dulwich 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/bin/dulwich 2017-10-07 15:45:17.000000000 +0000 @@ -4,25 +4,27 @@ # Copyright (C) 2008-2011 Jelmer Vernooij # vim: expandtab # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Simple command-line interface to Dulwich> -This is a very simple command-line wrapper for Dulwich. It is by -no means intended to be a full-blown Git command-line interface but just +This is a very simple command-line wrapper for Dulwich. It is by +no means intended to be a full-blown Git command-line interface but just a way to test Dulwich. """ @@ -46,342 +48,516 @@ from dulwich.repo import Repo -def cmd_archive(args): - opts, args = getopt(args, "", []) - client, path = get_transport_and_path(args.pop(0)) - location = args.pop(0) - committish = args.pop(0) - porcelain.archive(location, committish, outstream=sys.stdout, - errstream=sys.stderr) - - -def cmd_add(args): - opts, args = getopt(args, "", []) - - porcelain.add(".", paths=args) - - -def cmd_rm(args): - opts, args = getopt(args, "", []) - - porcelain.rm(".", paths=args) - - -def cmd_fetch_pack(args): - opts, args = getopt(args, "", ["all"]) - opts = dict(opts) - client, path = get_transport_and_path(args.pop(0)) - r = Repo(".") - if "--all" in opts: - determine_wants = r.object_store.determine_wants_all - else: - determine_wants = lambda x: [y for y in args if not y in r.object_store] - client.fetch(path, r, determine_wants) - - -def cmd_fetch(args): - opts, args = getopt(args, "", []) - opts = dict(opts) - client, path = get_transport_and_path(args.pop(0)) - r = Repo(".") - if "--all" in opts: - determine_wants = r.object_store.determine_wants_all - refs = client.fetch(path, r, progress=sys.stdout.write) - print("Remote refs:") - for item in refs.items(): - print("%s -> %s" % item) - - -def cmd_log(args): - opts, args = getopt(args, "", []) - if len(args) > 0: - path = args.pop(0) - else: - path = "." - porcelain.log(repo=path, outstream=sys.stdout) - - -def cmd_diff(args): - opts, args = getopt(args, "", []) - - if args == []: - print("Usage: dulwich diff COMMITID") - sys.exit(1) - - r = Repo(".") - commit_id = args[0] - commit = r[commit_id] - parent_commit = r[commit.parents[0]] - write_tree_diff(sys.stdout, r.object_store, parent_commit.tree, commit.tree) - - -def cmd_dump_pack(args): - opts, args = getopt(args, "", []) - - if args == []: - print("Usage: dulwich dump-pack FILENAME") - sys.exit(1) - - basename, _ = os.path.splitext(args[0]) - x = Pack(basename) - print("Object names checksum: %s" % x.name()) - print("Checksum: %s" % sha_to_hex(x.get_stored_checksum())) - if not x.check(): - print("CHECKSUM DOES NOT MATCH") - print("Length: %d" % len(x)) - for name in x: - try: - print("\t%s" % x[name]) - except KeyError as k: - print("\t%s: Unable to resolve base %s" % (name, k)) - except ApplyDeltaError as e: - print("\t%s: Unable to apply delta: %r" % (name, e)) - - -def cmd_dump_index(args): - opts, args = getopt(args, "", []) - - if args == []: - print("Usage: dulwich dump-index FILENAME") - sys.exit(1) - - filename = args[0] - idx = Index(filename) - - for o in idx: - print(o, idx[o]) - - -def cmd_init(args): - opts, args = getopt(args, "", ["bare"]) - opts = dict(opts) - - if args == []: - path = os.getcwd() - else: - path = args[0] - - porcelain.init(path, bare=("--bare" in opts)) - - -def cmd_clone(args): - opts, args = getopt(args, "", ["bare"]) - opts = dict(opts) - - if args == []: - print("usage: dulwich clone host:path [PATH]") - sys.exit(1) - - source = args.pop(0) - if len(args) > 0: - target = args.pop(0) - else: - target = None - - porcelain.clone(source, target, bare=("--bare" in opts)) - - -def cmd_commit(args): - opts, args = getopt(args, "", ["message"]) - opts = dict(opts) - porcelain.commit(".", message=opts["--message"]) - - -def cmd_commit_tree(args): - opts, args = getopt(args, "", ["message"]) - if args == []: - print("usage: dulwich commit-tree tree") - sys.exit(1) - opts = dict(opts) - porcelain.commit_tree(".", tree=args[0], message=opts["--message"]) - - -def cmd_update_server_info(args): - porcelain.update_server_info(".") - - -def cmd_symbolic_ref(args): - opts, args = getopt(args, "", ["ref-name", "force"]) - if not args: - print("Usage: dulwich symbolic-ref REF_NAME [--force]") - sys.exit(1) - - ref_name = args.pop(0) - porcelain.symbolic_ref(".", ref_name=ref_name, force='--force' in args) - - -def cmd_show(args): - opts, args = getopt(args, "", []) - porcelain.show(".", args) - - -def cmd_diff_tree(args): - opts, args = getopt(args, "", []) - if len(args) < 2: - print("Usage: dulwich diff-tree OLD-TREE NEW-TREE") - sys.exit(1) - porcelain.diff_tree(".", args[0], args[1]) - - -def cmd_rev_list(args): - opts, args = getopt(args, "", []) - if len(args) < 1: - print('Usage: dulwich rev-list COMMITID...') - sys.exit(1) - porcelain.rev_list('.', args) - - -def cmd_tag(args): - opts, args = getopt(args, '', []) - if len(args) < 2: - print('Usage: dulwich tag NAME') - sys.exit(1) - porcelain.tag('.', args[0]) - - -def cmd_repack(args): - opts, args = getopt(args, "", []) - opts = dict(opts) - porcelain.repack('.') - - -def cmd_reset(args): - opts, args = getopt(args, "", ["hard", "soft", "mixed"]) - opts = dict(opts) - mode = "" - if "--hard" in opts: - mode = "hard" - elif "--soft" in opts: - mode = "soft" - elif "--mixed" in opts: - mode = "mixed" - porcelain.reset('.', mode=mode, *args) - - -def cmd_daemon(args): - from dulwich import log_utils - from dulwich.protocol import TCP_GIT_PORT - parser = optparse.OptionParser() - parser.add_option("-l", "--listen_address", dest="listen_address", - default="localhost", - help="Binding IP address.") - parser.add_option("-p", "--port", dest="port", type=int, - default=TCP_GIT_PORT, - help="Binding TCP port.") - options, args = parser.parse_args(args) - - log_utils.default_logging_config() - if len(args) >= 1: - gitdir = args[0] - else: - gitdir = '.' - from dulwich import porcelain - porcelain.daemon(gitdir, address=options.listen_address, - port=options.port) - - -def cmd_web_daemon(args): - from dulwich import log_utils - parser = optparse.OptionParser() - parser.add_option("-l", "--listen_address", dest="listen_address", - default="", - help="Binding IP address.") - parser.add_option("-p", "--port", dest="port", type=int, - default=8000, - help="Binding TCP port.") - options, args = parser.parse_args(args) - - log_utils.default_logging_config() - if len(args) >= 1: - gitdir = args[0] - else: - gitdir = '.' - from dulwich import porcelain - porcelain.web_daemon(gitdir, address=options.listen_address, +class Command(object): + """A Dulwich subcommand.""" + + def run(self, args): + """Run the command.""" + raise NotImplementedError(self.run) + + +class cmd_archive(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + client, path = get_transport_and_path(args.pop(0)) + location = args.pop(0) + committish = args.pop(0) + porcelain.archive(location, committish, outstream=sys.stdout, + errstream=sys.stderr) + + +class cmd_add(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + + porcelain.add(".", paths=args) + + +class cmd_rm(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + + porcelain.rm(".", paths=args) + + +class cmd_fetch_pack(Command): + + def run(self, args): + opts, args = getopt(args, "", ["all"]) + opts = dict(opts) + client, path = get_transport_and_path(args.pop(0)) + r = Repo(".") + if "--all" in opts: + determine_wants = r.object_store.determine_wants_all + else: + determine_wants = lambda x: [y for y in args if not y in r.object_store] + client.fetch(path, r, determine_wants) + + +class cmd_fetch(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + opts = dict(opts) + client, path = get_transport_and_path(args.pop(0)) + r = Repo(".") + if "--all" in opts: + determine_wants = r.object_store.determine_wants_all + refs = client.fetch(path, r, progress=sys.stdout.write) + print("Remote refs:") + for item in refs.items(): + print("%s -> %s" % item) + + +class cmd_log(Command): + + def run(self, args): + parser = optparse.OptionParser() + parser.add_option("--reverse", dest="reverse", action="store_true", + help="Reverse order in which entries are printed") + parser.add_option("--name-status", dest="name_status", action="store_true", + help="Print name/status for each changed file") + options, args = parser.parse_args(args) + + porcelain.log(".", paths=args, reverse=options.reverse, + name_status=options.name_status, + outstream=sys.stdout) + + +class cmd_diff(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + + if args == []: + print("Usage: dulwich diff COMMITID") + sys.exit(1) + + r = Repo(".") + commit_id = args[0] + commit = r[commit_id] + parent_commit = r[commit.parents[0]] + write_tree_diff(sys.stdout, r.object_store, parent_commit.tree, commit.tree) + + +class cmd_dump_pack(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + + if args == []: + print("Usage: dulwich dump-pack FILENAME") + sys.exit(1) + + basename, _ = os.path.splitext(args[0]) + x = Pack(basename) + print("Object names checksum: %s" % x.name()) + print("Checksum: %s" % sha_to_hex(x.get_stored_checksum())) + if not x.check(): + print("CHECKSUM DOES NOT MATCH") + print("Length: %d" % len(x)) + for name in x: + try: + print("\t%s" % x[name]) + except KeyError as k: + print("\t%s: Unable to resolve base %s" % (name, k)) + except ApplyDeltaError as e: + print("\t%s: Unable to apply delta: %r" % (name, e)) + + +class cmd_dump_index(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + + if args == []: + print("Usage: dulwich dump-index FILENAME") + sys.exit(1) + + filename = args[0] + idx = Index(filename) + + for o in idx: + print(o, idx[o]) + + +class cmd_init(Command): + + def run(self, args): + opts, args = getopt(args, "", ["bare"]) + opts = dict(opts) + + if args == []: + path = os.getcwd() + else: + path = args[0] + + porcelain.init(path, bare=("--bare" in opts)) + + +class cmd_clone(Command): + + def run(self, args): + opts, args = getopt(args, "", ["bare"]) + opts = dict(opts) + + if args == []: + print("usage: dulwich clone host:path [PATH]") + sys.exit(1) + + source = args.pop(0) + if len(args) > 0: + target = args.pop(0) + else: + target = None + + porcelain.clone(source, target, bare=("--bare" in opts)) + + +class cmd_commit(Command): + + def run(self, args): + opts, args = getopt(args, "", ["message"]) + opts = dict(opts) + porcelain.commit(".", message=opts["--message"]) + + +class cmd_commit_tree(Command): + + def run(self, args): + opts, args = getopt(args, "", ["message"]) + if args == []: + print("usage: dulwich commit-tree tree") + sys.exit(1) + opts = dict(opts) + porcelain.commit_tree(".", tree=args[0], message=opts["--message"]) + + +class cmd_update_server_info(Command): + + def run(self, args): + porcelain.update_server_info(".") + + +class cmd_symbolic_ref(Command): + + def run(self, args): + opts, args = getopt(args, "", ["ref-name", "force"]) + if not args: + print("Usage: dulwich symbolic-ref REF_NAME [--force]") + sys.exit(1) + + ref_name = args.pop(0) + porcelain.symbolic_ref(".", ref_name=ref_name, force='--force' in args) + + +class cmd_show(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + porcelain.show(".", args) + + +class cmd_diff_tree(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + if len(args) < 2: + print("Usage: dulwich diff-tree OLD-TREE NEW-TREE") + sys.exit(1) + porcelain.diff_tree(".", args[0], args[1]) + + +class cmd_rev_list(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + if len(args) < 1: + print('Usage: dulwich rev-list COMMITID...') + sys.exit(1) + porcelain.rev_list('.', args) + + +class cmd_tag(Command): + + def run(self, args): + opts, args = getopt(args, '', []) + if len(args) < 2: + print('Usage: dulwich tag NAME') + sys.exit(1) + porcelain.tag('.', args[0]) + + +class cmd_repack(Command): + + def run(self, args): + opts, args = getopt(args, "", []) + opts = dict(opts) + porcelain.repack('.') + + +class cmd_reset(Command): + + def run(self, args): + opts, args = getopt(args, "", ["hard", "soft", "mixed"]) + opts = dict(opts) + mode = "" + if "--hard" in opts: + mode = "hard" + elif "--soft" in opts: + mode = "soft" + elif "--mixed" in opts: + mode = "mixed" + porcelain.reset('.', mode=mode, *args) + + +class cmd_daemon(Command): + + def run(self, args): + from dulwich import log_utils + from dulwich.protocol import TCP_GIT_PORT + parser = optparse.OptionParser() + parser.add_option("-l", "--listen_address", dest="listen_address", + default="localhost", + help="Binding IP address.") + parser.add_option("-p", "--port", dest="port", type=int, + default=TCP_GIT_PORT, + help="Binding TCP port.") + options, args = parser.parse_args(args) + + log_utils.default_logging_config() + if len(args) >= 1: + gitdir = args[0] + else: + gitdir = '.' + from dulwich import porcelain + porcelain.daemon(gitdir, address=options.listen_address, port=options.port) -def cmd_receive_pack(args): - parser = optparse.OptionParser() - options, args = parser.parse_args(args) - if len(args) >= 1: - gitdir = args[0] - else: - gitdir = '.' - porcelain.receive_pack(gitdir) - - -def cmd_upload_pack(args): - parser = optparse.OptionParser() - options, args = parser.parse_args(args) - if len(args) >= 1: - gitdir = args[0] - else: - gitdir = '.' - porcelain.upload_pack(gitdir) - - -def cmd_status(args): - parser = optparse.OptionParser() - options, args = parser.parse_args(args) - if len(args) >= 1: - gitdir = args[0] - else: - gitdir = '.' - status = porcelain.status(gitdir) - if status.staged: - sys.stdout.write("Changes to be committed:\n\n") - for kind, names in status.staged.items(): - for name in names: - sys.stdout.write("\t%s: %s\n" % (kind, name)) - sys.stdout.write("\n") - if status.unstaged: - sys.stdout.write("Changes not staged for commit:\n\n") - for name in status.unstaged: - sys.stdout.write("\t%s\n" % - name.decode(sys.getfilesystemencoding())) - sys.stdout.write("\n") - if status.untracked: - sys.stdout.write("Untracked files:\n\n") - for name in status.untracked: - sys.stdout.write("\t%s\n" % name) - sys.stdout.write("\n") - - -def cmd_ls_remote(args): - opts, args = getopt(args, '', []) - if len(args) < 1: - print('Usage: dulwich ls-remote URL') - sys.exit(1) - refs = porcelain.ls_remote(args[0]) - for ref in sorted(refs): - sys.stdout.write("%s\t%s\n" % (ref, refs[ref])) - - -def cmd_pack_objects(args): - opts, args = getopt(args, '', ['stdout']) - opts = dict(opts) - if len(args) < 1 and not '--stdout' in args: - print('Usage: dulwich pack-objects basename') - sys.exit(1) - object_ids = [l.strip() for l in sys.stdin.readlines()] - basename = args[0] - if '--stdout' in opts: - packf = getattr(sys.stdout, 'buffer', sys.stdout) - idxf = None - close = [] - else: - packf = open(basename + '.pack', 'w') - idxf = open(basename + '.idx', 'w') - close = [packf, idxf] - porcelain.pack_objects('.', object_ids, packf, idxf) - for f in close: - f.close() +class cmd_web_daemon(Command): + + def run(self, args): + from dulwich import log_utils + parser = optparse.OptionParser() + parser.add_option("-l", "--listen_address", dest="listen_address", + default="", + help="Binding IP address.") + parser.add_option("-p", "--port", dest="port", type=int, + default=8000, + help="Binding TCP port.") + options, args = parser.parse_args(args) + + log_utils.default_logging_config() + if len(args) >= 1: + gitdir = args[0] + else: + gitdir = '.' + from dulwich import porcelain + porcelain.web_daemon(gitdir, address=options.listen_address, + port=options.port) + + +class cmd_receive_pack(Command): + + def run(self, args): + parser = optparse.OptionParser() + options, args = parser.parse_args(args) + if len(args) >= 1: + gitdir = args[0] + else: + gitdir = '.' + porcelain.receive_pack(gitdir) + + +class cmd_upload_pack(Command): + + def run(self, args): + parser = optparse.OptionParser() + options, args = parser.parse_args(args) + if len(args) >= 1: + gitdir = args[0] + else: + gitdir = '.' + porcelain.upload_pack(gitdir) + + +class cmd_status(Command): + + def run(self, args): + parser = optparse.OptionParser() + options, args = parser.parse_args(args) + if len(args) >= 1: + gitdir = args[0] + else: + gitdir = '.' + status = porcelain.status(gitdir) + if any(names for (kind, names) in status.staged.items()): + sys.stdout.write("Changes to be committed:\n\n") + for kind, names in status.staged.items(): + for name in names: + sys.stdout.write("\t%s: %s\n" % ( + kind, name.decode(sys.getfilesystemencoding()))) + sys.stdout.write("\n") + if status.unstaged: + sys.stdout.write("Changes not staged for commit:\n\n") + for name in status.unstaged: + sys.stdout.write("\t%s\n" % + name.decode(sys.getfilesystemencoding())) + sys.stdout.write("\n") + if status.untracked: + sys.stdout.write("Untracked files:\n\n") + for name in status.untracked: + sys.stdout.write("\t%s\n" % name) + sys.stdout.write("\n") + + +class cmd_ls_remote(Command): + + def run(self, args): + opts, args = getopt(args, '', []) + if len(args) < 1: + print('Usage: dulwich ls-remote URL') + sys.exit(1) + refs = porcelain.ls_remote(args[0]) + for ref in sorted(refs): + sys.stdout.write("%s\t%s\n" % (ref, refs[ref])) + + +class cmd_ls_tree(Command): + + def run(self, args): + parser = optparse.OptionParser() + parser.add_option("-r", "--recursive", action="store_true", + help="Recusively list tree contents.") + parser.add_option("--name-only", action="store_true", + help="Only display name.") + options, args = parser.parse_args(args) + try: + treeish = args.pop(0) + except IndexError: + treeish = None + porcelain.ls_tree( + '.', treeish, outstream=sys.stdout, recursive=options.recursive, + name_only=options.name_only) + + +class cmd_pack_objects(Command): + + def run(self, args): + opts, args = getopt(args, '', ['stdout']) + opts = dict(opts) + if len(args) < 1 and not '--stdout' in args: + print('Usage: dulwich pack-objects basename') + sys.exit(1) + object_ids = [l.strip() for l in sys.stdin.readlines()] + basename = args[0] + if '--stdout' in opts: + packf = getattr(sys.stdout, 'buffer', sys.stdout) + idxf = None + close = [] + else: + packf = open(basename + '.pack', 'w') + idxf = open(basename + '.idx', 'w') + close = [packf, idxf] + porcelain.pack_objects('.', object_ids, packf, idxf) + for f in close: + f.close() + + +class cmd_pull(Command): + + def run(self, args): + parser = optparse.OptionParser() + options, args = parser.parse_args(args) + try: + from_location = args[0] + except IndexError: + from_location = None + porcelain.pull('.', from_location) + + +class cmd_push(Command): + + def run(self, args): + parser = optparse.OptionParser() + options, args = parser.parse_args(args) + if len(args) < 2: + print("Usage: dulwich push TO-LOCATION REFSPEC..") + sys.exit(1) + to_location = args[0] + refspecs = args[1:] + porcelain.push('.', to_location, refspecs) + + +class cmd_remote_add(Command): + + def run(self, args): + parser = optparse.OptionParser() + options, args = parser.parse_args(args) + porcelain.remote_add('.', args[0], args[1]) + + +class cmd_remote(Command): + + subcommands = { + "add": cmd_remote_add, + } + + def run(self, args): + if not args: + print("Supported subcommands: %s" % ', '.join(self.subcommands.keys())) + return False + cmd = args[0] + try: + cmd_kls = self.subcommands[cmd] + except KeyError: + print('No such subcommand: %s' % args[0]) + return False + return cmd_kls(args[1:]) + + +class cmd_check_ignore(Command): + + def run(self, args): + parser = optparse.OptionParser() + options, args = parser.parse_args(args) + ret = 1 + for path in porcelain.check_ignore('.', args): + print(path) + ret = 0 + return ret + + +class cmd_help(Command): + + def run(self, args): + parser = optparse.OptionParser() + parser.add_option("-a", "--all", dest="all", + action="store_true", + help="List all commands.") + options, args = parser.parse_args(args) + + if options.all: + print('Available commands:') + for cmd in sorted(commands): + print(' %s' % cmd) + else: + print("""\ +The dulwich command line tool is currently a very basic frontend for the +Dulwich python module. For full functionality, please see the API reference. + +For a list of supported commands, see 'dulwich help -a'. +""") commands = { "add": cmd_add, "archive": cmd_archive, + "check-ignore": cmd_check_ignore, "clone": cmd_clone, "commit": cmd_commit, "commit-tree": cmd_commit_tree, @@ -392,11 +568,16 @@ "dump-index": cmd_dump_index, "fetch-pack": cmd_fetch_pack, "fetch": cmd_fetch, + "help": cmd_help, "init": cmd_init, "log": cmd_log, "ls-remote": cmd_ls_remote, + "ls-tree": cmd_ls_tree, "pack-objects": cmd_pack_objects, + "pull": cmd_pull, + "push": cmd_push, "receive-pack": cmd_receive_pack, + "remote": cmd_remote, "repack": cmd_repack, "reset": cmd_reset, "rev-list": cmd_rev_list, @@ -415,7 +596,10 @@ sys.exit(1) cmd = sys.argv[1] -if not cmd in commands: +try: + cmd_kls = commands[cmd] +except KeyError: print("No such subcommand: %s" % cmd) sys.exit(1) -commands[cmd](sys.argv[2:]) +# TODO(jelmer): Return non-0 on errors +cmd_kls().run(sys.argv[2:]) diff -Nru dulwich-0.12.0/CONTRIBUTING dulwich-0.18.5/CONTRIBUTING --- dulwich-0.12.0/CONTRIBUTING 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/CONTRIBUTING 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -All functionality should be available in pure Python. Optional C -implementations may be written for performance reasons, but should never -replace the Python implementation. The C implementations should follow the -kernel/git coding style. - -Where possible include updates to NEWS along with your improvements. - -New functionality and bug fixes should be accompanied by matching unit tests. - -Coding style ------------- -Where possible, please follow PEP8 with regard to coding style. - -Furthermore, triple-quotes should always be """, single quotes are ' unless -using " would result in less escaping within the string. - -Public methods, functions and classes should all have doc strings. Please use -epydoc style docstrings to document parameters and return values. -You can generate the documentation by running "make doc". - -Running the tests ------------------ -To run the testsuite, you should be able to simply run "make check". This -will run the tests using unittest on Python 2.7 and higher, and using -unittest2 (which you will need to have installed) on older versions of Python. - - $ make check - -Tox configuration is also present as well as a Travis configuration file. - -String Types ------------- -Like Linux, Git treats filenames as arbitrary bytestrings. There is no prescribed -encoding for these strings, and although it is fairly common to use UTF-8, any -raw byte strings are supported. - -For this reason, Dulwich internally treats git-based filenames as bytestrings. It is up -to the Dulwich API user to encode and decode them if necessary. - -* git-repository related filenames: bytes -* object sha1 digests (20 bytes long): bytes -* object sha1 hexdigests (40 bytes long): str (bytestrings on python2, strings on python3) - -Merge requests --------------- -Please either send pull requests to the author (jelmer@jelmer.uk) or create new pull -requests on GitHub. - -Licensing ---------- -Dulwich is currently licensed under the GNU General Public License, version 2 -or later. We would like to change the license to Apachev2 or later eventually; -new contributions should be made under dual GPLv2+/Apachev2+. diff -Nru dulwich-0.12.0/CONTRIBUTING.md dulwich-0.18.5/CONTRIBUTING.md --- dulwich-0.12.0/CONTRIBUTING.md 1970-01-01 00:00:00.000000000 +0000 +++ dulwich-0.18.5/CONTRIBUTING.md 2017-10-07 15:45:17.000000000 +0000 @@ -0,0 +1,51 @@ +All functionality should be available in pure Python. Optional C +implementations may be written for performance reasons, but should never +replace the Python implementation. The C implementations should follow the +kernel/git coding style. + +Where possible include updates to NEWS along with your improvements. + +New functionality and bug fixes should be accompanied by matching unit tests. + +Coding style +------------ +Where possible, please follow PEP8 with regard to coding style. + +Furthermore, triple-quotes should always be """, single quotes are ' unless +using " would result in less escaping within the string. + +Public methods, functions and classes should all have doc strings. Please use +epydoc style docstrings to document parameters and return values. +You can generate the documentation by running "make doc". + +Running the tests +----------------- +To run the testsuite, you should be able to simply run "make check". This +will run the tests using unittest. + + $ make check + +Tox configuration is also present as well as a Travis configuration file. + +String Types +------------ +Like Linux, Git treats filenames as arbitrary bytestrings. There is no prescribed +encoding for these strings, and although it is fairly common to use UTF-8, any +raw byte strings are supported. + +For this reason, Dulwich internally treats git-based filenames as bytestrings. It is up +to the Dulwich API user to encode and decode them if necessary. + +* git-repository related filenames: bytes +* object sha1 digests (20 bytes long): bytes +* object sha1 hexdigests (40 bytes long): str (bytestrings on python2, strings on python3) + +Merge requests +-------------- +Please either send pull requests to the maintainer (jelmer@jelmer.uk) or create new pull +requests on GitHub. + +Licensing +--------- +All contributions should be made under the same license that Dulwich itself comes under: +both Apache License, version 2.0 or later and GNU General Public License, version 2.0 or later. diff -Nru dulwich-0.12.0/COPYING dulwich-0.18.5/COPYING --- dulwich-0.12.0/COPYING 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/COPYING 2017-07-29 00:14:28.000000000 +0000 @@ -1,3 +1,209 @@ +Dulwich may be used under the conditions of either of two licenses, +the Apache License (version 2.0 or later) or the GNU General Public License, +version 2.0 or later. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + GNU GENERAL PUBLIC LICENSE Version 2, June 1991 @@ -337,3 +543,6 @@ consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. + + + diff -Nru dulwich-0.12.0/debian/changelog dulwich-0.18.5/debian/changelog --- dulwich-0.12.0/debian/changelog 2016-04-08 12:53:07.000000000 +0000 +++ dulwich-0.18.5/debian/changelog 2017-11-17 08:26:21.000000000 +0000 @@ -1,14 +1,154 @@ -dulwich (0.12.0-1build2) xenial; urgency=medium +dulwich (0.18.5-1~cloud0) xenial-queens; urgency=medium - * No-change rebuild for pypy5. + * New update for the Ubuntu Cloud Archive. - -- Matthias Klose Fri, 08 Apr 2016 12:53:07 +0000 + -- Openstack Ubuntu Testing Bot Fri, 17 Nov 2017 08:26:21 +0000 -dulwich (0.12.0-1build1) xenial; urgency=medium +dulwich (0.18.5-1) unstable; urgency=high - * No-change rebuild to drop python3.4 support. + * New upstream release. + + Prevents setting SSH arguments from SSH URLs when using SSH through a + subprocess. Note that Dulwich doesn't support cloning submodules. + (CVE 2017-1000117) - -- Matthias Klose Tue, 19 Jan 2016 11:45:29 +0000 + -- Jelmer Vernooij Sun, 29 Oct 2017 16:44:59 +0000 + +dulwich (0.18.4-1) unstable; urgency=medium + + * New upstream release. + * Bump standards version to 4.1.1 (no changes). + + -- Jelmer Vernooij Sun, 01 Oct 2017 01:57:31 +0100 + +dulwich (0.18.3-2) unstable; urgency=medium + + * Fix dh_install invocation. + + -- Jelmer Vernooij Thu, 28 Sep 2017 00:47:21 +0100 + +dulwich (0.18.3-1) unstable; urgency=low + + * New upstream release. + + -- Jelmer Vernooij Sun, 03 Sep 2017 02:26:06 +0000 + +dulwich (0.18.2-1) unstable; urgency=medium + + * New upstream release. + * Only build-depend on git on platforms where it is available. + + -- Jelmer Vernooij Tue, 01 Aug 2017 23:19:14 +0000 + +dulwich (0.18.1-2) unstable; urgency=medium + + * Make build-dependency on git optional, since it's not available on + all platforms and only used for the testsuite. + + -- Jelmer Vernooij Mon, 31 Jul 2017 22:21:12 +0000 + +dulwich (0.18.1-1) unstable; urgency=medium + + * New upstream release. + + -- Jelmer Vernooij Mon, 31 Jul 2017 09:36:13 +0000 + +dulwich (0.18.0-1) unstable; urgency=medium + + * New upstream release. + * Refresh upstream signing key. + + -- Jelmer Vernooij Mon, 31 Jul 2017 01:15:59 +0000 + +dulwich (0.17.3-2) unstable; urgency=medium + + * Stop shipping pypy C extensions in python-dulwich. Closes: #868542 + + -- Jelmer Vernooij Mon, 24 Jul 2017 23:51:07 +0000 + +dulwich (0.17.3-1) unstable; urgency=medium + + * New upstream release. + * Bump standards version to 4.0.0 (no changes). + + -- Jelmer Vernooij Sun, 18 Jun 2017 19:39:41 +0000 + +dulwich (0.17.1-1) experimental; urgency=medium + + * Remove -dbg packages and migrate to -dbgsym packages. + * New upstream version. + + -- Jelmer Vernooij Wed, 01 Mar 2017 20:37:39 +0000 + +dulwich (0.16.3-1) unstable; urgency=medium + + * New upstream release. + * Drop upstream applied patch 'older-git'. + * Update homepage to www.dulwich.io. + * Drop (now unnecessary) workaround for #799485. + * Add autopkgtest for python3 testsuite. + * Disable pypy-dulwich build on architectures where pypy is not + available (hurd-any, m68k, sh4, x32). + + -- Jelmer Vernooij Sun, 08 Jan 2017 02:12:51 +0000 + +dulwich (0.16.1-2) unstable; urgency=low + + * Add patch older-git: skip worktree list test against older versions + of git. + + -- Jelmer Vernooij Mon, 26 Dec 2016 11:24:46 +0000 + +dulwich (0.16.1-1) unstable; urgency=medium + + * New upstream release. + + Fixes Python3 compatibility of `dulwich.contrib.release_robot`. + + -- Jelmer Vernooij Sun, 25 Dec 2016 13:42:56 +0000 + +dulwich (0.16.0-1) unstable; urgency=low + + * New upstream release. + + -- Jelmer Vernooij Sat, 24 Dec 2016 23:51:50 +0000 + +dulwich (0.15.0-1) unstable; urgency=medium + + * New upstream release. + * Update debian/copyright to reflect that dulwich is now licensed under + dual GPLv2+/Apache-2. + * Extend lintian overrides for testdata. + + -- Jelmer Vernooij Sun, 09 Oct 2016 12:54:38 +0000 + +dulwich (0.14.1-1) unstable; urgency=medium + + * New upstream release. + + -- Jelmer Vernooij Tue, 05 Jul 2016 23:04:21 +0000 + +dulwich (0.14.0-1) unstable; urgency=medium + + * New upstream release. + * Drop patch 01_refs_unicode: applied upstream. + * Bump standards version to 3.9.8 (no changes). + + -- Jelmer Vernooij Sun, 03 Jul 2016 19:17:25 +0000 + +dulwich (0.13.0-1) unstable; urgency=medium + + [ Jelmer Vernooij ] + * New upstream release. + + Add Build-Depends: python3-fastimport. + + Add Recommends: python3-fastimport to python3-dulwich. + + [ Ondřej Nový ] + * Fixed VCS URL (https) + + [ Jelmer Vernooij ] + * Bump standards version to 3.9.7. + * Add some lintian overrides for git files that are used for testing. + + -- Jelmer Vernooij Sun, 24 Apr 2016 15:34:54 +0000 dulwich (0.12.0-1) unstable; urgency=medium diff -Nru dulwich-0.12.0/debian/control dulwich-0.18.5/debian/control --- dulwich-0.12.0/debian/control 2015-12-13 15:43:35.000000000 +0000 +++ dulwich-0.18.5/debian/control 2017-10-29 16:44:11.000000000 +0000 @@ -2,21 +2,20 @@ Section: python Priority: optional Maintainer: Debian Python Modules Team -Uploaders: Jelmer Vernooij -Homepage: http://samba.org/~jelmer/dulwich -Build-Depends: python-all-dev (>= 2.6.6-3), python-all-dbg (>= 2.6.6-3), python3-all-dev, python3-all-dbg, debhelper (>= 9), python-unittest2, git (>= 1:1.7.0.4-2) | git-core, python-fastimport (>= 0.9.0~bzr293), pypy-dev, dh-python -Standards-Version: 3.9.6 +Uploaders: Jelmer Vernooij +Homepage: https://www.dulwich.io/ +Build-Depends: python-all-dev (>= 2.6.6-3), python-all-dbg (>= 2.6.6-3), python3-all-dev, python3-all-dbg, debhelper (>= 9.20160114), python-unittest2, git (>= 1:1.7.0.4-2) [!alpha !ppc64 !sparc64 !x32], python-fastimport (>= 0.9.0~bzr293), python3-fastimport, pypy-dev [!hurd-any !m68k !sh4 !x32], dh-python +Standards-Version: 4.1.1 X-Python-Version: >= 2.7 X-Python3-Version: >= 3.2 -Vcs-Git: git://anonscm.debian.org/python-modules/packages/dulwich.git -Vcs-Browser: http://anonscm.debian.org/gitweb/?p=python-modules/packages/dulwich.git +Vcs-Git: https://anonscm.debian.org/git/python-modules/packages/dulwich.git +Vcs-Browser: https://anonscm.debian.org/cgit/python-modules/packages/dulwich.git XS-Testsuite: autopkgtest Package: python-dulwich Architecture: any Depends: ${python:Depends}, ${misc:Depends}, ${shlibs:Depends} Breaks: bzr-git (<< 0.6.2) -Suggests: python-dulwich-dbg Recommends: python-fastimport Description: Python Git library Dulwich is a Python implementation of the file formats and protocols @@ -30,27 +29,11 @@ . This package contains the module built for Python version 2.x. -Package: python-dulwich-dbg -Architecture: any -Depends: ${misc:Depends}, ${python:Depends}, ${shlibs:Depends}, python-dbg, python-dulwich (= ${binary:Version}) -Priority: extra -Section: debug -Description: Python Git library - Debug Extension - Dulwich is a Python implementation of the file formats and protocols - used by the Git version control system. It can currently read from and write - to existing Git repositories and implements the protocol for pushing and - receiving packs from remote servers. - . - All functionality is available in pure Python, but for improved performance - replacements of some modules written in C are also available. This package - includes the high performance versions. - . - This package contains the extensions built for the Python debug interpreter. - Package: pypy-dulwich -Architecture: any +Architecture: amd64 arm64 armel armhf i386 mips mipsel mips64el ppc64el s390x alpha hppa kfreebsd-any powerpc powerpcspe ppc64 sparc64 Provides: ${pypy:Provides} Depends: ${pypy:Depends}, ${misc:Depends}, ${shlibs:Depends} +Recommends: pypy-fastimport Description: Python Git library - pypy module Dulwich is a Python implementation of the file formats and protocols used by the Git version control system. It can currently read from and write @@ -67,7 +50,7 @@ Architecture: any Depends: ${python3:Depends}, ${misc:Depends}, ${shlibs:Depends} Breaks: bzr-git (<< 0.6.2) -Suggests: python3-dulwich-dbg +Recommends: python3-fastimport Description: Python Git library - Python3 module Dulwich is a Python implementation of the file formats and protocols used by the Git version control system. It can currently read from and write @@ -79,20 +62,3 @@ includes the high performance versions. . This package contains the module built for Python version 3.x. - -Package: python3-dulwich-dbg -Architecture: any -Depends: ${misc:Depends}, ${shlibs:Depends}, python3-dbg, python3-dulwich (= ${binary:Version}) -Priority: extra -Section: debug -Description: Python Git library - Python 3 Debug Extension - Dulwich is a Python implementation of the file formats and protocols - used by the Git version control system. It can currently read from and write - to existing Git repositories and implements the protocol for pushing and - receiving packs from remote servers. - . - All functionality is available in pure Python, but for improved performance - replacements of some modules written in C are also available. This package - includes the high performance versions. - . - This package contains the extensions built for the Python 3 debug interpreter. diff -Nru dulwich-0.12.0/debian/copyright dulwich-0.18.5/debian/copyright --- dulwich-0.12.0/debian/copyright 2015-12-13 15:43:35.000000000 +0000 +++ dulwich-0.18.5/debian/copyright 2017-10-29 16:44:11.000000000 +0000 @@ -1,22 +1,26 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: dulwich -Upstream-Contact: Jelmer Vernooij -Source: http://samba.org/~jelmer/dulwich -Debianized-By: Jelmer Vernooij +Upstream-Contact: Jelmer Vernooij +Source: https://www.dulwich.io/ +Debianized-By: Jelmer Vernooij Debianized-Date: Tue, 13 Jan 2009 16:56:47 +0100 License: GPL-2+ On Debian systems the full text of the GNU General Public License version 2 can be found in the `/usr/share/common-licenses/GPL-2' file. +License: Apache-2 + On Debian systems the full text of the Apache License version 2 + can be found in the `/usr/share/common-licenses/Apache-2.0' file. + Files: * Copyright: 2007 James Westby - 2007-2015 Jelmer Vernooij + 2007-2015 Jelmer Vernooij 2008 John Carr 2010 Google, Inc. -License: GPL-2+ +License: GPL-2+ or Apache-2 Files: debian/* -Copyright: 2009-2015 Jelmer Vernooij -License: GPL-2+ +Copyright: 2009-2015 Jelmer Vernooij +License: GPL-2+ or Apache-2 diff -Nru dulwich-0.12.0/debian/gbp.conf dulwich-0.18.5/debian/gbp.conf --- dulwich-0.12.0/debian/gbp.conf 2015-12-13 15:43:35.000000000 +0000 +++ dulwich-0.18.5/debian/gbp.conf 2017-10-29 16:44:11.000000000 +0000 @@ -2,4 +2,5 @@ pristine-tar = False ignore-branch = True upstream-branch = upstream -debian-branch = experimental +debian-branch = unstable +upstream-vcs-tag = dulwich-%(version)s diff -Nru dulwich-0.12.0/debian/patches/01_refs_unicode dulwich-0.18.5/debian/patches/01_refs_unicode --- dulwich-0.12.0/debian/patches/01_refs_unicode 2015-12-13 15:43:35.000000000 +0000 +++ dulwich-0.18.5/debian/patches/01_refs_unicode 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -diff --git a/dulwich/tests/test_refs.py b/dulwich/tests/test_refs.py -index 3a645c6..61547a1 100644 ---- a/dulwich/tests/test_refs.py -+++ b/dulwich/tests/test_refs.py -@@ -441,7 +441,7 @@ class DiskRefsContainerTests(RefsContainerTests, TestCase): - def test_non_ascii(self): - try: - encoded_ref = u'refs/tags/schön'.encode(sys.getfilesystemencoding()) -- except UnicodeDecodeError: -+ except UnicodeEncodeError: - raise SkipTest("filesystem encoding doesn't support special character") - p = os.path.join(self._repo.path, 'refs', 'tags', 'schön') - with open(p, 'w') as f: diff -Nru dulwich-0.12.0/debian/patches/series dulwich-0.18.5/debian/patches/series --- dulwich-0.12.0/debian/patches/series 2015-12-13 15:43:35.000000000 +0000 +++ dulwich-0.18.5/debian/patches/series 2017-10-29 16:44:11.000000000 +0000 @@ -1 +0,0 @@ -01_refs_unicode diff -Nru dulwich-0.12.0/debian/pypy-dulwich.lintian-overrides dulwich-0.18.5/debian/pypy-dulwich.lintian-overrides --- dulwich-0.12.0/debian/pypy-dulwich.lintian-overrides 1970-01-01 00:00:00.000000000 +0000 +++ dulwich-0.18.5/debian/pypy-dulwich.lintian-overrides 2017-10-29 16:44:11.000000000 +0000 @@ -0,0 +1,2 @@ +pypy-dulwich binary: package-contains-vcs-control-file +pypy-dulwich binary: executable-not-elf-or-script usr/lib/pypy/dist-packages/dulwich/tests/data/commits/0d/89f20333fbb1d2f3a94da77f4981373d8f4310 diff -Nru dulwich-0.12.0/debian/python3-dulwich.lintian-overrides dulwich-0.18.5/debian/python3-dulwich.lintian-overrides --- dulwich-0.12.0/debian/python3-dulwich.lintian-overrides 1970-01-01 00:00:00.000000000 +0000 +++ dulwich-0.18.5/debian/python3-dulwich.lintian-overrides 2017-10-29 16:44:11.000000000 +0000 @@ -0,0 +1,3 @@ +# There are some git files that are used for testing: +python3-dulwich binary: package-contains-vcs-control-file +python3-dulwich binary: executable-not-elf-or-script usr/lib/python3/dist-packages/dulwich/tests/data/commits/0d/89f20333fbb1d2f3a94da77f4981373d8f4310 diff -Nru dulwich-0.12.0/debian/python-dulwich.lintian-overrides dulwich-0.18.5/debian/python-dulwich.lintian-overrides --- dulwich-0.12.0/debian/python-dulwich.lintian-overrides 1970-01-01 00:00:00.000000000 +0000 +++ dulwich-0.18.5/debian/python-dulwich.lintian-overrides 2017-10-29 16:44:11.000000000 +0000 @@ -0,0 +1,2 @@ +python-dulwich binary: package-contains-vcs-control-file +python-dulwich binary: executable-not-elf-or-script usr/lib/python2.7/dist-packages/dulwich/tests/data/commits/0d/89f20333fbb1d2f3a94da77f4981373d8f4310 diff -Nru dulwich-0.12.0/debian/rules dulwich-0.18.5/debian/rules --- dulwich-0.12.0/debian/rules 2015-12-13 15:43:35.000000000 +0000 +++ dulwich-0.18.5/debian/rules 2017-10-29 16:44:11.000000000 +0000 @@ -9,13 +9,13 @@ pythonpath = $$(ls -d $(CURDIR)/build/lib.*-$(1)) pythonpath_dbg = $$(ls -d $(CURDIR)/build/lib_d.*-$(1) 2>/dev/null || ls -d $(CURDIR)/build/lib.*$(1)-pydebug) +pyflavours = python2,python3$(shell which pypy >/dev/null && echo -n ,pypy) + %: - dh $* --with python2,python3,pypy --buildsystem=python_distutils + dh $* --with $(pyflavours) --buildsystem=python_distutils override_dh_auto_build: dh_auto_build - # Work around bug #799485 - CFLAGS=-I/usr/lib/pypy pypy setup.py build -b build-pypy override_dh_auto_clean: dh_auto_clean @@ -26,36 +26,41 @@ ifeq (,$(findstring nocheck,$(DEB_BUILD_OPTIONS))) $(MAKE) check PYTHON=python $(MAKE) check PYTHON=python3 - PYTHONPATH=$(shell echo $(CURDIR)/build-pypy/lib*) pypy -m unittest dulwich.tests.test_suite +ifneq (,$(findstring pypy,$(pyflavours))) + $(MAKE) check PYTHON=pypy +endif endif override_dh_auto_install: dh_auto_install set -ex; for python in $(shell py3versions -r); do \ - $$python setup.py install --root=$(CURDIR)/debian/tmp --install-layout=deb; \ + $$python setup.py install --root=$(CURDIR)/debian/tmp --install-layout=deb; \ done; +ifneq (,$(findstring pypy,$(pyflavours))) pypy setup.py build -b build-pypy install --root=$(CURDIR)/debian/tmp --install-layout deb rm -rf debian/tmp/usr/lib/pypy/bin +endif # Install everything excluding the *_d.so debug extensions to python-dulwich and python3-dulwich - dh_install -X"*_d.so" "debian/tmp/usr/lib/python2*/*-packages" -p python-dulwich - dh_install -X"*_d.so" "debian/tmp/usr/lib/python3*/*-packages" -p python3-dulwich - # Install the debug extensions to python-dulwich-dbg - dh_install "debian/tmp/usr/lib/python*/*-packages/dulwich/*_d.so" -p python-dulwich-dbg + dh_install -p python-dulwich -X"*_d.so" -Xpypy "debian/tmp/usr/lib/python2*/*-packages" + dh_install -p python3-dulwich -X"*_d.so" -Xpypy "debian/tmp/usr/lib/python3*/*-packages" +ifneq (,$(findstring pypy,$(pyflavours))) # Install the pypy files to pypy-dulwich - dh_install "debian/tmp/usr/lib/pypy/" -p pypy-dulwich + dh_install -p pypy-dulwich "debian/tmp/usr/lib/pypy/" +endif override_dh_installdocs: - dh_installdocs -ppython-dulwich-dbg --link-doc=python-dulwich - dh_installdocs -ppython3-dulwich-dbg --link-doc=python3-dulwich dh_installdocs -ppython-dulwich docs/tutorial -X.gitignore -XMakefile dh_installdocs -ppython3-dulwich docs/tutorial -X.gitignore -XMakefile +ifneq (,$(findstring pypy,$(pyflavours))) dh_installdocs -ppypy-dulwich docs/tutorial -X.gitignore -XMakefile +endif override_dh_strip: - dh_strip -p python-dulwich --dbg-package=python-dulwich-dbg - dh_strip -p python3-dulwich --dbg-package=python3-dulwich-dbg - # For the moment, there is no dbg package for pypy-dulwich. + dh_strip -p python-dulwich --dbgsym-migration='python-dulwich-dbg (<< 0.16.4-1)' + dh_strip -p python3-dulwich --dbgsym-migration='python3-dulwich-dbg (<< 0.16.4-1)' +ifneq (,$(findstring pypy,$(pyflavours))) dh_strip -p pypy-dulwich +endif override_dh_installchangelogs: dh_installchangelogs NEWS diff -Nru dulwich-0.12.0/debian/source/options dulwich-0.18.5/debian/source/options --- dulwich-0.12.0/debian/source/options 1970-01-01 00:00:00.000000000 +0000 +++ dulwich-0.18.5/debian/source/options 2017-10-29 16:44:11.000000000 +0000 @@ -0,0 +1,2 @@ +extend-diff-ignore = "^\.travis.yml$" +extend-diff-ignore = "^\dulwich.egg-info/SOURCES.txt$" diff -Nru dulwich-0.12.0/debian/tests/control dulwich-0.18.5/debian/tests/control --- dulwich-0.12.0/debian/tests/control 2015-12-13 15:43:35.000000000 +0000 +++ dulwich-0.18.5/debian/tests/control 2017-10-29 16:44:11.000000000 +0000 @@ -1,3 +1,7 @@ -Tests: testsuite +Tests: testsuite2 Depends: python-dulwich, git Restrictions: allow-stderr + +Tests: testsuite3 +Depends: python3-dulwich, git +Restrictions: allow-stderr diff -Nru dulwich-0.12.0/debian/tests/testsuite dulwich-0.18.5/debian/tests/testsuite --- dulwich-0.12.0/debian/tests/testsuite 2015-12-13 15:43:35.000000000 +0000 +++ dulwich-0.18.5/debian/tests/testsuite 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -#!/bin/sh -set -e -python -m unittest dulwich.tests.test_suite diff -Nru dulwich-0.12.0/debian/tests/testsuite2 dulwich-0.18.5/debian/tests/testsuite2 --- dulwich-0.12.0/debian/tests/testsuite2 1970-01-01 00:00:00.000000000 +0000 +++ dulwich-0.18.5/debian/tests/testsuite2 2017-10-29 16:44:11.000000000 +0000 @@ -0,0 +1,3 @@ +#!/bin/sh +set -e +python -m unittest dulwich.tests.test_suite diff -Nru dulwich-0.12.0/debian/tests/testsuite3 dulwich-0.18.5/debian/tests/testsuite3 --- dulwich-0.12.0/debian/tests/testsuite3 1970-01-01 00:00:00.000000000 +0000 +++ dulwich-0.18.5/debian/tests/testsuite3 2017-10-29 16:44:11.000000000 +0000 @@ -0,0 +1,3 @@ +#!/bin/sh +set -e +python3 -m unittest dulwich.tests.test_suite diff -Nru dulwich-0.12.0/debian/upstream/signing-key.asc dulwich-0.18.5/debian/upstream/signing-key.asc --- dulwich-0.12.0/debian/upstream/signing-key.asc 2015-12-13 15:43:35.000000000 +0000 +++ dulwich-0.18.5/debian/upstream/signing-key.asc 2017-10-29 16:44:11.000000000 +0000 @@ -1,5 +1,4 @@ -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v1 mQINBEpQwsABEACqYMFfTgdeBfCGdgavnGu3jzWAU0+l/ILYZLOjYUumFOmXkSUH AD9YxGh/SXi+UO9K9wnbSWaH63sZSYoHP7pnP9GoegQODQqZQI0lhFZieJjkVmgQ @@ -12,8465 +11,603 @@ Kijxo3jnS1tBRVuUg/53iibKl2sa7dxYJUX8Gch80n6Jct3On5vVhIThpUIpzFuC 6X7rhN/X8ooCHTip04PAOh6j1f2B31MVVmJTafzCleyeP3zzAYii3W8ktXddAOHa txG6VqaN+f4ASsAbNZz1Y09AglXmTS0lRBG/pRzAA/cRTcbm0i52TbCWOQARAQAB -tCJKZWxtZXIgVmVybm9vaWogPGplbG1lckBzYW1iYS5vcmc+iEYEEBECAAYFAkpR -JBEACgkQPa9Uoh7vUnaULwCgkpL7NdoZSSB2MoDpQ6SqdUzJzy4AoJIDFuKtzMI4 -aXkvVwrjlGsu9TGMiEYEEBECAAYFAkpytgYACgkQ1OXtrMAUPS0GLACgs5rbGHth -FNyBLoe85VSSyNAx9iUAn2FSOWQEFRS+Z1Xscs3fosk2rfgdiEYEEBECAAYFAkp1 -c7MACgkQadKmHeJj/NRrFQCfV11dO4+dztvAJWxB79EENFpjGSIAoKX9Aawbe3TX -JRREa92+Q8FlAYx/iEYEEBECAAYFAkp4XTUACgkQ1cqbBPLEI7yc8QCfdgfpX5rJ -zIM1pu/Zj9ADSo9Os04An38tpvdUUPY314WR1XpoC4Ft23nBiEYEEBECAAYFAkp9 -MI8ACgkQhImxTYgHUpvKQwCfcukQXNcaj2yvJLkPGH6xytJrj4YAnj1kcjSEm/pn -tguOnCrGauIWTnGgiEYEEBECAAYFAkqCm4QACgkQ4yGTtsQpvi9TuwCeN9ewm76O -cI2gl+wr/8hKodC0brIAn1tiiTqcZUiaHS41ys9bPHCnbC7KiEYEEBECAAYFAkqC -6gcACgkQQSHHQzFw6+kdIwCdH2hTl32yxKviv8GijXfm83U3ghQAmwS92hOWfzfI -u8EgaAYVG/03VDlhiEYEEBECAAYFAkqkMksACgkQwJ4diZWTDt4rWwCbBLB+5BRp -Mnt58RSMVeJRASewFwcAn2oXWDO32wOKHX9I/jEnRb4Qp1YYiEYEEBECAAYFAkqk -MngACgkQBARrhUouFiuvigCgqs8eoVt2fmI7DwCwRmGI+SK+PmoAnjDWXQhaWNZ5 -uX1vHdXhJNqEAVeYiEYEEBECAAYFAktWmHAACgkQdwG7hYl686PWdwCgzlCs0axL -sW+Zt3VPyANSv++pvmMAn0c1FBHt7qRcse5NqR+tLsuP1AmniEYEEBECAAYFAktX -tbAACgkQ/R/ZI1P0XTLA6QCfcjj97u1EAahwgiXFsaRAj3Ya6WAAoJOZFPEDDSr2 -w61uh0LZwolgKqH6iEYEEBECAAYFAktmAZ4ACgkQArxCt0PiXR7mRQCcDJ4G7ra5 -hjAdWE2UMPDamh2p3DoAoLl4IenOsXH6dFQe11tB8CQnnmdEiEYEEBECAAYFAkxN -7P8ACgkQZR3zUj0j8l3hygCfd+DfELkSDBtZCdIM95MTrf07WxMAoIK5dhlDw4hl -kJoZgzPhadsflDUYiEYEEBECAAYFAkxN+UYACgkQv2Bwi0hCbH5EoQCglXmfFuI2 -JDMmesJS1Bt2qrwAE9gAn3hv6v8mlK7ynQmAHyDA/Tx6nkJRiEYEEBECAAYFAkxP -BmwACgkQ9/DnDzB9Vu3sewCeP0YNM+H58x7mB1HA7qGIj5YRMgcAoJOWiHD33poC -Pi07VTrrcm7pC0MliEYEEBECAAYFAk0cvpcACgkQpxprf2uFLzcGjQCaAiKV+7t8 -WR94mOaDHItniB2VI94AmwQoQCMhKQgX0TL0cuBuGiN5B729iEYEEBECAAYFAk07 -uhwACgkQC+Cq+bUsy1JcGACfVhcUZ01zuu3DjQHu4qsa5v6iZ8kAoJnaoYN2YeWh -2fXrBnGPijnUZ0WkiEYEEBECAAYFAk3WYs8ACgkQpQbm1N1NUIiwyACcD67lFyJ7 -t+GYMd0uaCLHYUfnFTQAoJ7Xj6mKlnM76mPEpw4GmPuuW+DRiEYEEBEIAAYFAkpz -CegACgkQ9ijrk0dDIGxO3wCfSrXx6zEquV2ZHLOud6GnKWZxJI8An37NETZxswSB -yKLxS+rTmMLdxfGRiEYEEBEIAAYFAkp0Qr4ACgkQvPbGD26BadJmnQCgrRFweuF7 -RObzkBSgTyCut0df5VAAn2EynHnNYvUqvnvh1jYvpjUmaLsAiEYEEBEIAAYFAkp0 -dz8ACgkQw3ao2vG823PKxQCcDuHDmfjaSwTXASbsAmgNYcenMbYAn0M6A1NYE2mS -XEx4mIMQm1r3WuKWiEYEEBEIAAYFAkp0d9kACgkQ2UbVagjji7xYnQCgkRok5Hbk -0K2w7n+91Zxz5X4h6z8AnjqhpNvRVr0+6PPhj0zZd7z5+fp7iEYEEBEIAAYFAkp0 -nNsACgkQhryr/xwAx5A7YgCfXXKlOqk1NS/uQaHcjrNycRIxJhsAoKW6oKL6JPIB -tuqmF4/JR01QeTT4iEYEEBEIAAYFAkp5kLsACgkQ1OXtrMAUPS3sEQCeNDYtKNVK -4f9vFSRSy3bWSsfmvvIAn2ai66mXr4dhDzI7CKxcdJXsoxjriEYEEBEIAAYFAkp8 -M3gACgkQOzKYnQDzz+S1CACgkBaxmVyUVPuAqBq4sAhlmPyEp+EAn3r6F4waisRM -CZtWdsEe3B5HiukaiEYEEBEIAAYFAktdSXMACgkQBjK2hooEmyGRBwCgiUl8zX9S -R8yq6mmmG9g/nQuKeBwAn33LE2LRYBUW5CdqfcUFu2zFDEWdiEYEEBEIAAYFAk07 -080ACgkQOWBmT5XqI91IQwCgnHbEwHt13SqFfcsq5nTUiV6A7EkAoI0n53SGw0r2 -V5/DpwI8m+EnwCiPiEYEEBEKAAYFAkqCDPgACgkQUZSDC+wZs3h1ywCfSu/xEvdQ -5yT4UfNokzz+l88CsDcAoIcOqy9wpfn8z4KL/+2EVN3/TmxgiEYEEBEKAAYFAkqJ -bMoACgkQ3DVS6DbnVgRIGACdGSHKZwwWmEM2hbBhYbScXszt1B4AoISXtdW0h8h/ -hUGl4t28pw5YCXFSiEYEEBEKAAYFAkq09FIACgkQQSHHQzFw6+lNyQCdHfRtNXf2 -Qu5UJ4u/7Ei9H8I1fMQAoJsKToIcW5ZQTeFpUfvN08g4TaCkiEYEEhEIAAYFAkp3 -htAACgkQ2tp5zXiKP0x9wQCfXLqSU8/rIuQkgiB3hGwBLI7MX8cAn27AYQaM2iy/ -lENWJL0ppjCbGwaoiEYEExECAAYFAkpsmbwACgkQEtt57sR2O6XOmACglhmDqtEX -Ct9QNSR8AiI2o7PAzPkAnjHeIyCEc6UJvg8LnDace/lLMDa+iEYEExECAAYFAkpx -ZwwACgkQbxelr8HyTqTsuwCfdUp4Q897xMsvpzVlyqJ6Ya0Ix10AoKUn75SaHVuw -DXr9YWVkaKBuqdlniEYEExEIAAYFAkp0zLYACgkQfDt5cIjHwfcVRwCfUd6xIqzj -4kxSO+HNqn877nCwtzIAn1W48+q/BxCf8g8wWcQ4adTl18baiFYEEBELAAYFAky6 -zOIACgkQ8q2FrB5Cs2fe5QDeIdmQ0Azxu88Jgj/OF6C5/9tza6gNvyLW3FCxQgDg -0M1u8eXwMKrqNeO0y8JzqBHW3Tw6lNFaoPLs24jcBBABAgAGBQJLVphvAAoJEMKj -XUokOhMpf0IF/2HhAM2udLWnnB8oNQLTIj9LVmeybUOX0kpra3IypvS1S0n24tZ/ -elFw1qQ+HhIyg6m1FMlEgWrfxtXl7ICE1jhrERjFSiVtQRHo1qeMi9pHWA7py1Nw -mbCAJFrQjcLue6z7zOhVYudDXzKvIOI5Dnt+aEhOZ4+a157gz5t6SvgRlc3dorgD -dPyDO+rKfPrzokJAaecNbqUV4AFg5pDE0tPqmnMWq3tFJkaiKIpp8smXk5ExkPEm -CBN+NVkPxlJLlYkBHAQQAQIABgUCSncyxAAKCRCWgOvkqZGT4qawB/kBnSbMIiG2 -3lms+i1xZCkXMNt1ODBK1xvb+mvEyHld3AHbyD8n98b0rDD1l/1u4vvtFogzCrNT -U/3WizlLqkEr6QKTEG9PPFXU0f+9b29Yb5vFBJq5gxjebhE2pwBJnqmJiKI4P/vN -UnMzuu43o2cx9xEYqsBf+8hAWj46dNsxjh2qOU6DaIuX8xZ9BQRcQLZ/V3UnZ6AX -ZwxElDE4eNyGhPit938JnJ1TDFFtaFSlC/U98gxKK81xCDs7cfJMUomz+6Sc1ziF -9357foRMTEZUYzEnIBrWq273GlO2K6dOzmBzhD5rJqrkw6J0OS2Dm2v6GUB8BiM3 -YAo4iJp96qk8iQEcBBABAgAGBQJKdzLRAAoJEDH85+fdB5Rhx20IAJkpBnyYviFD -PQB7WMOYUTPBIaNx1C0iSdHzxOL1GSH0M7SvMQsxy+vLi89ka1ZdAYmyNkuVnScZ -Y7zLQlL92wjGoRn1ekRhJK4bOvTPi4HC0/vm2qdGlSbzAQEqVYlb6lkR19RB9cCC -8o5b4aZ0pMaqPwcj1czUeOIX7ElLQE0p1byMXDxZNo2Dp8Xp6dSFTvuHXckO8k68 -vZh/Wh9cLivwgeA/BBKuzqQ8FeVDLgD0gx1tGxvWS4dHEe9iSCu2zFhwt6a8awoM -5NVt5UL3w+aiFf8H0vRKbsIt0xeKtFFLxVM8/1wyUW65cja7Ig+ii4ivKoUud79x -V+YBj/vjcBuJARwEEAEIAAYFAkp0MGkACgkQ8+QSLx2MJhpNPgf/VJdjTDz+hZek -as3cbWbIOmkyfdtlpCWCwrcJApVSfxKgprGYCF3XeWmWy9JauFPHohgm98RNHIvX -G9lk8yxIOtgfLbs7E/hH+Z4wETw4vk2FGbF19qQAZZGH1hWtDRHo4ph5KG0X7n5y -nvcGmfWKazyqRInzEfBEzze7/NWBT6rGdyMJKKdXeDyfOXIHp3rYNu3a49JhSlFR -PDCmfHwjuqCL1T1A6HJxY1LH4LqBnAaY76hg3r0aD6wbIQr/jtsb8dCBWJV5W6kk -a97DZYgabAqQQPpbpmqPzVozfSskblGdNOrf8zo+a9cZ/6vCrdu2U6qHSSAX07sv -awgrWNvF0IkBHAQQAQgABgUCSnlf1wAKCRCWgOvkqZGT4kaKB/0Y9ukhupJ5qaND -YcQUKdFkVm+xKIjc+UpOwCLVWJpIYT5wFXFppG09uLOfkJpmVbIRKYrrBg/D2JRB -Xd3ysEveiZ6NuR7YPN9qmgcElu0M+LTwywNt7Z6DFULYo93sE6hNygzt3PVS5M/P -66JNO44Ik4zKKfL0qCcTtFFCOaVilNkPs5Ozx9BQGlHmWVqyxK8dmiBXt8Ko8Fij -b3WkyMsXZFZjOxv0hauFSAPLka3ME1zQotOPz6ZKcMvdaUHsm5wotKngxSThpQGC -9HzJPOxOa71fNsV/xRxiLMExasFzyMyVVnRyNqGNWqbfD1bMbpR8GYRw4ktCwRS3 -QoJlT4SIiQEcBBABCAAGBQJKeV/cAAoJEDH85+fdB5RhU/AH/2u69vvFEObxJ4Hf -6A/g5iSmatG+rH3RkO5JrYkY0S2FuXGHn0VG8rC84Bybzl2ztscd2j+Df2NBVyNl -U9b9CIlSc42F6rMS/Fep1Rm0y+9Gd2uAK53WVmOi6m7P6Y+EJFv/5bzS4VCcqGYu -HYcxTisCs19llZN1rgihjXA/COCqX5et7J1UYX67xOYv/EpnXnWS7zXfuXsc5hKv -eGmF/kOLrqJ5F1u5FtdFbj9YvXxcrQTD/JNXOsKDMZgKt3RAjBfUipahmUVMWA3m -7qON5P9HPeA44eQk4raio/7KaeWg23iTGvzE6Rsdpk8onHviEBcXZTKiAGXByOaK -sl1n2LeJARwEEAEIAAYFAk07uh4ACgkQ9+5hbuDCDP8LXQf/eavbQufrJu5oEZUU -Y+byVqWRBsazx82J55AWmLXxa6aJkZVhoEJ2qIZtgOwvzdIUvPwlW0dOnrLZsl+P -SwfEsRARu9rQOjIwHd3T8FYyG2fWhOiVHAIQoy0ZdRMlmMPCs1wprxSQxB06q4+K -8eXE4FfgeKvaybGladu/xu5NiSWwbmfxuj5fvX2lwJp3EMeLtsMuDIE/3lg6CbEW -aJ8svcufLBBQnypgM3SFUUM808/yZUcOgQQ3d96RdiR4C2eSLbS/rK//kPPkHs2N -JmAeJUZ7Gk0K+/bSDxpET47VQlhTa9rj0Yu6Z7XHqbgq0Eg7yF3QNK/rtCj7NR/G -Zdt+yIkCHAQQAQIABgUCSm4YdgAKCRCi0+fPUhNGtGoQEACAqTczNbNo6u1L1gjz -912vXwKmBmFdf0ybCwW+SoWj3bg7yrZ27zetXmhmXznwYyfDZMJhRw6P/HgMT7CN -M3N0cek4eIeQxtQfE7ymTdVwxgeaAC/CdmZ1phSdEYtuzzVjn3ep1RkEdRIJFOkd -jtc/SySnPBTEG4/eGGILEtKcAg2CbuQSEQ7cJRD9M30f3iu0khh4eUqX3hmWryce -1e6Oqmny8FvNbMKTf4VnfRRNtuoQqzJYeuB+oWBfnuKQnSOGiDJ2jKVBYStBXA9i -1pXyZlsFEZl6eSdC+kQzE+94fpf0s/PAfvUTO2zb0Zo9Rafczl8U7zplanSvmv0N -A5GhgQAPTg0d+93WG7duvA5AHTzRTo8oKx1lHOGvEPigl3pmX3NqZEyOk9dpErEY -3BHbDC85Uz8XdSVG/pXnp200RZVgX2SIPa1eIxenAAfmYG5gaK9adsBJLYNSTsUw -Tu8Q89h5KSiechI0HQhoEi2Gk5lJY5IX1+9okL5e4sI1QlwN6cse+ejXiui6DR3l -Rc4qd0M0EVZTfJm35lWi7rTLZQAWWdsqxMoSuDPVN7Vhm+cw+Sjk4IynACGAaFds -a9TrxAfwc5MR40LB+fZv0yuiHSqI/9pXKHnId0lrJZp/oPKhIbLcG2lLgPTQ8j8C -r1XCx46XC5xWe7GkY8tbibU1vokCHAQQAQIABgUCSnCX8AAKCRAsfDFGwaABIRiC -EACmdFkmEAcIYL1ebooKfRNYM4o+DfHcc6rTyoSZhGZ2M5rC+cNTrEV3fXDrEoKc -cGjYzQKSd9WeTbT52xb2YvJHj6znWYZ0dKpjeM21MFct8xnvlC7mDk9mi1/f5GCy -khpU+J4LDHzKI4y+GYDgvTbN199A/gDYgqgqhsU+mofY+YEbfIj8rPetlt4QHIFe -NYw3v1YCjDg+gqOBmdaI5j8zLMOyFPFUOOeQOJRotjw9xOsxCRmfif3rCFryJ11k -fwGzpRYKnoynHm2+nS/VhiMNYLyTvMyXxoTiZRDgYxtnRUGVaWAHDPBQ8dxselm+ -oE91hSrHUClE2fk4IT8NF59REpQt0QqYmIwZRVbVgnr/Xzj1iKmQzNqM2JG7x5Xc -YJeenitDoE1uAVgA5n0dZdTm/ajuXKwajptJ6SLbMHMyXbnJRDtG3Q5PpjOmwL6k -56aholgvC1kqmYwA4wHG/r/mKDXG7D63yQQ+hmxt+BdgSP1B0vSTvUr5msM8ZPoH -iTNzak0CzXTPXXdwdQQDy65Zskwq+KNHaceuG+u86LO+57+FlIBoFzVIIxceawpH -OOol7weCslBWJh5SshjcGQCYFvFBehWG8xW9fg0UG78ZuiegBMfQXMzvTxxbzcRJ -PmPFaWqj+8aQ9Nj0T8CajKjA4ti1MxSI/BWXfXqg4n18I4kCHAQQAQIABgUCSnCb -JwAKCRDthgS8BvWpH8W8EACOWfEvQyNlsnrNxF2yz7yyys4KjH9E+75Q15EHOtN7 -zhLLmOXvJSS8eW9dhEynnC/6Jk2/AtOKW/pxobj74YZC6rGwqdRmPIJ4dRARSikm -dEvOtJDit5sLgGVrfYVlxdetGqEolbHiZVa8z6Olfuxsl+uX5QeKH0aSWx++7Wlq -uC0aJqF5tesxJThx/3dfyWTNuFKQBB4iYxNjUUbTk51SpRX5WusH0adWg9b9wkPk -2sqrIzQjfFqa9qSsOoe+TI2C2aJAm6BJjlG7UZ2dadvjBManx4UxVN4yat4oqbIM -kQzJEyfpN+FTWA4uzx7KzJBohyni7H6kzaC8pGZybK38CC9OpPfjCiVOy12KxSHU -t3xkvxUvdAAxJvN14y9Ay07zDWoOlbET8ojkMDKAeFngBL4QASkUf80G0UjItgBS -jshH3jryXan9NEjSn/4kbyMUGabQ/6vqJBO9ddoSkbmVP5zy+aofmeDHWm/GmIci -3s30H4moLl3WMs74losQP+Ow5oH969gV20E3Po009FeDV72rCqQYfFGZ21Barv+G -SlTEFxdm+PgGx6UI61RjUA5Y88SBWypSHZF+yrfXggfUDIiwlrttDVHv3AaoB16k -30M5LH24LmhQL8PkerL8Fn7YJ0SdUUFHUprPfn+favQwZ40nGZT8oZmqEUqn6EPF -U4kCHAQQAQIABgUCSnHlIQAKCRDMeYZvmUCQhBazD/9dRIywl0xALQjvcjg0hTA4 -65j1ti9Jg+xHVlppZ/GUpiqDHg/CFLkBYGfpWci3WFzr4jEu8u7nNDewUvImL0fw -uNLDkli96ICrCzN6JIaMSCDZGXGznN9z+tVO3LhqIZfjWAEAJUeao/4WTkHYVlJb -tuutWPjREBvkBdYvt+jj8/OKbr3Rly8o//fvyNgIcgo02fdcmncjjCNPx4M6gs8u -tdxAfGTWNBOLxo2q+Yemutdx+zkfcptInSplxB61t6QCj5H8hYbN/v+TEHfpEsM1 -88uT4eznB/h8X8tWTfnkHZ3uILoeRcoOlKzPYIXu6fF3o4zXNzqj8wNTK8ojG0iq -xkCE5Y+qpRWFmVXhIqo29qFTdlxn3uP+4CKc5BBYzw5nAQYkD/gTgwx+UgfTBD8X -/oF6GVVuu0QlEvvZz9hyqZ2rhUJwN8khfl06t1ulu5X6q2DGrjkS2THSLGtu7Tl7 -6aGSzi56dpoEuW4Im/sqqd74+rnpXbhG+jfuLyRnTeCn9xmUqiraL9/V8kzD9iiA -p5pQtA1VxKhqsQ/wKV3zUNVeBurBhG7Pn7pIcSV1gP6gEACkzzzU84g2CtJ9FYEd -DsqqfvGnEeJHivOQXTqu7FX3s5O+ZO6ya51hu4CqdSnxsMNx5KV0aOO8mz1zBlKM -s4JTTLyuuY6cPKyW2legUIkCHAQQAQIABgUCSnK2GgAKCRCHL3AsTW4lqJfaD/9f -FOvyGLps36cpUMbOwud5qZ+yFIvYKm2NhrKfaz+m3MQrKGaFsR9P71ssUPLnRUYb -YB3U8nXj7OqCLOVOMTEulHx3FtyXqlyBOZl2R/6u7AVxjC5gCs3oq7nFXFD/Ebb5 -CBp/8eQ4SsrllnUOQfDiPVuMt32fU4dWlu6WxDreH7aRItNu1dxW01mzJQTCrhHA -ZQ3yzsBCno+oyftiHAROhP4WMONj81RUMpMCDDVdun63wDYrnnYP9GywXuMODB5S -ibCOXboqnBA5HttLVtvAcne1WcBsEDOcPnfRjr/4ZUwXxqG18tjC51Q50AIMecSl -zPY5GxGdetaOZFV8UwPylCwV+pVuk7XG8KgYjb0TvjV3AMjuzqRQuvIeMW0WT9Ji -Ds0CX6GShO+8rnpfocDmGCLnDny/r0/KzXo9nvxQldnz2lRmP/G3RWN3MtQM9DXe -HBejzqpsIPh+o5zazqHcSodY+EjKexSh+2K679x+gJngboySij0jRTp1Pnt/MWpT -hFuC7GXYiTcTMHMA3vz/b3XTDtg0QjudQwqqi9caAJLpEW9bGMu1DexGIOWjHi5p -W6u4MbLfBglAti4Hlm8mkFrpQJvXqtr1l6iWlTY2sk94eF9TAb6pNzrZIaztUI48 -XWa6PyGB0ZDxR+r8DbThXkFiOJy4gZp0nQx99aGgLIkCHAQQAQIABgUCSnVVUAAK -CRAdIcg9xFLg/NSyD/9re8t8XE4juKdjcsAfeUcFTYAR6kbaYYyFTpkJhOKf+0+y -DR7snYNwrSG3S/2kefBczcLAjUJlGpwpydA4hbg6fTMc1GrWn8yxeQKTdOvELWDV -d0dVZvoIx3kkRjHEjt6YUS3dD3MdjS83JX3iucffaNdKJg2sd5xHm++dUQ8qmE+d -K3pM3mbk0l6uFeWwyWXVcOLxdZFpB7PQ86ds4vwmhRvs8Cs/QmuokUABV3zsaGX6 -N2VExACXuZIblod3be6LfuqOrLCxeEmZnM9uGpjJlUg9y55gSGAv2kzqaipCNfnt -PbBMTXQiM9oSJbWVlyn834n3cgEQSdHDgJMuISN9vxdLGmSfjc5FIxSP1VBaLXD3 -yiUDg9dDwm/SE2bXXYZuIovEzF2hxnGZjVhS/txM4JB6XY7kjgHtQHCqNv0Mmen+ -NiBB5HLdUlTfjfwkGa9hdbTJD7Xdl7PmOHK6Y+InmXlHo6K6tQDNQuOq0OY+x5X+ -zhhOXY1pvA8IG078rFQwB0a/TIjsh2q7MqmUTc9AoLMF0WCQal7j5/VZcemKNBnM -H6zhsmkK1gREjcQe7KOPBiIJBkIOW6wh28jfXJoGgJLyJMd0S/jHjVi0dkPbmxAT -njlKjuIqIf1rJcVoUKmI/BfTs9oJKoJU/P+LkWdxtRSBD/SFIJEwJacPQayt0YkC -HAQQAQIABgUCSnV3fAAKCRD2bj5Bn4T03marD/48b/Yy5Zp+LpdXXdA14WiTk4sW -mcfdmGMH3y2bzNQNRPhCA7SVICcEBRU+Ga/CtcB2MkAkzEowSibfJuR65q6sEHiU -ObCe8YCYoWZSzpPcrsJgim5tAuIKeBzAUGwckup+unvnKxGw1oOJySsdkYqHtqDG -IhiMHnc0A4zdoGBpiADFs0BhmdSmE89Mt7mv1RVUTrOzL2dHuDM8xAfKTIKX/Xqv -UUi4c7wpSUeH7JwEjf79oe3xdgpF9TmDUNFDQEwuryejcMeKRIAxMuZCpxIWDhzW -6vkF3Zk1yPIoPuaW4loT1fl/Hvq43PEnzLAQDEmxmez+BKnUdkqVSZFuQIZA/KAW -JMBm+eoCcd86S9lSWJcdyyzy6POuP3BA/0YO5IjgIuolqP+JqcV3MWOVraePCEsG -Ys9hbc+v3FulAPW2U4m9hvNf/stgo+m0+uqiFRu/8D/zczfiGvA8VEQef27vaOdw -5GtCzCdrckwcIWHFQYrZ5XMW+4MFtja9DmdzJEt8O/KPNb3KcxMYqpDQX9nGe8tH -37GdMabbNPP+/uL4TleBhw+KT0fWmscmSJ5Ir0ToWxLNeHLAAP2JekB2qgjehrMF -heODOKz7pPiZlz4d84j9aT6WzW/5uCK6/qHISQiYApTpdyG+TbjiE5T+qRd0p8kb -HV7T8gzFqvr1N+6O6YkCHAQQAQIABgUCSnceugAKCRAMcFV7WgZRPh0kEAC8KS/E -VwQrFX+G/EIpGiAN6rJHV+RhtGFoJlhlvJRXwuHwspQ1/PICGtUYcghwBlBNRt1P -+FvKPX1Ow23NwtFNODTwSp6omY+Viz/sHG9A6miuD9N/BUQLhVilMbGC6832r0Q+ -d0hC1HJQVNX9H4t7XAGmnYPY7ofW8E5eNo+9lyco8W5BVjN5fuOr4hTVm00fGosN -MHwvY6DO+BDbGlaf0LLNqaSz/00KzUJ4wClWumpHq6jmfUAXS392pmBU7y6mAFvS -SkTq2IDyPrF/Q2ZMmySeWtt46ZDpglpCVY0VMVz3GxvLwfD5Jezc/FVTdAshE23t -dMVJZGBqCaXv7ByFwYePxv9IvSNgEo4T8Y4jOXnaPE7YPNBcKA2gyoJ9DYbrP1qn -KIlefaUw7qDK4q/46OTcrwAeqgyxkfvvMR8xmywyf/ygDc0HKzXUN/Xqhew9VcNm -TknUUaikD6Pi31RNvzsHbeSXo/UG6+6xYgGWTzR77GA6OwCSErKyWUWqa7h8jJX7 -94PLNeltwtjjWB1qyxkeKT1Xq/tcwjL0D2xfLUbIsftdBf6txDOsK/tWqZiTQBqZ -AMPwdDiqhlVfxDNqjxM54g0A6MXoGpq9a5x3m/npBoofbybVTU7UKvnfFvwLYSmk -ZoaXRU6DAunwg8mUzvnDpQCIfHSZfFW2VcO7oYkCHAQQAQIABgUCSnfTaAAKCRCH -L3AsTW4lqKmHD/9wA4xXDECc7/EZXeZsL4MQ1AXdbeBv6zL3pGBEB+KHnYBfv5+r -AsWk4Mm8YiMO0K88M75FG1mVu5xEbG5RibWED8ALa/6uKUEgVFnPmUAMVliaRIxo -cFjPn3uXrUM6ce+ziYBYy5n2PKfLQsJiTdXMmINBKEdkImB7qfVCM0f9E9ce0kx4 -0VlIlTFp/oCupx98hpGUvy2itD9seMSLw7K9cIR38w7uKYtBKhoo2Wr8P8Gh+2gq -6fHmFiL6bbcTQAp8ftlMpOdNc9rroAH0uyaulaJC6DgWi9DAjZ1AVW5LzPZePK46 -gaabNA+z89DF0R0BJtw8FQgwKMz7PFvggbPST5Anm/pK0zAwKHSwRkfqK/iJCsu+ -NrzX05s3+Ku7xHEIssVDImEZ6bTfIMtSNllG6EKy2UHJu6YiyJ6w2y83RoDYl8v2 -VtXbZqBE4fe7m+0cqtGY+zfm3OnYcsMl7Sv/3NmqQXRb7MPNht8wxpOi/uM1LRJq -KiJX4O1JVXcGQAf2fg2zQqSfDIIWZncZc4RQvEeFgn1kUMVjOC7gv5HsP8Lo7iFA -ZqEpJSqJlwX+eqk2Qv1L7h8BHxhzwusu7xjGDoc6LOJgCXwfwJgm19DtQUTqJM9S -Lu7PkU/cCqv8yLZEDN1j3b9U5XVPTAc0mAHw20cmpQz0K0eoyqf3mMmCgYkCHAQQ -AQIABgUCSoLqKQAKCRAyJH+7QK0fpp6eD/0W81uQ6UcaqKy6O297JeAutDo9721R -tp6IdpVz9yUjJx02cD0iutBKg3lKXcNGesKcwzoMgatrighSEvTFEqE2j8opzO7z -PMxkGAWeio8PZDw0jcocWSJZPxcpAj6NJxu6PTSyPFzueF1+nmANiTMj9YLFzaxP -Te0hUQ2/QUV0sDfVgnNi1k1gcSzx4fNCenFUqJeCTIqF6z9m6rIq3L5Jmkmy6yXJ -A+1+NZvl1NyHld84E8UAOLlkLnHWHmP7YSzO2Try+VtcoLsTWK+tp/NPyBIpQtfS -UbWrqW16cH+Kms90074/fiEzyH+DdyUbk+jGPp11M9chMWa9ARV8JifohtFv98UZ -xZuVuf6OoWjwbyUkeyDyD6q3GjltsiX+Uyhl1fNMOfRdAZvZBSlkW9/nRnbTX0/n -RsxBnYLlZeGQucJNH2SuPKkk6p09hFTN/IM3N7bmsz0S/Zj3f/DyYUHTALOMgvEC -Ucod12LVrOVSS7J6raUFn835/HubpTghfPA/PibNfPGolkeqUrvaQitRNdAwvn89 -aO8KlBu/w0nb2sOhwJGzL7hPhj4l6ZnZd7BpIc+hlazayj5Qc28tDYqbIaInluuP -jk9cdTkQLUGT9pOMS7kjs+TVivWc7QgIISELKeRJV/kdInYOP71uNgp/T7+lxsdY -VQaHDXeEMt89ZIkCHAQQAQIABgUCSoOtjAAKCRBXkw2rC4awZ7HzEACBqT/1UDxK -sUU3VmTRpPl7/dxCf/DKLABvVBTWKMi6ofgMyyvosM6oyaf4Dq8/FRmaUIDG+KaZ -loQkI81INLjw9z7TclnFuVfiMjuABEmFBqXp0EzahHKube3GRwTYBxk6V7jnMs57 -EVAu3L37p6OXlYFIPQCShXdF61/uKTwG5wmfc7I4TVN/6sf/ENZLD3667igheHAn -kPXtts7/IM2taJpQMX6I6xFfg3khtR303Y4vyAzYS5Io2CL1RYFc/2vzyZSfmu0n -3pw+zuGT6U9NW3NXfAv/Wu7dS24dn29c/Dw80WC6FuevJWUsQBBEMGHqTJQfv4JO -5AriTNxFnX84B1qgjdBjFKdYYt0j8PdICeSR1yiK4AdsELzyFZil3XRvlBCoEVVV -PxzrW58+z9hBpzWLop7rMol5rS2P3iad0WunV0sMVEUPOUd5RK7tMu2S0On6ZQfc -ERxrA3fqO6aYJOQHw+76gMCLZU+QgH37V4SKA9vfd8D78B8H2wvyXdJRg//Oma9u -sNVqBWrVAt1A9JmpcgIZSuVPCyZfbTmB2X53aQVYVze77uStjdS0Cll4457PFAAL -OEdWY/8rtHoxjgEFRH2/4hOAVB96m7FtEZzM7Qdg4lZUr6aKLbnLiDngQyOwAFuk -h12VkJtkMtynSZDUEbWWzNbxLnAYazeLNIkCHAQQAQIABgUCSqQygQAKCRAv+c1Z -YSYWtcefEACiatZXk+qcVuafDQj12C/nSOxiUv4PDQn0k1wgGoM484W28kDuhrHR -V6JB+qiygwA2CFre/6hLb8BJqYDI/CkrpQD6jYvlRuhsGL2s7sNmIGqUwd6zQPB5 -ejxVEBn/6puczj/FI+t1oDT6aQelj5YlQHM7a57eQgOa4z7ysxwxii7lD85ZEWmO -8eY11iibo4TAHtoBJyn4agyrwe6b3A97MrLcidDpdifVwyrzdses0VSCGScmLDJM -RF2mIjVWFyu4n3/2ki8iujjeBv7ntD2yNUFcIa1s3rgt7W8CJp6FtNkNNFH7d9W9 -zMt0f+Qs0ElfYUid6Kt6nMwOnKZWNvet/naHduKmUloQuK9XUjPqZ1h4eYLxCN04 -VUKADg/HNTzH7QwhvBqPiRITBN4Ox1wJj8baYi4jXfAqp4WYRTJWy1VPVE6s4xya -NcXuZYKWuRWJQ4bLM+KG8Hioi6ZtTjCJjWEy6GJc34utNpnXQHwxG9r8Sx842vPK -VtRbHw2WDyDe3LTBOQkGa4QSDugvXdPmrphUYfB0nUigGhEkkUVIadtiaHkwFmIf -GaGNEjN5QmWw8klwao3/J1LNahLUPMGNBYTrBcWN6tuH6bXX6JCfKhpITE21VG4g -1MltAibvSs/Wtp6LRchdDBCZF6hqCR1vtVyL7sLky2YIJjcq1UvygokCHAQQAQIA -BgUCSqu5AgAKCRDrcP7zzfxuTyaOEACWotgtBdQnRPkQw6V4n8HdBRq8/345OlxF -SoXl7c4cSjsBZBSsBvBy+moRs9uwwURmTzfqenZjhJQsl0bEV1Vq/cpvDnKYFJQ3 -RUc0M1XBQEeiH2y7xDhpk+rbt8bEmS3xSfC0rqHfDaUrivLhs+VrRF1Wp87z1kO+ -zUWZpPlctdbGghps/e5xtl8S6DrVnse8nm1uBBNRbE+J1cdUPfvnAW0SwGMI19Ft -YifFxT3nEUHwiJKJfW1BrbM6AJsYrFnw/4khGjjBE5G3998ls5rPTwOSLzkg5bMp -WQ8cXPczH/GEL7OvalqgEyBF1KUpR7T152LOa2RtWfl635LDfS/hnouzKKynMtIX -tKWqXiCoCxecbm/U8EYH/GYtHRespip0Ep9Z60SjsxT79MKP1M99UBoLrDBXtbYS -vX4tWGM3IIKLskbu0UctmjRA3eIVFn/cdIkqJ7NyOaQYaQeRlfkfF5ek7hPIf+KP -dJ36Tr7dHBFl6BM5XNSTEHdZGnw/oQ5YkjFXR2DyqvrnoknYkveUk7I59XeKfgPD -Im4omotYmhaULk0N2ZiAB8dRNlBjTEofi+CzwRRO1D962ollf6VlAD3UrEluniDG -Yz80Bl2855TAvRzwzEuudQXx1OY63oy9XgV8VQWfDNSOe7ligSKMwBGKCBjLob1v -4hpqgqiF9okCHAQQAQIABgUCSuXRaAAKCRAYFHsHO60rB+oZD/4kLJpLo8POYM+O -iMOJoUcd2e+BV+gA79tEpBun72nwSCmC6CkjvnaRxl7M6jzwFDsJtqNxX/uNAg8W -qJIuNWCbZwXVniD/Ss5QyM/7NYGW7peBPPJPVjFp4VAR0Py1Wwcl7fDwa8c05152 -ze03j6EqmUkhqOwhyWh++v7vB16UK1DsLMIYGhqsIseCSHM8iiIsGnmhrpcIula1 -3y6vD5X7ZxoeTvUV2xdI1Kf2Ase++ipk4ccBYdIntbel+3BvbThrLxtCXlp0XFQV -kSRyvckmDyqa56iDAs4b/cx98yqqDB7iS/g4ZcpLEGPPeuZiNW48h2P/NA0oR1q8 -l/7RuElB/opmh19kJ9Tjeo3sKia4w2wpFc/SPTtLT8oEmVi706q3Jr6yOKlhMYvt -elcqBfeSrNGXorhoZJRqbn78kXqPcCWIl0z0jUxPAf3+z2HVpR1i3gq3q+iK+ZlS -ZA0P8As0ZkCHLfa8XC6TugYGD+8V/z6lreFsAHdtOlwBMsSUb+cjk6kE67ZxdhZh -g8BUzJ9Js21X6Dk5hlB/RKqgS1TDOUdYjGgGwcs9lBiMCyAdwqne3KYZDqpAKqIC -7FTO9KWvGruStTsKEspdJlW0P3GktSKuLOaqepYkdJRTtHCiE+QgQBwGSbaANYPc -VNqSkUeGN2h1xff85uYT1efYW4pv5IkCHAQQAQIABgUCS1FdAQAKCRAhn2tgsrv8 -/BWGEACuYTZtR37pBu17F6Si/guy7uxnCDPQeTkjQHn9TYkUafFVP7CX6/r0A+EO -UOR8daOWAw+WnJfwA8UwP0b2mIhn2AoDcyg5PppI+tNt7I+h/w/BqDqlLW3T5JuY -B/dHbO2UPD+gMPkbT+ex2gyyIx19SeYzdYEt8LwrxaWLwvpQHaIsnWesjVdIpKat -m29KKSLdX7c8+YX/ujCQ9UL12ZRgXKF7ozdo+Bl8L+EScnQgZ34vwu7eKHW76pdw -8HISK5+JDMBMdvsQElCXH7MCwackeygNRO9tW4y/E9xEZ1BhphTZixdJJfnS55Xg -y9ZZ/fORGbU2ZzzrfWkCBlOkjurhIzdL/J++xCIob2wKSmFF4DtlIR9irb+bCGIb -gLJovo7boy/aodRestyLUZS8+APy/9xoDPhtDlKuXIien3GB+tJIU0ALMBR9/4+1 -38XduFxBBsscfMChjZkIOZf52HVYjZq+DIA+Gqbmc5n6X7XhshoIQ7Fk41Spr2mr -lCi9wFBjWEo6V4y9QIeNNkxVk/2odCQgoc4u+oQMJaaTMy61b0IKkWM48y6p2OY6 -4R19bsmZhKWHEUSzWrP0dyALqopAG7ozmXgHoUpJyPw4ORXLj/Yi2twbEyVC2lcf -SaG9B2nols65EvvV18nl8n9sreb296EobfM6rxaTNriwyFkz/IkCHAQQAQIABgUC -S1frbgAKCRDyNyM2/sOXRWqBD/9Hexh8JOUwJD7I+bDMPlg/UcuKDWT2lGJhOreV -VDpKKSpsiAG7NQ8HJUrFj3G3NGvkLtYOTDRlBpasfz6PieUOM3Xc0TlzI4l3mKL+ -z3qXN14bXYxDG3+1OH970qpeQUwLfOsXjBlArgba8xRhYO6mIpr9I8222wUgIrQS -BWMBTZqF13m3+d9B2mY4RtItF6cJeI4IiWbyxACu6VDjQRbW56cu+a9J5RXln60T -TYmGCTCLF+HlQulMq3FN3rYXgJmM5SDqiHlIrfEDUTOgk8ztxbYwfMmzccCKQ+qv -d1GJhLmFzCzXV6FbmZYe0RmN/xzPpNzTOhIPHTpbNPi5hb+eeLdinaa9/ICjNjRe -9wZeEL7pknOzit0a4xw/tlHRAAfSjsk+nXwnU+bIn9oFwuwRUjfYlZALL7CZ5oKR -RitH6PWIzMzxvXcdWzXNchJRg1KDcHepExEovpHVl/tcB+4QqEXKwXhGmpGFYLFY -9zWQrLWxS26MQFgn7uc/Z5pzOXh1slUC600J3O0NHma7L3FIZF+2vlsBzYDiMnk1 -PgkmJRun2faWlgDZCKiBUVBaMBuqxzShKZgD+u6AW5BJi5pWT5pFOf9O7mp1l6N1 -PHYCpy0wcsaz+KVvH/GD1BrlbMSw8IPX7QosqbymZUpYhiFxDfZdyaAFxWP8fL/b -6UOOyokCHAQQAQIABgUCS2XhPwAKCRCKERtcrkJpRLr8EAC8GYX2c2eOIB5go6KJ -34ypIiIHQ+020fo0/JURfGzc6n4coMfx5xnip6zZ/ljvuPKJV3c3t/1DKbEwsTPH -bAYIF/WaVpToyXHXz7UrKRrEsDiDEv/POd1RO2EKm7rotAHtpRokmRV/dHJdETj9 -l7aiw2dBUCT1rb1LQbOkqXwoDL5hUcX1a5sbEGtP/aFkvqVY6ETzWcFeHSel7rhr -syptffMFJXlLUkGZ0MzyklpBvlcmnP04ROzvK9KvEsTanBl3nFjz5rTisxPcIm6+ -n4L84/40/WBc8SXV9xIlHPg0eyPO/uf5MXt6IPIVH/hBH5bJOhdpCbkjeOgyCXl0 -uSiU/x+ogmPoL15m+xyUP3Pv4UrUM/ilYZoLpmbkSDb3o64uTkk8DKFVnFYJnopo -3XROj74mWU0HgYruta6gXHwR045OCKN4ir79mADqShhjvUNpLbc/qNx/J18gUHgb -YpO2AMQKDohgztN4n1o8C0DnapGfs8aklhRHolvPrrCDeOh5+3KC9LzWLS+AvpVQ -6r0yMlVj+Uts5+vAOL4DFXd2SFcJ+7uFqs7FpjNZYHMoKDgy13s5ZLHWTL3agf++ -vQzJ9Zqyrqn+2Nbcdxv5nQkzqxGAOqDRaqZ98uRBQ16ffpz1WQPtWl0alHZOm+43 -hEAmqa3th5E1dBDBruGRZNdaqokCHAQQAQIABgUCS26TcAAKCRAx+x5uElnyjzsP -D/9VXuvlIeVfdxjHJRacCj92eV5JgQLibqwXizcnz6ihjb3+kwaAnOjqszn4599r -2VrOMBh6KU5q7d/2gVH/z+8Yo2AMPrLzWllo/pMRlwTCrDlWlleFEaisrPV6yJHv -ycUJNdU4Abrv8O461FEjjkMf77fLMkr9QcqaBB5ar9uCK1ofb7dBOkC1CMitrH9I -r6y6tlwp8grFTqP8inwl1yljK6paiUAdz0Q3ChSBqfV2cOiK1aZrLWCY5B4RKr0E -pQanRB/P0i1AxC1KnkaMdveRd1ja93ZHcDUboueDp6Y23hviaeYJmJNjdATeh9Q8 -y22pycPQFPvFOgJ7B9lupi2ODIo+JbYW/z2VDtfXnR0e6Q/lYuZzE5yJf+8wZDTG -ya8KTaZZ3ieb8+e1AmHrEUdukGo7i2GdTZ94PO2c6eonOWWKcJSHdq3R/BozO681 -mtGqwQz9GE/zepB3dNQw72xqFRF1whlUciylSv2/GqhUVkH3aaMyUmrBS1l4JMt7 -Z5Qm0q48v7KHkWbEQiQ6SIaeA/1OdAxmpWtHAay8RDN1CMQPGB3Zzcm304gYgv31 -h0DJU8MdpleLXHGvzi+Gvp5zgtVrezb5244iJlp9wwPu2W72ofA+F4CTdwYrIVkj -78VL8PWTYMkkudrt6SCpV5J3age4kfndbQTbB29dymnbPIkCHAQQAQIABgUCS3tQ -DwAKCRAorTKyGMy4/hvyEACR5BzwWmKjjva5bxPM+CTokWDLMZzsgPx+7DUQQYpy -55M09+TR/b6xA0sCEXlmVkN/CTaFHcyzTZ1yeF0qC2gdC8xPLN9A9a9asBVkcoSU -/wxqUNISBOPEebNkMx7ZC2UDvVhsBDvV/34bqGqyCrNXyM/fBVXdeB5Xhs8mWczf -VtrRi/NMSjBXOWSK5NL3FaQIhb9APv4p8a89eeymO0aMFTl6KNF4P6AcJeZJvQxH -KZ54TKXsKvMBXpXL3GNNjTQKTaBbiM/SPE1irEFJ3d1yaQiGImUy870mqmQIx9fz -laLPiCzIt5pXWHQ5/hxlhl2PlB9J/vbW4TR0iJXxYMb9FO9eZBiCxXRcVbCQjXjn -lgVF5bEDqbMk8MCQNHQLuHKrlBhYv2CITwvu1iZ/GLTm4B1UbWNidViqZ1QlslTk -kCBPauxpzxXAWnn5EY0b2ZJVCWG8k6snkUcLSbAgerCkMnseAA1cXQQyNuH+opXG -6t9d6uwBKY9XcmACp2jYkZDK+la143+ESAbw7/c66LFPafMAWa1VrDEhW0/kfmvP -SXYgS5DV/1L/J4o9v0Xcs3A5B81pZPEihPjNPZUBbH6Igx+tCoSOFFUzqHNaRNO4 -Lf58Doj+hkCEHpCFWkepXH74ilJ/vi8tYT8pyN9GM/rpuGJr03+yZdxm4v0fsHEJ -YYkCHAQQAQIABgUCTGrqVgAKCRAzlhWI4cIYRWtMD/0XIXBsyqkc7+PTJZe0WbN2 -9urQhTOZu6RpBvrjzMJ1vNtBq++yfkdPe8yqPdalJxVXF5utL1J5SJ9Dzyveh5po -d0yeVnDsE1h4Vhd709G0XHfGiz5V54krhF3fT+iJMklo3ZVWdoCV8vV2w0UCnPuy -unhR7X31TxVVukqOBimb7+PWmfMg9u9rhIn/0LmX8IM9kCi/XdIv2hkV0NK1JY83 -IvdrOQ86hGdH7B78+y6qS5B0LE7KL5vOpf3DRH1rIWTSMosgkCZC7VKotrm8WGLc -CCLZRHMvqjYMCTqVAhapoAxuVRJQw8FA2htB+jVtdN9cAbM9kI/fL4237NauIlW2 -oBVty54CjO35ls/LxqfTxd4U0NTNWLHrvv3XOFPM6TSKTOFEu5mxVYgZkWLrD5LX -DgRN/Wxz4TJTEapmsnoXeLsQyI1JJPflgsGhZDpw4+VSr1uWriet1d4xrdl5rkA/ -FVVdaIZ2NXg0+ewuEE8XkwjE6eSP7QObYBxEMnKR8AU9QhNjyTJv8xnCTqcmQiw3 -WhxQv2VNkO8Rc3ZwxJqpxbWwYc7f6kZVCXszB3jxumH0/tRKxuo7Oie4lu0lQHvY -hzxigb4B1loiWmqO1F03o6GnMQUjAioqIfrzpbM207Lncsp0Hr8EiNtrQK5FfLtD -MIf5se7fAdUKaS1yHwAX1IkCHAQQAQIABgUCTdZhngAKCRCDe+/5DNv5HN27EACU -qg38aLRSQicIrkcMhHXc+QI4Z0j4eKIIIvYdzSsVgU5HglDYSVWAAn7DphocOHVL -gC7leNvGgCmQT+nV8E0ABSNFOhWDpPSijQ1XUFgnvAfMjKspptlT2levwaV9O68o -ddC3aGcvXINqypHJqJHbnPXZozTLbtCEBccjemOZiLFP1B/ZHN47ZlafiZMe8uDd -Pzqssh6FPgu4mtyp990rfN6U8SikzcuovHw87CUMUhwRxJozMG104+NapXib06gp -HoykjwNRYA/NYBojyGZ06ACHRspTpom2Das/VczRpYhf6/nvqs5nqyrmV8SIYJBE -gLMl1+3CDdzgRhOS0qStuk+T5Be10HHio55TXs4YibB6DUb22tmeiLBTkpffj7Rf -W0alGSc8AImHDTf5KKuRoDbbf+z+3JyQcQNhIZs8nffwjBrXbe9tjQMuwMF2Rm1/ -yiXpIJzsiKCR5sjczm1x5kKKt+JBuKN7Pw3qqWfSo78C8f1xjdEhEEA8YlsFaeCC -WZ/nei5giVH2UTJmiDtermvitgLnk1Y1hOIEeTf8i+8nC2hXkh1P0Pmx/r1zt0UU -KVBd5md/Qderge3nCFB/ndrf+1eLBQ+6t5CUdkdpdvakr3o/OvLmVRTK0IT2ZzYA -EslY8lsUp2+V93vGoFx9o17mYB2Yno9qPnLTN0aosYkCHAQQAQIABgUCTdZkggAK -CRC145mfvu5nZK6fD/kBCoR2J8E5VY/mkNLnohgKwwvWQ3no1KE+L22Qld8Pc/2a -IBWJWMtPCUVX/EgszzoKQ8DpIOci8mfLEiI6CGI1fhcb2JWA6KcKFof9/H51ANZD -A6aZIEieGKi5VDlL7PyoFcxDp3KzajODuA50HFg2RzLkOdbD5ymxNZAhFxOGYrRy -SY5RP9fuKTnI6RDijDAzBuuSQOBid7NdmiY64yoBPmjN6miy1heiSgO8hDfrm9Gl -fw33XL1Vvyyo2Kar9UO09B4zSCIzUDvax8oq+GprKb68PKdrqDoPjN/IcYt+JW7q -zLbcfRTVpAm86jpkmPDBap/1ssddWXQ/BcQ+PQC8VcotYzuq3qS2dh/jGiSnpPjk -N6Klw5JN8ozxAHX3dSxFoDJMJoJkNTXjcnbJNLXZ6b+q2dDkieNsuYrBMAseTR9S -DSlneaUcEzHOYu3Cpd8f276Qa/cEH8KyswD0ulmMiZ+1gR8QKoXR38Ik6xKeaFYm -P07yOQrHXWKyabGevXHQ9zsfGPNmd1XvycnvEokYY2FPNkznWv6KyA92B/OA2o8e -qRPCuzIatCCM1+wX7jHWjON80F6mnWVOPssPeMBd0WysTBGs+Z8g4Bwji5cI3+TB -ej6qxYKElIdGZYN/QplbSi2wu/Yu2AFaWiL5FzifJAt/F2DplVpLr54GMYkmMYkC -HAQQAQgABgUCSnG99gAKCRDxppvkKcD/7mfYD/93h2spXZjcqVExbaJAso+mO+mW -FDI4VcgMrCbrTcPwoFfxbJnK4gYlTvG2pv4luLYyEpijWV4fYGg+TCIMuGppeObn -BB6r/RlkFpdRDPZ2cWFsiQr8908k4yOyr4lnmVIWtvp6q6X+5P1JuPho/uhzAM/8 -D7uiX4kT/koIyG0j5voyBv/9RYCSESP5L8uLSPiDChGwKGEsvQW3HE0XXWm+N+vV -YEN88vdV3OKqnab96ZokLt2sMWSI+e8EiHzU9vpnLMQRvHH5C4pdBFGzP1C3IbqX -vDVAXsbY9AQipasHltIo2pRyMhWi0gd3MLz6tItUYL1D3gHwiiH3BvScFQvzfiP5 -jJTSxi3ZJBClU04+N5kpIRDFruux/o+t9oNyThyFdWX/c2+WyWb9ipFe0cPyqbjY -KW8o0lKZYpzkLp4zRDYH7Brvqsiao8bbY98N0DWW5ML2Cdh0LFn2PrZds3fkSh0W -YLnNwdBKU0Ay4z+UgGQRnk6aS3TGndG51V90M647u683YMtkIfY4EhzSA1i/RTKZ -RRenWfPa3FT6xGsBkBkSM4mdLvTXRe5Av8ZlqKuRUHuVO+cPOff7GtaccxQZpfuJ -VORQ9Bm3KOSKhRtCQocsgOL6DtYUqhO/3CQSvSMjqtYZjFZDypT//6wjhLZDL8Ez -Y28qcYeOBrjc3HvNp4kCHAQQAQgABgUCSnMJ6gAKCRA9kIqz8Pv1HwC7D/45BQGw -e9KqjD9rlXJNdQfrjWbI0jf+2VTVAIEq2meP8wXWPNrsaVDPPc7LIYor9wiZduhj -LTGjGLdL2dGD4LgS2gFlZwpadbxor66ut422xjgOqBKTSFlvsTPFJ907gSGGlo8P -UU3448twJW5J4sgpJI5HslFvLeazc0NZ79mBdaBGK5VV+VNEYAiNEJMuojnZS8mP -28GAancXOdiPkYdOZkBFs0SZ0W5Se/lkRoSucqiUpirKitwfsz73VScJ6QP75OUT -cNOF4A3bhgAanmcyDQZNNL/nsasvJm5J8j/hJVARiVsKa+DFvJrS+jfyN7HbnI7p -PdWEmIhD4Y+p+OZ2hNRC3dfJFjf97UicbK0yUcsBb57RoUSQflnF1WnzqtyAfBgA -4UkoujgLnsHRScVLmB2Gw4pz1M2D7+MwS/U2ihxRoVpbtHK1uNMfBRWOB5ZCBxP6 -bf7BbRCZmAp009erCXQmur7FVVdlaraw29xXi+XuwInJ8Xq7SSEvdl66wVX8lYG9 -k45zPZuArrMwgseuLuoAGjfIEqROowO88YlecY4+BeEaJyFn8dSAYSR3HYWzY1GL -mokQCdRFpz7k1q/WlxTy1F/D57xFDsuGfYxXvDVhET+KJ64fazdRFHZxrHRcxYg5 -573XHCvyo390OR77I7LZ3L1G0bV5sOC2fJo52okCHAQQAQgABgUCSnRCbAAKCRDm -GQRd8qxymtLFEACUKVe1Be1kYBTNMITIBhnErBE2jeFBQCl2yHY+aqbkb0yt/t1A -o85TOotDU434XCaNRKwWh/dFzepoSfYW49CEEqOVzDXvNdZM2IS85qaG6lqf7XSy -3cNsjs2Z5ZVnwP/bh3E1THybcVtsxw9kcsZ1p4+fKTyzWHhIIVZG7gQI1Kaim0NW -oIoxAU2+czA1vXaEEp43ZdfJyKye6Pd9FthQbAO/pXj6GPtOzbbzIOAUEmBoiLwL -WOcPOzd+yqgVJz6w0hodDN+ZtXvoR7IlRiQshe8NYi0zOte8PeCLZfWHuVWIQ25f -XmsN/KAj5uc/CiJAc3yRwYcYjVSERyUq0jyyDzI3TsQBKD0HNMWkMoUkf0IH96ud -3WczAPGjMwmcju5AdI3MJ9kEm+GnsWm7nqTsrfzmJMDyGZWZq8VhT55H2RFqwzST -q/1wm9/OO+qU8+3fPGFW5a4tuOPtbE+vqLHE8xnOlQ3H/NMEbfjCoFHDNXbJM+tl -nRLJehLL/eqCLjrYz/mjsNeFvBaG6NVnIXM5TxqPlY6MiGTLUo3ZWieP37jwcJMG -xArEonZGxMEzAw7Bw53mMNU24i/ba7E7z3ipv162uEk2k5AbNfLxkYkiRyLKvdDS -ilwnu3JRRZP4mYXCxYHtCyMFCF4GGY0BI/4/dUnh//FlBJzGXIgsCAjhd4kCHAQQ -AQgABgUCSnR3zQAKCRDf9zjC0Wz7oswtEACSuwOI4CwQNTH0MMzZU70j/+XaJ1/l -/XUM7w1dO4pbltHLF2TXCSOMxTyON+0SEx1IbV3BojOYOeZEFW+N1JCNAb3ZAeea -a6ktmhVP0Fs0JJC5XTk7JhjSTCwhLjJZ7hu8deXY2UQ8xzm0FHofv3p9KmolvFup -NUP/GPdKB5BHFotDVEnocKfYBH0h3+BpyP7xKz1uE8+70d6Z6CLAOnPEzD5mhlm7 -r78srhXruB4Hx0pepYPM6OALr1Z5zHj1mmxYvYVEivE90jlo17T+MP4Bcgrle8TQ -zJ4/kBKVCoqgtHIml7NG4kCg5gA9lszdrB+jG52M+mg0Y4d3htgDrpbft2rF1Yu0 -meNQrdj4edPZU/wOp5qf1aKjdTtR1v9m1dyQSE6dBNooYw3hsq+4WLpk9Co4Lm1F -ikpe2NUZVZDVPz20yvZY/RQlz4PGIq0lTJRjztgNmLa0h7B5vxpjUve8NjCHmD86 -EqT9yNJ4GDXtJkZhJw8MYUkkNAvtyff3wVT343xwbA58lyYsNaFx4q2HF7jgzzH+ -W0TRb5nLMu6C0BUgKS1hvSRbhintKakBgLIGnOgY8Dizcxv5HgKFv3PTmJ3fonmI -O3UKyQXH05JwiRpI5oLvMm29zaMk5Il8X8cWzUUnBLrQBt+O+6QGHl+Wkrg51/M7 -nsUNjKToHrRr0okCHAQQAQgABgUCSnR4DQAKCRC6nHgGHd2MmxPmEACVGY4IogoV -B2Sh0YxGRo6AWYlm+xnkhEXvEbklU/QA1m6/jO306Y2ugmZWNddpHNM2iRZEDb0T -VuulfvJzMG1QFRlL6hrJF4UqyOVSg3t1wf4MTFT5Yqg+F9Ln0ghUPCzqyrSQP0V2 -PfeK2gl4CK37hZxVL5qD1YKbha5UUUtH+5RuVaH/Q8xnZgYFiwW6zZC7CDptDx7f -UgMoxI9R9N4gV60S3+HrO0tzSI7EktWmNIdZcAirqLFoouY4fov/9Bc1/omhC6Cf -OZE5w4QNvmy1b7uV5JFNujz3yjBWQnGYEM/J5VE3e/TJu7pSN1joaeWtMeTD2XBq -sPHvaZdGyrBQbswXQJ5zpBsrn2ts0QKjubxM9VhyeGTljUVnpZg7wosqkiuLcjvn -IARU5rSth8br5RxYFmoCIMBMef7ee1edCfEXyXaKXx8pm5Y7Tb8fpdxp88JWYYy9 -Kj7fiSJZ8A1/ZVpkN+53E5tYWOvv2+jhC10DLbc/oW+FMS5DS7r+myjd23x00Twi -7jak/80totBxUIhZoZ+YmMNzMgrP9x6CSvv3beCdB5E3XOdAGvrCT4Xe3UpbhY6q -cn5aWlKaY00CvMrdFHgIiymDG5zWTyiuJ3T8hXzb0rpkNF4gfLMlS3RmV/33eZv8 -aJkDfiqzDMpSZp25VLqN3cRHvD5Dl42Z9YkCHAQQAQgABgUCSnSKqgAKCRCi0+fP -UhNGtCVsD/9wzsPXN9wUG1ZwopSv7LRtHa7RnOp4nZcHvzAXhab2AUdmpLzAxpWF -nLneSHDPsOtqE4T3hOJEdEKotYSkbFP4tFNgB4v5l0CpoToEfVj/w0Hx5l+KG7oT -Twbq4n/xe+rnO9DoF1WeP3OXDzxc3esnseOdPJeXWrasziLUfbzHrV+7NBCQtkOW -C8rhoMv5ia7OtDjzhbKG8Xuiq2nwIN3YB0QGFZtynf7wKHG0oR1LlH7Q/Mui/X7n -dj8nfwbYbyrCNNcfR/BnYiM3oXVkyh3Py5NlDdgphLV6+GXuHaPgqRcazRRO0n7b -EG8VncjJyhm6/mW1jaG8gRNYAiOioxIHin7yo8jY1vRktL0MtpvZHtAPDTE4+fJE -ThbrbyPqzZRqkeLLOJvfsb9rntteIchfnzlgf4bbsiWhmpeJyN+cbXmAljlzbWR1 -jav5heTS2wPACuxPKtqFy1CHcsrxhiAHL+58IyQhSEPQzROmQLylPS3QQnR9Hov1 -A5aPA40ySLEvV6GRjyRi8ZNdo6TAbMCYVQK6vxv+syE+ulNudhtMuyuiM59GrlQt -F/mNQb2OIFLbsIG/NT02/uMbJXoP8AEEYLbfqyGf8UmS9/pWOnOJWQH+fQOl5aF9 -f//RItLVKiYaqq3mBJJgbns7pobcxGALX2sp10h/T7L2fTBnXREyG4kCHAQQAQgA -BgUCSnSc3gAKCRAzvhoKjC7Y/1x0D/46wn4nqx9/Un7lbB/9kika9W4lr9psqVjO -6NwqjhL9zWQpGykhKOx2w41/oVZtk7s2M2UnafQ+F8TrWBAcXuSqPRUU3MZmz3x7 -3lWTcC+eGw7XT13u5j97thtGtov0pDMZVTVVkb2yGo/3vDIaWRHzAjmqcgji1rR8 -SD2nZ7rBkNf+yLs8toEarnDs+fWLMAvHU8F5IAwF2mOnUAe6VUI9OyWEIAVyxhkv -XtmDtK7zHaY5b+gSX0dEJrTpWMfHXKtBz/Isr5BJIx6VDWtcXqPMPyO4YB2R92/b -4xiMO6XoZ4wXME09KBdQPaQTu5eQl08unr9Y8eiAx7eKxRutBntAR6gl2Q9lhIuc -YdA5+usdBEiJODzHTlHlO0jOgsmxottORWVv++nR9m6QNWjDdDGdz4yFD+PtzGgf -5c8vlhckhgFXnnGM7OB8nDdY5fjY57w5LBf2Mu9lg8vVH+7CYMhuRH1G3Ja6aFzw -EQrK7uZKbOl1ZThtTrVXZdBg2pJwtwzXxwMmTXrMBtzyIHGaVwz4EUImX9IQL0i/ -iiDxqX9mqCC9Kvy9+bOHVCLV4GcSjKWeyH1BB0PHQVtuRWJjLPSqYQTipWYFGuyD -fyHYHBqple+3CqSaSAraA+hGnYVF0HaOzAqq+XZTBWVYlnXZEUy4778+uv1Mok4t -Jc3bExQ+wYkCHAQQAQgABgUCSncPJQAKCRC5ESBTbYUSjQEjD/0Xh90KK1WknKRj -IKOXPSOR3Vn0eIUROmgeVQO07+ZzSLJEXF/ukfo4MWPaiKB3sVGGZT77fqiU2I6/ -T+0jFWY3+H/4X3OBoIzitwdQZIm/oKD+BKa8MdlXE3YQYgckZ6axTjNVtZTM74wv -oPJRkX7wjrgN9aLWYlLnpXnjxPwZnCla9MbMoR6du0Oa6gi/Kv0tzw/IBk09unwx -vmdw3KVtzL754quzCu8a9iETIrB+iNLIbVAoxqHQ9fCTeixMarF0IFh1P6d+uJ76 -jlLRJOErzmVfAmMPYt+kedQHsi3a2pM2ivTwx8RrO/xyh50MheJN5HXgy3orEEjJ -liTokzRwynld1y/w2BJlheocU01TXhDsLFBSneQyoPlTzgeLsfaKee3p3BcIvJem -kitAmc8e9jP6zY+ZDzViq2Nbt3VWMx1uBsZZFG3pWFSCWAv/QjzyBghmv+wlYKF5 -neJ55loicxXoBAkCqLVWxVBzXPBxeRTyr4gKo+37t3JhGN2T/sm3X3P2smJWywCi -7liolqVa0kh+/edIVyfk9YI4bv3PPcFh14WZjB5nMAX3Mvbe9rQGhGQLFfcuN0V5 -TtPR+s8VABQegw/0PcRpC6VLxvbgCNjyCiT37V5mE+GYwSZ2XoWWE/UAS9OmhYmB -KKy8FTTjIYMpsq9FiNd/6ldqHNe8+4kCHAQQAQgABgUCSndNYgAKCRAsfDFGwaAB -IWfbD/9IoyWhgwCh98ee+azIdatQCGqf36xzQYpX3AaINzVcfgRgSoTic+gEDeAp -ML9lYDLpo+qu1r91vLwL61ZTzJe3HonPnqFUPmjsBJw6FOFr4RfiUWOUJ+RBOUmS -TktmB9pMweiKTdgL8xVIhpPnzWB6Eh2IO4qhxSoxE+kiBKEXL9mtEUjhczgWHAyt -BIasbETsgmZzj7fFYudaXU0qTn8Q+AIyfft+CPyQGmY0YQDhlCbqAr5zRdWRXVJV -7CakA+5Gc5SZ+PDZNwyBlbQiXwGyCZ7NUaopm+i4MAQJlkHMlf+0+BqY5uooOJQd -nvV0NVyTG9LiwciuaNSzcXG9ezpUSJIXxmYF+zdD6O/QO3M1RpGfCUPbyBp+xWBg -tqwtrJUEPLZ6ykGS7nYrLekgD8Ozw5t26p4uCHfOYFf2QZ40F2GnE9b/v6+iypl0 -hLlSXQ5TYyAS7v0Iawx3GhuAWEWmCNPMKDIwBYV9aIZrG8aieDk8nDG13WU6h2z/ -WFyAwShWjKH7SZfcMGVeHTw6HRm1CVOaRksOIbJrN1qRtSmnypt8Ow7mqksGEvvh -x+u/O7Ik/7s162aT1JGD3qd6fp9oT3+8GNL5O6Pv6mVHrF00neBiSBKKmOzeQqVE -R0EAXxZu14d5tUQ4B56dfaTQ9NtHwGb3UzOYofpJmozAZRjDx4kCHAQQAQgABgUC -SndgygAKCRAMcFV7WgZRPmyaEACnKzfBD0V08CapxlpH2jR7JvwIyFQxIQ2jNHwQ -QGFpUDpAa31MYYD7Ix1yiEXlObCjl6XDFk89Qy/USgf2LS19XVnG6x/Ha84Y3VAL -lbS73sMGOXDHxbOq8cTX4LOcdCwe0fCc4A9qXok/HgE2Q5M/YLXqAR7UbzOErEg3 -q++4OGu4qLN947XvuxQML0OiCjAMfq64yRsDCpMvnArJH4/xgXWWFD5XdCugkKPV -6XRUbaeAsVYQGhurtaX3FGnc6j4jdBU86zC/0wBH6bONshqa3u4pmOtBmKiYGKoN -FZ0NtlrP4xjkr+hIvl4wu53uSQxUmAgZZJRIdrw/rJn8kshAeeetkYqQ79a9Qh0G -nq/Ygz0RulXr+kjtwdK09m8l2qpjHTdTRp+ILOqBnWu+WQXVQoPyVdRBHccrZdH8 -RWZB7YC+juGVyB9dhXLQ57xRm9gb+96OiSWREr//cny1EciyvNF6uzivA77qOwrL -/Eiz+/up6sD3H3D97+1zfBmI8HJurmRsbPSzQSNqMz4ZLCyg2XMDwZBWRyCF0vh1 -2hUWKvHpnYcwx9lmKiRAr3+qNA4Vxv/u5mb7tRDtmES5TzTw0SrzIfOT/fR1eE1G -fNccG5wWCi6xXU030XeqoInWH4/1bZYPp4siHxyuKLB1S8TRyp2itywg+Bn22zvl -ja2xrokCHAQQAQgABgUCSnmQvwAKCRCHL3AsTW4lqA8ID/9BQrAnQoh9dUDzuNUC -QOux76dTqWthwMYJia/mLxNyuNGxrXgI3p7jowxvPkGzOjpEpmdTRH0JcIec6xiD -aJ8k8RuEK7eHjBCSabhaShZGM+ddVGV/lwC9pR44L2AQ1Fy8AlEd8l9oB8iTb54o -DX/eryQHaGWoHLT62v10Wi46Lrv8MDcnTMo/E50c9iYbL7Lni6uGujENIgR4RZtB -6LCssGdazow6YUB+P0q2BwocvFOHa0BE0ULc/XEfNz50Dho12NBxYVMI5hz7285T -HVbOPkS5P3f2/dMb9UC9Qtmp4pCnuKZdMsRau82L0zzyAqZ0scbt3hp9lBgKwMs6 -z3kX9tuIS11d3ybEWzqfBrL5vgAsFdzRb0cMP9bP3Pff9B/hdfFuXAnm2kgtvnyd -7+6ublsMcamWEEzrkD04WuDbwOpusWDLct2swra7yurHfyCEO4uiBQzjbSqjWEkD -KZPTZrmvpOJPdZMZ0xaXSdra2fbMZDvijyQ5SOV0FXcuLfvXJ5n0swWV+mULJ+jF -aXmRcSWcibEleG5cSPh8L3ZY5UkQb8yVAdBU4uGqFM0LAa/oSguM8LtmMFlE6ubp -7HxaeRBg5btQARQ9DO9N/MCfeQWQUSvQJZFi41hskwsqmI6fgMnnmWhQ3BdeGfed -YzlLXm7RdeMGQi49Z+zHQGkeAYkCHAQQAQgABgUCSnwzjgAKCRC7OmgBhkmqBjen -D/96o0ws8DnxnYGdj4QxqhAdJyVoAvAkMpGAZ25Tv/91wQLLCWMA/xITQ9CtP3Jc -og9xvxfWA7YNT1OY7gA7qj4BT0yKnue/st4pl3lbYUDSjZ6QJS5R+jsNvr45infw -36ckJRSmQFtGK4cOhqyGQhF8EuNher5G7bkDtgdtk5WXLpiU7EPI6FB061UoYTOr -mCxFVOwvpygdZCXhPjf7iYj7Wr0DAef1ZQJLDJ1KJRBSP70jWFOyz5asQnsaeGrS -sQDC5129qrEFkk/3JP8rRJfrYkFAp3LR4BUcpca0gF/72aB7QFgHhLsiA2Ty96HO -+XNewHv/Xn7gaAEahZEaEWyckcMbdBrKVqzUio3zN9rm7SI7qWtfialm1T/QX1r9 -Y9bpUhm0q8M0aQhZadbwSMVHW0kbGu87I4Svl1w3OMexRYso6mT72wF0FHviqi7o -s9HR+0CdOA56B+cFB2sY3lYZBBAmIePg+C0sQWvTGFKDOYWSkhxudZnF2ykMB/Wv -nLT9hUULds48cWRfbC13STOEcJqYivkxpQMIybOI7YakxfDOttV0uH2vFIJ3of0C -StqymyPDdXNDv84ftNWs2SNujY15dBtebRwwfbFH/x0kWKtzsNjq6pV3BKeYNQP6 -/bMgusODja5zK4ZrkFliKlfqTHdVO9x5wuxisBOf2oV4H4kCHAQQAQgABgUCSn00 -4AAKCRBJw7+JJ1U9Lj9UD/9sVFQm8YeL3zi2k7/xzQTydM8y6OgS8K+VWdn2qvza -guzr1SCPe/8VLUGinoJY5AEjAoUUZ2r6lwuHqXTNpmh3v/obsR2/2IXLqZMhtAvC -5GCS9vmU4MBHXoXr1Y6+7wj8CeqrCAYQ7piHTmCqCkXmNKiXtqCI2POnK1JelTMl -uuhDrFSibRiVjKOsmWEr99M3BNVGoWZeadefTRtNVPQTVt92bvDs8G9kHNA1DAIp -2Sx07b1+ei5fApSFOzIdUMocEnq84aNyYXMfsWHgglkpHOlsKvWGQn4ep9tMq0Ue -WXDO8kfrkkfczhPe2XMhyTUgn/4zUB+x/Z08808EPRMfEhy0F3Mw4GcSFPqtd6Ba -tkFcamIEHQUb5iSdOhS5xm7wIjqfikp/KViNftxUwEP5ql1ZL4MHfNwFl3KU8nFU -Bqqz0lF6/few4ceRk0+V2ZYN9gbgWRN7yB9AfuH0UCuPA0NdDgNMJKi82r0B/oyB -X52k3Jd7Xa6jd9wPNkxhHQPmgWXMd2j+EUkBqHjYDGWBfddA9oJ4l32kv8K7NW8F -tEiu6DzfBrzVJXyH6DVTy+hgbaus2snxc+U3+X6Eg82ZHROBl28L/wqFbZtgvX2f -/I5hIT6zdxHoWWTVMnT0b08DmLdE3ZFHm4CVQnS1pSAVpTy3sb0llosY+9GCM9Ic -cIkCHAQQAQgABgUCSvGDjQAKCRBNJJ2bI+b8OuxSEADAA9h6dNjY9CM/9otDBErZ -d+DBwMDeW4dCYtqEl/WCivRz1vYmITGoZDUGNF/5k5Qnj7/yb2aBTxG3dMJWb56c -p3LPHb+K5eybmQV7v1eY9DpQ4/TiGc1+lDRe6f/jHc60FBfLqVnG8Et95BIYqXK3 -ZMR1aFmFyBWf2vwU1I5hDsiFLA4L7hZn8MNISiUs45ilvhyPaFNX9WFVGXJDjkxG -ZbG/L8NKvbgHqcK4bFSHiPNhVg1ngfBwp++gAYOR+tbeBFVupj9LARrjYY1XnJeU -LP3D6I2sAxXB+nbAmB5gjf81Q14jXK9Z3e+iE0FoSn1CQUDWxsO3wiZvIxJLNu/6 -teqf5v0tXKtMXEevKeaCwe04yLZfFFtjA+Higd67IVaE4zhKtgt9P4jBoLqfrN3Z -noAphGczeK6BtxT9pTfSPdqAASikauj4LRX8ezzYqNYS+lCtAl2808BrB/HB1OA/ -1cASoMopQP0aB5JwVFoeMnIzOPHBCr/c8HUhGTVHkzksGV//Kxj+iQs05TyGbVkh -jSOYTrE8iLblAiWI67wkRFMnyXIXXfcn8/eErpvqXujHhW3W4EYiRXfxu+1KktUL -F/Jl6cxbI6oEBUtzRViyBhdh4xhhbIzal2EsWuuOLtwb4IY48jFhTJSriLj2WINV -StXwumQezWcfIqb04hq5wIkCHAQQAQgABgUCS1wUUwAKCRAWKB8uAHyY0UotD/9Y -8SymzGy3DBbpSmqyHVsnhhKH+e7Gxe1s83tb5xqb55x+eg9jJ6xcPSpnpx8UdXx5 -MSvI0M2ISPwDfuORvXypCSCwAL/zY0/2gDiWcZgYvDxncm6M9t8cvGUbzqMbgCi8 -Pj4/VWhXeLpi75WAN1HhJnq52npylM/TsZvEdQr4WYR1/5WEtBVHtaWxUnUpdDFd -achHBkbtAXICfQFGc1F6+J6OHkkJSaDU4IlAIOuMwLX6rpKPmAma0T4c9e6q6w7M -S5ZwIwyr03gwINI3EdBz2iYeRu8mY0aUXMvzEXdpQRoJO5Gi6qWCcTOktMtLSBFA -SUznq4NLSTfgGuDDPjO4D+11qy1CeZ2cqrARt+ZPL99ZA+2KW3t2XfTuQPuxJAdV -CDgzZ0gX+TxDZ1ENrVMC8Zo1sig6/Jc/WeyXEiKL/oui25DgbuvRisItuy+/uBr0 -v01cOooB0IZNpK6Iqf3VxjIDDZUdsf7p5cmoTaPUuW0bTSQa4lDFoChZW8/pXtve -AEEN8qAkW0OkLVKWtaTLLyZVf0/3EPHzG89fL17iCGjr0GT623LnS3WBNmoT8/P/ -SLiWKsn1ZoQXjy1G6gno7hHCfcL39qacPUzNVzdPkRJWMKQGlBCWdXz6tdAeNTiX -lP6Vkt9b5EBiOai3I+P0y45hYSsQiGc+ewyn/DgS9YkCHAQQAQgABgUCS11JdwAK -CRBQrnCFxr328M7OD/4tQSfN9QCDeCxLYjDxR8PZrgWDVFS1rtQ14UPlX5jtnJUy -Eo5itD4dlYIR64QpheYfPBoshysVzhGKnbW7RYPcGIpbNAuD9x7MJbRdEjIjKbQ+ -tcCFZwGO/RNlBEJM01sIYdSiYtOuqm1vH4EQopPxsk5AQnx3YLsxHCb8XeAclIXn -kJd5anJZH4IVbFqKGVyln2GUQdFJnuG3S2phYWrRdcn2yNWR8by4hqhWAaFPJ5zk -4u+VLms+naew5rv/SPNMjh+7S7gDzocOHA+gYblyzE4zR1XnGxAwmwG6AIc2H+ur -CL0B/21gcwLzjX/MLJXNNRFUlRxc3FxjWQn1OqSPK+yAdYq4cAVhJlamy7kJaqsK -EJL52fvEFGLH4wegSil1xKK6R5h8o7x+bYbHNMUwO8/JG0MSHuxqOf/pBsbRJidv -7+y44W8IP+vjbAQtp2eWP/6I4Vr8SncQtpOYPE7/7Cu05o2BrfQdMsAP4TFZub41 -ewKMKLuHnCwIhbwaa33aVCUcqZsTGdnbHF9IIqnwRtx2LnKAUcRmjo2emKLTsdKc -w74Bo96m3B4XP7mond1HuMswOFwd0VMqLhfnsjZxsIZsvX3RPfEKApHyeOq0t4Oe -aZjWa0/t/eObYmMQr87i1VL11ZajdGuxDQIGNXq8PDtcUdMcZofsEPXoz6Zj+YkC -HAQQAQgABgUCTGISIQAKCRDlYr2UvwNEXnH3D/oDENRYaTZTigeRlWg2sFZkqU+N -bGWooJ3+upz5nIPo+6ine5Q7ITlftUTqQsrgC7m3QKrTpL+wCqG62gAHz5J3mJta -T/oQtdUOBHIhZ+AuflE0fEq3jYfKDYG0Gx9hPgdCs49qU4BT5pw03X+EToL9WvyN -NMhrIneidWv74UhVgd0bVPAdLjFRbVpGhTIbXpnzazdjVh/YuWRKzB8YwmanyCbr -PjyRnLWAjdDtPL5jc+WTDMRSPkRwQmQO6qvl1xiBL8a6fTdLKg7RyglXa27bYrhC -MCvRtMoOry0MqPuYYh2T48AIoAEqbAhgOyZGNDpjeFIGVGFvL+tae2+SUhKJK6Zm -1x+rcVXJ1+dvQ81fkgXHurOPZp70oeZdiaL1xhyh7N8388KDozOxxqfzIpuTCtzE -ajrmU9W5xL7eypT9cJy0ACFChgIyehs/gKmUdanf6BP2Gp3Yy7r76Spa6j7aq2fX -VXyITOXnYspEIHGz/Wf9tJgSNeLCyGqh/ObmQpTbzdNs1yraq69BS4fi7RoftBo2 -5+rtjvm+f/suQ0GxfNUVhxAOTb1jHNHDF+LeTe90Td0dBhtb87X+cep5KmMFZTUa -zh2H6oO7oxgrxPqVAPFF+Cg3XNTZPsI9Y9UWVjMQ/zGXHoqyttEi7iS61GWHJQUv -edhr7Py8xfv7OGcFpokCHAQQAQoABgUCSnWiSgAKCRD2bj5Bn4T03m9bD/wM/FRg -kPtFZtzyJohexCQC2wc76OmcsEK+VqMVZjTgQDVVAdpP8+PQfrAb/zK/0aiJYVn8 -RR5UpARtxkFss3HqXI1XSq/uvY8emmZcqj5/taGba8WHmoNjQyDuTYz+39DRfmm+ -qTI9Jnjpg7BL41esT/s0eDReAfqP1T6TOadiN8LkEkiMZEsemz/exVXUvqQMLZUF -+LHkl1I0nw7eck4NdlwnWpS7KMRWVgu+uODP08/iJfzawiL9flIHpPKcC9CXuZTv -N4LYttF+f9PtpDEai73JBo9u3tBMiqfgx0dc+shyBlkTg9vG04z4giyW4c03PpKG -MLiUnGc3SlbAc8bNSqGE6Y+GFWu7Yzbc70viictSyfXzd+JrBlhfpzCdwC19Iunp -TozwKjN9xd/QMM2Jg3U4mdbKnrI40Vn1qLPWBARoM2WFtyAB2+xfGwUIEjUpD94t -Q0eEKeEGPRNfgqbKxMhOHk6C2Rnh29b6zsdwrG1P/sqjYSJl/8ImzKFCYwPJt0Ci -3YsFeMYJIW1bR8snklNtEvilJddEDm3zeiZzIF1Pp7VnG47cJJP/ptRQVH8UhHFI -qXL9fydO62l8f2a/Tjh77iZnSvXIKk8wuQqmv6fo9B/bFMjQUXSLwT/C3/C0okJH -TowiFkVigS2Nc9hA/zCORrwH9K2fGFWEp/CF5okCHAQQAQoABgUCSoIQgQAKCRAT -bpzxe100LbQ0EACW28sh13FoGgnNkS3+3K0zVaeXG9RPbi5lDfrUgT29FUnxC2Jo -FK4+2K1712IKqkz/b/g0U8KO9oyMqu9bQxs45ztxm1hSTXxldTPhyeZ3S4u4Jm73 -Y9WIxAIH9wTmf2lfYUb8kqugb3PlZT59+vffHW9UP8nQS5ZWzya5SiuD08w2be06 -3LAILKPess0p64+GNet2X3G+xvz23lB4ggWffL0FjYPYk0bbkWV9GAGRWSVNhfBX -UJ+JspN5m2O+OlaB691UwZIlgWHMsYHktgfTOh/wP8QwAl450NaqhXdGuapcaN93 -8bZa1vxgToXQxVrbybcl9b6RRl4oteUtosIeA9qSrPI2SoblUWYuBS3ObLl+9l+u -OwLhYndv1kexip0cg8vfqKQoPT25cgEv1sfy6VQYqDswnfDpBQK4MCL9VkwOXT43 -MQUTn7ObU658uLKe2gmUM7mdlG2iMVfoGq/rdTWa5lYMAc2tP797WQi3zJcVDbaU -HSg3bHnTnBUCbaUanWR9185xvM9RcfPspwj7VautqIvv+TWXewKkOknJD85I6VpW -ROpI4oO82Zp03KDHwnWBAVAyv8jRXM5KUT/LI9GfzV6OUiid9/kAGZ8Yvv1cROEB -wym4jjsSNwf984an+2SisToOrn+yLo2c+ajQsgbePyNGqHh5k+fLndv4WokCHAQQ -AQoABgUCSols1gAKCRCcJ7MTQrdRHdNfEAChFrYL1jP9muMTVx7Q30p6z7i9Yb6J -T0ZMv62GXqNJp1x2LGxRKQ9xQeAHHo4to0+4k3qDvg/XMeCZs7xxvs5i9RVcNSeI -1QJJuYVl0GfP/j3bu/cPfYPPSl4DpGCu9mk0/hK/l2oe5Po6dSYig0DfELNef/YA -rYdCWK2hKtqYaUEGVnqyydZgxUwkxkBn+Vg/AaBdu3VTmPKMtM4tZUyJ3DjbNlvN -9pCstCD3tX6ywE5SMVWPDO0ukWNkKifyK+bsKvYtm4EuXtGr1czfn91pTTO8kUTt -lCGRFkHC98UN09xXwYfHpmqZmue9jWAlWBi7ghoMuL5/uaCa+3sNP6+FV5rgWXSo -en2WlTDhh/Vk/Mp9Ub2XoEZDCj2d4NNs3YYKiwD5El8SwE2TnER9zjIJ9VDlBU0T -L7AtLCazirchFdjb10j6Hj1K9gKj3bUe28CzmNY8hUhymSSJWyHA0AQyyHgR3AkB -qIsyqMKMhrkUOFoZ6biE6n5IOCJRYQwqmTgFZ4//FYwor5i40NQclzhMYWDCh7Jy -3z+/Xy7i05KfvhMExOifTovuvkcTeeQ3rVBjx2O3dVWcReHIOx/1NI2mDIEqwpux -hbhW5nHNfqeAgPNei0o57P1LzU6oC7FhblD+biEkNM4MKbpJ2JACTOJnfylqEsOm -+/YBFgBmyycVg4kCHAQQAQoABgUCSrT+BwAKCRAyJH+7QK0fpnvXD/98hChdeNN9 -DE/h/xhjq0MiRvwFA2d2k0WP8Mttv6I9tu8nrRdKOT0beJ9+EwRYmmUyXRzykTtL -oq3QeEjIL+ueAuuQbviGtS4n8WLv3OvQZF3ZDEZIUdm+OKgxjexJHrVD9iLL5whL -FTDUC+GJ/sCuXi1P/WSAoAwR2fEKwTl9yu1a4QrMfLMK03NGzr/PGc06OneQfpDZ -xzwgeQWnibXvlzW6AclUndUSHNft2OL/XU5Wtg7nAj33KXgiMrBQ1jJ0Ysa288DY -e7949JS97rFFg4INEa3ccUOCBWWXVMfsGmvKnCJQqhYsXWktwWx4r//VxBqK3y3y -CzZyoeW/zNd2CYwanek/5Aua9tTHVjJ8h/jlT7RxxJg43bvAA6jSPlP5Kan5d6wt -iSHN+EqmTM2LYt7ea0cxOSbn7Br8pD6XtT8jb1oiOfaoJnnWcfuFK0ljwcsw7iic -pv8kSXsmxTyyAbwrj2kswfhwxJv7jx7deDnywigsdq590/M49yXrRCTk3qiEY4eF -7kA+Y5X5Ot88CAAfpAvJ2fVMSxXgFQzL5KHL1TIhpIY6AvGdqRmK8FXjX9+jKS0y -Ir4fC3ren/UbOOBzGgznoRKDE4yEhGyxApaFFaVzK42E3QYn5blcd5ZYYVKK5aHM -0cHCWlt0kY/TLQIRHlyUSahb879UpRyTr4kCHAQQAQoABgUCS2RgTAAKCRCMv5oy -KGGnkBxkD/9S59tSTeI8+npPQgRhvwzelzp/tExWSrAtqRbskZUJnmKtDpD2UzNm -RXFQcgEwcvvQNxmKHFvYEpWx2pULZJo1DceJP75ERAV0Wz13WBtFkXRSTXpyt14d -UPTb9PZmjMrHMfv2JGvAsdBLANPxN4a1SbsYBjvXAFTNViLN3Zm1sAlGJek481L1 -SAwDD/esytybClSomJh79ciEVNViloDr1smhYbJxNiRualvOwbUBu0opJKMT5rs+ -1aec8wDFtUj/OWIdPEkx+JnKFsVd3wqANTLeT33qbi+eB4RUM4ROCn8GawZQOw+c -asZk3cBc6suUnXBpK6ykWf/oUwyqLshfsS2PA+DyHODcBgv3ZPWdlxVbCPUfwiWu -Qsb3xgILoiFDQLZu8yUb3Ud/XCtxaZ/pRuYKYNgzj6aJGppOY6mdSVzKi02LtgO4 -TIePqjbCks+JMThkr1FRgj0MAKmekHUc31eO9P/wC9zu55H36iO1seKKhSHaBLeQ -tmk77d+RQhwDfljSkc3Y6gz8Ph/pS03Jh7OUfqu8q1J3JnE1Zu6jQCw7hdolKVUZ -z4uJjDNc2m4dLhCwC8KThTKE2bNoB32PdtLmdBdMmD4wRxyCuYkTreH/wNdkfzzU -E8nOK8UmCVN+e8//6DhWSRkbXg6cGpdGRMDeNLvA+a58LMSKIfoqrIkCHAQSAQgA -BgUCSneGuwAKCRDJENkiJRLjx9CiD/93qYjcX5JOSQ8ExUGCxCj8Yky2e8IvOkBt -F1+pht/lrCFXZ65W73yAdVU97hJhWkrTTcErdOibBZUqJCt/CqW7QtXJ3dCwYSBK -N1C+FIvuNAdd9BCy4mAwbwpkYA5enCzKSeMYvzaJJ5GlEMZohq1CMPsUKd0bKeGz -YNVnNzNg54XX9C2FMb7UmqmgohfYsF/uejgeImPF2C4q1TSYF7TCW8pmDvbggySA -SzWOgS5aM4i+QZUExNjeCO1usrvVKXbTZv72Zuy9kVqeq/VziKya4dNhQA+jIbtP -pmNA3pKxMeH5lD78NqkRdZNpRKb4ZomqKtdAkNcxakEKAMIatcrD+PdNGoHZVLx5 -ZrkTK1uIqKEQVp6KgTgTjrhy8bsnKWHq/sj+EURscGBC3QtulEU2agdWbzw6E0NP -1J1uMdI7MZAK3jVsJND8a2UcSUs0kSiHm6FBKIIZBuhTUI58ktlaPMCVQENSMmCT -Dnk04GBSkrOj0db+rihmN/1vtCG9cUkFKmrp7bHPwclj8Aj/BihSmONkuMzgEE5m -EU1IsZeJo0obyskhy7apSvEj/mo5BggauFX7NIKFqgDYI/Bag+ulvg49dChWmUxb -IzQMHX8w1NKqTI3EHq3Q+3FzXFVkyP42+zLXkfnVDvoL9YittM05rZ5V9kXuP8Wc -MKL5iN1D04kCHAQSAQoABgUCSnnC1QAKCRD1NqpwER1XFmc1D/oDphJdnGfGbt/b -+6ehf9keu2vkK/RWWdoDAja9yFJ9aZeEpap+YHGIBZgKhFv8GstoA2HnCw/NtjY+ -8YPkyFT381VDUCFI+QK9pgPWTYnfDYPtXctFqbCSWJf7+hOq3+HpwanWgtZGbFuq -vKUJFdz5Y0cmmdJP5ihL0UQprTWv5yfMajWXwiyXwq9bASC55NaDnXRrTe2/Nd67 -DyFcD9LWaxYRYgyOEAHx65C4ET9STqnilwErLQyH/9fe6jTRij3fttteJGoKBFww -fVzZHK0E1hdqGb1pD4zvOMlT3bSNhll8bP+qWKjsk6Rm2Ow3xuxfg3hUW/92jTFu -bRto+7hSU/acGhI7bQnyWvRusbxljyGObrC0d3RnhSN2VPQKO7yHLYtUqpl0hkXX -PwoinrWLmPMgVYtvsWhiLW2fpDgwJxtJDRpfJDgmL/CIdQlL56Fy5wii2CkH2EQH -FvvMY2XsMhYmhhS2cpDfNHqUxWKm4FfRmCcNF1gYo0ZtAOMv7JvH+J1tf6jLynRs -cud7oT/IFsX72AcDlQR4mg8YKpLKSrOQ3a2Mc/5Dnw18sHnE0mZYqNYmU0VvtfMp -DdqProCVyRRjpbqNfHhGek+b7lCLDEgF6TIfE+HJe9jfCZYiErOc8RCyYjTtsRQM -XcrOz7+qES4e9Os8M6y4ksrW6n0/O4kCHAQTAQIABgUCSnFm6QAKCRDGh181Qc79 -4GNMD/0Rd8k0uQnd2Pe68zQCe9MAFvcdNbgBJib2Nq3BxXEnTVpCkyc68q/uO56Y -QtaT5EOrhj7GOMGmXySR2kuo0ebzDXH5Z1UF6KERkCCJTxdhSnMdBkwCPMzWjVEU -niX4LbIcUhSSL67oNZCAcQ2SLd7sQTe0qM0ptFPoiYt05MUaG1eOkEUXFggSpDh6 -orkdYqyk9akMSBcQHifwlt2XaqcCLT6GJPQHefhupuTr9vPuFgwIw29cx8jAxikR -hqDW72h25tI9m2cmrxtg8lQrA5LMGme2Q4Ify6WoMWP3+gk+2iMePnPXpQ6x7qZN -6/heWHIItQR52obonktBuT5+N3hFjhWmC6mxbNi3/RhZA8/J9GM2FMImgR+piBM0 -CvDda+wc+I27KHGE9CyXFuYL40zSpwq1AuxGNSwhqrIIxuZSYBCP5OFjxXToKjwj -JbLbXIbe7JG+m/3O5vrK33YqV/oiSLg0YpNF4TUi6A5gkgR7Sf+1+3bNRn6LhWdb -987SHT6ALvDdCHAbApeZPvIr+7uTBm5ykeD4awJWxjh3R3GkqMyyAxNAVhAWfoe5 -7VLrOsCwnYT+sOZypZkOUpYXOioQPcCwj/Xwl7rFOfNh1oJ7kEuVOv51C08SSlap -r/Kud8o8dPgwvzwP1/c3jvuSJoxAMiA7WZKFs1Y2q9cai3ME8IkCHAQTAQIABgUC -SnSFLwAKCRDNSyrzoKCqqgKmEADDEp22BMihbhgGbZgr+u8hzFIhFzN8WIilBFkC -Zt1P4YD0btFjR7VAgeBVzzfN27if6UQOO/DMqud4LVOxw6JuDmrv5qloRihHUZse -Aotb60uUdFn80HT1wL0SIjEI++2rPMq0JQ/9BwAzmMY7TKDnkjvQrk81O7bDqDwK -r2B7qVmC3xFeqagvnMTiJ4/hLwgJQnUKYF/6zxMdeTa1ccOaFKNH7srC6foOGtHX -tIdZ1/4Jm208brg4jw6n5fLN1oSzUfjRkuXb7YNCOdosKeV73MyiKkGvwm9N71hK -JgmPGNUxbXKAO0BACbMJfeGy56xWeOlFNCt9OMZfpW48V1BRsx460By3UqNJibtB -EmfUGZD1kg3qD5f+NOPHoRPGdG5RctePk0ROA1DZ4r8U3Al1f5tc9LOEqs7XEx9w -+wwsTRHEKW+TVKwCBndg11NZ5Wjq6DS+0z1fJWItMpXc7F2uXI75n71EJ6pk8mpN -H9cAgrbmmtkYNkpA/dkLBDp9X3f6gco/xTIMlIv5t9yDjoqMSJoLxy15xVj+5L2X -8o3TP87r+auR9WqDu1IFumMKKjXew1ykPgJ7+p2bzbL/ie26b5nbAk+kqfGzZEZO -i3uxBbwkVhM1FMUP4eFaS/5H1fNMBWD/KVojG5Qx5EjsSxOr2mVQdSn9OwqHqRK9 -fVYIpokCHAQTAQgABgUCSnTJ0wAKCRBYeXlXNEJoTtu7EACPsjhcySqo4xnjBhrJ -uoH5ZagvOTWmnGGDOI+KmGCRdCk6d8IpHKW4YQ+z1MKLm252I/KwwoakbYEDYOgE -vPaX9bZ4fQOsIL/7XW3KhX6Wmfi1dqBWXqgmRewxSlden1Bfxbii5bw5JeoTMkR9 -3INbD3Vk6x2B/Sr0EZ3q//XmjRd5q1o8PaLZ0Its5oJpXKL7p3r+3oRP4/Fyf0MF -WZsfLfUCY1xamuuxqtQcIqqA42cJcRyubLb7gCjW0Lp2FtxnTYuXrXmRV1RN+B7t -fgNg3HnU2GT5mcqQnW55IQrPg3fv3c7en4bNKikWP+PBjXUsWYYKhnhfr+MCsoin -5P2/pdEoKEHp0LdD6X/zvURMKWuTb7W9r3LNO7nT/TgmnMlCL8Qx5eTQ//Un7X7r -I0DjQhaZPJ/W02MkV8lT+mKePDDnX9ppYwCySOqgouRrnGUG2ULpmnkFBT5TO6RO -31x3sh31nkqc+Bfg7JDEFKuAcEKnyB4ggZZQq4/9/GcfyAGkPCqXHR/08+D0D7xT -FD1ei/4Vf2ohWyUJbhH8vFgLnijrTBnf5LeVzSU2Xl0m3WfBDq72ZQeWWS4BzLde -77Pz62t3AdxSV4GcTqSVJOboUV1eS+Pu4deRDroMHOG6gmHrfrUNqw+AGScc6RnG -CvvzCEmgih4KFLs+ZwKyU8D3VIkCHAQTAQoABgUCSn2M3QAKCRDNSyrzoKCqqmD9 -D/wIKZk4bLe9KSzUI7AoWAvtbpn/h2OjXBFh4zn7YHiDLPKdUyEN26Sp44RXVwtQ -UKsy5DgJ04hYWSKGxaZ0TnUR7fxMtPaT/qvZPUbQdexiWngWVT464LfP2QeQASH5 -HI4TIjfW4TS2WsJNjRhbcZJtDXDDPdveJPeZPfuICtJ2PXoSyovkob7/FL4xr3Y4 -DeStQgr5+CX7dPBh1T6ONp8XfesWi8fnW5EBX9W6zEoYQ3iLFpiaFUQJofnG/xKL -0rbRje3gtCRJkJ9ht0yt6tdVgvNQDHFaZej2O+0k8BTtKhXqicZqvSjchAgwB5qh -xn/v9FibVQlA5iJZcdQa/Sa3BgTPj6OrUVL3Np0ZKb7SVyX/UrPY3U7djUTxDYvl -seNvtIlPQkpmdovHGvE/dj3bvLj75NAMvk9a/hs3gQjfkO6Ba0d81ll9DQ5tA6Hl -vvovX5RbTONnQ+Rhl+vQgZiwJZp9nBoisZO+4MsqcYIMu5jTU4D1Qit+SEwOwmWK -YBtcc+ZBHPKS3D9koQvstIvv22OafQgC2qbqVNTZUWGMzJyqJY1HZ3j0Us7te7rg -h//tI2D5QGI+HnmBrS01JvwL1LH7OO6EjXZGipdA5bfNGEeJmq3gJEvbyb16OA25 -zcAk2k92hj+bIPD1ujT4orheusoKWPxircX/zlMD7uYxvYkCNwQTAQgAIQUCSlDC -wAIbAwULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRAAgG8r1ymkV7HJD/9ADhUR -o5ibTBgICwVUg+MHV9pwx5iV5yBSVKQODNicf1DXmS00YRAbJTbGPglaP20+D4V6 -w/LbLE/Sjz1f8soTkbyoHWv7Kd1dQXnZk4dUX/W53KrrZSMl2bqQ8/6Fg00fNX8U -Yahsw1FlEVcy3u2kFUtKR5Aeqwip1hwBKJ1T7LwDgDhWFyFm9dQNJ+WqPQqM69iG -XoFk8jP7zRjoqe+CSFw0BepgHJEJ6NTmj6GOkFXuLRHQP6M5b2h8Dxn7SsIFVgfR -EvgqwbbMwLBvWM0gC/OR2xI2ITu3qLT9kxWbyysc7K+qijPBOfZGMi1rrbJxCkr+ -4LLbq8aN5wElf7BtTWMr8ZpuOpEV+bPvMZw55dLE2NpBcAA7iVj/TCDsZpt3l90Z -A7vUA2rFgCwzm6iXwG0kiMLAcY3cBAMkPealLG0K9aEHy/JVvq6iTchtpHImiBWy -E3GsODukDjOteZHspfVgDHvU4ealm8aDT4jDAShFzVrT3k7Qm+GYK3kxHWQ+xMog -TSnKe81E8HtcSrw+w0s/i4Bcjv7fLrPXdGqLUU6jlrr33VfCKwZVSRliMKV1OFKe -uEh29+AWxHvLz0+LHM3CUKiqRCSnGoOQJ19CZTdTX1emV5ZduoptQ7KeKcYuastB -pJSC7QeAgGC+wrshNYuyz9NssFY9nrCkP2Kaq4kCOgQTAQgAJAIbAwIeAQIXgAUL -CQgHAwUVCgkICwUWAgMBAAUCSlDyjwIZAQAKCRAAgG8r1ymkV9UwD/9uiY878ExD -34+zNCOwf+G3w2LiFkzy4VKjuGyCMcwXyRI0e8SpVsdtsegFL+MgaGEorbtZbBIe -E7iQZgRMvMz8GdbgINBHRsTzLh7MqlgiI8qI9JEY4RE+pr4kNeJJQtrwVpRrT7lt -RqgRYmta1kZMYyQaOdJy1z9mEJDyn2Vb1OvTERtue0g2qLl5EZWXq81MF7BLjeO3 -uqzYNeqLNjKXcVwURFpsUA+/CZRQjsEDHojzst9ZjJ/wYQ5qt61ffaJAzzQNaEOl -p0yV6IFqDc41cT1XyrNEoa8rl4CMtL6gwtfRuD0aMg19FgH8u4y0WRiTG8bM52/w -/BXi/cvueXURi9AIMR/rRaZDQdhlf/NRMpKv9T9nVaa7TebmjTMOQydfsiY+cPLm -dnWJRMgfG4rhgVkWnfuU28TXpajDM7E9JveIDBU2mKOXHRt0HpuOkBCB0mQWdlsR -eWEqrVv50yfE28CT8N04U946qDeDp0FtPy1P2XOOiOuK5abH/EnwBik5sdxMDgsi -gemSxq5KKezm3En51GLwHPUaA68WJFa0DE4hA90zM71D+fgUdNiCpiCth8l/pQj7 -XalabofPvU9cP9JM2Qj8qvgzyBijzWkkTP+KcJ5iFFA9cAmdJqFmvYyp6xwHigfQ -oG+uI6YUKE7mqUdGKgqF11j87wHZE6U4+4hGBBARAgAGBQJPhesgAAoJEFbn/4oo -QMcIhzgAoO5gJIpF+vJ2uQjGAIkLRLji+x8aAJkBS6AEk5UuFBIZfx+hgN3LS8KD -eIhGBBARAgAGBQJPhesgAAoJEGNC8uy8Wva5hzgAn1MCUV6qYBw90WOWR78CiaFu -SPKHAJ0XNX4n5T8Z/I9ALvLFBqpHqQMSF4kBHAQQAQIABgUCT4XrIAAKCRBn53Lw -g1Hgr0z6CAC00LhelhcFyhE3KJ2avN340wmABqoTk3WWxOEaqM7W5NyJq7i8dGkE -cJIZyXSA6HOm+z/UefX10ra1Q0wzFUE+iT+H5Y4rE/F+UvDDyuNCp+VIeNv9dFBp -8u8vz45X2ecwiQOw41UPK4v5sHMbNHzG7/QAbkM41FrLghjpSrXGpYqcfeE3hhnv -YiimoXMom/1aYX8ZY/49aqFpNhDBUDYNcI+hQOZ+3XyvbkoGlNC2GHnzBv4wj7aH -9JNlrUWoSLk7hvUmVzXKt+rR20q6KdsagnU0xoU10FuS1g84zgg43MX9n0gPSHmU -lK6233VJeoUZAwiRK6II+iPPxPcasX4biQEcBBABAgAGBQJPhesgAAoJEKjOKKYB -DW86TPoH/0XKQz7wOlyOHyLWdo9ks3W5RbuWZVIPhvNC74yYpeIfkgEwlSAJQZ9J -qeQmsj8ZqNAuZ9KV0MFM7kPYL2qk9VmgwW1KkokkQJU0EvLXTzNoKNLNLNjJh0tQ -hI8xzsArSHLfXAISO21SzD1Hso7zSjZTX2Jx3YBs0HFmHdpeBUJpxuAucZRotkkz -ZBsFUcpbTN7iEVpoKEZe5f5bmBjjI5KEXqNlh9FILTMopgJiBQOxfPjXWeyij9i2 -v10Dvd+JroY8IxboCgbpJ/Uu1rpmLH6XsVQN8nnqLbgaZG4rK3HK5BUaGX5hmU7C -EJN3hPib4V7yZS8wVnXO+5jr+ZLWCNCJARwEEAEIAAYFAlAgPLwACgkQJgc+/NrF -duYK5Af/bofsxuJNEh0tH6IScKjNyu0Ao1qW1ueGQvGryHvQL7GDNvyMyxVA+W8z -KCO2PvMWFIgXjhzdKbcboR02GehTdZY2Gv5JbbzJUVd+d1nVuVbVQvyZ0AyH4Fp7 -4QlcaerRsYtEL7EF/CGIxFud4ACwQqZtNgumG3CzmE586rqbhfC1IjDmY+JJ4pek -aXonnQOqHMiCdJuKtCKjFcnJHq3Y28zu65IZjw0PyMH7bMudBylxMgro1Jj6VQfs -HjGDdWUH60Qv3JsPOXmlmfIzx33xWAAaf1xyxR7btwqolWSPqgKhh8ilnect3AxZ -EdRHVtg9VLFFsdl8pyQlVV/C1WHkXYhGBBARAgAGBQJQIERQAAoJEJtG8fsIj2uM -ygcAn2s2rTo5WqczmiPe6unU6WmrtwbBAKCldvxu7nkZJa7ruH7f4L9LlTFoDYkC -HAQQAQIABgUCUCBEbwAKCRBLzQVnwpbQXSvOD/9bN/iKNK1LqttSphnAWdSBwQ7L -KA5eTIGFj74wTTgncLktR+mvrdGC1fpfl/dFLKrvUvA7SVN/VnaABqIWuYDufmrW -+iBIHKvX0yObJvvIJGe9l3xbp5h2kQmTgQs7vXg959UdTDvMhsKi5yeKrUHppQ3n -1cEhN8UX6+044ZgKil/GPOHxoiUDsGYSMk8kddlMRJdWTlJjBT45TMGRJqUudKhO -1ziW0IjraaVaXGnilroQKm8X2R9s3x3ov8CaK9UdOdIXwnwZ9X0fcHWKMjVEyS3T -qegaq7LkpCcZ3JCdFDzbjuWNve363/RnAj9fKpC/skGnxj6RuFEGcAtLAZSWhfTd -YGth7bdBaH6eplLqnzmpKlEdpk6aHrQpz/4g17X6NpW3PhwWdg4zHNl8OjDiA/9n -lUxWwVzRFLYBzcHEDvQ5sZDEmZvAKN7OY5/Epu2APzbqOFllsmHAbefWxZaEsoSy -L1jIB8a60Llv4mqqVMewIXxlsD8lwZVGiOtNXGEPAprWGhuTGrcvPp52I8nknoBA -3AQRsDDeuOR4ZZWrRth3lB/cjN5zk/c+VqQiDUfF1yIxxUKVgmidkmtVEbdS1MLl -77/sKrVnSBV/NT7GEWy6grSCHLJ+BkUMFqED5E5aPWsbkWBPYEsQOpnKMkMT7FeP -BzPaorSvv/zJwI8CLIkCHAQQAQgABgUCT7FhNgAKCRCCQpTNAhfo2JX3D/4iwaQO -K+LlHcXGL1uukMHjqivCWCACQXyuiLh70cjA1WBY1I7zBHZNhFpSyfRiLo4hbriH -ngD+SUbNw2qaBiU6M5D200QgvuGR2oOTxmzoSED6UPpEypUSZdXFTbt+tJrXT/Aw -PqHqNcniqEdfDsgPfPHje3eUOD68Z4gmzvyunpehS2l2OoaK8jUv7g6R0Fg4qny8 -3n5jfCUmDhi+RrrAqAloAupWFlSQwDTWbK/f8Zic+DTUAb3lzRSAH9G6hKioZuzm -ocLb6EOnUXvJWX2gGqJpruBV4zmjVbytIskqbHoQoVJJ681fNp3joNQNZMmBpeLn -1tWU1/p+6Gfbl4GeswKo4PUCe1qvejNElLe2jElGKRfwlm0eoK6WnrBe3WwsDIMO -T6mvS/3W2QifMsrVtRlIQi76du9RRowL9sIcT86uQ+Jkxk+jumhdypNxcK8R3y4v -EmHtGpEnW9UT1aqUyZOIzn4rYCvvqhBJ08xUFqrG5TCJbaAR1cikbP62m6h/jEYy -tKduwQOt6t449r7GWaoRz/1gUMo8eZM8GLhz1l8GPo62rU0dXsglptCXxlUjfp0y -6ZVrz9iNc5zJxrnIBXmItJNiyxBUR/ZR6N2YvIhGNru48bBKGhi8DSx6q/SMk1Hi -bRxqFMPtpi3Y3PUE+hYHyDPZ6fwFdpiSTk7uVIkCHAQQAQIABgUCUHrFEgAKCRCj -Nmdp3UDmhwqDD/wJeoKyuXHsSjj+wW7vdUOv/n4nrZ4DOj79OF6bcA5+zgufh8wZ -nlBxvcbwBPvqe7rVmEJUdsHK2H4o7oLCFmRvigW6kxPUyfOhnoi0tOYP4E6zrRrT -6HxMmKHjs+52UBdoNcE5FQl+kzGw+CdGsh8iPQ2SoR9YtgVh82RcO/Xzl2s4Tc2Z -Y7dFgGB1kuV1ZJfYSLnlpUAnH3is/vNdzAyoNogO+x2Y7oB7dfU2S3Y6rKXmW/Ds -5f/z0bCA4NrLHpk4mDO84du66OBl9Rgvv010k1rCtft1DReT28DVyQSqJnLrsEyk -lWgVdbPYkDCAgwGc+laGdBSr6yEUxkrrrQi1O+WwVqCh1kQ34W+5bvGoOLwJSRnh -4ZUkb+NCMhm6WCVkKYLb1fyLJcCt+O1oL3c+koBMT91Myu1f99JH22yczy4TDFsb -GVmQkha/ym/O1vmvHzxJJ/fwt0V3K34DingtV7w2NyYeiF1ae5tWCHAuoar9V695 -9yNDL6JH+PLyAUnnvqCO+mZnXr07iPGrERtTs6csYij/grBgoRFGH+y2jM/8Fy6P -9VJnSZ8j9dCil/QB6U2tFWdt9DibpFxxNBCOg3Zo3HD15DRF1QPa6+8PQTs0GS24 -fVG3w6GliqDG1ay++Jvco7byypVHohYcjpUhSwi61rR76D5OoP+QCi8idokCHAQQ -AQoABgUCUIMjNQAKCRCXMl3Y+f3VBuxDEACtw+yDQ8nmE3UjObUG6v0y7DhUoQ3O -DXvmscCzWZWpCPJa2a1BUOeSy3uQEfUHzsJw5XNxmT0VdvFcKDu3NnJ10qZfhrDK -AyspahJ2Ulq3DpyExeeRmumQ28ibk8B+0ZXeUI6cZ3H+IivFlZrFX+8w5P/N1p+f -5HYwiZKXlzTbfXsWWpy8Czevk/nS2uA57FoTdEiGtsFlUvKhSnLEGGuXnXpObY2k -8QzaIuZUZ2+YDKOWPq8UeKiev9xnEc7UL6SktzsNxeYgvLI6ES0Rwoch9oymtBRR -MgqKdiznQGYM0z2/j6L4G/4Ccztp4OqW3NqW/BSIyiLvWOiKfnAOet9avFdwX+lo -urXZRHN3LbXAuML5DcgFPfJJsQvfYM1XAyxE62Q8w/px+eTSESvs+cFv+ES3Jmhc -h3d16HbhpqRRVwTFUDPj9E62vyv22ZJpzGXL35fDHcqchrTp/c5dn9ctG5CC1ay/ -T+oMXaImEYqgbb21qRss83XcDZYZgInpVza9QY7ctjKvBD2fUcSBGoLFMTwq38AU -rMf8JqiMmNiD/tfimFACKwVxCELUhPVCR3rmO6PTlgDcAR+LgCsejTMy2ilWYrb5 -nq/+Ear4CEQM74wHQBYddFNGGbsO+pBxN3X2QuRgdxzV1k3zyF8bUB17jYS/knF2 -DB9a2Im2X6cQJYkCHAQQAQIABgUCUJBNAgAKCRD8UTvKX5ogj7OlD/9sHZVzetfT -sYEAnBF7Y6czfEaNJKofkENfxWg8i/a4jnzxzaPppM/UNzVQB+iU26i8cyjy+qat -vHIFrfV3885dRxxRApp/Jk+r1yU6UUjHI2Hu9LpMGB59QCWUBjwg/Gj2jJqwJMB4 -MoAl57Jl/HgiTKe05eWNknCnf95zna6zyS+NbOilTHcUB6XqTcZrl6S29ue3RQsB -k5PyIvMdnoE191JzvYFEQmVzjdS7+19Vcq8s1ybFtfnGqRVsvlqvgcGn6+Z/pmL0 -gArxO8atNWx4LC0ipxnFJ+sahUA1pAJ7pp0F1EHgeN4pAkP3o85RwhUCU/TvpO7R -2+IRdpPayErSPZ6LgKLzY0kmOGXhf1rjlrsP1LDyjwF0RPKOK9aqNcyUwI8v03PM -CjgAfFoQEj3khgFPUa2mjDYnZJHgVE/XvBdUQJrJXVqe0QnVNgDLGY6kaC3H2+S+ -HW1yg1OCgMfZfosghlZU9rRq90MiX9U/iJkl5xKb7RhvUkkzHeT8tlcfHNUS7Uhe -Y4/hkCz5GjU+HXU8xBJWc8xfxJJrtM647JEopg6LrOE/nTu8eUyzStUOxGDowKXD -bnGFA21YE5dW+MjQecwCuReZgfOhzbuGwsGHIw8yk31qbLVoJ4+f8preQLVFCiwC -KHvvPdpv8b0dYCVJOVvlcrXs0B84u33UqYkCHAQQAQIABgUCUIMqzgAKCRDXAPpI -E7KG2fOEEACzi9EA0jRNxB3XCxsU5gcmoM6uP9E/81F3I44DgBMMlb8eWSTOSC7s -xvjC+fSqAQNothXGgbFjC4ZWvMa+OKhrZt0a+CCPhhUqD6vwfsr1HfJOjKOwzily -SUrvZI7GCOeSIPENON+XdEGdSnqOETJnTsLJnf5mJY5kJoqal4m/sbaxkgiQffec -EJhnAMwVgIkBtHkdXo2L32d45TLTX3ZIY4Xs5XsMi9ZaZJpEhQG6OZKaDtNiXwyF -mEDenqSA1cPE7or/89wduyy0Qjh2KoSO6MNfLYj1iRp2uahj1afj58r2KehGGxau -TEWt4FfQODz7O4vVp1nJu/3YK83djozOifyJWn4XlbG7aiYk9FwOh+7WKVRFRgUi -RKcbGK6TiAXzg90WMm7WNelqaLpuPBupX/tSwqd/TL2m9fQsRmN0fPEAJXjcJ73m -4sUgOHX8glVaO4AFgH6csV4MKpP6Ii9nX9KvfWE+jZTnd8Y8hYIHzs/GT0Vzf7ME -qKeew/l7P7NdxrnqHhjWWx9Qxr815weEJDkH4qHcYRWlmM2Q3oNZOVWUq+m2+55c -7DrKO8KKWXWy60ZetRaKMlywSohgZhbadNTMal/96DsqFXrepZctD6JkQ3T3ZdAB -C6gy63ei1gTtP0WvMIv+lUPZLtfJH5aK1+ah54UThYFPrzjAsh+CcIkCHAQQAQIA -BgUCUIP3IAAKCRB7WFswgHwqh7IWD/9cAlO4YB3ltB4GgGldHbsuIv5LhUIMSLwi -7zjk9XVbwZTLB/9a+mc63U6ykdfjDXjBOf2N4797DjwMzKmfpXWdugT7KpWKPGgK -eMj9aaHlUbAor3mdeC5HEWdSv4wfvK43okXLoPCXN14KdCiR5mAPOiaveJFqFwQ8 -wRDp6w3VvN4wvlTwpIRIwwXBss+3wMZg0l9N5r9w1xpxYshlsUnGU+f7HqpQjOkJ -FhdmCyEoAs2YFUhiPclNlRVLk3J0Q1dEo9lW+B+zoG0GgI07P7W2NSV1B1I9kykk -IpQL+X4W/pGhF6fvn6zxHiUrzglWT6QBabCi/ILn3QSvZMkcsPH2oRic3BI+5lA2 -zSMNsx04/2lS3PuhnIYU7ka54x/R1KhEyxF+4GnmmfvLtEisksRubAtsZ7XiBOuJ -Lvx1VLHCfMe/bow+w87x4g8UK2Q0JSfd2DSBpEUJFlpDkXmYWGi3TVs8lZ4fZ3Kg -eSqib4TWpAkSkmVl4JY31k7Yha92nlZHrjRJlU6j76meZyncRtrAAtH3Hcdh95OE -2LNGfidv9OOKsO3oS98slIXKp156rC2Q1jlFwNLJo5A/RsrjoizTjztvifCXVa/L -BE4Oq+bI3/7AYRuTWpDhmBTQBEyBIsbHZV/+VQNOKW2x7iqRVRW218f6Sz0Udgdi -SHTlKxNgnIkCHAQQAQIABgUCUJqdTwAKCRBZZqMKU/6CMJSpD/4qwQo21nTYAPTj -rcKX2ZlPBuryrxT9CyKQCyp//HdMvIk5W05HFqjb5cmtjkKuwqqYublAKF1CFaTb -V/DQUXA2pToSUeERJohbYS7A7wBjdQLFo8E6iwJQ3Ag6qEYdK2FYwLkoyXB/4ALO -hMg6U+2Zqp8Waw0W6F5VexxYsudg98Op6SETr0hW9KIoFp/9HIAEO+AH6xgKD7Q6 -rGiJPwp4XzH+sdqcctFZd6k9GUg/3C4xnKcfO9P+YP3FBdVeHPzvytgHKJ40lNXq -q6BE5+ejLs9h3rou0fOSS0zZsY87v+uXycKKm9doMne8HcLKf3ivYYWVGcciXmYS -H8MSRo+zOP0qxiwmGH1pZeQ0LN+tNF4j/Dt+IZAiAd7JZ8+E/uarasBNWFJ/dnFW -APrHYQZfyvKh99hE8BLW+7ukdc7VxsYeAQgUTXpFeDu6PqLdCCG2bY5E1jQo5vEZ -TdgsWwqXdVWeVdACEI4c3WCwU++SzCm5h1L9YiXWHRG3VaFizQHAYl9f9glafuDx -ZCd2jBy+xSuMrYJQP3wJ+vCxcCM3Ep3oAQ/TJrkkBlsAeMzJ7r9zzyl0kKnHmj5N -6l6MFyuTq+k+6ynpQTWaY1IFNWBp0dOsQjOgth5AHHQAVr/FEkT3HIsgjFUWlHb9 -b6a4ddPYarLbnFQWf5LyhP9UQIdoCYkCHAQQAQIABgUCUK6tuwAKCRBJOzxg3q6Q -FpkBD/9DOVF5d+IIiLaR8CwjUt+mM07DV2/jHSuKYVnVjI/pCbj5NGh58lurvQh/ -Dj0TTkr3gRe+fx7SLAR2XIyUITTUxVIwSFVDeKwG6sRyWHMMKjA+rSx2FRdbWKLi -zmt4+1YaS/nHHFMBOyvYvMDJUiz9bfwX7ftK+vSDoncdSPz5KnGYXlcDwg5A1l3K -91JG8hsrK06S7yjFILjn/RGZwXnxR8Hyx3u8Q12ZKo/RKFaZ53Mvv+QOXuAv2kxF -2Nb3Axi98I6kVrqsXs4vOKYWxobFtM5F9Ix9L6/PGRpinLpjwTt9lI6OBmf1slKZ -EtAqVlQXoPylKMDAWUEpi+y80nDW923csKt0BhD08Wamf4xq8yjPOd22dJf4MJAf -dJVG82YONWtOoRg65kq76+FlQeSAN2yAisE4PWWaM92h8WRzHY4kg1cvzOICFUVF -PJvnzOhGgnEaorIWcRUyWEkU5Op1l2kW8Zr5T8jDEI1WBpsHlp9RQBC+AZQdtQez -Fl2V9JxfTVolNvkzMB7NmZlxckoWCYZbsAYyzHW1h5YepZGU9RjKPgXi7Hd30cHD -TtOeSJUcou0j1GBqMcSA1DsSN44ks7PrqsE28tN0jth5qgxJE8pT3iLS8J7ri9Dl -hkTMAAeYl+QOvXH8oPY5DrFrb3p/kl6S50Ql6kZGJZtZLCO3/IkCHAQQAQgABgUC -UdbkZAAKCRDK+zutCnXId9ReEACRTb+1YcosBIfarbOvx4vcUvPxMkTT/JZqorQo -AWMvxx7RKrhjvlry5/IIIhbWuIZwuepWP3zZ9XoZmnBqZqqLn94uAhD0BtVzlrB4 -/ajHV3kqO7qf5/gYs4mMxc2kRianPlnYvBmCkkWYD3e5PV1Z5omXB3OrNp1JceeY -NC4mkcLMkQTLIOPwjLZf1zMq3nYuSvDBtBmZ1XWTLI5BdDCQ1HfkNFP01gJ2e1Lx -ut8DUG7ny/4eQLAAjUtZXagq07bDSSahoIAQDfrI+99UHgpLV6AwfwyYbk6H4tFX -Yf6RvvC/UkWhjcYavB0nWc+yzd3DXod8wDU9gksWkHuHAlKv0wDIU5XZtxrNQCui -PTcY2NKQivxJuaglP51Gj65wyqpge4nGhxi/QnlmpJks6F1HEsqSjshvSQCY86wA -S+8vDN00o/Uka7606QoNfM/tSiJeb0bsR799h7fFw2IpBvBV+BxsKJYQ6RdlPPW4 -soDhF8W5zEY/id5/lzaN1FGCf3FN6rm5mizheprFAFJrTRjnOHrY5kIb2+Nd0J7c -Pv2kxP7n2gS00XtL3D7YrbkALGybz9XgNDfrV0bWB7ZDTIEOgTzFJNmbHhR9tQhl -qPVB31JcirdKGBsOY7zqcPTCWrZSVU0rPrvbdAVyo/7Fp8JXwF2hr8iznirrr/Jh -ifYnpYkCHAQQAQIABgUCUdfRzAAKCRDfxnHuszP6JTzVD/9BV2HJYUAu8zpR/w9e -oc3sp1OSiaFaUvIh30jHtSX6eEcH9d5c6tnnYYORiWjGY7n8gAP4JMuSUpXb+Ar7 -u3m+Oia1GKs41qRJ8GgXKJ+PGF/dwf+T/NtOYGoSQBs7rNYrCuq73iABCX8l0SbG -le83TdnprlSPluxeQoMi62P21rhTKNC29MV+VAh9wssTkOKy/suow8oFTOczRNCM -/Jqfto6N1hCOF4k+Ic8NQSVnoHK4yG8w4uSlHTIKz/dLQK5eOBG5PS2msJzecCze -ZEwcSLW4DfZd0XBdijauH1ddQESrkedJbnYyUvysnNDw8ALUxh+EbjTBMDyBx+VP -KbQM6KijQDbA3bHdi4k9cmxH1ztyKef1q9CVMxGOu8Ep1/GvfmAZc+3V7GXNKl4h -zkgNrNXPq5FZpT4RgQ6e3iIV7vJkttsAPIV1u55j+6BbnHbeDMP0ydafSJJiSBz4 -lZ0//1DsJj+zCbFlGlRPTUks+t5QJdDBUAX16aQhODuSvjqiQLVXrACbO+nbga2u -8DJgcxU3N4d1lppzrBLfxFB8MVmRG8y7aeBaPhv7/IddgG2CkCjS8iNKL7sv1O2h -woJaiVdH9g3flMS8pP8Ubc+HC7D5pIqxkfyK7ZUR/2zA12KG5wwTOoawhJIiZ7x4 -D9ZHHAU89TeufnJEtN7sf+CKP4kCHAQTAQIABgUCUhOwvAAKCRDOyZQtt624bz53 -EACEGCje/xJfn5jkw02mDPooiNVIxwEvoE0ZWCCqxOeRiWrL7mDMGbHb4UHVKwHk -LA+k6zNHAhJZiKuNPh5WHVqNofdsAo1jQaaXSFqHV3xpzXTPhPEImHh7kf01xhEs -Yby+ARP3n+mVHZqquQQPKW9jzWU8UdNerlViw9Q2lude3SwanmKGqo6w4vHoUiLy -GHkvtQHbJ/m/WkWCTIi31vCcu/WkyqOsZMx0tvleFwcYnoKnZ4EDKFVltah7Yevh -inUFeRYuQVpvBot3tehBfpB17PrENCTW9DClLlUs0N1mJXpeF5Yqz+NoUQuQEfFJ -gNatTdUy3MEUxPfZzzKTV7/6VInsWpmaavfFB17BCsH7oH0yF7quyLLNSGc9icun -p6Xp3z/Y7uM0VQvCTLfIiJ42AKN79H1wfWmvdPb/r1JQo6RvjIKhRrp/Zqm4UJbj -L4xMp8+rphqFmstsYnh4bBrJ3cbh2FjSiZzJ1l9WzkEnGmWmpnyWaeRZ68j8uLnH -e1HeWwo/jjTbjaoKRtnET8aB6NuJEriEJ1lLiv7xyCr+ZgTFhjUczCfHZ+BIsThc -uMC5DaqeqqTfY0b675cX49z5oHt72P6zBqzEua2q0Dv2mEn8qquycUGYUyU3Ry6e -8ININQpTnSwbh/LD6IC7kKY+ayHuK+cNnsA8ed6i2xe+/YkCHAQQAQgABgUCUm5Z -UQAKCRBsZYDne9dWxECQD/4t+7qJ7VEit5GZ0g033wnBq3bZCphmG3Z8PwRGY8Tb -zbuaZn0opfZNKxBtqzeT1PLiXHAjyCkwokxsjkCXRwJz0c0Cbi6RBRxgmj0kLNG3 -QWKdjgeRJ4GGf0qiDsKS7ns+4x4xzTmv6LzxPqxm2sJ/61IPTBtEJvdV5aiyjY9R -GbcTKPrCsIw5Pg74jlzA5qjCSELq6n0qfcEYYgHiK2MPiBUgb7u/vJweYxue3Yiv -F7XiEEd04GhCHIOaHGwXgfoFhsBeHFEF3JXavvcljNmBjX1D6wMQ7EI8FQh/jjFP -p4LaBlYPlgnPX99tqvJy/HbGTLfBfcdLJvMkXdqUyet9BibsngF/uYywaVODvDpj -4IPHAw4SlXRvQcQdCh0ZkBOAAvyTRJ7VG9iDUBWNDwKyDhY/RJol7DZx69Y2lVxQ -WE20ic9+zLSDB29qR8wvrRpaLQAKCHNOlrpBTgr66bAssnnSYLbGY5InN0KCkqeG -NT3lRbJFgPY5IJGLPadhgi0NFyCFL5BQw9pOnd01NBtx2SF/riYNwMJoc6CXb3kY -7RvvR8aJfvRvVwvjZoEXKP/yVQV99p00EhLH2bfXwFWsO2ShAlP2ANSYSTF+w6ll -e11EemjxpDAZBb6vvkQbl4AHwt4k/RtAKGuMCrZ309JCN3htPh3pxWMIUFVB6FS/ -8IkCHAQTAQoABgUCUmiZPwAKCRCNhuf65esMEGQbD/4uHHScAXBIIh18BK33/P1e -1H+fjK2TL8+MIPbHYIZynO7433KDL6A38qCOL8QZ31B3OwDeCYYDV1oa74rFfKzc -pA7a+kX8LnKtkhN7yoa2imb3IiLgUfb5vMfApmVR5DsQTcapsD3NfmM/gJphqFiL -/yD5Jqec6GQMFI51ECbLlFZsgx+YNuf2v7CeDvmdHX4z7U0bv28y7zJvlab208w8 -x+qAO3g1vUzg3RyDLwzqDAlbiWo7TfIGo5AUigV/BLVq8V8ildL8V8F0sPlBC+cQ -Y/Vm6anUbWOFLQYHDw0Koautlx3pZ0yCG5BfgQrM8nF5RVKSQ+xdOpLVYOiw68J7 -ChTUxS9jYGke6NfUobEg+FWc7JiDwIcm8TX/7cvSsSIniT81rwuxM2HXeaaUPsvj -ZoMX2NahGjA8wQCK3axTws+0UInuEN7gtsRsx5qUWwdLrml3BWUyswG6OBjf1DBs -CxWT+UljGiWXR9hXcu8OppOpAgO5tOQ/QeYER6i3zaeqL+noyQrxju/2D0yvo/eT -lkzX4p4CxH8ufPTKI+VamZsf1bx34SeXVyqeFVBS0j8k9xK6f3eaiTSmc/1EKx/m -dQlXHljIIxUehiSS8nRLxsiQelp7OX0eJ9GXOTJXu24KuDgAbG9MFoXOoU29mk9H -YMgfQ/Wyr5ND5zg2Y6RkEokBHAQQAQgABgUCUpDt2QAKCRB4GbAn8bBeNZDRB/9j -dYXO9+pBFwG1QjF+a9R7qsiLBk5W3hXU3pkaROTTRiAaEbM73D/lAU/xmZl2Fjuk -qusRiL4xMj5K92oSf5eGMKKrHLfOx9X4F9r0JT+wVfSIFSSugB3viBQ0wDcwrnAm -ngVNbYgQKz3g7lRkIy31p832I4Maxb9GKKu0HdB2rd3NlAm+2lja4i7E1zWR2RL7 -c3LNylcnO2ZBx0y/a0PK5NBzo4YNmxodZ7ufVQwnuJxUZ0jrfvULHFFEML+Wezo0 -dqrz6I1FkRP1n873M5l5BeEhzlz88j4XCLaoVoajpSdRUZSwvaKTiH4VkCM/Bj3b -ZN9lHLwZcAlZ65GBX0+MiQIcBBABCAAGBQJSkO3zAAoJEGXrKrEvDseKZUoQAJ9Y -ZupSZE8LQfgU3ICPY+E2q14/gY1KgpW2AO60O6hDXIvyzhEM1URRSnN7uvrzqX1v -DskaLsUiSkIyU2x2VGt8xsUc3D8xQyITH1D8xOQEcXqsrPLrtRpg+TFZu6qlpax8 -bUy0OnwLsbSqCuOiafJuMKmApeGKOE/mAbZlcFhDuDs/x9l568KK/FiS2dvD23qG -+KGfaXM4cMSUhPPV28mhc1bciHyYt+c56NJ7TnljgV+Efd4eyxjawAjqPdsDyXHJ -3thKvTmQMcRsFvsjTqRP9pBthSkQwN3zkyY3Ss9JOrfy7k+fORk45HZlUOqLES74 -c3HmYgjxsEODt/6U7A9YF5xNLzZPa0igr77xsmHmQ7hoyO/psDCSul/CKesaY10u -c0ESd0mPq4bIF3uypGUn4oT+vEfwdj476qt+EDXJqFa4FEVsJZOO2Xps8MBeH8oE -xWRslkBRsuN+RvvaNhb/yfEJ1uMGF4QkSxER8URf7NiOLKtHPCzey2O0Fh9HHUGc -/hti6TNAL3Yhe38C0odiiITnOOXVF2oLhejd1aLJyuFNH6XZj71eQhN4t2qv8795 -U+22Qzg7pZOidhPmVH11Eh4JR+lHxpxPRUNXGc/uYTsfr/v8tCpblCGuasrZsf3G -raDCHUXJi3pj/FoEUYE3UFZPx9jegtU+2+jI3jKKiQIcBBABCAAGBQJSkO4fAAoJ -EIGdJ4oOb5kqR3cP/RAeA14PmzTFVE6iEaUWjZXKVtpZjcq43FHP9bXjZGcpheXq -l0TETZH3zL7uH/OvS40SFVfb0dArOL3mM1q7U02BbC2uO2160utOhO+3BA1IFyEK -76HOHXj+/ttWSlZKMzNiPV+jw59DxoHeFgD+9CmIwo9l+BoQZ7jJ/nCGix0kG9eW -zOgON5lISGQrTQaRAeYAixgAkNCLrIaQYTsK9CArp0Ozti+y9ygLw3ZGzgQQ6IYF -So6cG13+LU8h/bMhwpJimOQciPWJzycoWFsa2DX/vKq7ZkY0FsbZJ7RPZPVCuNIR -S0CHGD1XGHacr7naWouiGO57Rj14l3/PNe+j/0/2JsSmUPbTBhO8w1lT7hBF5eBD -XJ7uXQqXFT5d7t5l2MSFMlF+XJJd5BAy+2i7BhlO6SbqT1VuzGp8WK9LQvxT3Nm2 -dZZ/iwm8wTDVUebbY63aOxjOMlXRlDaNu6opv38UDXrd3qOBXC9t3zeVY9Hg6tW8 -EqO0pflTh0bQng+us9OrR64kTDWl9HppfRcwok7Tp+p1fwW/+DLWUQXVuj13uMH1 -cT5kIidMEhj8yji4SaEyjJx69SBzmI4CloLCzeip1VQwJBhGJmmOhs/fgE1tCr+M -g0S9pmv4qS+wXpP/qwoxBbzFkdVetx95YgP59JmglL1SrrXZHV1TnXgoLz4itCFK -ZWxtZXIgVmVybm9vaWogPGplbG1lckBmc2ZlLm9yZz6IRgQQEQIABgUCSlEkEQAK -CRA9r1SiHu9SdgZeAJsGY6d85GJ7E3HVl13o+hcr1cEccACeLZGSs4+qwvUyESoS -DOPEfmYdtFqIRgQQEQIABgUCSnK2BgAKCRDU5e2swBQ9Lf/uAKCXYDbJ+TYza+6I -6eZfn4oFFo1iAQCeLO+Xui+OBcJYggj5EihwvbjalF+IRgQQEQIABgUCSnVzuwAK -CRBp0qYd4mP81MnsAJ9z4w8QgV71TqTnbLZRC4aG0hFegwCgoCPF0r826+nVOUG3 -zR8Ab31am1iIRgQQEQIABgUCSnhdNQAKCRDVypsE8sQjvII+AKCCrn5IzHxPI01h -nM4/Gj/TxUVWTgCfQImQc2ARE5bW6NmKPPFm/0KRzwiIRgQQEQIABgUCSn0wjwAK -CRCEibFNiAdSm/v3AJ4kTuvnhe7q0qlYmx7y5568Vp7PZACfdbM7FQBXKtX++UmC -cjzbhkDLAP6IRgQQEQIABgUCSoKbhAAKCRDjIZO2xCm+LyuZAKCAFyjWMB2IoVUH -t/V9G/JOCOtssACfS2RoCQchZstPF++0fp9TXLdP+tOIRgQQEQIABgUCSoLqBwAK -CRBBIcdDMXDr6UVbAKCtr3NkNpfyDN87/VcnDOTNmXB2VACcDHddachj1uRyoZ9g -Gy1F9QVNDG2IRgQQEQIABgUCSqQySwAKCRDAnh2JlZMO3qgdAJwINxFCmuwy7/Fq -jcp2AHDFQNpzSQCeK6gVi5fSlDWfjLT6N2h/XFhk3dCIRgQQEQIABgUCSqQyeAAK -CRAEBGuFSi4WKzNqAJ90QV/EDV03vhNeU/UAYjNyRKO8sQCgham6CAVukF/WFliQ -eH44H8uCgBOIRgQQEQIABgUCTE3s/wAKCRBlHfNSPSPyXUwfAKDCltlqbO7BzfOM -rzGJKJMo/P7z9ACeLcR+MAAqOAhZ9k/Myd3/o4imoQqIRgQQEQIABgUCTE35RgAK -CRC/YHCLSEJsfpolAJ9TFOTxAkq8KICPKVSQTO67RPvu/ACgpPHqDbgzt0IT8h8b -V/juHFgH1zuIRgQQEQIABgUCTRy+lwAKCRCnGmt/a4UvN3ryAJ44K6skMMBY/0JL -ic5EfqzXlCbQagCeMDQLbcF3VjdQ54NX+++Gs+qxTgeIRgQQEQgABgUCSnMJ6AAK -CRD2KOuTR0MgbB1CAJ48a1zkltygGQAxAPWHX/bRSxNzhgCgwggs/p8hNc6iS7SY -gje3BrR4RbGIRgQQEQgABgUCSnRCvgAKCRC89sYPboFp0pDWAKCWaQ3ND1leaRGx -Xkjx51p6EWrhGwCgmzekazWQaQXfWdP363CA9MQwCmSIRgQQEQgABgUCSnR3PwAK -CRDDdqja8bzbc4VFAJsErrlykAvufEkF8BPxFLIqB8JS5gCdGjhelMwVh9X44qtd -kzY/H7L268iIRgQQEQgABgUCSnR32QAKCRDZRtVqCOOLvGlAAKCFKzzXUXo3N0C4 -XOFtyPBAW0qsOACfVnm0TkQ++0UFN7hsQIyEiUILyDKIRgQQEQgABgUCSnSc2wAK -CRCGvKv/HADHkDiNAJ97n+Z8/l8KKokY2YYmXKk81FQLJwCff7MvAo2cCP7Lz384 -fGkWJPfDxIKIRgQQEQgABgUCSnmQuwAKCRDU5e2swBQ9LWF2AKCTutL0IZ1o/sUC -aCc/Gf8vH664xwCfTK+kFjHHmgdmX8GhjplTlqYEb/6IRgQQEQgABgUCSnwzeAAK -CRA7MpidAPPP5JogAJ9YpuM7KFuj1cQ0bMDp16nMvt53igCfcagm14Oe5xvINjZa -+DkLporYDjqIRgQQEQoABgUCSoIM+AAKCRBRlIML7BmzeP8+AJ9hSpPybQvdY/iH -c3FyeR62cPUVLgCfbT4bOV+mGINccVuRlpO3ly8vfKuIRgQQEQoABgUCSolsygAK -CRDcNVLoNudWBDYtAKCG/M0WfM2nuMc6UdS3xlZcK9L/OACglNLJi+K1XZ/WLyWr -OXajAXyhAvmIRgQQEQoABgUCSrT0UgAKCRBBIcdDMXDr6SGaAKCQvemrjrw4z3FQ -Sm6tMEMKiFRghACfSuQTJlo2klvLhuTWs0BB+dinJ7WIRgQSEQgABgUCSneG0AAK -CRDa2nnNeIo/TEW/AJ9C8XbSPucKXZzP5ZBq3tJ7cj7yLQCdEAlpKgo6BPjAWvQL -tsP4HyzzJkWIRgQTEQIABgUCSmyZvAAKCRAS23nuxHY7pSr0AJ9StcGqd9jFjet1 -T9b3IVFMWcXsdACfQ6lHHcNuW0WSA0cKVjZb12+k2EyIRgQTEQIABgUCSnFnDAAK -CRBvF6WvwfJOpKdYAKDCO834wxw5YAOBjn3fXhr1+7jy4QCgjP0g6i4cAu0IvrX+ -KPiwiHQ3WP+IRgQTEQgABgUCSnTMuQAKCRB8O3lwiMfB97FTAJ0cD9C6amRysn2h -05r5LuAl07h4LwCfZtMvu6QimJElDOi0eY2Q2nlOP+mIVgQQEQsABgUCTLrM4gAK -CRDyrYWsHkKzZ2bTANwJ5WBKmuKuy4/+/FKg0GkTXuMq0nOGPN9cCDM3AN90UoCv -8ZYGDZqAKhIdPdWLVa0er0H+W4OcXbaQiQEcBBABAgAGBQJKdzLEAAoJEJaA6+Sp -kZPiFzcH/AzTY5jaO+CrZJ0wZ7NEg99MJqEVZqDT1RVn4iMukhPXGFdNCN74ZSop -DnQLn4UI43ely/Vh//lkaFi7Jj3ZQT3izlRstUU3yUMfyO3+nYeClrXRp+busrEm -me83KDmnagQeXVcNrpaE4yII5suYUaxvKWNeZUvn2afd2k0YI9muOFA6fGQOinT1 -XMk67wNrR+M+u8FlUk7YGiLH/YbUhlW2XPuxblMsoWr23g4jP0Ti2hRsrLgNIelS -BsLv30peJMSCdY2gvaUkeeBaucWuflsWGjv+0z9QO9LDpsqwXMG9k77EWd8GwPDh -FlG8oGDRiynOpQKlF6tmNgREIlO2lCmJARwEEAECAAYFAkp3MtEACgkQMfzn590H -lGFiNAgAhSUMQKdn1dl/o+t2oyzXUIHc5Nfc96PJBfcT0V8d+QVK1M2wim5oa9RD -Uw7wm3xdKwh9zlBk4z0/b2WKkHHyomnuL4X+3V5HXbKYYbb+cKvn2z+y0juBmj5s -8UYCYPWmkRQR23HW5EroYW0jUGtul+Nay/0KbY19nhu1bEN1doLJxF1sOVP1pNWT -BuBiT06e1Gn2XoErHgdQrkW1WlUqnXARdDWxS0T1j6QEdfs+O8i2Yt5N6SBgP0Fn -ZtRzVSBe27sJbWv9RFz/StNi+KLLrrD+cKHnEMe+tMHkxE0T4+pziR16roCQZNu0 -xqyvCOTWH8wvKpl6BvO6rE3UNC/2vokBHAQQAQgABgUCSnQwaQAKCRDz5BIvHYwm -GjlaB/0Yrzo/XawyK+0KFguatmyejS6T0kaUFLY64nbXOS1UpXx8aGflN8Zo5i9t -tDJxkfivL3qmv6JqCNxrel5MBmhg3e2FJDkAbTy5Vi+mPCZnGZ28aBfP0pMFtaDK -FWSydxzi2QJdmKLvBLrQnZe+Mguf9+f02zXSRH1f+NbPmLrpeJIc6mqYbh0xkrHS -kDxJjnYOhPVaaV7EeYuKWhcySKYPd/DYhLx/7OvFK/9ZUwaDDA+JHY1Rt4A/nPa0 -sEqGlWUgm0lTaOhuOSRZxlRQKEyea4xKNRAtKuubd4F8guhtsuR4bAJ/3zB6wfMh -KG3BBWQnLzxSBepeJndxqOxoJGHBiQEcBBABCAAGBQJKeV/XAAoJEJaA6+SpkZPi -OB0H/iujRFsybyaZ5rVNrolo1xLKHzv040yOryS2aGIUyHuqDdyyJAIdw+BxiAmy -6JIJ0AdF6hxZt3bzmCt3JVqQc5YJr3m8xj0uCxCKXIIdAuAQfFflXGLFtdE+Mpbw -15QjkgcR1hTSzOAn5VqZeuSyR8D18baBgpNW36Ur1PP8U9j0UF3dCoywjelWrozz -hUEhXcw2ZJxvuSAzxi/IhUAaQxlEQB5JvkXuXny0G7qax5Gk8E4rbcGNbnBJwxJD -Pmth1dqsTBJNyVppIGbouuFdKBeR3HYmZ6LUvLTSx7lp86pxYVQVjt/JMviBnFhD -WCJIrFQJQnTa/rDVpnkk10iiYbSJARwEEAEIAAYFAkp5X9wACgkQMfzn590HlGGZ -BQf/Z4SjqBl4BRdqB1fk+owZBZU8D0Ft0Qd+ukmNcfY4yahNoWhRnvbkam9pI22t -zroizwoWwUz0ansU/J/7JdXyU2QRHaJmBZ6/Xem5eGKiCi1XtMWI9euFpyXkQkpt -E2uzU92lZSlapbDTGYYdwfkZURi4Izjhw5NuAv+ymUSbCaRCVDx5MkZYFcpFgrm1 -J/MKccpTnWEJZgPtuQMC+sa+DnENAnBJ8k5ZzRUXoMnH4toISSlQq6tf8fJDhA5n -mAC3HvmGqA+DRss9c72aPfkNI0De3lgm/R3rGswEJohuRAyMiyneqYhKyr42CRPY -uY46rciaf0Je4SLsje9h5HcbDYkCHAQQAQIABgUCSm4YdgAKCRCi0+fPUhNGtPgz -D/9DI0dajYhhgQVVyYBKWX3NN5Gc3exsZvLXzb6wI/1W+aR237YWcFyG0rMD1i3B -Drp2rpJXpkEovyjiT9RlEYDhL+JKM124zm/KHr9YUDV0olmfnuxrTiopRyBlIMjn -l/zM0ML1u2NK2MlDP5bCfL8mT0/PNxYBfaBmBsLLcI8X/k4DvtxF6fTHbuLp9XBC -/o142w9v1ggiFYsuIFtNLNBHEB2EVd9vg+HprBoP6MP8F+TWq09mIbAtsZ9Y/P2k -ZP4AMGDCIqqjQed66brn0fi669Q06ubc1tuvkjpBmh7zsWt4hgfIURNTU2X6u0AQ -4KtYnJlsSOR1a5uwK9J06xxI7iMnUL+yqi6czdCMM6feBO86Torosgrio6GaRv1I -D0pOB0eyXQlcdIJPsTtSn6ywKLllD6y7jN8NsiZfAjUA6z3PZvD4XxlCDMo+Bpi3 -k/mcR8xrnEkiQuYQFAUVlZTb3IVJC+08d87zuHTbom/T+gAMi1RDQWvL9ukc6lhy -DFU/+ZxPLViMEzLfhKVBku3VHn6Wz/ozbH2oWoGNdsQ89cBYWrUqOjhssr0oUkJd -4g705WmH3IERQZYEd8Novc20BpfrU3n4pim1IlTXvVrtNX1r2pq/Aj/0gSYgNzR/ -Q9auOLVFoE8txrIOVT02F/cbSiFrRrvG18su90YKnhY51okCHAQQAQIABgUCSnCX -8AAKCRAsfDFGwaABIakjD/9zCmzqUuScpdUGahazTYRJo/CfT6vJRUiGX743PYBL -A7y/BKG+g94QuD8D24hGA9O4To7oheFJadATdqs0kIDCkdh106CB7/Bz4K2hZEr2 -59RULMF4fTtsVKqYV+JMfNdhXT8stDO3tZnTo1XjkyKKBfry1Ui+Ny0ris3Duc2j -eLdoZnrSfdCynj6W6sFO5/1tGWPePlizZsu9p6zTvJNer3St2DPYmFhZeSpTnlHY -IFAvZofgRQPtnYBHy7yvG9/fD+8ka8CitkiX/evZhqhf0VmUqPbpZ2bc6AomHyCp -y7A+85mEDgzQKVgGu20JiL6gDyhxDYUG5c9OwBFywVYqbV8GXy4UsjSdbcaXvmcm -8s2L5jo8Fs40nOzYDWAMz9P7kAsnum8JKGxfWJIkaeILcdwfo9Ed4Sq11QQZT/zg -3OtaMBA6c7lTtrqhub6Cy9CYIc9FXn9HH116taEMpXK7PVaupCty7P3rI9i41GYJ -rFGlSim5MglCuIlG8JCYL1qJxlL2tu92m2JQ3UgXqTa9/THLoPb73BMVf8MpDddM -EeE1Gx9SXWBz1+rlYqgucxxW9JSPWoeAjWzVWHgE16JQgb3c/fr5bv6BSmg84wn7 -vX797vnM2arXF3x++yxfLM9Twnse2ddsHmHESmX0+QWn0L2VLX1N1SobP3ixZtfL -tokCHAQQAQIABgUCSnCbJwAKCRDthgS8BvWpH3aQEAC0x74PxF9qdH8bqxeKQkMA -fSAN3ZPNS3mJipOWhD2c+J5xcbpgNGwHYOgRzhoXHtTbdN21dUgtiHdRB0t/AN9Z -8+9ADhiRhd73HSRT/JZ2tiiI7cosDPOORDGlKp+0L3enxFkRaPg50X9jKrqfSkWf -TelMZ/+W/L9bLuqvrwQ0YYn9nvmdShOaKmQfdlWrhyzq0+bQqbdXlO1SQfn9/zE/ -4cUMN9OzQuYGWMEbEO1OAhtjtDIwg5uQ9XRCpv2LH7qQCthebcO7tSeLP0ENa+W2 -uJ+1QkuGxP47njdIewae5v/qBdEkII2/zf/xF00pEnHJJ6N1qR/0d59wLIda5mNX -8IYfE3L89cLzOIxYffJ/+ykxD/OjyJlDZZvAjeNLBU8nw834gHqZX/moUQng28b9 -Hv6760ljNrUCh0Tl3CBXeGBw7kPEfak6Sk9fpBUCRVDUTAGAhPuLYJdF43adBKXT -8+w6nbvZTLjyuiNXTHaeCNtFzpNSMGLZTpSRaAY4FFg9I3dlplWqMVYf73KowXx0 -TZT1Uo2uxfFiSZpTAsIpNBEH/qIIrN1ZKbYXAPl6eGGJCtp9IDgZ9UCfn7NYMJIP -UbGQibFBUYxW6rkYKA/dS9c68uRU+FYzTL5XnkXLTVCLGSF15Rpe+vMQ1tfBkpbk -6w6Y4WK7xRewR4la6L4QQYkCHAQQAQIABgUCSnHlIQAKCRDMeYZvmUCQhHqMD/4s -E+J2CAIZjA6UEPMmVh0ZYRGXHEOafQ+6VWIdAgNbr2xWXCCoxwPCOmKISThgEpj9 -D/QCVZ2vXBwZD95NkHoGRiI6+KEvkYUwFmNqEA8x/CEQmVo72685qzgscC6YP+LB -FS9iwsx0+adEc/7lp1Nt67BicFCL+89vlAV+YgSCtIxUOduURfTqvyy3tz5t5gAe -tK3g7TV/OWKFvmkyLq3ZJJ63OVYs1CffgEqknePz6Qq2CiAh+vAYZYClPc3kFwuo -u0C/6vvKukBrRyXIWlORdIFkL6mXxvSjPLO60x2LAUeGP1lGN6OfPQ80ZSsMyq+n -zFrEAqynBuUX3k2mz0l8sgUd7OIk1JXNzK70ckn2n/Bn9e4+SzHfbLD6mdZYl7Ph -N4gdLA9p3dPf+T5Fa98VTurY+loo0NbAJjB3xSA63cf+AyvBpu3OfNZL2Cz9eR5C -ZYHGv7bJYg+d0Ku66n/GOmRunky0FkYX/pPgR5HRZWBF/S5Z39o3EFf5kir9gZLJ -CgQfvqFemf6mC+ZCO3jvyv4P9M5GSuF3u3o/7O1aq5i15l4We8Pzce7XcXpAwd+3 -ts2cn2poSynXxBc1a3rrQWAsgQ1/THSVZTBmqVz0qC3F3eVg9udeQ8vEQV5beHP+ -sZuMZ+HGvsjwGCsILQN5slhpJaXcNnnjxtu3gnMWMYkCHAQQAQIABgUCSnK2GgAK -CRCHL3AsTW4lqH/AD/4r7FwCh4PTfsqhH2gxRxpuH7l+JqnRZ8o/0WVyztdr6qhe -CB75U3WB0gGHAbebFkY09WV8sf9MUiOvKX06EwY7DItX/+2C96AgmbnJGvUrkhx5 -2QoWiUMspZVgaq8XB59fORmKbgl8dd+msupNDdPgJkpEpci1K0jyk0ZOxowGh00Q -Z1mKQUOgtySsoMx2T4XdFosIM0pqiWsKc7gY7hKUPffiG6cUeIuGBmU22+y1VHjo -QEovyompREhzUCZUarGWWJAPEHo7l371QhXvya5W8x5GZiKme5Ux3dWWhpEf0kpH -8N0Yx4QZXl3mZHfwvjTPWiQmXIGrW2bcVjmfArpiRSqMzO00tGOtsWF5c3TLl+26 -SvMj2WSTz44sWQaeMsPXSPRpXwnKFZBCoArp+IfEgD5HPJmcRAp04KVmD0DEU9cQ -USNJ3i/Hk/YI5iSa4t6Vm9j4xCcTcfFTSzH3V8uQmB7u079DhLsWVyjq6KLLo9M3 -3xr7BQppH8et/amrXPwi/PgpASpVjPfKZahCKwxWGvYYKL90jfbWCx9ptEhpwIfL -zplwfOaRPhKuTLMtmgRDb+GU5VT7BF1eke9m11eDfhDcCkrbDehKft1C0WbB9WES -1mFIlQH7iggbFwhIH1KxDEIylucVquF8ABpBpRWQvAkRCOQpx+tWTJBLwzt5bokC -HAQQAQIABgUCSnVVUAAKCRAdIcg9xFLg/OwdD/9e1CO7xq0EcpIchTk9mfBCMGXg -RvRSqcBIcmCL5d5lsvO+DG0XgBFlgXHVkv1yGwL5mUbr/uePbYY8l6b+UBqe4J2o -EwMlyp6CXTMd3fdaYd7WsVm0RYlBDd8zIdTLIx710jCawJzPsP5EG/WCcpy0KhBS -4n5eZMnS0ZZNgjo/w9IJLlvC7F+UZNrJDHMjVDIyzFa+2mp4kWY0Ir0AWtFSJW0j -8+yrnETDthVO4q7plMfM6GB7SylKlgzELZVLOWHPeW3PBgsgpdWsIJtpJBqOMtFb -fhtIDEi4xMcWHDmeVcXKl7PbPLlAxPBDwzRvAVQy8kXVdnGSHrkzAF6l1Redf8G8 -X4hMMrKoIAubx13WjOyXCPYZLdQS/ltzjrusQDuMmaomOibN7JPKZ0rbPJ9Rk7uB -0TFFaL+ltsjCniQPDDYdoDbKmci+qppc42mz/XjAfcK68KhR9jgjhv5WwK5/srNR -hQd5VnYXHMX/W6sR751+E23i+4pDBGTRmTuKK+78KF7KE7NPxW60kaT8vxN6GPDG -jsByVgbZAQhS/AT5XwtLIZbZ5li4dYX8MiPfkq6s+BeTZMh6g7jPilzoIw+9l2Zj -NQ7Hq5LC7hWl9qWCbX5ARIlf9h2PbhYR9vm89qe+YnJDlDPmTXO8yYom4Veue3C1 -2CUf7bzzIPSkeYLwkIkCHAQQAQIABgUCSnV3fAAKCRD2bj5Bn4T03icDD/9u/tow -779B3HaI461lSpLdv4po9WodJBpvnWRPdJs03u7uuBjL+ngXQg8aefHUumnrRinn -s9TFM7ODxcxpljCGF4UAgNJPs+W0CKJtRfSJDeFqyjx7W0pvfqCuqwT31RY83Q1b -u0mlVMoORDVpf4xmo9SwaCdH3t5unL9VHiucYmrSaEzR4caMDOyWpkIyUV5sRkod -p2WFNBgBVoDbrQi6KH7qMNGS5joV4CpkLymXC6Zt+g48SnqT6sxcs/O46UzhudSg -Rl0JmB0vzOdnSoAeBXYjDytMLIe0K56Hia6FmJnquy7rSE4eYCq7wV3gaM+gY2ul -nHXxj3u8DY3U3w72ZSVoegdiGunPxR6e8YASM7pGJBhzHEysNMqjRiDZ+XUk+oVQ -02hmxUWr9Gbud9T27XE/GpI5Cv4g5fugd/hu+zbnYRp4vbghNKP1dmYWsF+6IuRJ -dof4s8BulIMIIotPlduDnau7gtKIngJZtqQy4ezWJ2Gm9gK4vR4FMCpe/xqNmPtv -h/Aq5tg6VDlVNRI3YX4COgV4dnXk0ffURIhXJnV2q0kE+rVkb5NF3evR3GpL3Hwl -TL0SsB0jag5a4wg+yQhuGGI3aHQf6JMGadBV6AMZ8bAyfyuQjejQuzRIW4ylvbss -dzpVFickVvF+g2jucTuhjSk8/2qrijcVOFGJ24kCHAQQAQIABgUCSnceugAKCRAM -cFV7WgZRPhvtD/9mVZv1hzGiSpWqaQtJhWAHk7Z1Ok3IAifaf837rgoU78RaUJj1 -QyPqMwawA7MHAyzOTI5IPOL3dbtHZVK2k56f84ItHjpBtuCZPiGFYBdtSxPgxgw9 -Y6n223tWHDz46VVnLpdoy4BKMlmAFEw6/1zfi/PJwZ3soLfWfJXo23MsWc9st/jw -Tzu/fL6xTYXjDIkLoBQBWneeCA3XGV8cdlmGVn8S6NTyUgNG2zzrFt/YU0uVWnpw -QtYAgL95NqOrlneVX8olX8I7IU7tDeurdoUB/evPR44pmttwBYA1L/erSuhJA5AV -2UWiaQVARot9vuZtXpWLWgmVncMU7TG/qEFF3Kmx/nw6rKoUZcanWObhCScQB2dW -zeBowwvtXP8wFr5qWtmBYRt5InPnqhpx4TBWmIj5tnOSpMX5OV4qW6xdZejl4o1i -EMiXxzKWC56smpLoyLYnJx+LrpVK9muw1+yUMiLTcONH+ctig79Pu2paV/fwxpHe -d9oOMzvgkEalQJGEtT1hZGSM1g49lOBlHe29GlFX+S8L4Z2SUCLse3RMswMOYgIP -UpOKL0gBMbqCRoJntSqh+IJlN1RT7GGOJ9fRJs/zOH41crmkxYMjueUAwydrJuWK -AM255tgb9xIOgny1bs6e1ULWdPlyl578DhTBOrpzgmfhWKPzsEIIAyyqmokCHAQQ -AQIABgUCSoLqKQAKCRAyJH+7QK0fpnuTEACUugjUgq+Wroxq5Cz/5PsUtadBLcfD -7p1PnXhxpii7YkN6pzeyGpjdtbEbOUoqZ1eNRQDoOx025WDlXLyw6h0qI30YO87L -1a9jGzNa5/vcJ7zShcfqW4KDnhuC62qahvHV0WoH1Dp/dq2EvnKGqrvfDa+jMPMQ -t9KNRfL97mJslv9Yxxw4Mx/gjzbLiCoUAuzTNXW4SNnS0QXygHFb76l870W054+q -hvb+uxSovvMnDurQgsf0OAs0PYXZHPN6kU9bKbDmvY7XicFMrtNqhBv6v0m/pu0Y -k33x7Tqsuq1nyBny1cz7+sG5pMJyUXDjBgIDWlEHYTdNingshKN1RIzcZsG/Gf5x -plabcVawGRqXjmOLAllTNvYLi2XW7klnPq7zqkc/r+PDdA53Ltk1PwCAmBOMnm+2 -9gLIUZQP4Lkme+gEIQgjMLRkTu+BNENSl9TCbayPWydE0sp3HlPCnitX7OYSngQN -60vWBCQQj0ANLHe6w6/7knhyMvQp3t1F8sY5C7KDhq3s85I9VquchbajL+R6EORh -X98fbBC3CRTuww3RTSz3iKFdomA19f43Sn0CzBvvex952/gLhHgtk7elY8hLf15a -QAtIV2F3WJZScdjiCNn1Iw2uSI1A0O6nA9Ab/o92+80t7CvW93VgYzXDJ9OZ43UA -AtH2+ffI5c2xP4kCHAQQAQIABgUCSoOtjAAKCRBXkw2rC4awZ0Y1D/9x25zjjKdZ -oXIDih/D/zr/dDEiObI4P4ieYParZ2l5NPcfeZhNLXvNNBLYyavZSU40BjGL8r2/ -WB0ChCfbxrZOvSRrugTUpSThNaN3ixtWMLI+RF/ity4myVWz1TEl/JB3mkUJazRJ -oHVfg48rZmgB1yMXwfjneg292pf2w4YrP1/8/uxQr7qyEWCQ5aRcuj+6XYn5OXn9 -y6TVCrP9X+ZMUV1nJc5Z68+ORXB+PkH97GKYYSC/zZSsc63i721+KawdO83ruP10 -2rfybl/nC2SfVae5iNcgWRU92BkIHQz5XbWyNcji0V2qie+iMtrqs000OgzoPhZC -mJYUCl+pjbKdtA9F5PZYwBWR+KDXPEdoDinC6domvHgUpKX4Z5QDyjk/QpDhimRa -GwPPvzL32P7wWzgyyJaAftwsX/uUVQUpyRi8biNrD077dyKmfpW7F+OOYYyxq5p/ -D9rSiGvhPDYxjDVScJBYsnc/85VD+71Oc1LzsbfuuEEwm9L5SmAq/CfLl3Ibg+ok -amvlcz4+OVK2qVikAfdsVpiNIkUKtxdFS3RXaSsozpOMU1IUXhKqeRJxaUmF3g8a -Qh7kWM00h2ocpqKProByUGRZbkXcpoduqxcgR6NKGxoIUyc/R46/to/W0tJBdnKS -kpo34f7FZ+UKKlPaSbTTDBbwt6dZfa4meokCHAQQAQIABgUCSqQygQAKCRAv+c1Z -YSYWtekCD/926wSdYgo51uPDqHUnHM0ke2rn06m6weRIJWpcBUQf9VmF75q1k+vF -GuXQZrNiPLN0JeqZbjInDwvn6jwTLRfVrRvXOXnpGZeTH4pdeS295b/1QOF8C1Rh -ZXxrctkIzEbzDHKLHY3MoEv4Z1mt09HStnDeHznWP44CItgRUncyl2zGdGwzrTEl -GPY2FQt3JV0z20rlPk6YHh9vl02zHE+pG573oDes+XeDzwOkvuTuaP6HD7r3TH0h -/fd0la1NipTgVwzz38JQKsl7i14wz5pqVPqrKNcNhcYoS88M9MHLWSCPjlCE6xi4 -Bgt9zbbEQ9j2iAyiCZn76Jzq2EZsq9F1bysoAr4MjDiU/1OWRn6dYpqqjLXE4OY/ -rkzAAP941uLvyo1DMduynhQWjOBVKZcgfDYVAR30rnNvwAQgbGGxVh898HnD7AYd -FL8LSQwVzcupqZe6r+bB1YYJbtmdC/gu6SW1fwe8apHfKOFXJM9cono31NG2odgO -kmmYhAmhGWo6017hyzxZ3j1Qau3XO4Yy/dRTwIYxmt7nRm6kHwdZO8Ve3rJzXbov -1QWtbWhf9s3rn7MpiShU3HyKgLx3wfxlBxymM4hHuIzXxbT6NPzzGuQAxA+orh43 -cFK/eFoj4RlaQs6pr75w3lucWE1TEso0Rcs8SqP8vfGRdqOgx6LutYkCHAQQAQIA -BgUCSqu5AgAKCRDrcP7zzfxuTxZSD/0QKVklR1VTdef6vhWauWN+Em2YAqjSQhm4 -bLu6Dyjphu52fqCku9exoEnIjBeJx6WxgjVP8f/bdwWOWDpOUf2Ch1B3Jlx55RXh -DMPWA3RFascL1klu7uOiJ1NX1U1tmBPN2+XbSquKkRz+blDAEssfyY8MtmRppsGB -uVOrI1xqHDtXuck9E+JgodkLpkR85NpeyvkiOshwjjaNyyIvkPqzMWWM4hQG+gTe -7bFmvR24hcpSnah1TIehhclika5bIeeWMW7JqEKp8YYG1Suez+YG3IJuSKuW/6aA -YE1qRezLgwk6wh4LETgULTX3y3BLAuuEQnEXJGySpWllElnK7Wx9Ik2My6WHVE5W -EkQucBdEqrfGmpu8b1RVJVSQykom7pAgfF0FAs0kpveTsx7S2xYMTe6muRIHdj6t -SmxJLkLuT1UDgdjtlZwwpY0y9A3k0Gseen4A2+bonhD2VsoNFcg1poKXe3rzPj4F -tROqYwEB2lzkximLy4PkSSyfIHvJ3C7z6WUBga6zCFg3AhYyVhiq9PceWcIllB8r -v01KFEPEB1WND/inaNAC6qrObow2aqeVHAfO8Iotr7N4wd6MbwUsYnjynSvzm5+h -plsqPj3N/E4b/vEnCgUIQBnRQGJsxFlwxAaXwGYu/V32vRRXRFgb9g0iUNVYyQtk -10mOeUEm+IkCHAQQAQIABgUCSuXRaAAKCRAYFHsHO60rB4XaD/9wjqmWaiqqSe79 -fgQKo3Ddh5Nk1kKFKiACwuh5st8iRzCxxqQItKAq/ovKxdFnphSymjemoXQH7XZ8 -9biJ6iWomoX3Z2vhWLzFL7Ixf499jU7Nv9kBYmyXGFmxlOatlTF9JKVJB9E34JMe -hLMFr2qJv9EldY7d7QxfJVVuRPqwIPqBpEbSJawWM96D6jeJYk/rPXI85BMTCsHA -uZDAlzcwuiC7rSvEBEyBIOw1qs4Az8vAfINbjUZbmPyfO1SPGj9qxdKaUevFPw+K -NCdwd+85q6MU9a0vMFksMAQ33G3dcLhE8SV6UD49upNyW2c/a2HveawqJAspLK05 -Rl5MtTBsvUw97W7WJ5n65H/K5Q0hhy1ll3e8m07srM+gD7+29WO5ygjWCV0m7BQM -0t1uPTorHO1cCGJ4Ii96HXTEtsTjF7g50BRVK/5GRgURRSAOoLbstmogAKUxSLbL -R3mqs+AKURC3CPjawM6HxPvENXpoSndKCMvhywqa6otNeRkkuy+lZXwcbnNjhyhm -calAsEQ3A9J1d+rFZw07IWieM719pFHoDrxodwYPIhP1EDb9WYBunujaVaTgG5eI -DNG/IL1CUvnAIQkYaH7eNZy0XwZPLqgUyExXAyd3GXxSL+ogTYYf7VZmf2Lon7+g -HNbwmNj24XsdwkMkeiBcouWER574aokCHAQQAQgABgUCSnG9/AAKCRDxppvkKcD/ -7psGD/47uDuiDgMa78Hn0lEY4sV9zl0PrTXq3T8KNCqP6PK89Kmd7qu0b9smZCmL -WqC7L3tvC8lVcLM5yto3s/U0A1Zy94b7M4B7YmyJ5hEAkVZ1/yOiQGEUQCm8F3Ep -/9MbYcOk1DtHXnn436nn7vLRM2FBrbC8owOXPnVmb+8NyRsAvktISSLiklAEQTxK -QUKSY6b4eAR/pgtVTDedcu3749QkmSiKxiSMOUXxIRPgPrQxLezcAlW31MK9g1ud -YCmQYZ2zFxcoj7H0+6TWjM95ZFr+m4rZmICEbJF8oiWS3R/1A5SqpwVn6WtO8kZp -RzV3exTaJuL73KaY1x05KsP3mO4QVGUOZy9cVP+haJELq1+eaLsNHit1mxAyb56G -8Vj4BMfAfxk1Ec5oiiZWE1I+GViqCdq/ocEcZwSG8Iz+Ia/nQBDYFdG0Vg81cm4H -8zI3D7kY4BR4fMmbs31phHmBdlIew84SnhiNQSaalFEPZUJ7t2FC6EyNr564MFka -wr2L9wEzI8hoztp+i15l4UDMvfZr7Ahw/bEN3rZYCcrd4IQFaZbcphoAtjSkwCqF -n74ZHDkDgzyUM6CCHAHimU2kw51T6yJrMv9neOTsCn5iWnfS5W+Xbm+IWfiLQdUd -fDwUT0KgiWS3HRCcnSM6haHHC6jJHzfLpeU1L7xlFdXCRHMk1IkCHAQQAQgABgUC -SnMJ6gAKCRA9kIqz8Pv1H3cUD/45YlFiA92Jw2LVkeHVWtFsJEVHTeMUjSctF2dl -F22H8p8WnrI4/srqiTdcw0HVh9wNsvTOtFkBkeOmjwnNFl0watA0x6sTjFFfcyeN -ETRNJ9y4CKJBpuU+f0YCVCfMo28Dr07XdlaeLTVpHuUaYZM+hay4bGZiYzVxMmu1 -iZbi/74+6evkQvDMDCmKdI381XfREsEMb9lgu8mFAGe/6+Qh45TEkB4RkdUE8Sp+ -lQufXF1lJ6bygPtXe45maOwx1L1KLUN8fe21vAOy91KMueu42ZbjXIooD/fVndYo -QfQvQ7cDZXsEDkBfDbsyvtxCfN3Jya9On/PKtRahmJthCGPMkmecLzD4xTMGmRCE -2G5JvDmZAy9krgpQHUG5pYZukUvEm32SbLmJYIs9p4BH4Al+31eg92AIgaKUYEvI -8PFMKb2rzgn8x7Ngdn7fcwkd9e9eNs615Lx/FTBYYHqUgqKLwTHhvxQbsk4qk7tg -oeHtEmKUX+9kLuhCcy6Viq1OXQ3q5syO2m2VdfECcwMMvbfInKdhFMEgORTccWwU -Oylru0OlP56f/b0dbsef+eUSEFe3GdoChYI5JMnhA9hva2DEdMLRc0gWO3jfezSy -AOtq5wTmcr296RRbpr5GGXZDU+5TOmqtO3vgf3rwn7oeIgci2iEAu0U3JPZGM+wM -ws5IxIkCHAQQAQgABgUCSnRCbAAKCRDmGQRd8qxymonBEAC9PmSD9DCY4KZuQMrT -8uTMozS6MZ3IyEdkvaxLe5aFNnKRkwjmV1EwNjSe0GASZEMcVx0PEy7IlA07RNsg -KC6gDeTwOW8d9DUpaQj/aDkroA7ZsFEQBeaY2oHwul2L3nCKNH3UPQwkICvmmtEG -paGNAqTQjoAbMHQqsaNY/uz+G08cZnAijLjIp8Vn9ZiFwJXi6I1SJ4BqSelZGi4+ -UTx/beAsMi9tebwF4Lr4dq01zVl35os0YitqtnEvgkg2LuiXpXYwK5e1LvSUdIc5 -bKjLHiTm3VlyFrGF2xeGeGYwXz3UjH6aTW92ELqhb6I6CTXsmP78DewInIOP3oK2 -h9wFgOdPj/Dh14uUpHaNzC0Jp+NX8X44G9P2HeJwI/0ZbS7FB0GKBCV/tCtBM5vS -VzjfNCYm+hB8P59TTX9qz7KMfCaHNsBv85ARVpSMnCR30zRWzi8cYmlAqXQ3QrLO -eTwgI4YpMdmmaWKZhmjOTYLQLD35ajzhBx8fD9qn1Ns/OlGHzEqbnjIicE+wO+ln -pxVHqLBeOCgyEje866N8G00l6dfnFTm2nJddxxVEGWPNgVxFoYU4l+YvSY/VYd+G -QMYk+rbN2lye0g5gDYIYWkTYlj4yr1nbvnoXvRDjJDyjbj1uRTddJt6UV8UtENFA -+RMaiBlijKqZprK9EeG+CfDOK4kCHAQQAQgABgUCSnR3zQAKCRDf9zjC0Wz7ou3J -D/sHR1DHsKW2qYMNH0E4MlgWom6w9lWC1AX+7VKjXBIWoI9vl8/lW2Z4yp721kwd -jpCK6gqN2PydzrZT+KByrJGONI63XakwiCzV1jeXF6Q7SOgOVZmANlN8aM8ljB4u -vmOAwG1zXz7vS8+G0o3qM7IMiCjE1BkeE7Ed1n3FE8udbh46T4CC4Er6vGQ6TRoX -y3QRVcTnHPDLcw1/tCu9GeDatM5ga37vhF+Yng+YDBqQV3gW7zJlPXyNTRpLEWUh -KciDsJMdZovx0rMJsjpe1qR/pLmAEMMVGZmLiwoS1EcUfwniN5KJZlRA0g2yKYyY -Lo+JuLgJfumt3fT4/WVj1dTLhHjh8SEXSQltmdV9lHqHcG6hXy/1VxQKKY3InLSg -8XTLHB+QNlyCYDh8sbo3T+Nhh1cqK+XfWsC2NTIass2vk63XHlu0lt0SUn7GEOxL -bhyT/tDgiOHixBrE7x1uLRbyE63yPxc4wHOIv7at8HGsgrMeb0GsvMu6TK6SIlJD -8rEGa6rH8LGeIyLvmSgOCMkdCkSXDBaQfrFKOiTP/EevzRhpWsPR0wZUzv2gTw2O -/6mzsG7nfBrM1zhgMKUFc3LbMkadJKmsYHioBmpaItReV+Xrh9FmgIq4CLGe3VDw -YITR4l+rB6SElQNp3zGHxJ2Mr6FIN8G43rp26Z35PZs6ZIkCHAQQAQgABgUCSnR4 -DQAKCRC6nHgGHd2Mm+R0EAC8KLGGvloTt3GHoD/R+6ujrxSGyEpK7eyisCq1pBOb -G8e5g6rC45iQ7GarFeEGZMYZA08WOl6OxdVHIpLeOLRPvEEpDsGuFfZUMyrPAnaG -MilvFxgrZbeeZHrj+5g7Hos55/TM3//HFIGlIXSs97YlXK2gHoxd1S/OPOhFy2lY -tsHt6ZZr4yPcC9k9ffWr9TS/1LxwnnANuRcflcMWOCBbQC1P2zxlLa0pP76xcTyM -tK9L2Pdooc6p1HT7WDK/2b4uVUiNM76ambk5y/yw9+uFq6XojyG1dIPTKyWggUsj -H8wwZN8CSqc6f9OX1OS8bpbdLnnjgLU7Bp+brR+dAPUYuZ3fNed/6G+Lfing0YPN -RQ6/fp1hxGPnC2FUpss+qpKT0S4CRaENWfe9+jy0PrZ+KzPXT1YCjTmwtcPMQXLi -nXVsr4sxL4XCUBICd3x8/Llg3Xtaar4e4uXyvcmv2FBpVbtXb5ej3raDvrs4PEYV -7fGT7Ry5qFV/tHnrjzlTFPhJmr+OcYZof3vLKhsEPsxEStd7japiIMWghX5tM47U -7kmydSqJjDBQnev7t1YxNZa+E36hy9u5FSktaGEsAEPMWijZ6derYTorRe7gWAba -CNAmaVbWxLfQMpAQSFQTh1t+KuHmgu3S5jadqInI7JqTLhzRfH8zjCm3XaFyzK6m -kYkCHAQQAQgABgUCSnSKqgAKCRCi0+fPUhNGtOtMD/9mNEmca/t66LYRbjbGgHwl -/7KTy8siYADPP0h/UXNk2EDcy1QPc2XipLdWhvGI6CeaIVKRzdBBahXQO6xg+0py -kLV3k5Gi8/zRLzsoKFOxH3zdntLw49couVQSftHjGO2Ou/iRHj3HnzXriMlLU6k1 -NVD4jH4NyewQzbcaMu67499qoM8TGSbBT1JKppKPprdkCvSpJVsBrqvAPgWBMX2m -RLqfHn9qtTBCkevRZ/rEeOIuNUixPYfIhoh0cRdSESSx43QC5/WDMy9FzMSSYgkf -t6s29LLFRJglQUvmyp+/PM8jxjxSnXwbDcp86uYu6eLzkdFxf6b2275Tl7KqVNEu -WPtv4qHRd51WsDvd+o798JVceFhk53eQGsyPzpcyjI9svX9M5n2sNFNeoX2KTlge -xogks1sW/bNVCpaXbMjTRq8H+VSTKOImef/PHLPSbjOSfGuVPeDBh1FYV0xVCyz/ -MM2S4etG/yVTWwJTIVc4nE6cAXb/IGUZsMLETDNN3RUisXG+EtUyYfLmRNP2ZrBC -hca3Sopse724n8nPWiaTP9oLVyhsohlm938FSHUxyjM51g2MQTJa2TEZNMyLvhaW -mUozDAIZv+BU0BVicf3/AB7w9r/on2mAQ++a9puokBoZgukVZMiS6bABAgwzver/ -LPUnKjpLS+NRxrWtZWYIEokCHAQQAQgABgUCSnSc3gAKCRAzvhoKjC7Y/1XqD/wI -bmvk6GIct7JstORstAt5SPduzfDpjouzK/SEIzRdR2G6ajeQUBo9L+2dkSuWj9vi -aveLdKra3f9Pfe2xhW4Pg9odNXty2j3iRvx35MCAAK9WwRlx2JzCS1F8/phX/Y7Q -1HaKQtJpkmTfnkVoUK4JVDL8s3oNYcaXE5OGvYhdvgur3F/G3UPRCxucxO1akt8s -BTDI2il1f4LyRsk26QeS/dYx+3ZZ+LKssg5feKOYp3VvvZwflreBM6g2/hHhqf8h -Z9RXSVT+eXXC7hSuH0eGmD4DNVfo7JpZlWHPu5kVQLK562+ZhNjvCmh2ysDnFDBn -GgPuAeOrqQqCsVRDbZAqaQQobZtuRnxYhcSBrATkTP0ytPnt3QdltFbKzV4hjPj6 -08882vb62hxbMwBdnq5mknoC/A9vsUv1sF3MGuW0OiPreEXpYXrV49hfdBig+CdE -CT0b2DsxL936ooPag332OnaBUky0d3ivCyM2Wq0wpTieKILllMp2jsf1HS4IEeE7 -6s3JakTIs7lxIF5aOBSJaZl81dw7ouYnBQquHQFDnXEd1LeyaNY+Mv/0YxSGSgYt -+YnLQQM6+pDG1IGmdKZWyJlSlyyfjYKwOWMANQ+azUJSJmLcBAgZezNgrZsrJESi -SMyrQ2NXdY70mCIUMlv7lDcDDJ5wp6NnjphfHEJfUIkCHAQQAQgABgUCSncPJQAK -CRC5ESBTbYUSjYiuEACh8514nXtWb4hpalHiPVQzjLklXOKMOCEAnKOpwQiRxF4i -+4pvcP1IIuaq5lAOo5c/9d7WUm0GZ6QN7NYm+2gcc96QiYmNiOb5J4y5gQ9pI4uv -Aq1phI3w+lPS3MoXEe4O0UOBMIfqf1UXlvaogrqRDWYo89AaflB0lj9Gfa1nXtjd -u13Un9DCoFxX6FAPRt0CnrKQlRIzFnXd9ZXxRplRY2ImWVBOIanbOhNbdbcMMeaW -Af7MpLImxK9F0wguT/Cnm3iL7ZWij/15oXKAXewJ1tjeVlNZt+9cXGaGwRXxA30m -08RmeveINYDTHzDsS284PpSPoaMR6djlD7c2OCkqRO8Dx1GUjW5LqEk+iNlCWS/M -VsyK5FGydkzZOo71JWeKuyt3E0grQrmAyEGTjdL6QmHokPhfrn0p/j9lMAdR+PGQ -g7u7vc5BJccLPSKJ7Fj3BiD5MQD+alTbeEV7K1VqPYkM81o66j0EWZTKewmpy7rU -m+jDjRH4vcRjVHJTcmfugrjesO0uauLhquSOMSvQFOupwcilwMflkoT1ImPYYu0/ -igEqTwPfEFs8yilIq5duBIEAh0tgdsOsHRY9v2LdqVx6JTcILSjh2UaCDDgXCfS8 -tNRO95/3FbzH+h0x3SaARFOMQ5MgcTBC503uifG9vwulyecEEvaFCl0biWtaBYkC -HAQQAQgABgUCSndNYgAKCRAsfDFGwaABIcBCD/9suw2sl6myR/VVQSWhPxQcyZrf -wNHrvWnYr8+zD6MtG488/QkZjNrwQKrVsA3NVfTP3FP472w/bxOnMLD4MzmgbUVj -D0uqFHeRqwgztomHUS3u7gSqGVumshOJ5GNnYs57/OJq6OPUkNe7p+JiyCK7PN27 -U5IrEhNdxhxojL98vrcwOs1uL4CpnDFd8StOltYHFa5XHtP3PRjK0iXwNln7sVqV -VCzigaJsN5cEY9rCSiWykuC9LWCqsVIfrJRlGkrHXGTx/G5nonoFWvlQ34KzZmEl -W9wq39tIby/lE7CiMZ6bPERO8tuAEBHqmyVF0FSNFYSvvD6M/7RB2iR145+Kt+pW -Ox6PvOUDYIhjsP+348k2rIwb8ArOiRWO+X6N4e+dq+AU7YCwBQM8jN/HE2O2bfHJ -eB/UkfaxmIqLyrMotJEHO71bG9pC9nI51LUW8zxaXjI8z6JLUYPLRCKWZUln5THy -8A4VWqosbBUB81DaAqpPBLvoVusXZBj4r+x6L5M0dUNV54poiNLCHgysTLw+LVj3 -xZDVPSu/THCBK8yt3R/KQ/iJql/cWvmMY2nfuvNfXNbbuLRt1ODhduY34zCz2VCo -UG1kdkHukfljU1uBFIpf6fl1XFpUoPZn1dww3oECIafA0YmYGgrMx9IxJ12bMC7+ -iV/l0aP5/kUvlcgUcokCHAQQAQgABgUCSndgygAKCRAMcFV7WgZRPtwoD/9FTwa4 -22ZvLXXvbJm8dQBGkMwoqkdTZsTA1JnxZc+ydyaLDgHGjXEEW6Vmfz02SUtP6WrL -hsNDk/bg3yQdXyKT5mKvQZi0N7CqQ+c2Cd9BpaF3+oPM5FU60dvay87pU5qKbvye -DqS46il+/AwZQc5tDdSnJPAXOuxIoWdlOkheTRhy3ASNzQ5U2JWBxS5H4wTjjlEd -99i14RNUkeXYthys4xvlGXJRfb5MO7vqt9BbKOZTDOJNQTa7s3ZCpzh6vTmCC1Q7 -T7wTQsccp1KwQko+9yeZv1KXAYGNgtjY9Jd1cQjfYMa64BHzNubPgntDIY1LjF4Q -N1xgQ/B8mUMhj7e4Ad7aBrky26BIC+p739nEzdreDwvrGz8pv/JAEhh+xeTEnGXh -etN3Bn20VF3QkSzAjaKhxgYeFAEi1+6S/wVk1m15RvntVIMy/nePqy1I6tWhQnD8 -HbMBd03Au0NGDTMcKFv8PLvbSEIT+e5RbXAku8kZT7PNu/5lKyPrdKjbE6Dqdi+O -+MkwiAxcQWJfV1Bcc6t9QCFDK8F3VKr0p2pbWx2zd58Wihvh49zVyVaRZ/M/x7uN -npD0+FLOdoYtNxHBkVYch8SCZNOuy+Ayw7vR7uvvGqLsqyuEGpxvYBZqqp2BxbI2 -9/dbnpRsBg/Np72gc55uV69H0+KYt/8H+leeNokCHAQQAQgABgUCSnmQvwAKCRCH -L3AsTW4lqCVED/9wf4q0bHNS+/2T/1Zohrbe003STfciuzkfu0jqEmom1nHL67CH -XhLZYNXScexb7B+B8woZaujj0Kf4RTOKgptzO3Fjo/RmKM2SOhIPZe6hVxPWspKE -vaPtGedlCPU6KjItTQBcH3B8zBombe1r/qlGxY1Vlnv82xsoHBLehql41vApIcAq -jg9hdA9RwT1HZ1JSgE1MCRnJDWdRxH28yb/yyOGSzL4v3XfifNG/CNXhowFayP21 -u6zIaIOiAmCUDBuQPdsh8v6D6kmTkiV2xw87CLaimvpDbbcXPONycLTKnBNyg0LA -ws5gFvSWHH8CQlP5zhoC87ApY2Q3nWPLClvyXJJhrsHRM8AlEc90x97d1T1JeXKq -n63NvdceD2jdnsbYbIUrVfgQi6XVVXl4RZdkr4qx77HUfgux9brIv0lst1UCpaoD -TePr4aQKjvAbAf0GJFZt1y1dCCosOBs48MNZB/zqMNUVCOQcVsCaTqY/BfsC1lao -W5hJ/ulzIKObB0G4CY7dOz9kPbgMBcmni2wAEjeuM4MGcIRtoKYj/hyepg01vs8M -sjK3GGhmFyy4ws4sOBjz0HEz1Y26oh4+Njs+qwyxvG5yPubr3+lqE682gnXZkvwh -cGu28PPL48MbMbfBw7MWeSM1dmJRc4b0a7fUZAPDnwJhEPF6gI1fl+OC44kCHAQQ -AQgABgUCSnwzjgAKCRC7OmgBhkmqBoPoD/9GCMi8p7Las0vAokVn4aQLwgC6CJ4N -UJAx6jBpYkNNh4nLzPh5qjHRYcDx0JRgXQ/ju94uKMZrKeodg8UvvdCINJlTeauq -KMvTBHjPKG9pMT2UfmXusdCPUMX9ttrmllRWt+Cj5IT0jDaUYFshbeLZCzD01ofA -t7uDLf1VAgolcmvAifBHKQ/W89s/qjl7W8W81zD3QK4enyTdPEuInPAWbwcu5eEP -LgGgJE6TdWyG8m/u1ZcJjjZntYJXJ9wYdfCqYJltDnO3NAQ7Coun2mxIQUeiFsIK -MuAQ8q1vvDioN1Yf08GF/jMqkREBMTv5k+Kxh6yiMCiBeOedip1jidHUX6gTMOY7 -gHCZIuV4hUiWuOnqVcetY0s+itx7juEC2D8S+JVmfQ/1CDZTFmv2FEAI/f2mZ+ZE -+HujhOmhsd+kuhMe8DQ8eiGid53b7qV7T1sfk9S5EVR/uRB/xXS/g5dvrAEkF3vo -LBIb4bQD0qBJ8YCjzaxO3xom4u9yZAUgieasroaqPjM0KLXmqpvOz5ZEwrdezSMT -vRuRIDAKLg/dx9XHCt/rOwaSVWaqRjJvIuhC7KJU9U3ZcSuAYYBiW/M8Cmv2g6Gs -tbd7bqJ2LVtsFCCPOS+Y109PtMhk1VJIZKhnaP1VZicfutaH8eGOZPEF3JaRxfRN -mslQ1b6jvyjIwIkCHAQQAQgABgUCSn004AAKCRBJw7+JJ1U9LlpoEAChW3reZaYS -oDk7A27qpA+H9Yr40Oj2iBAbK1O61BNLrWumUaHBnhjMXk/hCM5xsSZvQFtaYpL1 -PmdoURgCil2uWNMQ3bpqyFraL5uo33zG9mjqYILoNaqTzDf512nom9OUiq1BTTaf -PotqLLmDqZpVjVqJIpXgs7wNhpySNvSglVhWY2rfx9iECrlkY5KM1L77sm+ceNGV -B/HZcWgF7drHdBhg12RYHrg3HTzYJIFgjbqsheXlnyRA1+5mWV7xXPWNX7HfqXA0 -77DdGrHZnLsiBv5Hvoq7kwxZrlc9ER/LZoCiYrnqISlKLDdj0bokmuotPa1By08h -6B/lDErHpK56JXNxUWuSU936nOMz53ZrlTtH8c3GDFbW+ZLqjFI2bPjTvoEVe7XF -uGZGUIWBRlib6bJfKFe1n7uD9RNE+QWZrdNCV+B5eRVLNkxsDnzbDACEffHP0Ref -GHxkw+Mi8pH1KLeaiL786dmy1xp3/rdjF+leSSmO/8fZDfxkbmWVwMLF77dPNnz+ -VcChzP2ovQYJNssL4jq8P4/TwBg8eeMq6IayJXFok7xT6FYKxVsdZJ0fsmDR0uXN -KHJRUDwbJu7sLs04ovhtfIDsrB94kFCujld9nR0dXRCeEvHYBJ3EU43ALX2UjDVY -Ejv7LZKsU6CmgwB0YUkJ5UzOjVpT1NRL+okCHAQQAQgABgUCSvGDjQAKCRBNJJ2b -I+b8Oq2PEACDwuUtIc9rBX0ZJtEnT+zs2yMNl15xYfhg0PCmDhfvKFmftY16L9TQ -lxxHWwvKb//gxtduBJqgiSak4hokznBUTpfoDFlyqP7nJtz00fSJ8tFoldp8dKId -Hj+XgXsTHkIV34XMQshhR1ge4yQwEcm5AtP770GvWnFzHGtn6dDVYKM+UKt/pUMH -Q785Yv4QBPAWa3ES3Galph+Xe3QcfN2y6i+zVbiodM0wzQbQ3y3JLKsnYfY1V3N5 -2FaEn98kUaag6SBAadV/OVtHR+6+MTBHOdM+2QzVdREZn4YoYMaUvDNJzmdM0oWa -Ld7NbHwDO9ueXEd5dcDB5Bbs1VnJISiLr+KSTYuxULgzKmext9PUXnwJ00d51YD6 -v5pEHEpCaJK7iAAU3wTPT+0PSscdaw8XKrNYfYhvjWPpeVIVVmO8kfYkDj8QHtmX -/ZRViW6ScUYpQXQHf+qaG0sWUIaAraII62mAsPpeYscK0jflYF88VBUKfVXbhk7R -ExK7ZvgnKQFhIDvg6Hxu5T8TUBz3UnVx8tIhv5SxL8GBqL0zLuGa3Q6ocwptkI1Y -e43MngvsrdC4u1joi+8/MsWXbFEoVppOma5paR045JNcjdFF4B39nEXWyIy6MnBX -Qi0Y2wSwOIQjfB5rPLhjF4MWKmuXyR3/+3E4ML++A+/7O/dxMj01O4kCHAQQAQoA -BgUCSnWiSgAKCRD2bj5Bn4T03iSTEACP6WBG4UTYz6DXQVY3wnmkZJuycv1GRy/X -X6HUbLl2wjFchzuq9FezLEx5iTLUJYJzaS4LhBuW2OR7XP0HFL2DKvJJFcPH6Bz4 -h7X3rfltyCkusL7Rqn3fCp3W1j6z0tKGczFg7/DAzz4PmRa6JtTQqouwd7J01NFv -qvYnNmL/240RLmiyuORGKhR6j6jXYq2dyr3geg5GWR/pnzwhyPECk6iUOwvvC7GF -7nsdRhMGUOUXp57nmIa9Uc+zLMTMqKOiGUZa9qX1NgA5mt4nwb2ISQTCYGvYX4JW -ZLPvpcftYcaV2u6TXg+ZSTh2xDw7/LY8Ka9Jq2KeP+Phv/cbKnsE5Ty8kw5Zneln -D0lzSe8hal70MmanBxHQcKqUnT3btdiIrAhGjgqsbFU6q2HV9lrjknY4XUgEzLEq -ML8L461NG5wttyK7c8a9Au2REp+2uPisPQGMJ9mireupsxVbf1sNlCL59daWpe37 -srd/gGIa53nrZ+EJEdUd6bUSJZxRfQRx72focxh1TfbSMswCpnXPMEVx5UkQGI2p -/Ol7ax7JU6E8DZymV3HBqhp4jF8fBRfPMpyMW9e/ios1pj51Nex92NC8zvCf0Jk5 -ImGgN+sTgCPxcBhqLUTXcYhtiYpU5B269CAaw8aruKqIlk7nCLQhdHmrC1ChLWEN -HjE8CP5Wb4kCHAQQAQoABgUCSoIQgQAKCRATbpzxe100LSKlD/0ZWg3y/NWdfCtd -69zUrk1H6NG3pPzvlvL5DzfoHp0+8pnmI2UUmNwtwPFsAqSqcHCYsEVLIAh2ZcXV -u1yXosOgZ5Qm65/5OADaZTyfiz4nKppwGBMlAp+M3C5uAcLkRjYQftE45vKLYsPe -qGGiXsgc/s/47VoRme9Lo+zIuw9wBCR3xhUvs/cWy0NYdz7X1YSajWT87YNYEWuB -cwZ5kRasUf0TF3q6RcnDpt0eCrfxlJbXPqGwS7HpXnDdx0g41Cb2RIBvi1fe84zZ -/SBWduTPnMH9QHprpMJiBZAhCcf2+e+8YGX+C3XBjLSRTeG5RvHtwNDw3M79Cc7P -L2bQn+E9u6KVDGuZ1jX4KReUv8jXVmwQf5T47AxlhNKSYDgsv8K7a2mj/K+PpEif -Ote72f2FuUSdRMMOBuc11OfQWA7OdPtn4fX3A9zeBZbnMLC54rie4YCkuIjLeFGn -azKZ/dQis30lJozkHqaFFRPBsQ4XUmQXCnQnrEbUzBW8pG2CLat5lv6mnO9wpU/M -rgqcIks82El1UR5hcGSX8vMy4V+u170yzfqg9ZTk+ZS7DDUGDKbHZi7qKwD3Zdd5 -Vc8eXMsfL+3GyvZiTQJJ36nWU1D16UEeIfiLbTRcx6o2v22mHN2j5rbFylf4drj3 -qlvmqBHaGmxSyyTkLkR/UxLJd1HZOYkCHAQQAQoABgUCSols1gAKCRCcJ7MTQrdR -HQt0D/94BPp1WQlG55B4gmkfo5hMNonGWiIJ5QnGdnv16bPEFtDrexXmSi4OP6AZ -JCukFsIg9zzs4Wb0VXa7rEgTZqQE2gIyIGSMx2fwm6XRka/Svsfg2l/aUHCGmI3X -t4VyEFis3SonOy2spY72+MBhvCMlZd+kX7amuMQ6WUqiGD8Fgd9AvUdfqKdaabN4 -t79KVqYFn5cfrF7nBFTvDkHrVfXiG34g31ucm5bA7WIUY48K7BHZBdLTBY2s78yi -Qwrp8xSmV4MBzR6zBaO1EYIvngQ3sSIiJX0vMg45tu71taRo1rlFlhOIKcQa+QCP -QUoHCM0l4VzZpjo2gSi0rBwQmRDlb7fHd8GVw8HcfPp4SUQQqYK3yBnzAJ4eHRdm -w5bacmbFkf3+/yafKy23w+ztdMFpfZ3HpEny6Fv8k2UIerbIwxHRVGKB8reWJHdu -IjRvJscflRs169T/jJQnX4aGkVqcRLrpZSWTwRKMcrXnl2AG7EBb9LHUtQ4/z6VS -tNlhtfvJSxGwdRbOAUWYhSm21lPusHD8tGoaSWjV3XK9k+f6km8tKDFiPYIFyw+4 -uM9lFo4XC6CKGRj/HVpzof2ALOFo8CdleG7cAOspCuXR00UmmDQr0cQCcy2duAEG -/0HwqEolH/RzWFbEnDm3HWQYmhTySghZIk4ZNuWsQQ0ArsZeQIkCHAQSAQgABgUC -SneGuwAKCRDJENkiJRLjx2qtD/4wv5PifYy3Bv87xhQf58BGB/nqNm/4Pxj2ZI5+ -UiDzZDefL3MRHXw8gPN3+XtJQ3mGTQ/lQkYnkLrRFL767ELPgS/AMvxbASvV5IqL -AE4vU4I6Bomj5u+AO9VjvqP2L0bSsv2yLkLpZlr96LdgcMGP8bkyVDICkl7DxoZ8 -cqvFoKNlve0dW991yaEDPA2vg21l7gxC/P5dd8Jm/cAEoTT9111Td8bHCNgMswlo -usLjL0BytH09BrKpnJzVqOmaBQqPp4ZPJ9VmO1EQbf2h+3udIoNScwwEuMai3jpC -n7lKC5KI57L/35x3i+QC/ztn3CxPOk7tHp1bT0d1fDYforO3PU63flFy4tG2sgxJ -LnczHnvEkOYz+7QCDtBGjlHPiKzuHJlRx+GqiAwzVG2OmDfGnYhhGFKd7aG+Mazd -a+R4h80Uk+s7pLhOYhybZnNg96yzIvlS96RCyqg2w58SQpzKiLXTIX5R0jPRMjYA -Wqk5mw952w970BazdQai5GOm6XVGK0Qa4EzHKGdSjpBHS5DXN+WZi1+COZmHLOr0 -29CcXPs+mBqJ5nRpqv9y5n78gfpDMbp6oQ4P4DIFI9ViTJANF/5j3PU8qyzVFEUb -mbc7eCTZM9yTv5dmAx1dd6HrQfp5wbpnSjtu85Htkq7aYwBxgLnIWoEIq1l2tou0 -MYtRT4kCHAQSAQoABgUCSnnC1QAKCRD1NqpwER1XFoEgEACpz4QnNIBXubArkjNa -SVFs7bIqzk19h+Y77rvzirnBQiHup37cqJneYA/wGeJXzKS5mEMxPy9uTPxy5gVS -SgZTLm9hLWUFF5IYIPxXGj8X7zbDeUalO7pazKwNcMueyw24E5X1Ca6BTjOSp0hz -CCVTV+XJV0FYnSiivOhpIjQJC6jcdtE7dyH4KYGY5HpgUehSoN4A2z4d07t1IfKw -zF2iQQ3b4/09H2KgPsLF3HZC2MSoclynH83zn7RvrQ/T0Mvd7W6FIsJ2oLJQkRNK -1CQeAG7mtHnLkP/yf00h03PvESnNix+YuTphdPRKhvNHsVdYMcDoPIqlwmLLXJMR -veuYuPAxrgTi9J7JSTblDRS3cGbnNXeHQQ8dNvta/wkCQ0OKV/mNP0QPL/t4HMZn -k13iiGTzszDsUao0rQdpLAbI3Y3lSSAXzH8oc8eX/oZ89UJ6wYqAnvazF7QzsoZJ -6yrcG5VqCZ80d0gtPlbIzv2td5T8hR44PZKm4jYcgG90W+NMabE1LR0hVQQUb9bc -fU/WPIIRrooByL21TXcYfdJcIDUslnzGiOiOQEQBvjaQmXvlb2oG2nepTQFP79mo -TI9o32c6V6wL2dRlKCSIZ+tEciczUlWwoF9aH/l0X5JPCkTjMWxXtqBIGC/z3fTf -lUR57G0W6M6ZlPiJpVZFMg0psokCHAQTAQIABgUCSnFm6QAKCRDGh181Qc794B+m -EAChFAHmwTap3REToQmXbXq9eQy6R9MyS/GF4VUPhG80WHlX6p7CZ2BWbh4gWyV1 -7tiLgTHCPdvnfFyOiq618uR1PyfYaRedeKmiWJZCAVgboFev+7Wrh3QeQ1hWc0Pm -7E8NDZaIII8alzgi3rXZlOK2d4B1NjR9/m77dbKBuEuGBmLXeim8y/pJO3fVwkJE -RG6i+EMWGIbmvObbUbd4OcMXMCF1FLlxKzoqqdDAD8Bt8KspAVD9IVCXmruNxaoI -6rjH2OW2ommMezaE2cR7vPxt4yBTAxRMQUZGsYfmiVfp2LTBvak9TCGRFBgwV5a/ -JjF0aYSadbtIhb4TdWG1LPVsJkNcExxfcy1756mLH9m4NTT10jOypF1SqXJIUcqp -ee0x2TN5Zq9gBN43T1P0qODTtR+CcZubPLMBaDUWT4JTsGiTsxVdrrYEt3WGNwuy -/pi6OfZuA4wZaohxQdg4+5wxfh5+bNODVIJeoJOVFKrWaCVX4Mdh3nOhhR74tD7P -r7aWQ6qU1fBtE/ZcL5df7/C4P1IiDtWI9WKJpuOJggljpv7VHHHNMTJAEx1nTsof -beLsPMkdX3rkqoL7IQUQjCiyhPNgoFjk65VK+iOlr51RUsG8RTK6rcp92Ji945/B -Y2SNu8CQDTjljSD+cXMHMIQgtGLWrqoV3zIjuNoRniAQkokCHAQTAQIABgUCSnSF -LwAKCRDNSyrzoKCqqni1EACsdKy4UkISPRWg8m9cs3F/AiAEg1izVjaz9vntV+x1 -jZv3L2ssfviPeIR3ERlR/Dt4ZcihUbHb0BtfcweRX5AaZ4q7yfKIKF0GOb52rfZL -rvZOe1LcXuCVnH+/mnaEp0XDYvtA8mQtcVk0giKOgO8nJ76oXXuq4NkvoifnubKJ -NHGU2WKvsX62r9l+6xWhjDa7L7amHaGw08upxmv6eItF7jVDgBPX6nElfdGjaSBW -CqlglDo5LFsDnZiEBMAUChma+LORrs2NHnPOWL/0oYKXe0u719Aj9mvjL7R2ExgS -GUw9hLavIrotrKjYSDAmLSWcKdFciMLq5gRbnWxMeP5E5lsLISl5ErKdszk1e70r -7rk9cDWjZxjQ66Div3PXdsHxlBTapeFigpFtZtB0tM/qFegsCVaCay+jTpbMqKsM -VwB+Rs13Hu5BK748TdgnAhmt6hDj10qIxRpA1BhjITs9FkmReqMo4EkNCDh4PfEO -mtF1hNOoGdE/qeTuRK5wKI56D3OZsHyYOFn/qY8ldBNJTHIwNPL/y51holBGZsK6 -QHI+hPHhMEa7eaBsnvrd2BYTEM4+HSe/DnAGUpbVlsEcY7tEK3S2uWqSNtC3DDfU -tIpSmRqOGb/a5cvsCvDHhpdhtD3xI6iRupPdJNLo5+Nlk0mWdZvXANQffjk1WAbV -6okCHAQTAQgABgUCSnTJ1wAKCRBYeXlXNEJoTt7XD/4ybewrF8BrXrn5P/v4D3iS -j92PhnY+mWmGrs4uMvD6KZqYYx0g/Fw0lMLihEk0rNFw+8z0n0NbNicQhHm5R7h+ -nr1Stg9QYZSPoOYVjX1qQjOnxcEqlRoEZEWtj/uJuncpemu5jF+SMcU4e2Mw8FPI -uodzUwrwHH1sC05MaQJxxu5Ho1qp8HCU7I8rRQD8Ck80e3n50o27uDJ4dcKrYaln -AWFxWmd3Bu22XpbsVyOq6nSmb8GAQ+yMafvFyvspl9FGLeZ7IGcr4zLR+N0Euf1t -gauKREyZ+6EtMNO/CLegURJn6iDN4nShZJAKN3UuFlI+Krd13kMbaYJ9rfyOUYit -85vVk94lwsHFWSmwPe8jy2g/Pl+eBk5uuBhJhmg+sdMR/4aHbwx0CEuqSt5G/BKg -iQzjLCCxBLCVE0EmsyA9ccLmjyuxCcAnaorq86Bl/bEytSv0ZgTWoY3LgfU9sU/d -4ugdxbOrTj6rXPqDLNcA6lU9eTRJsPbZtOzmcNnVh344aAkqdeg0TqZFlwsjKgj9 -H9LaGcW430r1EUK8po3sA3k/HwkvSqytyqmz/q50lhWrhTgdGs3bVUAM/KnHuIw/ -AMOVO4oM4Bz86ps3XMwkU4DPpjn7XphNoc0j0S02Kx7OeaM/f4tHzjzvcR2V2SjI -5QAVEd7mGocAnT6c3zHqM4kCHAQTAQoABgUCSn2M3QAKCRDNSyrzoKCqqisaEACn -IhpsQxgEzN7UhTZS3RJ1rUwFIU82TX67sU2oHehdxELoZEODqq5P3Zn2ZgXVA9Jh -Trw29dh7EIYsxStwhhdiv/WJQa0H/zW7w6DLyjXujZNoDwFiUjDUFkPKx+buk0Yl -oVHjT/J/EHeJ02XnD6mWZ20Kui/TKfbAgIBNs2xQimxzhvb+tG0ogxGfkaha4xLK -nENwtOOe3ozCvCYB0QOiojKW6g0UNjsMYSAuVXBJULVopeIsqLm2MZ+kJQir6vMa -Gq59IU9MH0QsRiHuWfIBxXaV1m/9aN0I6eQz5G1fvYCfL/sHXQoYShLT8/zCgiOt -iN6VbxCs49882VmClJdfqWmeqWUYgxlUFaNE2pI39TWYBIKqrXQPnnADjKQQ9CCf -WUvJ5WnoOG1HpVTyEIGTbLqSywQSDqylD+Boail/AzlBMVh1jgSTjAi+R5bIqNjG -UmdroIh5ky24CafDPSyJQ/M5duk41SRGZh2dWTudr7stOSlSCqxietGIRkcEuv3m -5fCfllsJarb5s8f0aIZEFFFydy+CIVvJKJ09/mFwoYJsBTQLYJJFG1/wTwRYu44V -+oZLiJBEsbeN62vCQA2R3Vqldd4lyYtEkUaoLpyyF97T6yg63h269C+7yIeni0dt -4gedvHSvaPQaU9d0m5+DdjAlOonmr64Q0gXG9efqZYkCHwQwAQIACQUCTRtbZgId -IAAKCRAAgG8r1ymkV7dcD/9YpJX5bk+ovR6eVN8eELp4mFQSuVcfsVx9gLgUVUdu -zz3Bg9m/TrZB2pV50t8U64m9YiR+8Kzaomj2rsJLdpN0NDIYM9CoUyyePiHwQnGt -ee2Cx9gONBja4/V+9t/tbWNX2XfqsWbj4WRI7MTX8eCo1fQvBU74n/+MJk3gvD0D -tUP9cash1aGsN7fj9RbXxaY817jowAVEB22QGkzoAnnfRRg0ZsoJBbrhn0Ke0Tes -avLZd18memrHPGytSXdQfjSMWwaML9tP2jN5HCTIEsT6XgPWhm16y0ukqE/yoBxv -g70AxseLc1DIRhGhQLoRU6GSAlR7/JA+DRKBrUNvp0CO7dFmBqMlTNKpJZvIruFP -xZvNUcbhCPSDjs33of1feUxv9D8sgcDho6xYFg1E3xQ6eUUTVliJxMQQ8lzUagh6 -0bR3hbKbyz1v6iFzTkLy706ZsCno9a74AVDQxkuU6NmBY2VeQeQ3KjzRl0UnLp6u -8cffxTbIPFtDdA3V5zgvrH+4hV16F7U/E94xMWYEfdTxjgDrP9LiU0+cFKMGdhrJ -wUMT/rWnVSVb1R58KoZv+/7lgFzXAG03TAPyVsVhbv1KrCXMjYjzjESYxV+jgvh1 -FnmPe8GAfy31i9x3WNK5LveKCgH5KVfUlZW1XNm8DZNA9K6nNhd5dwOdjb+zkrCk -6okCNwQTAQgAIQUCSlDEtQIbAwULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRAA -gG8r1ymkV5tHD/4vp+0Oal/DT69eObBpDgMpt6A06WRbzkloPRuKGUzBJmPMLGDH -bQ/MBBm3FwNtC4a2KVPp6h2bzbKYWD3fmiZNP0XaSQXZEt78EvgznYcS/0QqG8Kb -Us+Pr6K1WEoGErMXpXcAPZDM7YmO5prcj4dym6hMm42HX+NELa9UvYFrsjz012/i -zZGlu+F/FiV6mZnbV7pk5M97nGju0X4EejgiZpx15F3qxIkX9atKr2V1Qp4SUGM8 -udnG/Wg+ROf5HBlHRd3qpx09MD5brCGpenpBaQX9gZk2ndJCYFX+RYu+OvqP3eeP -s6xGasOB1OlnsEizwyu+akCeKgmlKsTV7J8yFDL45K1BygcC+QX75Aqfblcu8DjM -icJMxVcuZanHXRFjELAYUlDChYD1PGpfTk2PafUCHVhFuc2KgwwAV0iOMWEk5PE1 -t6yrf/21msJbtxhehi5y7fmKYbQvKvYrfJT2bQaddECX2as984Bnhr5QOACBK1ZA -ImrVbwDxdj4FddwXUUJQAERPd5mkj1KBlxah8Y1NDVnebo/Lt/qYiUIabcWTGeKx -XMI/YskfdosuU0gfbErVT8nYHsFHyckvK3aRjkLKrkQGeNdX0hxSZNQkyAzoiKL0 -bo6NzKGTGf7baQIqmjin6KM/C7bn8RCFiCtLwoTTXwp4EvpffPoFIlGHu7QiSmVs -bWVyIFZlcm5vb2lqIDxqZWxtZXJAc2VybmV0LmRlPohGBBARAgAGBQJKUSQRAAoJ -ED2vVKIe71J2GuQAnRPqattDvpfmZNehfJPklMu+g0jnAJoCqb8F1CEN0Js53C/S -/rdM00diDohGBBARAgAGBQJKcrYGAAoJENTl7azAFD0tK8EAnivxrmdcbgIetb2Q -A7Rf6+CPw3+lAJ9t84xqLPLlbjpyAhgjrzfFaX8254hGBBARAgAGBQJKdXO7AAoJ -EGnSph3iY/zUvhgAnjUP1CjoT8gD0Mo5p8SxoGkOB+QaAKCKI2s8MqKsbYcIEWq2 -9DhAcM5DPIhGBBARAgAGBQJKeF01AAoJENXKmwTyxCO89scAnAn1NpaW72DBJjXZ -dwFDTnz4rrb0AJ0We7t+1ViyVD4POdM9SPk29KnXKIhGBBARAgAGBQJKfTCPAAoJ -EISJsU2IB1KbUtcAn2DOY7G+lnoREaFpXLmGnarUEi4nAJ9hnh0AYBCQRF2BX5dZ -sHbUxC5DnIhGBBARAgAGBQJKgpuEAAoJEOMhk7bEKb4vYlsAn3cYx14xrZv7POrg -5ogEk0xwF9IJAJ4oN9cGa2yuilC7Aa2Xp7CA3iVzzIhGBBARAgAGBQJKguoHAAoJ -EEEhx0MxcOvpQY8AmwbcrOxJtteHmGE9WYD0gB4vtJ/0AKCADI1NLa0lB2qGoKo5 -m8bsNYYDRohGBBARAgAGBQJMTez/AAoJEGUd81I9I/JdgYMAn0tci/4txxWN0ymF -5fzxHNSNVg6TAKCu68TQb/uc47XG0GpKAMffG1kY0YhGBBARAgAGBQJMTflGAAoJ -EL9gcItIQmx+qgwAn2IjnlFm+/oGtOFGuF9MGsAczycIAJoC0r1rDJSHFKCv5T2E -0HMaDpfyuYhGBBARAgAGBQJNHL6XAAoJEKcaa39rhS836nAAn3N4q09tmHJ+2p+N -Dk8eN6jksFFuAJ44KGgO2/2jXWjTTlpm0i0KEVtsX4hGBBARAgAGBQJN1mLPAAoJ -EKUG5tTdTVCISHQAoI0HUjvUUZye5DSFRQjAg67c3MbVAJ9WZeEJ49iw6C8sAhHZ -JHsNbgzMIYhGBBARCAAGBQJKcwnoAAoJEPYo65NHQyBsaX0AoKpA+AGJ+qDQQDa/ -m5Kp47GpzXsEAKCL8Gps4i0MA0kgP9zL0nJNG+T3qIhGBBARCAAGBQJKdEK+AAoJ -ELz2xg9ugWnSM4MAn2wiGM5+pofNM+XXnDUddC2k6KlgAJ4/WmVFX6yK1IEGlsjf -6+2ccEjyzIhGBBARCAAGBQJKdHc/AAoJEMN2qNrxvNtzrrEAnitjiD7NdbrkBe83 -atPoexcJe5ZKAJ4l5AXq8mpSb0wrChkC75tgHwaNKYhGBBARCAAGBQJKdHfZAAoJ -ENlG1WoI44u8/gYAn03QodaxRamhkYfQOmq+ebnWdhKBAJkBDyjgTjqp9pXMio85 -3TpdPJo2wIhGBBARCAAGBQJKeZC7AAoJENTl7azAFD0tt2oAnjs6ZOk0Rns0Jxjr -KL1kDlcrwuZmAJ9u9wIBTyG09n4XfA0yxc3bR37oEIhGBBARCAAGBQJKfDN4AAoJ -EDsymJ0A88/kcSQAnj8Wdq8zR9JEbmCoHXX9lB6VjcYfAJ9dX50+RTbfRQQmHYiF -AcnEM30Y3IhGBBARCgAGBQJKggz4AAoJEFGUgwvsGbN4TYsAnRBkdC0/de05AUgK -fiNIAhctd+AGAKCWt+Hd+oOROXJ8VETnzqye6LT5hIhGBBARCgAGBQJKiWzKAAoJ -ENw1Uug251YEIyAAn2CXBuCvM2H2gHuwcQWL0dIg/Hz8AKDhWNfjp5tjglN79JGs -L7EVVDC1pohGBBIRCAAGBQJKd4bQAAoJENraec14ij9MYrEAnjQVjrcS0FyTAn7e -zNL/yrVl6jFIAJ48lZ2oDXBFxS89Txs5rB0mZ8N3m4hGBBMRAgAGBQJKbJm8AAoJ -EBLbee7Edjul6dwAn3sHmUIhzFZaqj866VDJgFDzLxY6AJ92EwL3c+nK28CueucQ -Za4vPXG4BohGBBMRAgAGBQJKcWcMAAoJEG8Xpa/B8k6kPy8AoKuYqaaInGhX6jOI -++7i0HEdQCu0AKDJ2d8V0vafqPMT3579p6Zp7x9p6IhGBBMRCAAGBQJKdMy8AAoJ -EHw7eXCIx8H31cwAn0h4256aKLGaiiyg5Yyhyu5xdd4jAJ9lstVrPyQc8vxStnp1 -YwtF930gOohWBBARCwAGBQJMusziAAoJEPKthaweQrNnZ3EA32KSuFqs7HfCpwlR -qDHay3pmbhLz+4aUyAFAejkA322Ovn3C8XtRi8wpDpBoF5TgEEa+UVQkKIpFKNeJ -ARwEEAECAAYFAkp3MsQACgkQloDr5KmRk+LJxwf+LS8rqRolCO+zvY377S4vraq6 -tIHkKQeYTw1DJ9oXhH5fJ99K4o2QfBlqCcln2DkdnviyGl3LPij5mUM564N4eaDC -X0X8rcp0i8DuQv/w/fJ2W0Ig8FZCDoeFuX1FBZ4iYFQVkVRd0GXeCzQviDaq5gAx -9NQ841zZjJNU1QKmp9WsBCnHiKXwypUZ60PENdP+CoLULwf6Bwt4rFcndW+aKkkB -6bBwC1IIS9i3fJYgOXaI4IYYo3i/iH/BOsiL/GpQ6GkwRw/6QT5PmRYfHOIbOKvA -/6txMkkXkso5q6HO3US1lpFmq9d8JuDuozBwqn+BkiMhSu+25xN7JMUt2+NuW4kB -HAQQAQIABgUCSncy0QAKCRAx/Ofn3QeUYWIJB/9gUwmQ1C6nJKzFvjD7SOrzI/sB -4dwTEg/g0sls+7nnhQz/Gmn7UiUD7tf8UsV6RRbbqFxdErAtek2UtVjNfbmi6/mB -Xf/V0i3dVizl+2qDI3JJmpUMnNBIqsqUugvPVKThFteXBnq8MasGyqPCpTOhmRKv -D7uUfEOWJ/ilrKnRSV/pmxa3MgBZzYVrMfIJgBIUKRyb6FyxXQzst0PvjHbodYtb -EhcQEX62hfpFC69mvnTPoaBf5ak+9QG1IWYl+re7f6z6GEoErqX+YKacPxW5y9Vm -oYmuhXO1/INPf3yvj6XD+YeCWdvBN4KBQrOx7DTGGRDmUiCqy910obj7xNFPiQEc -BBABCAAGBQJKdDBpAAoJEPPkEi8djCYaXKkH/jNsHljMVsDMoNNgEl7NbPxGKSK4 -m+fTDmVMQdjwOtdbfBpUj+BKo4DZjfFymED1LTpLxc3ybdCskr/I0UQj5EI/xt3k -LudxACQdono56v9fjnPZ3t/wgEpfxjXFpRFj1JZsYWcQbRcgg5X2xd8EMbKykK5a -c6oU7STZDYp9Nc2jXib5Dp2Po4C5n5ZrJDHVgMxCFp3R0QkdULoq+R4zNKeBYFHc -0ijmFzkdFuFeETDyohB5eV0VJSOSZ9wiNOBNj61BN92DWNuAacwlYuOMVCm60Vxv -2XCnPRjctu20SfkLS2X8Y8hu3icf9EhKSUSAEUQx2E6M39idG56w7Pel7HGJARwE -EAEIAAYFAkp5X9cACgkQloDr5KmRk+JMZwf8Cj9mMEmwcEO1IhM1eDhiIw9UGm1W -v/RGSipQinFDqHpIcTVJkng9hmJV6FCDFxCglcHGLZAZl59+tVe4Bcqbv9x3jedM -Lumd9E+Xb8owxhueKNoU18JqLukW74usKUNctqcQyyxPJb5ZW8TMuBQ3yuMBT9zg -XpxDdOpRSoXTOrpzFuwTqvOez/AsFrqkNPoWKrNyWyS+1ge2ZGQNuMhICzEpoq+o -Ekq+rb3g1VQiuQyxZIJEJV2wUaR4QO+PJ0Zv9HEWsTLjUIVprX82+g4eL2bjaeNF -e+OKLLsHzx3KnkNBCfKsyS8hEFveZXHVex0pnM98X3ZfPwA7eGI6b8Y/EYkBHAQQ -AQgABgUCSnlf3AAKCRAx/Ofn3QeUYQSSB/9ImYprys4r0XboGotQCBs9FXsZMUHm -8jkDDZkti+asbvq52phtjrcCT7zFNZdNoJhHSEwxjh8gX6qcUIo97XT5xv90LcMg -W3hajcT2OmVh2QabxZEWEh91oaI9ztsqiYNXau0+ByYxmbSGQZTErOw4FVhqSV/N -asyX6C+Fzkp+HMTNbft9eXxJmQ9bAGC9shii8nxojY/D6XAw9ucLJQoUgmHRhBrx -E1o9+W8kueEvZRNQxrTaDF2a7mlcG/l/sqAvPLy8LE6J05L5+u4QJkcpaFKx9zP3 -Au3vj9dM0k1esVoi8DOqMkeAY/h8ZPdJ9XXH2vB1Q36JVsk8fIrRAMf9iQIcBBAB -AgAGBQJKbhh2AAoJEKLT589SE0a0oKsQAKa7GPUcJ3Jq1Dv9yPrjK/FDXIKT7CL5 -cdsg9El3BDfFy/oHH7tMDfDS+94tnLZKM4jyPE+6UVtImEuw1wovCsuBHDrXiTHz -QPcC7lhdulp96sLUesyn9t7QbgQxUeLBMfSWGzHNmqEnRvN9yu/OYHVP2QTqPTbp -0DzyMDwBSYSSCdQqg059Hm0Y2+8t5+OoCj1Yzn9DDVXcQn0zaQMbZy42Jv2R01Mj -3mLVXX7i23YEmjPDP5srrbDSHWwcE0iT7zW9TodIvgZMLz4CSFHRqW3qzWoChjZt -a97jNVpQjHGWVru76fye/iWN5Dk06Z6NmIlJR5rhkfRnEmIu5EkPb89IV9Dzfs2L -fpef7mMCutNNT7h60w+n1QCervmte1cR/8GGP2xvu6JVr5P5iHZcq1XRtRAxdluO -GQadaaChqb0YkvkeYGyaK+7pZr0ZrADzaKT6GO92pGaKlrF6vOH/Y/rU7IiEOt3w -piJWNPgQu2bJWOiyK0U11XDzPpJVLY8cAsBu/Mts1vSCm/k7A1ywe1BcHub+KhmD -pE+EhG9MDJnRCL9Rfz0AVNm6jPNJ+Jx6oeE1ZwGFS8WZMy9N2qnCr432YcNfAIGS -5n2WXaM5mS6OW/CmGZyoh+4ou/3hpxBEcliRwGyL+AGQWhxpSkLldG/T4DaScfgn -BUQZMxYmjXrciQIcBBABAgAGBQJKcJfwAAoJECx8MUbBoAEhULkP/13NGo8Z8XqQ -JzAg0imBSw6CrIcRnc9jQvbQyD31Ho1+78X2Y0NIErxzNJ1V57M+NwdXQEM4L+U0 -NMuM+ZWhEWta9Y4GYXWwYWEmF8DOLSx6LQ8YfNVSM49KUT/8nnLl01so+aswMvsc -3YUNCkF4BAxkA7ZZMdClRK+vZ4DsSiMkYtQelkxeyTSF0f4CxcNNAi9ME9uTmmIS -lHJNX0XIGUddykbWreq9baHIFuCWPAT3lkq2pjVXdiA+8dvQ68dsofwpmrhy3TWu -fw6c4GyrTiY94Cd/G+SoAIGW5/I+tW2SbZa/e8KN5gPGq5iFqrs8bLyXwRs0t3Et -52P7UaMLycZ2C2Tg3M6ZJg5qDbMVXRNw3eLTbizz7yxio8B770hZD/aB9MsPd/Jd -FR0IOjGJZkHc3LAGZduFXEBriDGv/d/UcqLZSwTD6KA+KRoBVj3z9CEmXdjOxXNB -qLaQTVpTOWOrfrBeW8EZ2Lp9G9d5mwN8ZRo7yaQfDb7Z6hS/L7feAHAVw/IigZk1 -b/LoFnUxXoUukTlw3NbiADkkPiPjfBfFic3YNQwynurfLa3ur/hQ0gggGxX/rNpA -OK4Bud1ItgIkHLIcoXYc/djVAbvtRLnb/uD3x60OycROBBLeH6tBcC7L2K765Ogn -q0XWse4gmePOgG1wIdS8T0bfejFRijZ8iQIcBBABAgAGBQJKcJsnAAoJEO2GBLwG -9akfZBwP+wRSZ1ydkJRUTVUu9ovniNjBso8Qg2GIF5+GJJgvdf2EPv/F81DZvG5k -GXZ97Y1pSLbPQzsxa4mzQ1Bu+uIc9IzmycNFvPMa7T3i4VugAkkl7aBJoZ2ES3Lg -iz1AXHZzfCtJhTu0E7tVnWpUQ7Zwx4znnojoLaylUCvf/X8wprtvPtnJBG4F9pBh -A8RbiHP8n3Rgn9yMejBLOsACZcq3rSA88wv2R1RMEVuIHT5OQbmiJZ5Y2vuiMpJv -0alJ/6A1wu/qBO0bERszh3piC0sncsQfuJ+kLNO5Xh+bpXCLKBbKfeKjS7j9zY71 -yFNZj46dLY5N+NLX9hYHvhlGAl+8xBCZM92EYhFSszBmDe8RcikFNOWdIBxXqht1 -z2UUxZDYBP9ywwVqHkP5U6uIK0bgR3bWIXEKeVxBsiOz+XZ19f7h+rZ0KPEnbjdI -jxsFoehfp8gvZfrcc1KLQzwKiHz4XWOUYmXTzEvrEa/DyIKgOrs2HM8QnT7+2o2n -RGTgPSR3qtTGzhl0dKPdQY1AZEEZVk3qteoodcO+LkQWTA3wXBp9eIV2x++vimD/ -SJ/DbIZRSpnaLTQSz66WqMHlGUCdUE5RLYzdl3SqXpnm0xHidPo8R/Om3u5SI6lC -YKjEHBYcHLIt9CGZnCd0vlh6EsS3beRXiaKdDQRUAT3foonJVYmeiQIcBBABAgAG -BQJKceUhAAoJEMx5hm+ZQJCEhkAQAMJjJULX5TGu9cWJqu9UXBC4sdGJs/IRQe3T -QynR2A638PrYwTAna7liQ6Xfi9waNDvGv7aD3P7plcJ5+15NFBVlaQj9AxyjX6iK -TrAGwjcYJ/uxzCulNuw0IUhX5lLeN16DTtjYKFhg6os4V0GgRsQi39NhTsZfx/Pr -CmsDwiTR1CZlKNEaULMH8nbD/Ax4Ysap2OHStoHMjBakhImYBjGHcYtc3vDT0d97 -gV1Eq3d7eiqnzbhcBzlyT3DkuEtfLe1Zsnlq6T4U02BkRIibZIEvAa99ga0QFCmR -Uab5Fj4meNI9jwB/zX13Z3ROtUoMtokR8nhOKrcgoEadv66oVm4Kscew0onYmo5D -+qf7JVy15rQ5fZuWChqD8TXs4Ml29B5Rn65emw6qazBoK+UQUPytYJ2MJAI9rYfi -CDvipSr1CARmAQANN795pW33lLRMxV0g4tYnUpa7NDov0WofpsAfau8KwCqRuvHi -zGLWv1sx37C41YIxvGVXtWmce0fadVGEJzJUw9uCQnXt1fcRkgEZ42VqYnGdmS36 -Le25fYdmxyn0qrGpYeNU+MJDiuYAVt7b8F19Yo9eQYl3Fp14StpWx8RNTXEaELlg -IJxZV1wMYcapC0gJdQH4l14rbnzktdeP8YEhhbnlvAat+BJbtyUlbRkz1Ys4EwuS -jRJnP2O2iQIcBBABAgAGBQJKcrYaAAoJEIcvcCxNbiWopxAQALdn1TBWI3Tyul5J -BmrEeA56a27WcB5suG8nLemtMwmtYZhLwXCN+BycKj3sZfFsL1freIQRlEJo78yu -GVJnDzc5cz5H4QSIUNrfV2pMXWKsQ7XQhJb3vdfG+cQ9F6h/CEUepLVucrlmR8bW -7NRCqo+Rm9T7FNRXb+c3RPg6ZJuTR81fyP902x/xY/CQPKUF2QLItMrjmeA+RRbt -8hfWDaEnwSO16Afj7oNBikjGv63L1Yz4g8bvhcSE+FU4nLyCOIo0T4Jm6bd/kvQ8 -sJOLuuYITK94J+TMA4i0JHiOqznA/hR7saLIXxVxzmapVTbeFNFbrE2uEiD449UI -MKF6spbR8HGNaVOGP/sWCNHtOwtXbmFH0w4P6tdAS7/l1WQd56YsBu8/a+AZ4vHi -Ae5jRl6eXb1ZYu5hEEaOeTDmluDP/2Hk+hJiueuoXcpSmfa/AWXHTVcgn2wtR+Zy -fAJj1ElBWcQAKNwztJL/vifwc7SZQxXZcuIeFOHpXrvyyldfPzzdHPCm2aUVNqCL -d7ERje00tgc2f6F4XcTDWjEucVypeR7uhMFtBlVwwdih/Ax5gYuUoLZ7KyCdrRCg -3L2JaZTxVz/czNobQwX/dO1t4GsapdJn7UHvb9on+4JHDeaF1oaTJ3iMS/WrYgZz -fzJlAYYi7mYnGrC6+wV4qmJV7gVLiQIcBBABAgAGBQJKdVVQAAoJEB0hyD3EUuD8 -MmgP/3E8G7A4778UrGOEpUMWsU1k01lx2dNck+eLJXt5mK6sBm+cTOE4GP/g4r1Y -l+l2rl4zW0UW7z0PCToMM5DdODm6f8x75K0XvEwraiKXoRsIgI/KRiCf21OhKYsX -UJclOYrElLmk8gk7rob7OmYALeDhW3GwlrCKu7gmUqX+LsyxHRRQiNunaJqD7xfC -HsK6pge9ZfEXElMdW9ye5dUPWxuHGJK0E5X7dNfSt7+Rkf0YxsrznE3PSAg9BLNG -3rYPgFi/B/hB+CqbgKSi6YFMyJ4S6XBoR2rgGaIWUyHJh/HQFIPPgLASkiM+RvHy -bIWWGm7FEKu2MJu0wYfN822R1EafMfe0f8FVJA/K3hHCS62cOSqt0lIgj7XtmNEa -t61UbJFvS3N4/c3TUslkDYH7tkbAA7U7RFfpozZ19plx39I+mcKF4na3XM3nXdIm -D7/TJqlHkyrQXDfwCl9wzlI06SrdUV8styEdix7Ip/kfCVjTQQiz9omh1oVRf8/Q -cgRAjxUVM3nbrGdmkPXnoyCasS9AwI1utzQCi6wlSuveQirl6LN9llkZIIuUl7HQ -cmmZAyrIbXfY+golpQu2aqjoTlHaewZQZKrngqGuGpmc91NS5hZuLLFBm/CN4FVs -ZlcqlvgLo1dazpyWPiHa28gf52fKow9iiEGwqLcLaSTak9qRiQIcBBABAgAGBQJK -dXd8AAoJEPZuPkGfhPTewkgP/3jmVhVkg5gi/Nio6LqIoZzPi+GUkdrFOB59hWnH -rAIm87Q1uHUxQ2mbNN2IJ2NRRYLI7iaIIY244e9DSD3sclgPgfRPg877hC0zo311 -HwiYBbpTm6s+YKOxlJmFuTx2H/+G+vyJKDIf8XWNYaRlnASoaaHDrdjNInyb9+Yi -l0N6VYij8pfJNUXWfCzz6AAAYkdO9LW5zHHG8vJez18fdr9bV16weIw2Z5jEU7NC -KghLhhCezOBOkOOifgD21w93rGjvZpM2XHspYM1AH/Zd7xawJwu6lZ15BzdPl4ko -Cv5xr7TbSl4fAmEaivnVAZD8jaMEuZPcAYOHzbWjFvl3ZqU/DBfVQW16lHKqlLcP -Dh33S33Q/3GP1liwOrcKConS4HxT3tJL09wAHOQgKYgGkIGDZSk5EVSahoTx69co -cbOPbpyE6FfGqNgJ7Cr0020OHjUB6mJhk/ndCVDrj1yQ4ca0YBiUy59WSl0VxFIL -ddLRJe3SXX7tK5pxY5K0mQIRM8kOSs4vlqKa7WrTtW6RBj0KqyHDVyVM3l3zqg/k -Vw16GXqdlkLUpmZgMIdSfjFXT8WE5fEwmPg7Za4bDKVSR6MD8Ozp7lnud+RBRH1N -hbz4FavEsA2NLOlkdka3jJXRBGEUvELP9t7kRZ1m8Biayh10Bmb+Kokj6jwt9ttm -GMKyiQIcBBABAgAGBQJKdx66AAoJEAxwVXtaBlE+vsQQAL1g8Q67465P2fBhc4to -BjvJZ3ujLcVjhTNi0rMTIPs8Bpu2xqLkNc5JMXU+7Lq/V/zQl6OjFA//K9Oipt2W -LEB/z6CbuGDmSoujnTBBvPAL16KSI60vG8+lmi143X9bovbItQ+g0v2RsZVTCVEq -JncSXhbhE98q+YOAklB8/dLf39OYt0X7L2Qez/gHWh5p9d+VEYxAhHhAPTMHhaLo -EQ1DFUEFVY3GbvKQKZ8tpv75cgYyEfVlykmyZfIAC50t3i6Ake1l5VBBOllJn8f4 -huq8VUdx08kh+FIFW+HEGf1LZ06+CuGV6wBQfuV2fYFZgmjUf3zmwFjcBwzgEBfg -29kT1NMGdVZV7GHL05aRUE6mfrPus/Qbu+crPRNb/4dx4MsfEZoFy8AQzGA0BAm6 -x8B83B5mRASXcVpioBFHCefijb4f++iYuWq4TDMdESBw5t+fQKEpGcxWHGPGFn4y -cp5QSJyCkyt0hTGs0iPxPQOccpea/A/7YvW/g633f+Pm0DCA0WyohDQG3NytFNxq -AX6RzVHvSTGPWJJrgBk/OqeUIucNcPKcV7nZ3MhwUqbQbJLHqI9EHPDW2mlj8/j4 -SsgVIewXm0tScRXOl73PmX16sJf3TmDuzdlPMlOA2IlX68CsfqZ2jhjOT+CdwvN+ -N+DKuDy8G7WF8RPe0VQzZJvviQIcBBABAgAGBQJKd9NoAAoJEIcvcCxNbiWo0lsP -/2IoIaiJXOlCt5QaY0UnBIF8WG6M3FQuitVCEuiERTPoTqDj5NAnbSayFNZDXEyJ -RQZ5+0XMtnyRMxQMvedvVLCremuyV7RDbAD3LS1FFtM8/PcLcfUevsPHd2pQI9Zg -zWZfYRdo/Gqh/D75mF/YIih4d/WOCbuOpH4SjUuya1VQ2lqGzfi0k7ive9752AuH -kCYeu2Hyd9WxFBlE9BZLT0dFQMwCmu5AR/Y5YWqaGLRY6xDDmiPKEUwMVBU9zzoP -Lbke+ReJyTMivyL+cFHL96BQxvACQpn4K1JjLg0nedKfdfYYmVyjBDAPX+7ua11n -ToJbH6Q7j4TT6YRMBIRrlc4TryAJU5jJj4l2xtu3pCaeMJcwoN5elcZrNS7AW7qw -BtHVCjXhfbUe6L9V6ZhmWuKFcYj9EgH34eF6G4w+ZLJlroCP9qa4d1mI7r4Eugz/ -mGQOAqeytGM63tOEokKdDhV6thS10J5C9fOibUz+xJWftu+M/I2iMqyCjROZyOhK -5vTrYyjHAtF9hKYrnxl37eO32ANxv0iXb/FLUlqUCJsayNf6MD7F8M1lND3kya0w -dLyMTe5c79ye7DkQEc4Dk0tbxh11+TBB+TUJsig8W7ngxDaEptJJxL0jqjNDJjcP -A+jFRJjbb4we/gF9Y3HIIAno1JZ4c1KP1xrcffjqQ4TziQIcBBABAgAGBQJKguop -AAoJEDIkf7tArR+m/34P/i+kAR7eE9IzwJ2q/JVsuzFF29R5cLrA0cJrXiQ97OGD -lEFh+HlNCu0alPbLtvdj2H0D5aRn4m31PuE0b71xMg9GynA8opUrOiaW/y4cFWX7 -ceAKESCKqEngKSb1RNITyf/7eYlv2fuByv2+46Y5OwCeYlZeQ5QNeC/O0MDz6+T0 -b5yLBZ2HhxqS63cKiXNrM0GBbsXXG26yJ9uosgwOIc6v+oC53FVpkAywk4qyDHAz -TDyAXJqes/UaWgoRzj0Fnnj3HVwsR7P6ewjRcd58iBnOgG4SbSyzUfLkjp4TGf+M -KkNEJ0cWe9HQld8MwodS5wx/UwSZpaCQqf8IBCHWu0H8UHDjlrO1ig+TawKzIOLf -ZtLa2WxduS1dEXSh4TyLegmV1UmCMrvDIrlu/xP6LkzB8Tgo4lmXBLQqImpgVqLL -FFO/g2TkroY/RU6XFfm+X00JOMJhQIurQZNKl2nuCLJxIlLo1mxwVcZ7yghv6rJy -2kl9QmdW8+HlgEuLI9yC/bkUSmWcMh5INLW54NXnI4WSMixFi0OjkIuDF33hukpX -iiiZV2uRIjy5Z5FCDINNy+ka/RsmSK5Ruz++OTRb+WwMACU0q2S5336mk7C+9Ba7 -f4nvGbC8jfrGKHp1Tf5OErzy+ZQDj4gFNoWbYZzHLlNj/ZzIHjDxSm6HL0+91rRK -iQIcBBABAgAGBQJKg62MAAoJEFeTDasLhrBnlzkP/2HglT4hYHoJu08jBmKJvXPq -12EtGivm+ha0OLl4eVoZb1xhd1Vyorc0ymwK9T5C/vabKfxmg7yN9j4Ckq8Ecvmt -I2YdE/5ykpu58O456fTDIlcQhwEmwXnY7hSbpdmb3IVf3Y0uN3ZmVGlSkL4JymIw -lEwoMwD9VliZ9C4exoUWOdhB2Dd9yORlOfhGUaGaE7KcB/xnCxIhlOHtcTtnvBhZ -yCj/WRYJB9hdJFFnkWHqjpIp3un/YI/1RXDVfi4TQwq3e/2nhN6EnQqJjHjy/vEF -WLlxk3HY1D46W5476FHo0G9nypxf1a8CvuCy/EKRkEHfWZ1eY6hXjO8d+l9GIWq3 -Plsp3flrmVevn1CscY93EMa/uskCIFGou96oL7Iw0gFcz96ZWlTUbncJwEzu8Bi0 -040zjIw1q6bsfEBTAJfzYMnGRTkdd0so/8ZmejHUGmF0qyquORR9QpzcojccvrET -8jinKv6HMp8G7HFIoWwWvT0X5GAbUF9J93kjYykHkLvxtHHkNx7OtP6LioXeCvWl -OdJIXORTFlOgL3sq2Cag3AN31kZ5VUSTtmC3mi7iGM1wJ5M4iohJxBP2ITbJD+B5 -jozBBaMXlBteHjxiXgLu8qnImtUZWrvdc+IHfp+x7zFM/yXpeJ/7SBKljKR2AcXp -IarUnv6uGsSmUnO6IbARiQIcBBABAgAGBQJN1mGeAAoJEIN77/kM2/kc168P/RBy -piude3e1tlKjRy3+q98LySQR+/X5hmxWb6bqR9mWH9/qHNxDR56hBPJnUxKy/xtb -XrAmzQpecpHWI0pCT0eusJrlSC+jo59KhdwNuU7Rf63KPcL6Abe4MmSzcKL6iasy -CWFiyCVlhJlYw5MOzrStnBUgHVyfq4M2kCr2z11NtcLCRGaxUW5dQPjo2w/ODOfk -j3RlhMVzx/RDibwCPrk+k2+QT796/BHw2iLKVlosFZKjHeS4dGCf/fdIclu19huV -/0rf555w4Lo42ZyAdbb7AOZfUFgj8G7lPWl79xpDgFj3JeKWVtKvGHC6pBcDsOrl -JZSKcS7pGaS+2oj/k8aQaX8BXsHtfJJFeQ7H/3JGzNzYzU9gVbmigMcDtYGSQRo5 -UEfFqCZ11jk7YEmWGG6rbeRK3ErbAZcs3NXwLQNxRNp20Mj0/aVjgNqr9A7xKj9Z -AJlWCV6pqWWMG6s2TJcLmLVzqcLrOgu9SaDsilZoFWL8xxHxPGFE9LAJZCPF6cKl -q/DP5siKuadnLbzyYhUgyZpp9VqO9Krla+b3mGv07Av8aIAZlWMGnTjrhODF5Buv -2ycdFJI76CmM+bX+2KcY2m5ho8XGWHQzF4+pl0W+7llOiuR7vTm4X7cd6OHkNTih -vp1KC8fcr4ISJTDftJjvKQ1E/riwCbqc5Vs9Yqr1iQIcBBABAgAGBQJN1mSCAAoJ -ELXjmZ++7mdkSj0P/Ap//rIzax0MGBGbCQOyZjbLOdMt03O/wcW3AfWqqKchW2Ve -dhCT5g/9AmWlOByROpYoI4XaEvdqCJwLYuKSiSd8JXFHO6OnNopv2PhkCjm72cqK -9+PPD3NZVXAVrbfnQ5aCfpV0uoZBWEsRcn697o9fi5irx5wRRspQcxU5Rj+lUp76 -o7IBC/YqIsp51LHFPOD8Yx2EJNyQmEuhe90c2ZkaN+hsKBzreyIoXDQOVLA1XVFQ -+BgcfEtsROdaJsLoZjiTbXY6ta/3o6gKYYB16dKV+iQbLhsf/Pi3zM20k/Qi3cr+ -zweXFM06FJ+tuhacteKO6Z4rmTlKAElxNxJCjbwWq2Xi07TvmxWioclrr4bW10iN -s7D4EuGCTU/V1ZXEjIiejO48FF+ghtdk/H7lLKY2fokjqT/ta3SCw+9XVLiX05xy -eo/s5dwJzOKrLsaU1uOiB/zRHDTCbXTEETgQPdkRuZ/ObCd0o7lj3LB9joBSwv/C -CcvcPQJHxgGeui4x3FpOReBUXkBMwj+Xz2nav3RHqyrZ//15r1vckR2rAgHm6z/I -xUkEQ79NbWpopq/Dh1s8+CP8qSc13LfL0Uim9O8KzK1ccabubJQymImhFgoocuBf -8d9gzAb9DSg6YpBrIx95rsw91wXkHAneW3jPig8MDuvAt8jdW2YEEScsfbl1iQIc -BBABCAAGBQJKcb37AAoJEPGmm+QpwP/uXnoP/icS6LC18nLCB9tjS71DHRaCIbrN -6fYisSo0QAD8SpwQ4ZSnOOcab2Bqgfc4RMqSdQSA22d0TSZ8LnpEtyQYHTn4qA70 -5+Cm7g7JMoxb8tyfJdQZqz1oYy04OFF4QiXgHjXJg7Ul93kbg63ai6l/zhKVuLCO -06Fs/G2pTqOBGJEugz7obKgjfAmkAtiT7Nr3UrV5+7F3OF0tdbpxLboELmdFOmTX -O408mIv2uaablk7ysslfbaysNn8XAC9k9tpZF9gYj6G9Tdau5Dj3Hvs2loCPIfvJ -tN9ldeREq9RbkmgQEh8pdKVDcTX0kSDDMWW8hmNMFT1I06U9pPgTpCz+07Vfut/n -Ir7I3CxQrwGpvT4IZTNonKQa03Ee3EVbtt7XxllTRwLKE+In/QafHLi44Mc3oaof -rUOLr7RN19FEUvo7dblvE6V18g/LyHRwadJPLTyQatBxtxmVHbiKlmMfSOptaIpG -G+QmMK+m9R0qQtTHSpzaxmpTJyxocu3E6Avg/AVDWRMZ4DC4XXR91swD5qzhsnnF -srhWzGn/8VZXlOKR3F76s6p50QPZwVBXHlHGszbSJEEkyoKPoBH561QTqgQPlpBb -yW9bCiaZaep5u/qp6qjq+GSEUI0JQwYy3I6Y3EoAjYVaXb5VLqk1LX93xhjQm+Ld -h0b6UHvuMYReiW43iQIcBBABCAAGBQJKcwnqAAoJED2QirPw+/UfvO4P/ip3DlWR -AqhmEzRLJxevqF78EMFaJ5vdD36nFtSgk1m+l8rqIiVp+VoOG8N5Ws8p1wHEL+YQ -4fhaSn/pWEHvhwdLpwZwR22kOBNfxz1hN451M1Qj46lMcPn3bdhsw/KZM784VLUg -dsOrfMI9PCiYYdobRWRDO+QbZYKrNj6rPGcK1yhkF/nFkc4jhgtCVZTUon9XuIA7 -YvvxYhmNopfT8ot2OREdoxXQU5f9YdOraWZD/8BMaSVPosarV2ze21np/cEp/6+t -L7UWYfxpm7udtoXMIlyLOD/qeuiUbAjADJuPvFZ74IAM4r2NuGsXL/P3RRjh3yOJ -DEPeEvbTlups2iQ7Lk1vOjjmb96MVcq+wm6trK6h6JEV/Opz3YUrdz4K9+fPZ+Bc -MuXGfWxW+QJXoW0/2tlYP9PghO3ISDc24RQS4qYoYB9cyvZzT3sxLmG/9aUlF3PZ -sZLjgut8KdLIZtMCAEJaOVemLJLL3Rd/sjQ47Hbsht4NsAjGyktK6Aez4IJchzWd -LAU/gsOuqy5xRN/aCqpB2uGqQ94chH0qvLkvLGnM6OpHQjaNANhxAnO2iE6mZL7L -6LlKQ7AS1IxLVMAn2CoSzf6HKvi2awQ6MZZX48WpX4hz3ND8iYi9JOiqIJdCb2zX -dBuIg8f9V0spt+ND7ol/UQDyKOx8cmzmE8SDiQIcBBABCAAGBQJKdEJsAAoJEOYZ -BF3yrHKaQaAP/28AFdONh+dCdn4f8cY7WGpwU5KRc0bhxyy/Z4x2rT0Uv4Hz3vlP -lj8Xb7k5y0YDWH8dREZFxlbHMu827+LWqFmr1ZVeGPotKAvU0MlZQATGPUTreFl4 -RCshbgy2v5KZSqJI3uSfuMGGpWFfsEQ3wX2YVaabOJHonLzUsWoQxRhw947EDbFL -o2NyPm7lKdBx30t4n1JqystdxfO9pmSZrC6+2TvkUObfJPokAIpcJ6yb1A0L77mS -erzedxyApGVw/SYuFDo6dw16UzU0d7kgpkDvv80v74w4C/GxF5zgFFwx90H8S6rR -CNwBUgHClZgPvh6Hb/mUIXKw6AOpuFV3p6vlfYUNgIh7kHhMF4rfuYL7NT+vzZnl -tshYN7DSHSgFdhtHzZHFuryCgIjClbnnnIvyBNZmyPrWBNj9dinufcBeRhhMS4ux -g8NvW2XQ0cmszMKj/lbnhfhl+Rx344GNO/ka0hbdmmXY3Oq400Pd6YKPvTiHYHVi -ccpALt41dO/jhVp9A9UetG0A3T7bvPcLfa9alzA6C/EiA1/MMQI2J3QI9OeaQjNI -kGQBfoSul5MgpnuVVSCXyifSvIawIztY+NCtn02tzfklJ4+70BFYrrZhm6WyWL0z -I1759aXdfR90o9lnUlF8nERD5mLM8pSD0hZVd+W/V10+uhwKDrTl+HP9iQIcBBAB -CAAGBQJKdHfNAAoJEN/3OMLRbPuilXsP/2/yaJIZxxp2M1c3qANiAAev+gzWv1jp -Cbp9C7RvAqydnu1KO1YG9CWCvLU3RFiA5FTnkbpAzWJZ3gtej6JV/bAzBQf4kAqF -Ma3sei2+7BcqQEsiJcx66sIQD7WSh/1JRe2P8phnoFp6gK69wgCB8WrjzkdPPzcF -SNPpb6egqZf7xg0ScYNNqvudRngbMJkogqbESI7SV6OZrSEK9KJ9NhWkWG7getya -Kt91A+IXNrWFR/k7dxbw5fhF7QPFdi2olLpMsNr+W+7FrHncAlSP7YiddROSY6ez -bSPBR1Gu3GlCMnVoieTNWN0sZLftziuyh1fBlBq7V7qO4pIqrxxkykS93wBjYfa1 -mWUxmRTDjtsXOU9nr8YzsdAjf4TkHGgyQ2jJxka76ZBoF7We3B0kExMwnwxXZ2P2 -len0T+v8h0meFD20RpziGMhvevTUH/Bx3ZeQJEX+5ith9KJfA/rr7hI7kiIc48CP -R79/4zB4cL76eO4TSMHgJpnA6vwCmF+eHQ+arCbpJbUSajHNGofWT0hRWPRsvbat -Q9YPuEskHydnJDoBiFg9s2ffPZgE9TZrqC/8ARo9F6gTCNcBsv6Qp/R6Rc8CgKkG -4zuuMmPQV2d1tiXLnhrLCGlkrJ5tHaGXsfbPZq2uCIcQySe0CVfeGEkBzzHAyXO8 -52GEe7/v22CLiQIcBBABCAAGBQJKdHgNAAoJELqceAYd3YybJ5YP/A9ImnKA13gq -Qs2VRbw7D4dDGPdl9xD+n40PDJxd9icC9N7XzNoNG81BcJn96vjY8YPpCJckehiM -1zAWsmITUSMadI/84OOk37iCfyOc124ARxhsefwBlOSppie8llHweascTyIC4TZC -b+wJp1K0BJK6L3AqTYONuAUoCunxPyJ4tNLNV8Q+O+9Bhk2k258vvQz+iSAHWOFZ -16+L8cW/fLGlGQGnxtL/dQQmC5C5Gw1VW/Fq1pp/2jLBLyQICMlsFpb2jjhW5UUj -RtK4BwIZTiAZF22zWU+8Y7qiJNR6kZifwWu/Z4nUcfdVD0cHBPcmStgQ4nBO5PuM -Wh1nfIyQfbJ16yihZi3ZI+8jW0fRKLyJqEP56E7GKRMU2Q9BZnenJ6o5zikR8bi4 -y6qsMrRXSXyyb/6hqbZ9qFieg76scSqEu3HXjoHPSO/MhtIFf9wLuBSZWGNOYJV7 -EB9irvvn2SWrfJsl4tXWg5d8w4oQIkcJ4vf3kJeQ2w8bnzHYeJV6nb2hgkTbxB86 -abm85cV/8vM9CQtIDTHCutq+uR+in8am44RoS0SBgcYTewgTUL5nU132ZxUieiEN -pgf3V1gu6VOBxG7JkdV9ORawrEJpwhKJ6awpFmFAfin37bxVl0vkouFMVHkRumBc -ZD/VIyMrDEYYreDX/L3aQSjo9HjmIpeWiQIcBBABCAAGBQJKdIqqAAoJEKLT589S -E0a0F8sQAKwzbpNTPQYPS5yd0vIsOMXtVRaBJbGO7vGWyrHciX5FwDbrNX4mwgbm -dPnjgYFONNlJ9QpwKO6i21SO4q8k4ORUbvXZ9AZCqFvpSUSGIRZb8JX+z1SRUtUu -SqsLdIxFy2SSJe9j+XP52MK4kEQnbmhTuD97EHXVb+j5SK1B8J3QkkMbFxwdz7bA -MLQGxa6dGXD1z7fUl6qUFPEkrwbzBKGHmPv1CbaVC95D1WFvPCKsv+WmCwO3TFpU -x3xjHfgbpZqnm4rxPpmYPNg+/PycXGqKtncGWrca5H/iYVQLlx/j+ikhY5iHhgw8 -tf/LFNUIKAr1VKtHYPIWiB4r2pwn9jMzcyDbXSB6InetmvFwZ4nNQaIF/tgRlWMz -08strY5fMNxpvjEeWSrqosXuBkr8jaTulqp+3o4VOX13VgUZu17h5lGgjZY73YYr -MUJHLFMKX1S1AgXOAA1xaABOeUFrDVpk2D0azr5RSziiVXttZv1w5SAOUHRR4vSb -yblLT2Sfb2zJTCryUwD7bZU7d5mo2eOTSZUP+Gk5/aJiVZ+TAVqH8Y6BswHqXNGr -3ozzxGqJQaYerVp/YEG62dGk0qdpvtHgR7NQGEErDp2jP/qBRy3g596LoXQBUok4 -8hwNZPEn5ge4Sk5OtSoGYK4oUcvFQIeA33OtbAd1U1EraDpX7U5diQIcBBABCAAG -BQJKdw8lAAoJELkRIFNthRKNfNwQAIaygo/XqP3kee21VvDuT2cGVys9RxUvdExW -pFWVTnrxxnbX9fgv3ENhWhBas5lqweH17FVJybPMTNT25tZZJoPZczfBJLja0lb6 -hvg3/ATxaXh0CPtWnQZz6WB6Iq1gRY6nMO3QhILl6hqa7xwQPWszFWahosHg9Qyu -Qu8xKqoTubRkYKTK47IB9V/SBE94pPX6HyY1ESySwFAq6XyoZk1C04rSHiJBoKrG -R7ne4eLyUzIkC5RkSBri3P1nPwJpOTT7WsVRObOhUmYInA3NGlJgeiMjJSYUtnLh -6M7g1gn7TUWxCVq2kvH9JUtbePNGllU/shjhiA2AlYxWs4DR6lTuSIpIpxjdf+3a -Z662sXPhHZEJQbQbybqdaT1nS2yoFUdrOSNQz0EYZp12JKv5m2joBKLU+XgjOhan -LJjLbAqVBBtUyG/10IbgsufOtiwd2vkexeO2jLHrIN686P6qWerRddNJXr7eib5T -mMY6aLBYvjYUGx/0BUgjKjf4PpIAn5A0US/lNlrOV1VQDDLJAPVVZijTdnoP4xB5 -7mJe1lj77UKfQLnNR59w39EqvDee5oGCfsGSvaUcb5WD9OkAq9ls8SjoCx4xWHEH -TzoLD0Ot9Dtqu1jtfV0tsTzbEgeYoszmGGjwvwPNT4LJrQGA05Oz8QZ1fEveLXqe -xhbSx3U5iQIcBBABCAAGBQJKd01iAAoJECx8MUbBoAEhvTgQAIe0s1dx7upHF1Tb -A6A9+sSWHyZS5l7z9jPVgJLXkJMXqDMpPx2H/N2twbT6oSt8MFrZ2CGMULO5SIo0 -NprxmD+pTCgopcpuOenYwWuuDMMyYr/7nCS+ErsvidrNgaaspsQ4BjUWjFOwOone -kIGjksEjHfUxuX+MreD7KgoZWPYtgCgPbN/nxpHsF5S/SteWp+XPI6SbGkyNEDpD -AF/g4jznY3TbD3bXXVtaoC98AvPWC8nK8plIzQUZWoDmhewMRV/E1F2bGeTxyxfl -i12O8kbjQn/8HOgqlzRtQkrrfdiYNl6UbOT2kXnqHYmWphXyGoP7clEPrMij+kCf -10gPBa2UnXnEigngobM36PD6FxU21949AGkcUAuw3PX4ALPpy5+wHVYkhdyQDLnF -5GAeElXWq5jOiSNc0xq7EvECqD7IbCAazra4n+VGf3z45FcO0bxHCB3+Pj0d/kOg -mcdsqb2f380dIpTTSsclY74AZt+petxVLhdP81Z1EyDPlEMKm1Ier+b4wEhx1vGK -8dtEBt9fcQQ7Ic1Nw1NLJHbpBuAvVRn19iluOSJFzW2tcEFdgdWKqDXU6XthXnsi -hfXfjmUW0BNTjq0EUig7IxdoA2NvXmUsc2RkVpyy9L5UxCWXkcQ1OVUdTCmJYYSa -dLuj8BBYp4gHu8UyguLDitWuNtxdiQIcBBABCAAGBQJKd2DKAAoJEAxwVXtaBlE+ -ar0P/1H9TeIDyEWCj2xliWjjhTXvhmj6NHus7tfEEcjTFyklTjQwFWImxoYs1twu -k/uXZGH+D4tKu6RhTyMEYZAesBUtmkd6rF8DlX/woUW28ud9bGJyun58485Jjppv -B7+sgD3V1qPb18y+mFHOxYPk2Tx2G5SjlFsiKuW6ylXoUhu3QatJs1kqmcTdm1WD -Ae3mitHyHTczedsdursAaqxuXpYgiek5WXRzkHqZ0J/XoCxYgT41SbiZS0cF8Rgk -gR9FEEbCfy0bJkoqd8HpfUb8nIvkM7SbI+qDyCPYMBA1DIn3tyHDIdwNXIFC3QHx -0jIS0mX+YFsgVs+zIRX9ioC5gsMDbSxe8exf7JhlEFYlWxM02dRlBvJ+zvBIBzib -HhMq5BdX3Ko/l3OdbOiEtGg4JJGXXnHtuNM4ac8rNWwVP7HWepElSoH89ILYq6e+ -R691vxCOLuL9U5JvtGO/mp6l/E4lE5fOBVY6rMtGSm5GcS84qrhEU38Z6mSUyrwN -Xxe/p960a1dIO1K4oqbNiq6nolOyGgEvu+XFWhHW2wNz1stdJKMHZfSpPlC4Japw -y1fTAxHkITHJNuE+Sl+wS8Oi9FQcY4WroC/TtmCLuOU+rKAEffd/5Nmog17rlQNL -2c2X/V1+SJvSWeNKagu/TPelsMJciJdzGnj2hs/95ewa0ZhtiQIcBBABCAAGBQJK -eZC/AAoJEIcvcCxNbiWor/EP/0lOqMsMe3Y1usjSOIOEtsKotGcV0BV17WRbaGiZ -PSUBCt5ngz4wiVJM8H4gQDPI7VaspArwqgUg+plbBYLWi/aVS/jukRIx3vUA60vf -hPOAA2P8mTlj9VZKU/BeJms2nMLNMMd9IxOs1DDx0bGF32AqDz8KhCusuwIa2Sgl -f+HkMzyIOi33XXcgRilAkifZQ9Awz+FiHnx4sw6H/WjQlzdjaZJlA2G1b26JWB0n -7yxzkWd2h4kW6McipSnA1Om0BRjwQCVL72sPEEgBA7ZUCVN1eLHRyFIknXi7B7CW -q7ywC23gBo9w/3EBwlNYkklnd83N3gB/a69GGR0NdlSmhE4IdoGUcXD4SrLOC2ir -CRFFvpGwmQcCOxf2pBZuTzSmb5FF+Cc2CaR8yg/jOK2IoCbHFVbv5n/CxTQl1xLJ -sYU5O9Z6NzPTsJiYyWFIG7AbE1iV106CgZY+vctnrSzHrkd33sME4OKQUzcwfkcg -TRVgt/HG4kmLyAz8lXMlvONNtCdgf3dzPRZv3n8aQ4KQFwVx083oFZk6pyqTUejn -U+VTFKAQKuqRFwoQdtA0kWFLxs3iFnc/NB8X5Rs3gyyBeFDWdxXYicREjO/7HIwd -287IDznfZOZPsJX9Gp7+rTUJBmRHK8oI98I5GMhLZNg2ctepZWQLL2aazxli/uZV -mgkWiQIcBBABCAAGBQJKfDOOAAoJELs6aAGGSaoGEWAP/RHYBNHG5RhxFjVqWqWR -ub01Ss3I1xN642Gv6+n5s9UDJf7/j9P+bTqbKkA33ua8NFqpYVBiv6CDUqz9lmZZ -dX0I0wMcgKQQ/hWjJLK4/Hoe9RBE2H3Bbc6nXbgNsurbm+Z8xhyUpaLFSbr1f4pB -XjTbulEQXukb5QOcHMjA88fWvqSyDXqsZaOIr7O3hhStGDx4t0Zy+rc/cSSW9Wo9 -K6DLguAVLzk5rIzOBrsa+gFAVOnzw1C/CGr42kFzDWvV+KEbbwCOBRziItnXV1QJ -mUqrsYsctrJB/LDLPTRE+iqne8t2OlVmJU09BhGaocD7JMQaDhu2XyZrUZFwUKIr -gXtsJ1F9j2MUbMYcEcMsrLcBHg0ZSAgZv0ALKenoCS7cso/dTexYaC8SUUT00pEq -roFRZ7jnJ0KDEl4WEF1Jv/H9L3Mu5qSD6Xii7qrwbcffgKhaJoVcbd5EBOoae8DY -HeQfXxQ0SJwVpZ2zRgg/hLJCCGnjZR66lQ88RvVU70zd+9FEM5hwJY4Tp/AjqzWw -Ip1EjYeiepdMk3ut1SwB0lB8XDA65+FsLPvTsv2XFvw30ITK+WRomc2ZgLmTxhEz -yvhK5mi93dbstxLGhwCfTvOXlGcOXm/fWougTvmJimHf2s/7WeaO6YuWSTJE9Dl1 -ULZnFladbE814U0YWHEdyE9NiQIcBBABCAAGBQJKfTTgAAoJEEnDv4knVT0uc6oP -/iJoeqyn/bFXc82WCs838z59Vnu4qd+EVO3++7XikJL2S/lJHa9IOsvfF/lN0iFB -vqp/EI1/uKQMZNdNVIMvFk4PS+PTG+g+lmUqVJkteGO7pvTpc5zJgTgOxojvvoVJ -LOAbhH2twBCcQiNZmdyRBzeNgYG0RgfxJY4vkZDRMKKZ2hcEKjoaZzwX2qu14k3s -+izxR124KZyxKXzhiglaoGELCcKp5bM+VEkD03HMWrL8ynL7DFn/YUwxBw5BZtPI -rpvDG+dJqYBDMKqW/8K9025ygKsYmq9OELvT6aWo1JMkvQEc69YTEQqUu0UIzvVp -2S/ALm0QNdUplSVw/blauCpHdxpaVmFaDLST73ZPoKIlaaB9cCn+LXALjwY0VtYr -xFHpnwvdQWCgDvHPh5pGIf5QbjVwWZRoQDNsKcjPfXBvfrAyVNn0/UD9QTvopbaE -HpZy1YOGwm5TsOXCMJYWeajSI94CrXbwbHlC995Pzj6IipFBqw182F4LvgGttsl4 -qqTVQ2f+31AQRfsLVZzHQ6mqy0qRqSWTgRn+EK+4arAdquOojzGj2IhoFfkOndSx -tfOdi3i2gg/S7YJzZ2Jd7Yf/9VBbrfS/8UE/reT0wJfRAH8gbrWN+IygqhuK5E1C -4ZhOl+JT5muvbZSclnH5iaAX/5YQGYm9toLNJqMjloE8iQIcBBABCgAGBQJKdaJK -AAoJEPZuPkGfhPTe7ksP/2Q9kjfsTPN1x46k183e5+WoDD8I92RlukxvRDAysHVb -qWkfYhM4PK0x47uWGlH3bcjzCRe7gZMhX4Bnj6Ln54kcXT9rnyOKV5BMcaMuCTch -9G+s3VvRuhvg6KYeOaus5M+Elpt6AH8Lb05TTS4cD6pkxIBX+wADnvZM7dWLdtnQ -BL0U5fuiq9hicIuxUi6jWOE6DL+jisrAgJhD68Qg34VAV9ITE9KFLHsktY4Swnj8 -t6IXpZm/k6XlSpMDBio5PMLOFs0AnIFIECdBYgezgdkP49i001f0r6KGMsytt+Ns -Wj+J5Wz0mXPwYsva5j7jaxFtAfXVTNyyrI2aMFhu5MSs64bz16f65lVmqLD/IPNi -IwYIKKktjtAMYw9cNuOCxMy6wlIE4wjgyM/+MJEPTf7hhZyMKPKyhFwoxp2dFmOs -LrxPJlEkGIq4r0mqKtAv1Nu/rhltNNhXh5cLT1DrQgtVrKJadFxC1kuEIp0aD1p5 -lK7XjckINpPcbZrsDbzSINuZLkFOnRD/P5n1TfRVty69tZl7gVPWvOQbo8HVjMSn -qeOglOZqJy8iACf/zG1kJZD+n74fCKDFUDjrMYABucX2zJvrdh7qJmyUsZhNxhGJ -NgIWdyARTXcRMtYo8FXUfSQTWQ2D0OsPwi2qHQE2lr87Q34pnIf6Hgt86EkxNMxt -iQIcBBABCgAGBQJKghCBAAoJEBNunPF7XTQtNF0P/3Di0tYugJSzxCHMYats2Kkz -yQOrJ/lZRZ0MYZD3d8tQYj2zRRihDFscWQLR1FVQNbI08cZfJFNvfXrp/QXBP+8a -ai73u12hRD02fgPpxvmsP7/XWkoKLcMomW04m50SqQ7Hpb8mHDzz7SEDhYUCRRLZ -ubbl4RPfa9IFabUSbCPmweyskQHcE5u3T7O/qltjmPedvv2qc89hMcQKh88L6mtx -Ixd0p4tmDW2d0PNi/MzoVkRCLa4/3htc22qseNH9VPalxqaZ2F8noZuTN37PoIV8 -1wtzwgajb5PQ9dPUIWE+SQKnINa4ltSkTGBODiYfkG7b6hasdFcBZuwJPjXOfByS -QiHhZFBCbBWNUxxd5bDhpS9iKqW3jQ0TQnJWsCMu3ESBebtiz159ed8/wOYtv2x1 -h9q5nVFFYI/em7Zg0F9CfIoNCd4kDSGHIPQokqKF7eZ+8IZzC458iFWwO7YhkM3X -Ti2NJfuR2vRStgl59p0NbSvrbWIvWe0CVZPrHaCKu305p8KG71w6ZrDWEYY42u+W -df5MSSwTVf5wAwnkTbLUopGxTvNFyiTC21LYFNq47LVuCY3k/5yfI04PsN21OITl -U5uS9/pT9eLTzxjLGU/WdRUKcUTcwouS3FAbv79cHWh2QP/NqRcCx1632awrYYJn -9t8SnS6O67xr0Rh+tuhBiQIcBBABCgAGBQJKiWzWAAoJEJwnsxNCt1EdLG8QAKWW -HS6QR0SUQAxfdENOZ9+SvSqlGELIp//tetAHt42NJdzz45QLgiqYsaVf9zCm3ctV -4whsUAtXRnceDcJIvZWjNEncXrXQhJ1ZY4Y0EkVOxHogzHmVu6/MCfAcGUvXqUUg -J/Kk/30BH3otoexGXSrIwcs5fF4/Hx/bCo6jO0kWgIqTDpld1/Hw06ghc/4PfHq1 -tthPHHSUxaldQ/f0kM+F+q9Wbuw+Nrp7xarZa+W8/4FCPwJVK8MwDHD9LsjqEB+R -va+S+SidoSevnOU3T37xDJ/Z7ZG9Z4I7hoOUGWnO5huKSfx0MT/PXtMvx+EsqWCw -GzoG6NrkY/sG7IqGXnCz+WkiKpgd7aQeyipifqhmITp1h/ybcVaADRS+PJ8mBAru -aJSPqKa0gBxpUhDMemsnLXOUYB/gIj/c9RmNMHYIgLv5Hvwc1utG6PVkpmW2Tal2 -N8PnngZ1+mN6a6/m4ZGRSndEbJAZJoJnS0v6GoA1jZ/xMKLofmLtmwOA2WjxVuKT -jw/l7Lrl5DBn2X0kBESSltjL9jbXrT9yLmq3XMJpOfGC5YBpNOJqI5dWHz6zRnln -TiMbcrnNdL1AqfVzeM0I5I4WIdsSXdi6xHgbqYvZpUefCwG3uhrF1LIUh4tRVvTa -K1biW5U0YrDHla2PiGWrVzDmX/MdjnB75MFaHN4riQIcBBIBCAAGBQJKd4a7AAoJ -EMkQ2SIlEuPHaToP/2xcAV0dobl6IESZ7Iu+uVHAbLenWHCTYk2lO0hfqDIMfI4J -uaAL8BGTGO6c5HgT4ZgUuy/u13Jzy5cuJdR6MJJkmm7xqZAKGWa3AEnKmBG1RMqW -IvV5cD96Js/ycwKceVcY8S/gLiThm4KQWqpNZ9382mecaG0raQrYu0adM00oE0QQ -SoCKToHcZ8etyHxq1S2+0eewCX1dRPkskY8xtTdLiFStcgd9GvcWkRgpRI1g15fH -rkTf46/aMHYIk4Sa6u66knIuQu4aXl7qxvdcUpmxUIGHsVNB1Rfnt1zsp21D8jhv -YltjblPlu5SdZctk9n6o1wW9uq52rqxcKOaPrZqlSgP7cwN9hPvF02n5kYItDyS4 -6BsTeOPuPBfsbdhzrN+Z/xGdeGd79TjimiRJ9CeKkTQOtMncFwntqMecn30vYfgD -YWGjm+U0k2k4Uw7ThnbL3q5C2/16pjxlTiFu7Ku7R3qUVwYg2e046b2/ahm3q7v9 -WYnNE/bFH3kfLHE9derTUevZFV7Giq0A344tsTshBrg9u0ub4lV6kzpm05LLSqIE -QPZtp37ItJLg1ccL3LVz0VIR9xK7kHDqcTS145bo7ALh7ioic4DCqlvXLU7de0d4 -qUt734ZNS+SoMQeQ1j+PDvHew/HXQu1Z3gDKabdJp08m+6vzwq/hL2dskKX7iQIc -BBIBCgAGBQJKecLVAAoJEPU2qnARHVcWAKwP/i0rBJSm+Bcfcm2z0wICZSDBHdcL -hcPnr+LGIhP3n9qJHnZgYeW/qOZC9N4ynXzZHvf/XBV0zERUA3JIno9+A3Sm87IJ -qL2YHPb+VxvmGQwdeEA1LhnW1ph64FH/9wv44mmjRRFy4rehVI+N0dFVhpPUGGmU -olXDoOJJu7bQbUa/6hCJLnoJS+mhg1gmLxZghNHdBNXJy8Q9UWvcLoz96eITQSXa -prUAEgk0T9Dd8KHj26jfITYhJSk257OZDJ2wdiY8NWC1cplGkvFdQ9U5bsAjBHhd -qchSmT4foc/PV/C551bCslJ7xnNirKiXp+3Y9Ggoq4D5XK2SzmLUXtnMsl/2fV0q -jTYFud/R+o6fmFyAGt6lnLY4OA7VD/skvq544bY7Vr1DQiUM+eT8NUqUONNXuSkB -ZrZ9pTLbzJiqedizMgFliQXZFjtxf/mwBUZIHWQU9Pttv6tV4X+kuC/7cYK1ICWf -++XUSjCENEjIvi66uHcX5uS0pnbpKAjyE0V+e0L5haGYBhzCJcELrAY+fvcG9Tmz -N62hWz2i08rswD4R5kUjBfX2TeS38KnkR1+fe4tJBia2JJfPEJrz3E0gDngyp68f -Q3XhJdpfx3OR4OLNqavADsYBqIxrG7zRbiGQYvLe1CLMIpa20L6cqjGiKe6vKU/c -Xekd95g1FsjrOHP7iQIcBBMBAgAGBQJKcWbpAAoJEMaHXzVBzv3gthkP/jy73DUP -eKT+uhNYI3EJbpTK9Ke/WkHdDfb3jvrecCBRPonbF3z3+npTR+KhIjS8WWgx6jNL -MGanLO1t2I5E93ubprixjOhNSnllK27o1xzidsR03thfjJXLf8lVdAce5UMzFZe/ -ZGwFnMcv8wpzBSdOIN5kd4MQfAFQE/p9F6dukixTk/4lZ7BhkWzz4v/gxjda6jPp -wBjT8LtgIHlEWqnxx5Gzg+nVX0ST5HBR0KJQNela5QKPpgZYG2HEf4X3i3sF7O82 -tIs+Su9EtVwbbiRP2hCSJw+hQMUrE6KgGjdDGtqV5tUKgCcTBiOybrHHIxN3rpBu -O9/75WZmlc9SxvETUe9Kr03p4lHKdy9oHSPFT0Bgpy6PeSSpDe7EGLTe6HIKrByj -lWV7qGoOKzHuervQCouLNHul8I/NVF2uEOLFOcYbo1AgtT9tilO85hSjdAGE//Jv -//ae91Sid9y+Jv5+++KP6v/biUGwZETLIhNi7jpwwZMeemJIwU6jZ9H7h82mKjNt -bNegBu80SYV/btnDfUhLGYPPf/WYAvXU//g+rCAZA/8LMiUmQNgLL8J3IrL9kXPj -vw9DvZanw5j70V0f05mRYKu0bcPWrLVkNbioLWTyarqjXg7L6u/dxrgh/hMs8ia0 -IyYqn1zOUYc9CFFkX0Zj8xvK9kRSm1c5vMa/iQIcBBMBAgAGBQJKdIUvAAoJEM1L -KvOgoKqqxr8P/2adC6JrjOUfHA6rFz2ustdUhSCCBKnfbAIfallrAKEBXjudqUXr -YE2Du58ndea2anAoZNCM+wOIkznj0trHD2WBhKyx9pLliqWuwBIBYTlYA9VlyGvy -wEdcDWSmn6DZq3znCpFyjszsDnx5696vaM+Y3UdIFp7o53VJoA5XVzO/I5PQnWcj -yGQXr+T3TYwZ7IezjbNrvRgEXi6//CCD9XIAj0dzI+z5JM7l/aQjfQwueiOC8PS9 -ese+rGJZi++AiIsqYkUTvb1OPXBuT9j8ICwv3BtgafVO8z4z12aokiTXO48QXwhD -KLJErucrr6NxmkteZxtU1LmEvw0geZi1yu7EdUp2imVSZ02VBD77SNgvfZFMWyUY -dZOOque/cA5e/kYXhro5L+vd3Omz0Gxm9yEe7tn5oU/CVypNRGRnYo0v7pAmlB2h -7TPRgqT7ZTyhloPsx6apMorftE9tXS6SPZt/n3AhLUvWYTbYbg3n7sHrf41/U4Rz -DAOAFn0u6YFFsJwH0E3iIVasLw3VSDlgRpwicKUnij+KmBVQgv7InnKbEZXcA2yh -nda1/7ARlDqRRhLZTY1irwL/DY/3QIdqSXxM3STiLZHjT2/qOB/5fvy8RSqU4f2+ -VcAIyfMq31aX9DXAedEJO9rVqRNkO5hrVWMGqY3sOg2zI5EzuAmWHvHPiQIcBBMB -CAAGBQJKdMnZAAoJEFh5eVc0QmhOLE0QAKvYzML9SH4wP1Ty0HLTKJLbNo68ByY6 -tchFPr2cawfoNUV+4QDVjdpx/PjiQycApI2wQUmIdjjBk1i2Ud48shdfmSNPapwL -V2fgNMEGh8J6Q2jScvVLMTGvxC767CmWN+4lhVeA1u/biFtnKCSIw47nwsvqhZDf -odHcqZXAW/AygfMKQMzdX1BTHivw8UljhbrQ/u9R0/rrNqjs/fnLL0BftT1PsAJZ -NkpoYHzuwhTAbNoRT1g3ozjuFbNtqbX0sXVm5Y1nNhyKJQYQ9JbxB5Ru4/fDWY53 -VLnGU40M++O3u4CS+pj4WBA9gP6cuT662LbpJ5pdVFGOqyevbgQXMqfIuaYGUNd3 -C7T6gpnRIrPQsB09hm6LzNnoR/Lh3epCIIWWu1KjdmP/IBrBcoY/DCAY/wupI9jc -Q0VssrwlBKFeJt5Y+kzi9Cbsy9hvWA5/iq1s6VB9J7LCEXhgb/XqFG/ElCuW03mE -VaFwpPdWvSFX8v7qCp8kgZfVuVwtami1O0m9anD5G758k/yxQubla2eh3NsJ9MJ3 -BdKRBLWbTi7XXvbN5bAn1BQcCHFksB7+87V9sWNBTRxzg1nV1bhXuJ3ou0+vXPg/ -0fnRjSXRQnqhaiXeh10gAm6TEtRCS3NtqGY5U9kNevXIj6Rm9soXMQNgeVIcwm0w -0HvD7ZvblYJ0iQIcBBMBCgAGBQJKfYzdAAoJEM1LKvOgoKqqouIP/0SNihHbuYqF -qWwR8AMN6IvP8LtBHbkht765soIKPdV0d1SdFvayHLTDzIoAoAoIDe02GMczPbgX -WXUTHQHOBC05SSy2BlGl7zAktRRiJyeEl4npJXTcNfA7DjOPF8Kw7J9bMMxMUeaz -nQ6Nr/1nu84KjfjG6/veTtsts3oNg4zBqVg5RRKYkhdxCDqVaAJ5kRk+PcOUdUHn -pp9g9r25vMy2nYtbXsTpUAPwjN3644CTaDI1nqwDQUD9f7rxVT3cZCWL8SsCxgBh -ikMBSo+jozcOMkzWbcSvmQSwufaoFQo9jBCeQTEzmK8/ncRWQGCmjfTXamCVaITo -cs7d1OqzaD6+va/VbUidcVC+3s2xWy0ujpm9zExhp6G5yNwVdCxO2KQ17mGwW06Q -gKKgmX8m7xTmjrCbGri90BxJQyWXWCzlqbDbLalVvq2Ib7AfM7cvH5ECdXaYvQGq -lBUVjqNFUePWt8ZBJCYCtx54Q1pKdldygUmVQ2NHvSx6Wf/trmw8G8D4tH61V+lL -bpOei0+UOOACMZBxzcMuNoMlvu0aLjwsu9pXw6ibi91ax1O8iYKMEt51W9Wx6qTL -6KeCVHT/WUUrGjMJluc4ozagnSkDiL/f1coqYqdXe+dOVAGnsRF0n//swh82r35f -5xz2sloJAugID1waD9vZz81jrkYVAwZniQI3BBMBCAAhBQJKUMV4AhsDBQsJCAcD -BRUKCQgLBRYCAwEAAh4BAheAAAoJEACAbyvXKaRX/44QAIytOHy+5M231c9LFWw+ -Oi+LKX3qEGSx3Y9Q+XMiKiZynwSnkQf/OwSW/hMhwnz3jUIuimeZTb2zA3/ZhWYM -8w5bpuuvPw3qaIvIN7VW74tbs4oiqldJXbqYkTTm0q1HKmofYPYQEHDxruxNOpk0 -ISLmiKaCS71N56dB/TCeMNKDdnzmjTYcsjnebFKkG/g1A+3k76iq+PBOqdGJClkm -8ATmnhNmQqVvDC2Ra4IOMglvFDW7rxqqcRIwLbDXDQpt2PxsFyJ6dgI6rJnmXmMp -almEAG0A+tNXcjrj0eW8fSVOHTcv4o/6/8SHfrTGqER+tt0O+jjK5MVvfO/kby1P -9PLPxFap4xtDc4WY5y09XGS4hPcRbYv3ocARpfNjYfTOWs8J0ew7P1Qn3s6VWE9g -2zCc4eiqqy66PyEEsoMpndGprWbAXH9SFCOW4bz5VT0ecaWgzsDLuUpUjD6v/N6I -ws9PfKV5AopVkUp4f50WFQByolI9+VQWwPSOHhKFmnsRs9b8NCvIqugMJOOwzH7+ -3dRQ6VpPQoHBkeqrLRzYQdOHDNH0rbNF7byJ2N60skpUz1nH/ZYgaz5xGvubYCHV -crTRsG66Ov/E/eD2u027wgkDgVxDh6cjo/07+1fV2r/dBiCbIwZTmBK1PSAeN7en -z5sVdRQsZiSZ6safCZGIPYC/iEUEEBECAAYFAk+F6yAACgkQY0Ly7Lxa9rl0/QCY -oIZXWXJQUTfoEoErNgVZpfbNTACfea7u23oO5alYOsXTNRS+Pb+7tveIRgQQEQIA -BgUCT4XrIAAKCRBW5/+KKEDHCHT9AKDAdOR0Oy8QFWG9rne48fK/ZiAigwCgwWX8 -wXekUT5TERWoXmbTvz5Gl3CJARwEEAECAAYFAk+F6yAACgkQZ+dy8INR4K/5RAf/ -UgLJ1CRVXodxp6RQ50RW3SoPkMKpGiXsjvaZ/nAW9SvkITGln5FetrvLpDMPlMQC -MhvJLEuQly053DYo+CzqscSdGHdoDqElAKe6SRDYLzWgrYaq2Fpt+zd146hsqMF3 -toTsFe/ZxbGu11x0PS1yhsRWvUGorDqgs6GAHaEMIf6PFH80xIebcfgUMd1UT/MN -yFMRqNXJn1wOdR6pcNbwg+e06TM6q7WPSfin8+ApmLn6jNuaf6G90/PGLfZh3bM2 -B+g2lhc0shoEHjDGMU7I8YlcFeawQmyaZfGjMopd4/dFQt4SZzoAg/0WCVHjeDl3 -fI5LHDZLJztwjuUuBlAD1IkBHAQQAQIABgUCT4XrIAAKCRCoziimAQ1vOvlEB/9A -wd3njAtnk3omRq16DIkzq+81/mT/weg8k8epgpvzjUUb3/O6bLs6qGpMUIXrwHs9 -kYPGIvs7FB9HXceAOTi2jNMnZHz3x1fyzVpmLI210r+ZGSuOqhQMFivjyBNLsqOn -nuHMC6W6P2SKvT7q5+6o+5NtzNhYo9Ilu1denuIbZrnHt7L8Dz35vME6tBPOWCAn -ljmnAV+OsBov7JmGgyiCsf4Q8L/1RWsgAy8EoG6gneUwQ+s8z6zeP1oOiEZMSdU6 -RANzqy7psrMouzvVNh/NlgVAWLJAs2qoLaFvrJafD6hovol/Pl4jVQ6JXDzArij6 -REG1J71gDu9Vzqeeg6aTiQEcBBABCAAGBQJQIDy8AAoJECYHPvzaxXbm2HoH/2Iv -jDnBV295demRTf1oNDfUIMnwg3azRxpeSn1MEB2F86ADaHBVFlcrjhl588YqDrZv -f7N21s4h78sPmqLvXQBglKp3Pq1/R6Y/B7B1jgjFpGpk2prx6fizaM9NltjGPJ4l -dcZR4/R2hbvoiIo19gzS8X2tbasur3ThcCHdvtEL4NzkG+16TcUKp6GfedOyc/TQ -3y5o/IOd8LpNUIjzZeD31mpy6epUfdQUI3SPzKZWlRSjAWlLzbSetv9wl12zkYHx -U4wtYjOZtqSA5/8WP0KNMrRvXeiWJRhOCZWaHF9yEFxVCTqmZEwJ/s7Cmnsflpam -MA/5OHjlonl71OVKudeJAhwEEAECAAYFAlCDKs8ACgkQ1wD6SBOyhtkfFxAAxDgp -90na6t1Fl1jBNWT1u1tVL85JKv2Po2mobAsOdtHQsVSYgLvIqaFCMFfTHBCiEoIh -RcVLB7cxP2DJMKjVZI+ZEQ6xpduGAftVml7ZyLGh5qEH2S3Hpr6Ttlq2gyJO+n9+ -40UIEsOZlFtvRvO6UKoZnzL0PcrIYcXCwhCpsG9FHaiVc2U0bYJ6TYa2PNdAwCb9 -otldBYq8skVxx+TBhAGJ59xZYVkCxD5TVZMtJkQGUpnrosRcLuiP0m9+zNbkX40V -ZXrlqmxXolHYS65ZPL6h8L8j62ase5DeX4+wQCQSpADU/vXoTaS7VEjLVXm4IwY0 -r++jpIDyaXYcPRkC80xw7U3Hu2JZAJRvQ21XfHlY3pJSxsPYlRZLj7iYRXf5AeI2 -Znb66kSA1hAiP/7RTEierC2GY5TTpVLYpc4yIukhP4cVJ+YaRN1+wvoeImIHbllL -t5jLNe0Jdp6aqWvS3dwxn3jR3KvMtotDZjjXlQCA0QkOCTDxIz/lAnpED37EjbJd -HBf3ljo4acu0yQjFHREwTDVTjEnQEUG5K0MI9uDKICLI8jAnObrIzfPrhpC3uayF -dV85j+ZQrJof0RPM7rvBl3oyYufsYXdNns6B+Md5mEJpNTHVQb85fIi5Gb2VTJMr -Funcn3OFx/lKaSdV62Tiyf0Ry58iGNUh26+Lp4eJAhwEEAECAAYFAlCD9yAACgkQ -e1hbMIB8KodQhg//cz3IjcNEkyji8cM84LMt6oGDJQVLWLoASjUFPWLoCfO+Bb66 -393s4Wur94DolA16bBdEx0Hnl5EjtAvntlnsFiBWYezyg98Tr8RHzk4ebVMloW0Y -+HaK4Moziskv0g9twugKNiaUvF0En3dpgJeiyPvl/gHs725FqeR0ZQmieRQzCL/3 -jCGXdRcV5ajgOedR9UD2qCNqcqw4mTwqCYzUIsCjSjM+frt3/WMzS14YuNfqKDpr -cWxTnHJfI8tVQ3pAqTiqHUieFHohfMK9OWdalLCABwvJ79PQswlFHNJnUBGAh2Pi -ltgQI9PFeBlfm6xoemzaN/6WPenzfeqbHDC3XsJAiUUddln2bl0Mo9E22A1YFJ7A -I43lBLV0+EttQW30kXwL0PFlHzdyEg2CKr9mRV9n4p8IB7+yXRKvMFb6/9/qtANK -6Wf+qHdQtCrDqFbVNtcz80GaQAGuXCE3BVvvyKLJfG4bUla7e+nzRQ/LYEKs0vmJ -EZHHeVsKsZa0LTzJIEDetZzZDVTDWNhSuD2Dg5CHL0X/t9WxoNzbeMPG8SnvdRF6 -lLSN1ixzCm07491tEagRKdcnqRx8EW2Xjkk+uv2HH1VhwvRuP6DOma6W5LJQ5T9X -JOPIgq4trali5UnhG81EIXHVR4p2yudjiVrroN5KW4AAlz5twYPuF+tC1YGJAhwE -EAECAAYFAlCanU8ACgkQWWajClP+gjAU/w//ZwDlxcwH7pJ4bvsq9Tq+LWtrJ/Dm -WOA7uFmC5zQeKFr59XwdDqfY12ho8HULewpYPe+839eeVrOUvA5tub68oneprlpV -lwmgqSsLG4YlW9Jld3hv5Hs016n4WmFTuvVCkPHh7VbXt4ijyQNoPlolkZYlsLoP -DoRdMLBs19EH6EIcnDrgzAu705CsU7UG58Xa04SJ3MeKKQLAiVBUzhxdZM9KJK+1 -w9NhaSBup/OIkkqj3CeOEX5IwoSzR+NdV+1Fc3K25rvIUG9VMVS/hahQ57ZE/Fgb -/Klp/slJP/vjeh5ZrhAuWTuq8H18e/JNTzhU9w7sn8rU6goAYbAlb6dkkjK/4n7c -J6gS1YQ3TdNx6WubCin+jSErTBaoVnI111Jy5DNj5kEMZ/xmiibzKomn5dQmD9wq -D1MZhn699frwOlZYikhU7yJOzI8qOHMUaNXhhMLwZmHyut+7JXbCfWovw4sjI2cb -VjD+2Pjfjv/R22mFwBlXy3DK6fI/xobOk9mjU4jKUcqa1VjQh7XoSyjlKWYEJCCn -v0jwHjI7CghNIj8CV5D+lW9E5kPe3YSvtynaJTJjarD74055h2EbQJWIzA6woY9C -gYVsLvcTZpWpvmoOO80xC2LQIhTsBYRihgPr4PKU0IPgDIhYM9RF9ShAmTuyigkv -l6bvxuIOf39F9nKJAhwEEAECAAYFAlCurbsACgkQSTs8YN6ukBahqg/8DVPLYDrS -ozOs8/bhinPT5Co37FxMFCYUyHSG7+i99iSQXPbx3cQfscZDOL5V8D3P/346ztI4 -GvxNpubHhEmyEIL4Jfwo4y2/pDUnrSHysbPdj2cChayBj9SvBEjMXbtbdDW5GW2/ -U0CkwLfTaqgNnN5iUaJAOlo44j7mBzGn3BNsDgzl+j4A+wA0ietLqkM6NyAvAZkA -uxCwlIhvPN5RnJ4Y4aD2hzcRVFGMfNW/s98BgfybM8N4K8PMz0smfrT1L0UxuShg -iuTXAMv7P6d5cp/8BKluEUezcuImV/hshvrqG6Bsan6wYe3146bN4lwzvC7l7tlJ -j32xl/n8gIXdn712j3o+bTqKqP8e/3OMBzphbLhrpTMkVXRiBK9Jg2VgJ5wyde/b -+R5pWG5DxQI1JCJS1/IBHeoQ5WjQHXVhKnD3RljOFgwG7vhD+dBfFB7nXmYrnoSx -uGdoNz7LIPBTrzVDLhthT9dsb54k/x82QGU2Y938IEHhCfZ36ICNJHtUkcKwom1Q -v9DgSy2n5XF0pWDwtSPaIGzE8EBLnqHXMMxjmPOgkRc94SxagEKXiOXHisjz57I0 -J26OeeMFifockxcZnbLsrrSDjTGLDwLijWkNfw+e+G7wZwovj4pgAqv5Ue1zfP12 -2dWvWxXgQ1ZFywM97NqYXiXYZHb29u+9qaKJAhwEEwECAAYFAlITsLwACgkQzsmU -LbetuG/83BAAmaktkF8Hg3KDS6SVUKKdSoQZmDcoSXG/RR+2UQ/dJQPeic5NnOr3 -sqqEhOfqidYhKp/1c7EwzzoLt2+3T4P2rwP5LHakF6ALKZRzobwnaTytWBdSi6Ja -cdrBiCLkKFO1JkPln6d62zxRIhZn2BNlQPwi3fsCK9nOYVVDlb9qAVexW157VWtN -s5spwK3lTykH/a0KW1hxecnZrlt3NwvuA4evWGXERAihwNFEso0ZAd+4oPPaEhw4 -bVRCVZBv0PsHGOVLm9aEugPu0eLPDA4ykPNVVsc+LqEE/I/UbIRvdBcCJaDWqt7q -hYftZdv01H4BGkYhr05WxeWkgS8OHQVeV7skh8TTUGAKuOKZT9DFmz8Ihd7tIPZz -eV38Vvvk21bdPV7l5DbKz7q32w3UIkZJyfDuvs+AL/c/ChtQmngfuTuXeFw/vW6O -2i+oqaeNL932dhIWq63or5aKtXAka+4kRo9BydOwpn5ZC581QPa7RuqvO/9qEqHN -GweXwyL6sR6PpyJkcN6bfqrU1lhVfH4m8VzZL+MjVaxQ/0DGiyD8NAe/rn7Y51l6 -QkASsLIPU9wO60IyGCP2/CPiz1MAXwRKZqL5cAii7b10nJGkW9RAZgFxh83e2RQ4 -Fb6MEO3t0rIPSLwNtFzdVThP3Cm/3Nvg/m2u2OKOBjFX7ZfE0xtATRy0I0plbG1l -ciBWZXJub29paiA8amVsbWVyQGFwYWNoZS5vcmc+iEYEEBECAAYFAktXtbAACgkQ -/R/ZI1P0XTKxaACfUyz4lNJFvSukTpc0bSgBlj7R2aoAn1bX+MnVIm0lPGH3FL2q -YlTkEDZHiEYEEBECAAYFAkxN7P8ACgkQZR3zUj0j8l3dnACggcJXPS/W4eem0pvs -ZQ0z/l/R1esAoI0jwqqFdwz+XOgZQpC1SD2WtcrZiEYEEBECAAYFAkxN+UYACgkQ -v2Bwi0hCbH6GJACeI0j8hfx75tmBCYcE2vNE6ItU4/AAnj3LmLCu4N+kWozw2Lzz -syDlqE4PiEYEEBECAAYFAk0cvpcACgkQpxprf2uFLzdrvgCfXo+PcahFq/t9IoYs -DDyVRlNIVQoAn3crPgNzNvsJbcIWGAVsuxwqzJM2iEYEEBECAAYFAk07uhwACgkQ -C+Cq+bUsy1Lw1ACfcJTwjfaiypo6gE5n0URwW4ywT0sAoIZBcMI7Q04Tfnl8WqDh -qpSDM4mwiEYEEBECAAYFAk3WYs8ACgkQpQbm1N1NUIjo9QCgoFBk3QSXlrRAQINm -n/W+40kiMVUAnipnKm4JZp5hTUMwxFH78zasvCyqiEYEEBEIAAYFAk07080ACgkQ -OWBmT5XqI93tpQCgjV4vC8E7mFxhPRPJfGIX6KdsevcAn2k5Xl5ELVvFvhzeHhkf -br6zYOwziFYEEBELAAYFAky6zOIACgkQ8q2FrB5Cs2dXVQDg4qr9mwPD0+DWncgk -Z45pmHzX0j4CgHosPG2pbgDgg9f3JWhNXjojgXrEbj/ZV6lNVBtv5Yku7Gr2wYkB -HAQQAQgABgUCTTu6HgAKCRD37mFu4MIM/9qIB/wK3CZZsBja7R4TD07nNDLuW69G -P790wNfOHONtZhopdoxXr9oetwdimA93zVEtwe8lSisHsHDVRLzra05LxHAhQ9DQ -XRe5WsaUv8cCq+QiDvraDd8R5xvGahamSipv9aA7DI4kL6I5JutthPRWBlUOGyoS -J4LQIDxuvo+9NyUKUCB5wqo0peTlTqHVsX1BVyMzTQKSppKYjTTo3zzmiu9XcCBx -PdIv7hgnDCW1ATeBN6yMfSKtC1mznksZSEcDX9VDxl9AuBiptt/krqTMCd/Mo2A+ -Q0QVBYIooexV8l9gZLZ0pBSBhG+JXZORTOoHjmvlHQh1k25C7Y/wlDhdxhUhiQIb -BBABAgAGBQJN1mGeAAoJEIN77/kM2/kcsF8P+O7MHc9YWfYsa0EJzqQ/4SvZ/5Dm -Mrpd+abOwmBHycG0umVtFwqYYnhHJI3w7VCHgs8N4ddIQd5C1q6Qaul6eu6oP4qQ -dLa/IHhD7m9iM6OmUcH2Xwmb+p7/DWa5Qb/Y5z/6NsIvgQJi/nm7Xj2LJ1qxddYX -aBhm1ooj8rJqWTxShN2IvMWvkHe727xOC6tgv8KYYwvXakUpxO7lqfn+s+xd8J5e -pIsQN+AqGIzSZEru7OlYb3Ld9xlxP/RW3xPODYfDhpRL3Hc0X8nlDlngKM8D3/0D -uTFdglZGlMddNuy0XKiG3mpXEUYMJQ2/L7w10UmW4k1UuGcdSEjx16okzgWrf4Yt -eftk2rZVyv8tvTiNXC9vGCiKu+oA5k4ADpUiIpUBD+SRVdCLhAA4nm18yNSfW8GV -YVR3xGk4Iy0ITukO6PqpKHDJwn0vHh4wFcFe8xrF3vgp9ycy6zWdy7HXrq/wl96W -yCNn2ZoYOOO7je351TR3hOSSG2jKm5akk7203uC17XdWkkP4N0J0rRrIQD7gJ2c4 -iCpTczBPHw8rQUV7wZmapcFO9fJZvTDCbuSMQHACLSMg/C1aKb3j+imi7eyZpdic -hWEoE6MH/xmwOqcV/zSRMfV+WovN2SOkL+ZuErMDvmuqN1DEUaEcxTc07zIkd2xp -V0MV0ntniPbhoheJAhwEEAECAAYFAkxq6lYACgkQM5YViOHCGEWj8hAAh4PIrG4A -n+Rvdgg9JQAM9dxVl1lQYcg5GUVG0666wZvXPkMYatCjtizEMDwlKUDTXD5zGcy+ -IBRR7v5cQ5gdTlndF258NF+0clhGkXJZ1ANN5GICwpl54sDLij0NrpC0H6eTtrT4 -avNN7/leP/KIGne2qnjUUBSf2pP91dI2QvpHWBAe/+JXDIcL/k2sMAmcubNEmmlI -XSnOLBEidOGQtVBkukvZAwfSJH0JQVEvG05PQALNzugbLmFeCu90wQq2pM707+GS -CbvchZep4ExbKx5JKKBf8r1u5egKM887ttFgWvbbthlEOFyDVl40fXZwI4HTVfH4 -MDf53h82L3bIPRrv67Rgr9hw6PTL44jaFsbqqpBslcqJpKfXd9s+rPFXS+HaRbYq -Dz+7tn6s58Mni2i993UfPonC42zd+c3NM48tEAIpbGkE/pGgkHxGQm6Fk0KKiTeY -v6EZeGfa5RSlIjn1w+j8klMpg1NPIE5ViUL+mVAOt9DLMSJqw7FzlcjhHE5qWF2y -DSgK7eEXtiz6Pw9dlpFer78yhLUZuIaUdfLblNm/vecr0QYhsHXPRn+pbDo5+FYT -8Bvx/jPzTCI/kV3Ko2OnVecfoKOTLacnP9MxAV4Q5tA8BIQhUjTv6U1oyLPgzuBS -v3XxqCzQUZfeJS9jIpog281xYTinE94kH46JAhwEEAECAAYFAk3WZIIACgkQteOZ -n77uZ2QgEBAAmquCL04ezt67nfD2hC/LoI7sm9meGv099jWyYAJUDprvYuFqClkG -B4eF2mqzAfwZwGwC2ZfBmyout3O2OAQAXo2vY2NbN6cuRQd/Jhvy7KidYnIzn7ov -aRPpRW9DecohZ5A2ln+U7jyJf1cShCmYXaeJNdw6ugtN+7RX8JkFq8JNVJUAleCY -taHvETkaimEdj1B0w14kNiRcJErfxUejIWDmlC0yb5UPNUoxXFxtAxN/zoxugMa1 -l05/2hO0Dk/zUhY23e3/ImhnLC7ZugLduG8evQBlqIuuwDZmqwb3GicwO1wH4pLK -0LpgoQMyXvam5rQXZrg0WotmNzkHv9B8GsxpDikz0I8MNc3n3VZ3jhaTuvTjArfw -XTuzczWB/jvNlP+LOZs1Po8td+JwplBlpHn2AbdwHSmoxx1h2EZfkT3Yry57TOCV -gpCIxrkyOZI1I5oZ7sN4+OdvV1hNSgya7OMu/i26/YD4sEfxhdD6C5UdxXBGGpMJ -Ja1wawpk574M5789AiEU3ohrKIG2Nf9T1MhDk43maK6zvh4FYSDxSHE6hHpCI9lI -TDzoHK5FJHpQWQ9sAWFACG1NxRfPJqvxhz23r5RqY9g0nsW+eYSpT/+V0ti9V3B8 -lUyf9I9ztyc2E7cQlnxAkEEOfzmJkgNTTU+wYBEQdNFIv10+Asw4FriJAhwEEAEI -AAYFAktcFFMACgkQFigfLgB8mNGwExAAgIFVdql4K8JBJ8DHzkMw/z3Pgz6uTGIc -gB2Z4N82ktNfomGIrEB6G6KNH2WzE9O+RP/EwUAjvBYD8nerpkHKfgMUlObiauli -ZgY8Au0kB82Sa5olkszGsWka+sX5MrQmEPthZ1VQNd8FvjR2Rqk81QAzlvbYPSrC -QRgyt7IS8jPP9TByLJYFFcs2QaMoQlZ/tuyvuqaQ3gcWr3JSdyZhO/HTrOS4JjIc -qJRgwyb6uBXKsqYepU3Xwa90OSBTAB2Irx+H01f/2W2kDD7b+WIeKWDdkKqTtzyG -o/zaVOpWrcy5MZv3RzaIOSa6OlM00zVGJodn+k9WvcDrQOOIvhKXVUSzPFHrVgPt -rDfyN4wtY6vplsNPXJgNbn423JqdfOVrk4P0kY7wq6Br0qu6ioBpDNY8y+y8hS7p -MTXh1gFHL9K5suLa9aQ6M8I4y9W8hAn9z5kIvyf+pvpPUuzWmYil8W/Y9tCeyrLq -rvHz02x2N5N/hQstPK4eUp9wU0xUA0Xw0+6GKwNQiVBkv7zG9mo9iANm7tDb5AQM -uyQYlsnfQC8FjTXOK5ZP94ZJdx/WqzI3n8He4W+++tQn4LLCsaTmG2eM+BCGv34H -2CU9L9H1hbVYxvnZbEjSjvS6piWq+zjkdNpnRgYRFnFrIZDz4Wc4JNPmT7do7zyR -AxX96SaLn/iJAhwEEAEIAAYFAkxiEiEACgkQ5WK9lL8DRF7DxxAAqb844FFn5LZl -nfHrvyITzbGBAsepebwJhUgg6IMLd4wQi4V4k7KDKIN84r0JuSjLCrx8pxLJXtr7 -kjDou78GBbPwbGc6q9hW7RRaF2dLlmY557b3oGkySXaGXzF99OTthfFE2AKx+Aq6 -yXYoU4Phr3Iu+LtVl8UO89z3MQTKdpzYZuyPtpLa2+GRzA4EquBWujWBuOB/vwcX -9xcKnAU04gRT83JZQDMDgiM5a/rTkg4wOuMKPQfn+P/psr5lr4lS6mI85S8qMJZK -v40zgGrpsJJ8orrLeCWnV87MhD8LZHYc8vCfUgrwwp/UuJI7E1kKPs2SNz9/c3DQ -jlWFkfvETOuh3+yoVaHKm3dyxQvOUVXWvnDSMFgiJC8slt1Cm9vgML1QU+Y+Cs5a -yNjeyVUsoFTfDMQQdwYRHM5SX5qR/6frLuROPOPVxUjGal17WlkEdjA+WVt/ckVi -E3pyXUe2hNLwkQkhlHnlBSPYCmyLGMausCSiuo1KoQNX/s8FAW+/W5VnLxeDKqFv -OGfQj1w1vEu3E8Tv8QYCFEtzCXn+u7FseFUGCWoLxpN3J0nFvsKJ4CbF+n2ICp1Q -D+FTAOpruYgRV7c7MwRTojs8LS4DkhCLaHhLonJZ3rkLC6oIuhUhw8rO0hm7XigH -DvM1IQ0qZsuAODXzODiGC5avPlEb1yKJAhwEEAEKAAYFAktkYEwACgkQjL+aMihh -p5CQDBAAjclk+2qI2hzuGWbI+myFeyLLvJ6a5Oh/M9q1hV4fJ9tTB1WgYjJ6DD2T -8/wg/NbTyaX+9szT8QZwiWiHCccQd2j8s9GjYPAgvDrXmX6YApsN0Ss3fd0Fr+h+ -RfhFVWMhY3mzcILzeLRJSVpZ33NUj/RgMrBcxInCoijP2V67N8qbAXwPh231zOjw -LML39KY8oqALVc2QuK9G38ZHVe9/Y6jexvb5EAC0Xb58yw8BLB9Fb6yg1QfKyYHy -HC5kblmSDMnX468LUhdW2UejwwHI4nlS4nouI0P5A10SHO8Y4v5PkjjycJrouaA+ -6zbTqC4tNAZdAatpf46kkV5DA9dEAJvf/K8OT1/10LVORrJRr1soWcmF0kho25LN -oAh3tGV63GKxasDz4SPbDBLp+FnFi/8EIL2i5dmOR593baFbrqAQQjPFU1JnK2tj -d4mJW+k0kqiOK7YU/FSmVLeTsj56RjAnCJX1+qCAU+gOF5eUyzItJK34BMF4Q3U4 -s5wCmt63DeLNjOwMhQ+y6+A+0ip7qqkqWhsEcxKCHVsOCt8Abpf8IgTZmigAXM1G -dpQu0+wzakJzPwFfiEfKQYT9BL8Ac7kSPSVa4y4/HwW+7Kn2CWvjbj7sMk85N/1u -1Y7zAKVg8ahhNLM4ZV2nJHaOFTkGiFq621W8lm5vuQXr0UwBheiJAjYEEwECACAF -AktWcA4CGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRAAgG8r1ymkVynsD/0W -0qyddaTtmyuoPT+wPRZ67zQvUfO4g5ozKXlG1JnX9L1X2fXOcmvKQFGjmKJamNqA -Q0HgFZ+XK0CIrokmudA967QBvFjnrzypw3wGDPrhI9KZBKoIbmiHCszOq5v+qjGR -FSyEIKOHG9DyKCwqn38VWC+qloZkXIWOPPlA2yGsel59RVzsA3IPtWo8q255RvXP -DH/QR2K7xgJ5Aq/p/DqarUQ0kR/XaQRapDo8DZtoncwE/z4Zx2nYhly2vXdD9hIV -SLec1j9y4ZjW1Y/eOguOQSTfSw2FEEhiAZXTFsbdkaKzgEbhlLi+r41aa8Ep99iF -sjaFBBqutF6EnOgMGSfvJyVUaNLkWDu+MbUj2ka9aqV01CfztTizTmAEk4E4L0eD -3SDa2h7ahDsVVJZ9SHMaSF+uB3IX1XZSwJAnPiJagii4tIDRBN1y2SRwS0Au2sv3 -j7aPJOCIWfBgr4R3XbUIr4kz/5MGFd+1qTu9R1az7MY+z68v1HmxjVXXjhBjXt9z -KcvuRa0DaO0I73dapHJSpX8Sgs24rDY1yLgWPf+0fi0mY7FrHpU82rG1WrAOOvcI -ge4l0uuP2nyNBiIplZc5A5OOqJaQ0OdwCJmubLj+AY1xDgGNDwvh7QVazqgh/NL2 -4Kk2FZRjVn4ClOYBxPrpdxecBVwsWlfsZrHBMe7eYIhGBBARAgAGBQJPhesgAAoJ -EFbn/4ooQMcI10wAoK86CBBkeK9uYm+H5qJLNTUc+SNOAJ9mGwDdSvcMlq2st85V -ryUAT/WBpohGBBARAgAGBQJPhesgAAoJEGNC8uy8Wva510wAniZOpx0taS0qtWQ/ -76wBShwKHIpmAJ97JtKWuZXNkwUnF4dlyn3GKpkIWYkBHAQQAQIABgUCT4XrIAAK -CRBn53Lwg1Hgr7rYB/0e1aFDXPqLF0h7nVdOfxl8e7VnCsC4xDVS1Qs4+uSSiGHC -J/mtHG11pBntKF6E4bYaBpY8y5fUIDozsMmtA/3Y/VcULTs5Zs7c3wCQEzXq1NqU -i+fVuNmUbWwuKJQQnJvZut8460pU68o6Z6VemWE2J0NyXXfuzbvZXqwK1we1rxCx -LYXRnFo2U0Z269/hPSodNQFKn5ok0g6h5vKdi5N9T0DNe/AO6WQHXWT5ddRIqFn4 -Dy8dHHl64+Uh2LZs3BMf8306UfjRv+qmmkW6uBwCi5Fn8Pb+Pf9ABdmlt3Azi18Z -izh1+LMRH6WApWahuM/aV0XeQvGh6U4hOuWLGtq1iQEcBBABAgAGBQJPhesgAAoJ -EKjOKKYBDW86utgH/iIgRmWBhUG9U9jdLYppA0FicZAlzOijKJUUfhiallWz5EYx -zFwU6hxXCcvH6bIZ9y6+OrN+f6A6CAgXS57zOx5HH/jAPw5wDAQmGdjwLXYhAMdS -mUByryUKhd39mrswMkGGoDWfFySlF2KBcBd5/NBYQqug0i1fh/R485Yd8l5vtZjT -mtDfTexFLODi3cAu/KD+GCWwcOCwroiQP/IbVc4Yi5yx/9F5fNFj1YIo104HmtNx -h5ee82d/7Zbqx9KD6FIFM83bVisZ12kdjge4qrcv9YHRziu3q+Xly37A5fKImE0u -9qEJPwpKyCn5RYfNO5iuT2aa4a7tv4eyVzun10mJARwEEAEIAAYFAlAgPLwACgkQ -Jgc+/NrFduZ2JQf/VGlbT9P2qkH0C8jwWQQKow8EJDFityigEgpQU3WxESp2oR3X -l5ylRQSWfav4zhI75ONanurUdqNvsLq3ov8sJfCseSln+F1axqFYcvY6yWl6EjDb -U+dyOryzUBLLN42LWwiM2PwjbWCI/WPeMg1yLtjKNFX/KPmmQJEFMzvhmthT19Ea -BhvtvMKogxSQj4i+hfwH/IoHOdpMQCYYpvFtb1rXmHhu2CvuRGTRE7DfQuUelwGT -wZjoF+IUdiEgZ5YY3D52x/q6MKJeR6CR8NwoBq3ufWic8vnovF9+EQgCc50DrRlg -+yN/zSj029eW7EsirbR9JbozfqsQViCtcBtCpIkCHAQQAQIABgUCUIMqzwAKCRDX -APpIE7KG2QmlD/9X4rUEJLS3tzRQ6K51cX2ZlsYcPlszCatvbOxycB/UhFEeUZqb -pJJikWETYOvF9bDdzgPvmq8Vd65eqGyqN01vh5gjKR56yt/elENUtG9Y3BuEsfxq -n6lPOpDDjHgOy+PI4L8angDdBiTsAPz19zM6Go4LCXhzZTf48MznS7dG4Y/bTDL7 -7XwR3pGec0IEeCm3nN/tb0S1c4LYzCceY3BPZzmJQG2OyvAINMyushl2jyTTtuDS -Ng6TwLy+dt+nIA7HQuvxWqVqY++2ZU8E7DLsIBJXfMsPI2hsxRZi3b8LLs+kPKzt -Cdy42svaP9xtAuCCXlFRq/NI1VTJ261F4wj4c10WPH5hYmaJip9W6vvty9ZLWyCi -D0l4fWPviZC2/1Z2xbh87tzRq20i1wYwxAcGa/878C8JwPxOTzd3W2AqtLByX26K -bD+xRCLz2JVeOffnQtfwEHlvivjLEc9Fv+SdgNGaN3fRJVeQ/STI1DfW1aSw2F6/ -qOg70K4qnu+oYCqI+Jz5YIJEQ4yIgF9Vh3FJEPJljxmxCdNB7CW7Pgytmsvz1xRG -FCts+gpQmthDdihqI9kGg1c5zqgk8oUv2g24Xw2l2jJO0M5zSXza9VZTX5Of8gED -xTr74tAg9Yf0PpABYcpbjH8Br5qzmLFGXhxJ1BD9I2jEEnkQi6bz0goy8okCHAQQ -AQIABgUCUIP3IAAKCRB7WFswgHwqhwvYD/9HyjyJQoLxsylsJDl60I2jwbCjn2o0 -wTKvJBJe2IOWDFKeb3CwBkjL6aUP+oSDBHBqxPjaIDtQTQzKvqYTztMMDXgHjv6C -4tLc2D/LNc/hyG9SEqVAHfZN6Arycej5fEDJWoOyICe8ZASMe+v+el3CNfOA/qMn -kqOIGJBAD/sfKN+aNAD9Ul53I+pMTiuDhQuwv4EsKnYjpAGgblIF0UPXBorvSiA6 -zeFRs+YdYMpt+vaNFxa+uyI2VW1urLymEPfXlSrWPzm6aXilLHlW8ngMm+aGEIYj -PnCbQom3deorYCEmp9L73dZU5ZsM/Z4vRUYDHriggaW5vJqgMAQeGZxlliI1rwUj -tY1++Cu24J0msBcYxCXvVZ/6rsRVMvuPnZ4QnmMrM5fpdWZhcCeQ8FoKhgjqvcFT -XjjqeVYz5Wp/7KiQhtIBKYtC9UOtkwQvhihjgGTpC8Aj6JFKi2VAly0HZGzcxPNT -+7XRNsrT/fizJ8u5SMxmWYXKZWHWRTSRJXK4laf3+qzvdDghrRH2TTIIXWDG70KZ -BZcK/gPjjqq52lowptgPOf2KPpTKet5iZMr8DiW9NPwjnOkiVlFL8k/6cC8CT6l5 -CZfJfgnGP8FeBKWvdZXzbabYrm16iFzcpCxbDis21PGwOoNnBAEJgUtQwFJE97H4 -Mt+5xFuDH5OzAYkCHAQQAQIABgUCUJqdTwAKCRBZZqMKU/6CMK8kEACRhuypc6vk -9GmULT3CIIpxqhcqQGVev9NZ4X87Dz40pDsTPFare6MYjg8PnbxAnPSLDydoE4oY -7MuDX0+B5LLqdHBu6uDRTfFs/f0nN5uCzayplJXjjoWd2FRYkLYB8PPQtQA/T6oF -i5Bjs0DYaXdghWWYHfCRaNW+kvUpzPIvhjl5T3RMVxJrcfmvmPUYsqk9rKTS32sw -7HZ8d78TiEeEnxXtipW2ZrmqzJ5QZVcjycScXiPixI7/SHZlk8ZBLN8dDMhahq3L -bS/1JIkT42hFULRVa/lujqsVAbY+RzrdIvGNk60QNzzUzVHCAPGqxzWpz9S+AB02 -+ea3ahyu6hbW9mumpCYYW7gUiIrUkC9f+5DGdlIxUmN3bCtg22nDDD3w0mVmkfYK -optLWz+FzMzaiks9W0aOJv/VPrykSa6YJUpDSrV1xZVudOw56VMzkTMjtMqQ2Vi7 -Cm0UpMEFajrW72beBgcRj/iCxpgrQx0oc74eR56BGDxMYnmD/dZPQ7dQp/rsNe5F -HrNznRvvYfUu28fcvihZ3VF5Nb9TPa+ddCudaQP3tWYsTa0fulSWniDRVqw9cle/ -G/U0AtqRG6GcUYp2iJoqJunlXDScISekSqyp4ThpMKH/CVJQSwSk97Aspfbf/DcU -thHICr1WiiRohiOXUqG+5JUhcVI85UHOAokCHAQQAQIABgUCUK6tuwAKCRBJOzxg -3q6QFitCD/sFmxT+xxmPiqPLD0xdkiw2tN6GP66neMhvR14Z8I4tu8NT+jWhk9da -aLyE3KfhZjUuDaz/YY6cqiOeME4Aq8IzSCNhrgJDnH5WeWf9I7w00BjU2mk7mL0+ -kWM/QaAVp4tTDl2ssNill6PQQFX0l7eylRN2a4s0I7LPBrspa9zBlkzw4KzDA0bX -WH/wPSS6oEX73e6396Do9yQjbMppdUQxQKmjoCMKx9rb8KPlGxVvwidjsqeUdHKZ -aKiiA+jZ9iThUPxG6nuwL7H1WGKsA7OkZMOb+YlNPpJR3D27pklUNvr+GlwFmNK4 -V6NazV4ghzqL4TKLDATcbfeGCPH04mK0z5rXcYeFfZvZ3U/SI5vWewYcq8S/mWpc -0yImuVbUagLTP3vND0m+XJC+gX8kxj/x0s9UhRAveJcd4M7BB/EvhWJ7kolh1MPc -L7FGQ2wntqe58bQRryAioNPtxJKDu82T//narPPvnPjNbcrzInzfA8sOpPUIHZ/g -BAM+5MMDTWZWlqjUioibi1w7atFtFCI/EZU0wn5PfYMzS46u7KpbML8Jq0DSajKS -JbluMnsl8g1yVRf9khhMpaZmZtH1yc7S+qVh9+NTOQQ1w+eo/SVNppfScZSkXqs3 -1Ul04pCsOLt2Sx8M3K+lZvODABePON6Kels+TQzU8h0kKqr6xc/O7YkCHAQQAQgA -BgUCUdbkZAAKCRDK+zutCnXId8+GEACWewX8nHVXZC20y0/bC59h/FfdxSlpNoyK -USvH9v8W6CUtFak+yZrc/fUzDKnfVdp0dGIfi0FD0aN1UCtT+BHqFd1OyHvGk62L -IJlWrUqfjg6bv1eGezWnJjD1hJmaDx4OSFPuJEZg55v/w5Y0uX2QxekuB+jucWgE -e0iXFm2Ee8bW7W0IrHs9vzbND4qQt29p0Ec39XZWh3O3PgWgKMGTDXxYgzJLjyoG -hL9pieBXNXlCCrFMBd5MxWg8IDUCyWlfQNdEVNK+aGf+IH7gfy6fF2nq8DsmtA4k -xB2Iiq60UpxCByTwnamGGSwTw4/UGrWNHPLx8E7mXC2cWOhuwTNFVdC2rbtH062H -3hvbz93EU5M2N+55KOlb5/3mVAoVjElA5mE+t9PosOPh3JURjhSC4i3vIpJmADsw -K/SWfuRcyEphWQMnpyQRwR+p1DAaEu4Ib2GJURgrkYu7t/6MptfSI+qI0uUVJU8B -tzTgAnTbZ2YgBMHCAC2D8e4V+MYD4QaF/VYQTuFR1zpP7OwUOn/Cj8gxLdkqVyWm -mf6mlWCp3Hv/bIYQOjonSHfev347xettUkmCpBs6azXwhjuVjClnjHIb7pn/VxHB -laH252nfkCwj2qVJD2qXUF+4stMOX5S9RNbDwJmAYH3ouQJBNGf3ofUqMHOxILsf -ZlBMzqxPuIkCHAQTAQIABgUCUhOwvAAKCRDOyZQtt624b1XrEACi5RZBzRE/zRLt -bQYxXq1Uiw1rqu99y1NA7Dpgl/at8oHY6qe68ZngckDYZDzpNNZfqG7QgmURktju -Efgcd0TD5tM8EEiOPP0XgwB0CkT03T6YQQa0DdUU0QhMAOwOaSf4uF7AAkZ019WH -BnxHZZjdAg1ddgfXhOf7Nmm9UXHo+nMuW+b82OGx2NcjUgmCAGafdeE3dKIsBuyc -0e2c6bjRkQeqcQmXFYzhMiL8kLapC5Ai0sbXKZDZqgNMq73SkpDxPHELtZ8G/3iH -8HyYvGUulSHOJzOu9Jo4gx8zI8ZwE8PS+kFS76650lNj+CCHXFY52Yg/ssO7c64H -Z/lbnl1OFnE4UALRd0DUZvm22joVjCHoCO8+mOyOMDOhu5gq3sgZXNlp7ag3wHo+ -xMjKsS9GIvGNZAViIZ1ZsUo4yE4kmFyTzbXQRnR8MS805EsDAta5OT6X7HHbuIXX -NXUle1iW44zv/zbxf63k+JLBBOOFhhzXjpNbWZ7fGFfI53F3rRqP+dHuLe6NNiNS -H5IsCGHBfRHLAeiu5JzreXUErpH06dihMrC3l1pQG7M8KeFYW2eTjy+siR72UXri -mv4eIGEU8LKfZw6hZx1y4vYm8DU2mQMkXh3rcY4xsazMIM7skLSSo7PBJ8yblUeO -bOrb1Gmoj2cGpX09SLE6VWXHteMwHIkCHAQQAQgABgUCUm5ZUQAKCRBsZYDne9dW -xGh6D/0QYQneyd6fiCA7FPl0xiiBWspvUl4wxFR37SpBuWbeyDymzEnXBBsj86DX -ZMFd7lRAPKJmuHVPWxR/40jp55WqmBtJfzgIK2X6ffWG6xkns9w6J8yueUI76P5j -kwKqrJosAPv2ToLOHwIpSxcZNYBOIlyDk2c9avFaEhJQuPxhByzywxTAAhXPfB+d -FV83bM9TlJ2e/NWCggB+L3icNQorDq/9CLhK+Ype2ivFVMx2N/3FBTpNFejTN8x0 -yVe7ppUZGp9RUlRIEDHnNO+iMyPs13VuI66ElGrsKSdrdT+4uaZTuGojwcBxEm0e -6/5O40lUC1729OD8gKbxEh3aGY7jHfHeoLomc8qNKstLOTJn2WqW4VTjGNZeVm0r -O+INGE57epP7FiBAZW4RE0ZAhNSMpAf6LqZOjEDbsMlGJqY78Im/dbJFFiMRk3/L -yJeh3sY9n8A3TR0LxCFgLqTNGhNsHvahTvTi4Wi738Eoa4w4jqk6wLpyKME5V784 -RdIo3VtIVqNZS/66KzklHxL8tmFzueDVDptlZFg7jj97UHuV2WgFEJnRGuEUC9rA -EzSYv/YZhKVzVfQGYzY+iS9WcgRKWGWCvk6J416XLV6W6/dnpd64H8SFf5jvOBaH -b+Jiit6rEinFAsxzHdhhpOi7qhh4gyr9lg0BEflY/CEOarXWiYkCHAQTAQoABgUC -UmiZfgAKCRCNhuf65esMEKXYEACYgvBO6D8APld+n+kItQZJ2RNBPdcWG0kTw7wZ -YZ5UjR103Cp+727zq8ptHtV9kE0Mgk+70y0dDXFRrZ4IBFxzJdubyCfWo+WqkiEX -FbDGTp21VqfhXHSOwok+dW5Gup14OxxCvsugEhJ7xDx6xqSTemiMeYm/eNTZThy+ -mRi5gNu9f7A2a2smfV4LAWGKAd2WwUhoDCCGiTTj5QPPWapr1SqPG+hdm2Rxu9aQ -dI910LJT6oesstU3vJJPYyyBTeU9jOSm279n9jeY9Xz58ugF8r1IypZgbOGr6W5D -w4xBRF13DfnxtBptKL8caiq6HX9iFBOisOQUhUJsSfnbFC6m0HJGdJAOIDyB0yWr -tLrnDwpG4TtV3j0J/GgaiMWmy5WbkdpkQp+z09hqaHYHWK5GVqZN047vE8BS5cbl -xbi1r56kcmrHft1/PMPqJbbWUXCBeRfk3rESK5AiAX7ujsXEo/ekWFCPM/GtyN85 -aCrU075thGfGUhhwrWqi3ncHmT8EZivOeYcEI6Nb/R4SNh4Bgb7U5TW+tf5Epzep -c2ioWqY++B9fU52IRVAbQwGLnAnI2gbekDb0RmIpg6XgDUow+/eKudIkj8bzTY65 -hRoEtrWJamRKUpmyH+vp74qzQzaz0J0lGBCp16r0ljNzmJu0fA8R3zvKG902UVbI -HbXAVokBHAQQAQgABgUCUpDt2QAKCRB4GbAn8bBeNecOB/oD9aBx62C360XKiDIT -ztejs2Lw400rIV2OWEVtpJPn2+AT5/C3qDwY98kUciGJSq+09uvbtAHuRup8EDFQ -WkCqtZvkji52kaZxDjCDtyEZGfoQpWFVJNSv0HBaJatS6uInGcs778PDp04czK74 -REdufw1acx5e/54pvEq2anwZ6diY4rr/o3njHr09INv9Pm2ASi2qzzx+zqdvKrhF -EG8ADuYc1F82nHfjKwEvgNn68ojjzjcjyJY+jqsUv/ox136i57ClSS5oaKClErTH -RjX/Qt6HeFSYghBqPL8axAVCvhPF9idkYLxDizT75cL9BrgSsMq1TSC3y9jD6E9R -nSLliQIcBBABCAAGBQJSkO3zAAoJEGXrKrEvDseKMcQQALIkjpirrV3qqkZFPTEs -uSLEKXcaWTw38CBKYiUGZPKp1wMbYI4fz9Dmhy567XFC443CzAAoigjCku5uAgWF -piZSEszGvbdoKSBHoGiJOe2IxjlH4zAz2NBBWaw8ExsNccrHOSaYn+FdZovt/Mrl -1cjDTz9x76koZMBmQgdiQIWX5Bgh4tyFzX03Mrd4H+72xDhMekjmhoaEwnfbjFXa -ltDl+Hx2pN9ZD6BS03sxyU9UJ1E+7IHZ61s4un5fwtEhxQ99py/V7MDVVC6qSKMg -im2BnoT8ziwgboMSzN9MoJCGWLuUMRwebttpFFaX+4Hk6J768BAbUhgre9D0vc9X -teQ+btVl89MSQ1K0pam1oYbtLZHr9KKvtpTbUWex1Z/coAAjyTX8/KDEA491ybva -lRKlAtX0Jf3Qayax4peGPf6quSMqbgvt0EUuvfjMQfxo64y9nI3sbtzPoKyA2rTc -84Ruq94BZl9jkA4y3yrLJ2Wy0FB2ef6wEkB4zBNuu86nvYFNgTLnBrh7bxIUBf5Y -H4xQhIjfsluKSu1op6HzBw5rNz/g7r3NIbQw3omnf9R9vxRtFrxcmg0ljGiaSz5o -j56AA2tOhKAz1O2Btwh/HKLfiI0Y+ebYg1ubaTSl0JBfnV7eGEHvQ8fphOYt8D4l -TksTNEfeUHoEdhGHy+9hWU9kiQIcBBABCAAGBQJSkO4fAAoJEIGdJ4oOb5kqlN8P -/133l8vHHR9s2KfuIRuyNowPFiyHjORqVsqB3PbVByTs7stNd1Y9RgoOpaDbvcT2 -tJkSORmzBgxvbI2pQYpz/XP8xOBTK7r7tsBRdxbz3hARU3LfLCrE2KzbaIOvrWxc -Hd52viDCuZI3hxQhz2eVsSMG9szaTsnjrndMcdnqx8rPO92ngD/HgQl9NHB7f+3A -Ey31z4KX33ktgX2LRs8j5CKXqqGYDMy7V+eKdEP77BMI7ie7D3tcWkYtnq1XoDCQ -bvf2yBMCOdnLLKr4ynZ9grIGZNSCef0qN8/I/1Jyjv0RuNiiOkVrKcFlo8sHRbVj -bUhjvY3tWemVO+Vgs93nyrbvGvJJ5bAyJXLMe9ZfQpWPJQt2fxyHpJKChGpHlHCe -3jwMoS9qvRLC5iXkm6ZYezkYnHdWIVMezfBc78SvkNyxf04NINW06MMmDR0xeaNL -/V84RfiSSbFSuFoOwz2Wdo6xohGQu8FW5+tMDw/sXbYP4geSp+317m+hthJhO8kU -3rMseBEHbEJU4rfxnIsN87wS6Uu4pATbVmcY6dIV/Mbft2K76bfjQaItAUZuAiuy -YHLetvtXYvHxL7SqZpfY04eVsfRznxEYcZ85tFy8qiQ+uyBG0VtVzrDbIhfwd18v -1jO1h7m9ytNf8gmH/NtylqEhXF3bbCuQbptnV+1RL/MxtCNKZWxtZXIgVmVybm9v -aWogPGplbG1lckBkZWJpYW4ub3JnPohGBBARAgAGBQJKUSQRAAoJED2vVKIe71J2 -i9gAn0ho0xjGbptT2TwFsmXGsCxppN57AJ449b0494Q/G/UuzGDzpDvT2ffTAohG -BBARAgAGBQJKcrYGAAoJENTl7azAFD0tTh4AnRSxRB2NIg0tKj1QlAt1HzzbsZR9 -AKCneTJsC9n95cQ/UaDpXCsYumUXfIhGBBARAgAGBQJKdXO7AAoJEGnSph3iY/zU -Ry0AnR8bAJJjk97KJYD65lj7oUwmFlcqAJ4vj+qSrCNtYYPRF4wYQVSpPvF5IIhG -BBARAgAGBQJKeF01AAoJENXKmwTyxCO8+JwAn3QmoxMMKxHxCOZHVssHnUolNcaA -AKCHv2UlHHsqZVy1roBHtNhDTXO8KIhGBBARAgAGBQJKfTCPAAoJEISJsU2IB1Kb -FwAAnRj3o9vCZBVANQhyEp+xdNdKNRMXAJ4xvSvftq9bLIajYImKjaPdYn1D+YhG -BBARAgAGBQJKgpuEAAoJEOMhk7bEKb4vx7YAnjj6Q5bnucXYjT2rEdJLngPwuZKk -AJ9CWP4C6yaL3fuEdd8eCjdXqB5cnohGBBARAgAGBQJKguoHAAoJEEEhx0MxcOvp -W7YAn3XHkAu82cyu51oLLunAOt68zvZ/AJ95PwUEbBAj0LFItE/h7BGNh/0atIhG -BBARAgAGBQJKpDJLAAoJEMCeHYmVkw7etIMAn3EKTgDY8aBMOiXjl/ePcsVr9A9D -AKCA5wre2Br/eqnQkP+GcxfjmC4uIohGBBARAgAGBQJKpDJ4AAoJEAQEa4VKLhYr -2OEAoIQkksApzN34d03TUpj7dIFDWxaKAJ0db3Wfh0k4waPM6FbUonNY8XWZ/4hG -BBARAgAGBQJLVphwAAoJEHcBu4WJevOjbLsAoIo28e9KqEDb05UR9o7S3CWvgqWW -AJ4/IpKWhRFNpypM111/ZwWtTczfTohGBBARAgAGBQJLV7WwAAoJEP0f2SNT9F0y -ZKcAn1DCpWLnKwX30OMlB0OasrukTzNGAJkBywrpDUMWe6acNKd1GWmVAOYanohG -BBARAgAGBQJLZgGeAAoJEAK8QrdD4l0eItMAoKCeHHS0noHATADJ+8q/hg2dLwaa -AKCqjVA/4Z6GYaOKQ6gsNedX6z24QYhGBBARAgAGBQJMTez/AAoJEGUd81I9I/Jd -LRwAoMEiJxcm4B0//I1fpSFB81na4yngAKCnyKr+XvXDT6slSuXOKKeWbn41T4hG -BBARAgAGBQJMTflGAAoJEL9gcItIQmx+cHIAn1/hhSDRZoPe5LbyY/s5dFBNVpv/ -AJ4+5q4Lq+RLjlKVH1H7AhVzCjsz34hGBBARAgAGBQJNHL6XAAoJEKcaa39rhS83 -5WIAn1UiUZujW5XHlhfE99cUGqqtEIlGAJ4u8ssuo23KyjztJvVoaP1bOPekwYhG -BBARAgAGBQJNO7ocAAoJEAvgqvm1LMtSFLgAnR0eVAvMWc5IQ0TzFN1W+piN311n -AKCubIJ/gXZ3mn97wG+ECbgkzMl/mIhGBBARAgAGBQJN1mLPAAoJEKUG5tTdTVCI -5PUAoPEIsXc03k0daRo/wd6xhzdmZZd0AKCRgFBJ3ZSiZ07vSa1sNlU9xx58H4hG -BBARCAAGBQJKcwnoAAoJEPYo65NHQyBsjvgAn1lHu2siqYNN7b8j3zywAT6R622R -AKCOlBCsb9zsMgFQKa3r0iH06umizYhGBBARCAAGBQJKdEK+AAoJELz2xg9ugWnS -kSIAoKEteujWkatmGq/trriOD+idVXWDAJ95izPkpO3P8chMfOkER3PfhehmQIhG -BBARCAAGBQJKdHc/AAoJEMN2qNrxvNtzRHsAni2CKXNn0+ZvfkpVwy4IIIYr4Upn -AJ0Vw7t8hZ+c8ALEl3UfAcKl23Pn/ohGBBARCAAGBQJKdHfZAAoJENlG1WoI44u8 -Iq4AoOQv0pe6QAzBpMXOLMKOogjScklEAJ9Wi3odFGWEYZFPM9dUlz6KJXPCz4hG -BBARCAAGBQJKdJzbAAoJEIa8q/8cAMeQADEAoNcoI5cE0rUxD/rEG5fzLLHdttpa -AJ0ZYUbW+C2w2V2gKJTb9FohmaJhMohGBBARCAAGBQJKeZC7AAoJENTl7azAFD0t -aaAAn0Ewm+FSTbFTkHjlAqIegUrjkwXpAJ9NrKP11nKc2K7BuGVKGE1BVLqSAYhG -BBARCAAGBQJKfDN4AAoJEDsymJ0A88/kMUkAniNS64SHuNCUnbD/xwwdscFE//Fm -AJ9Wizd0tjyP1YiFs0LSaBrmZy0gqohGBBARCAAGBQJLXUlzAAoJEAYytoaKBJsh -hPUAmQFlGElQrBz8pOJJfZnnbRKS7n/2AJ4nWmB7QaQR9FAt2sk2teWUlgvTbYhG -BBARCAAGBQJNO9PNAAoJEDlgZk+V6iPdql0AoLr7nxdJ2WAs23cSty+tPCRIutjt -AJsHrXyNL5Qum2FJjK+Qu0igsTxlOYhGBBARCgAGBQJKggz4AAoJEFGUgwvsGbN4 -WqwAoILb/TeGowLIp0R6ksFywRYH1b0lAJ4sGdLlMdAb/vYLY61vOCuJhDvO5YhG -BBARCgAGBQJKiWzKAAoJENw1Uug251YEpVYAn1c2C+coZyqA8TJ0hr1pV73juHdL -AJ95q2AXTjVLUAKufQkSRqYHdAF3bIhGBBARCgAGBQJKtPRSAAoJEEEhx0MxcOvp -xLQAoJgae3m5ocVH3/fAVFEzH/RNRC/uAJ9yQHXLajiphe9mi+ryl/YeYTBgRIhG -BBIRCAAGBQJKd4bQAAoJENraec14ij9MiosAn1MQxStBggZy1MV/GqeAY7kK9CTx -AJ9ULcjolNZGc/3fxGvgKxpa0Y1YtYhGBBMRAgAGBQJKbJm8AAoJEBLbee7Edjul -JYIAoJu/p88qnhOH827f42l9mlka333FAJ4gCjhj9RvsCaPYXWwFxkXWYs/jEohG -BBMRAgAGBQJKcWcMAAoJEG8Xpa/B8k6k6I8An1kEhw1lvx9/cw6TS+cW52WCTaK/ -AJ4k+bH9XoAyC5rZ9ql7K8NvaceFq4hGBBMRCAAGBQJKdMy/AAoJEHw7eXCIx8H3 -8EgAnRIdk6Erdhf7ZeY7qgWtq4T+3YfWAJ9Ypvf1y5TRwV7wD1275MseKeLzNYhW -BBARCwAGBQJMusziAAoJEPKthaweQrNnB5IA4PjqbrkDMYjH6W6AMHbC9LyYLwqs -inj8V6P6JXEA32ltPJblxONhAbZ/wAxsbIWrFVSqLc3onnZv1g2I3AQQAQIABgUC -S1aYbwAKCRDCo11KJDoTKdPOBf90Xi2Hsb6/0Iu5taNiO04pvT6kaeO/ovemMWDI -g3iTRVTPhaUzAbIBtM6sz27pQ0fLgK809D/RulWaQ/CR/OFXVn6MDrO+r5tMnje3 -RRUvMeHIhT40C/Q2kV1NzK8l50E0rAPPIfnfS+mifFenIG/gm+xiw0Q71WXhBQy9 -SXzmN3AoN85Om003nIECEvC1mYl1XLgbM/1zRK1+GjMBVMjCsxGsf9E6NP7msFqs -hacTonBKH/bOyIrOihZHbbOujo2JARwEEAECAAYFAkp3MsQACgkQloDr5KmRk+LW -DwgAiXb1pjWwaXyavUPIMNzNkaFFdw3mgQAak5WxqD+hu5DvgZrsRnK2ZvYlaza8 -TnXqsCCRjBVL5Fdf8DFo6iVRY9kBWdHVJKVJUh8Cm2nebBcm5EFbvfzIvuSQDGUO -qWNZ8gpztpOJnqcAiZA3HADXu7Bx4m+QRRXEAld4OOVlF6J0qsUTZlAeJ54iEywh -x0bDjbuMj9ExNO4XjO3II52EOGUzDyHBOXK/Fr5AE/3svY/Rj7o0kRbdZBVPpScS -I/iTWiqxbnyv0bKw1lAaIc5ua0CtwdS/HRCLAZ8HRgKjdtXNUFV+T5tX611FLo09 -KbLd86FA/s9fuy2w7HfVGQFotIkBHAQQAQIABgUCSncy0QAKCRAx/Ofn3QeUYTdk -CACeCV0BIW6mX9vKiqnP/KQESlizcddD8oWFj/wuVkTLszxrI+CuIY6jatOOW8sz -ClcB7QwVULOJC0IOJQtEaMsIiVx7CnNAOwwlu0nZC4hR5irmGdxFhC0RiYVjs35y -6Xd8ZtZIXTEKxi+nGEum2IDoArX6EGn89n7MKxVHhS/lZmuwIF/W0jnSaaIvNUqJ -CUE0rTd8rqUVLZBGG5qTImoDW7TTP7dvf58l1uL6HXb1TI+PBZEGold5SQ7N+zRg -3rEvNBD/f6FKDXA02vUNSaWsZwbrm8wwDbTQwDZAhLtqJLEsCkDDF8gJmVLcV+oX -TNTgvHOHq/SsSRyvBgeFWbp4iQEcBBABCAAGBQJKdDBpAAoJEPPkEi8djCYaUq8H -/3iEBW+KBeN13RxRb2sUdbv9npxi023jpddS24AmL6hH6+vbGIyyFgDojp8pbzus -cEBT0M8vxE8RBKn4llMpA83bFuDmATalixaWXALu1bc0HhFxDK0PVO1sWDe2lHba -R8K6cPtyrS2a6y6r5IBRe8vuOCgtiXe1BFtihKFDZAxxCCUH98GGvv+w/ZHUiAMC -Mpg7donjVCni/pTZOhtASleq1UGgy9oAvfCD9t7LOmekUacZMb58Ygh0jVKLy0gO -x+aoFGLfpu9IknJWshLy0tvq9dhwRSWyHzZY2s6n9/zOnQ+DrbfrOthBqgmAVKqf -gQrGNB36fgrjxW35iXdDaayJARwEEAEIAAYFAkp5X9cACgkQloDr5KmRk+JRDQf9 -H9izTy8V047rFwjNPtNlm1FSfwSsbJYCvy/rTo4OBH8JvC37Vkcfk/lokbZOErMW -4Lnv35JXv8/X1NIj9bJXxZDkkBTiCj1mlb1+lAEwmQYJyG6ZgRcvYbeELKjcFyGW -eFO0sSoikV3jGmNHARzmfdXkTJiTM7Kb6D6XBUV+XX50YO5sj8elVuF8ie2au/Ik -7oAGl282x7oaGb68hOoY5GpY5GxRYLBWoG8ABGWlip+Rjdi385CvMsVNn9LMkhgK -0OsrESt3KmYDptYDO6hCoIPzGsey0C/DweacMfWBWj5g8CO3obaf2aJUb6/77lGi -CUTDugyRpllyZ5Ft+HSHbIkBHAQQAQgABgUCSnlf3AAKCRAx/Ofn3QeUYRhIB/9t -uXiOhz/d8uHQqM+dWAHJOO+zhb4Gr2MMlwQxu6ApepvrLEk5LuaNNzfOhczm29c+ -DoEPQd560yZ3xH4nziUSPxpNzDrNPC3GIbHvMxLZHXq1Xm6okw5IBuZRADVOX36/ -xAi8FO4CEbtckTbW8qz5bavxFT+EY0oJX+hsipqrIxtP7rK1tYUBl3AjJmKA9rJM -TzvfVWlRky9O0PbMFBDI9TOIg7Ml8WKet4Pl/XiC9FbLvU008i77ddt2nUOi/6jJ -dl/AtFO6CXwnLNNRGEYxU4N0PX2hg+X5oQ+fI51Y3zq7edioweWZq//pS8/onDCF -qDqKUBzQ5CS4UkREh/L0iQEcBBABCAAGBQJNO7oeAAoJEPfuYW7gwgz/khwH+wWM -tHw4yo8uTuPGBAufmxbGCMdRhoCXtdxDVBIreTbzw1N0XGFCpfobw9Tu3abFildc -evouq9ZBli1tozWGrVNqGiCTnn9/l6vOgpp0SyWY+0t9FehPJTWXlLH4jKX2S5OG -LNRVIDj/IcIoh+st6Ji6YzfZjs0LNgS223LwVNfIHIc84rl+7T6CH/+TGudY8hnw -NNBaFwNywfxdRplassaXn//zukwbak9HtoLMnYxxYbay29wNGBpzfLdhRXgVsPlb -9J1bGaN8Sbmx6aFM+CHc3F1e8tqRXUyGLV+Slm0ZOWYuqpklLwf9XIYYQf4fq/yf -ihYaN5C66MsKAgpk5SWJAhwEEAECAAYFAkpuGHYACgkQotPnz1ITRrTlzA/9F9c6 -fztJXmlpQ7T7iH87fk06Q4A4cg6vUpOpZ5MIDqGyZ5VExPGbgkvinZjIsB92ZS8z -xDFAaWJVeWpdK3jJjio+XLcZAjUGr6W/fPGWhD175m570nEqkxvdRCkodEgeIXKB -9KpXL3XIH6l0hIt9l8pe99gURXjCRpStJbJEmgSduzWZQwCZSIkSap3NtgYq9k4W -Kx+nVAaMMpOnGsnjLa1nHRIBJRYFt5NCnLLZFzprciOulLCOb/87WfqJ1GzwsXzZ -PkUDIDeJdaqYNHf9yyiLbJPk/S7kI+UXbs3cIs5UntneQzVk0TgtgsC8U0yuLuHd -jswg+tgA4Fde8uTKqw/hcJVLOHiWeND4s2lV8hEioY5zjBAqScdRqelWFBRwRMAt -Lby8dJlJ8G1NVjTXH7BVWKghQoazkGoGhe7wx8yZuDezTCoOwa4ZOukGN7ZiNIKQ -4Ja/ucZddtG2oU+8KTMb6S9yVGbWxHJkLfhJYzzZB/jXES6sR1XeSZLEAKxviCf4 -n9uronToQLJWaM48hoDRmlscLXaEqIZu3MlZMesxK7a6iZVowugasH+L+Gq0wo5q -sy/Req5XBpx6u42U0BfgTdzwUp3uiNsgLAxCeAhFYaAGkM3C5ZW69Dkj3hVrZSvj -uuX9DPQ6Vz80iQAQIL2m12RDrkcbwmxqGN8J6eSJAhwEEAECAAYFAkpwl/AACgkQ -LHwxRsGgASEQaQ/+ID2qTLWwTVObSV0ivXHeNrKYWceNinwabSfP1m7UpWzpvNuc -+rAtSC7mpU/1QRq82V22xaAhE9yHEfybFeEq0uNYYz91gJUjY2j7WubWOK+mWu1w -E9rwPDuPIhydRqTQkZSIJK51NpdqL1lYhB7EMAHyjEnC2y6HPGdyUJpmHAL8PZxV -Or6eH8nmq7pP4FST0FH+lMs9eoUtWrhky4guShQUotyhiogBQMLKcMy57vwZTtKv -O+l5WlBtcQyCJmb3QllYhULuWpb8DCjNz12cfaRcsxq5FAyjR6sMilYhOELWiOJ6 -3Yo8+q/biuYIfep7TTkGcn5dgL0BTui4lFh0byz71EDstxbfU9+dQUdeQeo7srso -blxbobi1T2oTO0PiJg0IYEyLYywCUi7Rvmf49zlEH2UatPUIsvqCAUlOh7x1cKxy -AQFMr55NThpYX7ruQ/vRxXDjOAJnWUYy+ZY9pMckm5b1joGIMvcSab19nSvLODrz -ZW0ruQ0XSNkJVdTY1my4grgLNWtNaUGnDMgFR1iA3P0xD/aT6NvPx/XWty7Epw+E -kpu5gVmfqU6fSg9xogzlg3pvOfxhDH4BUI65/KtUGkTERMQZLqX++5gdcZFFOBlL -L20cf1v06O9BD/iYGiZ042C3kt76kuMBzkhBXjKq22REQAf+vDEIJo4noX+JAhwE -EAECAAYFAkpwmycACgkQ7YYEvAb1qR/a+w//X9V4Btjgz6Uv0XBSEiQOJpCLu9sv -Q82Krkr+HqvEyuVacWeNwAYmrZFgRbnubUte374A2iJ8+dr+rT6MWWF0Vka2q3n3 -qxKfGB/9DP2ll9rFFKZgXjyB5TiMeFvJS1MlVYCtK2M1Z3+Nbkk2J3g91rcjO3WQ -5OWxLxD2Bm1kv+uuiLRrh5CyM5+Td06Ya+OXhEjxT0tDDR6cXfp15RRvZJtuuHKs -6Iyztyujw5hSxhZloukSTUmEaQ+XjnHc1YxYleUx065F4wP+dAa870CBho3HIvog -O/VgWxaHshwgfApcz8V4oMdv1W4A5liRw/BNEOE88ekYtuw/eYWX5QXxCsAJpqWd -qjnUaDMM9UgwoJfXF0uEyzoAlrceLFFK8bjv3AE11fXVvHgI/PRoG0w5ihtRIRfW -QmOvEQRzzjUSDOAJA+6W4AbxrauIpSISXOGMBpvL9NnxX95zXtSSHb7ZfL48DNqo -oyRERPENkb2CLZIpBRX8CtPZeviU4dic4u+Ffb/QMD4+dEUr29kA3bc6wcTQ5XDr -rKDPItCdwcz97fwFdmUDFGQsQuxEZALjHX3KPFzwM0vF7IVlKn8n7+XGwRnwYOqK -hxNJWeugCkhQODq6A5bx0wld7U8SXybYLnd2189lVWqQpGz0tsbCuFp8fwwV0D21 -MZ7cLT5FuP/L8IKJAhwEEAECAAYFAkpx5SEACgkQzHmGb5lAkIS9tw/9F5HLaYGv -BfJxs/CmrD66cdLftVyqYvZ1XkydQ/u+ZKD2rPVUhnKLsXn+Gkq33Hb4Dh/p+4vL -ug22s8js6+MDZ5x6o36hm4ecvnD+GJnQQ/Yq5Xf3YT2y3FF5WG/L6znU1jOHhIYk -pfEg4uo9EnK6jO+oub1w1nfk29xI+DrzBnTADS2sDA89AZ6qDnj6fWTEhnYzpzoj -+bAPCPurnQf2y4WhHtvSknXpA/TFrSHjjfsUfDvBD6esRo52achM4XqTZHWIWhl3 -9PVBkkK73ZILz1vsUQP/nSxS5qb9Oc8NGmJOShv3wIrZ4bnd+zmz3xb47tGPF19x -jC1sU0D6bYHS8psbpo6q2llr1RV97E0z7y/N+sRxGoyfZHzrxIU2TWWJ04swv0bf -J4egDZuyjLcp3iMuj/8WJ0pVwQlIPS2Xbsfx+5tMbK2LE+teQhUSLBFr4E8iXwxK -7btNLko0jlTb7+9wAYTA/GR0AXUznDHhE/DUC9up7BE2gdf+HJquYROvnIw2z3iB -db94xxOlaLKbA9/hpth/XPMz4Jc6V84VQ+3S06noifgLmX7+340rHrpfOcscfG4M -toEZtYXUut5SLXCyNoC5j2TWU/WD+898jnveDD4OD1yhwz9cLQ3d/vHTz1R6V7Rz -t4WWwweHqEH6DDuj8rNJJ/D28hauT9jwY9qJAhwEEAECAAYFAkpythoACgkQhy9w -LE1uJagmNQ/+OpgSt/LecyJppOrH01zmaIEesnACiS3QQjekUFv0pgb83ZZk2j9I -yjF8+flb6AJO2S6Ut+n4xjSnzHz89xVU8hRBFE5kHZUYEGNVtR+lmzXC5V721JoR -AnzdwkdwZhPo8NWhOjkyD+Mu90bP4llqqYOviMNirxeT8g4s5pKeHEQxPSVUFaVf -gexzrpJZ0Ju9zv0UHNY3YJizH7mpeYNUdzMCrJ6O4tyXhl6gGv8zmyU4Z1/ACSCx -WKpcDMY1lEKmsBDWcwKm1s6tU9lW4Sqff2pPhC8nMvY0KJ4jpRGpDAFkKuaYjUit -36bwE0dYhwZTa6XjEKZ94lqEGBtYwp4XFOQrnbrcjUzjeYZW6O73Ye0Pc3TM0/WL -AqkI6xOXZcvdB7rYi5YAmKSRGbjwezWnvbjNUjompQ4cerJ9mxAHUuecdUeEno9f -fm/P3DGZkn9mpnL3+7dO7jUBZxbAM8hMa73wQMoqEgg7wO5a+r/1oKbuM49iFMN1 -kq4/IW9jD/pv7sFHzMQZ7xuOyS7l4yjEF11alb5DVnlCWbvDvOx7C70l0XMcr2nC -kRTqKybv87T6CryqotvY89fd7VENgdU4ElzOrtvJE4OpOX1278ihquoJZ1tEzeGk -1yHX8lMQkUw3+NJZXALNISAo0UJCiRAHKcdrOdI2rtzWkr0y8OVtQbCJAhwEEAEC -AAYFAkp1VVAACgkQHSHIPcRS4PzeVg/8DGYeDft3EiCwaRF2qV3kEYW6WYH3xL74 -y3/bc5D4jyc8esKKRm1grT9kXkXoQcFHmj6tdWAjtu4N7hE5oqBcowhkJXotSgtz -h3+Z4sIXaBTNP3TF/p+0jPOKs0qy4l4lkcD7sDR92fYdY5Oy4cdjWEO+RCu5sqfj -9aVsAZI2nK4u4dMoez5z0wAWgE44Eep6iDF8H5NsUWh9nQw3aDDnHi6eQxpGO6Dw -UMBkHrgZAF69rj46IOPd1mb+xDzBRn7wTUMCOWK+W+mWOAOZ2BCh2bmMtKIII+M1 -nW5g94C26yEDGoB8SYtyZCgopiRjBPUfCcxw5wqsCd3V5v5mrWO00lZZeLsLSKV2 -VTjp3lBSmMxDwlPu3Rsrr8+lWhtQiSdDf/cnihgYQUvlykp7N1s9P66zNlOer9+p -lqqAwJEi/L56noa/fPanK7froGYHyNBfMpS2qYuMS+wj5Pe4H1WgqvZ6EwIFSJw5 -Hcq7dQE4MQiwU1y67mnwsrOAoIPyyFAM339MhA8xa2wiRW7lWpfL03yn1RJXT4FC -qv/jEhH7EROuXF3I4/3WzsDcVB0quOLu54pwYj8cpPszq0ZwK2JF0iCLwlB5gHS7 -hybDdxLIzCBgmOyAxs22L2NHMykY9VymiUnSjm9PRnpcgV0pwR4qbUvm3vBKXDxY -8eaaRY86ksOJAhwEEAECAAYFAkp1d3wACgkQ9m4+QZ+E9N4sUA//QrmB8J1SXSu2 -6lJ1IbCUGMUDDspJg4pG7GUr9eZNTYQTz9F5T75dJLHU2e/zK9cbWg2B1/os8hfK -RZFtlM+rogVlVQhkCjCmBdQCxHETTQevXB4DKdSP1aWtnQIy1Zdg3qHPTDnoFvwy -4EjIWTStQ2v9IwqhYAxKW/mCaU8uMZDS1SLbM25tVGCM/SEapP/Te9nuyOhiW2Wf -o98T12a0fdJBDtNDxrBJK0Z+sn8oiLsKVA1s+R4W9jbzTcoRFHgwch7za5JMvHxw -ev1dD2j5MzQgNuhRzOcWCYNDHYM5F5bQQ4LAqpos3XuMpw/gDP56woyl07KCuW83 -Ss9LhG4A/Oel9iHt9zEUyE864NtjaiMYCj3UFGs/ilZrftWtBaV5e/DXccFwW0mD -aXgnhMgf8xJTeQJP+zyjtJWM3BqY6Kk+Hs+QbW6dawWKKlq6BI/bX+V8LIkUdFUQ -xpIj3Yq+yj0Cda20tzCQTZZZyCXfTjhotK8jvlCa23O/BE43SsoXehkoTDKsAhl4 -aMY0g4G6hYaIAndVx4bfQpuVxyGEpBsF8FKCWici/Db0gw81vrwQn7Ht3WW8MQ+t -kuLk4pJrVLbFBVC/PuTPA85tjEoT/U4wLvqbBX/Kju92DPkBH/a8SQa8Xy/sDKnd -NzVQTW1tC7T2efPYMkvpqGfiIHIM22iJAhwEEAECAAYFAkp3HroACgkQDHBVe1oG -UT6WpA//diZesjWlt+jzceB1rWxluWyRp8Kh8YXkMmq/BgM/6+GoCDurML6v7JLo -mnwbDeA9o1I5s96K49Kh94H3gSYkz9B6JTkTjY9eL4wQJt8eOrPzN3irq7oTvaKQ -1no9eO/hXYN8+g3BrRfzZ7d5v2iv7TXi0qUENZnuSTA+V7qCZFT/Rn1nhTwLponi -oTCf9hvcGVco2Jrrg5BQBq0wqfmMbWF3vd4GSSjMqrdjY5EZtJjeA6rFwbKrx4i3 -0XdkG+0B/dW0nfjbpQzThBVTj7YkhobvyEQBNRvweML9qXtj/g3vivYJBa58aOZM -oejtrWrqtuAN6w2m04nN5AYfcXxPzPzENkVJVSOnpd5z+8Qvd9XJ/MwzMmnMrcSx -qC8aB9wMR75JLAOdEzhoMGRGhvl+iqct8xtqT0P7lok2cWbYuA6VTFHyorNqKryu -XQdL5B7Y+AuSX7+iAOc58n5ooyXF1l5SVcp3PYnzZHHS/pXUwo0CBKCgP2rGXzpY -JEwQzVPXVtrwcIdUFWEl1GBa5xPbv3SGHbh4Qvetz/Y7FU+TTaweR0A4HdS9ryby -6kG4M0BgbDvNca7ZEYbruaJnKj9xA2WORyo2DoyrU+3shmWUcxIk7uy6u2dnVVhL -ev2c0LuZ47sPOwHdTPBMzk5aLh+yQk59+FxHX08kokDqoglBYkGJAhwEEAECAAYF -Akp302gACgkQhy9wLE1uJahqexAAmLYVAhb1HaXnQdwf9SvJI5Z8dzdS11AcaB3B -p3dWe/1eCdMhhwt+yQm4L/InGBZK5WpT6xFK0PL1vSb3Bx+9RfxNx8BApKDTyR8L -S+Z1Uz0tfFMDmc9ajXuPj6rynSg6yCr2fJtYNUb1C2ZFkEFRzbFv4q1iug8aIjoL -xE/9tC/U5TEsX1OAK/D/2/yQNgH/2iC+wUIcxBsZ4GQ6bOPsrnT3CgbevvYhoK4+ -viXRbdqU205grbYkk7Wei8RYYqfQAo7dmdZEdkju6XwECmAs0cAA3EPEs44JoV7g -iPYQ1lF33zqOqtYFNYaR7Yt3Dky1S+VI6uwb+QjH/giYzx6XXdGeVAR9Foku8Oe7 -pE9Zh4q8zAgQoBF421IqCX69NT5qrBIs7b3MCN+QrwlmLz9Ax614baAKLZfAhYvi -dejnNJrQ1x2Bw8EV5VqM5fX1zyn4xxOFRoJEx0xb0s8jUKwje+zI/c0H/zpqHPIF -Yzm57F5IVp8IGqf8Qf6+hqR6ZNT/5eEWl5LHiAZHLt0eNqlWopm9if/lis2DoXDC -6o/vwI2kl0/N2yCJO3OcM4JK/ifjwJc9kw8ES3LdbFhfI/gZBsuRNjXCF5Yoof4T -WXQw3MkycTvGCR97VQ5iv93lqqVMONQo7atqEC4cJuRdJ68IbW4j23IdUjBTKCx2 -d4cFVUCJAhwEEAECAAYFAkqC6ikACgkQMiR/u0CtH6bgIBAAg4XWOuAHvNKfG53e -90XpDFHyuRFDmlyWmhMv6PJw0MDiK1WkmiKy0HeZbKzzAZIWB7FFd5nUxLHUvLRp -kGeJ8flhM4gQL5KXCaR0A1VO96fAmowzvW4AABcC29y9nE47T+BBEc34lF6RNwYP -8BbHLxMrTVRukK5L4aSa5m52Zl7OAwyyvwd1qI3N07UWOGlml0ATpuAV5434KWll -onHxi+PTSemmZ+WDOLpq4+Zr7HzL7IczkLGmyDx5jbvf+L7j5WFyaGlO5e97YOL4 -YBx+dPsz9dgTdENFeuHHKY4Gpq4M9lj22kHGmDZ6C+6r15igMv7PAsw36XUGO2Zm -bs1FsIWj0OTXlAPDlxCzMPEWKMkKawgU4MIAxmk6v8Fe6dso/0xpZNU8uiN2ZDJO -8qBVEgvfOE39ysb6VEFWByREt5RxNUb5puVV2rEmHqT6DUKpI32iwWFzQvIpveyC -0OubDhQdkiPRLzwqGzL9L0lENrsrMNNhCMUxJpi5IG5ixNL3r2XZBaOr66ZYIdVs -5NRutMvd7OV7LYwjh4wPtnevc7/FNElXgK4503XMGAF+WD0Og5zgKUM3hO9qGdDA -aCBN7t+kZmo+wzJTxNL4r9tV6w+hMi4DIkrbwAWiZOTxEKtgsbFdoPBIJCwHqM34 -Ant7166je2JDHfB0wsZrJyID4tmJAhwEEAECAAYFAkqDrYwACgkQV5MNqwuGsGed -WA/9G6JdbS3b5qAgDoRTvlopTozh+DUwr90uvvYbDHoEMjRbl614Zlntp59CtulJ -r79E3f3xLFJoA9hbSOSa5TfxQCohds+jP0Olo6k8hmvLldh9/uxCbmfyJOmPX7Ls -8uvUIdOCTtZi6qBzth6xDAOxxcRVA7JtrCAeIdG+FtNtjjxqCeUlC9xaAe+3+Rvu -jNL7uy7I/Zk722tREdhGPbWVrZTr1TzDUf/aTLtV10qgm3tlz1vITB7Y3fn4SSd9 -2KbKFKZM/InagPHdClpDc9aq3QM7dZPCphNAge2M+yCBg+MUjZFBRk0PRGXLI0QR -yHz6NahmYijU+g/DrcZNmCBsn+o+F1qyPbBfPR62hU5CebvKY2pzR/oZFYaJR34p -12hlK2YsjWjz5ZpO1nPFe62sgCCxZBd8OK/m2P50204T7XaGK9Y/E33vra0RgHU3 -rTtAF4SBiF7DcQGhg2W7haLnVBHeprNVelEm6ygc2bXSRG20ebq+kalArxGrDNI9 -FL2fZ57LA5GVGTB0FO4WsvT1R70+7gC8Bb51Wnlj743Ot/vfQiLE1bjsX1YcvQze -Nvl/Vzn9ZumhQK38fX0PSF3qsYA2j4vbJNZfoazV6TQ98m2JEbUnG4mde2Bsm1bB -RGTuPNleMQzef0vqSU1eYXGhqaczq5WzTIZM2Qgyi/lCOwSJAhwEEAECAAYFAkqk -MoEACgkQL/nNWWEmFrV93RAAl2ycx0aBMnQKvCSol5sWmU4WbaxRgtxPF5x/bbtB -dtXbNogV5qOfCbWPtTJ+k8Fqndutk3VTjWCdW7roIurfVDivBvRQf/prKZP5NyKR -TBqrrae5sKlKPk7J+xdGQEiPG4EHJEA9O1nQwBvFxMQZk4h4K64Ea5pWY6eoz0c+ -27lAdGEEq09i51ktDqc90yzQLAAls2IMhAxIFBYgjm4UA+EM2oPs5I1Is0Hq+eC8 -1I6ebcf9QbK1Ckz3d+4W0QAX+z7yAafcNfaNKYy2iAMN2jiTJQF9H2Ev/oCgDqJe -bMhD0wf9T2jsfMvN5y60stngMwj1qMobJvH/jPAIzjfExu162HHWamvaVpGXWcwB -wGpGk572L5Zrn5nQza/fByPJU/fylzSCpNe/jmWaeAszEk2RF5JlJ0ncLtHuL+/r -m1FY/brF9wLMreYfX1Oxokz3Y8kmggkr9GiAO9fqT5NpjAT6h6Qxy+TOvY4g0d3+ -n3fmgct4wFhdc08ekIOSDe+kG2Jn3zrjBnQF0xHLJR0e0QzD12/vxQvnTMgGAmzU -TxqYJVhJfKA87zbFqYMNhO9o+lYD6lsE3TpHzTvnEohknKMXtow2nl3WkT+08aQ7 -1qcGjHxN+cjSdiMtFh/D3eUrbNuQX4R6ZyyvRV+5aEQ3tI/5QNAMMbaiT9+Agj8X -FLqJAhwEEAECAAYFAkrl0WgACgkQGBR7BzutKwfmmQ/+LyN/SRxfo1Ex09T6L18K -rGFsh5C7S0EmWKaWARb/UalKcD9/uCtcNGJlZBNf4JL1m9TJicp2d8PbLGsOfYCL -+GkYIPbI5Wrw1sLb56WM/k4H3k3xU1xC/1eiDjMF1rlt1jy4GSts0z6lf0r0Ymrz -vwf4bVAT/H3zWH33TxEaqTdbo+Nw8ISG6gNZiD0sXseOqHgt/BJPirm8fdpWsWsf -i5Hp/OiC+UueRK0t2eXkdWmdqlTLGkyGDuo1GajplMV+dKrwVF1pSFMm0OC8dPOi -7VrFn9Qtzk9XdguN5Fh/HPcKjv+OeX4myleEoVYEWUP/c5qiLRBse1sFH6PJ7RY8 -2FcQfPddSiHwWATVfVDP5h9uDoREKsNbO4oyHMj+EpI4pwTZuE/ctxfFG+wDpPqh -5gN7HaDtT9aQ19NI0OwmXQHgvoc2SfubMqJWBBxT/ztOfTyXzXT9ONRAJvh0v83T -JN9GFatuSxv7wxpqDHli/15esCQYjnJmToUS/B3Qemzbnqev26fx1pOPA8mXjaNL -6XpG39DeZHFO9BIKZ7IVFeLtd60HNYd9U6GjGrxmN13R8/pSJrH1NiZhCsJMYXBL -YRO89DejsD9bur6HaNfUW4D54IirK7aAyOKFJmdqGRV4x0feYKaRUMyRzTUDdXhO -yjznUImaeZa9w9rPyw25gm+JAhwEEAECAAYFAktRXQEACgkQIZ9rYLK7/PxRIxAA -iqimbeyOHJqwNScV+WkRlfZu/ghKFmFxWuhesZV5k9Xc8EWic/BF2+PVUdY3oGxb -S75o/TNCctWlQ3F+qW+xeJOfuieeVKDaA6/l5S70QcBjjS8d65Ps1wisxZbEcCnh -0oGcqRcdgDGBgfbfeOIoCKdiwiS1jMSexpb6AVmOadL0dl0RWlmhrggYzDo7jLl4 -2l9+7rqzOSpXaxANdurjVH0/PlgbBoJkAry4qoBs05GJYp3mtR66HUKTAdTfAipb -+O17zqTas1pNlaMVpfY5Okr1emzJZvlxG08e8VBJq7XO+ix5xrfdnqIkl0gXMArs -UI0dcf5DFhpXfKZr+gFeRVJFqCU26gbPINTaG5g4F6yDRkxEVS2l3osBsgnbsz/S -uvwtNgehOSnsIBdNHRqmpw10trjHAmtaxXgsQlc3kN/cMu1dQ3eifI7pO7oBVhcD -UXtUB1NurjdaV1NPK6QaZyzefE/vubb47WNBiqd3W6NYmtIo72ivheqnl22yxYtf -umbKLNFxkJYqSckOvcNuLuWHYHsELTaI8UEN5ae0o5ogVVhXYgBLqU+8Mwwzto++ -cuAgK3VTN+FGjt4a7zeFKDWORe49lLU08L12JJIcIre/dPQQcwOXhFCXhDQucD5Z -1tDvzSGSqknho9B5WYhmfy5EEWMoT5fL4uE6Ugcd00eJAhwEEAECAAYFAktX624A -CgkQ8jcjNv7Dl0XExQ//Ri1jhNbBM8Je3DmiiCDea58Y/YDhAPNKA30awTvNXsyp -pWlR30p4aAW3Wwyttvm9gpQfXQENT+W2b7KrHCipenYKHTMrE8jZF1eT9L02bI5Y -IhV8akBpLD+U2EgXuf8yRp16otlyFTycfh4SeVsIDjOWRHJOHveiLjQ99RPcMC25 -UPo3Ed92JAFwLO5SmGplnlK+AvU5Fd6m3zcRSWrpoGUkPwbu2wcWtCwBlnfsAHoG -foyEbK9yTrhBtwWiLjgUv7ZZ8D0ZCZilNe46aJLfRn8QkybYBD9s7TVBz6vAg/M9 -+4jyTsPP4wy8VEPfqpvp0vh+1A552J+98S4qMfhwqIrQRmQoPRDYMuPrUT15eYhR -XFl807J1ypggamciIks2Pq1Wxn+vf3goauQ1tHW2uI7ojWJ8uuc621nvx72sMOYe -7XSrAGyyhEyTM0VTh14IJsvWOSHgAnsVRENkCSvKonRQu+T/LWclucjFy/xgYVAR -thG0fii9g/ybCL2IOg3jCOmdD4eXt038LiVqDqshyMJTTdZK9yaB2j1UYVpH6jNF -EUV+uhSmJFRC2rtPyi3PIAdiWrmrEHbcrxIakTMHEgQCYcXy/xIEOy7nZLEfRsCe -mEdT+uzEnHEj1KOGPFtVtDCmGXLO4BONq1K4ByODbQGb/9v4pgTAAePxcU5lYUSJ -AhwEEAECAAYFAktl4T8ACgkQihEbXK5CaUSKqw//Zt+vRZkyUx77OjQa7YUHVt/2 -RYR+oKeGqLROEBnOV3z2djmTkch6hMxXPGHvBIEQB7Y7usd/qa+6iPiA9mN8doux -Sc8dUpXZEmkBL6TQCEhgzjGVZS5xEBiI/GZudvnwRFuclW7ZmNvfbvj34djFH5jX -JBWnKn98MvkJ9NrVFAuDw0l+azgNUlyOgQO0eKNMpI+ySErB+92rrOak0NIRY20g -bldQsAVciP1Nihc/3a20AKnKoWg0WCvSkBXVKApsLTJZfz81vpLWLwXGV0+tQPw5 -i3mWSYQkN23d3V95V2aJBJCvU1ZMGEWBPhf+M77IRzV+t2HCPV8Y90qRe9pmtNXK -2hV7RHdVACQy+gi9m6WvQEIfCCOen1XlIHa6hGVx5ahKsdSNxhiXk9LnyCb9/bS5 -scA7alWV/14HPySwieDBUm5iyctOllxwmCk8p9+7sEjyFPCdEeB1/N1kWYa+ckpy -WDGFj5mjbjzOIoMVAaAaquZyzqNKm4mhAKqd2zchqG5DSNPo7WTg/HarBzA28oT4 -5CSlB96RkihrfgFWXF8tWYzB2Oykbmxar96SzZeDwUxectq1NCSlmQwy0Aqg340F -EIjg1qCIHoP/7I5A3tkrF74GuVzXrs/Ga5E/icFyMMY9vt2jl7j35q6AV7f3WHON -vxEqREgR3ddlAt12bouJAhwEEAECAAYFAktuk3AACgkQMfsebhJZ8o+Yug//YKby -2nCvHJ17pMdgWB3MTA8Qmf7ap9SLhJtiUGXXfDD4kw/ouzC+DcH+KxPU74z+CP0r -EKMKNWvZp+mI06OLUSiYKZQooQATgZSLJ5nmvN/xn/vEytEWkA+w5TvB2jj+dGFZ -izyEfiq1hSax9UjwIUZCmXD/hgODlxOfWWlgKu97Ux29OpXbfLqqxUBrMDNX6Gbn -XNPsMdSonEHrceeHrugKl+O5WHJjEOjXDnrH28D/VcVFMPAhVTBiMI4+blAVz2RF -byUaVEmFv5qxJ6t7FARCWNggRejmomjdB5sNMrB38glY0GfNg8hEP8FnR13b32a+ -/dY7ZUMsMhiBObnVB9jXrB+B0h7+AvUVzknjPSbdCFBtd+K/pgkBn4gxh6xvDC0M -dzxYhOdUUnOPQ4C+9IxqvrKtdFO4tQkiP8rVaFic5NA+68xBC4sOMLYr29E/bCyQ -f4LDOFm0jg7qcs3lc2PPkDXJku0gEyoFCqjt+8eQvaA+Fj47iPUSkbmfkb6VVsAO -rJTa7Z2QdFcWo8dEyaiowpeqrDxGNwe+rF7egT0jKJ17eJQHNQKW59Z3lP+YbWAG -oftBT1pMpv6tOLzI+jmT/iSJGOGTB9aT6fTGQw/v/R1QJNA7YS7M143gB9S+p+NU -nYjEEvV1Fr+G1lIZXf3lH4rZFTjbyfmzIBHvzTmJAhwEEAECAAYFAkt7UA8ACgkQ -KK0yshjMuP7fPQ//ScOwni8vbCBV8vTcdOK7HZ14FXwfCjhZJOIRjVYDt4cWTKlR -la5oPgx5O5/Q3k2FydpcZ0G8C6xxDkM8oIDmyYq8UNq4/IzDbbNuL5mxFFRi/dCs -5Ut97d8+jp7VDfap+8st5U+Crpgj3AZ3TPvsB+ie9nXfBNzQUu7RYTRnT1OIKWEy -io1dGAynHTW+IJBmCG//tgEx3SBesNv4G81lJdTiT5r5/hPBhSXG6/AogKQyFhMm -dSWeqVeuuw9BYtPGcN6S9qYJ4231bOdHLxG8zZmyT+sv9IG1eG9qlEtiF4fkPZzf -hYY+LCa4HjRoEIxBlYUaYuHgnEurL4WfKKgxV73vXnG3Cn8HiPbqYYz/5165N95R -wKddfjBYo/LJ8SGunuemyMih83cAtAocpD/IZ6Be9pUkTlQLyByCvV9ngvfsZwLU -h6WMou86YR4QaGJtw0QE8QL5HycxoDcbLuujHypcfYiAZJRLg09NskHCHgXdJ00v -j25Wg4SUwm4SvEcIadf2S/Ys4WMb2BRvwZZpb/scZPzYr+AsEqbSbWSYjoun8EZy -vU/UUx9bnUszYWNb1Jl2a+xrXf7QlREHRWzRTtkZA3cqInE8sO2gZ5TOqxjtnurU -YZY6n7i7OqYQGcdUgFtmx/AAUuurOY/RSHoRQS7DChbfQh5FV2AhgQvssTSJAhwE -EAECAAYFAk3WYZ4ACgkQg3vv+Qzb+RwehQ//UNdqL2MlbhY2K4LeJu/btakV3RGF -LXdDSi70CwpIC8X02HSXPxAWheHz0G4iO3+OVTZBud9gYL4SqyVdhHbnubxqsxZC -IvOLBx+PLCRxiKdoA+NIaj7YHfU3uy8VQUVsrBN8c/nX7uca0c8qh77HGbo0crnF -D6iBgJRYHu4kWfbbI7THoeDBwq2HTZ7IgQVZEpgrsftJyiKvuJlO/iaWrbgDxISc -uw46xEmi9M/WMa2vTt7FHGuLv/L/NxLUMwsvdDFlmLv//edn7gCwjxe18fPisolH -sF0zJgrd0E12fqMJaZ8ApqgzdJ2++uigFhyTc28Rf5Q0cOhoza5NXM4HoCOlfQIx -Jv1vjulMTXXAhUe9LsaqbcFayFdXzmojou0TVcHioPs8oNPyrEzYU5N/FkLXG6jO -5JGIJ60X2oJPDGyq1kPKgJW1s7FaEXE2gQ/pqt8JPbmtYTDImzYs0x83gJ9YVmOs -ZvgAVVqM0kcKQbzG75xKSiAZ+NTvZajezYEJWvoknd84kmnGwOBQKrrARGKTVSMZ -iSt1jBfanIyC7UQsFIjse5Kq5vH5Hh4PRVCZE23dLQO/1dK4atemyHyo53mOFjFP -55unrH/++EWa1jEU0++ZPyvCNKMvsmN2m+bQexCGd5YqNXKaknlrD6dZLbgHiIU3 -ZrfrfmuBjiK8/UKJAhwEEAECAAYFAk3WZIIACgkQteOZn77uZ2Q/Hg//ZlzPOUKr -i1Lp9tPRDNYnOgDdqbO7O73PFARtiNBex1umb7+1WoHMn/j+NAXneDpqvA0N3NUR -RMTUmlbKc/ujT3KSUa9L0g2g/2NZrFLZxUyIIAvMum+4esUVsdn5Gjxd87I1CfiM -afYg85KkcaKz/J6rsbOZu3z/L4gG0McJV+4g8AquXSWvaRQ10T8dn673jVX3wbmd -1/RG0+A/CxFAF1vAqxzoSFhqOacttcONOUoTfew/PqigZvTeeAHXHqvZxhtytHOh -jqNTG+nAYaeVk01r2i4+96h5DKY5DM56i2ixvqMac3HCFusmvkJUCdMzP82eLjJl -556Y3UqJuDqi3uhVcH8YW4nPrqLB+SEzyegc2uRYhCDt0RZAOftOrEj62JU95xlV -1S/CYDxhaLWNvkNGHdFBWGXvh+VOlTL40HLPaPtWmWdP9DNzu4aSACY2IpEQGzR5 -C2P6KDjTxBnb7UI3K+9RqWnylkDNtYkKKIni92LkYgWWVpMvGsUYsA13Gy4sBUow -b4HNcvEKfUQMixAGu0tl+3Ndq3gq633xQepeS1sa/kbVYX9LUR3R3hrMQD0ubQf1 -FbaIFEE7EBxuuWEEFH2W7nLbXxkntjtGSRCIPuDtJKf6tUn4KC+2BHm9iqnsvVLG -m2AzNfYtXdFXo7/Sx3kYgQDJj3+MWm7NqMGJAhwEEAEIAAYFAkpxvfsACgkQ8aab -5CnA/+7NIQ//erYx5UOFVMwAP75BBYIpche7cnsKCKpFeYJse+oDKDQ60E7qZ6Xd -ESAo2ofxlQk9FrLX3cKPChckwlNM6EcPSL/dHPn8U1+jH2XKEMPKzLJUjO0R1N+z -0pbUo1RplujCYREq/EZiPLk60I3K5VW5cE8smf16RyCupPS2kzvvnxNqwZ2VyP7b -3kLLCA5Lge/3z8JLKjEEfvhyp1VdTGiwWuaOv91ZcrL2NukC5lw4R+olaqrkz2lt -YQQ8gkHW+k456RFPtPXqNNoaRe14mHpVCAj9Gw16Qe62Xf15DeHWTftciQVJWezf -b96AkWjEG8TkKeSw0ydiCQR5v5EAy0bScCQxihGy3JO4OsPQ43Jq5uRhlm/h5iRi -loZ7p9z63dSBNE+cEwrInHFXU2Y51Mf56x+bI1Rexw5aMwl949UscKa7EhU78CDD -e/eKmdlAV85b1EGWOis0+DcBm2eAeJ/tWolyortDL60Jd9NLKqPWNnMjIUvowbxP -hn03n3gAYkZgV/6oshqQ5lTKlpxwQB3CaLEQabezRxbFDeEcE5c6fHciCzAZQwo3 -ZXvlhmcCxyFlCfsLe/roZcxTheEsLQBI030uPqyl7jRlkxSGzQ1DSL5kc4/h0rIe -h21pVKAP0GuBPzZsrLdn6k1prUnHWdx/2kieQzJWXXi2iySwxWl4akyJAhwEEAEI -AAYFAkpzCeoACgkQPZCKs/D79R90nw//YgUwOs7n4fUtdNUxVuUmkpZ173pJdnRR -B+/archdvPCCWvwGLXAzw+NYdK8T8d4OWm59ew4DAH859oTM1+OgZGzObYhuBj7m -G+5Atkkp4ZdfznIZcV2nVKQxnf0lduZsz18OQlehNbRtNpMNn0jgW0JA1rPBByFA -CQZf9Cj1Pe/cPXGzUyE3AumEBQOWaNI04EYUcL0hnwPRgi2c3gSeXJZkJ1YpC0/g -Jv+dL1sM804uIKI2XNcWK1jgayMRFNUkbAEPgd6jFzlW9KGr5DHNNy3jPP7v4OMX -O9cciNzVHdp9QTqX0midYKyfXNhDogfHVlQyAUd3Jy90ekYLuEZ83yiKb46L/Ush -n3C858ASQCJvr/Gr9DI0mSZk4DH2m5gBsgmLNQkmFCP3BPBD+BI5pcEr+9Iq43O1 -ScBo0jdMisur2aahf6DKX72DFnCiw+Qo6OsaJ1jhNm53VE69F/BZ9vtjk8fR1ZUi -UOXbDvrWhdGYRRzYMuY5xoh4XvGe8SmQpff9eWFjL6AVXMZLWFuUgzInFLdg2PKm -2C28Sl/gJG70EouARreVSK4m6UriPp8Zv7d69K7R8a/hJdu3Zitp6DulUmy/vRlX -cfX0ValJZqZRjSu5BRPZpUVWMxA8EZkxLN5olMYMasXKxEnTG191WMezakBOI05z -xsz6ChIgM5WJAhwEEAEIAAYFAkp0QmwACgkQ5hkEXfKscprdyw/+M6nZ1yWv+4A0 -bUoTg/rrCQLGzzj078aF7G4sa2mYcrtm5Tcuwa0McCf+fXYkjqJW/mPMSJoYAalI -LC7fn5ORSG1L2ov/kU2M28PLTj/UGkyW2gzBzKGFQ8wUbzRWj1mnzOR6m5Xorbk1 -v4ayVIhjXMnfgNGBjoXBxWrN0Fvn8hKtnOmVa2W+MG99STZO0XYN0pKtdEf+NfXO -17hCeJ8CH165E9Aqc2VwJ7CBsRvYyr0fzy3Y9ptfE4PKeLkqIwRS4Nk8CGG51cmn -ZpJMehLDWj0sKAWVMIsPn47tLahpssYB3R6vx5ljE4eJSDBH3IEq4BY+EAgLUOH+ -ensWzPEGUBEPkUIkesjDKkR3x1Wnbe2qYCslQX8a9Dx/tG1dJgOclFLYt1lm4d1S -YpyzA1Hcqe7Vu7X27FWvmKDD1Z6cJuOny3lf14wGRwIYk7Ar4Fz2uU7gqcScpn6x -1lkSmbaYl7taNCB0QM7txHhUO7ZjweF+M+ENha8rDeQteVQom3HGYWjW5qtDf0sV -iyffqcs2kgsUe/z1EY2vBJQoSo1sNbSAMwDNkiu0qywOv3Edq0gsrEFuZ1ujxsL9 -wyBa6celKBMYBM3sgs/kHf9/MKA/T/wDP/bFRpnUDPCnZo4WO+jhdx/tDMOyqNV2 -kbIzQMy963+ZcLuQCZIQ2ppqnSsmBKWJAhwEEAEIAAYFAkp0d80ACgkQ3/c4wtFs -+6K6bA//bn/oC9YHrIlGNEUXtvJURvDHqwKuc5Wwg7cjJ0MiCzEK8p8A3zNxSuVY -if/HivDkSfDtvTm4AeLyfY+LPOtTU9rINo2u0zYvRxdpRZcjpZZUIkshnYrt8Lxd -Jtk1P0bwZxi2JeXSukFSp6OgU8UU4DhsG/E44SAvG8cE0m9mAYWLgWnPNUDfa25M -/0Qt8c20jUDOkpXugxD4xZKGLjtWq9lgrkadE0Bol15Zf0nlV7lSQTYPHZelGUTs -PiuzQvn1TeYrVfRcX5inQfELfcNkwtY3cbER6A9Ip36TuUwu5EaqkBNVQicwkCLP -+SFT5s+gF+5ubooE5dOA9JTlMdcP4JllhlPAx7HZdW1evrJOIVlmRCU4O7t7PzCY -KjqnOgOVehEn/GUIvuezhtR8vZj6K968wHpNSR24OuzglnToQAbwNXu1EAYPQVlD -lTdAS2mcjofaKb9aXwKsw8MWGoedX1iqphjFZfexQ5h8Et+Xbc/UWgd3gdRiDfeh -6CyP7df4DoSLAyX/9WhEa65GTT/IaiUSqAFV3PL1zA45YO9wZ3R818LfpWqSZnOb -1HK3XQ9OHd737xRZNeRy0+K/4EQdV8mOscm12Gs/do5OSYnuskOClCpUl2HQDgiB -JUT5vMnxjQZuy5ZX/AQPjROXYZO1HAR11tR2TIV2JS67jmb2rr2JAhwEEAEIAAYF -Akp0eA0ACgkQupx4Bh3djJsyKBAAhx9TStauyBXq2MAeB2hID+7O066HRXEAgymM -PHzMGEauZ1pWLFcEpt0IXdLQMSyiFQtp55ZHneo2n1OkUVyvHBk21LEkHDq+kc4e -k4p30Daxr8MsbmRC0rm4THazxDfpVwEOQG9u6NixJ3pzhJZBGz9QfhPRlOiWRJIo -yD03wFDZr4MJTyjyLh6N1h2tpgyLLh5kJAQwnE/HaoAL6KGNHbE/oLJFG6mOMYLu -MrP8Jr1leV6vyCk9rvaZM4YtW+VbH84uhHZb3MQRLA6bYTgve6HYhcdQ3ZsGYXNU -21m6Enz+SSodisfVJsEWqkJ71TOI1w1JuY5zCT1WjKAaMQKFOzx+pQXpsUJZIGyN -4h9ydVmNIG65vGcF74x/t/g8zMeQFgB9WW9BZir0v054/cl6JAXN9WEIiJIBA+Cj -TQhUOxAE1xohhwtYaWgTNTzD16PToJ3KmPc8OwSL6Xchmwo1ti4fSrgRvwyUfom7 -RGqEh33Pj/FCUu+iffPpg6k2dsqRyWiG3c/LdT5+x+8GQRL2pcvRleAgnzaBcXYB -DWs73+w+pXNbcrXDESFJfZ/LWnwCxGalS0FYw61wrO2u8NytzflsgQRNDyJqgmrc -azaQsi/hcMM8zMA2ib8Xnp/1kYThfCV3TQhnk0td9xDIn/0vmQunodgGQaATdf1D -x4YWCimJAhwEEAEIAAYFAkp0iqoACgkQotPnz1ITRrRoQxAAzp2JJVMQlbjb7MNc -PDzHukP+VzBa2c+2gA/1KVlqwcn6zSLVMQem8BA5OlRvso8k3PzDca11pQhBMmCJ -A3RLw7tOsQHztHd5ojCasluZAVMsPcrDV+mvUTG0li2DdNQaVpvpejB/kiAjXXb/ -5mmvqi/A50/LlkThYKddhDV+uGoEMo4vV6Y261c3M/0OTSmRQ6Wsu3fnRZMnhx2b -IkL9E1SzTswK5rDt5z5zZBAnnlQMNnB3xHJz9JpC17Cl4dz+K9z8iAj9iWmLyP32 -cqG7HX0qXx8EKXUzkepuknobd9yyKyoPX7bohnNHYlub/4RtB+ola+rcpcSnhRuh -jczI+nki91wzG5l+g7DqB3/6QjLAkr8ODG+2yfWIiIMJ7vh/FV2MNkAXJ8bXxykU -K/Hpnzt/C4/5AK+8rw/c3UbEz4+6mXBrtm14vVy4CLul8TaKVMcSQG78F0JKNzS8 -26LlE0IXLZISMPEoDgkCXQaMNLXkuJpc1hn0crKvxTcSF4sTQvTL1P+HVBYwVbVr -OymnaY4US35cCszaq9SM/K49E/RqDMi+Mw500DYMvGaamxeVt3SWCN39A+qtVccX -clyf2NS1qZ03QlNwTgnj7zJjYFD4j2okpppyg3o6eGqof2LTseF/CkZ9md0R5jD7 -sIKukXOPaThkTAClmnWy9tkJAYqJAhwEEAEIAAYFAkp0nN4ACgkQM74aCowu2P9/ -fQ/+K5eoN8yqB/bV7LORgB8W13FabnC9mItPgrKLDzhlNnEEJV6Zfwbhhi0Q3I2d -iUpezlAy4ymh7syAUK2B5zCkU0ZPekApylBZ8N1GVc6ZW3blZgn+X7mXbn4eOJtE -MXA6p6gcCUkI3BubqxvhqVWwWTaOobkAce+Znt1tOXm2kn1fTObX/viefVqRM+Zb -qA9pGO63AUmbPbG2sXKTlsAMfG81Ob9Cj7rC9YJqMK86Bx5p4qcG0o+d9EB2VkaN -xrzpcT5erOP8bK3BfeNwD0Axs0diOr6DF6ZgG2MSjwWfnOw23Ze8mI6rxIJ2BR/F -E1omzfsy6PJ1g16iObcs6QRB0gPRA5gsXVSTFRD8ir9mZbhvs1zJwwajbhamDES0 -xzvotAtGzvJ9otlRoNCjJSyEd5+UsZA/hdCnmN+TFFAaNXRLzfGruJ4VqTB1r8cF -EK3s/WWyyklzHFlHkOrIikmpmQF1uTF8Ie3Qaqy4fe36WKxGPikJaluswytcME9n -ODu0htKsMeYQu/H30tM4ZI+X3u57CamClnqU32CzkYYYgdDOeKfZwrgcV1VTV6QB -ZxtocuuIW0zzMUz3+0qnU8Ee0FL2zTF81ZYey0VIT2qgefKIcNQaV9t24s3owgd4 -E7xySnusItweNss1y3xkXv4RedK0vVnNBvD3em3ucwDVH+uJAhwEEAEIAAYFAkp3 -DyUACgkQuREgU22FEo1TCg//W/ZB/PQ1oUedkaWxm2fgF+L08VnZSUIxmSpjx2zY -ifVLi/gjC7h6JPns9cNK2yEfbsr80oBAZQbAEFwuLL/v5tN7c19PNg6mkD9WB/Sn -1AUnvjRqWd4F03X5kebzSFNLPC1ptYs4cr6vrMxXhlp9fPy5yschpJYkWizE9sKB -BMbmGDuGObK+zWNR1jX+mt3bf8JeWem/mBeM2+RhcA3QNoOn7X/jljkVO4EqHM6E -1GdG7Yb8wvI1gpQcIv3baUOHiNOPkNY9a4k+qzCOxzblCQV6eqIzzrLaXfpDFs4k -BaSm8UQwN9os0glCtPG9JrUe89YMX0vK/UE236xEHZ/WZOlNFPJH7bNhjnTULz1D -H+/FtTMxH9GM954SYerfpF6JdHizARCLKudyh+7+/P2n+xC5fOYyJekeW+MWVty/ -UUCIIxF/2Slk4pz/xAYGO9Qp7AwV7GTHa9z4Gfbdci80QSilw9G780DP3HpfhOLm -VAp56/XWCxo4FG2PXiSeGXtuOi50xMs0RlvmriMIXuTBXTJRhe558FFrx8sqxyFN -Q7ZFczzQWrtpo+ONujNLe7NCAGgc2V1E+uDuY/k1eWgDDXrQVovXRY0f1VgWzwFb -p2LWgLWicw9dBRxrwY7H6UC8RUx2FtcJX8Ftgt3bUx+QXlTIe/hBHQQ0kJafyjEX -HROJAhwEEAEIAAYFAkp3TWIACgkQLHwxRsGgASH4eQ//dBiseGPOVWkY/IZkKJ2Z -XMEQHGP8XMgIFZkNexC5ZztpZzkuiT2eqXEPJ2L99APXV+ORGLbXjyyHExTCMMYM -Pm0lx448+oCTbjLt80OR/+5gnHN9goISd4hEZH/ttrBl3cuiy/kKYtdgMhepv2Ln -/QEMOA6gTOqpBrvI3IcCu4bH42XhG++JXWOBA7G5B+seSzLavoxhAUvl2CCNzDly -twTtOMJUUrWlMSCXbpMkCX/kK8d2ZdvLz6jbkz3iLXAjJs67bpaBgjfWIfX9nu/z -kM20ifGY/N1qxdkVvnLfn8GaC2oOOh2t/VihdYnWn842pFvAvccfmR5KdwG7bp1w -UkkEYlMHT3ZE9+o6rA54Q+GW0SdocRIkHP5VkmvW80HwGFe841fcMFV/01dZRTV2 -u3A8201ZVfxFzekzdvDkQiUx5RRZOZVRZMTQ85xaEww0e3yRZMhLPbgixfAlKvtV -kh68ItY07lYT/1A18BFPPX8Uu9lKHen0lwLLYPzKTKTGfafQmws2e7vYg/ggqBxx -ukjr023oHliGv+KfKGl7czc4nkr6+05lXYvjie/9rbLq8LWShc+3PEJYQXsXjdRC -ui+wB8u71A6ysLxnl+s0McrrSk7S9YLScKFPajPFozvC7h7hj+p6JOwMrkBbUm6+ -Tsl7jM/cDFg66XQAck4p4hSJAhwEEAEIAAYFAkp3YMoACgkQDHBVe1oGUT404hAA -vboPEEND39IsF26k/+H4b9q42IeE0GSpv1XD2vuLV6AuUg7xnNG1BnVmppWDhhAC -kmUAqNieQPTDTO4TDxQJgsiNsmHX98XMa4Ui2cm0027nl7+NzDzcBcyU/DQwnC2o -uhatffWBtV1lmoXKhbwBbm9lb96ozHpFy3J+ahG+gEE/lz5gqnNhFUKwKUpUXhAj -6lC3pIS9p6Ivb5JTVnUcjQm6bdnghq4tPvRcIai/AWsIMcG3XXuADvJSXu/E7F1C -MqoY3Jk5z3KUoV2kiulwD4BS2U8ijfREA6ZGdgpfWfOicTz47BE+4r6id4AOMmLD -tT4Bu1/tIOYQgM9MCEG4rIvRY5a8fCR3HxLzCvHkYXxHqzcf4h1lqfhqnLu5DSoU -28tD1UvNzCDDYHN32dLE7rOKzkhDp2lF/Hk91qjMnqu5XKjReL/RSaiHlRpY7Lzy -sV6L0qmlv/hZnzo1cYI3/5rsZ+8uD8FJax67fvQHOt980AEiz7BXBXh1yIpDCp2V -PChjw3nKhkswq8yyMKQTPYjNT18idX56o8tH6sSpEIVaAfdjj9DjcLm2C1voMndV -quiiD17RjfbpTSMT0B1arCtUxfHZ0DlRFwpDWENirBKQHg1H6/gmjO0AR8H6Vj5X -MuzCEZkp2Nj78HpI2NNBEsQXFCq5wlVySpmqT4VdLQeJAhwEEAEIAAYFAkp5kL8A -CgkQhy9wLE1uJaiPsw/5ATQnbAvc8nXZR7cn67t4aXNhW9Zt9tEFvvPVGWFZdlIq -bJP3GJZ49Zdexl/5FHNSSgJuEPfOoYaRWFEcYGKPGMBFkyUE85k+LJN4aUx1QmXB -g4qUWnKoAfh7nVx2MJXBkSSlEMyO8gEXuxozDPqa2yT+CXALosnZDj8FDfYPvAaV -jwLV2qs6K/uOsBKnc2QvDuWC3kVHYoz1ck2zKW6I9HcMQMAwp5shzFVz3D6TWrdE -fH1Iv+usaWcgBKCCVmNxAfLDYEtfWMJzdRTVmKTcEuLRUUbX8RejS5s6oMgJCP9A -2YJJ68azNxoxA6sz7B/xpirE/kYlnkDRoL2z6xfmME/Pbr9nI9oRPEMpxNi6qqb4 -dAmZ6TDRDAZk+QD9TxQlRE6FFcVXcSZDET28ISWi/kcSQiCWS02lIQlXH0Ui4Gtz -VaZ/qtsDmTYz0yGpnxuc2LVoo9KiS4raJLbKYtKsHBd6jf5LsMvxDtm0JHGWIRxO -NmNqGksSLEThEp89ExjdCeXDw6QiZZL6KCEVswvDbo9y4iUy9zgovj0XWxDuI34g -MWRBlTi2m4r0qL8kkzO0lE0cVqQWr09nuy+uPlwdy1+CH5yx0CKLt+sCO+hIst3a -Kj5XLsLCaLmu8Po7dgzZsxX4k4Zp3XOXbXi+Qgj89dlMP+1hfXIxRDmGfVUQICmJ -AhwEEAEIAAYFAkp8M44ACgkQuzpoAYZJqgZPRA//fYs6kzvd4cuCYpvheQQEdzMB -Tbve/eja06T63RbsyqMZAJ6sK1vGMI/zKuWkUwl4QKCD2hCTslL6DFgg5OwhB9a+ -XlDIFpDRngU4iyqRUefxuFyYe9qDkVFvALJ33ZdkuG8tRfOyFpLWeQVQnp1h+jFW -6dnGirBIZ5XpwGjmK3n70uOzzCEiMFcHE5qBt1QKrtvtJA83NqimT2yE7QFfYWze -TepOWcFJHK1fAyjva+PnhUQOCMO9lIyuaClkJ/t+JHcj7ANzveTi0TQ3HDeh4de2 -9fEmcy+PhS8fTDDjgzu4mIUbn5d4m0BxmASadQqhEgDZPR/AfzUsQIx5uBGRNcm+ -pKJkUJ8rv7OnvERNDV+oTE8wxwmESzYM7QyOaRPMpM0SNuMhu8kR/ocYZaE0jZDH -Ybf0B3rqlK98GcPCYyPsSmK1j3lXHCxSRRkprMqqPOnXlcU6AkGnVJANQaIzWf+m -Wgiq+JdrK9XWng+NW3Q0+3rRs22XtoT0RzeNUqFWtFhhXRZ/wo2kFe3bTkjZLr0S -FszO5Kjhto9CSP6xrZ1kjp/QytgvPOgyBv0bVEA4FtRFqoE6pSf7q/AXIIvxjLue -0FNKGe1gtoUiDY3MNaw1sZ9h4UE+QhMs4aurrH/Mo2bOyfwueCv2kbCbaIXTFUmY -u1JNt9i7+ItWC+T4w1GJAhwEEAEIAAYFAkp9NOAACgkQScO/iSdVPS79WQ//bL49 -bUxxxHm0FT6DcKgF+OnBOWSCUmzWBG0I7vAJ0uKgVhwtUlUTr2XO1v4+fqTYJpEa -i/V3p7+04D+CtElYA1/nY1OKa5Hc0GpIg92ZCiMnUo5US5gmWRdiPHx5xmHIA+Gl -3M66JpnQmrpol1SOevcNM6jDTWDX0TcliSHud5i8aTgKq+kcMUhqX97tzqZLEVNm -ZXuES/K64btEDvrwrTKSEkqf5uPZJEgO2N001hKtaVVs6dKSZeyVqvoejkaS3IS/ -AgBOXzwLTLAceXFNaIfR0XWjoY04ylr+O+BAvZVluDORyu+OI+7kOLCNa3+9nxb1 -sss8mgu857EpDqwiFT2QD3h/KAVxMWKs36VvF1bCJBDDeUek8u1UJ872X3UZcVBR -dichJ3ShZjMs91w5uIaxFoNRQAUHz3I2If/plESlCLoV0pekUK/1MdAwxG9SizTR -L/VKGQQOXVBGg/P0J0WInHjsjF3ZaoImCQxpLmKknciOF8mcYeVnZim48nFA2n28 -BzTocwWT+LyN1oGlFV8sShdfapUWeIENSS/24CLrnV23el9+RAn6+ieaDyOfddLq -aUEkTAtWSmT2jl0nLy0inq8xoQ8qvCZDhA5QwFYntpCqnYUDG1i5PtEOa/SorNM4 -jaFKYrPH0je+NHMX786ayjsGI7cC7jC+QxjbrDyJAhwEEAEIAAYFAkrxg40ACgkQ -TSSdmyPm/DpLKhAA0XhTsVyzehdCAjNqRlyfoxvFyFUT+AdFO8uAfWuhjJO4LE/e -hdY7p6/OjxbwaNbK/nW7t6OTPOkwqg5L1IYLNT4YiPbEyy73XKfBcwN8uVHuaZff -/ffJXNdgxIP4F/65KIKA+KkBMrmwA03U/Fkxk5GgxFHw1bUq1K5ZRGz7ULR2Wanu -eVoVeueCv0u36VaQrNEVmi8qy/wvCiB2+BRqle9AeESnd/3tiIuflmZN5AvupKu3 -Lgfia/1yJ+yT4xtztG9xjY2wkvVum9G2LUejlD1IBW1NtsIc7fvvZNjxxLPrANaY -MtRFMNQ53BQXJmL5eDSJkIKJJUmpsj7O0uT3L0Dgbrj5vm6zwbDBD7LVKQwZsykK -c70GbBF7OP/a6RwkVdki1Mr/yHRzeEgJAMv38FCkIfx2pdtbt4+NDq2DFiu8ldwC -P6+zhe+Tpp5++XgEbn514d20MBO2MN0fP8uMRmwyhT9op2HdaYu7ShZjeRIj4Zhp -t87+J4XXT/xGs1TfFHhEzO59NaJsLECtovb5Ew6G4I5k3VUh3GPtJ8NeCUYQWlBJ -uFgapCt8seeoQBqgMrW3JYWAfv3feDRfQADgk7LLelLSr1pB9FlF+et8FEqozHW5 -P5jmKH85rBlVKYg9heoswMtNEV79XqPGkfGTql8KCrZEg+hz5tQvTNg9tFGJAhwE -EAEIAAYFAktcFFMACgkQFigfLgB8mNEgbhAAoMzUL1bkHzDntewSt6l/d4t6YEpn -fq6vO4QKlaAV9xBVT2lRhkHj2VuX45779wZekT5FJ1bUgQ45LwxPXiCeGlgIcqDI -4+SAgYt0byfINzY6ta1s7j09D1aMQmced6GzxAahC1gJPZiAdekqKUpm9hGIJFgz -RlCOrCMubGbB2xiZ8Xy+MmkcONPGH6eDt8L/KkQRxry2FB+TzKHhwK1UwnoezZra -aw9z5+R1Pe22/F0cPIEG0gCtVVAuDBfED64feyYY8EoyuxEJXpbZVSvGgQ11Nh8J -ffhFmHwvjObkOKRjwyHuzsYfPQiJsXP23ZXsH8LEbRIjA2Y9XhDeDJy6z0xvk+TI -4SQF4BT2aaBWphlFCY04i6YRzbx2VGBeBjL2MWK3ePb/7PA9isTOEGn/oTzCkbN1 -tdWZ7UfljMFg+IQ+83s1gtukZz+va3ElBCh1JYk6Euhf9t/J/fPpR/BkdNx48i1c -WfjLzPyR4xvBfhOZtE1ZAGd93EA5Szjk4dIvFIHlH9KbnjWAZ8ttoh9qabSt9yoE -YJg0jxiDFTq8s4tbPAAG+8CwkgnQNiixyP4eqfUrVA49i/XC1ePt6rDu5k34N8T8 -gwvzNg74b252xUp4MSmE2ulMph5LjchslitjVMAdcbVbkGPcopb0ff6bdfqoXM8T -Ebg/+M4puBUapFGJAhwEEAEIAAYFAktdSXgACgkQUK5whca99vBzrBAAzZmSQwmi -ANWoK0npoT1XmDWqObXkLeLfQZZ7hN9qh/RBOOe0r+AKICzS3RrQc1ZUZnN9iZeD -0UlHWitzat1lXWomHLPjvuohMJKhly9ElPZPB3jM883PjJyJRHHzlFWAhANPjC45 -nwv8cvoF5ppJwc6CdIgb+UFqSzd2xYV+pxG9zdy+VbyzFOU111eUifIjnNrxLqCz -IFzyDtoZbEx7HFZ513zEb+QopU7QdHK8/UIZdWmrNWuFOCFGXQGy3bDPkY0s26J9 -1N7ERNtOuXZweurbev+g8jRwCY7FjcPhmFd9Tpix171jA3fPlhIxfLXGk2fCErTA -iM/HJo/bjpKgrUXuzJo/+rLws4n/ml9BEu4o+UD0mOZWCeDwLEjOOE1TfyytFpGw -nV2wvwMIiMx12aefm1z23s2l0+6yR5GENKzHujBn6KdjWdsfj8DviO1bYWl8USys -5oqlHDtVnZv5SBpBbzfkfwdU9PWPXTLo2FPw0bsmNqe2Z40JqN6dJQjnGSwsIYOy -LODIlFbVoC+Ja0/n90YK3SaKGFldqkW4Gr5fzLsWtCJyYsaEramF9ZkVsCLDXov1 -TvCKIJ5SMDeRedEVj2MjcGZ2QYA4eo0FkADNcyiOfl1aYXMF12kE5RSlAIvp5s84 -YJO+dd7NHRpJbF9HrWn7ldrYWRS6oQQ17uSJAhwEEAEIAAYFAkxiEiEACgkQ5WK9 -lL8DRF5pmBAAmt7NM7xEDeA3RXe2RIf0+juV88RN8xBy7j2qgxjydrhf8o4T2aVu -Su6PvUZoW5Lmwln8ea0gFPJ8rQUDZcl9SqX7EMJvioznyE7W7DcZ3WbOfLArVhSI -u7S3bstdX1oW2YTV3eEYYuxrSQ/Ne3BqrhTq935KiHACuV795+2GLK8MjIMQMmeY -YuGizer+64KwbdUcLh0pUdI3PT1E/YuxIEH4MIXIsY7ASnko2f43VzwZ92gfBRk9 -jnkVbjdrv8X+Patwew8juPp0VWAfFhE8mnrgFK6Nqq2Hd1J1UpUx8suwsUgt60A2 -fnSfm1mzMlEtSyYx5b/HMiVVVPKAkEDYQJoml/pXzgV/Zb+ZztN+5U5BUKJSQKLO -4M/xdqipD6JXqzrlxwwJ1M7ZDpJkr+Cs5g5J7eLIvUuAWC/WZbDoGnLS2Kr6kNMz -Uvj28DoYxpV68/FOBQ/uIxe6PFoBdya6f8f2cDYorHEVpq6keaE6rDmP8IKQdW79 -dBy7oE+zGSd8wuHhJSpHpPnf4KiNYoFamoZJ5NFd9rHO7E3v+YhJK1kwvju9NJo0 -Ge6pINy5kjFQrIaWu5C9+Oy3LR8Fee8JbcGn2fEiP5DgbmO69aVC9nPE9jOTFgRX -rTxGjlS0Xq2p+q6fq8Ikt4J+wGU1RAG/dmmP/rxYoLm7UUaYYH5hINeJAhwEEAEK -AAYFAkp1okoACgkQ9m4+QZ+E9N5U5Q//etRCb4UtE7kXNZE08xWDEdcV9PiFgNCB -uV458jLszCgVOnDX/BVjqlCKDaC2sXHGGPJ0NDe9/xUDQkMz//NPP1DGe5jIwRhf -Q4fu07whtF7S43NBDKIiEAsMPe8mVAIC3Ur3YxwAQbZY6wblJdci+NSgyHXRWFF7 -/onhB/Cpp3LwwStg8veMuoeSGDb/L5N4GeKWzIrbgwuAR/V7oTITRUB1J6kopWZB -jJS3qXmx5a6lMsUYHI+Z26Efozh0h+qn/Vr1bCzEiOlneGnCW/ku3gxUY5CyCxND -Xqpuq0rJAW5cdQHe9onS1e/QTvG9P2xAwV2PRNgFJ9bc/XEtheLOtZAjI1Hgba2Q -C6Vwta5aVCULkpN5BnJhLR1HuMHu3XPif3octGN65ja+kxz27zLL2CHFlUY+d4k+ -ctVUJ9StNEb+dK0Cglg4yg1jnFAqEw6D6DIkn3hMc/gCQoGnGMXVw6zydh9Kv3KB -fZjAZ2p8pdKMyMvgSIOveqwDBscfNcQGsfuJhTN2Bfx9KE+sY5rT8e5fuHOUaoSH -4mqpl9iELhaM0g3/pZrlK+yuOIBd790lHKIW22h5tGZR2fZplBp5shUhPORvJ4Bz -GofpTHi7otixRg4id5DHlrfTGy/PvTlros6cX743cscfEx4Uane5fH/VoswtDUTw -QrN7tkKspNSJAhwEEAEKAAYFAkqCEIEACgkQE26c8XtdNC0dkQ/7BElh5EnZolmq -4rFx5yiwXyopWCQdtLWCI/AsH6mlh+4YovqfVye8jV+71cteLuObMjnRpqqUElVp -fsRV6RZOuRbxGP7g8Dpoj6V4vb696qhJrSJG8Z9aTXxWglv0e2XmEMRBYOl8E0c2 -vsgzz0kIrrm/TnzLS/gaC8nYh0QuM+i4l4yXdI9Ac6C6CHPubQ8oH59vle5qjD4G -Y72gMdK66Qt1VgLYS7UPGXsiRTjV+NyjPFJkRjkeSLsO7VSgZRye98KZ+sq9Y/TX -QwEtGyJU5PfnGxdrBruufmQ9CJLtk4H0bbDiVnGaoyqlPYN8r6F7w67axlDupP9P -Im3//uhortLfxxAzfjhiv7q9PhKBRq0RlUVzy8Rin5t807khUaR9PFKV0VKRmaUO -UlfQzh6zCFEjRrztdDW4EFXxBh/8E5ipRkq9jD4Ou4Ij/8xe7qe0kPSQlydGIR80 -OwVCD8hJC8HAmw/vx7LUFZLDb1gl3/9WmIXobqEr9obE3zzdVV5/pL4WieMIsPrN -2vhPU1xzU9MmukZcMCoTQe32bkbJiND+vFwDYUVC1Sur4YN4qIBaNPEAyOxysI4L -84wHKOutW/V5Z5P7KJ7h01akyhJGrDH1QKs4cfwy3geUAaCDQqMUNyBdLmRr4VKr -XfPr29kD5o2x6mwbQREsjypsEAYAC0eJAhwEEAEKAAYFAkqJbNYACgkQnCezE0K3 -UR0PDw/+Mi9l92E33DgWYdZLX/UWalCI/NjGMzpWMNI1xvtYxwX2RVxEv41/o8ua -Uy5GUQCcIAIYrPJDcCJo5f1a4LbqmjKVRW/bb7bG3uImRSaM9Yevw0AHIsuh8C9f -VBPSvzjJKR0Klm/RleEKa3DcSlQz0fxcI0YM7G845LY5FRyYHsWpuaGv7kvCRh/L -KMJeT2RbMP6aVJzbwaoa9kS1P1fYabgRi7GugW+DVYJ8Q66BKCJN45PtiiSofmr9 -eEDE8GkEuKgew1FGW+lV8nEEvTRh1Sm9k366bVGlU+vkYmRF1Khqfv08Nvl49mVI -g2QAzz+mAirJPau1Hi78ARrJ8JvnbpCCxchg0riTcIbgbZ0uMpWbChmG39cJ2Njq -BrqB++d6Sh4CNJ4gdBqTKeMlhsR7/8E6189v6/+ENfpualiVeeAu5nw2isx2f0p3 -oH85j2fd39LqGNtG9PRxyFU171lRDsfL9Vr9BpdWdBqJkPdXr/pJJ/RzXsWxTt3C -EksI20fK0JjaKX3fJzMODqb4tlxggl7A8INQR0hgoVPB8HEmjTsAoK8ouSwRJMf6 -639O/r0W4oRfh80mYIw421l/T/Q8xPMt/j1ZiG4xDVMJa+yJM/yLN3sADAUeRDi5 -tjrOiiHx4xi7TDqlE/PJrHwg3k+cOHAcUYhsKLdP3IJBwvzDmS+JAhwEEAEKAAYF -AktkYEwACgkQjL+aMihhp5DuaQ/+J5fH1PD7kXhfcqeGUu5u9L/j8npiGaxvhrOb -6lE8IcVC8lTtulRs3AZEhVwcVKLZ0e3UBGl2cMkBxr/a2tQUWQtF9qvyRRwSAJ7/ -sfRbbcTh4pWiPbZcoLXtv/mJYeFTSbd4ybma/mxBqDB/6JPYBL937cnUOHCn0QN4 -8pzhRov1QqyePBLqwS5qZqdgPSdX/1CsQ+QQjwUdhM/Mee2qZ19gJMijYqfPzWlU -FgEe1QihKRpcwZRv92VgbVR9MMpXiflPq3LLoSHWCBOcE8q8gpPLjzDdGa8+Q/4n -a8HjOBXvPkTZdfdc0S2Ny/k/iqT0qoDfFWraKUUpBgUnynQYHW0pvbJh2sbSaG0a -ge5h1Lr+MO3Zb3+I51QEECC3cGgY5kxMe0x/NuUrqK6uPPiDkxP+XUvhwf90Hn1T -+b0t0kz6+mCK0vZnzbn0Jois1Y0kraXvoJNbnyeovaNyYAjsoQW2k2XRrFOFQwyS -+ShsFQX9KyFeoU5D+vlPyWJRBBTp2Hr14fzuCOx1GxeQsbP7D9ELn6YDuIZB/DDT -BYpfjfJg0nfZcy49HUsR4yQodwWSNnf5/9fQDhyxkFxLu6OgEfVdTkiEEh85BEFf -Uu5FY7ZsRER3wOYWJc0vdMfIzvIaAKPb7RuLUMrjWfkc/Z/83/xmgKlYV4JTBiFC -0qIZBpmJAhwEEgEIAAYFAkp3hrsACgkQyRDZIiUS48cUAw//RHlNxAaDkanUdJod -7jKJI20NF+tZND8XBFIyCVTYDOuZxal6TPt9aDuhOrFyyjbOQThbSGmqkLR7c7k5 -h56whezShblj7DsWwe5jKHfGi+J7CGFPDTnAxMjfzvKsxluY2zF+znO5WX091pmt -Ri+R7AXlPG2FFPkEeRl59+sFg8J6cy5pRifUV9lFtPr4zhYGHRgi9K0ds0qDaBH/ -2FXm/86afoBWR81/YiAIVhXQmP5yRUecpOfi+blRlj+KAvgocCm/q+kUMkv8tZtv -guWmpna4PgYG/YLP7lYnleqGapnYoRyuPMAK3Mga/o6cE50JuIe+0of9NX9oBPre -rUHWjsCANo/vcmSf3Gc3wC5v/6n+3+yTR5HCaI5rB02eYfHKL+v49Qw/XyZ/annc -icaZp636XDzjZVvZQOzJEIRepi7VNBox23kqhHM9pMu6nINDff5IwoW/FRQOZaKi -NkzSyYlblM0a7xGU/dd1mwEU1fE5mHPB2mHgh3VyfMqCwZQIH1U9dgFvUbrV/vkJ -YbXIysFSTKqyZynXi/n1e1w2eVaXGLi+I2O1CNSOr63Z/TT8mSY8yGuKnoyPaAtK -FAW77YeTw1cQSHMr8IcDumDo9DcKGPyTZ3PGoRhWKx6sIXwiCi4pnqMjXPT9NkXt -t3bIiUN0AIV59L5g1Zt88QgpX7yJAhwEEgEKAAYFAkp5wtUACgkQ9TaqcBEdVxaD -ew//fz++v0z7p9OhQZmi41dnwh1ApdQWXLx+YYgBHdh43fuCwiF3iggHfCnPLAh2 -hvi5QFxNXpYKHe/UDqzwCb3AdEh1fvDyl262ak/CiztdQreuo0PoQLlRWxOZ6Qha -FAo1HvKjPhS9NXz5XrHABPWRLoMTRY0x9N59Yr4r1DVRXQyU4ZW6wRnnL6bzVIln -BTbDPvG3IfBiP53a8aFkJOY06+8kxfwtuC//m6M9PXaM/iP/XengmE8ClvJdIqAr -CsjjDl7yCqrpPyJWdHC6BTzsbBPGSFoBjVdvi/HzCKBgUEC2YCLaobP9rnHOU0NS -2P6QDtgtkZWgc9DU8dELUo6VuUAaa0eoP6cynTPEf2LgPHQ3yOa3w3LaXCgCrzL0 -fSera6K3JBk1fPCKqbkl0oq9hAfVxsqvIe85211kzzRLCwY+lzCDxGnZSIKt9APo -3bMRLvEjZKNIOl3nn2lOZfPGU4OqHM/fdkjQOCbeP7YLJFrgXBwORUdXdMc7pzDy -ru7nn3nPD6HWiRtyD197NXimiG3PGoHazOOVhgY/BxQnuCxmwGHmDgWAYbyrnWM3 -D4DOk3zkETQYMv+AI0lZ9RCgf61X2Bo2ZygRX+kZMAMQgY9mUqMRCZ1zbzZSAWRA -fcFF6Ff9wgYzAMNpsiOVI4p3tFiKNLMrtipCaSmIh+lOX76JAhwEEwECAAYFAkpx -ZukACgkQxodfNUHO/eAsNA//X6hVjOIOPNIe1w7EZqxN4fczZGxfWT5okQBofd+i -XUCBDklLc0Dm/6FbvHLmVWHVnJ6lJUQvCks162Z1vDUtq9QzJURBM8Q9ACXXtMIV -rZJm3rTDsJQYutwrk2Ya+8kAB8fX6bQy5jO45gZoenul56RSzA/3vnn1+uRArCyz -HDVgKjitck4L1Nh1IWALLZQn4cLCzb7CPrhBglBiClXxKwmHq3gDzxSiZHPaaRe5 -hLpkKAmsCLxEPkwPAROaUwd2ryvdn+85FcceeTDRI2jT+ir5dxesZ60zjz2LaGGF -FTr5y1a5rc1lxqcO7OXM6LRszYPWEllFYyX6JqTi/c6nPZvEUsulIodrghTx374a -QJzZJCkjfau8ZCwQJx8SoDk/9VOuCHtF11a9cskdtLeQ7VPg9HE6fuOR6+mRvCfb -LDcdc9XU1zAyPaL7B6O9yvaOpqN/PtodbkJpSAa7Q7znTiNlQgsG+UvdXgCDQX5d -zmaWnrvPqcV0eD/kAV9RRMx6FheVJsY5xzXOn2xJAvws1DUq+RltNCyokFSWUfLK -IDV5uMKzCSoEhDJY1uq4hQMfjKxdORy2DTaqvNO6W7E9xQXd6cPUPqal0+4TGjjU -Sf4pISTC3J3s4Lf4pfM51HJFLo++bR4dI7Df04lrloogjw53DhxDqviWfIbJvf/q -F16JAhwEEwECAAYFAkp0hS8ACgkQzUsq86CgqqqWNRAAsPV1CbHUn6g0tZbohObw -h38XR3C2+em9wiHWK/cZ3+i0Z3WuL8OPDJ9KEB7Z1uABZbmLEHKt6zRIsfc6tN+X -vyGq5ErOWCkcFucO9wKravFrGe1YEIbO0ckEZe1xH1lf7TVZ3alaYwdbEoHei7Yy -ZV616ZsVvHfWUud8uCtCZLhi2s0bfyJidbLa3t2YW70Krlk8ZrY+DwkH/54r07SC -igvvvt24c1PbRq1eUmHBb0KaMZjoqMivh1D30wG+Q9WhzJXxuXqvZMmaGI198my5 -Lzb+n6d3pFIZDuEEwhJNhjp74jct0y7Ty+iS055YT3CVW/SnDQ6qNwEB1cMKw921 -lv5F2kSUbgVuYcVOl+Ny81xwuLOGY3HRlknq05hmXUCNAqWkU3GHRDFDvZKKmuWv -xMy6NLwejaAHP5hrhZt0Am0RDwTqHa5L8JBRh6o5Bc8p1hW58ScftyKGpZ9s4jT1 -PuIiu5/fB7Dr5HbQbAjzw6kIyoqxI2qMSayOyC2OKEmfAHIpeWRuCKbDo5rtJ5E+ -HG0/iEvC/ahi877Yt1KYPJ0C3IwZWJpk0Zx6/qZH0yMsCt6lQXUbIGivUBzesfKB -+/f/qKylGEVSp8yRDBieorN3/dL7dZG782kA5MUb5JiWjuknJrsNRUzx+++0Qj+e -qIqWQpRF9Pd0ikOyZdvTwhqJAhwEEwEIAAYFAkp0yd0ACgkQWHl5VzRCaE5Akg/+ -M5TeRAnY0P51HOyydASinl8cIyY5aX3D+n2GS5HVblNxRmCfiOLFsls9jqdrGut7 -i6Uz9bOW8YeLMxw+ajXP2Zae0Oz81sQ0n6mcMIqkPs8qU53ADv9kGtmnQgL8cCmp -dGaOMgcIdbkgd4t85srr1/+Rr7ZXWVY4R2gmPuxvlK24HaEsunMXp8nFZOVZcLV4 -CQU3hmhFkPQdFilFQ46QVJNLbESzoaiyqb47XDlAbA6UN2b9pBSL3fR3HlLDVFA9 -SgzTcADz/b8ozuN2/olYigb5to03KVC8Un3Cg874rqeBXawmx1NT5kcdC+7xlVkQ -RtDg7fuQpqUkblGLeMn8Bo/LJIlAdZDzokbq08TwRo8FOrwDLaO7a11A+5kz5IeH -yjPooJLf8+NV/pzCcIxFvGMxfXQi/MHhh0BMDHUJHB6KKUKkbzk0AjAfly7rZus7 -JJI9rmfHAiWn33139SO8wWvmScWqpdMbJjdzqO1EcMP1uwi2ybwTjenky/+2R6mo -62ktG0F/x0aqgz+CMnlZq1EGjU3NnSsoPaZhQwsbiUXHQaYhChqN0zRdgDvKAlKz -oed2bSxJa0pHkdYq2t510U6Le/gNK1tMJJQEw1IdSvq/K5mj7iCBU8C6UdMyU8m0 -UHEN47jnoNwAhogoNYoktjmoypkGiUJ65CE8Z6GHbb+JAhwEEwEKAAYFAkp9jN0A -CgkQzUsq86CgqqorGg//UalGLPka9QF0+BJHpveBlD83YkXgHivc6JCgkaTjR8Pw -IUjzIp1OZLEDI5yy8Vv00OXZjyToNxlhKPMf4lr1uj5g3+J7Ig8uDC9XB81+kMo3 -ALammlkVwyCZjrn2g9zoRvjAx1hIU+979CHa4T18keq4adpm20MxAb3C8uh7QS1t -WJDDUU6lGktugkMtN0G65dL4Wbi+uAJATuAhuLsXgI2obRLPuHl01qI/a0FhwkV4 -QNVCaD4ZJc242/yrD+/QXwyiRL3xJQ41V7tNd1pr3dZvBGBtpFIh9RLjIsDVdvi3 -NIhtuEPGnWrFd20COurTkyOnF5NdhrACkOWBs9hpatVF3ystyOHaumym8OQbAduA -/9D5jj4e+HYwor9r0YGE4t5R3X+Is/wcr+xqyxNFr5j1Uz9OPb5F7H/B+VuwK1Ym -2L29ImukP+ZnNbrP/7kXawQzG0Pqz/EteOl9ssznUaKoyINuL4qXXmCeQ+1Si+88 -/VICvxA4kdWwk3rUxJV4X0K3y8cVZ0XpDAApMVMPkh1tfxH0S9vdqOjszoDNByGE -qvnKbcwskVRys8fTM1DiYmdBcp1zLo/mnsRxYkfTDt/fn8dAcYhxHd8rGJBKEz7P -8cOr+89vtDHekflU1j2FyTBCPBDjvWjzEFLLCokZkolp+YQSljt+57bYyBcYhTWJ -AjcEEwEIACEFAkpQxD4CGwMFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AACgkQAIBv -K9cppFfayQ/9EvRJqzFofLCXFYL9I2lwCsnlxLSN+1h1cAs4slwOhbhPdGbAVviC -yZfetY43Ff355Rxje/jhe7PD2bMOCNj8i+DW4Tt/x+aXgO8sWvhDCpYZOVN/utko -mLqeNCsWcmnt9TOjdmISbBVLKeLMhJ9hczE0FYnrga3AZfXA5zsXltBpW9j/wABi -oBXvgXUwm0CgTbt0wFnR6vnhTJAdWFqdQfvkGtTxaJUxD2VtkgU4kzxoZUwC1ODY -1oNtx09X5DQuMcGwmswZTXBTglxugS8QeBRZG5zBYQNEjbRj4WldqE3HDZXnh+jC -resMeFnVS8I3rDHM6V7RghVty3LFTbWfT3icXcpfTNMWz2k+AZ44MFeg0LbrZaGN -wH5ZlGyAC0B1OM/CUjQVTb6vS3zPhxhI3CI+1IGFYQ8H2Jxc29rgSdNxqwACxsBx -D+W3WyZ4xq8UpH9XX2fRY3fLPYo5t7Cx44JHC8wSxc7oCcKydwCFlAawfHVCg27Q -hzRdfe/F2aDtEZ50ElywD5xM46xhIJPRrO8177Wk0Wz2zDtZu2kJCSqXhyFCq2wt -pw6ggDww/FHNamr1PmZzIZAkJP0Xfbb7hV7JGZO8XOiJZYfYKJ7kCtiG4uFZr4CC -YvAjgQ49uaoMfltf+yjtFgIJZ55Z/5fMEZZbG9hfOrFMFOrhD1xF3AWIRgQQEQIA -BgUCT4XrIAAKCRBW5/+KKEDHCF2wAJ9eci6cslOnTumUh8NkCq2CzV/aBgCffdYf -J6lTc+KEG0WjlNU1kLHhl1WIRgQQEQIABgUCT4XrIAAKCRBjQvLsvFr2uV2wAKCD -i+BoFsyNZCBvdmxITgDi73yyRgCcDp+ze5eYzQrIdRpb4oplDZuUjQKJARwEEAEC -AAYFAk+F6yAACgkQZ+dy8INR4K+V5Af9HZ8ReiHijzr0M2uBbZKL7oBnd9GVY5hQ -3NlnYxMkelX5WNb1exmya4Br3u9nR/Djhc8p/W8G08Cqg+cAJGalzysph5+llAKv -Tu882n5CSty3bV/nk63eeytQ8pxao4hvvib4CPWSXDB2fdRpBP5QDIIKwoy/wIdj -Ithq15L5LIzatQXg+gCc/4UlNPWpend2p3d/68m4jeSUeY4BYSFMb5IuMWDqg3o2 -DXUw53RsMlSd5Uj/LMcdCFhEQzLA/lEi1fmJ/b3Oww4Xa6yanddyvnPA5GbkrUOf -KkxqYCmfIhFoxb99LBwo/DRTD8ywHLLQ/I3QoW3ViWqWzw3H1K9lR4kBHAQQAQIA -BgUCT4XrIAAKCRCoziimAQ1vOpXkB/4tdtx5apPAVI53DHnPRHg2ow1sF7WBCK6B -h8UjX3DgUtw+Qw1ZOwrc2Duyq4jYt4quxoP2+ZmXR7Jkk8HUjeMMxsX76MVhzYCz -ZjT3i/MGbXxRtVgWM8STwRyAx0vcl6cWYpL3vKEhFeVw9H4AWeD4I3CKagwnq+Bn -IOACtbCcgOzS6U9LtdpODZK97vHbgVLLpdvsO3gAv9G7Fj0sHwT1Xjlao4GsBscR -ytpc5fpG60B9aPGQjiPp+xnm8XN3QlXQPpMry9IsaW29I5lecm6vkoQdSYFTmsM9 -Vs8GI9jYKdbS5TVsmVgXTZXjNjtXv8FFzFeNryYgA0ZchddWU9GKiQEcBBABCAAG -BQJQIDy8AAoJECYHPvzaxXbmrkMH/ij56gCZC/FrYaJmGoS7NVfVfGgbEmu0Gpwp -njokczyTfmeC6BDM01On/elnyVnL05NSQda3qMYNSxd/nswx1sr8xi4xbLisKaGI -eDxCu3DGlWsjMSz1yfWI5LWiMZDcKJNR0d/lZJPSHdxWi0engKfoAzHPIWPSog29 -RccW0ZY83J+4fr3FsjhJrfAFUdfuoBKnCzyt5tkJTNGVqKdX+M+00xb8EK825u/G -pGBXL+o5nWPEEUh7CgY0pQcJld0GQtGDvU93wA1iNrXYGFIf462uc9hdhkwEmcIO -cvVObP+bzCHwnn3gWmUK2NPo6RmX1FUijR5bhm8kSQcVMqQObjuIRgQQEQIABgUC -UCBEUAAKCRCbRvH7CI9rjKDgAJ0WpSL1KkQ+1+36Z4eo737Vm6Xh7QCgxpbvwJ+Y -yrScQhN7h+ylV4gyr/CJAhwEEAECAAYFAlAgRG8ACgkQS80FZ8KW0F32zg/+M6pY -7APTt6cvQ4dZln1BbZMUKwe5rvjhibk5OiWg+Iw2jpNjEa52zaHhzlBj4mSmwTgK -/Lc8K/sBBdQdpgImwgEmbk5AJnq+0eZpxUYmS3w/G7B8voLogLLiXJwnmojNsc4r -KxQzL4MHBvOEJb9goV1C+ezpMx7KIdt1A+UaZKWUYHrDCJeMMJV/E0MIUvkTfb2m -gIgWursTKf8ZrR44KAmcHGydLRIa/dc7HUVvMCaavZ9CDKRltEI54DoSwru6MzmJ -SjjRyhPbz5HQql34bmhOgdJy0JZxbJSjKS9XI/d+NZ9u8CcrBKpDiDB9CkjoTtHV -DzjjZg/aDMSm6Tf99pGTEpX4LCllw2RO5kaX8C+2Sl/zmbwPrcHicOOOZwGYH8nw -Yp7Y81Xoxjlu8I6D65iizIRdM+7La96RHpaeunI9irk+81umGkxrwRWwH6xxKGA5 -9HkcUqLvqAT1HRyiw0hulPDQT4PBOmSKhXs8anmGTdQF409yYmPjnJnccgloqtpo -PUDFEgawiT9S3lZaB3DW9a1Cz38/dt414C3ompw87EmjsekkZseKBMymOkGmsVTT -SmshgEE/JYWtkyOxFk/SWLg/l8ZPrliCeFtiv+LtcXaj/yB7nYXkUB0gXyUC/ECw -vIot0kkUAKDDEesazO9x6RcT9Z49KdQlpmgf24yJAhwEEAEIAAYFAk+xYTYACgkQ -gkKUzQIX6Ngebg//S88LJ7BgioefXnynIroFRDC3J/mQ7i7Rl1z2bKFWbO+LP4xF -YPY+ialE0wqMpkzzZtCL6lhFYDON11H569yaLXTkDu/cjomwLWpB5rTmyCrJWNQr -W77IO4fuXs4XC38NTENYcEJHx2wLXK9ygu2l2tQjad2DztAAkGsB2XsKDxrRqm5T -hamX/8S9jHwPWlLUvVeQe0hyKcuCdO+DaBSGkKdw+m8C734wo3RTSY6UXLlpTVpT -+lfb/mVhD/UKitnxkgIsgBUwXWQexPm1OYo/K2AZQXFT95PcFZ8d97YaXJcsAqzU -YLyufkuXEq9qS0GTUXRrfKrd07667vyLUmOjwiWUm8/8ZQUZMttbindv9J1xmq68 -mH+AeGzM3wL0sOHXveW9hYIM4Vb7fuPToYg/arOnM8r7V2SOcf8sHH+DDrX/DXGt -Tv89fci9pVwecGbR3lnTrppN3GOQD6F4HPo0YuHRx/wY5v3dtTiMlSOP010JLJGN -so3Z0AsDMRmowsqOeWqHq/pgmEOh9IXAkry9mYEIjtT7gQCqG6iD5jD0RnFjDfbt -GYlIjAKitTvrUxANutorQ2OintKmwXtwgaQoeAoOG8lMQiyk+CHS4zwFN5NRYpv8 -6LNMFPDY1itcKzpuazTIFx8w4p9GXyJfyN/UupHMcsYlv+zH6CcX946+njWJAhwE -EAECAAYFAlB6xRIACgkQozZnad1A5ofBshAAifRbEUkybOAY1ghWD2Z7bnqmzK4z -ZZMWTGlq3/nr/RP1552d5YddEm9823OO+PGhDoUgNWZF5tmNN3X0qDfU8xQMxaaX -5+6OC17p0CIGCv2LBjWQd7PCQ+y3895p22DUCPGjus8PVKUzL/X3nYSbNZ9WMbMX -H8Dgnc38caIukzlYAmvFcTypPrHzXTnXDCcVgHr+ngP+6kGanP2wvb9c440X7dxt -/MQ1T4BLQ4aXdHQAEIXZWEyPQ5jFDw8B53A35OTldu42BfSqU53xpTjh09WAfiCy -b6t5/7RlKkzp+scS87yXzzUanFj8fXxX/sPH7xt7JIq8SOd2+UgakSCxjLnJMTRf -y5vdPTiDe57DSq3gzIHUMtwyX9Uaq/WE2qv1KBHSi1TM6LTsMb9/JzSykpi3zBRk -mNHvVuZSheEh415DQJP+sn96wzAr6mh5Olp4mFuTdpk80lOUtO+y6lbBTswTZKYc -G+sGm9fV+/51mBn2jBrze5mUWRlBgCIsdUjSfuVzZVAzJ1Ox6QhIMLWM0PonxgiN -1AFukuuAOCaONnaPZOx3pGhdbCskmQ1sACZvy3OpXi+2QP+oAJjH2kUcmgjUv3+r -4tRYykXhdgKtd1qG1J5ueOymcbBxqmSlb9daDHtbjEjW6CbAM08L5nxZ3fYibnKM -6XDcdwagNcrULOeJAhwEEAEKAAYFAlCDIzUACgkQlzJd2Pn91QYORw//c70beP+r -wpyfgvgpr9Yg4az9p8oDxDSmAI+NP5tKA8SKnZ3C9KXhdP40K1PSViK4r71itext -4J5S8crEaLzt/PKa2vbmo6OKZFD8B9nxH9zoA2IRWeXjw1Czft5PEHgtHzQXwi/y -6R71g0aoJ79dCQtkTnU+viiR+It/BWM5ec/DxzvupQdtryfUAYuo2BupP/zkjk9C -Fuz8U7i12sHQPhHx26tIcVf5IipeqKKsD35V3x1mnfM7vxPlsaPOcmltBfNSVzsY -K+0JxyL+3RbgwttalCaH71rnPOl21Xv9MaOv0LWEXD7bC5WIfk6F7S+TtXiw7aqH -ziSCc2/a2d57ODe9jX9ugV85jGsl99S7g9nPRTWHE8h81RIul0ZGZI6AK5U6ZOwP -6kvK55Mqp2AH/zbqN+BSQfmrpegWtDnXhrZhn5OdMi6DekO3Ib0Mh2/31ZX+H6zW -97TKAOY4Fvln4Tpgz1w7WRYtW7gSONdYOrz9p8Do/rrDS0iidSqV3HS5WMjQ8gNY -tJU/i5ez3TBfK6Yjbpro/INSSItlLriadxrlzzkBAfyZ+HaNdu26L61RU2BU4MzP -5U8NFl4xXpNIlbzc3xts7+f1egKcDvke8PKFm8LOu4A4eD+MDxPPOrWZ48noS8cu -nDlPhC3JyiVXv+zHvHmkY8k5xsEC9XGfPT2JAhwEEAECAAYFAlCQTQIACgkQ/FE7 -yl+aII9VbA//bSXPDMTJNY8pfsWcK6kiOwd0k7HQ65LPicxznkehxDUuaDbJdA3g -KeQTUMPr3Gz6kk8gCQBf0nkl+KkO+tqwv9A7WZdFmbehPeWQYF65NN2TvToClSzY -0f/igUxXWKIYTkBCtFcjaY3IftBVrXmchdwccI64IsdaReG3vwnUzvuATzyGhZPE -15zu2DbrSH1zl7a8K5w0wpYglWoDKcoxPDfifEZ7MFlFrbgg221tkCzkq+SuzV/N -mxzH9dWm1YemGq/vBguzcVSxgPXhRWLe4ZgNU9Z9Ejbq+krvaGXMgafDqkvR3PnQ -/MiGxYwFr+t0wA/hq9m6XbNOPnkKTLE98pF+2twacftn8d5c/XrI5lXjDPa5KeqN -c5uSDYfwGJAlQksEcSDTyyyQbqtbYDaBobBevt4cqZRJif90jZK0da+wPD3Hi5z7 -0s9uPpIsx7kJUclr/MByeX6HQjzMn84PwpU1I+mPJn8feVjgdpK9Mcoz1kuzVsbu -hpts7Uwhdk8XWVVZlZd/qAw8ec0/hDdOwbwbldQa03blmf0qf/rsn39OHSH0qYuX -i7YnL9GvLYuoK9c8WZB10W+/yu4kplziHVOTrUG14e6/IP0BYL0WeBkWXnXExkZW -S2xxcbi+vxcyUwAsykKKj/Pjhd2yU/56TN9p3Vay6RXqiC0enhgjttaJAhwEEAEC -AAYFAlCDKs8ACgkQ1wD6SBOyhtmc0A/+Kfz655mG541g/jBNqzc5X1ZOjgUzMUaM -mRSUlxEw/fdH1PeUTKxkdImQLVJBk9QryIjA+FH2Pn1T1VST17JcuO3uD5lUX9Ul -MdLMktLeJiDRbS9zar0wOMJOlVRSKUY3XveVtnehluiT13wamqWuC2XZIhmgrxCU -gL9tXP+o64HVLbOo+108b0HHlNTTrk456Ewag5gZzbR89peCYNxvGtVmDl/naa5d -TJajU5LujBwmc2gu82UCJWAqkCgZVRXdGRl38Cfv6WJVD2mgpAYiNaozkfzHheH6 -XpbK9zfRoLNDmP/iZnDOUsqaWa++ksgIhaDVRDRmJspSqKos1blv1k8/3d0dFLeI -w57ED5xHo8YGyM4UMWxGw9RLaXFf3mzk2y9FtYoQbifen5ejV5w1gB7SJMq3D5zh -jiPSk27H84AAbSdvun4AU8aXup1X2j+CUetmEqx6Zyf8LragBHi5pOemOos1MhkT -6Gujlo7YGtIexPlb2m1NezyHk4AvqvGLxoblmhuvxiUv3J+huhMlsiGm7COol9Ow -tZEXRQtIFD4svjPLAeHpeGz9D/BfLdwUTA0oq9pGHMyfZpyKHowZu+IKyKAn0mwy -taA6TJ/PcRhTq0SdJSDDD/D1yinxYcalfyRiuntHkHYEO4mbKv6/u8ooC6pXtfUH -OksOkTzOj9OJAhwEEAECAAYFAlCD9yAACgkQe1hbMIB8Kofcng//Zdrhjqg6USg/ -yEuky8Em7j5evfaAmiuU488tPhWNceYa8OJndeZNbpSx8Dbb7rP4xGVa9Gv/eKZl -gfsfyytNeFereUHYx1BAb2hnFj/LmatGYS60WMtMk/Pk9DMI+Wk7UsPDbL4RrP0e -TAcbufuxgM3XipqsFF8liS68av7shTLAqzQPWmkJ+BTTDgeg1gx0rkyFottXu336 -wRfHE5Znasi7VXYOc3tsrTUfi4lCx2+NZituFYLUgGkHGBLjlrho8It7AD6gBbG4 -9xEDuBp0hn42sQHPJjvGY/EZQOvpeugkBL0OP3i1iIZw8jzD2HSs7CWN8C5fD5z7 -VaMpR1DribwT5gXamYJMaNBCVCSbDVsdWpSus1Q3dRuHAW/PoW/n9b0us37Cyus4 -v7Z1lvHYt+VSyNYc9cP6WjyJ6idjQU3WD/t8mpeVLql2z2EyLoBKFptR/uzYuR73 -9bqjlYlbx6vjWrVe1ATFkaz5PNd2gmBlmR+flxHGi3sy8i8vM2gcxeMFS8rkGL76 -83l/YjFA5h++1C+rh8nF/WQhXbZPTkszv7wCLEWjGbw/le8TrEyW4hSIPHAP5/oc -LliXF2GbBawOEmer72QxmVcrTDxpHZNWVVsuRvvsEknBHFF9wArmE7gB3Yycbw5N -IGTlJ0if2+KVy89myo46tECftcsRdnWJAhwEEAECAAYFAlCanU8ACgkQWWajClP+ -gjBvOw//VwHBSYzGwH7xAZZMpze0Czmj6sUEVLNIFlieZOOMrZw38vu5x5FkQ3zX -ZlXuLqZYyW65Htgm9sfn9o1YosZRqdCs4144v6W88gou/ptu7Ie4qCpSFZXPpAm/ -kJ+yhG7ixKfg1U88Vf5SjyJf1ZXs/31GAqcqM+DNACcrp7Tm7T0AmxL3cfz327gt -pP1oLSK8vl5iOM3TTYDjU1798y1uPdNjcIHuHXqvm5ppjSTGH8R6ho5/8CmtSMoa -5vl0GhVu2AiBRuAurF1CHUFLNdmaK5KIo5MLl88C/xSiH86bCCtEZ5c5r0h1Ipr5 -bScAK2dbgIRFsfXjICqfhysKZmj2N0fgN9y09MvljOv/fzcZesozzqaaxTmvtpGr -nA4bGbd7AFvkGbMuwVySqtDbpjbfKTG4uiDGY0oXDtsF14JtdFuJSqjK+Lbb9BRc -SMscuJspOA5hgzpRK07QqZfnEGwW1JCcnayqaNPMqYfDxTKULGZjDsPdWxHTczIH -B0aeDLNXej0miE4C1achm0Z5ROsdmsAQXvfW0DH0k164wJAupUL0kVZrYVRmh6F7 -BTd8Qvl0V9V/kN3ba4KuDA2ej74xNmA5mUCdDX1hsChkmF325TS0h3ueiq9bmTa9 -7NPsWIA0rwq6QXzD6VkO3jpaUP+/yYQ6vHscUyRLo8NT77lWUFKJAhwEEAECAAYF -AlCgLWYACgkQSTs8YN6ukBaB/w//XluXLg3puHyEtAVLFc25LGsHtQ7CwYu2qD7f -yL6203X80XvYtG1kiy7N1JfwMgWFvJPjmRwhSXG/p3iGkKs7tvR4VuDAZv3YhaeQ -btpZB0PRGZYTYFJ8AiuQQb0riUmgbGGNymcZfsK+d3137G0+SUYPIbqhsJkiQJbe -BJIJWz0Z+tTUGs7jguYoxs9JzmZWAfx6DgES/rMPFxptdC/I0lTxslKilQOEPw/F -qOIfoHFUIDJeUTPW9gwsIA1B9B70TIdKiw16IbfJjrcuQ9UxqSiSMUz9Jwp5pd4V -XxhkLChvwRv4/pGfjICT5xd04XHUNWhVB/PVLyK0LsVZtc4V6A2VXeS09zSadPbR -UaBgGt9TVEcSZoqy11L6wlphVXJpHT3BbmM27tp43h6PwRoxxmb95ecGQLonLN0l -9oLJbqH9Wf6ALBWsZWV4hZKbL+SwJDzAMJ3s/Sp4ZlvBjtftJGpOLNHRfDlKwzH8 -cg/6NiNn8GonrkP6pyEuce5HN3S83zEmm1R7eTtFjhNCWy/94KhMvRl4+clU4CjM -LcX8eNv6RiakICOIW23Jt+nFXnx5zGc3pSNiV4wPE+j1vrtIYV9c6ynhu5SEahKQ -bc1lbBop6kLvSFyZd/XKIhJukhaIYkobesSAJQvYtGCqCegvqf3WiIDuG7dVUnpv -eAUIskeJAhwEEAEIAAYFAlHW5GQACgkQyvs7rQp1yHdvzxAAg/RL1ucMMl+bLqLe -CPxyzXs+SxS2rTCdbzCUTilmZVwOpy0RDLYl5Y7qlMLI3m7Yp8AxpcLmCfV58Cd0 -p0YW19mYpFoJhV+inGNVU8uRV8j12VqRk2PwkSgYwjYkTtqCaG6H0sZoQYTRc+vq -itKLwaBIaOq1z+GCvpVDP8eWelZ7F/t8taYM1ndbJJlaX6nRy3WAnm0S4D3MQ7eL -K3rFYlP51U2fxD9aZSzSDjZuERh9yNUXB4AMZ4kQNCa/SaYOBEQmRz7d1m0xvXkf -Y8Ns7IIywtgpkRLHtBucn0Jq9BdZpYzsQN90OJDI6o1futdlJyK9f6Jo6Heo6QlH -wPFz/nhTAHdMOGfsjMciD8799vFBIDlksEQBDLT4kIwtDvyJiqMKSeLHdPYIl6nd -VME40H138p6vEtxUjF2BgSvP9r8FkG11lQF/cmjJCgyT1hYpK4MBURVDgcS+uGJc -FSO0kIwAzo+5sj80oczl67AWQMb4CoAcZGKtHCfMvDE+pNYwiJN0j46erlOpm7bz -So6i3H3mUiqlQvqGOJbpMIlOdt/QwwJFm1Hnqyvae40XHdin+Q7QH6BHVyHSZWCn -jLdH6UtmypBp3aOPM5RtuXmDOhQXhLnob4e+YX6j6h+0ID27fiVJGOh4yTbUvmw+ -0qSLgbuXvRQvUkaudFjmzpf4Ub6JAhwEEAECAAYFAlHX0cwACgkQ38Zx7rMz+iXG -mRAAkwzro7d/y4O/p21mzv5LwI1j4V5oH1phzsZuITnprD8yhgV+ExPuDmz3e7Qr -URsBzkPUzCEhxl2NzbBGw3AOkAwwxeNZhLKNJitzeopSecjqEMsSbt/VJCQ0DH73 -cGZleCOfF0qtenVYeDejY9KVJwrkPl96QlZOLENZTXSesOz9KE+KZDq1lfJmQacu -8xw0qRiK3vhLCKG4MNVamzTipmjPbDYlQAaUJ3qTC8cb3dbvx/RnAJZ0aU+zfjU7 -Tuin3Obp553l+q1xe/3tD+8dIgn9oV9NrD3jU6oNdx4EPFRE9FWRLlsOaKYzn/Ig -/Vhk5Q4Nr903cfbokV8qrfminVdtMXuiC+9xLoxfN3fq8qSF8DUyQCE9JWm6o+So -GM76X9Wkq+xpTOIyTGLz6QZlLWcOwqM8yB9U5w4gehRJZ5eZdAhjRSVuA0/SoZMN -wtqGIOshhfAJb+Nubmx+R0F+UBzaJ1upMVSXXDJx0+9uxHDA5P/v+OYfHHhmXDTF -bmPI2MAfJpFQKBCypRggzaLiLcW2P3K53ruEivfkFkMGKLz35EqaTVA0HsxXCVvb -5ZbCbbAgUW6kuQ07Bs/7JUxcQmrBH2REh948saQLfp/MYT3YiGMvzR1sqiOdJzNW -tj5neOH92c+yBBit+r8N2TCpfaDmRVbwBkdGeGBKU4bvKR6JAhwEEwECAAYFAlIT -sLwACgkQzsmULbetuG9NkA//VIaDDrduuokzbVM7mbv19h0aQPIXaszm9okCcDmb -av02yfOaEmp+HRwoy0Gaav7E/9G10o1ynxMbmHosz4GpajC/YOuIqSIM/mvJCU3/ -vwrIi2z3MGSpQMX8OgXgKNoUE+wOIR+o37TkxIX2O0xCh9JXCz42N+xH0qtfwo9C -Zk2HZkictXE/laKmT6v4NLM6DCGb6gA+8+LO9Vm1kylpZHZowXIKI0zhrXQZBIhP -jB7IX4l/rC/sqLJgnlXDJOz9Zx5OQMGqX5/NI/Gy3elzBgn/ORd/KyZ61ODv0Ij6 -L+uLmhkE3McoQxdl19h9Ig6xX4JjOnoDsmG1mwX/i1fCh7rz+Yh7Hwm8EbwkcX+w -RanJMGzRqt7IwNlyj/r1Lgm4esScwTYE6OFtWjhG4l0sB3wKfogsgd2tx6e81GPl -DPC4Mwb8CCzg95oiDR+buBDPGmnbakATXQnnAMFyZEsAxc9jj9+7OZlwkCfBO/Rt -rr1Z8mBgGvIpV1q9xnQ8qfHKTbU43OjIs7bTh9EWZsBv+xH4/GG5EUhCEvJdzTsm -o9uUXrUzwrGPt+l77vai7E3b3K9Cfxr134ER/BO4S5c3LJmXf3s9EXhsoJyhfPgP -J3bDGK3Ff4n7jIvj/LyzP64UNkJqXLicsUAT8Du+C/OUMQ+mF5LqQz2y/9bm9s3n -FvGJAhwEEAEIAAYFAlJuWVEACgkQbGWA53vXVsQjBxAAzE3Z6tNSCr9d37HWGfaZ -C4CtAHux6zrluSuZLsxcqX1Zo2PkP7SfVwYM8VruGx/+TL1zekHHAHIvBZbe0ra9 -MzK12Teq9fn7S+k4gmQzEGQ1Lb11EAqfNwWKbqEk3n2atF9dt3FAfT8CDvI5jMa2 -KzQA3ASIYXeDL1mXQkJ7o4VJHOPNGAUvrulPbhBwAt/dOActBl0s+ImR3kPpQ871 -IX8N7vPqJEh+P6QZW0iV3Zb4jXNlfTs9ijSR5t72n/Cn+a7OwkCsFjvUj26fVm0J -shkvX/johzHdrfcG9CuNunQiHKx9JU+j6pK8/JB9iCyJBNQobdAiny9BIphobQ3V -1MiPKosRcyOSlEURn4GFcxnYq90kvfqsPF9iHMR8dxkW/mYGUFQWhixhmp9yoBis -65K2utyX2ZcTHyYjXZaJCBKlm/6M0YCGIIYKKo+uX8FvjpHmzewN0QMXTgPrIn2U -4kr7MYwy9NxksIjmPikkBjWr2BJEpJ3gCho6oumJ75z7R3plk2aXreLqPWhWHEHP -mDopiomY1unsBbn7aB/4HujZBqPt0IF/Z3yrINQWmAQPqjWjcY6Vhe4oWhnKpqW4 -QdrXfVfYB6pt3ZSFqKZLYMQ26069IvBssPHyaPUzt94o+p5YgHsdq6EO6xY+ww6R -xawCRdypCfuAbPLn4pV+e0GJAhwEEwEKAAYFAlJomZ4ACgkQjYbn+uXrDBA9rA/+ -KLDX5WV+1jKJczuIN0W220aUrzMa3+bjoFvutpZtgZkLIenWbSAwTEBqrJv8G+dh -fPbyzlNC1qiPWqp2ncj6p5x5Xn396qWF9IkWqgxJUeX7EO/XPsn4s1VRjCMJXos3 -GjEHdSHaPfaVk7TKprrZzbMj8lHNESfZDSSeBz+FjsNwb+jIDbN85TNFZxTBbKhC -oRIZEtgYQpibl0rri/AyiGAsB3k1jRMFZsJGFPq2F2SJ5B97VlM6u0PkwYPgDHVB -fcWD0NMzkh9P2iBHK0VcaUOd8Qnut6SXjcPYiZR+g5g0NE0CpgftgXip6D7eJovH -4f0TQL9STZ8UQItnIpWAH+LCghP92LpoM0Kic7aln7GQi35kn3rw2pSVW9Zwa1po -3uX0gz7ZqmZ0Tt8qRG+hUIdqLVNWPwXLkAYx+NZ/K828L14sJ9M+Ei+4xpcBwYrA -CziSpAmauoYub9jhZrZTPuEedw124EH61vc2u+swT3SgFuDHCANP1B2++DXszd0K -purI8n3KGNeCUbiKsMgYsedp7dqGBPke/lpK+h4j8WyfllsH0z7+GMyBRJ9b36mB -2xCW+jCwn8sRThcVcy0tZal9756P1hPyPch9geRehZe5BaenNSkE6bMcTb5D29gl -+klpWvxVKAXEOMZi8UICGmBP6w6obN+FuSyeDHxawtaJARwEEAEIAAYFAlKQ7dkA -CgkQeBmwJ/GwXjWxbQf+LXhu/h+o2cgyDfTK+fn+K/lsNCjg+AaKicOlGLm8V1uZ -CdHBkfUAyJ7iLOEXdJz+ttFEu4qEY+klhycriMe7PREJQ7tJ7cahVMSRf9yNLrpA -01pcwO/+gC+E2O9JHLBo/gKsQVHxO4UyMaWjg279snolvUWw1L2t84e70Gk6Kigp -inmd11LeLaFDyXxCyXTulNYfgMMMmWRICO0iLSSMC5TqGn6a67N5OaSY8zf3oPON -7j3Sr+f80Vo2o78UwQPglv7rlm3I5EbJK0Ky72X/bSAuF3LBVpEqkaeAgy4YZYaZ -/YMDADe1+MPEPqInA+pTr8pqFLti2IxjNhCSgkrys4kCHAQQAQgABgUCUpDt8wAK -CRBl6yqxLw7HindlD/9K7D0PSCv6CFh0CkGiU+Z5wwWp0OjG/EjU2abYpds3IfdV -HJcHoY3iwGQ6EBvT0XcIf3/RPESqe7M5+J9ZKB2/y8byh1pgNE7FEV3s8HoJyoLB -f28l1kFgnhAcr3rCjyTdQEnfxgHIVRKfAksAm1SkWonOdTA/WIzDZmmNOMeS1xL8 -VyvVXLfv4qMzc/jU9OexoTw6cAkxN0fBtHJVoYnFg0yvDk/fbY8fDiPtkbhBP2pL -agCuGtXti7hr+KPjpGVWCGTxYRgtKjnAE086sfGldvzlLGNhHxsTXtE9volH6Vd2 -lgmJnfx3zJ4bLQqommHiQS30tOAcsY6H0bxnIJqT7v2focP18RSXOCndCokhFki4 -VHWp/sPvJ6JqyPeQZcHk9usZuHh5idIbF+Kj9UCikN2iI9yuY2EQnr0PGQhmcweB -7+nCu/P1ED46Y3OWD5U6ebxGx1pJQgs7HHU0cVbCW1wFfcnQfFiLornd2gdMhhRc -ZaVsVXV+40ZcurNB5MnFbxOmMdjyXokhkEJcATJ4DDxrGANMsGS3RuVFPlZ2uIvk -jtF5ibegnuA7Hj0bVcquN3DKioeSVQF8ZX71Q3VhJT15dFaLnsW9RcTwmmCCh+Su -gX28aKr1QcUn433z6fP5n/nEA9ZNW7SBFp6gKHseeRNIQyEuOrO2i75z+nPdL4kC -HAQQAQgABgUCUpDuHwAKCRCBnSeKDm+ZKvLLD/9KF3Axj7gBpscUj/hsHE77QdpS -awZkxgNlkgckcwtXxP6h+bAHikvN8sEt4SZ3Cn5gYzlYc0ycsKTGNDplaIthuqiG -oI20sjY7uN6GxMNPmWl8X74YiM80NtyxJ2pET2xsSNVPobEewol1pRwiHqBKgXiO -VB3QoUguU9PyKYzFcozoirY0s6tbalMdc9TeOotGZY4rp68hWHkaXj7bywCbuhYQ -wg17S5Hw8N6Q4g95OnAcbrlIVV0nv5AXiSwYmvr5GEm6ZLKGYsmoDYLL/KluR1uN -KMId+Y5Mrbhb0BSonWKWc76CvMBQG1XIV3JCMHiGEpsCfW2BnLKcGovYGTFoBR8w -6Lr8UydZnkgdvYa/rnuFUSvbwdVtU6qD+tmlixXRmzU4YkNA0IugLnZYOdzzBemr -o5PMwb3qVCftNYWl5VHJXtjWbONYKKQMdtYoK5CR2IEVmMRGqxZ7Y+qfcgp64QF1 -cdzZ6k4yNVder2VMzieMtK3QWCiev/KZsdqFDcIQjk6t4fyqbwSQ8QD3g/OwF1QD -LiafKYKArSflCmB8QkcNzUl1XwVErePi2IKaJWTvhLIfDjGYECJE+A7hN//+txbR -Q4qHcMVFNUcvlorsced5QmZUPr3paKMf3YJrQLiwfm9BHA3RRIoP8jQO4jEYMvhF -g7j17HPRtKPz0AWyvbQjSmVsbWVyIFZlcm5vb2lqIDxqZWxtZXJAdWJ1bnR1LmNv -bT6IRgQQEQIABgUCSlEkEQAKCRA9r1SiHu9SdhIFAJ9CibD3n8lEhLgFs4e1uv7P -d/suXwCfQVTq//OCEBBnJZ0vfhANhQBUmX6IRgQQEQIABgUCSnK2BgAKCRDU5e2s -wBQ9LVDGAJ4nlJ7F3Om3JEXES/GU723KMyXWMwCgiVqWZoRhfqgD3RsYRtApA9x1 -Va6IRgQQEQIABgUCSnVzuwAKCRBp0qYd4mP81Mv8AKCQYUPqMSM/ME51ToAlTlZc -tKTuPQCgj0W1B+FwThfs5+aKdC7QaY80eBOIRgQQEQIABgUCSnhdNQAKCRDVypsE -8sQjvDTYAJ9wLtiVTSaCqjVG+yU+MndAhjVv3QCfWx/hHuU9nSo2Sg2c6o05NMs8 -AoGIRgQQEQIABgUCSn0wjwAKCRCEibFNiAdSm3BjAJ0Z8b57W7n2L5RW1yDrv9KE -mSwaLgCfR1lI+swWA08vYMC/A6BeKJ1+eG+IRgQQEQIABgUCSoKbhAAKCRDjIZO2 -xCm+LxdtAJ423GSlMKX6KQmIOj4YRKJghBy6dwCfVBvX0bE5fRNVodjup5E4yM73 -WVSIRgQQEQIABgUCSoLqBwAKCRBBIcdDMXDr6UDsAKCDbuvmwHqosySmNA7Tv8yx -asV0FQCeMEgEy7zvrq8Z1oHy5EIgURt4PDyIRgQQEQIABgUCSqQySwAKCRDAnh2J -lZMO3iFhAJ47Hb0ZmWIvRhliUOSILBxTpUYEzACaAtE0hXRex+iBe8Aopj11O3hr -idyIRgQQEQIABgUCSqQyeAAKCRAEBGuFSi4WK+KoAJ9vE0+Lzpevm0jBCZmjS1QJ -paZoLACeO8VFKQOosuAQD4D9xa3qmrnYsIaIRgQQEQIABgUCS1aYcAAKCRB3AbuF -iXrzo976AKCeA52L52HO58jNvEWgUXlS8K7MTgCdET8UB50w7Peoreyv4zHAo39V -1AiIRgQQEQIABgUCS1e1sAAKCRD9H9kjU/RdMhWxAJ4nuzfeS88C2YYuOp3v2G6Q -c4NlHACcDjTrdaKwyhlQiISV0OCGssvDU+yIRgQQEQIABgUCS2YBngAKCRACvEK3 -Q+JdHj8mAJ0SkxTQnXGV2eATkcXNZ/pe6jYEGgCgjvYR3QORWd/JhvNAk3ji3HW1 -DiWIRgQQEQIABgUCTE3s/wAKCRBlHfNSPSPyXU0UAJ9ptKVH4kreLFjwBGLb5vda -iH03lQCeKoVlz2xQOgFxy6poPhbY97GwgFyIRgQQEQIABgUCTE35RgAKCRC/YHCL -SEJsfvZRAKCLgfqYB2RRfTTWDWSuenQkDuuicQCgnHj9ROFxjVMVoNS3V5N0frjp -QDmIRgQQEQIABgUCTE8GbAAKCRD38OcPMH1W7ZYgAJ4/w8965q7Sz8z4VEPAxgOt -JctFowCfXmOofnA4FFhkojjzWKGgDih5UHyIRgQQEQIABgUCTRytegAKCRBp5GJ2 -T8WeRCOYAJ45dywq9sJrMEn56D0DpT4tDlZh8QCgoc4lxXHAN3kO+uoCWvEeM+jh -DEeIRgQQEQIABgUCTRy+lwAKCRCnGmt/a4UvN4/gAJoC3T3a90TzKXMlRqPgoCTZ -kEiuHQCeL4ZczXNmcG+T155E5c88KNuSfHyIRgQQEQIABgUCTR9IEwAKCRBBucon -hIhpbovGAKCXhicXOl+vbB08Ic7pcKqZyLWjdwCeJYVlmgWNIFtJOzw7szvVbiar -Cb6IRgQQEQIABgUCTR9KbAAKCRA7jqQfgvYSQGfzAJ42C+gp0gQCOxJrPKj1e0db -ZT1QiACgrrgRO9/SPnVqG4+SfkmwtmTo63aIRgQQEQIABgUCTR9otAAKCRCITtYV -NE5C42gYAKDJgc9TPQmEU1P+4rHy1nYtOWLhDQCgixkkFSbOiFMqu1Rdfeqw4wSR -cf+IRgQQEQIABgUCTTu6HAAKCRAL4Kr5tSzLUr+tAJ0VAW4BxMttROt/OVpH3ek8 -LaaZfwCgo6xQ68P1kO+FPF29u1ZU8SCi94mIRgQQEQIABgUCTdZizwAKCRClBubU -3U1QiJGoAJ4gWeMxPLldN6e8ePjNdTJPUU8V/gCfRUs9cAi2y4v7AkszD5ql6AWw -1f+IRgQQEQgABgUCSnMJ6AAKCRD2KOuTR0MgbArjAKCnybTyC6kzvLnyO6cy0yZH -lRL1yACcCh+BO0OHBnwSHxzaFQPPENszEhqIRgQQEQgABgUCSnRCvgAKCRC89sYP -boFp0lsxAKCCrv2Ko2BbdPZP8QSPV86fykwgsQCgvDeQI5aFTCaA1ucVWv63MUR4 -xoGIRgQQEQgABgUCSnR3PwAKCRDDdqja8bzbc2FZAJ4nzN1d8qSxbSRCxQNuQ1gR -52zIYwCeJfKSAXyMrVivKDBVI8+e7zwv09aIRgQQEQgABgUCSnR32QAKCRDZRtVq -COOLvIxiAJ9Vo6wOEbo+c/STPTx6aUZCJsuY8wCeODf2rDmcmktyEUMFaZRyby3R -fhiIRgQQEQgABgUCSnSc2wAKCRCGvKv/HADHkKPDAJ0eVJZRJTeCE1Q4zBoQIpaB -hu0mZgCdHeq2KIwAw+t75Z5ikAGVqVXZztWIRgQQEQgABgUCSnmQuwAKCRDU5e2s -wBQ9LTaGAJwNoQb8OKF2B1loySqBFtR1OY9wPgCgpbeu88Spzd/zf6rUnqx91Jri -1jWIRgQQEQgABgUCSnwzeAAKCRA7MpidAPPP5IYOAJ98Dh9aEz7aZ4UaWcI5Pcpm -sso2/QCfTUf00yyxs1XO/JNjugF5xW7Ub1uIRgQQEQgABgUCS11JcwAKCRAGMraG -igSbIaI8AJsFuyRgMyDiJ+wpauFAfGk51SxOPQCgsCuTNExPnYlE2CcF5YeaHgb6 -vZSIRgQQEQgABgUCTTvTzQAKCRA5YGZPleoj3c+RAJ99FfaHtR3z3VP61Tka7ooi -/UbNFQCgqqkLgPlyrWeFEwlmdfvL8k8PuF2IRgQQEQoABgUCSoIM+AAKCRBRlIML -7BmzeIQ3AJ9APtHqf396SADYPLUNo0PpqYqPjwCfRRpEnf2HP3oWP7y4iGpCK6YH -yLeIRgQQEQoABgUCSolsygAKCRDcNVLoNudWBO8TAKCAfEqpIgyIU+UlDtdkQugh -iIPStwCgp5dT99QQA88Aywi+R4B4WrWNpImIRgQQEQoABgUCSrT0UgAKCRBBIcdD -MXDr6fAEAKCWjnOTcYIiOpRh2d7JD14BJ3W2OQCdHm0mUZnx+uXZzZsRR8YDByO9 -hA+IRgQSEQgABgUCSneG0AAKCRDa2nnNeIo/TPJ2AJ9EfBtYYL/nFw2egGkaV7vQ -N0XwhACeNi68BKpOozA5Egmt3olA3R4v2tGIRgQTEQIABgUCSmyZvAAKCRAS23nu -xHY7pSIIAKCHlV8REvnA69DVWQ+gtcHu4pz2iwCeI+fhPzEAsxWPEjIe8w7UCyxC -7imIRgQTEQIABgUCSnFnDAAKCRBvF6WvwfJOpNR8AKC63NjxtsXE6Mx1xGfIdMMn -mh1WWACgqkvhBQBmmXWHvY0wGmyiaFBhYE+IRgQTEQgABgUCSnTMwgAKCRB8O3lw -iMfB9wV3AJ9h81spG1avuad72vn38Ceiin63lgCfYhxn/OqhR22Z+THYzHHhPqwT -VuWIVgQQEQsABgUCTLrM4gAKCRDyrYWsHkKzZxURAN4uWaEQARkqmxguwlo76hTA -nooun5XOdUuHoigjAODjKgs0nTHHYFz1JoXBz5RZS5rbxjFSY9eWX0b/iF4EEBEI -AAYFAk0d8oIACgkQvmxYQ3X+tBbP8wD/SMN9EvEFJUsqeV+fw0S61pzB/aEWtfxT -szCDC7p6i7AA/3KpE/zyKBd4NYuWlFmnk1ubwseBojKpjpEOV8lQsGcUiNwEEAEC -AAYFAktWmG8ACgkQwqNdSiQ6Eyng3gX/YQEcWx5Qj1/nqvMjbvd4bAH/WJXU3A/x -5UqAo4QOQ8rpLjLfBbx8W1FS9Zton5m4eY0XjqXUo/XXuFW63Htbdbf1e/IXNbQX -BCGl2lJX9OtzULMYZ7hbSMT90ecvUHlcizGl2SCTNAEmhLrlmoxoliVc44ftl6o9 -oCFEAEa0o/G8ipQ1iE8NbLbxm1cPV2SLdrJcktz5TRtfFBdUY5J7LHFcsOxAcs7+ -h/BT64zjY/cFVrRVAdiRyPOcwZD5tWHkiQEcBBABAgAGBQJKdzLEAAoJEJaA6+Sp -kZPiJR0H/iZePhdnqnMoE9/D8GnMdTd7puKcawPlfk0HT/fR46hM+Bj009b9WSkw -Wbe5IuOXX5pFApAc6ZPM7E4NDe845RYKsLTVAFD2j02nVNZMYCcW6V5WmsAhiVWz -fG6P13QLjD48CzVVUAqnz0afRXZxxDyWdAeLJ0F9LiFBKeZdNyQjSgquqkeWOCZP -uZsJz6v+bX5NI4TEGrsf90xkd/1pYMNaxeATLNViqYhlzHdbTkTqzc8MQ6EiaBv9 -85c1e7+mMIBnYYibvqZFYIxGSuJW4fabTil/1pA/qFTXhKEEkBcxI97mpDfIMKZh -FXi9Z4pJa/q+3hwPJJckk3GezrhRoSWJARwEEAECAAYFAkp3MtEACgkQMfzn590H -lGHO4QgAiN1otIsTQDC41FQ3Ya2fjfGTkbpdNMqchuEC5AUSJYjAPl45bVhZB9oe -Vq1qwOZKh2dDedSk3LjWKJMdgQjjYT1M2A/0iJDct0pDkv3B5kfnATUBuBKCYcvU -T+HX1zhfv5eqiC/sLyS/lWWwTkYqHyY1+60lXtcOqyHu/3mNS1N0kb0Dp07dHqrk -byV8wN7yMuVMERoKjOwpMe8/B6NqoQiOglKGyhhUbQtO8eBUA57DIaxtqDtggjA4 -NT1URLrmDezLMsdyE02k4Zk+gpdjtyASG9xPA1/3H6cm/V6r60vW9n8GXYyv4/5A -1xzGE3TxICaZtPMclVddLEF4y8ANOokBHAQQAQIABgUCTSNA3gAKCRCsRu/m3lAL -PgswB/9tq6v0HP6/7NqBXxwM7ycJhhvN+El2jfwe88T5zOY34i4LWQ4BaIwCZcd0 -yLuxfDh1QedW2aodaxJKDyID3DQOlpoOfq8fR5nz6WOZCSm2DTrkmljCDo4/oWJR -gY5NcODReAen7MZNTy5dVXCjLJqs9JPJVJxMJqFYz2R5NE/TlZwW/vwCcBDrdhFu -kooACJshv4Y12/xOs0w8nvAcPdp8UhFisZ17bYxX7uQLahCRyW4gUlndxsvu92AJ -0LkA6JPz51DBsCTHeVDMh7mQ8DiODZ3u0JokoXy9EpBmIL/VGR3I9DDJTLwEv4hf -1+O7/LZd4ASEU/G+A/g/PAS7ziGpiQEcBBABAgAGBQJNJd23AAoJEC3qwIuQwOaG -QlIH/RLZJQRVSGLU0eRsU5QgcNFIugyPMvFbPG8KbJUdHxsI/qyFykMNFmCQGRMR -EJZGE8C6fJKhrYHjuppXUyqA2afNC7ExrhpxZBpnZ6fiJS18oRo+sdFZ2TPol+/t -r+BF8fa8WcIB/G4w0S8JLShaAZECqN6A1knredGVkyN7j78pkXrs83N53cBAjS7u -f8WYJMjdTmlfu1hsCZ/NirKquSkL7owydhYZtHeZH03QRR32k5/7bwfsZde7c0il -BTCPQPtSRFwtXv7wcKDcRhAfZKcESqRvy9qPm/51J4bPUh9/yO7KR5upGEdzyQSz -nnBxQK/ajNza8E6QyQInk/srXyeJARwEEAEIAAYFAkp0MGkACgkQ8+QSLx2MJhqG -XAf/Rd4cnWM1EG7xoh7d68NzVauVifCeLI0Qz673W7bJ21GraI+/PjMVtSVz1EPQ -fp8mbOOJEvST3IwW8KTzEXGIJeNQj8D7VBaguG30nhqXI3L0zabiGPPA1STMPbSj -GVMiFxaZCM3/PUiTKX4DwhjW2fF2Esc8Fh2HLU1fwijO9OrLDHd8Mj+G/PfrfCMU -2c8SM9aXVnXCXV9Cgdd0aWQxc/au5owQlMD7xHdsGeJGAUcAn4etC8rwheH5gBXl -31Y6YCfVgXRxntJLkoZbvvWuXtpBOZt1xDJ6LwXXmh98mnUoOfQSQH3hJoL/PaqP -OOWw+gt085E3eRv5t9J/XZS9tIkBHAQQAQgABgUCSnlf1wAKCRCWgOvkqZGT4qjc -B/4gUYThoOoPMxtXRPYvR3DJBDRG+38BKNmLMkpQro+l6C4YSwK9DpvpDqUOLan1 -5XdkT1kjvvZjZinLxuWQ+rWAfmtR4BAcFZhgNonoLvTJY4jvfm5EtitZjXA7Bv+E -mnRRblryYvPXKybxvapMgz68T1lexlFi0+eEbSSQkbGyLkzxO9TWTjBbxzNnbZRK -OkODGss4HChQq9TzHKzDvyD7cQGLgXmCni0yIP927sidPO90KiTbn81x6h/7vm4Z -NqGO1HM0PYsNsrMKhYMLydKJxxY9hzcb0lcLDZOvs63UMEo4clKLu+U4jMA4IOzV -DXwpvrerM1qTbJy6h7FOvDefiQEcBBABCAAGBQJKeV/cAAoJEDH85+fdB5Rh7oYH -/2iAtb8txBhJN9x2suroEWgUTPgNauV3MF3r9gbLIPusAYGgyJsp3l1yplUYN9Ur -WpLnEtWUJbAhST5SnPWr1YguwVEsKvFj27gygIXpaV/QHJAnD+mkp0C11IluOaP9 -XpaA6FGdTFBbrY1nW+LQqGhGoWIN1lAhdiP7q8M01kT4KsWPa/YffLNYchWhenQ2 -jd54BbYKBf+kpnbdEOT2rNM626sjOWZfJ0d0WsRJAlelaYprnbXkpQkZfiV6zp4D -jzbF0kOT328iujHYDKUIwBTIF1BwBj1ZkdzSHZViHhi7x2JtaxldEQzXezyusOo8 -RmKb8jT3Sr3YuWsMt1Wao+CJARwEEAEIAAYFAk07uh4ACgkQ9+5hbuDCDP+m9QgA -mzb+vNKrte3R6DZ8RGq7g7Q345s8RisTnWrrrMq35xhq+n0AjGuyM9OQBM79+6bz -VqnzH/xLatOe0ZpGR0PVA1pZdp+DuB7/JFgmT0QRRcub0gQ3HGEz4ZU9OJUyyzwq -LIqPAU6SPVZapiUmoDWcCM86wydb05hoQTSnN3xBnU0jTTtrIgLD+ClK8Nn5R6DU -5OJ6Icz82sGXTqtCVwiHmrH9nmSzhFlQWERXVF8exCEq8/E7mstp8QA/6pmtikP8 -rlSHt8mGWk8deKG1K5EuqJAsWSjQm5Z/j++/i76s0b5A+stIvkFzP0OXl/UDnkPu -R5YRK9cQcs4cTxIwKU7J+okCHAQQAQIABgUCSm4YdgAKCRCi0+fPUhNGtKbyD/9d -0giV8yjriJkRESfOc/3xj3p1/MWHvlC0fJ/JZoCA1/aFCcT0NNpy0r1mrbh64apv -WdJiAcW/xMDnDttYG3cviYQTnfXRe+YtaqXEkdTh1fENh3Su0unji3sdxXxrmPTQ -vQNoPW35H6YvZSrqlRM7n7GQzsRcDVxVP2Z4IM4fkFIc+69pXzaTEqkdNqdBoY41 -tzNbL25LFfodkTOFrh6RNocNBu/d1LdA1tcrSCxLCNT/iW43xp46Yh4EIdecNx1k -YqWW5+WieRC7nVvp3yARsSg2fpRLC9Ed3nDpV9FQQskiRuXjAb5nAtebzqZAmSSG -xgppDUXexyIFJbKviKx1Pp9v7E5hdgyjf6LiThBLiyqgJfQv5oWRrBA6wgRS3UmE -Q6voT0nIu11/UXYod/KozGyGVg9cNWFf20CdgsJAVyU2iQtzlQdHGGPjN0BOOEJY -JvLJexFTMOImH1JbgIvyzgJbLhr9FtxRPsvBh5MAoWLgz63mHvIAhrhcUB1INAK+ -Qu/uHyyqW1vwB8JwReuiNBdb8XL4yEje9J/6FRL4peCL4HB8Wmodlb0xTeoqEOPm -B+5WxAn03AHKXyKyWRPc1TSknNN+HgIS6tqLx14tJcQQ8bvK9cTcObogLVy81etk -K317kqW3wPFgNwpRpMNmrtkLN57A6icH55gPhkbRz4kCHAQQAQIABgUCSnCX8AAK -CRAsfDFGwaABIWaZD/9lZegT0851xuQ4/EL6Wka937hT0BGw5XuXZ7DrHadhU5d2 -3itUpNKXXzUdW/jzuKHBP5O6bCb/kgNWBoYSs7rKzRYz6uFcTXq3ObSgmZ91FfZ5 -CsWLR5/tEEF3hBLcmMrBdd1C2BNzo82zNRzoQlS9npYaowsedmYmPLeQ11KfKHrm -Li5neum5kYIYRxBV8CDeiW17+ETvTnmF1UknET88tffG6K8IuD1igaY3vCnrulRS -gAWgaZyAOiKpmlLkrnmI8Wd+KQRxJpdhMRxOlMUTHFKJU918mYNmwP1AJp3r+rdU -R8xpzWCUxwUY1PGtt3vBdIMrKCW4+fANWsJR2Wgt9Of/A/8bO9sbS1uFj4HaKA9n -s2E6RsZKhB2kM0SOtizshkg4roCbPKw/wk+BKxaDiiPRXt1pS5fXFafA6H02FJHF -Z5cm0GTakSLckr1JkgIsFoYZybHv+4st1j/UzfLUnjc9FqULaCiJFgwsTUtxJNqx -E9E+TLwvJwG8jToT8Pyszp5QtD6rNEOL8ljwmS2Xu0cKf9KRM2JbfmJB8BU/9Gc8 -VfFfYUuwQDZRFbJYPQKCedGee3U8f/0PnuOeJsToQaFMiitWTtZgLY0pbFP0/eGl -u+B+UYruwI7mHzdwuuXVBV1iYcM3zmIjcml5gD+P1lOeHaynLRI9+e7kxDP5cokC -HAQQAQIABgUCSnCbJwAKCRDthgS8BvWpHwNmD/4u/76PSSGGZa128xlZuFLVmCiU -We8Xsg7Kz2/rDVv8wKvDHUrMhLqo008ihIORrogfGR34hUBscm8hAwscr4VmMwBK -gY9srNQW8Zdzleru3lYv5GjmAnYmL0s6mZWFnTNKA4qPtxLZcP0c9WkI+vQmwxeX -gA4QN8Vw/hZ5R4cj2eNiByqxGSPV6wRb9Es4QrDevf8Q6zTk0YOiBh7HHZosPHbQ -Zrb7nKQY80YXMzTyDAKpdWjgKit+tuYqrQdNIgZL7dqoqwso8jJJwt9COhn5a2WP -Iw1vLdkZs332TtpgfC0OBWSGinqkKV2EaPQNpKWuGTChM5DznKuFpKkEEdKfFOAU -RZMu5SawNSRWRqB09l7MSElfOegon1Fgd2ZDo/Vc7GNy/ihEKZUSvWnKDQTjdOqQ -xhukTSh89GIMBuU25HAuObDK+3hFLIR6Cz2edMA/J6sEgqZjrDJKvseBT9bs/vkb -iCtVkOaN6f0iFqWwfGO5/UjpQxLfgB2jx1F5B8WYbnCJhKh+TRx3TTPOcCF9tQSz -Yq6S+vxLJYGG2RzVcHtlPrPieJF0kPp5wk+ioLnDyqvKhdlrQNUDDJYsJd6D5mhw -KjABQ2lGGFlI995DApk79BvTXHH7ClNuogj5cMkH2m0BfhQIQ49vDkGLo8jw7Y3a -Bkso9C/PVg6j08bZTIkCHAQQAQIABgUCSnHlIQAKCRDMeYZvmUCQhOdtD/oCM3M6 -gF9DUxNTEINlxcFjtPNcgK4Okee8OqhGG8VALf59gTRDra3CLqaC/7t37CnLD8ee -kbHI+aJruuUjoDtqHe+58lbsRhAlWnzDHofO8lGMoC3Z9J4AFuVTo8XY5+WsdLR6 -xuelLT8KMSjfaBDBrSx9rCFapg1V0uGKb9odmO7TlKPEMw4UW1DjfDEkASD4P5KK -sxLK+4qOfougjfnKw5RgBTga6sDMYTX5L0cDLiuya5ULlVcf5lGqCzNkwXprs5rb -swQG1xzWz+RAKt+oenG5R0ad2cF5GrHA3VFhEEHUPo3yFuKHGfHJHNJeQ95wNSLx -8JBfyAGf1MSioKPhbYvFE0FJYQ6CKZgPNIPlntP9HJfHlzOBUCz9O7/gaiV3zFCh -3sTBGfFXODpzCztBHFIpRCi4EvMQ6Y28c5LZcGee2GrZV/9ADTS+K1YEiBZ370cT -w5Enk1pb6USETa6buHbR2lDkBl8ppcA6z1bamEcHgFxUHTSzzj6lCB1IPAQL6Q4W -jg1kNjJRP99NRjRb0kSuM4M/8WlJwI/Qyko2qRd4/wmB3w3P9p0Vyh6IZTXh+Eil -MTW1/EHYadfX7fBkY72ZpqMJk4V1WBXPv5FNnEO4axI03ZMAc+Glz85nUzZl3GIs -QjeqXEmmyBjZpnBsB6s8e2n2C6/+hkn+ErCWlYkCHAQQAQIABgUCSnK2GgAKCRCH -L3AsTW4lqFJhD/wOwUeVY2DYvL5bJIVTgJ0jbnG1WLtOYx6qPnw9o/FbUf2tTuIT -ngdLAN7koz7+PFMmyZaR8cC9lTZVgT31p5FTOx8YlV7D7FGe57+23ZRS2el2NPEU -hSIos2s6l4dOesuLn+gvGLjoaFpwdCPcvH0WloderBc7dpN+o6rgbMOMgHQu9ucn -x2k6O2a4EgmwaoOczk0TZDNyz2zJ/n/yTHEXQltjE/85/xcBUXU2GJTiIeq44ldR -4jI5JgcRvdMISSsX+0zlY+zSIv/xMx5IeOzm4RMuIDEn+xbENXtUX/sUm+szm+oa -9PAejKjSXqisakXdJlgUZan8FpE8dkkRj4+EggiDd0itxJJtpYb74tuGdrNMa5iT -DP7IBC83QHPdFq5JMMVuUTHDNptHCNI41EwHqhcVGbi/FaaOsI2zjM8t5Yn58pIO -tCGjAfGz5F65wI+XcY3616+ESUscASFFktbLGP6gw+8AViQ7wdlWLvooXPHCJfRE -VanxvUqTp3T5e5Z+/8oMbux5Fj/9x5aGvGIS4iJdSKho95WIWLp3dQZQUTUKBoTV -Mn0wkiGsiDBUXVERfC1CQFu29tQ6Y7TA0kLGjYTrbs2BL0rb94zZs5FleKoK5svG -n2Xia4QJhiKNgMlrpOJj9xULuIZIbarZDb7V42T4G11/B3IIM42omXL5/4kCHAQQ -AQIABgUCSnVVUAAKCRAdIcg9xFLg/EPAEACK0LviYno+BZui8oNrO6jY7r3T9xe8 -1bqMoKYYhYcamDbJgWwadJ8JKoYYQtztNE96J3nfAZFTgVGL4XvsxBakzpFL78Wm -YDJMS+huM1ZS7qOx7FzgloFfXOZ9u47IXkG+cufVrgTNh6vyRXpvHZqWAHXg/sxD -HjTqQtREpv3yriKyl5JpVmah5R9MsFcq4/QLzCo9lqOAvwb93LOiySAVq7QUkV2P -m6sx0zTAYtJ4Dht4qwtz2EifKYNgqbq4AAfF+Mqb/253oFZSEJwCBBO54zgBbclh -YgXKSIMdK0viGsdAymQJrDoPeyq5F5dAj9r7Y/5jdiV6/cSha9IBedRTZq2KHNht -jyfeKMr2GxxP5uX4lMrOt9y6QhWuGSdc0WUHt6P8lyGUQd2wqLAgY69LLWRQfQy/ -etonEDr43U6/UsEICHU6cMDzdIQ9wS+gGaubbyZKmroYRAnAunP9A61cyI73SsvQ -wv8ixEpkhwPEL8elw5i1zKVqd9gR3MxxpmSNriFz0DNhBPrs3sSKfso5b5jiywPg -m6r3BFLs5sE92VTDK0yeHYNSDgaFGpKZxIAci1zo3QVHxHnihuXH44ea/QyDbpqz -8S8L2qiy78tkuzcm0Nz4OBvjhGbQgWSqf4oWDIyhG1yK6a+6SmxBAL34VHwswNOA -LMBhbMO1gJFP6YkCHAQQAQIABgUCSnV3fAAKCRD2bj5Bn4T03rcaEACSUPGFr9ID -V01sp2EPze5s5LSHGqwXCJDeXmT3MGn0Of937jO9/L0glk9mIYfIHZpedGjuSC5t -FC4hm2hWqzceylnRvUXk2DZqxEhLAQ/WbhABc3med9F/wS341P6ZlgLAgUtJpA+8 -jmT5HiV+if3C7v2QzC0LXu8WYIzZX/xzMfNq+aJh7MqXF4pIF8JShez1vFBtj5Pa -rnbnAjiC80JWiDk20jHIocymgUabsjL4dQb7tmMtp4RNqR0w8++pQsHh7OPHTKos -KKHyovMfdgS/I+/QKxgZWuHAKyFAJlICgOtbSqiecmh/e3H1Aw0wan0XFn5lz/V9 -8wrdPKLZPQDLrvkRZWdvCH/kiCCY0S1rxc19yNbTAgrZbLj6JCQwnROfbi9vnIqY -KQwmtx0U27z0ac8KzO3MR+CQYXrKNsvuCS4+CzLeEw1TxbPJA4pAMn35UcoRi5mz -8OwmSS2yVLxgcQN7PZcCNRBQfRSiKYQVc5HLkrMZTsW8ZDpTonjgto6M3N51mm0F -ydxTZNhY8JK+De9Rn9mlmui7nup9wqYp1hiLQlcW6MrvUga9lCmKVySL0UqiivUx -3wN2/mgEb7OccFVB9RS9hQSWT0YHwNkVlkvvwQnjlnoAWD/nssa+1VOzVr0AnEuM -GqSv3oLbVhm4zKBKQT7pRH49NKeIsum56IkCHAQQAQIABgUCSnceugAKCRAMcFV7 -WgZRPo67D/4m60X2J0E7mLRhRr2115UOvh7RdOZ2ZTPwmLQwK93S470uV4zqWRTb -kWDCkz3tgMvcbgwDF/aPLQT2enOlzCYrQakmktB+CKxjf3M7On16zDJm7KCk///M -EDCbIOeD7HSxfaoOd7XDeO0eGg2fhBsFiOGvrI5hO2ArVjZOCIfrJKntM9Y40SLU -EghvVTJ9JbNUVGq2Z0W58X9sRY3okjKxBrpjVegtvI6001duwPIYgNyNp/qiHYmA -AyUbMS9IdmVNJySp+e7OQhzA6WUdrKuLS3vSmTVLQTtyAcAIyx14i2BakpUsq1Dp -akAEZGu8kzDOJYjgCyWJM5jsNTeDSPwrfRQfaAnAYqnVEZoUC/HiYc1+buwLRByv -x8zTS6RJMvb/1/gfmpUb1zQmdryJqW30aipAmZwUxNC11YEYKHhRDL4H4odKbPEW -sVTFaRPH0kReIrDeYSgxer/HfRo+zr/6SjQqMA8goRrtnNEBu6JFVx51FAlBUsB4 -ftkS4REd59FVkGSVcIZhdbaeAoJMILqCaeUXbw2F5/f6NSx0mNFoMV7RRdEV5KiE -XTPtFl3wSv5GyI8/VLrJUjBXu72gMzps4UJZ6jYXkFtN3PV/pe0LqbjMfcwe8t9A -lAfI6LI37j7rkkTvaK9bG3H837C1KE3pXDYMaIv71i9kqiYPDSqVsIkCHAQQAQIA -BgUCSnfTaAAKCRCHL3AsTW4lqPPgD/4wT5bhW3vCvhZe4pedGZnrvnvG+j3MndyE -plFuzegIEzfAddmkcGx4jOxC373mdsVtnZielg55ibjJF5e42MSfOOX/LHY29GS7 -S4FbgtJczyuu4Kg6QGdJDOv3+JpOgaFtrGzkqMAiGYS2ZTbGilW/CkWG2Xsipmra -RlmRM9nt8KKwtp2rCYf7efJ0aYnaKsEWaul4bazpe1xVMxQtkOr2hdoURSiAnP0e -ht7eYbGMxeZT5BYzchnWJWoY1CVhBKRPsUzjwm+atXDU2H6OQgAsGoQXVKRWiHvr -x9dicWt2Wop5gSHRk+BNU7rJd+UPlc7VH13rYBbiyRYpDGGP/yZI+UHtqghXc7zv -gq5BfDN7iQ9is/Bpi/ejfdSSBkCZlBGQwfC+ZlnT4Y9pVCUsCmoQh3a06PJ/XDsF -0cp8rrct2ehYjfmw5J7tIVMJ7AsG4PzZytgBSkfiMp3hW4Qo6QIV6oKc19m28Jx4 -IXYVAjNeqTuy84g/QNMAJgYa4IyrOwgdoGpkt8wQOstHSj5e9fd52+qtzBO7XO+I -jbGqGCyZMxFZOGEFwenaoCbN13ctDD52qAfr9iq7Y8b9OtnloFpoOAxTBmKCSMhv -E9/+XVIJfwM9v/yawF5Ca3prjsRdW3+tRgfxSXDqC44FaK41JtgrpNZP+9qPDonG -wY8Qty3jeokCHAQQAQIABgUCSoLqKQAKCRAyJH+7QK0fpp2hD/4nsAkhkRsisWqi -VJebT9JvQk41PlSWQP9MECIz9eM/UbO/7IuCfIyyqnLxFgVUOTCYIa1cjb4YxfuS -oAagy1z+oZNzDQuhMzQTo2bW8DRx9/mR6eDrClx2yf7EogQyS2KQBzt+4lAfXUHp -kk0LLyxttKrePxrjgKqyu6mjylbLzirDVpzhwGDLCrc2SQMnoLP2W3g0RLkRMz0S -UupQGslqecx/ucvyITISi1A2QhwKJBRucj/19Np0c1Qa2F6qt0AL3muHediKm4AS -gRw3Ffrm1haWL1jXLbxE+L4BqFc2C8Q4Z3e8s85GfSHY0E5Yy9gSBIHcvu9nwEp+ -kVbbE5w6QVfRV0J/utQJozXZx2Nm0/RHFigiZybUNCR590UhlSgfuq0yvRKhg9iQ -D/jfSNf7czDJBZf0imyGcaulqKT1IVxk1XtBoX+gnsgaT5YIEkmXM0pX5eOuFO0t -sfgi1/3qmP717FIPHQ1ENa1Wy4gRZ6oUMaDFz+wbCaLR+zuR+6zObremBxPo3fTM -Fcz5sRVjtqKlNtd18Bahj+jqejcSP0vS2tdibYiXJeajegt8g1iueoJIegvrt+JB -69XYN+AK0fKDpGi5ZHzBS+uDtcl1MEec8GzYMyfrX7flc09MYQ0K7HPAN0JiREG/ -TcW852+tsgOrMmuxkVVEWqK+8W/kl4kCHAQQAQIABgUCSoOtjAAKCRBXkw2rC4aw -Z5+tD/9qEt9FzbDGOmus3mac/sE6806TeBwnpKtT+te0Lr3gQSvoobSD8q2cvQzM -GBXulLpIHaEZgicQ/gt4gHfmx/e7JmluGx0W/h9DkMPtQe/rfuXTSA6IBF3OaC6g -tkjcnh4eEz4X4K3+gsE/igoPB4Amwoa/PtQ5ezUbtXu/iZTX5bVGJaQQXZpLG0Ea -j6yILV0JKXSvzSlKBy3h1OO3L9wFDi9PsRJfLxJwk5gFtxnntxXHpmY7+IUtVaFu -8epP7tD5soGNLsz4T5qQNOW/3I0Bpoyt2QPvh5H+FJJNiDDVoQtJeyoZ+luASGQz -Qnfl/+WB/IR2bhzwh8blfSp09ptg3jhXiekzQWp8V/tsld159fS0r1sxU327JcUy -rDLDj4zlpNKPd4SNPSH8G2tiBGSNd5GfbMV+GFT3s2KP5DQvMRuGpynFLuznfoEr -iSQoD6bfx8PRJvtDLZadyp59lzIhYgEaM4Fx3QcErplK57/a268X24PmvJOQoZqj -SvyISk5jCNm9f0PeH6StX56FfcQS7UQPh0O7hvjNnm4DjI7vR1PMEGP7Tyc2Tk0o -AnEk/lwiBRviilfRAO9WhEZj6zGedr1cEjzFaP2rrEnBjCxMLVvENE+QgzyxH6d1 -5d+mEigL0lXPVpNI87X78rU1SHz03gVtzVs6SkvckSLFCRWlQIkCHAQQAQIABgUC -SqQygQAKCRAv+c1ZYSYWtYfGD/491TYXbRssFsmdYR5I8s/1StjuITBWsHsFXm1I -hlrQLPnNTB5s+QK+/wcJobJp0AuLgBXZfdRarzbffplsz1Z8GTAuRGbQZh4s5w7V -5gH3FJ3HFpFzDLGUg2OTCkaLrjArPPxoz8dbcBG9svSkQtv5G7R48WanbGxr8XKW -a1mKfNr79dpTcUxJkRSOcB+LkMsewbFpi7M4sKx/lRiW/tVFhz9TX4rxrT/CDfEs -9Xd2gByzbjbpyW0HqiA3lc91F1uW5HyyS9P1/OBPF6YWiZI9qES+4QVWfFOGx4VJ -w8cJnLKju+JEzxYtjuX0H7B/lYwwqPNQJfVCFuqknbqe9MSCwYmz/zertDxJk1OY -cztqXI6B3iSHjZBmk9OG7Vep0P6BlmLZa+WNJUnoVvTGWSa1azNhNowHXX18kmCF -gr3ifuwKmRPescLynetafBPw2gdYTObVhO+RTo9FSyqG/uoGMYBfj9wcWdjJHjOR -j8DY6kLZrrc9A2gw4wcLeuLdCgNlhQz5lxfSDE+oSjmouZVL4enOIqZXfF5G1rIe -+c3TXHmHaQWSjOn9RuXGc3Rs3BStzLqkHJ8UQijPLbLmgCweneVT4jr78RxhmRu+ -yCLQhhQ4LzCxjbMVUnu91hLHgvnIOOhSrG/19DhdUchZtdULRawi1pPeu+m4fp3H -FgYjqYkCHAQQAQIABgUCSqu5AgAKCRDrcP7zzfxuT6TpD/4tdOmj6uX0ktjXaQ48 -+r3HEONNWJ/dSfpPDQucyIvX7OHJjBKRGW7ABReTpiuGkYLwKmCQhpwFwdZ2SpTZ -IwH1QGHShX4Z9EyI1p8D9JI1e5Zydpo5S/DmbKc8sodZ4keanK1ZdMIjoof9KuAg -jRM/G5I469lPaaou/Beyh9QQpqXC6zqMPRO6++251tu1IWczuv7Twx/57dX61esA -qtC2nQTbNLKI1yurpEEmG0DoBasiuiEFyYi/XYEQvjXOg9lzvDlR2QMkvrImFo9f -UUCD5yggmqj7L5M3JzE8Fee/S/cg+y4xH9R1pimeV0w/8YGKX1/4nePWDc8qNt65 -HGft4MwfxSFsMBbCBmDWBaUN2VLzf61AeBZcEu+lV6+TkXwWmVZFxRAS5CtU0A5Y -bdP/ycacq7O/EdG7exIf+0Bmg+lKRNz1e83tT+Wm8C2giKg7Q2krtAfuuJsniSLJ -47QSNgltlZJ40CHaeareSLhq4ZixFenPpT7Y/0hn+VAUTsemoXj8GyXuW5iLYAVo -foMjzpGxBTXgfo6j3MGvTMV4W3VwPlmJjloPmZ0xwOBzVqhcAW4pGnSE+QIf2QmD -Tfib2EKr3ZUGW8NuuAD3YPN4gWYooeS30iqRjB3fwONIajFp5qGc5Ad7julwLPfw -BpsGK44T/g+C/D+kTPsvnMpBLYkCHAQQAQIABgUCSuXRaAAKCRAYFHsHO60rB/h0 -D/9RKeF/OrwyZPBe8bj+OSlNJTvG9z7un30CVsPcJyyPPGkIxIt+YelZoImbwtGX -zDM00LEUodJcTefRYHdf7HyR/jpqKeN6T69pvnQXFQVHUB4lCnu1C0PmWtyf9uVV -3vH3Mng33YenaxnH17kSIL49/DIgXwJ4T7aK6vPRBWUboxB8c4eWKZTIPbIjeD7n -nqGrOM/ukac6qGaZYiT/GJZ/9FpWhDA+Gz/aQD9DsCcKPXyN5UJyp7cRcGcwNLtz -X5TeTM5euAvNWWM6HHM3XH84NJH2wL04ed3fGyTxdPNQV+uEUMZqaVGy8T3TMLJ0 -d/8n/Nmk6VKgiU/bDNhwHUsph5b9S5c3Ab3olUNB2wf0aZzr8HzySJvSyzWfkauh -ow2n8Qsi5t7UyE+3Ju14ytJDJVEvcXE0oDRNX6yW+s6bPPu9uMhPxYNe7aOyQXSM -5VeFJIaAlyjNxSYPRlogs0bfYP1K1b39qPxIl7Osmu/DSCJU1oIJ+S9FhWhdbLA/ -3rLkOXJ+nn3iVC+/b75OjegAnGGV82jSDcfOcyq3gr7JyOJAFBQV6yMkncQqBTVt -6QVfBQYDULJJlxOxuUINPtUl4cs7A813GxPUqkxhunCgRaLP+ndMEY8RGfB7GwpP -XMDUGUNsv1J3zR4wLYfc89kV94TJEFH/y591FYOncxNjgIkCHAQQAQIABgUCS1Fd -AQAKCRAhn2tgsrv8/GlVD/sEjQAE9cbl1EYMfgI+Y+gVxeDloa+hJQyv8CpDb7I9 -PU4lWTtaQ4Ip4QsBTVfimvJc4JvvTRdC2SiM9yGDY4cv/GKv+McRWQ9snoWQ2Uar -ha7kuuly83RnSmx6DdKztZCb6gwzE/iEZR/YikbBPzB0nBo1ef4leFZN0s7PEFll -J8QPS4nsZTmV5trrcBXjkNbOL/XFQs1+MLSC5LOe0zwrTcnBuhuY2yFS5XBjx9Xo -y5QkgvgGDdI/GE0eSrskAHXdG91Fo8Kym6dEZkF6UBaQS5yCO8DldCcunjFLGzWq -ty5Zt/CmGDYsvp/kMDne3RH1NbMSE3e8t+81l10X8Ph9PXkjZCGwxrKqHkmLM2iv -l/tWHG+K+DQdiOhIB7Zi3g1wlghFRjysZxfl3oQH9YRa3TiB2RNCFabQ6F3j9Nae -+KNSVOLfIUxS9hxa/jVm7QT+Ja0S83MyXDS/+wDwjp/c4Rl5fD7reW9r4vARoD8D -TxLihY+qxEForTSUqrK1KeL/28vKDBRjIjHn2J4v01IpS9FvhaIYkEzpS0wc7J9q -dYn161H8CnH80lGf4kAxZtbMzC2Oohaf4RFGJcJRKtePbYmeSOZKaQ+KK4xcbKcY -A+WqyWE+i8Rs0YhlWzWuT3MoQ+YUEuAxt63LyBFjA+JWsTUKinqIWXqfqvDf+pGa -pYkCHAQQAQIABgUCS1frbgAKCRDyNyM2/sOXRWcnEACAnwLOiuoN+G4DSR4R2xQE -9mQfx7aKhBdyGwQcFP2m5K7ew2MCnfgX9HbELsIbfSTjDmSoT6eCRb6b3/1b0ipM -ISeipJBGUb92nhXxXPG0r3N6cNnqgzX1i/0U/nho+NhTFhpefRDpXLZqh/AAU3Xx -LWO4j+g+3AAchwBnj43roJUMNf6/L2wzI+x3337RAKPpST2xvuFk+Xr0hxC7n6bQ -o2p9s3yg/RUb4XteN3QpNw0MzyqwM9iup25sdhF9VVSz8pzmgyOx6YG9YEU4qX5I -FveHyKDBaA7E+02taldePCizQ4YZ25JeOG9x4G9U0WV9M+Ku/q8fVpdoOhDP6s7q -BE52ujfP65Uw4Edy22zXBJKI/cJXshFGhP6XQk0O3P8Utw+eTC0go0qBv5MMqSi3 -WGdwyNqjDDa3D7lCh0hkJvloYcpXKmSfSi2ytedQChjel5aUZUGMKGnjz8GHgPhU -PZJnGSXN2l+RKbh57k9uGJfUJFIr6sLALoVsAm267WK49OoGeRl2Lfs2PPn0btNs -nxHHqMz2CwdCxRrOeyktAwGixa5cUfWyAT2GYRYl+gA7JGoR2wgSRXWbQHrn19L0 -MHEvFceBNNagM6h8XZr3+OkCz25fz8FBDmufrl/ZX7RdwxW/T7GM4zYELyqpap21 -dw+mqV6U1Z/zKHcDmZ3pcIkCHAQQAQIABgUCS2XhPwAKCRCKERtcrkJpRO63D/0T -vAFk9pzXo5+qe+tavl6ZCoXowIx0dOxO15pJ4hVorQTVV91Au/t42ObeSCevzq1n -/TeQNLUq4a8XT3czg/Sw/7S0nGF6+dC2EICwW0E4TQR08vhRvv47opnNoKRH5Qt5 -2Z8hLBktPij93WzAopnIWYDrtifaWlgOkL9RFkiihAbjnT2PvpXu/LVqwAGU55J8 -D99elJCexbekZpNdQfeq/X7yi2TkVKXnB7Ls3y/b+ZSn2VLizGOcLfXhsb/vjDJ4 -4LLLBpF/Gf+dBB4kdWYl5uYbo0aezkS8jHnlANGKJ41nStj4IiAM/uFHxf6YKcg9 -xxACgYps3dxpn0LsGUpXHUDksqw2n/dlGRL46ug8Im4xzsrkmf+JVOYF2pA6qbCK -jZrSWKm7P6KSJJLwDuNuZGRfFMDhGr9Dfyxx1qf0Txd9Ix+t96sMJpFiD5dBoVv9 -MxvHQcR/xXK3CdlkJD+0H97efw9fdJpTmwbCX+DIeycju5m0V+tXMAQ6mRfwtEro -rAbpY/pj2UBCFh0NinHf5Cy+GIjSQm3RQrAbIrwSAbWx9m8XKvSWFzNCNWeVEVa3 -rsBB9eFrvm/SbNi/GUR2glb1tV1ZkZYGFiSgyAJoEykzo84shhgcxJtQ3QrC55Ne -tTVhrvNMumAA6SnHce4AeYk4Cv3IkzLc/xIujkIpCIkCHAQQAQIABgUCS3tQDwAK -CRAorTKyGMy4/hv3EAC1w7t1gWm2lpCFLP2Bimk+ErFOwLwTgMD62SsuEzKFU1Bu -UBr2IEMsjIf7Efw8PiiDNdFUR+S7D35VTYX/AZAiOWDWFSGJCCGCLgJx9lopEEfB -kEbKHVwa1jJVjDVZyUHdfy8pYvcDc+O7SoBwToeyF3PRpEGsvoSkiP6yix+PfJmw -ZSmFxEbUvwKHGsxsEDkTC/xkytUlUP+Dwx9MFHTkrktN4c2gJXn+JJIifmp+dpNm -FTDfRiN5nZcHm70hOUfetrzkkUkBCMeIqRQX1LO0IXzFieWp0Kz4t/J01Op2I4hM -lyo0isuICiE9QR1rzc+W1NT5g09yfEovanyU/q1oAlc88PkuVzRI8inU8/R+Daql -UkCXSTx4ymmRHGGGkhZ/LwwpRF3cjEPMggqxbuCgIQ78Zl/+RK25jHc1uqWL3aMc -rARkMOq0hc7HxQzxpdaDYuMlKONRa/FSNuYUF8R2uGqIDDQNBXahB1nPSN7lzFdN -Yb3EbQ/XbglXCCn/I0kMtCKttE+2/BLPC0QYeU+8RruwSJ/hdiBv5oTkB134YdUD -mtsL3TVLV5pyiw8/StI7PNGlPiQdkHy3rUnRHAIDbFBOhry+7+T/MZ8RuiBg11eb -epk5WwTq5k8R/6oiKtI55+EqDjC9fgzm6ILnasr4RWZQOKNvxa1Y0twkJRHisIkC -HAQQAQIABgUCTGrqVgAKCRAzlhWI4cIYRaFBD/9vwNipGDqt204K+YeKDxr7O+SY -28ZB48hFRGKNG5ayLy+UIF4h36Hp0bjsk3zy65qtbVCME/eh5HZpTQSu3fSKypdI -PGILP+NugbXqWiz0LeWvS4LOsCYwJ9wl0VvSwF9tmor86RI9p/sDN0OMj3+9QSEp -Ig67T0xReZjgh/fvZ5liunnm1dFNpENvjJ/jI3bGw+V2+XnlYfl4IDfSjcgVSYve -o0gvawIqUBQWxx3unWUra+1cAluV9yAH+2i672t1/NGJdh/TV0ir+OXCVjVssLwD -QkBLGO0VA4a3pL9SmelGUtXu6LHCIQfl2dvSTxIFJWSvHbsnroHwu7a8ocpjLPAV -9W3JgSNKpZcd5O7CcSnjwUTW2vSXGjuhbOZL0fZrmITLTuimyHhccwD/q1O8+yxW -qT03i7Z5qqye5F7fqLk/gvHuawFQR7Fae7QpfoomNsJK4xmjskDY1fDjMS23rYC1 -415wxyUKCm7KOM6kNj+5aVkoMLsch9b/HqvFJuXa6ejvzwpHhWhvIeVjsvWo+qgr -wJFoOSPpQoVLNoZ5FIfoRvQQ0q1xbb8+I/kW82CFtUMDCx7RQ9PnrPLUq2++KFBo -0PB4kyos9ZFMMc8tzLsYm8jrw3QXhrVtCTkTGZKCJVtGlsYD/evZgj6f5+DcVjDc -P4LWNzmqogD6L+oVUYkCHAQQAQIABgUCTRyivwAKCRDY7tfzyDv6msNPD/9/ElUW -YmGd+vGxxixZGpmwMoNBSNv5ll35zxoaBpB0S/xvpwIEKoCfotWeAQzzaGL6jQmj -oox4J2NUvm0J1YyxaJbA4XpOBKiw3kMAsXNCRmNt4Q+dkAE4IAFJoj88KHMcrfar -aJF3nBU2Pge8fpq0UhZW0HSfES1lgni2bCPTV4jMg1H++ccaM/R9iMKiXh76HRvH -G6bhbwaUhcBK7EoViMujSnxIZCf1TFWBz3Nbyhk/MfHHORijg3x4DUTDtnrBYsJm -AMfPIX3qJLM+FCIGIHANu1DvUuRkyqs5mfN5Dg2WKjWRwpF/C8PVLs5weGFCpm3C -jjnDm9f1ZX2NlDXav3ho0SRFKNOFw2Xqe4Q6tL+adutjRgu+1XTCECINWMkOkNQH -S9M608e2egaBbGz4gTbBIJ2Q5sqciXJ+OjLQyjhXXTyjYlDqkC0bqKuYrk6aCjp3 -36bZm210izaBJ5WsmeA4wfYkr9E8T1aWDL8q9P03h6T7g7iuYyRxbPK9KbOVe1xb -sjHZcI7eHyTMDqxfRPppgqZ2p7zdRV6sxxkrXnPCfxj9ZKcWsg133r8IcF+k4tch -c4C+zGS0actfA7DlffT1CW8YBvK39rUazr4Xcb57hAzO6oSqpQPUES8QYYX2JT1I -d+GtWbtuFN/HMzZs+7RYa06UaOfAeJBc6pniAokCHAQQAQIABgUCTSG4bwAKCRDk -PJH7fWA5sReuD/0Qh+FLcZX+upo1qmA4bF6KmNpv8As/VzOSC7g7CPPogivFBhtV -J1ILwnCwfjrOAOj6vG3Zc0cYKV1k6N/KU4N/2RrEAqKh4uKva+Ocif4KD4hpMahM -g17939of849wFdntglxv1aw747+R4q9mzIp+7L2quA1nn0emxiw/NYWkqnd3OZR5 -PeJtY9xqNUGcFufJjphl8r2hqvPs0/vL0ZXyHOG/2RW5x0+uCwjfUl89Sknp/D9s -oK3mItwm/OsEALdsphxfEE2+E9AOKm+2ZOP0Dv2Bxf8DMgl52K5DcwwJuoXnn/BJ -jWoIlz+9XCmSfolWqoiDqIlX8TRgDGmb9ioC0ILAcDN3515r67oKdzx5NfKn0lQk -h+2fUe2MVjohCK1TVuWMXE8WxruAGk+fBEyw/uJwvBUajeUcD5aXAo5wOxJxzySN -ZpSIN9h0uFe2QczlhVRwn1KVEtm7stlD1vGy3XLsr1++rFlMOWkCaewLfm8yrDt1 -klFO0u6McRWJDqtzf9X9PxjLAexIFaO/UsVIurf633hOaJs3sIheBi6AOsv2x7sL -MPoj5CozxC79YReqRSsSf3QeNZvqkmyTnGHF5EVD1Q/Ud9vzhEXsS0XPqsiAyUtX -Q4bNlUHL6SYr2IxO/0QDAVtJsHePOUTUE70jH1Pzlad8PYqKm4PMk/6yMIkCHAQQ -AQIABgUCTSIqzAAKCRAod5dobkDjL8r2D/9lhSdPPN4L2AFGqqySahRq1radq+Rq -Rm295FHRtGm+HxkGNFQICpxsqxl1D54i58gqmvIgTqqpvxfiXWPPiMl1qdZpzLyS -JMkVieSpC8r0ZTG1dq/HXE0z9VO+QbPWO/f+VLTArtA0olaPh3Er8GDUOZIQ8Es6 -qbCeqcoE26YN/o2uxwcLkp4AjdSBkbxD3V1Hj7d3VIRmxgV3NawQLk7f6+L2tZ3/ -A8bLToD79M7qguSzKalz2RBC+YG2OixHcdDJCqFvrHerZEQYmmGqw+ky5rYuwdcm -OqYqJ7eZE1MxaG8Mn4FCMBEVIU/wKD6tsv3GuPEWQg5+8EGj1JNQvJohArsb8s7t -DbZoqM52s95wJe4goH2Mh1QSG4aO2VUYvyJ05H8lCkr7lPadMD5MKp5nr6bbLktg -tAD9k7pS+tVkl8O5b+vFMDg3b2ak7Nkeg0nDMSGcoHukSgQin3jKl53wsNLZ+K0q -8iHm9drbEhkoIOTTYHw6NkZJdVEL6DWYYsj0cN/NEfQt5az9lYWFUwMoLLJKN/DB -R+VGR0Zt71rXYxQHKzVKEoLYZ3xtPsz2OWtQXPUicfQpEKq++UGO2ypdSl5uDktH -CfaiPssgPWU4LdzVAiZcVU/CQx0E5QCf8/cf+eeXmxC7MVx5gQFaHQtZ1d+vQS9+ -b0mcQfoT8EurAYkCHAQQAQIABgUCTdZhngAKCRCDe+/5DNv5HLCvD/48yvZ5S4Aj -/sbpoXS2re6P/FImWsEsSmZgk9Zrme1C4g5393atfazui9fV30RZ3D92fVgdl1BA -WRAoAt7VVId9xom3DpdNNeLKxS5AFgOAWBjfVNbkNODesowpgcmTTD0L1hTAywKl -1uAXLOgf4Ifht11az3iZeUBhuIfAq56bMIz75KGARCfmsFmTHR0jkqO4t2qJ5KPd -ya3qEeu9bagakKn1Pha/uMKMgUsi0S4xfHWfr9y7CLNH+Wu2hj3Q4u52dASMnaV2 -HYy0RBiKAbgXCiWd0BSe889NsLzIUcZPlv1XbgzgNZm/UbldV1P/d1iJxksbSkuM -7JD1855IynRqHA1/okEfUofLQ0DBn8NGxqvSQYQ2xKcL7bgC6Wk88ppavTYYyp4N -FrZKoU0DnESUHaakTnZfDAnrHbgWIs2wAH2hLBdSidtznP/TZdLcOMZQRSFTh4cr -v3hLzNvOzRnuOu+nO+/KYMudC71yjjpi4uVW3TaaCIELYNFFQJzkgSl6lKZAtvXX -59MPCaOxsUbyhcyjfTLe/eIj6UzrwGda+6MJmIRHr3BDFRuDq1/fEJp8gNuTTfqD -oLz0fty6EAMqBbbRFVS7XRKLwCNu1WG68FV+pISLvJnmji8cG0k/dw6J7E0V7+as -OTcXfFv2UJe2zqfbOTMxVAfhN6ncxostmokCHAQQAQIABgUCTdZkggAKCRC145mf -vu5nZIbvEACU0f+Q6zisYdFvny0/bXVq9jiQboHX1TkG0GCYLbXRJyeoOkB4Xcnk -C+OUmc0lmZOUw85emnMctouUDWlvaDiEI2KdtPSV1t/YahR7U5Fw3lclhq2wD20R -Wc20mD1JoYBV/hzDSGdIH9iImo5FERDMrMsaMfO66pOqjtG1End0XHdwKf5xXw6P -d0+Myzk6Trvg0PKONBwgV7a6WMguOhqMTb9HussQsvIBfIlMoCkWJ/uz5qPUAvui -YxTI8OMho5ZwXC3FqFW4ngOjk8JeUkpHGBDa/xJDZzaMqe8VOET9wfLTRSlqoHwY -C/J/p0dPrXZG2o+UA+LORB1dpOEGm87b1Km0B1W8YA4fz9rdQUO5Sl5gay1Hx6Sx -Q51ZV4Z0AsZSNdDPuTNysXV5STdG9+b7VJYDjO6FyGrF2e+e5ubj0bfLJIe2Z/6h -KoSoYYwJBNecClq0q+Ak6mMGJoEAiVSbroSubcKgaKJnTjMPuso04FyAxS+YtZzp -RdUAic4VskWmZEGfnOV2dXVStJUhPyng4zM/rPBcax6f2p8twMr43ssV6xo5BHre -JbBeARKhPjatTeBgY6oY0g7LbIDb4RuMHxokssgKor/5RikhGt3ntRj19jIoX+1X -VYNHmANinMCSbwEdQYGSerJL5+5gaAot8O3895T80BsxySjq3EeZjokCHAQQAQgA -BgUCSnG9+wAKCRDxppvkKcD/7u3aD/9qZOa9TwkJIuZakS2vSFcqja5vSYDDc6+r -gc+j90G+aHrckIQ45Szu7niEe6VE8vtbk05hLrO+qLV6/hGLOKaBc4KiK3oKCrK4 -vtofyf1d4cUIVYa8O62j2yTuVe4kxGCi2JOgsaoykQDG14nlLiH5IBof8L2VBsdH -V5TjcEeuDK27d8M9RE8bdZuM3zwJMFS9ltXrkes0OTJxOVXJLpGkWSORLQM8k6On -UOs1iiN8ZIon3MGlCqyk+HyAUhWF69xi8QvzYMCmROH15oYUZFd3m42oGfnPUwTN -X6OXpKW9qsiOcDaR7+L0YcnAeWsZ16Xk1dRMnsz6Z3xJmn46sZbrHR1CV6ZXJYDc -BDTcvvDFruIokhXH7oz9AKLbBJhiaQZBpt0vScfH1O03VkxRq8naGHkXpefs8I74 -uhdkE/cj80ZhCIygTUAjiQX0yEbdCGNLPoqwQ+26rgl9koJcsvr3b0wjzaT/WECY -zrlRK+SQrct/+I1pjTYUaS4eQqWTRSrxNtMndI3ZVU4a6A3vCVdsHTNjUMRgpHWN -DhCZSd7th3UbmwWz+6yfyz4zO34/NIobYP0ARp09nZjkjsW6565YwMgLBdRwt5CL -OtMbyIuUfh/PCkrYu2USG7WhDBXvbHJMRJqehtph3Kal4f+SzyakLafWMUkgdqS8 -H68gd8kWvYkCHAQQAQgABgUCSnMJ6gAKCRA9kIqz8Pv1H8tVD/9wYgXErxFt6YkE -n22I2pyREoUgj0RNx28gPyzsBxmH5RSF/GNnvEuWFKbhtcbk0+e/QVmu0jpGKnFm -n5QX34q6ymqHs4bPYocXI2BLdhtLBqBmCtBweTCQpuJagJedsG/MS0RcOV0RzJGn -WDa58Tn/QFAsBabub+YmvOkZiuaBGIfvS2wxi8ZhjO610zL5tJffgvmvAfuY8JQL -lTo/AQAN3d2ktcaZzxHwccMdCDk/mw3B+ViVNvWoAUwtc7ZGSckT5LAYID3COAia -PUqsdj+z5n8lNQEeomUQ+fxPm3Dv7vAAlgArZMvMcP5C49/5QwBDE547jw2JcRMv -4TIt1yJx8z6nZZnUHkg9l7gcKB3ugBXvDCCerceBvl5XJPit9yeZN0DYwvauSyfu -JJGi74Yj51DpEuIc02WNVQSviriPppWj8WwvC8LX3pqNo1iTP2yoAcwwWnpzA0pf -czUjAFTyLkI8DQ4PTbEQJAqyzgUz9btjL6KBWO6xeFCl3ER65/iDg6p2rfa3P+Ck -AkThz7N1eX/JhM5yTfZo/mTS3O/PNjkQxfxcGjI8WIvGHNQYj27eKCv8e7XDnjbr -F4uTi9qYS9pbfqsDUVBHj6T3EUZ2b7nTN47dHXrKs7mjCa6pLnv9HAqNCDXHPYth -DvOg62H5s5gODBVkwUHmv4FzhTpQ9YkCHAQQAQgABgUCSnRCbAAKCRDmGQRd8qxy -mg/QD/9XlVUwOsK81K8WQuvmcixb8ge+afxtiOgm1htLj2mJd1wWqIuVFU3qRzo7 -h/mq2JeugkJu/TCv2mvZdTAXpojpSs3Q4f9lWPQoDm8iZzrk8zoEwtTdmR/Q8VT6 -aeP6YvxPvMTZWSqaigWHPEMJToUWNOnLj1sb/nxPipINmXetEC3aCbkv+qLpzg9j -QnjESmUZo5FUgG+6imsReerPEp3+vlviKUww4WCXr0ObXk3M+X01pVJ605OzG8+a -Xrtx2jwGu1maWesMGE02VOzS5H4rNX02PiN9YhyR55ZdeSEb/1ZrYyRbIGUJxG4A -fWq2GZpt5YjmmL+U/CEtn69os5uy6qtfnCO45PIA0qTBqzB5WiNpPj+MPcjv++LW -lHQx9+kdVzVr2KnH3cOeG2sRdPnroQk4JcGyfUF+3yMf0aQGqrvQRPXvxTn7Feld -xx0ypBYlSoI3COzYxgR7ey9uP46xajrpiq3YIK6OVLytaovgJN9SOAY/SvzsHqVe -qsRNLCLL7h/PtdFK0gT2oMrbEi9vri8/12cQPEKAKjtApgS38HsbgNBwBieeRIop -xs/xGburA7VkaOeQmVODIloTx4R8IcelAj04J0FsgBg6QBQ0/OpLSO75fWCrzcYz -OxVWBC6QkUgld2YEl2R6XBcgwdiznoPw9jmkLwnbBPjAxAfIa4kCHAQQAQgABgUC -SnR3zQAKCRDf9zjC0Wz7okZdD/9jVx53I9zVQRmwlN+WDKeIO4WQstKrcmmrZTvb -fghVYsJ06/P4It5Zx/atnXOnjdgiUB40f5DFJiUBOlO3LtzI8xVAIhzFuPN9TNej -cHheNKNceKYKgBcaOmoqxyfRWu7FmXXCOmAaSYkOFDmhCZ2FkAHdbxL1ULWh453p -kLlkDJpsrq8Z/xBrFoPswrOg8KDDxNj5agCketZ0+mZ2fJiJ3xjxZkVW/xaILn8h -LKxajXQynYcT98c4b8CLs9VvonT0i32RNJ3PKVNowV9W6bovHD3iMnkPLsri/KdV -BGCkkffxiuQHQY7IPSCdnsYGyJtW4lOCbNhVfgTj97yhn8r89m0WMvGIRzyPTBCQ -HLnfC0OG3ow6fTrABPFxu6SB4vEJX0c34mDjOzUPVTMAh1tmverSPOUHqzDgOKY1 -mFJtEXNPf2EhXXTUz1dlc9g8eNs/z37b6XSaJAyY/7hJs0PUB/wURke5dzXrWoJp -t6ceuEWq0HUquyXb2nnyku8yv7vxFtAmzSCkGewricufnhEAdXyU2n2kkwho2+KB -y52nezArYIjfq5D+sI9QIdy5fKtcTjncSzHlvns7UeGgHiQmVaKsM99WE3HfzoR+ -KxAixoHI7qHot3TH/jV93QRLeS0JbZTCFgsxzxAjH4RhaEFpa698j7dMSZQg4yzr -XGoVkIkCHAQQAQgABgUCSnR4DQAKCRC6nHgGHd2Mm0s+D/wMThO5JytZ8RcyM8nI -Z53Y2iLxT/OtCIdJcAaM/Kx/Zj1FZRQXm6sCZY6fyQ8VVjw02gaO5gjebg9GHyHc -ielD2XgG3GaNp/PJScXEHSttYLNlpmzb3dFpi/3GSSo68yI2C/49faAc78krM8E+ -7ONpHiKyvKBAJkljv79GtEHpo9wrvrl5IxsHsB+eHhuHtODeHJFYZLk9EquxhZi3 -i9oXKM2B0ZTDbcvluR2ewmYOdP0gLDu6oLeEctErwK7Q9boD4q8rpJkiEMnqWjU4 -iu8pkZMsMlQ8Tr27Ov2Fv6EyNPJizfKe6cjBBOA+3Yz4ermeoPueF8OHWzaMyJLt -lIcEJ/9ViJMRSWSQtBKhjBjbeB3OYinDx3AJtiV7aXGT86iqDFGsSffA0OXAk8G2 -E/ly59MSMIwGmt3Ux9/kQV9kil6B6Im577Aqfy0x3r/2kVRL1rS+/V3DScosptF5 -PRYTbecjk0YS+8PBaF/XOcpggEZge5UfT/M2tX5RdQ8h23ur+c5g8D6uB1Oxsd4L -HSETvaquSAVxb+TLuno/ErAZMZpLqyWv3OJ1rn7P45xPXg2UmDqet1QFjaMY7bAX -omHksYlSk6LgilReFw8bRtcXhoqXsdoztfeqL4XJGow+xpodJu0tL8wYgfARuHPI -scJXAYUfi/9XtY9UkrXmDMdThIkCHAQQAQgABgUCSnSKqgAKCRCi0+fPUhNGtHSA -EADHNkP2/xsX/6iyw2GZNDGgvYvrUQqKy8hrLtqEf18LDhkfBXbmsp3D4qAh1Rkn -EA8dPcaqkK5N4MlWES3PTireKCq48CZJ19GAa3zcGrT09h9XTcNVlYHhPGpeBybH -5rUIJUtNgzitJ5j7y9YK3k6f41JMh+whi32jXfcUFdD7ctzSEiJlIoXxdOnyoPH8 -qhbqCNzghl93ZFm/9Ignsxws5qlPxWdgGeuhg07/I5eZ/nuDUeiVXLssgar1+qc7 -8c37CyvjuN9e7gaRTN0RfnDQ5L7SnUJLAOLROeb7H2zzbwmNF9ONt1WmvgcRg2dY -dB4z2m5xxNg6qEnAfNO92YiL8ly6/g0LKQxgMawVqorzSdpVchRG/qZ3gPOLxD12 -YMxg3MUYY5fkXJw1XYFyUMdJcIbDR9lWyNuZ47nlfe8chPw12jjCxnhlHSijbzWi -W+4Mtu5TqxEXfSaXE7P2u5VvHkmjsBDD5eRqxCcl4a2DhgQP+/rnlW4wZ/I9scsK -hzzOxDEWwaF3TErdV3QZGHAe7Mll5EDDUud7eadiC8PT+JKMc5W6nPxz7raXA4TI -PeBPEIKhvlvBL2bdSewl5z3dKEYuqrfjeZfcw59R5qdBx0djB9j4tVhWVSpOwqYc -weY/soEUux7Zpil1XsKqNwbOrMe3BnGZqLV72d3N2fQrNokCHAQQAQgABgUCSnSc -3gAKCRAzvhoKjC7Y/62XEAC9Ta11Kz2nhyqSzWptMmbxuIJ/BnwL3Q5rTOKymDkQ -j3Aa0H7ev/VIRJf+RUH1hg7NMHpYCPEWSwOZaPQJp4EKgIVYvifttyfbsDqHJWCC -b0XifmyhK20vp8lL7NvQ6HtGnY/xs8rJ57krvEmCONN5rGXIrKaLZb/ryDYRdktV -cyszxMEyUIY6vbgqoCmagR5vnsha2zop5bgkDTH7ONf3aTpwBjy2C3sc1ndq2IV4 -9MCqOWrE4vBvfePpnrP/2yjaeEzwIObjZB+6T06dGlhDK7h8NJc9ZqjgSHTId98x -Ec1xaok3IX3KdlKxpEe0wqxyXkhCtYyZsgt8r1tMNWdvreySG3uO72h7BnXMc+vd -gijp7reZrfLNQSh1uukLjlXXqYi6gfHI38LCjp++iAH+7i3TAX47/L2wuK+r4SMw -bHPPBbSDtJ52e+ghEls0Sx3Z2VWZ4OsDALA9xGiegEnNRt0mD6IYMrTjT54tpLzW -2/LKtUguyZWBLRnmD+1WlNxncisLHk2sM4hLW1557BBOvtLKvAXnRYUbgbE98JrY -5elMjd+CG3m9+JgJuJriVC09QssyEIvuyw8fULo4oCRcI9VwnrdQ+t7UkB5/9CJX -eQQe5b0/mL7kkxgIEL5J8eXaKgm1AJqzrAUXG7H8n129sjcGxaOi1GpjRSbG3YE+ -GIkCHAQQAQgABgUCSncPJQAKCRC5ESBTbYUSjd5/D/9zAtdpOkUb0QiiUKvg0WLN -/9It/T4KQu5WYdCkBrQJjX52bXhs09UWNzI2gEE+M+6ZmAwsBfrXmu9p+UtcM5ur -trXMDll5VQJXEivT6gpZctFUmB3K43eLCV/ZWof37R9yrykv1fjWzZ16U9boFOqh -anheQiY45aoRlDd1aMEPfbFpWKhAIAH/y/m5Zq4VdmRM7hj2c1pmue5WZNIc6f5D -Zmy03blhnJt0tNRb+7Nf7szzs2EXPKdhDRYZCRRGLZ+r1uPOQtY1JsM98iHKaZSM -+acdUH4jSd4YNwM05jx73QIv6lxZfY8ASPQpGY2hAelvUXVPbx1HioGnLORRt916 -tphXu/On6gyb6vJuxnmGFpBQwP/KKV1sWP6rVYW9IV0K6UBgu6OKJPqWZxw31tS+ -aNhmJncuvnD18aKa0QH+nlgAFa8GzAlIO2zN1KcoCWQSslSB9SkVGvQMC22jwp5k -cQ/CJYuozKouc6R1Po2MSkM2zLs0LhesNzvneo3nXVGt94VcuI32XFIEfqlnWKZV -vXwYG4v48GQ8wuH58fBXfJRNURJiSkybylJAkfa92060i0X/n0lFfrRnKsgCDfMq -2N3joQu9l8xorYA2XZETSr9kXxPe+b17fxPyrpAxUpbuvoLQC7/24slPgVbxuHGV -pxPavd0IM+1pE1iyrq81N4kCHAQQAQgABgUCSndNYgAKCRAsfDFGwaABIQB4EACC -Dr5eiwyBGvrSEC3y6UOgWdDK4/oQINN66AGOdaF4GUeBClNjOS8oVqw9zzwVz7a0 -xrAjEh7dLTpzrp/1IY/MK9cbMd2BfXkrjuYhcDhTgbfTOgniC5p6vQHeClNpvYEy -1A5OFtpaBv31JhUgjINlxNk8tWEtaypmJIIXg/d4Q9HgDeFlFq7Hkws6W4GTXg/+ -ZoXXD2ZZyHjk5qn1na6FRjdxChy1o/5KVAAgLPiAU1mme26vJ4qZizeAohLLvGYL -SqECG1SrjDbeAdddtySnfogCQfLnZ29uVMzNKB1/Clom6aoZjiztrrTeKHXT6Xr7 -6AsTTyp7mO066bOz7cZ3aG4aOpmMYp9vjxixdy4K9Le7LxVfYDfJpyg1layjgmyM -UEgUVr5I8GRi620FNIIocJV+UJf42iM7KGdxiWcmHIhN1YHNKm9onDjzAivIw1zB -Qel1D+z8O945dUJPkn4jkyjC27WWNVo6uoY32vUOi8phCl/ZBxTDoI+/yc/ylotl -pFBS3OJiaDDKWbbyp8F/Ox693Yg/B6JhbyDEHvWdswx0V8z+CuKVjWghfZSN8ota -o75Wf66P85CB8pThu+tERRMN0s7ZsXSKDMYbxqNtuHIQjWYe2qlShyaAF6iygNuO -wEkyQcWLJYOtR602dPcz+ZlRABw8LsSPecKQQgE2G4kCHAQQAQgABgUCSndgygAK -CRAMcFV7WgZRPoPyD/40UIxU8jYL/zpIFLtPCCfvblrPlN3Fi3zoYpCJRXmglk9/ -++5Zt8x7Mnj9K9+aPsY+s9TiOq3kTsyem6JpM1VU9/I28Ci+Q4MtTJ45Ge8P2QOT -PWcMg+d96PYtcHJv/PtUD+nmxSe03zHiCbh6C/n8JnQ4oMIUV74dLmZZDu+nG1Xz -xmoRdNyHdIjNbEzuf8c+teK4OYbIodZkfJMyoNCdOQvX6NySZIfMbuItJujLJGPq -w86OpH0g2wmsmyVNjNqDmsGkgIFm6g6j3hFi166kCE7HerEpjNCDNA9nV6Ibs7e7 -718f37d0/TXkAeKmPNcMnNCm+Xhlpz53FK2waioZ049Ie/t+p9MEgTXILSC1Cyvb -Dcxpn8U3wyU9qJoiRp/prYiXKtoSAG9Rmqk43Mf/heh8bWZE+RQNgjo4fnUtclRv -oK/2m66rfqLs+Vua+h8y758CgrXx8z9ZFtlpfrfhZUtRw9uq4SBXXBvoISb1n9fe -D5xc/kvTSaGmGSKIp1fpM3S8C0zaA4RkgYsSZ3aJGNe3FO5HqgNWytsZoaZHMTwE -XTgM9fcOv+sPQc1tGVB19dZfHYZ+ddjYdfMAD+imblGv0ENsXVDps6FrpvgmCQyi -T3R0aIIPr/8JTGFKP7pHLKQo7av2ra5VPDipkNZgXNjadDZpG82qpcEjjVfZLYkC -HAQQAQgABgUCSnmQvwAKCRCHL3AsTW4lqIjoD/9Eu92Uyvhb4hQIVoaHxI+Tqmd2 -CglNFsvBTSi1EHP+nBWTTjn08wfTXNI+4o0801U4O6YVdDRmyDf4MF3UgokmTMQl -p6wIpxzFTgScRWBcwZTZBSwOzvVLqlFinLxhTQNSnMQTwWtNZSnkMJ/WOyUq/8OH -RBtN4pFBfgwfV7YJ8QhrqXJtabNvSde8sZalL7+CrGijWfR9dvU7CjnGq22pdg7H -D4YBjkzZCE+ztZiAueGOATq7jwwQ0bBKmSadvHIbPd/JLyEqyn2KGneWXxpN/i0f -vVQv2WrLxCO1iC8H8V7scedctZA7LpAmoPaCRWaPHHYVf0NfsLmyHjcMVITDJhG4 -EKTIQyCJivthVfdCS5FBAdjRlNKjJauQrNpo5JfgffDMoLv7qVfzvlGxhEOeoMP4 -U50y2VFvn0qp0iWUmgWL28gY+Vhl/cwlGw7jXQCEbM0MZpP9wxq8L/Oiex1Kl8s6 -et0Quz58Lk1vj0NXNBXeINiQ2TG/Jk6uUHNnzgBEcvHZ780bmoVNo2Rm8gVQPYIB -V6cC9/nSWGUvoXXo2b7BtUYvnkzESm0MOapEbJtv1sOxqCv89Dte6nEOpHgFRIjO -oSA850fsX8wwwQOP7/GiSERMalOlBlA5qplgbRof+ZBSYY5RjafOiWPP7c6hkLDN -Jewce4so6DgyxQCo1IkCHAQQAQgABgUCSnwzjgAKCRC7OmgBhkmqBieHD/4qu8Kf -D0+fHJo4nhEGioW39Gw74Q2SEzFwV0nymx5XmmsVihQWFo/Spv8nNw5qsYMRksLf -uOfwDsHZBtcfX1dsjSc0bdSWrMWelaQVGgfgJSO61WnuIlu+NgPPoVP1S8Dz3eX2 -0GmBlw3Szfp7FYa8QnaCWHafMTCtALwUz4hV4eb3vloeRG9pf9fHNZ1/zSsi4K35 -IL7fHIa/iab7xmTKHXSLf/1VZNV7xbhkVSDlw3E8XrGmEIE78GbH8DfvtNdEfimQ -r7KkYGUw68ycxTi8pyXH58EpS7cgHBcDf02PQGXhD4yDburMhvwY0afAxgdiiY1Z -x7O6TGKnqXd1ay3PYa3oYeL4TO9+OKE67tBZI7AUUmCYWw70LussazNdOGhY1IyA -yDTkQwv0X2n/9zWfQa1T7t5Lzb3KfysJHdyj2Rs7hZIibb4sDf9+LogKWmjVh/7P -+Zy8dxIWBZNSUrOa9ruut6r+5gSItJBpSKy22T4E6VEgmjN0F+yMTLwT7jmbxCla -Gx+ARLoYxq2xVyZf185R2bBKV9MwHJjPxzGHBEcyLt4J1ptCgxXSRFS33vji0srM -sDmCiJP2pus4R3TMzjsob8EBDKlJW0X0hu5yZ900SGlYEUknGjzq0Fk/vIz7XJ8N -PhGC+Gn35HT94rj7oXcvv68LvWIjwb82Bk48U4kCHAQQAQgABgUCSn004AAKCRBJ -w7+JJ1U9LpoXD/kBH0v3A7ZrD8XepMiFw4DjLUsNqsnJI3PpNcOLzZ2g+OJPbdo8 -b2rl4Mmox5HPRT0cC8DN142x0dZiI6GN2vA4Wtqnxx/UfmPDoN1P73YmfnWX+gqX -kLSXKk2fBHzGea4uZZ/ZlKLuAPXVc9aHQQ8pjdOV+q8pH8HkPUJV5f93/21K+fBK -D3AkpSRGvs6eyO6KEMJnCq49E42X5WWEfIBlcD+j4j0SGRhpwoRsquUzhy154aTx -4UDtpK586BWniWuJELy8/TJzLOz7y2dvdSQRHz2KzQZjiyN+I/himalSQ2saxD/P -ScX+et+RtsHAzrMuBtMMRvyuIIxd17h+lRdBstdeRLpQ7LoQrgvC9B3xqlY1X3/J -xTi+VTzK1gGl9UXSs12OVd6slhbQyxsjbfbGFNVUTLeI2tVzh4HCVfA9m6mAZsx6 -HUXJv7c5NFKfKsPxawC3j4+ikn2E5+owqqeekOeXjge0uNYj9vLah5W2HGl+wjoG -VjLj3Vi6j98CPYOlWjlW3WdyB8fpuyjR9MczVSWHyI7eVPF6aaNK44gfF/sAahUk -lMNq85Nh7r4xf1fnQ2oKEtn2DSrktAj8FHMGa53o2A+AhVJKug+Sb8Z0ZB1sleuH -Ffye9Orz6KMQhX6GXp1B6UGuxZKYNWXV0cMdc5E8BqoA3KHjBXqbnaYR6IkCHAQQ -AQgABgUCSvGDjQAKCRBNJJ2bI+b8OlkKEADqh0Nk+SqsfabFdN4qzvYd43rRWgS3 -m//3sbwsRb67vTh1iun6DDVNMdau/L1nvIhN0XpitePIi7KGTFMbWrab28cz8iRn -6QrYXG6YZ1lOMcrwWj4FJ35miAxbP58qYfozXHnvQHhUOw6tgrRXA24cIA5IkG08 -oort24t6bDAwuhhwYeYYbyuTTb67fnVBRlXQ0eJaG+pjPgFpVG+CrXbbzdJzGzW+ -sJg2V+KD/0ijZPWGjasppX/+yOU7njZjjHR9/ppH6X/lDneYBrQw0EJ8pfYko2Oz -c3xqn59DStCBTdIEKxL4WjLV+cpEhpSd7XSPk0Jo5MqNe0GYKC8nR/sO+rt/vojf -kwle2lAqJzgpdDeCmQnP+KzgawzvMePTqVFeIon9C+rXoYBAUpvA4roG46ZwiB/9 -/B/5CR+uT0rP76izBEOocEEVKqxfjZOvQPYGeCe+epk9jMk/8puNTrBL0U/H+eR8 -oTbMJ39jdLv3yU7T33dNiNbSKDntDmkmrDhAEqsF26/dIaOib9VtfI7MfMeKmsPM -AnN4j5SKu1rg7Oibuq1lzVH1PEaRhAd+BNH+TS5PHKbx6XxNkGyMhL8BO1Uwhjor -IW9b5JL4hS1Io/gO9iei8PwsY9wQcCPXN0PLMx21esOKMOLujIk0gViPdXVjgd+w -lLH84jpn8X7o/4kCHAQQAQgABgUCS1wUUwAKCRAWKB8uAHyY0Zu4EACgwMrTtSsy -ToV90IDdI3st0IdzwB2IPQCkE7Pd/4IaFsMJB93eo/nOUeFnkW8CBNf4w6+yco94 -lnNCqGTfr+u93ceL98/xIzSsNYSLXhpUbggQ4cpnJrw9DzZ4JOxQXbdWqlC0Wsmj -ATNB+QQk/oHmydDRmafxRm4kcZoI3BaM4cnJyB9LPWYrI0zcMsgM+0y6cj//f4nt -tpIeSffUowml5MUtnM1lNKI9cW7dKWzpdbWX/Z9VJhusRgbcQYXS4hiwX6mwHabC -+lO/Xob+D9JtsKmUpGhrs1oTQSSR8X8JQjWPSW+rkva5uQA5emQj1SSRiqDoQRf+ -KAm/YqitbzXTA1ZPfsZBzIbgzeGWR8yotJZB7qQ8fhglOmcRjdrGgYnDqH5IDfmA -pJycqKxdJgMerSlLdJC/rlWUMUJSOCVq9FGWr3II47ZzIuRM2KKayOd1MEQAl9wq -+oRs6ojiiozVsQHdCSRKmvyImVq5WR7NiOzr0uNq9xI/IScpxy8RnyVRsDqwcjxw -JH7lTvkDLfv6lZdMqwpHh4SWdcihH2VPLkBgbdywZwoVYGE2GWkW/4jv954M6vx1 -tlbLImJYmxi1ri/uKMzGHaL2wy+i+M/an9oFBrGLzUfDlMl3ZfMKIHkXJOafW88U -gl+opqPigMBLRJXSYyP8Guym0ierAkOkzIkCHAQQAQgABgUCS11JeAAKCRBQrnCF -xr328GIMEACs6TBKZ+XUm+K2XF8txjnytfc2kCiGOc8tx8BEmpGNYch26k9/C6a0 -jJBeLXXXWw9tytp8auM7zAvS1H5xN/ArfBgd6f+mfdXhHn8NG6iilfSjOkFtBxqR -fBo91peB8hEqgbytS6DJCnLg0JZ2DQuDaHAiJXQcEmMj3yHwOCdlA8b2w8GHJs3R -6A5yDEBVvKAV11LzqlonkhW1hH58utx/+eXQfESGnk6Tz5mjZlBfClJGyipxTJYq -HeWm1GGT1/KTJZt30Ni63+1QKGk2+mnxexsm8P/AA5to5KeEZ3oiS5jONnxK9bUQ -4foWyIQv3wcN2WLrNXB1zfGX1Sc7ALkk9Cgh/FTyJbIxCThC0wdclJ6lhMDs8Lx8 -acZB6NvoPYwmKdiBYi26gQHK28fXcJDcAWFucsq7rY/VxkfggXxZk5T7JESnR04P -E7kZ/WNcqyGZMNpwaPdt7Jf7vP3JiUY3lkOEqgtLzHF5oxJnjM+su0qyNA+XTPCN -2r07PpSoS4Wup+TVmwkaCgIf8Rqt3Dmk+RXheKiwc7ogDLmdbhyTZblEI0Cq5F2S -K7cFbfHMDfzBaAIZTthvy+xw5DeZW5Llk4wRnyWVTYLV7YEhGk5srJNhT4QRVlGA -y5rt4AXG8Krq71hfgJzSU1vfVQsQ47TNXsWdVED2VMLJIcVO4i1VtYkCHAQQAQoA -BgUCSnWiSgAKCRD2bj5Bn4T03m37D/94AabIY1L4iqpSjF3X9lBIfHg7Fps6lPi1 -Vz5dwIJbbv9lUImJv9FnxcNHaKtRbT8b/xxQJsPF0drxaPAGYS7Fb2YalqUzuOX2 -IlspONBdTStE3TrRib83t7mMd4uV3yTEIalMe8qXC+wwcAXI7ph6EEqsytgaQj74 -2+9iZcHXVwiXuq7TWLsX+zLoimLgHdlC4Dx53BbmRPii6pO7YQ996rieN4A4P04K -u4S0WB6Kzx8wPlJmbJK/kwsN4ZmFTD8isFGFc9jYPWH8aoaV+GEa4IPewLhgyZgV -a7qbaM/16HJQbQHdJin0I/FvdCZgjQDDabsQJdP3+MjD7KuiSx9uJ92Kj1tO6yxD -2TVtuci45suLHRCovqpcvkCU4HTLQaAHX2i17SvWychPKegk0P24kTkRrCBzj9rz -taPPyTongNDKtxf0+r5JzHTG/+LwnUmhnv3yxO+WAMxZhx/mFzsf/KKfmXpHZrpw -Ju4FCBf1dovAtGorXmJZfKYbTt89oKaLEf0FIISYm2iYc1u9cJEwUilNM7kh2mfg -A/tV8GFWCHgqjy/3ucFFfOfjcoJ8usB/s1bA8DarMw7BqsHktW7qcRg6UIQ5mz/U -WCm/1L2PPS78iYTWzAb+5A69l2RcZQR0DiEILW+7/R38f3GtFyXnl3znHKKskQ2F -ResZBCf474kCHAQQAQoABgUCSoIQgQAKCRATbpzxe100LXE8D/9L4fx0SOtoFRgC -6cs8TDBJCLRCSUfj1PU4DBW8DmIJyo3sScgNAmsIanCQvaczObuHizqsf5IrWha5 -8thfQxItaM3jnjPMvS+fKhRauRaj/fFEHkA7EFDnUKjz5Qd72qV3aLQT4K65l9yZ -rFCG6S1+LkNIwiD/dbwL8YM1ZHKqIz1t5xpAXi9Zp0RrPzhZ6jbl9bCbckZfeDDF -MZt7Rl27EdT3POP2CtXYxQnTdarqmKyG8xr2oQ0Kultqg6PNnu6qRy9bnFxZGRg4 -t070AkS5jxKjrAObnqZPmrIkSTUV+fDqhDLqCidNW4ztUEjSEXoPdSmYNEWB9+/C -48V1e1kKyKBa4kqUukJ5q6wFH/NkGngg0ZULDCqxhRj2aVsX22NkIFLmopk03ZdX -+8zo5H9Rp1xsNtUNG9eole7NpD9S6XSmb5V5bA/BsDVDqDTXpy8geJu9ei3LS04v -QqVXIR6dHJYhWc8jFQ7UwhdiuZfG9yS0bsfvRj9d0rqFP0/89+Epl/O+UoEqWVL+ -GGpQfzIMu2IJNJODfwfE8KNf1wdgAQ1xSaTDDjV9E3mStzrAK7BqAMh8j/Tm7eMd -W9/vRiN94IDUd8pGifBG3E78yHtuWC+ZFKTP7atQdFaquf3Isecd0+a1HVYEEfq9 -2QZXjW0255BUXQOSH+ZLCPcxEiiaIYkCHAQQAQoABgUCSols1gAKCRCcJ7MTQrdR -HQ3OD/9sLvpVJtpCPwGbYA136F9ZULPFcJgbkoYq625XYnWoZA5fjG8x4f27xFJf -/ULt4GASE5CzXpcdFhro/fKxdgX5bJqnSnTzpd1xUO5Q/fD4PRie5BxuQ/b82Hwd -iNRadjhv09xjN9VvgcUwBSm3etC6ZtfpOTzoyngfgLTwJKdKCuVvQc8fu7AtOi97 -g2zbb8Q9L7m7wIdcmv4hKA2pPw2oMUDO3+wpviK7wujil+KEtpw7nXEQmTc4J70i -lQ+XcjE/KvvDvoEnk6Sd2lH2cRqFeFw5Rcu8CqW8eyJq+nD3fmYQtJuQBIJH45Ov -d5BEZsgoElqX3XZ7WVw1JQYySwb2hb9a5XVBeQr/WXWhO2DBUqZlIsH68C8t4Iqw -z1/OkdtFjSnf4QXgWVNDXHGo0MKfWWRhICB0EX0fvGiHtAnRZkpYNIOJOsOXrgtP -EoT4GTkTuzBUmWfxZUuG+MNuMeIhhMfDl3K4KOF2+PXwnN0ycem4/1UbawTquD/H -30llPhdAtNxwwib4u298vMCPge9FD/bg2ywxan+KyxIl+8SUFihsIYOcSMAc1htv -y/nD5Sm+yxf+0s4Mk+kP7IwDaochaKnr0xWmg8JbAeB7SCqC9a6pnhCc2Shz2MhI -RFctmYZ63eKHdEDOMFYBQ/RUGx5OBY9YqJ58D8Lvbhd40aNbeokCHAQQAQoABgUC -S2RgTAAKCRCMv5oyKGGnkCYPD/wONMnFwZ6iLymyXUl+cfSIhKDLRMBDlTSUIiq3 -yTFE/Vjm9uzvJD+WsxgKjrGkjK9hJrVENFA5ZUPq50TtKGQtbDwYX+MJCTRJnGZt -vDxTXNQTubfmwyy/rSp3UwK2MfvOeDkPMi8rPeMi84bP6RPqMAnKEBFkfwg+rXlF -3EG+E4qzlIi9JCX2ws9PyV8ffKv9wbMid8Nw79HeiTaWxESAZg4eHCyxUTcIaGoJ -t0WcxjlR/sZZZvZOKdkCA45mw1TnkxF0/55OAm2b54kGLKU/j/DNGGiSGRdwYjim -fgzp4mXJNV/TYI+9+3CNS8CeTFplgn+YxKvV8QDX6KAbLwoYFRH5PQFojHsGl0eV -1Goyt8Y+MPWiq5fRFOaP3L9wP0pT8sRzB11f7bcngUlZAO/TE0JEizJUHzo8nmt8 -cAhtkdcWuc70AOxdIJSUlI41xz06iE3tEt1Hv0df1gsLQznKvKjKnQa3gJt0hBEk -dgRcnHjh7W5EK6uR/76m8/cP5/rysYk8/4WrUBh6Xxe6FFvlVDumKhRtjBhjnPic -IEmAFbaX/cjkzySQQlp+Byi+AHbKvh6P52pcmkOGsato1D6th/8Dovjafq8sxLTl -9Swt4fh4cdbzHAdjB61AjoujAGW0aAhKj/WXnzsl3lD6JBV0B5qo/bSyonMw8Gg5 -lKwsBYkCHAQQAQoABgUCTKz3IQAKCRAG6qBm45eDL1BxD/9Tn4dKPHToPJj5zGZa -8uc/ZVfbqkASj6L2uYX8a1a9Srod0tYy2+8r8fjwCa5pvJEaOK7Drjo29OLQ21jc -Wu0scAda51/W+oOlEbpd6VJGhbJytOrIJgd+yJJuYkE2vveedDPR07MtIHIf+JZ9 -/nwT3YSCYeKPiT3RfNJtPx+Kj4+UQvidYr3UTQ+AwkIcgqzt7xtdx4R0MbNVKi+C -bXuZv2tD7Pw/NCOfZFiulOIg3uc0qsqKpsw6BHaowErHMt2ltXuVdRfPN8yxNCzG -/GOLbb0Y9lxQEAnotvqOS85pCXhwH13Ogng9wLubda4nlaknSytVmgWN3bCybrlt -KMhfwFM7kBVj9p2SK5SxPmYHhPH4NsmQXbOdSOfUZqHPxiYE1Rwj+pZMIlHO8qvu -hEH6mcW1YGH8gFkUWezZ2LVTokGyuMxLUycTfBHFbj8u3IFnadNHzwtDWXb7kGRc -bsUITtLbSD5GI1lr85xdPnM7sh8Tx+ByHc4msSrVDyng0Kmgq9TDAo3+1Me84e8i -Hzm22w2KDQmKkpPK+4IS6ieKiZl7PoWGBQjza2qzUHpfQuCcHBLkX0FGaYh3E1XM -1wKIFZqzVhhRLYXta4X3O4N2XPe45OZ17mE4xI2hAeMB0YlrtJiZNP6JULiA2QSD -QnsbT26z/Hlv6hy2QVoPnELJxYkCHAQSAQgABgUCSneGuwAKCRDJENkiJRLjx07v -D/94Tu+2iQeOHP0GYUYHuffnTrrlR1yl//iYt1YmBfyACs5fidLDQmcEVoFmvMgg -AUshYDLjKlCVFSUg+V4AbEhBsr5qxUf22z7FCRvPnvyOT1tuTPfgzkh55idLwVd1 -xJANXbEgyQ/7wO7jDWCtps18n/7t8eUptQpp/t6HGEmDYpLBkrRvBkHW/joIAOBi -GsWjYMYYsTpdYlUlmT1RwhjC4bxP6n/y4/YD63hnJBYZBJxfugMXhGfaHE7wXGHw -4zLEnom5rDTDHr1dsldZywln3EIsaLO2bIZeVzX2R96CbtuTfwonYjr83S1wi90m -VeWufYmvqz3Z2dj//+aKdcioHPBTRG2f3I+FSB/SfRHK58mSfQVsKxPpLJrN3Im9 -5HEAM5VZwFyfjfqefO9fSajOqrgTUGNXeTOmEVNQc/+71fMewovMHukyuu0NTgSa -wQA5OUw2PEQKFpQABq6nmlPVafZ3H+/N82T4MoKTC3OBfxCIe7pYzxJXfpBJS9Hi -oC0a/rSpTo0NTK+pPmm0xm0aur+iKZve1NIYEEOKEUpbqJb2dWTmXGeNvLfcPhPW -XIRjfkh9PQgE/o0BiaGFxx++MASG6blcxrPzHbtCmlcI9GgKElTCm5wb4+z3zc9d -h37EHXW9wX6asHCJlHZNZKIiWyKfKQVhKotKH96NnbldNIkCHAQSAQoABgUCSnnC -1QAKCRD1NqpwER1XFhgyD/4vZBTYFvNAyy3ghiXoPB9ONzrZpWIkdSdcjpT0UG1V -57pwxbvMtEbuksOApKc8a2vRoVkasjeLDIZ434X8kZ9Z7VSY35yhBnEDaIwpi+T/ -McO4ZLl5exjDz5/yI82Y0Un+3tGOg+QW7Gp/TyQ8USJDUUxDf/g9934Ri9+tyM3E -LtDkdXcRyJMXiIgFB3Qq0becE5YaQyFm7D/lJTZbglQKYW5jXQnle06NznIlh49D -b1Vet3B4GBFDzGGNA+7OPcA08+PsPQaggYupebXMrsIKeOA55QP6M0JzHU9tBD8l -OWAdbDdUP4S8mdYiXSxlQXS9WAKt2+5R9HwVIoMLXp4Bld3RDiAT2FdI816i0KrW -UEq6oZ/daWpyutRugj/hbr1qSWC0BqlWu/OjigDtzcIMHfg2C1a/ykGsyxg1wd8P -7InkxNAfMafGqutTQwKQZenSBk8r9f44eKLhOT8kPTASrNfNpwelZ9KyiXymFjn+ -2FAkPP6C8nZSKY7jkBzAplOlhc4/IYPd6jDFw1jpPd2/pgAw0VeVsKoapOZ+wboJ -XlvgteFsqKInk73LMNOt5lW0Ar1FjscZhIuMT+HfZPrTfCI5BkOX3PZtd4GVai5p -CqMkdSkXIRUNvkOUDtKockRdb9TMG/ZUpKMB/s5xD3b8UegZGcYouDM6HPa5AiVr -P4kCHAQTAQIABgUCSnFm6QAKCRDGh181Qc794DRwEACE+fHyoT64cinSMcVhpqwD -OcCVbCRvOhLa+2EcDKz8e+5hdINd49lHdf7dWwu9tMINJQjlrsot1RV+/iuXjv8E -ptgqA/FY7kN70jju599W4aJeXHypqlni+RtZlrwzxaSNW99z6m6aVAbWgAqHzSuZ -9DW362f/xZ5TkX4s8w0xciZqPpFOe3Kcqgtqvk+XbmUH9P+He9Wve53OTwLqHqSv -PlOddrFETwJrozOzb89MSjJIVvZwZnss4yibZ4eT/OSAgXLSYNPSq2oDOWlLMFgn -J4stbxHaDT62cRL6UicPJ9gn3gRMbtIOzxEyCpn+ZIMxoMfzzgMko6DnHCSqEHlV -D44FK80tR+mCd3pxCJ1P7Vfx7GC9CyqDyMzAThwAgYu36+Upsct73tz0FmBRNQHR -YHNIN5eIoM4vZ5DEfRdOE8S1Cn0TEafHjMDFD8JNS98VxrtbH+a/1ZtDvAlCPrnO -Lz8wIpmmyslQhY+LSkl7zcI+fwixreKg7GwqwYXVVatAhhr2+oDIRQL8g1FW4VWo -PI839i9553aBeQunHgc8pXAlQ2atgpkAY7c2coDIFLvmzmiSaQnnEvs/s/LwMF/g -wrISqVQ7F4tOjpeKr4X6RZustode0YGd34LYdoIfWhoxim8E8Fk+Jv40bVbk2o7+ -qu3oLbUVXgJyIaC83A6KpIkCHAQTAQIABgUCSnSFLwAKCRDNSyrzoKCqqvJ/EACf -x6QefPxNsB9KU44SioMr5PB6u9ugtoAwh91ivnRnsSDK5xIckchzNjEKuy5f1Pvk -ukwXlwGJcJYD1J8ofFE55Ix2pzafrudsbRmKkrOCujX1niaJyqZtJXQQ+ePJJqwM -0o094HOnZS8SJnqA7DKS0oVaSN/FOtpj5opjsGPd3rdofyH5nD2RVqSMDQbxOGwK -b7GGuXQf+EO3KkiDExvptC2sWbLpOtuLct7gdJKwv4m1Zj/T8tIkVnI9jSAcZS+2 -tn4ubMfDev3RzrRNUgWLCAszmIISPgsP4R7JGHvm8nPEtPQLkKQgnxvzI984Pw/t -yoZsAmRMmZ/qtwRQEJZaaMg2gI/iaJTop/VEPzhSjsh6VMqbDkZ/H11kwhbjIYSu -7JFBLB2cIY7fSV0bQMIJZOf5UT7N1saqDWc79rt2S8HRiExxRn7WKnRG1NBUhSNr -VTVunZxnwGB+BvEbgQv8/JssN08KkyJ448NqYtwL9gbAgWBAdgCpDWGYEEyk5+P2 -/IZrf1Z+DhPh6Z6mPdXDf1RGQ0CQjZuLnXmljHpaNtopt/hgHdbdElrGueoocESg -NCeW4LC0DY8y+t6Pj0wGBjs8AJzk6WdL0zkosIN/oquRwLe/3TeoMTbedd84lnV7 -jKdOADy6OWpLZCgJ/HMkR30Bupfoc5S8psBqMLRZCokCHAQTAQIABgUCTRzWmwAK -CRDAgRLl1yzbpONsD/9+G3e1vgpL8dffcPrEuzQarswPj7ZhKRyXjwSbipx4Ugm9 -BCTokrPMouTSqYUw/JoaFFY7lRCW4xdNAqKlrcviGGQN+Cio0vHYpxvCGkfwVm6o -msoSx1Sl6dhE1j9JOzzz7EMzwyxR1I5aN4PaZqWh2UxrMHZJpiJzCSzNG81V3Lrm -83ZGN2mHNZYlR27hpwY1h6QU89ijbn18vv7WgMfOjPEcS3hDMkR9ihCIzCNf3CvL -ETgDxTujMA9XS9ZZ8zcktLLWVdPXuwubXbZNZcWmCAugaRdQWs9rfIKtPybipsh3 -rbi/FJ5n/tRnL3PyL4O4BUwfyzGuTq8ZPPxxViX8gpLmCgxXn5gPMkfG6WbWpKqa -9jGYgKtXoUTP+r4jhcevCokT3Udbu4YJp0GHEwWB0KtGxQlsPepvVbSWeobw+RxD -9al4pfj5dEXqWaSudI4bNyAlDtRAWO1Xj5jX9P9j4rb5jyTkOQhw4gIS5ftUfgn6 -keonmfjI31bSL15OhKIf0x4m6EmEAjc8zxQAQLvHRG4IBxL13ONBC4ZRS7xGQeTR -BgeOFvjH+4bTaWBQjdrjZaBSC1TRUMWBro6nM81vs399BCUcVKReEEuupOGhRLQh -PPejsxY74NDR3KG8/HGCEJPmERZwej3N6FUqy6HOKv6e9WmIjaXO/jYAfpXDQ4kC -HAQTAQgABgUCSnTJ4AAKCRBYeXlXNEJoTnsOD/0ZGwVA1m2fgP38O6zD6D0VAxXP -hM2mczUwuaHQPs5cjeMhjcARL+CQEX5BL61mDnMDJqHeAnpFnwyt3mo0JS/Loof5 -OFNP+rd/uHOECN9sw+UfatW/MKmdIxCzNRkcADAPWZglX+y/CZefWuNn69XfWsIn -wq0O3eYAj7Utt5Hz5cUhkd6zZN7nkidYXBfKg1i6XDaCTzP+Qd/iIjdLT97K43S+ -37Y8gnj3/BC3zfxapRbLUaBTsQhxl1n3yHFrUVpG7+vI6Ne044yizNPriJ1oPD4d -l8y2CFmuB1tCa/gyYD2EDLGEL8MptrWoTgbh5xmTKc84dzPCme597JawsAQGzPJj -kjycM9Sjbbf9sG6e70JqfFgnn0lfuRrXz4/OiKur+Bh69t5wv8/q6raqaBrG+9hg -jJGi8p0ipFAY56IO1PuD4njmNMm172bu3gTbL8OhEw4OSULUTN7Bpm8948RLUIX0 -aW3p2v1Y6qVloAWOuzWi1nxqZRfqytGdTG9Hmrtdz+wv7mKr4mAfFtXJg7xnkAWL -PwQVZRtYQL+Bh969FgJUL57uNdhn9wbNSySV6HSVDV0YKIYS9XVZsjmFTGP1XJW/ -rcZLsN5BzAJLTPSN8T5u6OkADZCtBnTWEiA2VkB3ymclLD6a95z2Ww8nSHATUvyO -/MsP526WUTzKOluiPokCHAQTAQoABgUCSn2M3QAKCRDNSyrzoKCqqsKCEADIgyte -3cxRoa40CqLNofyoicUgDlU9DvzuTavJ+XUf0opW4AKL41lpz3Kbd7CFDgNldGa5 -Nng3yr0S4/WRSImAxLSQ4gqx3f3XJQHRa+a9CLvXlvaGD5LTFTRONd5TNy5n+qho -FcQd84oX8H6UJ3DNn3gyGy8+VKj22MlGH2agnTyGP9v6y5AbtSE1tWLOHmy0VN1V -1+Fq+IoiyuamYcf09iHaQv+9lrTV2IGpBkh7I3K07c1E6ZcGN0gufhptpH1XRc8R -rdSlBH74dp+EVTUYNXBJZJR41PwegZHqUOWXbkKicJDHbn9EBqW1G5p4q+WCVaXU -n+iiIVLYE4I6sBav1WvImzTmnLjXfDQxsWomWgf2iqFl1giGjCzKVfs/mM8CiQwY -DddYnpHJRGvfVu5/NK5JISJb3hSFZkGF6VJ/thh8XhgIWqx6hcVfpwz2lRfaiNrK -hj0YvkPXxTyWJ1Bd9PXgudFd+Pfgt+qkgaGBXv0iJHVEEeZFhNaQnhDHQ7+qsAlg -3SYKi+bTwsDc8aCSQp0xH3y6mKeJN+uIef+JrnA8uifY0YZQQSG2FVw6rZ8I/0IO -4NuA/P3oKKagxff4VwucQjMNElMUCOqMYIpz4DQOPOhqyEslh8pJl1L94x5R+t1W -sJvikqjt2qCX/X/TeckNh7/86YZTH7hdXEt/34kCNwQTAQgAIQUCSlDEUQIbAwUL -CQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRAAgG8r1ymkV3OvD/9mOvMYDeWA0cSf -JCignNNPiRHz5G/PUtpcNnG31njRCdzajYJ3Qq/Ao18HDAYvqrvmvHsJWx5RHNna -RbvBPyAevZqXKKPjN8YEgmEuosvwlSWxyYKyKH/3A/Q1MhX2f2mR4sT7AQa8TxaJ -1Ef+hYotzmtbg1ztVJBB0CpzQk9vvBIhcoBC3Xndbze9RtA9jq0L/KntT+fMgMDa -lPHkW3uRbTj+ZcrpbxlYXNiBsLN5iqEzvW2MwBNKYYYnHSlPm8cooLUUI+taHDCI -/y0pJ6jHox4gEZgnaMRrH61p1GdRn4BDQZ5iUtJpNiNxMY/pkGpeyYqnA25cdVtP -f1yij+f1zL7zqWKe7mLSb0M9VjZefazWCznOK4Vd380ZffW2as6eRMskTCjiFya5 -R+SMghHtlcLhfD2EGRGfGliujCT4nUDzvX4xgOViVL4FGnxSco6XRYzU4GrM1zUs -evffdt0tc5aPesYcovV7b/BBQHRKo67N79w2s0hgjzFEwqJCcOub5pqgEwzEmswl -cVH6VcZAJEvOMcj2R0btyKjOGfVKxN8Q1gHqTmULn0LA3EmocZh0Ht56ClxC/KQe -6QLtXUXWXs2YwG2/a1YPhoUr9gps5rficExJLJsVIROfk+/ccIfAFwed5mm+qxo/ -/AFrYKEIHKITkhwUV/4+EyRMZKYeXohGBBARAgAGBQJPhesgAAoJEFbn/4ooQMcI -X9gAoNyMPQg46Jpr1SJwJt0gjX0eE/73AJ9IXUYXwTjm57ujeIPPXHdnNG8y6YhG -BBARAgAGBQJPhesgAAoJEGNC8uy8Wva5X9gAn3wcWBQDy+EqAqF5bubCnf9BP8iv -AJ9B4AK9Z8EWZ3PjeSsa8oL5GfO984kBHAQQAQIABgUCT4XrIAAKCRBn53Lwg1Hg -r/NlB/9eD43fWT/+zjaLEUxWT3pgIx4C7/bqa1bQ50fDPD4k+/Fqo4V+e9PFcwnm -gTaMd4ah7e0d29wf7Qc9iMQXOJAiQPfwBlNGC9u2ugkM/r8pkEUkk68+mEFQSXIZ -9oFz5pez3bpBGkZedFAZeEEzejDYWbIYL+kB4JFAjdKJCiIPIjHoBNVX1WIJRMtF -OwKvU+r3XMya5xjI4Yomj3UGKnFtFwhfstSLajRNxtY3f4UueexC8VL2hoeRzfTE -dKb8MK5YiyXe3gIAxxyIUuqPcwZDvByZoLjfjC3QxmwD/+xDVBnNrBVsxPE2U/Dd -aAe1+Lh1Cd2CdeIgXximHo+6DcEfiQEcBBABAgAGBQJPhesgAAoJEKjOKKYBDW86 -82UIAIrkqlqrbYZwpVJgzn3xfRiwTgESNd97orZudU63a7hepsO1eDVBqCTAkJar -+5qI/Gwo3a6xwP2sSOBvLemmdz9SazM9bfrqFW5MrJ9mwXe3JqWrIGLECcgpzVFS -nWks2wGW1lTQa13f7TuZV42wt/TjYNDsXi57iVy2Oe22EunHpjiLA5Du+h92ruRR -SE/N93I2vNnXwHmwEO1T6g1y/KeKXJtmDSalIBUQEV7F3mwepevjedzKeT+ubNvu -gkie8YXLFfujHoGZltJBfg7tQPkAfe+yTMku1tOBfMK5P/Y2odEdajJ6bPaHxrPk -BUPccDGt9hl3n0i3VRRLoYjsV32JARwEEAEIAAYFAlAgPLwACgkQJgc+/NrFdubX -jgf8D1K4LAvo13x0+h/Gjvnifo5I3l7Pw7n74/kgHZ+HBtBVWpU7Y+RtfWYiVVSJ -XMv7Bsms/OJVU7Jsj6Uo5KWnJ03K0ayFCSsqzkv0Ql1FdpfVTmj1+vMxN5yP2xFL -dNjNgZADrZ3gyASTjIStHzriYV5ZMwZGclAARLfFIGRcJjou25J+CO7uVv6PKkZK -hpYlU+ct29/FPrnsVH3eKtrzfKxRaebDv9q1Ya8k8EgrA6pBgAf9rxgbj/fwemSr -F/aPzD+5KX9/eYpbNvwaQMbFep54LJVY6ageTGqv9limBKisyuQEjfSTx68xzssR -7I9EPM1VGI8kSyt2JpVsdYi3bohGBBARAgAGBQJQIERQAAoJEJtG8fsIj2uMtcIA -oItTOv8mvsdHT/tBizyK9Mo6gZ3KAJ4hdRRAZA+qXYDkDPoTnimxoHbUxYkCHAQQ -AQIABgUCUCBEbwAKCRBLzQVnwpbQXZr7D/9YnFmgrWJEX9r5bIjHmv2QHCDq5Flr -fwPW7OPagZsUc5SFiUjYi2mc1UO07GiHTq3kFIEdYpAL2CtffwVrpDrYqZX5wygL -PuaLiDbllyOuX3Q4q6YxWU09jbeGkxVqkjiYAbnwTKhIqD3Az8OLqUWnN9sfsqXg -52sJta196A6NlzJYb7mYf2oltsMsK8UZVqyYNXs6xd8OawZg1Eqk503wZhoNJwBA -LctF89Dr9hseQ6MsMjM/HF0EublOcEMIXrsdSfOhinwBxLVggIMrP2eC7GtGM/77 -xEjGGBR8uQEGXOgomZXLjiThBk2MFFOW+mnJwrCTIaimKNlF0buq0HbHEIeicBhn -KjT9PTdx+ILuQGk62l6PKYXgc+8up9YJjC+K8bvG8jpzKo6yTNekW0qf7Vhz5gg4 -KoR7Snq70kQF9jNpHYd0EFQYmh8HJhwfcwT/LJ9lmZsQh0zV8Y0BiBU7ebLhcXBt -zxsyepU5EBz4aquHIaMZF/tXwhuWEyB1eBAGxUrXrTz6+mqONsWLG0ENgGXY/7MN -SyDTvIsQykPwp5Vv3KByCpPxpOmJUBINmey0UXp44moC7jAPDmEMYL8m3OX/6XnX -2DheL7HMrUzfQ3e5thGKOOJuOLrLy73oo4mObLenjphLXcDGFtly3HOKlBoLFVVC -3uPz6wBh5NgTrIkCHAQQAQgABgUCT7FhNgAKCRCCQpTNAhfo2K3EEAC2e2xdECYb -aaKoA/HSUuNUAwPiwEAp5M4dxUP7bUWOOGA0A9m2m0STG/W25Mqy0GC41eJJGPtb -ZLYNryiTu2nyJ2QdtvGszy115PQPUgwiGJr+8c6ArnvEARBibVF6muuQENT1izzQ -Dxa6vyurhtHb4/hANIpyemXOCPsXXplVu71B2r91dNsrbjRM4fWVtEJAEOBnpP+B -D8kSmsI1CEWSTuEdN2XgAF5EHsSBI1jdT1I3JNyxHNe85N79Rp532EfE3EYBC6UG -RQxDJFx4FgNLrMrA/jxCV7Dfunn/TvCi7w80ZTRtWyjOnaNgmGlUQeWWq3h5j40e -a/4AMgJfJcG478TcWf3bjzaKeCFf036xVIkgFdiBjvs3iL20u200Y8x4kMr2P7o3 -Glueirtcb8CSh291Yn9z32ZDycZs34Itz7IyBtIqYVM1MERECF4VAzTwNv5Wx+y/ -kvf302W5b47rZhxw3cpcXvWSt8Rnbc57J0j9OAS8xhCLtWTKgUYI1f/jMqvE38fH -xDjau/6NXbX/StfDACkXaK/Ln+U8Jl1uFnDoerWH9EZHaAwE5jjNdpZeET+5FPh7 -yLKgeO7K03FmenHw+ozA/mlLFrKE1gSPrIGdtn5d+s3Ts1/9k2IKZS8lPo5KF81d -BN+sJkbKhoEHnbN2xf8lA2OSeXyzWQ0u3okCHAQQAQgABgUCUB2SuQAKCRBchDAT -BJfiHczRD/9JctvserawqCW1kuCGUbOnA4kvyuLUVmkABlKIzGZXuZXnk5eCyDQr -/SoEkeAIYcfSEcbOopH/znkBXNo43HgDLV8C/rr5DRjYMDA1TXbXrZ2RattcsA1T -DiwTilp+sz+GX6MERbqRzsAiZYLd8+GItwfD8UgPvz8Q7IttcLCDgE0RJ6dzp7rm -VsDTbVg1sa05SAc0lH8gDnNZKy8dhxgddVhIfe6BVMhkSRQMyY+hlIbwX3HPpcc3 -rjVN2s9IAcFJpiQhPMFRUmB+YgBx0abcLm1LOCcOP2eeCPD5h0Q5nl1hSjn6zrz2 -yPOQl6pPqZTSsGOlewGgfUnE3iTg9skns8sQyFny5uaqudcPxb2UMdYr1KRpEkSZ -HhvcMYh5q43i9DFPGdXPLz2IN+cjzTT8Gz0kUluPjv99Su0lzv2ZlrHGwnofZ9pl -3c4R+cJSEieJAH6L6hvpHUkC9n1uC7ILU0z5JzGzYFwtlnMLgglOGka5k7grwBcz -gyQJWIL8o0EtKux+6KSz9XyY32pUeD9nJ7GGCR5Fqjg2uGCcZLgG+D8cG2SWKqkx -TFwF4VOwYziGPcoJBS1qH73GHH80FOBwP2MXr2MA66litXx/3mqtXumWJYUHxYvW -E9VwjiGcfyofZiaaxfHrHAQRtsDwtTi0jQLVo4QXN5AHgPRMyxnQYokCHAQQAQIA -BgUCUHrFEgAKCRCjNmdp3UDmhw9+D/9rGXHz5TwzlEKDVGL3ASPRSg+iN/WtdZKD -XUukEyTTv9FxyEeS9ASv9wXqwceHILHRwoC+upNARaJCneQcvl/GSoUO4XELK90E -zUbS5SMlIHIzNSg5AzJpQGhOPsy6su1aa0nKMqBQlslTuY2f8xUI5opUwbZe4iM1 -2w1Su7pG1a0sv8PHzxbcv1lAYyF6YI+jYiTIc/RIf6C/kEQEdJYT/JX+RY+kGj98 -D7oMwR86vMvwvtqYtaVZq4kx/oveOQJh6IQjmUFkVcdsB5qKvacsRC2KKbLCHieZ -9u05WS+JY9KOiRsO4aSFsXk16N0SJOQAaxu3hkIDTAhCA0taysJivltJqCz7g7Np -XBMsNNfXxXeF9PPxh1BC5tbAitgEZDlnAhs/Wve37GH9Ez68nhbyg2oiRgMjkh52 -ls3MGS3PtmFP2gK6O62UBJC+wCIGS1brHJ5H90+LJ8OKaLXl5nnsnlrFFJSL0YbY -UzFq2u5K+J4lTMEo3E6/Rtf+kj9mU3iqXFkq5/CbkBuYAuPkuf6J4PxdGWK/vUww -kyXZgIsABHgRNyC1Li/xo5JejLG97yclsicMIboRKp1mMsEnrnTcaJAeKzu4++X5 -DcnW5SNiiIi2yBFqLLmj0WwbseOu6ZWGizYP9U/TFfyOtuWH9tmKn28TKOlLtFYy -u1F1FKsvjYkCHAQQAQoABgUCUIMjNQAKCRCXMl3Y+f3VBgJUD/wK6LH/E3CSZM4y -kMZTufQrzrBaj3R6U7s/1yUK43Zj/5Vzcku3T1psn4YAKyf7bWAXrJJ/BYr0lhf9 -HJP8B/BDMlKZZrHyubRHMAwFP923E2Rk3EA6INJL6/s6v9fpxSay0Zmk6GiLB1vs -8KmMPFD8hTkDgYqPwhCmjzEbVnahRX/V0E4C2lahI5sqjpMVT3SXVOWI4GAvyYel -Ne2MNNeg74EsIVsL1gmIzy1NDLOiiolRtzKbyln0l92ybhfJy82Yu9uvwRreWSbY -HM2sjTmdDSr2CzyCTjVixEVOyB1ZVcA+3YfOuy2QqLPfnB22iuJy83SYZBOv41Fx -TTxm+AYV7gC80bi/4Nr3iv+QFHq13LZ5BU+LAsCRhLb4I9HhqhfoxOlekyxz5aLY -1iYNzaDgJkt6nnAACfb4SzuVTRr2wd+KdtI5KLqA/QITnlwtCsSmu/giHkPkbXEg -mQZ+0+y/iathA6xm9lwYd6erDhpp1cXCZdjZhn+8Db/jzJuiWbmvAHdcI9mIi8J6 -/qMvYX/YrNk+ioj0CfkZd2s9PvMd5N3Ka2BpFv+qHoRB4YdB/9KLlhABV1/BNOiU -3VqZOA0mpoje2ad9E+8OXpkvF9vObJB4RY99knyD4KFUPwCx0ED4ffW6teX6EyCC -VbPkeZ3IS8TuzF9Qr6161kv5jBr7jIkCHAQQAQIABgUCUJBNAgAKCRD8UTvKX5og -j2m8D/9z92rqpFfto0CxKdefFuHHbclokxLKTn0gWdxeCiHsp6XIj9UlhDdP9by2 -8iymhVXZ8RMqy1m79MW/7PitZVF2S88dbpAA1tuGAOtK0P42QRmLIpCjgiFGO2tx -57gc/VBn9eBC06fPBGzMk04vhXmCou3j5q9PKhggIu8FYijT1D2f1ixsV+1X33Me -V4/nuQ3T6Z9r6+U21rgi7yEB2MLH87HWwHND3cF4ZztFCc4NpMtZ0eaHY8wIXE83 -agU8WCIeWd86xFdd0bPP6CziJ4FiFgx7PpaXl5y5WMVsGEgZcNJQKdjTkdgxFZWD -AsYjfVVuoAi53fMm0zrU/145tKmiqnlZhIDlDd222hX1cggdxjPWCRjFetjcqjvf -TReAcxa5xdYCGeYre4cNKCpPUyPtTkXTpfZaisGvzveGK7dWuHdivn/n0YBw6uwa -lV8jEIewwo4eS6crEucd5dgpd1dFDBujDvm9vDhNc3DzPRM46th3gwaKkEWOPgRL -wFlvbpX3UZ14HLAUIlPB3D019ujx781ABZNPS6XihOFpOmMkfUjfPT9CTS6FQ9N3 -m4F7sL5iOqH1j70b+s8dkk46XJdIEXUtnHRM0C5JwvpIUZ3nnLm34awzYPPWA2XZ -TTW52xFIBhaMdZ5iLJQ8fy5ALT8Zt36pTAFJXbT3SqV8qw6Gn4kCHAQQAQIABgUC -UIMqzwAKCRDXAPpIE7KG2cYhEACGYomRClHCtu3YI5WPqGzVt5yyZm/vADJCJSMM -mf7K8R63Zzw26vtpQb4cTPgp7WGhbXGQtgPlqy+zJ1Xh4qarpJvzubbSpRc/k0FM -L8wokV3wUKF5pBQ5zzs6X6tnyVVP4zrsTJK2Hvh85LNSmx7pTyw9Hn44grxuJLvM -DJ0Zxi9sAoGiI6oucmw6o+rsDwQZBPvqNVS4u0UYoka9Nv+3J3ecabsZjYknGoLG -IKY41x2XVFYhJ5DTwqiPqkMsuvxxz8grrwgF16MQ07aJ8+VxOOJn58xvmorawreP -iylzsy/bsXEqPIiSl7+ZY+GBZNJSE8tkyLFkw+VPk6erLft4wf4YHw6vKMcwY4ri -W/Ni5D0oaoT6JMpEZVU1pTzLUu77aN5vMOxaAvhrcVUCa9URvSrUSYYg8PsTDvjC -a28AtaJhPBKxNCCNFdekPhP1q1pRSCNhHE7F9r1HBU6i9dSj4gCzmtHMESdRC/wJ -olnTUxkDw1/dn6tz4DSWdY3o64xop89Lx3P0HCE1y+ZnLaCI89dj4qllmpE8afZU -gVmDCrXGrsNwImDUlk38GpDVqcBtulwth+ie4vXzDwY/JRSzpVUUu6R4iMERY2Y+ -Y0dnzY+Gw2K3Qw9k2dmHbdRll7rQtz78Y/DtuIIhJfzJXMwxcnb7D+t+7b1Oaehi -cTi1cYkCHAQQAQIABgUCUIP3IAAKCRB7WFswgHwqh6R6D/sHgpAQYSP51Uspigzb -/E/e0/TRY1TEM/V65FHx1WbEXBtoBas9aKj9+OcC/YHBeOqUEvKsNd6T3lp2MvdH -ho2ckroJxFJufWQyXTp5sCxF3U3UnhAC11kq/1N+WM7QaiPVoyKQ4vb8Nh1LoSSo -JgDGXl5lllG03VVJKTlsQSpxswC0T8FYSgD/g+tjPJa3/c9puTe+XEWRFOUBFZx9 -mNb7i/UAyIBCogJO3UR+Gr+C5GtN1/9ZSDFo9hgwdSeXsQGMGLtXiriJ+sgnu0hG -9TLuBFLeFSs+7MvQVbjSqsOZ8z7XtnZzUNzafacuVoLwMILtZ7zu9iEQ8gfRDSPt -TrBe2lY145d+KTB6qacVuOUQw+qJkXaA2v0EF5JhpKu+Racw4N3DisNItehOn4/R -fuXj8dCb5P3fnTPFI7YD/WdxW8VAfHkjrUmwk0GCUVzv+ib9wSwLnCnf8OkD+n/v -smU4ulzhUrqn5Arefqzxj1u0I3za4MnXg8kYegi8NcdRZfcVFDFOcowefjHMcGuh -TRHnfN67Bb55L7n120RH4HX9XAXxVFmRAFKkKUS9pzkE5rl7PEceZZe5aHEEkU0N -NzrF4voBaI1mNoWuAPtx3Hal9GVVAgAnuD+PoPeYAxl/DZFL2ikxot+mZsmjXm8f -hMA+OdyoutPhADNCokh+b5KAs4kCHAQQAQIABgUCUJqdTwAKCRBZZqMKU/6CMDgL -D/9iPJGOFseCEqRmr0NZwbQKBZwCbE0aaOZWoUYVekT28ZZPn6UKlEcUJEWdfD/p -vO9q6BQCOqSTeQd91Hxf+rzQtERse6megrkl4xplkmihW7lvySczvDpgW3qsFQTF -k5Lbs1925CAiEQ18KeRU/r6vItYoRwn8jiIrTwVK9DmFziiUCqb0MqFf4Abp/uX9 -tMznorTuCa/QYIILrZpFMuPftCnw+B4rcPrS7WnSAN5l8Qe69lFUrlP9vjc7rZ3R -HIx7cqzotB0R898HZDgQve5HuBb+uXS9aPw91j3aj/NISWIio8BBNjTCu2uzCgio -p7zBx/fS607nBNOWFVnzbKeIa7GDLnsIVWUrYzuV457YnDxAcLwrUwLz4yrVDiG6 -yigkF+3fFstt+c0NwOg/GVvk1ksV+1yVDUHf1pStDasRA+cZ+KQuJV2OQG/mFUCp -VCgr1KeFzlsREx4McBcylet55cBD5PGMwpsymZqGrONP1EMDH8Q2un50yE1pXJZG -mfPnsjzhxWe0BzBnS+dYZVq78TU3una2DFZS3rJc+7qBRSOh4MDerVEfW4c4L0tX -xtEw26SPA8p0nydPH49zeBMNM53wAOPpM/b2zwANn5da5I4LZducQZ1U59YgCXEe -Noh396XMaCwzKM84hQ+SD8xxdC/crX51xyXe53Z51hYSVIkCHAQQAQIABgUCUK6t -uwAKCRBJOzxg3q6QFubZD/0Q55xVkJ+SI5/LWssubsBIXGcuse2qfGpjDgWUot/B -zj03ynsHh10nU/3sv80n/eAT74zkAPWmtR4iS4MODQqppQQ4oaJhxXNCRuvx+jnS -6Ehz6tqvqWjuSoDVhsTryCvS/CvJ7Xm4VFEyRLpH7TT8827e6AAKzlkAoavCHZ7W -Tgi6UCgsonAbOiEwcNkMnhDNPMV9fcrgBX8nzuyS2NSlBFVhGTURhUOMyuEcEwQq -4EKCCek1po/xQldBlsNCuCakvgZpkcXLZkb4mFqI7gv8X2IMxAg3x5NW2puqm3YE -j9SDbaqkmtUbQhKgrZN+ouQ8hIiT3ua+VzfQTIGX4G12JjO/gaO29GGYOuInV20k -jAatIkRqk06ph7EL9y3pVM7p9trquaC2s9fYXGVyOkBH1WG30NR00EKInQbXY9AB -bSZ+5LaXE3xn+jmtfL8vaNfJO2rEY2TNkFcDNBPglZtJQs/cRUYaPAKuhxhlr8d2 -1RjwLlL6FfhR4y1rIuqK6ofwwzDeXn6JXRznSFoK6aPptHUzNnXufd0LEoI0hbnx -Zs5bk7MhyUQqC+kr3v3dDoZmpbjrUGek/dRW2iJVLtLBMNvsH07gWGbJpTsnbDhv -ke71mDo0C1QIq0x4/OWodEOeZUGffeJwgqYLVrIs5hfokxak6puoyMXv0JuTufLD -mIkCHAQQAQgABgUCUdbkZAAKCRDK+zutCnXId1m5EADFODFO26s7Sl+kRAYU2D4k -riQtky0UWp5QYIu7Z0V0aSmXj+axPFpbo2Y8t9FIxGQ6rCJJROhb6FDv1q+J8X2h -i52nUBqwCK+q8n0QjUu8/nQRIvq5MW9XbefaxunCir/DVemGpTZ9xFZ92VlrnGoh -DHqo0NMSyFb/pD+s0RFALLjZNcEtPDW8dK3Z5g7dCCLNazZ/kK24lCC56GjXtMp1 -Yug/kgJJqVXAWB9Tl2TqWOHIrxeO4rjjvKExQMaWmuTXlpldlCDrWkk9cm4/yFkH -k+zzMYpSWnDHXaMmaqDMafSPYnKQcUEwlDXVbA3upllcr6IleZ/btdm9eDzooKD6 -CxOZkhOhcHxP/jFj4TcakLPhuWTv6csBdhOTbclp3VcJVuNojscUV/LU/T23sZfy -M2SlprxROnb+HFS5sSBaWmcwNlRaOIMqb/Yn22eLtPVIpf8PdqwgRcLwRw06TMbm -PzIlx8sQTvf6eLspbbhm5u82F5Aa1iw6e6kfRcIgeh62I2lNutUovroIo8LuUMlI -6ly93jJH/FUUbuThxK/+MsrXqkTsAalFV7mnPl6vsHQWtY15TevYu92w3Yk8ABFx -2zpaUkhwHHkcdb0xVbpC21zpQIyseyzYtDHm3w7et1Ht9WYd5z0N6sytT7xU9XEl -pxeXhMQ8v1fMVFmpcRCz+YkCHAQTAQIABgUCUhOwvAAKCRDOyZQtt624b4EOD/4w -8CzwIWSQUE3E/6dYBxWNE23wXrbWaHY0u7QJlH5VLYNSkE03sFQweT/+uHLsXM7z -sTAXxV9srgGuBoiQ6CLRvk5GnygFN1cqpBySbLfmKUZzTDTak++9quMz5B1Hfuyy -nFEDdsel8MLGiRGIuc/+nBHIQGxmFWgmYTUlfpHu/WoiykHvr1THtr4V8tLKEwYU -J9lAwNVTxfU6NywP5D1GD2rk63EKSnMFr9rC7DeG3dMGVVLOVN+K9Ea2Ux3rCVgm -0y9cC4+2E5aOY5lWQhhI4sW8PTWHZP/bhHm9OUvmGMnIckj8MwSkrpEX9pm09LUd -onJ6OHImAKBOhyXBcJTbiQrWlQEwHxpY/zVNZVwbY4BpXMdhVN0VA9VU3E0RKyzy -ZtwFCvpXN/Gn6QoSU0oANkRMDfZLSD1WnydJLRpZZ+AsT/ysxOG0wzDmgBoAkTOG -g/KesKfXrBEKSqNhWeAiQqsjyVeIVO7mG6v6MBUl4AuWM6S9fl0ZsnT4beVVsHSv -3xSkQicuw9dLw8HspdbhvC7zRFaKRO+xvjHKaSaJNXCDGpDZLP6N0s1sYXqzSjrc -x1bML3KZm5rWFHZjtnoPeox8yV+RfjyjZJ8CfXjEwjGaqovWRcsX/heaUOFd9I+V -E0SVGQzLARQg0pDgOODNZ1EpilidxQyafh7ZFmYZl4kCHAQQAQgABgUCUm5ZUQAK -CRBsZYDne9dWxOx0EACLewzuN4tykLfQUGktlZgx9/qLK54t2FF7XDPgBwE+864+ -NFWVzDEEqt3nPk4FSD97Sz/kSwdAXgLn66ca5BmYBHNOpPwW1KS8kj3upLGsbKOw -VY9QU7PY/9ymPUrR3tn8CF5a/yH+yCEuDCYAibrhG30LJOel/lgMuPl/x8KexNSa -9PdU7oT+l9yyRViedRX5pYp3l/68eE2ys36wEKUziEzjHe+iGBPwsyLVQ0+W1ofp -RYh74PLeI3mJx2QiFMpETWGI/x4L4bhVR1O9BrBOIe2WnQUP49JG+Rr6OdIzy5ko -svwwjTDjEI7T42HIf0PQUVU+iRp/EeSBIfH/QoM6VjYQLBITAcoTWALlWJmpKHUj -JwyoJCz4ODiKySew6hAGMWz/5DIaLiIeyxCiHmLr5TRBdwNDAbtFolXvJNyakRKa -X4XRz2IWlRHKEoyt1vzkR50SZlEb+SkMi+iV6vrLn4K4qi/Iezl5fICaJBURO8yc -LT8U94o7teo66xio19bc1jd+0MQkHQYs7PiYasYDWfdx0a+lMWqqllxjY9V82nwv -UGtCbrR1rSTAB0/EvxZPU6G0VXbnGrNLDLVuc4kzWoyMtEhlTexz6aAcIrl8mPSE -QUfxINQ9FaQRx8vCyhGmjDQK3rA/6nXurgQRsuXSWIAAWG9TA0xV+KOuwZ2beIkC -HAQTAQoABgUCUmiZvwAKCRCNhuf65esMEJLJD/0bUX54+ip4fXAyr8vEzo6k+iGc -6Dcdcqr9OZ977sPv7uliLOZXMOrVg0NgmmSz8BSPHQsm3bPtYCrnPBG846lvysPD -d1FsJ9t/3+GuRHagJfqiRPVqfr93z9FDuGc9zMRQgzou0n7iapqJcZkXKqPJKbVo -5LRcufIFkO15IhmNjqBQjCmmubDL1IDz0ewxMYmc3a9trJYjKhzsM6LWCBFRtIJd -SztuNYYMQkvCpB8LGoaTTo7kZoTLCBJEBkdLZ3LYU5LzOmSsO3kmJgKkFybfpRkd -TzNe1DDST7PT5vEKaFUJ5DUW4xm9c9OuNXmoO4n6vVcj/zQOahmnZSrBzxBypg4N -L1QDTgYrNWDNxTbFc1IxUf0QGIklg6GdohUWxxC7E8LBiiB0sH7+qJe6g+ZVpZ4I -7lDwJGVnYSSBPQHAMcmwz6Qs+45CIsJK9rjWDW3yz5bIIP6MWCnNAbPzPq89Jpdo -032A+DkbTxJDZU8IIrUXhmwypiG7kUn5kbOto7LfC3W3FkFflqmkwoFlKQfZKEkD -CHqMds4DeRQIqkXibc7fmKy8i8fO1zT3vwo0eEaEF/rphF+G4Jg9185QdOVLAmsJ -oNAic7Qp53Rd6PCY1ZLsu13ZXWh3edYZD92puvZH9BgpdPgvFkz8Hf9eTkWOnknF -vg5xlnr8cO3UcZGGc4kBHAQQAQgABgUCUpDt2QAKCRB4GbAn8bBeNUxoCACM5K4C -1KyAfSofWnwe8dX4Uak1FjR08BOmOwas3m3GRrm5Jp1u65DTmcN2ecS6SyUhxcfz -QHjOZifWfkyoc0PBUVr/mkzDYNkIx6+rgkMRlzxr4krhBI7LgA8O2GY6znbwxD86 -CBiyzGn/qJGHDyApn4JuyMpPc/sDiZkyDUbr19A6En0GxeoSmzSeROYizImcfrzE -giGYBVrCdtjxuDg3xCj3MmbfR68W1niJz9qkgromuCopHffEjY6nS9ydX1RBoj42 -PDenDug9PSBB/HVCf2l5Enopk8zm/dPelo5ZmIU4Sq/QQQZjBgwlKywwjGP2rW7g -ZUk+LGFk/a09UdNRiQIcBBABCAAGBQJSkO3zAAoJEGXrKrEvDseK/McQAJYyRKeP -ZESM2juGPZ0daANZqlSShftIrNlv2AFOQZf59CYhgiZ/1rxwAjYCBpkn0+M/sza9 -HTxQVJsUT4BmdLrKOC0hgzqSq5umoj5VLlJCiq66mEbbqRbh9swUh3OHNopYyk4k -tVe3IwcIID3zHBi+3a9TiskQ5WWxTuiVeRx5QJf6ME7F/RTI5XK/v0J3Z2K5m15w -NqzJvv+Pr0gpdrqxa5VveKVZdnnBuRgtxDYSLVQlV9aiKH7mq0YsVsS/9pkJDBGV -eyBw0mo5r8E4MU0oLSob99t8onaYblg9dnedJc/CqJ8SCbZh8ZQsx85I/JUo6UtK -ruuMXYy28HIUugKJ4Mh5qR2Y546MCkvzu+HrH0exZ8p1Bgq0Aw5hWucnz2WJ/8hy -Z9EhDWQVv/Dy86jG5zhy0gT5d9ZiwP+jSD0DH59cmZvmJNBVQVAyak/j96yChf1P -iiW02d+o7fs0Bvxp2XHEISG9sNei5uTwTefQH/k6o3fjh8xxY4Dz0lMqsRg3Y9x6 -HvDzt8K+9/5DmhWy06qZLBtAy9M6xCuaBsWVRxXfu+AEgU0WsetVRN1tmmQlC7YT -wwMO/7qVZAiyad4AzqTRCYYV2bo+QA4IJNGmdDOZQ1VbXePmo9yP5DULwUIZKwmD -coK8yBZMgbg5HmUdu1m1tjRAaUt7kWJBL2/KiQIcBBABCAAGBQJSkO4fAAoJEIGd -J4oOb5kqL1YP/ike8jUFOj6v4/eiz3Caj5jCEMS5R0Y7hGcVE++M0kNUvghRhGAv -0GZ1gAT4aROX/CyQYhKb+7w8x5BsVTVY5E2W8FaI78RqNN70gn7peyxk788oB6lL -x8OD/w89Yq54kcpaZ02C8ebnN64GY709Mw4+beEHJJHadEr1f8v08/8Amqn9PQij -l+MqHLWQMykb7XygYDgUhAjLDdZqkaCrGQt0G7GfM48aT9c2tZSCSQNzmN533sov -kPBKzoVmtZaaZPbjzWn8+Bz+mpf4F1ifEtmO9QtrR7ULCW1253x2anL2XJuog5oM -Idta5grZbeKtTBct9allaUhajsML37w+sNiAO1QcsOOaLqVApKGJ/lG4nuxGBYdC -okbDt4QiTEs8MWFiiKrHD3XfxcrDuCclWZYI5RWtiLRo+V9su/5woWfL6Otucmt/ -IZW0uNzAe4tuItgC2/j/KnHrpNDw+9+zucYVaT84iC67CQIb0RoKDP24Mle+SJVa -nKQJEci9cdrWeuRorrVWX4m/7Iy8oGZuFdaPwrkrjt5Zj+DnksVdFvxKAHGN3fLU -750mNHiSu3sBanDmwGf37vgUSJACYJFazxUACF5gPiuY2MTvuAVzD8OzF6WcMYPS -XfeC20hCBWAqRU08Yd8y/ltifTOtXg6Io0TzUoJf1m3jHyFMcL8Cp8POtCNKZWxt -ZXIgVmVybm9vaWogPGpydmVybm9vQGNzLnV1Lm5sPohGBBARAgAGBQJKUSQRAAoJ -ED2vVKIe71J2Db4An0+MEiRTbpt+ZZBuNpqYSY28gv2SAJ9KquDuWtgnCM9gMdz5 -HFgo6TJFh4hGBBARAgAGBQJKcrYGAAoJENTl7azAFD0ttwwAoK5cgSEBX4fGscdn -lEAKytEAX4aAAJ9wigIVnpOnRpn4i03mdSm7wI+buohGBBARAgAGBQJKdXO7AAoJ -EGnSph3iY/zUFDQAnj6wOhfFPzSL+cXgFnIhh+xozkIPAJ99f2nOt9okJNv2SD9N -FiDgQi1+rYhGBBARAgAGBQJKeF01AAoJENXKmwTyxCO8NYIAn1cAAlHwLyKDfy3x -iQMcdEFKj/yoAJ9MyHdYOmsf8tSoui/1RHtKcazRRIhGBBARAgAGBQJKfTCPAAoJ -EISJsU2IB1KbojkAn0qtYk1l8o5V/ZxZAULMCPXp9f67AJ97O7cdBYTIgwmmMTJD -8MHBzmpOQ4hGBBARAgAGBQJKgpuEAAoJEOMhk7bEKb4vA28AnRPcd15huVJMDdvi -89LIYVhvCAeNAKCIAVGq45J7/BaCyA6jVinNIcnWo4hGBBARAgAGBQJKguoHAAoJ -EEEhx0MxcOvpvYIAoJY1BiIjHbLpPpvD36xLTCexCrqEAJ9dr2Vleuyfc9uN6Sej -eYtZaRrsOohGBBARAgAGBQJKpDJLAAoJEMCeHYmVkw7ePT4AmwY3Pl0opZoCIobE -SQOyy7qmBiQXAJ4vNUpoeJ+hpzNobwI4kfuSvLP8yohGBBARAgAGBQJKpDJ4AAoJ -EAQEa4VKLhYrNTIAoKadUzpUH91W2dbuKj627In38DWWAJ96eqPCK5pOLuHrp+Q4 -7y1kSt0Aw4hGBBARAgAGBQJLVphwAAoJEHcBu4WJevOj3S8An3lfK8J8XNGVCju0 -74D8x409PIGnAJ9dr/Cv7c9yU3heA8xjI0JdD7I2eIhGBBARAgAGBQJLV7WwAAoJ -EP0f2SNT9F0yLwEAoIfP5+zflkkFVHbC88hBunuqNLaaAJsGGAXKZiFRvc1Fftei -2SpbAJESfIhGBBARAgAGBQJLZgGeAAoJEAK8QrdD4l0eWR0Anih4dmbRt5jZAnLJ -AbzKZWlSrImAAJ0SNEZRbgvE7dc2tlg58sKcFVNc/YhGBBARAgAGBQJMTez/AAoJ -EGUd81I9I/JdNgcAnixxWcjgKajZDvsWLl1eLeIWzjlbAJ4z+jZ3n/keAkyHNgfk -YZBYMxWdkIhGBBARAgAGBQJMTflGAAoJEL9gcItIQmx+1XUAn2X48K0M4+9Ic1uZ -eWtY/uxZYc8GAKCc2SgFPJJ0dMyAsnXZ4KB1cgTQuIhGBBARAgAGBQJNHL6XAAoJ -EKcaa39rhS830iMAn3KrzlvE2glZkZ8m6pM1CZIRLY1eAJ9v3GjcZK09zmFi+cEN -7GORzdkuXYhGBBARCAAGBQJKcwnoAAoJEPYo65NHQyBsL3QAnRWM7MISobZ1b8g1 -kSBGocCHbTudAKCe/8rNCKxror+bL+XQufzgJwuQN4hGBBARCAAGBQJKdEK+AAoJ -ELz2xg9ugWnSKvIAnRgAa4m0STn21V5DW8NdO1Lz0+uMAJ42s6u136o0AgtJ99Ye -8aqYHKztuIhGBBARCAAGBQJKdHc/AAoJEMN2qNrxvNtzXcIAn3PG1ToGcCIGvpr4 -gxCbwfepP7/AAKCGIfh/6mAwaSRmDCtAOFnQZWTIBohGBBARCAAGBQJKdHfZAAoJ -ENlG1WoI44u8oKgAoIbHjTq3ohz6IdMNaaBNAB8clkLIAKDJympQVliHrfbYQzdM -oQ+xQ05GAIhGBBARCAAGBQJKdJzbAAoJEIa8q/8cAMeQI6QAoK2c75eAANwOpGAR -WBey3OypPoZVAJ0Zhr/bRCb0inFZFQL4jFaKezL43ohGBBARCAAGBQJKeZC7AAoJ -ENTl7azAFD0t1XMAnA/lqHdzO/I0jwj7ceSG6IkFebKtAJsHj4aHWSdx41qKpG1O -O7uFlnIOjYhGBBARCAAGBQJLXUlzAAoJEAYytoaKBJsh2wkAnic4AZ0J29PVxtf0 -PmlJd5RrAUd3AJwLAKoqx9Ya4kFAZbfeZ49u5DAqAohGBBARCgAGBQJKggz4AAoJ -EFGUgwvsGbN4OnYAn1I2MW/j/3i7UIyljaWWnkFCK3pVAJ99WJc0bCxL3zYmJ2yG -rOW1zL4fjohGBBARCgAGBQJKiWzKAAoJENw1Uug251YEMagAnjRUDa8CjjpjNmk7 -Li7ZRolyJ1RnAKDqAo3OV7CPxtiJs2hNB1SaYK6HOohGBBARCgAGBQJKtPRSAAoJ -EEEhx0MxcOvpvzoAn3f8dPpWLmrzJbiOKQiR+qxzGPf+AJ9b3QOGsVX2l7UIMMoU -6iUl66+7GIhGBBIRCAAGBQJKd4bQAAoJENraec14ij9MsVIAn1K/eY9BX5HH0OWX -bX5n6Lz4TNfXAKDR419xY2PhaNHMDyR/Os1Qq/OjeohGBBMRAgAGBQJKbJm8AAoJ -EBLbee7EdjulOVMAnjrWHt1SGD0tEtu/iOpcNpQ82ZaHAKCW5TTDZJrxlhoLQgZN -zpFBiqWvB4hGBBMRAgAGBQJKcWcMAAoJEG8Xpa/B8k6k9NQAoJfQCyW6/2HDSozb -wzcCoVFVEIAlAKCcLO8Qwi9Q4RVXYNIHNK2QJyCNHYhGBBMRCAAGBQJKdMzFAAoJ -EHw7eXCIx8H3hagAn2AYDgkQVuf0WJkUlpzMN7dWZIh0AJ9sUi6pXB2JwO6mnMrT -htiLD5CUYohWBBARCwAGBQJMusziAAoJEPKthaweQrNncOoA3iFcKpv1Hvw2ezvV -62L7uETJYey94GpVspdrsr8A30rhkwNDug5Gla72BWDc6jZzfJ/UihEujzw8aSyI -3AQQAQIABgUCS1aYbwAKCRDCo11KJDoTKfFnBf9eCfsNtxNUH2bMbHwE70Gnf+ox -plL31zx7cCsxMWiaCFcaU5WJWD8cxByXbqK/ThlUzjrG2s2KOucslGi69MW4JmwZ -0yHFTWy3C2GQ7PB6MsULHiwA76oroFDuPs/Cwb/QsZo8rrnTbVguCP/p3iMR2Apk -lpk9zyq4DzG+YT/5zptRcQmJSWCPm6j+2GZRz+xvBsm05cg2J4yMiKa43nOp29gj -cVsLNx/qiUfjmPJgcRM43VNZ8M3397KfOn/3dxSJARwEEAECAAYFAkp3MsQACgkQ -loDr5KmRk+KA9Qf/a3RNhSMNP8gC59EoP2kLr3STUVjVjVqgDIBKnIQzUCeOeitW -XL8iFjqAJOXk9UUb4nY0KazWwWtfR5b4t5U+ptl53HGYgLQNfFDLbJeL6NgAG8KX -F1ArqWR1M1UMqU20fSdy153lpZhw1+E6mv0es6bJ3QyZRKBU4j7ofaOkuHtbWwdD -1hIP54QOOep6LjX4OpkbWb2y0d24hoxQTh9Ekw1KLFzL+x7EUKrPSylKLMvjdqc0 -EnEabW5ak1FfF13pgjUREsY1Zu3ubiFOYghs32uejfvzs8K94QeKrMXdL7xprt7g -LdmusHQM9/iFaXc9bKrJc51Bk+kHHj8kV48mkokBHAQQAQIABgUCSncy0QAKCRAx -/Ofn3QeUYfE9CACab5hiIaHm8OHccVgN6lEOVnXbGU4Z030LeLe3VUXFTIGDKcgM -6vDQl5RyWTYl2khRnswy2aD8gQOBQDPxUZcRYQ32F3vMFiOble81k8Dc4U2gYAXH -f75tthoSlKv9rUXKO4M4eUm/YS3uFryNouS9ngk0wn3sHtRuuJm8NY0Vp16eqiLK -NgVzo4P05lJCo11VyyhtgnYdeZKdMt+Q7lCRSZv2snKpOEmZ0D/rs8c5bdU9REhW -7TxNQq4LWHezRAsz4UzlHjFCGacOJKdQD1WgHDeFmdQTAI8UZjrwaeUsQiu2olmt -DZlE767OiGALCKzii9Bqls+vLGnnrY8Ga6D6iQEcBBABCAAGBQJKdDBpAAoJEPPk -Ei8djCYa6z0H/j9bY80BONuRia1tkvk0abft6cJXv+FB6VVsNLOQglRdeVq4/0Qu -HsMDGFvbvWkuayv1bAdEjkgHOBt3HAX8q1Wjx9WgT1n0RSbRGqXvoOoe5GF+1cd9 -2C0vkJIx6h+UqD9u1FN9zm2JeV+kcxXv1Yc4e680kOytiRBwZLnYaOlbuGHZEApR -7M64DtEafQEbGPrMCv22aXnoUuDVKJ64LIqwq7J5Ll5RtUz3Pd8s9cLatS7K+ZCB -6aQEbOxleqwne2K+X1eLRHNnrKlWwIWQRx/495/RlLKYv50rYcutZNAYvHsnt83F -aiQomeI2j/1Z6jmR5RuY7atc7QnI+4bVFnuJARwEEAEIAAYFAkp5X9cACgkQloDr -5KmRk+JmRgf+LLVVaXad1b9/vEBv8akRDZyThDVEdSi9VULZWz4i+hP9etdee+UM -nO/aNU0+BUWRQU++UHuGwW8RwMzQ5/rtZ0Oua8YmxqV2QWjPOTN6O106/AZkIFWl -dHES/7benAgIKrrzIFhhHQMjSv4uTTImWOHM1h5U90UKV2sJwX9teNDJRlM/h77q -2+6cD4C8YpeBo7AGWzToTK8I6AVA/Gwg6vAJ0z+uKTvle91yGIVxSFOpkVocHxi9 -19l3C9u0cXIbeL61Iyo3Rwzm3HWItHtKajQDTyPJUmTaL3UGZ58vyiRct2titmVJ -u30+2s6CGdbaw3Jkx5ZiiIe5VXEZrQDap4kBHAQQAQgABgUCSnlf3AAKCRAx/Ofn -3QeUYbe9B/9G5qeEELv5JXiSqYYEMbaG/Z+r4VWm4FXWqB0SQoiuFm4so5hOIt7x -zTYhiPPQo47RsfwC5RDthy9DtLqoqsbjb4Z1S5PlfsKH/BEhLt+zUSZCk0dyYFRW -/iYJZI2qh+MFBqG77CMQn3O/Fgw/9LC6xAoYE3tgXtlGTe2QtqQOBBMVO2J0o1OY -671tE2PjY0Vb5ZPnRtm097jgbTJdpSbQjF+n7VTE+TOQ8E4eQHCV6nn+l6ty3wpC -jBRU/BRe8xmbL53oN9Ini7G6KwQzEwrLVpYF0R+0K7lBZOEDOS3g3Btv/ZLDGhMI -gAT6cj5ja0eN5xgH9HKpJEIZyEMCHMXCiQIcBBABAgAGBQJKbhh2AAoJEKLT589S -E0a0EhgQAINfhaBj7ZswFTpAJQfQWwWV0KjkVqAUhW9ZuA3eg1kd8VSkb1QsBu5K -Loeac0XSrIfZqSsW9m4P5vk85e4/6GgZTnU4P4ghJXlLa/driyIGQMdxjQWAnnJq -FE9t2EPVO14DAQH0WDUZZLKTzH7VCZ3M4c8eJCl5fLhhsRMrYs3t2Da7nt4W/Hup -ytbm+p/rnx64aqeX22qNgDpgJkzDiZjfmw/G6PixKEIPX+1Amy28NHDb7FXmVyHH -zgJvzjM82Q2/OmPfFsIUCGMdyIJVsUcYM5vsyRyKr7PdKvsE6Te6TQaoWi+aaEjM -daOy/O3GcSQIfbcZfvi8hUcs2M72QCFNUjg6gj36Ye28Be5MRqVSXLPR4FJeC6xX -CFA+F5GIXLpXGrBKkVrFudGW3tqOBKeizrsuQWa+oPHH409HB8VEvbUSNePdKySK -G+GRqjcp+c27q/ZjvDQa6/usuIwGcYNrJ684sEKZnClAh3GihBA8MSnPKqYKr8Oa -lJwaQDWDm1w9AX9cYHwBaQY7dssLk1Hw2lg7M1HcYyEm935zG5HrTFkb7GEQZdxg -mwWWEhMt07QcEAERi1WviZtR1+uNiciphs8qoMuqN3otm6bI56TrMmKfgs2p7xiu -WanmiD8QkYbpVkTTdboTfQllgSzCTEZIBUzOlpKVOhCzZGNTeyygiQIcBBABAgAG -BQJKcJfwAAoJECx8MUbBoAEhvlcP/1IOx22ahWLI57g2deG0l2ryHns1CQP9wQRa -XO1g57BSZNCO8cQdzy4fqwad1ElHKeohA3bOgNvBaTtHd4UfPH+9euu7jWwHGuMP -gknaS5BLiKlxlQjwKk9HD0sm1CcJ7UwhxVBQTZNNEMO2wtyGjveT+xhnAo7NI6za -mUOnbUfYXafnlrhQ15hnksql5gF9WU43OuDqEvjj+5xchJO6nCLdkuWzqwgYMfun -81ucM7ngDP23/8SqRQiEgaUIVAwKDv45L82VRCl3VYRLW1JXgXCRORBMo7dU43yc -TXH3NDn4RWJMcnsx90vajVCsuEndH/+9YYyvqJU2wiED+4uT31KNCKmk/imS/H+V -AlTWI9iCrudv+ZrKuru54GZ9I3VQ8pJUp6mlDDswOVs8YClbSGhNvpFG7uwn0ie+ -hciLpSzVUPSrHQYwFnbK8Rz2X0oIhQWs59dAmyHSSsgjbGo0utl5ZcGpe7nPRBaI -LWXG2e82dAhJ9fut+ejlO0H01hDwivstHRPkgzJXDVLK9KIlacYcfdur0S+Gwx9f -4Y6rYob1qzpHiS3aHN33Nk0pPtk84JpAsn5TpgLLYSmOMiD0TkbVUmBU4AK5wd5j -IiCBejJ46O+r+5eYDUsyUYNrGyEqYH2a7H+qCHtYqjJZEssoeOFdyU4BOi07tyCb -6da9PaYmiQIcBBABAgAGBQJKcJsnAAoJEO2GBLwG9akf2fIP/jN1Xq1sW06xKByR -TG3nGp6YCJ5Nb4Eq4zYLuQXzjtFwfoV9Mumf6CRuSchkvMxTxw3c4FkfnHczTpMx -opCjPcFwZDQFcHbQuCJc/UxnrjZCHI0EpXtGkjDC9bDg8EPp/2gmD+sSBmFt70yy -OSXR/WWmAn2AmFhPOn2yR+XLPqJIS/2kbIMHM8+nFZKNYqAtoAo9rHf6YbZ/67w1 -RV6lCWyKxHAvEPqaihoABEetyBzVrbPW6plccDoy3DuGwr9A95LohbNqH8iZHNhU -oN2NZ6oVPR0JxSFBy+qX0s02wuouMY0teY47FgXR57SiAgsJTnrAcS0tA40HpT4m -KaeJzTv5ByxZQkHsoozWDzlHqwVDVn7qZrUtrhZcfgSs/ud+SXtMW5TalzF+Gs56 -NcHhucinTMSWw0N14BHTzYVoZ1WM1fYplNZh4MzJFDRRpTgwpk21F/ANS3CtVbPd -z8nVg2gNcwpzEnWDLwse63Mxc9FderfhuXh/ZRGYfq6C3rcNoT4bGfxnvDXMSgR1 -BHup0w/CH76B7hr9XMmGlIRetdW9MkruVDKVQh8JO5sl8LNoezaPWOK5a+TR17cl -JqXbNOme5t2eFBNuPHPaCP6Xq/XsrNS2iyO6Skzw1NCQwn/OlqtLm1O6hbj0Sgrp -AMr9uNFf+UJkpOxLmGvl26EBiqNViQIcBBABAgAGBQJKceUhAAoJEMx5hm+ZQJCE -0SYP/1Bp5Evjl2Q7Y4GVNwFYtZHOp22V7Ssg/01EoODfk1DNbO0zDniQVqDnoY5f -4iVh70YtbO5dRXRNSuKkVWh7myDLNoPlyD2qmdjrV4RQV6QuXFwHNppW4G5/H1zS -HzwvSD1pxQH7YKHiOW3yUPDkhSTynJvxpygkk+n5KE/vVkyyPaAozzwBCEC0Qz0s -cr9gpND2Gz/xgLexfrGwQ6igY+x9HMpL/YCGpe+O3QmweCpu17L6H4i831BEV3lX -ivfHyp+NGCciLuVev5yDAFDjBY31yaJSnhNtA2pquQ7xNRCnFm5Ubs8ztyYFKPLT -z8Zg4SJGCjXC/L98gBatiI9xLJn8UXRfVE+QGS2e5C5YJaCS5qr7X6BVIUzHJnij -0i5UZsUTpleoyl54uj/J53pM1/0Bj23kQJ1I8iYC+V2+Q4cOXQW8hHT305GCisA0 -rRFjdPImExM3WFex/T9WfCTprsmj15xqiZx982qv/xKVNWQdzXtNgl6Y0b+PrcNa -ms1sWd8IickRutvLVwpyZZZq4fy32WiVcosPCbJgTmHF4ka5HcBI68jVIK0IBhwq -vK2yRU2N1NTUUsALFfE0Lk9Ci/6foALFxiQTYReF+PjZDSmKw5XY8zKjL70tw4Tx -bUhAyNR9Jkmf93mFnlpNidDEGJOXsfga0j3c2Qi/FW/0otdmiQIcBBABAgAGBQJK -crYaAAoJEIcvcCxNbiWoEagP+wamCmtO6JEddS6oL+LAI68YKc2YI9Wr8LxNaN1P -4VEQscAYZ1C5FHkR+SsnR1jO6r7whe7s+bkDAenGUcuZI+ykDmGIMW7+qB61HeNF -lFSeNDFKH14THtI/rmKmOxTNsV+G77LVNuDSGZTUFt+kEdpSKw+JE7vIVkl+I+Vj -nfuSn5/bLZrcHBhOZ55cGcrOH86RuuP4TP0NwV7qaehnkaU2Co5PmKOuzrDGdE7S -vNMdNHAzaSFgG4J9N9evOkLaVly+ff7tk4XWxaFX/VYpojZ2VrlfMJ8pRpkRi/BE -BdUTApr8Wd/eZQMqnM0C6q+eKP2vmCYFdOtIvjIh7p6muXXYXSFIjWTjo3adlthC -DXlqpZ+3ruHECgAb1F17yKURrnu/VvlJ0H35WKj5BciesEAy/ar/6p9revzJUutp -BpmCJ8qGKEeN4DkrKzQ2ukmzooebWaqyfjuMjsLRJWIhr7szSKNZSoZq7qDxhm1O -qE1qLd4iXEAXIN/kR+k7vVvdzbVYBrQLPC0SaDLB639VICFmgZUpdb2PvyXmnD7d -EzTZJnaCxBjPA5CzrqEcP0iqh7v0bi9AYCYMIcSlwsfwpt1kQjQp9USeCRxaIeOB -KjUqE0mRhEzxvMBC6VH6ozTPSL9fpPpYvCLoSr/hqB44x5OutSJxWaXmnrCF0wAi -QkfBiQIcBBABAgAGBQJKdVVQAAoJEB0hyD3EUuD8hX8P/RHLLRr24LePEWHdDa3/ -hlvtHEZD4yrOFj1Vzbv0Mq2kcfvs1/6m8b+IkZOf1JBLh+ipcnpsr+UAhZ0nDrCb -CPmAhozr8Gp15dZezSifXQ6OCxsJT23pNTS+8jDWc3SxG+US+vbgIKtKIwW46aPX -CIsWaEQikDCWLGJjsc0eTJsZCsCwn8LCPBKAz1KYo8BBBzLWIlg9jvnk9+3Nl+hX -y8pDUjv4j9aCAuAPdAcHDCYTogYS+sePP0D59vaD54TSs5+ZzQQ6DZSlMIaB6CAD -4VRmWm7BUn2meLG/VCaes5EDnhlDr9PGLWxe/11ow8crMdOwrsWiJ1IHFiQPw1AC -XYMQMRJoEoIMRs4By7MV+S943eUzExr98M8mueCcCtCUds0jS//ohzrmIjLn09dI -55sy3bCz5KofADVqnpTcza2SGmbMcza5TRjZIQW0mbHZUxehVRsdB5P81EXMs1C1 -VHPIuFF2nYAZoA/UrifJJktSfzgHu4lB7tSJ8cdidIkk6o6eCAIqvA81k7O7+BJF -4YinffcR84/8XuOpOUcYsOUwOS2sa/cFV77MXxQ4s630LNl0EgYkF3gufvteZimA -29puo0Eus+7fAzSCLWAbHGb2ORhgeBDGtx7o560ngqtm6HP05bspTTNZZoV35gRl -5hoNYwKvNXmQQW65Dcg8DX1diQIcBBABAgAGBQJKdXd8AAoJEPZuPkGfhPTeLHIQ -AJfv0OleYmj5DchwDxoujTQ+uSgCLsZknMfvqI9qPA1DtmymqTOgb0b/OGbNl8eT -w/+dWyLhk7iR0XUVk91+j5Qef5heZziUs7lOTSbXg0pgOHBWGbkdJU7sFO7ckChM -Z81eC3U0pZpIBJaIXbLKPDR+zDg6OmLazeAztvE9VgGn+8Q4pOADXWZzKOEoKuTp -/KjHIY0pMTdJwM/Jz6IDZtCD+JAcYcjQn7U079F7TKUoLADzE0TO4uMrbYjR9joZ -6ErvXRHxiBV69bQN7yxJs1Jtwk9SzaeM7cjCmsUBraOIYniJS84KXgVHkr3KLTV0 -+gA2llVY/xSXSBGOuJnIUCPnfHB4nRoc7tZVezKXdRKyBn5Gi6gpc5fksdF/tS2m -jkzzVtM/z7Pnwu4oWX2W+o4fOI3L2GAzC6B+VaTV4MykXUPNSeBeufnjPx9vy4wj -uaHfxm3XZ8vqTuwOeHL06jqNo6hLwxvXkRBWGQS1NQHjayhzWo+1NnfOMAsGLI/Z -QxUspSGzvQH4LfE2gvKpEdQrD2snzWgitflrJce0nOe5TShp6V0nPzyrXjU+wAAo -6NfzRg3/Ec2hXQJQCw+3epHzFeVjRXRraFb1UfUO5xFsFgV/gICvWLL5ApDhr57k -zO2WAY+NAAsf1Yj7c41WzsSEbRaabHALKVKfM+fdkJZ2iQIcBBABAgAGBQJKdx66 -AAoJEAxwVXtaBlE+xw8P/2FnQCrG/CcRpZFj+AyJe7aUAxldEW6jiKlBaAnMqBgm -t51PtwaCFBXf+kBw4lnqtlzwEsBNqlkVERkQqZNy4rOh3V9Y1SQZrRDtrqkem1FR -2Fq6gHGWXQDN9/98PIEWauAwaFKi8omKUC/KgFPmPfSabZt0xJfx1rnLiv0qf786 -yGeQAiz1al48WGDN8w/PG3GYsueygAB22zAOKiBSJDlcOHMFYaAcP7LhZy9bIvAv -z+OJheAqREFQL/WvGS/VUw7vVa1B9Qpq0DXpD6UYhAi6D7P2cYFhWxer4dhe2E+p -boA9vEiJVUIRPxEdlwAQ0PJTy9rbvksufcxVgTWnn6gp0pwaWg5SxsTfTD4yXz7s -fVnupUeUkLBmwBVvW1IdWg8mSR9VuYXMbH1AjbC6PRRPFXlgw+xrm0I16iJqQzjk -luHAmrrU1Vs5VTdNfcwoOB9lqC2RlkU8YErA4mN1h59tEdBv9BK0vtyZRCzZOW64 -jdCPQW4YC/r+cAneEvJym+S9fIGI3p4F7J24QNzzeeU0OTd0ePbCgtkwQN8vcLm4 -pcN7A/zummn7FfAv0ONz1HM9BCehtm/Ry8CEHzKt0vvb020EvmQZmdFarSg6UsOa -0s5HV6ZhYaqFZa7oYJqvlnyCNHTHsoQIiV87cBbo9jgJgdL0qF1C8JgokCi9slk3 -iQIcBBABAgAGBQJKd9NoAAoJEIcvcCxNbiWoJfMP/RxIKxNVN1tbmB1w2OaiDQ+/ -+8vqkv8tpFpDVk4T3OJKS+jQ7M7mnA89rqCxU9DmixgB8eLm7coz2yODGTr7uXiJ -Hm0kq4vm4MW/nrgk+ip6NafsGpeda8hkYWBkWLSUAGDU+gFoaYyTFc7P26b4tQ67 -KAt/kzCe2OWpuguNzNLAsymWIuJPPsD/Gt+U7xg3NrgABEz0Ado17Fx0pyYp3Gy6 -3DQs0znLzByIk2XBhEZ4saJ5KmnpBNu/Sx0tRQYkm7t5m9v6wGxSPVCOxebqDFpj -9Eu4dkH/VhkgFgDCZanOj8vSq3iU0YBh3sb/kC5pnfNp1ZM2xaVISkplahY+X03K -OyiG2UGh4ywpXgd/+EjOHYxkLTX2yzdZcdOsKGX3jf+PrIK2hkt3IXPtpY+3+qNs -LvZ5cRsWannj+Em2YWvJFilH1eoVSwnQ4DgUMgEP1XiLePf1KXGEgykPVwbaHm0q -2Xb0oZrUnKXMjInjFxejmvbPFOczf91cwJmRP+p1z3rG2avV5yxc8ocs5d482iqT -K2+NuK3igM0ZitcGk3VzaJqBlUA8asfUKDBKY+iOGrDSrlbg6vtlvlQp2ibIqCYk -/7KI0kjnn9tLdV0gwSQLMyidc+m6YJznQwwpjL0PG8IhI8Q8U8VAORE7PPpC8chZ -ezYzNLIk6Gr77ICP1t1OiQIcBBABAgAGBQJKguopAAoJEDIkf7tArR+m/okP/0z4 -VaUMBvyJ/X4KDRjqUrBj0dRFQ/SVSWv5l6f0m6ft0yv+yBejxf7VlPG6/NewEhmP -bWf9jkNhYxIzoeHV/WSx9pTVrkDMnFfnnUEsuvxuKCnZtPNa+30Pyy+Gqm5nbWll -Eyb3M7aJoJXaHBNTrAYuqdYgJJLVh4z/kQMRooM49lshEmDgl/DcTxfNwFrj48iX -4uQMZMwhg2Cn4YewaL2nwVByDpWnzQmEOxep38PHBqj2ruZV+GKPeOZf7spo4ai8 -e9Whn50PbwX8qJ08GnTaLsM0nnGgnBd9EFPB6tjOrrFDt0q1vyogjdD+Gh6BToQn -ekDHO7hstuYr39I5qh1uZCf2B/VvzWguWP5eMjvLyCKLP4DUVWHAJznQOHmbuuzD -gELIeGW+HzYAER4/0b/8q3Bu20lf6ZhmpAdZAWrjDZDokLtch5GYUL/21+Ay43oV -KL6TbKIPjnm4gavx3OcPktM0kCBpHZSNMWp3Hc7RZwZ7vudkc3/SEVjMnZsn8AAy -7NqPVWz0Ix4INBQrpDhY5pkdXrRtkKBAwfViG8PLtWoW2pC9SjT711L414Y5XwTn -Wq+dVQCh/WIowwfCfEvJ1gkdKNwx7nwZuSwY99s58UQSicvHQi47yQVFbpsRWCpO -G6G9pW4aoCEXkctqY1qn1tWDIXbKaYOy7Lt4LenAiQIcBBABAgAGBQJKg62MAAoJ -EFeTDasLhrBnrxgP+wXXj38DDnn+h+/W80YWUDBXJCYA+In0m5CjmddV0+v2Pc42 -tr22Ct1yfzfMKHJwvbFa+ijZj26c6k5tiqxppU6vT1NUU931dVJOzb1IadYQDRst -3ZYFiU5BMMURJocl/e4OQZ80t/LRvV8PVGy7xg61srgFAVq+lGpyc5SSoY8lqy6F -MLp6DnK+AeWYCt7M1znEdgK/yW1w/y2XTqnFx8NqwpLBwSRu17bIi1a11DHPc9sZ -RP5uQ7wJWeN2sQgs3O4GacrvZeUSU/EZAQVxrcHqRCIMRNZOwHBfemW8Og1dMGmJ -s4m3WKfNM2GQk7KAvKFAnV+4ViK8PIQreVqOAGiw956eyDX98ZDiig6NSzsivV7u -4OL+v50DivYPSM1A63T+txajIdwb/2hkrZgQDMu2umFZ1emIM/7pBCKgJnE+4aW9 -z+k56G+JyDlUcvwRG8AjMNl8TVzApfcTDL3wq7NYwkUtaWIuZS3iaykW+Hf2JHJi -pLl8akZAMaNJBISCewEuERiViofYg0Z6/b5abeVNRL9M/WMBJ7fdeMluXxGWKHun -5pjCfxrMtBVlRamuxAGX2CE9vva8NECuAxuCDBlqGTuVvqSni15QJRI5A7w35Ooy -jd7qmJR6Wk8upxaGL3QFWljJ1TEAdsv0a+4ZtL/dqXpnXI/8MS2P1qSzIxpliQIc -BBABAgAGBQJKpDKBAAoJEC/5zVlhJha1NNgQAIW24RnRfSU+rTu8Gy3AqQod5kBh -N982unZ6I3Af/+P48shWTyX+hWllkKiWW+9+MZkPOSVYKaP3wYmbUhWvAc9/uiob -dhHUiwEnkOGz96CtOyMI4f9y/qMhiqDT7Qq4xe5XBG3x+CYSLHS6fljPm43EomTt -EinFMC5uDuSQJjpI5hM6qeJRzjVS74XR/GaNwRH1hYqjErxlHGAYwprXo3berWeK -/4o34j79bDvWlFI40yl6g6dbxguaSd+0OUZycQoljR9iPQXzkOMw7u2mYh2EASPQ -L1rPAiOBXdqrqyPlDggyrd0hDNpH/7AXY4Q2M6JqFqVu+JmN1eo0TE/yaizHo5Up -FK1ifpq4VDOGfyzBUWW+njYDkio5qcsP0o7faNuXaohvp4/sB1kXrkVM4q1lY1Na -wwbUKGF4NmJH9Z7zatL08ZESE0sNElcCupz2qDlRuEEQ+Ut7yibT2PIGe340kQtl -y01ZveKnIQm6lQ4UvHyYLt1siFWGUE/CfJxIxqPHAP4l7JRHofehrqQJtRUK2L32 -yJ+J4JErf8H3KYEE8wzlTklysUdKIkFj0g5PKYp6ALYzuUgzdHRQm7inQpDPcN+r -qPIG61VJobwLsx9rIhePYVCmBVwn3u+2bHK7H7y6E4nGwTNPFtxpurPS7BSo/y09 -XG4EHPPGfRM4xRBWiQIcBBABAgAGBQJKq7kCAAoJEOtw/vPN/G5Pxc8P/0I0/5pM -E55bAk1vZ9K4b+fzfCqf+46kwvN48ppX6Th1f0IivOs3m7GF3zIt2g6pAGrL9ow+ -Kz0ExWR4RBa6njGzEaIU5EgsnBzDz0vQf+Spw8kIDq3XhN59oF+0A1U9Lsl07DsG -Z16rJOLt2xQ2okebzse0kD7OMIsMbT5fNYjZ/hsIjP2Ag8cBcvQJp/A7jqz9QeRc -a6OgvRHLcLRFK95bKOzDqGgAKF9EyjkWD2UVtZc2vi2mDGDG+IxElGGo5ahVBE3p -WTvS+ImdJ9Kj/1GZGMuYVXWOghUiCabO71HCp7gb0rG4dlYZYsmgCGTknHOLuPWh -ykcGIA9oKMeWLeg0GLq5qn15+Mj1kkdPW+WLZQ/4elZOME2X+mqSGAm0X0tlBrvj -qH6Dq4YrJPLjNIC165SpWcPh5+ojPdlmM9qtd62DbrAYO/9x5oVBBMav9xHCgO+j -f3F6X0cpu7/zk5DLZ0I2JbAYQUGqW1IDd++YZa+XH7R7jqJfH3vlhs0wZlJbn5Y7 -PCe2jG9A8Z0WCkkGxZYXUJtm+svLgDQ/laqCxUQKtxIobn0cAjuu9iAW3wmsaKlo -7zV4yLk+Ptw4ZQNbU8NSFw6s2yKVV0kyqQe7Eo5RLpyG/bCHQYHR42CkRWPvLw/J -8VEZarEESAOYwlB4eI/WeLvxQqYcTQv5cFyZiQIcBBABAgAGBQJLUV0BAAoJECGf -a2Cyu/z8PJQP/2gPRCh3k+d4QpiRKeeqnE9Ssr+DiTTvhVxkqpLXtK6+heDrrvdQ -dPAhGYY+2AZjn8NTyG9qAJMqe7xHaP9UlFksVL5CntioVV6lDHAFcPqYwsdhntuw -NMKI2nwXzdCwVUO1SUSepPuV655nKCRgUtnjUs0wqyYfs2IbopWbYCNGeoE6v8Vk -yBkowC3J7nho1riubkiPI3OopdOPKDwPy4vYV8DVsmJz8O2ZkxiHlsqzBWYtyANI -6v73+vz21Q13Mxz3fh+B0Jwugt7HQoxmnnZz8dXSmjhbO1jHrvRsYC0HcWFMJamZ -6ZRyBV9WDtAJAhg4EzVQ8Ghm+NZEPr+uYfqtJ00Sn5C9rXeG0jgTW+Wzu9Fvo824 -2JhTcfQMM/SezNduFuXNiR4EQu/MzKikoN23XaThq1uf6isdkkONJe57wcOvcylj -tO18VIrF4T+K+H+ayhOEZemdhrGbohFS4kDs+L2shF6vXAP63307rIvtl796JzE4 -Ld+FfSpna+mSTMNmSo9WLdClpcmj4oLsk/lbkctVCZFiBI0DTBgzdtPmMHTPGQRe -vSdC3MJ7jBKnuO1ixUG5cbXrrFqveax7/U+TSt9DMBRuUblAaXA9+XG+oWb5frIF -sJLYpcbvVwBqKe4CAc6aj1d2l96WI/F9TJMHPSrXv38kFkXhiA4BqbSViQIcBBAB -AgAGBQJLV+tvAAoJEPI3Izb+w5dF0FYP+gKyHOo6nBLdAjHKHTZpTC71f1mzg5ko -8pJ7H0VGCmGktkD0438LTb3NO/kV3LOn/bfd8sKOzRHZeD8ELW5t6bYCgWlS+DFc -qGcb8cdc/py1XVh3sM0VjK+YjnCGKhjwSQOABZDbwhsCq4TSAHevs9c4Hm71RjPp -aom2pnsfgOePuvKRv+q34O5EIuxzUG1BcQA5eqRCegPfR2tX5xxiPVpYZTJkjguy -+UPeXgOjMKNgg7+B9jz5SRRgbgD/qKGb0xGmelxrb6yDxO3wDhzmHfjy3KNwwG/F -IorTqYDVH2qd9UZrClCDjRmccqyu8tryKubhjAVMYur9HDu4vylgSUjDKKFPkX7R -zDI655gWiIONHQeR6I8NCbGKSi+EWthKS2PGv8tYmdRIQRdKYx+LINKaaoZDilGG -ana0kXu+Ab4Zb1hMsIqhe4AwY4KbWxOwdbQajCOft4w7S7c/WwCqzv+O3WuMMCdQ -61YmlRBy6Wy7FkDebt2Vlo8Qz5sB2SHxUj3J8aEdI92dFZOxhY/l/vrx5S4CtKwK -Ny+fmV1pMDn6vshkABrHeDVouND+uuvAzNthAj1FNZNTHfkLWhrpBpTYSjapALie -63jMIY6Ckys+J7yv11mzOTz0Ef+i4vWil/Y/24K6WwWkbWmQtZj4Lv+W9jXZAyIZ -hSug6koRWF0BiQIcBBABAgAGBQJLZeE/AAoJEIoRG1yuQmlE7m8QAJD37k1rHMAx -2o2k2CxjKl3XUEYsOhTtkhS+N2LMfJKj+XZ6Iv8AmH8yVNLGSYHO/r+9VpvqYGIe -sIWJUa8Qvw17lQXPChljbNYe/nCRBTDA8PcNPljEx/L5YbfqHz5vOVE1gbewWEup -x7GqRS/5VFdw2MULlW9Wtok2Mg1w4XEeirrszbnDMeguQGWGEcyfU3ARvNauX77s -bzEID6FKdCSG+Pq2xp/g6SZFUlF0GZrmTtryAvv0ZWzFQE5lAwMqOjkMIBceTIps -gcYH/giRVUf8WYpENVZUQw9SvzGdDBmLzFPxRfkU3RmK3j0Bllh9XQvYJpHoCjVs -gwRF8F3BFcceX1WvUHjkIGt1Lq2sHQVDQkkWbbljsfI2Z+e5QbZ34WMI/3eUnKaa -5DJaEtXiscA1C5uo4HuTpLQxW9/vIDf2N9i755CRU34p/F/7VIzAKV9eCRTzyETD -/PF/ks8bP//IpT0XHltdH0rBt0hr0T+lZmWC3JOzJnVPX7tLZ3Vtl/etEaeWQ8V3 -P2BKI7tNxD7Y9AnNntZTkSaUKi4TCDkMM8r2RRaJPFYI1nHdDm2K2IYg7TzkM/WW -iNTvajp0Odx91s6dR+Z0ZUffo6FyPoXaG7GhH85aRP4X4NwFAb/6AAztCAvevTRN -7+4OYNmBHoc/MjYbg6gE0p5MIYOguGx7iQIcBBABAgAGBQJLbpNwAAoJEDH7Hm4S -WfKPxtEP/R2SYa2zdEp8PgSPXuteloFDfYMDq6Lcp0UePP9+4sdc2B6cRiUz8mcH -xDyiXpqBiPlu4+1yLPfxjDLN/7HLxFO7SPOXji3+U6yQQFHc3DEZBt51rDKhbzHz -duSZuvgugm7KgDGU4W0yiimXA3EIAE0ybQJAYq96TVTqAXCow1ogMrAc5xyhyDys -hNZG9KysDpadixiieCgIB6dQMEO3Q0c+SLR2MDeRP1hzCWTUd0Q97+E1AuhRsJk/ -auIPACg/4NzerjdhCs+Z6lQzdbGoiNJYLtkmI/38fpKne8rudVlWphZ+KwfTZBmK -xyGv3Tow3s51kwC5RRzzpqh4KZ+xS166tEHmeGbYV2lhTEWtt6+dk8S34tdlKAKq -rKMRgKcbkg6kFhBIdV1hMk5YtjVbWwqqdWFWxBsktYbMnHRL82ju9ydQH9Wjv3c5 -xaEvATYeTt0XBNNxW1At2UnM0HcSLSHlKjRThg3TOhRpe251CogBeyWfGGLcIq1n -JX55glO929hWjxZVCX+jyZVN4ESUpqITRZdenEbPRjPB0XCsOXqm/uGfC6kaCv/6 -/+Dz7+LiSRVbEM9NNrfRsWxfzBM88sPGxI2W9mFUt6i3il94L4av9mrLD1HjJ5nO -ZStpYUWZh/5/3KYXLwI186yEkrNM+acd6SVVVdPuFWW/YDbyYTsoiQIcBBABAgAG -BQJLe1APAAoJECitMrIYzLj+GRsP/Aw03gELq7lZvSqblubzlAo0TtsJgY4GxB48 -uRtuU4iQoWdIaUibXM2TpE4pMHZho3EjDRbyNUlOAb4cEwVmI/AW+0kQFfIqNdAr -WGNrhWIUKb9BIitJPGdYJRpyDSBJFrwAVexIGV48QSUpSJkHgGgFK0+HV0YAaqdN -DNcloVbm4CmZ5dfhpTC1ya9wGcTNvEXPpzXBK0Jkewjq4/slNz4DiZXWvrAYhT9A -+QPjmsFrYRDZz6Irmbk9OqbLmD8G2Rf0R68yIp7aL7a9tnDZ+DZOhzi3qFp/smcE -2pnTfD6aIORl73vLGm+dgjndhqLhuD5tSEYDMjBllHD8ecNvxAIgDjq2nOP/9jmj -3RWg07c2HGRUU+FQMWLgADxquAGIw/v4I17cRtsNxfBh9R+KvQC64cg1o1AvGcR5 -LfxcUEMxIibOI1Bwe/AvpArzxjBkUElqvPpTRmovptQ+2wQYOCKiISuhYUlxp9y+ -RLJLB0Sg+ULFpJABIJkyXv9HEraYvdoDVEEDZRE5NwlgnBrubDWvCkad4+sl5hCr -vz3KoaSfUh9zhtG27Dxkg1eHZxq3PmCHSZ2zLsCMXJAATbLpMuAIzkMizT6KrvWT -cp4i1crTjI2wifgvwj7NKzndhEO9l+fUBwHS4FN8eWjJBFWyi3RGT+C9u34KMGkh -dzYvZmLGiQIcBBABAgAGBQJMaupWAAoJEDOWFYjhwhhFq0YP/iUpPXAeDbEO4iZc -fnDGpI9ucLU/3iXuqJSiRjbeEqLxSGYZfdbcZEkf6LCRfSfVg7+qs/8rli+XNSVq -WacJkR0dCPg61xZDNmzuATgqpyVVTDaVZdLfxQBln8W64CYoD1ps6MZqGZqURRW0 -Mk/RJnZQ5Cl0YYLF8VxetwkKVVO0Zvp1clYwZLeSbo7EYp9quStvx1zHV/ul4hiu -qj35v053kvD+2aq56hQu+khbCDAByjiNeuqNz8cd9j/4U0aOERld5Zlo50X8GtQK -hGApRv6pRGAgNTTBAsV/7ldJW/BhUKCtBcL7k0G7v2r5aSCiY1I5yxZpaAPPM+MI -K2y4WS6lF48DerxbrsGxpPxAdGMr2lKTwSSzVVbVLNMU8nRquNkqQFhRgPxWgLdZ -jOVf6nK4mrvLVVvkquVe0yOBDF+POZ6Jle8QV4aIuwyCuwR8F7lREQMxOK7CRR4w -9L5lqrS5rX7hYzym6dQd+Ds1UlJ+O58GlsHFfh/eE1TurLAU3b2Ww3CzvUooBqkW -K9s63o7FsF1OWleyMef4Ys566tp+28m5u+mvIcejRgbh4iKOiQUB72BOg1JxfkRZ -lWUQchivaJnjCjylzHe4B65+cw6TbJpswtMQZQABZQ57U0Tx4vKIlAO9lrk6bZsk -T5BaJq/Embdlb5j36sCudqlCt330iQIcBBABCAAGBQJKcb38AAoJEPGmm+QpwP/u -VPAP/0SEkS642JC2SRE5XOjFHMvVMY+4bJRJDKePfS7zChVc5VZafOW7LorQYH16 -sH/s+Qy3n9t1p9nvkWoloamWHK4iMEDlL0C3XNYOULJ7yzJXL3bUXBxH2iUhNNo9 -2rNks/TlEAanjnZaDtbFtvO9THxnbalbHjAp8QfRXCPY8pXtLkBHeNeagjzZogJ4 -6ptrWQnafFLp+XuxGD1RaRi0aeqvF/l1ES2DJ28NjBKhXcz64tbMis3XwPyyWplG -0KU4Dk8NPQbvR5tsXK/V4/IK8/IuLqZ/tZy3DFcu+Pgtce+2h1udZ1WMtOxA8GyP -uu5TulxgJjwalR2xfqgBZkh4x/NHHbnBP7MzDZoRu/sEZEBHIIKl7kgLxYrdF00F -psbG+FYCT3rDtfnoTB4qKB4LCKV6spe11oTW6/2b+WCEoBty35lZWQx3a/bifBzm -9Afc/tUoNYz+qCu46D+5Na4oVPwxU/COhzH0k51YEi+OimvLktL7qHILi8JoFI0j -kswpqY5lIODkNtublvem831JlNk023y72QDwUFioM26UPZY4dyN2O2f/qfsFMMBK -OTEkPMz6II0HP6YBqYoS69e6LgugaZU4T8pWlhotdjHQwlasRMMOly4Jkp+3cShG -qAa006WvOD7nvgrylwcpkY8kY5aSjIqAVvdq+Ga/6K4v9lAFiQIcBBABCAAGBQJK -cwnqAAoJED2QirPw+/Uf8xwQAJRdNfDkKH42+sSdClhfJO4kFTvrjAQbaQudS87s -atT8QErpLWFwATjks2Rq0gIHU/+nRBAwcuApSfHZTGcioWgTOmumofBlXZ+2orBp -npvGM8RyRNns9ICLx5F7Uz4XhVqwvcVStSXZnvdP6TU3ri4aIyvsXJ09E2HQht8X -29DcQdsILzpF9l16MWX8A9MKxTQ338bFn1O7s5SHIHOuIXoTOraAVOa1XJ6o4vZP -GmhpotRkHlaoMD5OzWm2DVkWWi1LSYYw40E6RKsEEwoeVD5AnlyT+FCKh86/CRQq -o+nJ0FXi3XwbbzPjGAEUXfYa8glq+rRaM2HpEUhYZr+FIQz1ronKOgOrldjQr26i -OXJMf5ACLqMsaJmoXwBjpWiMed7sBr0nnp2xcqu/IXqroFgtLWJ1wBCPDE12Jf4w -eTAxNInj1BbTjC7AhVIP2sP0coVUDSaUwdyM4A4YaXNhwNCKveZN6FEehA1DGsCb -qdxtIa/amlxlxEPq8no5jpH3u7Ntj4E0Qsm2qkyMxJCUkIceYbuE3m2hwV69lTDW -WYA4k7RufLE0biDKiwLW32Lcs9TTFGrolkkkh5E2oPdxW32QS5G3DgaaZT+35dIK -f3mqGvxjr/LLT+XW5aNeR42DQhNwQrEXJZAs50Bemcig7kYaOktlZ+9wGMQP+wNf -t8ubiQIcBBABCAAGBQJKdEJsAAoJEOYZBF3yrHKahKUQANA/LLp7NW1Qgtla28Bw -NFIX/yD5kK2wJ1boTzqvnyat2Xf8MzQ4fFtiqwsNw7Yzcu1bKNwJr3/D6LSjclB5 -+qhGd+G/pJNAnxRGYhbYTxEPOTVH74guMWBDYZ2/jdNWQ08ZlFn/h6a+F1s4BaXH -dKHC5SoPPtOG3A4KwK+yOvePDf2RcH/7EoADtdaHIL+9PQf6rq4kxrEXVughJ8fD -SMvgAdclfW2Uq3W95lbyNAlr2acLhovdZr0MkF/cNie18I4YLDHsszx19LTDMjtP -LnkooLHWaOaIEzYxAi/U/a8SmQBzz5pCnGYxflrjYWJhRDuZNRtf26PU5jrK3sbz -a1JogbYND/FCtGDJQjdRFfQSGbSUD4XoTFOZ6K30kgLEc6hO8P4G4VKSVS40TTZK -RCu8Rr/AXAxUKUyDx9KpmXkv6Cie+lQm6x3ZHS1aVVNvcMn8TdX/jVybIrgeJOAi -tTQfd1ahMFPo6HQNll5ecHqSO2BG1k0Tad19/aJ2AZ13Sg2JSuFduL7uIXkL7o2a -NfLCPhEAugwiRHO+1C81kSuYLSryxRW/q6YNNElBiUwizzJ41lhwXVgERQreI0DG -WXu0gtHCaxnV1HDMQuKC359l8whKG/wwSdcva5jaHJi3K6I2IoM6muRUmwB96+nj -wF6gETAuX2tIoOcYs1P6eGj+iQIcBBABCAAGBQJKdHfNAAoJEN/3OMLRbPuiz4UP -/2uR+XlEjKdv2Jm/Jv1Ym2YNiM6nSwQ0C3GnDmbcF9UkPx0AVGymH9Xf3hZyYdP0 -mOmFgaT0GUy/0BBd4RqjIGvPH9Apv72J7+vQWTwDl4mJfrKhx6XtwfZtFbxhBvl8 -ldMCBlKAeoFmauvHITPNw1QtbBc+x5EXqGQJFImZxDe80Syzpdp13Reoc3XBTGZt -iPSpmQWFm6M1HQAVB1wpr+21WcpkF2WuyblaCUKZMGCE0Swa7mNYeNFGaa1Lkjmz -ox6alh1GkfqBLSMOotorFZxW7ws+GQJzPAsyy2Kjx44E8UJMq2RazKcRX05zRmie -22rQ7rvnhBkjAcn9pHRWQdzdBEpn2X5Os4qxs+sD36X/hHmTnuzW7iWgyv5Zm9vm -lW0RCUQbLHL4dyIUTUEHDA9D5NJKPtqyCAhYs+1h7jBA7JwBV/4T9LvSsba6ybuo -S9orSV7ypmeVUVj18ZBapAo6frYpxkIslVRcnbLoixQyDSeE6BszTemjjfEawpu5 -rpTPWDedHofWDBhSKoGhyMUFrgnT27jo/jdlZGICX52BjbKXniE2O8KEiRluoZZz -loQSq4KyO07oOC3UAeB0Vc9erCh2qitDe14VaO9pOMk3+lz93wb88qKl+kAFotW0 -0J01BQ0lhK4VYGxOoV3HAQYRH7gEx+mQM74h5IovwdlciQIcBBABCAAGBQJKdHgN -AAoJELqceAYd3YybiqsQAJ5xIgM/4V5HwBTn7ERI/lyOYnbVg//9KStalKDOLBnH -5AdtxlLi4GSG3tWjdTXl1PI9VxHP8ivjlnN4oJQzPxjkPzwESfWGIi2WzQKRQELt -mFRENJ+2wyYhNhoM0dO8bwIeImYeUajX3+yD/iezNGWiOyijK/WDRDsltbsc5D+M -bsIhjey7N5oXsTlxIMeGjBl8lQOEA280DQSsoFDVjEYIjf0mgEkUw1Fj41FjYhbK -jnSeQhrvje9m2n1NGJz93E+WfwpU8pQT+ENp5HbQJFYD23X/K58pac4TzEh2olQv -gpvT6Y1E/BrAN/nqPbP4/bO0XB7JyRIdc+4a70VB+Ph9kHdJRg+tY/Mb+/6KIk3I -EDBbyndnozs3FtPBXOxexS8yr+tPLXg9xyFmuk/6QnURIl1NepWwufXWmlKoNCgS -usypy4kvY33/c0a1/Rz39dq2uB9IntqO7T9jQixnU5FaHEwwXytOmH5hu7YQlGOf -gd1noahnfaIFmn4S2smuTu6Pe9OHNYRqIF3yzDQFCcEUNBWRlXJKqPmhAnZX3e7G -h8XRS4WCzyy9ohs/hqi1qIFRq+G1sYjUJ6rzcSzfQtYXrWED8+LUyXjDV/ZJT8ym -PEOL5iKDaMRX3NmGxNL8y3l6BYYOEr42axIYMB8+I8d+0B+sAAuRvFKWLdm0aSTA -iQIcBBABCAAGBQJKdIqqAAoJEKLT589SE0a0aF8QAJB9U4RCzdtVstwfMdGCviOT -vcrALSLkSUuTz1tJ22Ma8tpYG1QaJ7v+xFY+aZaywx5XAEVxz6aHhUvzBvtq/t2h -039y62JzyxFRrVD31x/QITZxwEOZNO/oQqjy9T1PFiT+8NVwWIF7gkNdvTH4z0eO -JPytvdh5sHlRAsMY03kbNwchVpHoR4iH+y/UnTU/+rwE4f/jvxQ6Z+xjYCOp48rp -JuU4XfeVQkeulFh6QR6hJyqeATzbl3P2g+cNaXmSucZHMn++BLNi7XR31JFqdhcS -33g7LBIjQhkYJ26lP1CIQJyjdzgROFG9MHr5q4ONfFKUD9rZvCdesvLcCW7XyaIi -+tsq96qJEcAicVdQZFFZHsglaTZFKRyUkuOpaEtM2PIB8aBMUrAn1YytX4M0eJwI -uSRNhc0dsqcqHql0/MEzj5TtvlaQFiKGaeYMv/xcZ0hbWhL/PbCifieaolcaK/wO -nwz0+l5uG4sawjPAeFOXvaGaREncVS+3aO3NRW84KAxUCLMiGQwePFosAXG+zaMe -aiuACvAaccOSKxSKnUeud3yL8hvDE5VEfvX27Pi/t7BQjUccl7yjWCJWNSxRvPa8 -28yZTorqAnQSPEvpPWM8oI8S5zxSHcK3sZOIJPDpCBougHKGgBSCsSRJgZLs0uNP -L1EUEwM/AwNQ7IfETVMkiQIcBBABCAAGBQJKdJzeAAoJEDO+GgqMLtj/87sP/10O -6ZHDPM+aoWApPD1f3XF5+eFGtWrhhNuqy87U1LydO3WKyhDvH3uKB9tRmIbuQ3kp -Ad37dWQamF40VaPy7vekmrlkhghCZ9owrkADj1eb+N/HhppX3pXqQQmwVbQoc3bA -EurPdjMk4RcF+WeM3DTRyJJV36nLGnSkPv2dESG+/bNzbJ5JuvJ2BTKClvywuCxE -7BewBlijUG1EYasart3IEjnVqG8I6DQu6b3evh0a89Cemu0CDBpB8vZ3KijweAOB -ZAcTj8ZqcPsgbiBUDB4s4yzgJSHN0zHiLksgCxxafjAdJygrSscqYH7P/p+JEydE -cTE1di7QhZYZufcAnM8BLEuDCBjWWDgFrohsSvHBgRQFGdgEwnFjLVpO7zd7IFUH -Dj6nPIAqDNKIZsx3mAw4o4LOPgz9Zo+Q5QRbncHf86qDs23yA1bLzAU14E8fxyWG -ElB2UgQNSfiuRN1fZocI63NuNwCnvx7LMcoLf6zNfhceaW4iuWzxMbhb8D10ekpZ -bi8kG7a9ZMe4NgAQvnaKGapDvkWQg2b8BL01Hr2ESgzuAMJ7PEMPwla9MIH7qO8T -0eV+0OzEf/R22rmr1Xo3LLF3d3x8ktQvwS9w4FJvpsMBQK+Q+nBbLyHHUiIEVEP/ -jgI23rGkNL8BC0/y3YYMqA8XZfnl5LT/FFAyQE2miQIcBBABCAAGBQJKdw8lAAoJ -ELkRIFNthRKNXWcP/iV80d7f7Pys8Hd/oCCfXCtgWzSSW7dXr8AtV5J9VHJS/DhU -7mVezUD0Ds3qP5P2j01mxY/bmnhxja7EBuemCkuy0MGXDGqqVLGDqHdUh6gMSwUq -Qrdp/APKVH7ykee43IYE4mxqpOukeQkQXhEcRZnBOVLx+xv20R2V6cafVvDObs4C -lFwNPgd8jN5prOuwEifSMI+FCYJMMyCcXNZRfq/dYE+siWuj0czREQACsMBe416Y -0qb7MGE1g3uIATtKx0/3LNuDtMrfVh7Zssa3cApOy6wUXghOgNou3A+EwWceN/6m -uDJoc72iFeVUhQHZmxdNKGnAgk9D9ZxiyZf3esCMEiD1HbIbhcmzYtFu1AxoxF3I -2K6llY0cClH1bNnfiGt04qs74Sw9y2BZc4crk0SRNjJLNlIKT52iW42kLv3aE3sk -gbSeaIhMqviTMkzDbtF3pEFEMOvQOtsVH113YQMi3eHWaKyRFSKTFthzeuNoJMv7 -J8DE4z5Kor9evWku/9pbjW5ObqRBMLRzeMdZNx37Ss657tY7voZxPX294UgVWUD4 -CxVu6b2VL+u9uwEWAgPg+a26zfF4pMX+H1fYlmP/pFSVUhJiXT6kTEYk5oXDVael -txXyRroSTUim1Sg7j23HXwoQnRbs9fhQgj6vmtNbKfKVjvqwQS2CScy+JI2SiQIc -BBABCAAGBQJKd01iAAoJECx8MUbBoAEhE7cQAKqnYgM/xd010qm4nK+hxUVmqiAZ -v/U+hZLg65vmBSiuF2oCwJa+0s6CmArZVZGTNHb4aCi6U02ctR97Hikpgf2lVHRA -C76+JdjbK6iQ1UQ/O4MoqH2GuOuGUcg0gmr+B714S9nNeecUCe3bUogAxZYrl17O -gGk4DpP0Tmg2dFrQ0lhcS2W5qb/IQ3nlQW928TtDs1Ey23Y3MUCs0E2TfGz34Abr -u5hJBm+Kiy3ncrArJsQX3Dc4HzVIoUBFmywo52zeYV1lucZNq6KHoWwzBHMSurnz -t35v7XHrEvSa2rsfC8rHK636hzhqZPxNNa2+vhTXnIXfCe2eUSDHSgkO54X1pvqd -p0LHpdR9OiyXPAhjJlGwVC7io2HF/dL+wptJYUAveMMmGHUYaH0we8RrKAfFTk8j -eqI0SmvkKe8qfgAlSjHn4am9l/lOx6ptMx5DS/DA7CWPPCJ2JIGbiROlosNNia/s -A7t++k6vPMMMPTBEnTzfnesQqLITAcRsz6zOWJfwitAkJp2j1Ppy8qC3XZub7Fc3 -aYPsM1duysFLLebjuqk3cfk4bFfz6+c5byanU4FkjiM2xBm6M6+56WTOo6eq318z -14sGmXcq6qgoCPMHTaxpJJWFyAFE4tWOFitF4kFm3zAlYkHhLfbt66PtgsCgKZKn -X639du7YLuZGj+L9iQIcBBABCAAGBQJKd2DKAAoJEAxwVXtaBlE+Y2sQAJt3gnLN -y9DAO7Z6t8BVbhrXFypyz/LsuVtzA2gKRkvMeaD1AC7pFYKnxBS7CvYQyYozBjbw -BR1c/lzJcy+xVFxzkymEIfjlddnVVlrTyuNAgf5v8n9pTc1G5BuQtx1h65tqZ4vv -rLCeWLYQSyqXklo9/QCAuM5S83hnjA4P0WdylvVMQXro0LayfaJHgTn47jEmvVIn -qcQax1SxydstL0P2fUz25bQPIpH0r0+xQCB0xONifa/sjjSyw1HJ6JzwfZrlZzFW -0lg7Nappbj29X2oXDwEmW1l57E/qcrGbdYEMchK5VyjDzyTol+duVH3Hx+LGycRJ -Kn5Gwcva/vMPl4XTL2t5hozMYADmvUo17+2LL/fdBru7d4crkZxdPy3HReGe/qE3 -ZQaWKMXbhkKx0sK5yBbL17cfWcT+1DLEl9eVBltdeT3R+UbZocLkL5DJhfOzEIxh -kR2QYSqOGXjRbsroFGHfhxpBySjKaG3F2YrCdjIhlR4L9r/lQ6/u2Qb6IoW0EpNf -ho84F+dWbYzw0nqtONsl/IEWkdI27zgC511opGchlArLVnIXa4/bNGeN0KlNXWIl -wUVgysu9q+MeFQHgnCHCeJ40y+79D+WlQ1vCp6iisf9pG/D57MFdzk9YkGRT9x/X -j8/ZTiNm7eoFAJ9u82GQbZ8O16RDEmcqab+oiQIcBBABCAAGBQJKeZC/AAoJEIcv -cCxNbiWo38gP/1vkv4PfNhcnHttESKlPLsrEBe69hU7byJNWYY1GWWXjgjZJnK3I -vMoSajb0y6nVUHk4aQ8DEdQE2UsqySrEk59+fSYOufOS61vJUrIbw+zha9T6meT/ -Hc0pAF13QuzUPetRxPUUh6fwptdzsK9Zmo7RSHU3Azr9cYQBNB4pGAEcyy+IQHlH -OvtetbKkpNUJ9elQUK1zXRNIMZxGlXwoPvBklx+NpiduST69dWwiUL5V4qtEpRCC -a9sIYs4hFFO5nV689ZtrKqTSwAv6c5fg7X1dg95nCC110hX5yTuBeH2CfYYsHtIB -BDwjjkipHoUmesW5XDLjUGLjkx2fivYIcndCi4ulczX6CJfrQuUzePl9m6l148aO -4hImdMc68OpbfeccXVhFGSikZnATKYU/fqyrUXF40rjBIB8nR75y5H2rCGbPzq3i -5gnqFnMrfz2/4JjCSdTYHULS4w7fEH3brfyqduruzGUkBXhMU/4sEVOIhHp31rup -c1xuef0rLF43Uhfq+Gc8Y5KMcFPg4bDikCHka1KBxFX5kLvB87GUh2JFtnw88wjT -HIlyjnhv8tLW1ZxlZlkqvrpJNBgTbSMGF+PH4bAupGWwWpLhAaDWJwiFPAAD3z2t -+5tl6XSgtyq9WvtkXyO7Cc+exVqJx/RPrPf+WZ/6pwA2bzlXyvq5O3eGiQIcBBAB -CAAGBQJKfTTgAAoJEEnDv4knVT0uAvcQAI2M29usjHaS+DNql522mt2hpFo2l60w -2ZjSsNDLo6rvKSsmUdHsSshRJvSp8Y0emrGD/cEyR5BOcgwqw9cp8NxWYJGwoINT -uHZTBrvgkKkezsAwGUDiYgsPcdkEWA6aIlgko3///tgiy25sIKzzCghVj0mkyT68 -GTyVGJ5t/npRgemgqsZjvt6s9g5VWTNHto9MMGqlEQFY0jbPgu5IkY7j2puotReS -uHntgPLJVF5PY9MzjDcAgTEiKhd4lJrYMqJCD3hZsuUjoMxGPfgDj5V4mTWDR+lx -8GUpc6qxoFpiskLogtje7VqUiC8Q25o+V9CAhorH8qIQcCZhICzVsLDsR9nmlpaf -DczrQo5MwGFlWSZjf2EhvtNOwQxpcSbeeevpCCOMcZGIsCVcOqZdk4vBi8OKls/y -KnTNyQ/3N58SStty9ycPi94QK0aTE/yLhrrZPVb4EXRi6oxopa5+JyrlqkdYWkYn -VGXe4obo2blBJO3XqQWulzvHxJQI6hD9vK3nmqdUX9OdQZf/MW/fZdyw47vdxpRl -DJQvjb76JW332G9kgJ92pURD5g+xUwvIAq9HikDYgpn+40xlGI96qS4JyuNBR1HP -1ErD1fq9W+iYXkCGvKh3Gzamy8Nrhr3G/7gzKgIK7G5a5IG4sHCRVQ0oHztfLT+g -AdQXwNMPWejhiQIcBBABCAAGBQJLXBRTAAoJEBYoHy4AfJjRhhUP/2Yp+Cf0zH7x -YTLtWDhxgaXmOqf2hct1oi1Dr9TPhGhVDt5S5WcTvYV8+6QdIOSupLAoZHmkw7VH -2iHoCEVaTBllK5Y9kgiN+nautR0tG/0i/VlaNmAzlCBtrHCYxwYd6PfZgvy8Zvxc -4qR8l5lRCQ/vBNYtlcm9MTQWR6B4HDZw9eJmkjLIbpeczQDwhnH1NsxfINpl/jwJ -KJ1OFpuCwjNkaF9r1nOb3IsPXqraZH8Rd1LhYAHDsohMpM9PUAC9bQeujLcIsWYh -U6TSZmwS5kUWHF0R/4VF2yN+HBX+SQ7UpzI5xm0/Cqhw1pNxpzEYmTbmtzCdub22 -NAFDcC7FnhO9AorLyg+6bRZHi8DH5i1Isll/iYewEzmfXUBgCQlmqyB4EDaxhFV8 -TJBDiv3DwbuCRw7Lxfmo32OS7T5p6Fvi1O1qDYA7Ahfrw5R7R2xdJWWQkYx3Lt4f -18OLRdkA5+PplVU0ZGpLSivIuS/LUiBNlTVXPI6G4Skbn2ySTrwZkMQoRuZnbyme -gMmNnp+ryVxJzWTmHBl2sduPmMm7Bl/JeXn5/gL+2Ihy0RnZYP/J7pH7b6GFRWkP -8Ujh8kQxL5urn3I4epT923IrKZEyf3dyiNx4G5vRB7AR5fudy4TbxZutEei2ncYw -yO1fjwsx5yJLVnaV1U4sRLzDYG+XK6c0iQIcBBABCAAGBQJLXUl4AAoJEFCucIXG -vfbw424QAMaziaiMH4LewXPH+if2gax6jrKl2zZ8U+BJatWHezAGLD0CBIzV7S7z -puhziKblRtLl9cXY0nZPf2QzI7UUgYWqe1J1R8kxhQSDqJz7f8jSo284ogn0sT/c -QTE2T0yUbe+cSaJPLOLHQz0oNaJA8Uj9lMx0wWzBPv0JDAc8M7Bnj5d7anxqBRHl -ewD0XUNSu4jNbqQTmQn+Pgx2RlqkgW4aoP6AqG+rzJAP7XNdTCPrKr4rqae7l2+S -5inbeWwQ3grxQooUFuFwXWiONpa5LGSM44wwLm/RwOMaocd3mZBVQqOpDfnaiVTK -GsFuEl6S/BCOsYP7ACygzsZarIa4TAK/LHEFCSu26JAEA0U7fj0VhA6IVZPdMxmO -Aofop4zeJBb8V46U/mvzPsHEELwC9A5gKhGBXZXA3eYVFDP0d5yjv5QTQ9i2fFV2 -qYXMbgUDcOqpmvQicHQjicmYL08RaumB+aY2WE+Ky816HfCtg0Ui6xeFpib9eCCT -/A23T/ZK2Z6HLfme4zac+S9AmJBn/0o6Y9VnL9AZlvoZwXo0Mvx2mSkJVgzN+tlg -XsZ5GkAeL+40m6kf93YLZCpgfX40cHF0HMjs/g2xTJ1ktwp55KnWZ4vP2ljz6XR7 -v//1jIYylOKisIGv0rvM2CEaSKNIcnueocRypXdpvpSI56cA+64giQIcBBABCAAG -BQJMYhIhAAoJEOVivZS/A0ReBu0P/RJKef/YIJPAx0y1yb3covX9vnGzJnikKv1p -J24D6h3LD/U7gB/KaoY7UaaNpo5gf0KTpX+jBqWm8oj+FRr6UrI/zE7TsQGe8B8k -IYt4ek2fmdOi8SDLbF2Bc1K0CL0IPx5ggxT2wQKHQRRRm6QA18UrX3uPK4cnwQko -HLyhvLD1jzjj5gcEQOIMbyy9MsYScLyp71HWOKEguB62+jJwWmWPfr/k2w/oxoX3 -cersJKqX+ssQIZ5u9PNfJzBIKUg8ew4j2f5HkcpGCWaS/lBfz1mf+wkwZHec8Cku -dYymweIu+8Fp6sVo54ryIh/Ks9HaOERSSpDvMWTzCRibTgIVFSrg6/OsWnXNeTVD -a+QmIS7ZElHIwxfy/nGHwt9TB0JZHrp2JZ3Eq8swKutgKj5BCCc0gfL8KhS3w1OJ -jXzVo4W/JMBgo3yW8mCMg2xbE/1Ij62R3vNT1cy3KTRk/XX7oBqJHkP7thXCEEUl -S8X/ov/za4XNlKxOLu8s94DfMoKYTc51vQY5FjcEvCAVYflxG4+gI9aYgCkotRGC -Zdv7YUjpFlifEx4gXnBSe1ubtquj51CS1VE1VuH8xz2Te9Zk+MWWKCXcWbZwE5S2 -chpL6/+HOyuccW+Iy4Y/MmP44QLTGKRzbKd0hIt1E31CEzwejwd/YKftcB+m63gq -4BIuax60iQIcBBABCgAGBQJKdaJKAAoJEPZuPkGfhPTe+iMP/jszR4LJs2ymtKYZ -LposveSIV8OUEoRaWH7e74w4JFI/MccBPU1YIwDvrHA8Xmk7GZOl8r6eL2X7mhd9 -DawLnES3B6aohVF0/5GmXKvnh8ygwJdGEIST2DwIYWphd+RdrSnxPaeVdzHEcTMU -tSeAGHkQB5DDHaCWsj7/M7cK/D+0HyG/J+k+4ImIGTCvPIYY6PsvWGBfa6VuCgVG -yOqZG7lBQKTMxoHoBfnFQq7mFHU7rNAWPZA6HHvODkjwPTciTO7xhEZN8wLN8tHB -Nkerh8iY1Pnfo+Up6zCkK/oecKhWDhDYY5TY8L6ErRyWA73TZzqyv1AEMLtHTRNz -FKZgwA8iYgVzK2kpePaTE0pn7gZ5bC71/Goqyiq9HvIKfU5B7XTn4K4Gv35rgrZ4 -p048S5/sGuVMUa6fykRQJPBSrpxQDjsnM2wWh7TLMIj1kno3CSph6rjJERbQIHvg -ccs3taSMYbGlNldTB5qczdNutTxrTkVZuo495yZqG9/xP2pAdmU8Y9a+e2bQXg0P -vt0+aSNb1oGIVZVexGWpTWqERPZ9PVH5ivUFhGxYnlvu/Sp5ZYHrSywoLNaMc0jT -o31K+M0A6squX97ZzbAdFwMYnKPDgdtDsu0LKUIduePfa2G5P33Q7IExQCg+uifJ -FEsrhLdGaC5BmqXHzXBWeH/18q1CiQIcBBABCgAGBQJKghCBAAoJEBNunPF7XTQt -RvUP/0Kg70IELcz9anKkjY3D/oEpQPsR4PKk0II7JevsxyT+cRhqUqgtufcplXAD -Q9KGFjKEfyeG9T4sxkaL4G5s7PAj7GZf0KEMIPtFZgTS4joPr2h572klZAtVz3IL -z/dwqLAy8vpraQco9Hp3YoZDON57nkB0R+xAqglnfKLaNN7D4mibDuxNYd7HHU3c -KcMm/p9FqtWBP20jiWOZMPwyqLsL+zlLTwTQpY+TtRIlN+FcVYwoDXwCt7XInHsF -mojP8p++BNQf0HkUJZIBWyNiyBj9g4oOsZJ/Xk41n75OaIC3QDqIf1VMN8Hm1rpg -JL/0/W1aEhgiCBTPMUP18M9uVNiLmR8VCeH6oFuE6aR1EyNbRMUAq4adhDTqHcVA -QisWi+Ej6iAjEIrYHZuEyjI4t4rnJDmWbxnI6sTmpwPWAx0KNHYj5yWksln2bEOB -eV7mIIBV6YfRmyfuRpTun39ve1D1wMYOSizCR9I4+Lr1e7/TliK5b9cfq2f6gF9N -NfMw5T7dGzvd3sR03NgHgedIY6WSrsmbmFGAMZG9gShbd7SQNbVXxKRGzzBZQWnR -4mCH+aWSsn2SbEkRv6fMFT57lZtLiImvz3+Y0A9PLEdvu04s36r3hAMiMdU3ZgvC -7hdCgmmiTJavNIgsPj0AKaHdQAn+WO/SkLANYusXogW2gIRYiQIcBBABCgAGBQJK -iWzWAAoJEJwnsxNCt1EdJ2MQAKxI7JS8DPKGL2vEkMIFtKnCJqsB1wd9XadzZYNr -2U394O0PJBbm5FcJSTjmpnbvgZPHKSS4fpMdbkZSNdoNuLuxDgSNLU8X4FntaB4R -lTIquYhqIFOReWH+bJ2AtOhHCmepVO7m8zB67VNp5uxnyYCxObD/sodwf/2oLL2P -vQcKtEGVdSAlkKUcJF1pCMCnpOBEmnKpbWq08hEiAsSU3Zh23zjHHZzX5257X4Wf -pNTDWVq8/C1Plq+Ep0CJyJSA4L2ifQPV4XYZ0XofDWUaOHAmYeOyi5lBBcG9LxHO -QnAT6Gs4t7ivRnw2ZvILB6aA0VK5jn0JK7eUD7zSvS2INFCZEppS+xqTIU1uvBYH -9TuNU0409g+MtclZ5USlmDPy/STuX95ENXugjD2uRp460KzYoXrrCoBNRu501n3q -HB2ZbT9PIqW4VuyEltTYcEaOnSPimgW+O8juQ8Y6Lq3ylmOgdTj1OvYGlj7SDDcB -XXusBCqtOE0/GAk0FDNosMcT9JiXiwjaOGFg+DDPTn0bZfHh6ScCFyXpefy7nlyH -mONujSNdjcGvzCMaxExdk6TlYe8ifdvjWOGsdt0/0HsijyCZdH/EvWIWE/xyKlR6 -pzuB/qSDYQAo9u9OmAJnLtOfi65HStnV2SS65tTPthfc9rFlek4/hN8EumC27cN2 -OTr2iQIcBBABCgAGBQJKtP4MAAoJEDIkf7tArR+mH+0P/i0PTnSxVKyBr5No+7q/ -ZQCIn7i7Tw14x3aAltAWuPtvM5bbd9iepb1Wfmx+6O5vIxratbh+ba1uxTPbgl/9 -zEBUTUZ3NMF191X54CgVsPTSLAF/0FUr04jNBMgpFCq1ddf9npyNRAA2MV0yBcVn -1LJC5CCoDMqh8YPjZY8lTugb/JCYNHgZ3IHzQiZLfy2jDDbNB4ZgotO98gdRlNo3 -Wjh6W6BUMKgAkj9U104kYjv6JHvCcT+zN7ZwStW89mnmOxN17oYD5HxsgEDwyAkt -m+SexaUdG8fm3JqYRqccUvymOah0A8Urlh4hg5COlo3YmBdK8bSWZ5pfabqPaVxK -cEm4dxsO8y2pikwmkZiEl1XkJ9/Pn4KzQ9UULtHfY4RKK0991TEynAmUXUfhDzC6 -Qs5cWQHj4513nj/GsfC8s7xJC2L6zUIjTCfW6S6IwyGQcbSdNGKMWEaAcl6yQZ1c -ClgFoa62O6AdnTIZSHP70wYfyQNX51Xj1raGQpzWT0W94vcGZCKudXZ+T1Vq62u8 -8DB0THPTogLJz3G9+sOWKqS8G/OyC3gda2DrKTLoaomNEmJFV5D4LyDEr8B8kX6k -yP33y+LzpSG5TtKHQ7/Dij7I2S51xqDaBNBfVNpFP9SeMdQRXtsCznwIg4gdK0XD -ricz/n4L/Z5eaBsaVY6OABqPiQIcBBABCgAGBQJLZGBMAAoJEIy/mjIoYaeQf8MQ -AI3XUgRSZEqbI8r9I2oJaIJTbBuLV8aHp0+gkM4juIguILOeKrp8SnpAdL/fsKBS -6v46/15LLi/sUfVV40Txc8f4nuDqQzHF8BvhU5R1SP5m7bEeDzAU6mjJ2nSPd91B -nqLpnrgLRIKrxBNzU08xPqgZqGx29uClvn+rdcVhfrxrCVuOAC8/txuOLr+rEcr/ -ipFW4cdI0VhlN2QkowMO1cjCRkqT7Lm2KHtLleu0MT1IzbIXhAaKCss31yDuR6xs -AFO9py3IfB9YcgvTGkEKv9MMYZWe/VOS2eVgIOOCU/nCjKqC91dK7jzgClFbzI5S -ysQDO0lWQ2mn1I/MbcbD+hWplaoAeYTJKPkHt7eZd2mVLUFtbpS3asnOmMcBiaAq -CnTOeMTYnkFzdDWIGU/YyJiys3oe7jqCVmCekDT6IQAM47yOmqMwCH/fYjo/qfEw -LlgQIh/z9zPcW0sruTDnNPtwI5yk4qPjwiIekLNqco1Wrph1I4TjC6tDlPQmFDso -XUaL8QZslu8hNzJ85rj8W9T9jnRO+2B9fFLBskz6Jvjj7HVsHJe0fI1dkcRxzVKl -f1ciiiWcjGOFMke1NxbHtVodBoOifHRpWWd2a0DaV3AbHUUp3Q3RJ9a66S05CDAX -wmZkv/gySBdMJmscoGPz5wH0oWyx/K0T57QjdLLzFSZMiQIcBBABCgAGBQJMrPch -AAoJEAbqoGbjl4MvwgQQAIe8kqh0xXhRu0P8tkRWhbiMalSXaQDp6F5nzXbZT2cN -J2i2wdSl9wh7QH5tKXjgatOwExiWKxn8hHpY8KwvjdpjSZ872XoFGMGMESYlBx/M -+HdqNujls7zz8SqgotZxk+sPFbdWVdHLeYHhR1cDvxkDIWvcaPlIAiuvZdXYu6uk -qerCs4yHaEJTTAh2kG/yom/civv0YJrVkbhdRgImCxqR+cdVucAZS4rL08puJmKo -Y/n4ced4uPCJ+iSTtB44yNKfFyln4YQom/Qzyexbjbrf+Spl1uCcTLaWfdrgu0ap -aMVGNviS0hfB6n8xTm7tfN/Uj67luCuVgk7EqF12qdyhzmIjgJj2Q4EZ8jolRdlF -FB4FcnbvFBNC0SP9DWS3gfpTBL53HeRQ50t5gbASuB+2Gl9oeimpz1LfTIzBWHoS -tmoW8o4O0EkbNKGoaZYeeFBa93y4Nc/tJmgcjsNB6bs63vahWeXSNAUcEJ0iOmgX -HKYJT7W7D781oToNdv/pJVRUVsCR9bwHIDSqnpsWUDglQitdsu51PC40Yt3Ds+6d -WtE200ZT9QH0H/kLccf5Mj92tat0zaeT2Zgus1ReCe/G40vXU1y8M/Moyu1SiaOz -YMBkAx/WudY/IGl0Xaqq/TaDEE3JLCya//wUokD22L7ro9Aw+yne5RuYEm4XzhDT -iQIcBBIBCAAGBQJKd4a7AAoJEMkQ2SIlEuPHyfEP/jBMTVhBIoWECD8UHP7YH6RW -4Lpx2d6+RQxYYkJcwH9Zz0fRbKhLfgaXgz0SHKbPYugw+4ydSJ8DXflyaQ2Oj+ua -RLCkhh2WrYJnpXi3hjMQV3LU0JORLuHLB8WmzDAqBZeAkc7fZ2nIXax3uRFDTrKT -iq2y9xbYHuBSc9Blrc+Q1/s+2a+G9hZVJ/UbVv9YUAA3LOWVTYM8JQkrv8mb9em2 -cZ6GV6usgpENIEf50phtrWhKr9l+gvbp8x9ZNUABsesJ0lVrF9dKEHTdA2obhojf -pPi+MCH/LeefjbqNbkhufyqA2RWqrL+My8LU8Nm3n21b5lK4SI2sXRExUq4DeHhe -VNDa2IZdWttN6NNR/5X0y6Cei7g3AasD/DptT2bdcbtvwk/9JIU2+564zay68Y8P -E5ywWfo/LXC2pn94K1X89pWoqzK7qP0rEeA1Y8Sojcxgztswckwdxm9yl9LxmzJo -eiQPuSGqmbkQ1aOd9WpLvSk04oOSlHZviyd/me/oZXc1SMpkrWuxTW6usY1l8izN -JA1/GSw3yQelJE/POfBg722V2cAuxfGoAAAS2tieu+6kBEHopi+ZX+m3cTdp3zEo -zTq/pdC8lJngNWKUWiz3F2dXxNuUdN/UZY8VcTNSfHV0XJlv58G6MefPSE6tKnQS -HsssgsM8mp4r3apuXWLmiQIcBBIBCgAGBQJKecLVAAoJEPU2qnARHVcWsNMQAI3M -77b0Mbvf4SF3U9UgNGLSIzRqgmZrTXAAAqM8YJSuTTwnzTFp6ZEnQ05WxBsYz7QG -zy2EqQZqupKF4ojby+Nn9u5OeLaqgfPrymzouugEBv+Ha8SNhQZDdLIa8uw9Kl5J -f/gReZsgedIiJJXbnii6f6fizNOoogT1HHMaw19WxPCHCX55PyA/AAXdUZDnhs03 -nOrmjx9jEY6C8sL8ez6tCexR3YqtLDFBOfZqx4pof00l6njXzXQpZ1Li9iUKhgLM -kacf6G5MbsUMtIhHe3lNNJKXQaAo0v+DwH4scFoDGSCsltLyqSP7MxfclE/MohWu -2FbUBIA8xSqJtYgs+BEZPLgl83nCCMbA6qlfxqrBAniQMkVXtwYsi34g9Hxrj0oY -MMb0NSPPFt6NqmxV6Ig0A8r6w/iSxqgaNQ1h+9j2/LNqf4liKMPBHWbNJz3M0CpW -6X1NdwNgm0BG6dygarL0slRqjPRMcndNCut97Y/vT85cNF9kHgXen4McwqKTVKTj -ArTWXSCtgjFFJjHyxomL2nrhdHD42MLw4mWnEk0yl5ei6NaSVcoFbnZW14Pjkm0W -3q0VSyTW7mPIUU/zn9W0/DWdyDBOyUUqKaC9HiVbrqszIrFyqjJWfherGhnbbzdL -c4jYl+c2CqHWopzCNBcjokeGaXzB8DwalQjbXjZpiQIcBBMBAgAGBQJKcWbpAAoJ -EMaHXzVBzv3gkVEP/RaSVuiEy++Ahxo97feejCIyMq7TbQxoguQ/NEmVxsccHF5V -lpalLsE1aLnCW4KOtQcZvB81u764+wll2tKPhZB7aQ9k715hdLukOj1fdLuUnKcz -OrD0TjN1F9EO8uuZqgn71RCx3p2W+B31RICL3fXCHcLVNsrpsljgmwarqpdWPn9m -ovrzp4Zgtlu4h8b4GEK8rMC/SKJayaNl+dpGGxvBiLwkbBQJLjn28kdyWGaaPELf -uKlFDmsxyVZ9XlsWE+kv63ZkCW+sp2aZH1NUXzLMKwS/jqWpM+2PKnr/QphajV9S -Dhuym1ZIUIEvfA1VnqOk5EjqoR7gzXrDVfek7eGRpeHZpIqMBSNDS3OhSTYxzyze -t6VnunxkBSsiQumFMIVUWKx7/O/5CPPTl+NgmZQNdTt2jKYOHuDbJU+WoijUBo3V -n6UUTTmJ2d9RQLF4Zc0Wg0/b8y0nvYzZcU0SjxFQl+s2UtPTlKkeHzuAIvKxp6H0 -ksRET7Ngz8VWIjGrOER8gAifIg/YpcpmKbWy8Y9hrBGWItk+C2CODW/gBzP/a4h/ -1LolrKMUumRCuES8fnl2mWv9pUnwENxVuNdl+iaULcmqej3Fj34K659qhxYNVG/o -dVLtA5Uzt+wRrZ8cxWe9QpBtCM+S/XoMqNQyHxkSGXSJ6bZsXcU+j+w4rqPSiQIc -BBMBAgAGBQJKdIUvAAoJEM1LKvOgoKqqC/QP/1GRnL9J2pXv3JRQ5zSo7H7MaWiy -6y023/4P5QRUGpTeEw7g+13CxKaqW9i18/neQqkeCdIekE7soWyJ9huUW0NgFmsJ -VXTfyCqULHr/PacTrhaeLRLO6hetGZ1OscXUEJPuPsS2xf/SegPbUp/YSSYN2LU5 -ebuS30bKfKJ2yUjmK4IQVHGapR5sP3esYo+0MMAKQyP0uXpRsuzDCUXBZQPY3mmA -uWOuJ2cJ0t0VXWtm/xo6ilBiMzMxOI1FSp4EQzk8BhNHHDU+qTQNj0n7QS1vFOXB -IqvKtZ58Lg12xpfa25+6QpkpxIrtnk6ebob8MbyCco/9LSEphS7ooD7Dke7gU1vR -C/RhA69bRbcydk8oxHxmONAGpNxjAhrbvxE6tp11TF461Ig1xdMPOS4Y6q3XQtha -/h1qDLgAlipN79/E9dT9qYevDjBcNHsGrdQE8lyjym7afhmadvF07q7eJqb2Oje7 -uVYnJtr0gGfWIhvWU0qmBvcPZqXkkFdQFmeEhL5iNFOhvve69c6mhkfYKsPoBEjc -LD2WgkbWnT1uu3u5M5z5W/7cbMjlE+tY74c1t3vGLglvVE1nskTNzDLj3EpJohNk -4Sw7PL7KThMS+bxLUTT3pZ6sF20rFHniw3gJ6fE52uy7rVxk1Yx3yfjdPh2aN7Wv -AJ44kHMkX3q54UwMiQIcBBMBCAAGBQJKdMniAAoJEFh5eVc0QmhOsYoP/1AJhOV+ -Rm+CNzqssAyPd0AoFGP+eVYk6tXUvYUlXhUIZh4nuRa8iKeoFDf7UI0V6Nrggd7U -ufYvxTe3w7Tz50clYbTJRMMLSx/m6oG8nXmI3d4ZPKTRa4DEVy+5pD2yz0NBiori -pn6Q5tW5Z/2ly3NKSCJZMpSNGvveNvOePuFeNyulz5pHqQdWrvLPCdUWSsEwmiAX -2ioP1KuefWCmt1F2AsQm0qLPk7EdtfObx7TGSS3Mfk/BjS/toKqWOAxD3LnabGTa -7IzaZa9xEsDS8pAh9Yljt9IoR6KNFT02w0RuJ5EP5HhNkbqomkbT6DbunnN/pGqn -simsM69MSffXr85Goe9RFGgg0Icy4RCS2aZx5DDIyPC7Ih/F9fh+iEugkfzUrHrM -QZBHNh9MJ3O9IANXIV/wp/eUg7/ywCd4m/ZP3ral7B1u7edEdtl5vcPe+0L3GUYz -vm5fYuoKKrOjydd6of+0QArVxMXnA8vPePBX1UEsmQ4DIyEDewxn9dPIMWt1SdsV -nLPuksYm5jcp/BwQPqHdlk8nrXd7R6YbowO1W6hb9rR+lTn2ZjlaL506Zu+DtpKE -oOkdOVa7Iykd8LBrdokP9/psE4L7AZ43eWe++2NLFRLSkaUZZZISGheOXw2aVU3T -fE5JNKm+fjrP2qGaWDt/o0R6RCtbjv82VEleiQIcBBMBCgAGBQJKfYzdAAoJEM1L -KvOgoKqq6p8P/1+VZfPHBhrY5GnbYydp2IospEvzQlLpcPPbDsmW7ufXzlUFOrOK -jkQqN9AlX1L8DL36oF89DIUSfVJDPP9b+/3c4aA7gJ/CjtqH+Sis9Wz40NG6zbV/ -ORwCB2y6Apo7/Y2GtWMaqxj7Jx/1mRwhZmJi6kDquPSfnFRW697ueCSJvw4Jh6rK -uQ9TkhwrIDPtbRd52mpe4z8mzmRrzKtdQOEWdx6+fuKlFP6tc3r4fFe/VzUUKiJF -bzTcVVgpAXv31Vp53nGNseVMIfjHo4aPyGZqpJ5VqZu4N/AXJjqFLbJzqY/pgK8J -/Sdh08vloYm3Nf4g208E+Uqdm0e0s43zWtcpm5yTLD9RsnQMcGL0uGD562NWcjh5 -GjN/1HoQT00clD+JmGIe8e1IJv8JMdmqrp0Af7sgbtHbBMCbKh9TpW7EJRKnR47w -CSBaAe0p+mhjQw4bBK9ApAAcnCeq5eGo4w8eHrUQaoHpOnTPCh+YP/z/bmcujxPg -I+W98xJEvmLy35Yh1OUJQAFz7a+uez0w5QEDeh9U5+j8xYzkEVRMm2r/m7OjFygP -O9WoPjBAXSEiZwuxskW9REp0TQCFGsD7nHHIPT3P/Es5evF1Aymu2zRpPRZeHaRd -EMJtMs2zFmAH0l14H+bW66Xn7WmHMl8iOONqaswRKFIQcSnOPqLRACb0iQIfBDAB -AgAJBQJNHIl7Ah0gAAoJEACAbyvXKaRX/mMP/3A3Sdj9YFDE/ZwR/HDWL9Jw/HoL -leolgH86gpPtpnwoZ8ak040tO1G9+8kspcYND/In6z/hU5lKYWP8F0WkiHpRS2VC -YQ0Xw17zAeUjEa/Q1wwa3i1nMoTAueDzuIsqHNUvQ9Qx/95ryfe6rbIby5qA9/iU -WaMWTlzWj+7eI7AIPgBMJyzJAWIY2GJKfAvhjzQ6mSCUq07qER6BtBakuvZNKaR4 -H0pNq9jkcII/kRUl2fWNtSoP+Sib/Eqph2RwAits0WXmE60gDhtEySxADhTQ2jiA -wJeiemXhSocd9FPrVT3ILsBuCzjxJTLuzX1sVHnTvS8kGNnQvYhEuhmP0vT9QcT4 -TS5DH7pPN5L/HmVxOGAhduJjosZlV68T+k6XHsPnbMY+X6Jv7pbOx7kpb+zqxK+k -0WFRtNGAa/z5Yr2sGkXbQQG/0o+f89KvCbMiqRPPkCWPMqBodKw0t6aOmBczSwad -T8mWlG0kNMteF/XoWgcAjsKxUJFIZzUBOEsekc5a1fNWByIgtwJ2nhjck//UvV9l -pOCF21/fUFffl6ZqYxHNOBF1nJkJk+cberp4CMcm4eEID9EIbStpTXB5Lvhu0yOa -lkiA8rTp7i5QHD8j8SzNoHUcvhXYh5Nmtfg+sIhZ2brYj05xvu15xGxvkJ6XFwsv -QsW4Wf7lu0VPKtuIiQI3BBMBCAAhBQJKUMUGAhsDBQsJCAcDBRUKCQgLBRYCAwEA -Ah4BAheAAAoJEACAbyvXKaRX9g4P/RmVlVhocYj61Oa2HKYsD1Taw2Et1xSGC5ta -IjUs1ZvzkUdgpMSKESp7B7G4Mr/UWcpSFB2TaJmQHV31XF9vvDfTvjUgRBw2uV1x -+bhBV00Vmci0CLHeIVILO58BqXnAuR2ZaE2ZujSipm62kqhodPWwzMgFCF/z/CSw -yO2muoxfLb5+d4rSZMSGN4H4fI9O4VIXi5El+HoEMtAMznEz35pwE14y3PlumboW -0EgWIksA3+sjh2uIuFpyrUr4imws3TeZDISnBS81q8zI/is3/65H3PaGt/xEBpkf -MJypfemhfaI4+5D+w3c7Wi20cGmTq583Kf0FJs8wUBpHs2xf+/sEZLsxHznLxZdI -5TnixSGTydYXk+UFzGCHzYdjwfk02LqSsQkX5qPkYHknM8tJnt60m8zSC3DQDAfm -IMDDuKAgbATCTnhUrEMyLW3Ny6slchsJri8q6oV5NsNokiamU2LcXu1BHPv5f0Zq -nb0F72d76iQ008NhvSHUGv5WcX8EkxTuHkVUKjY1XQHYHP1TFH/U+mkRGYQttvpi -uaPIX5JnCrTGb+dcjrZUMjsmPYu1eKwCsAYaKORNUcG4XkUFwy+6watJhxyAJdx+ -DZt45h6DOORvLaLB6QO/IevS1BjI9gS70Q7Nrcw8Zh5+xPx9DBFOVWU7z++dwpZ7 -k1Xg/QbNtCRKZWxtZXIgVmVybm9vaWogPGplbG1lckB2ZXJuc3Rvay5ubD6IRgQQ -EQIABgUCSlEkEQAKCRA9r1SiHu9SdnmOAJwM0vy9HZuOHBlzKOq7qM6leCr+5QCf -bGskx267ylYU+ZGVfjeW7ReKB1SIRgQQEQIABgUCSnK2BgAKCRDU5e2swBQ9LdrB -AJ48Kv4cmStxTQhTrec9Ii8AOL2h1gCggRBdhecu7WHY6T0zY4SnZaVZyVaIRgQQ -EQIABgUCSnVzuwAKCRBp0qYd4mP81KIJAJ92OO6MKiZS1iG3y0JPgUpKerWqdwCe -KT0Af+0yZHMBqgdystysEt6PQlKIRgQQEQIABgUCSnhdNQAKCRDVypsE8sQjvI6s -AJ0QznNzzPWqlkQM2zPhBWtXqHp1yACgmvGzsiBAwqMTecc4jnr4R5qhUyuIRgQQ -EQIABgUCSn0wjwAKCRCEibFNiAdSm+5nAJ0WU8qCVHdkWzjxgTOKyoT9R3JxKACf -RG7V4l9nQ7xOGszMhk7B3tbUXJKIRgQQEQIABgUCSoKbhAAKCRDjIZO2xCm+L16g -AJ9w1jwTOwVrmHnFNOFbhS2YGosZTwCZASn/rfDZG0hDo0u4gabV+WTnFoOIRgQQ -EQIABgUCSoLqBwAKCRBBIcdDMXDr6Z9cAJ9fKws+Bt3DjaAzkwzhLqKN74kPMwCg -pdYZ5TUBWjm/H4A+27wEBdBiVZmIRgQQEQIABgUCSqQySwAKCRDAnh2JlZMO3uNI -AJ9JxziYtO2WynyHSa4KL6N+BxfzvgCeNW88RaM9J8E08hKCxFohto64D6iIRgQQ -EQIABgUCSqQyeAAKCRAEBGuFSi4WK7eqAKCIJsmhZMteYUGD8AahcoeqiK6GtQCf -QxarfGx7mhJPXsNf+NA31ueo6jWIRgQQEQIABgUCS1aYcAAKCRB3AbuFiXrzo0Ec -AJ49LB0f7QvxOrb6SJNHesHIwKAWzwCfc0cd0RNrSxKuQFZ0BM9D04qlA76IRgQQ -EQIABgUCS1e1sAAKCRD9H9kjU/RdMjA+AJ9NdusNuR28Cnal5tRAye/mxsh/dwCf -dmGzUMyzgBjn6WtegVhdzJjgc+yIRgQQEQIABgUCS1pODgAKCRBd4Tq55ytLv1Du -AJ9jR6QiwBQ3/36BqCdDiO0VWtp9QgCfZqpw0+8btu1lIbq3+XaawiDdiouIRgQQ -EQIABgUCS2YBngAKCRACvEK3Q+JdHruFAJ4itAchOKaP2RXHTFKHVOx/H0k8dQCg -uFEDzTxc1x+OqwUhCRVdNrf9AZ6IRgQQEQIABgUCTE3s/wAKCRBlHfNSPSPyXSU7 -AKCxCiN0YunAoVcj+IqaRgrMCfx/uQCgq9us1XgDVwV/Y0Vt3kRUWK0tPieIRgQQ -EQIABgUCTE35RgAKCRC/YHCLSEJsfmEuAJsE8SKSg+ZuS1y0gScQzfEgrGC8pACd -F8/+RVcnLgNaYsVkg94suMRtxkmIRgQQEQIABgUCTRy+lwAKCRCnGmt/a4UvN4RZ -AJ4vi5xHq4+UHzD0ZCbe2HG9uBQEwACfTErPKrijy4w/qgFki28xBEqk6M+IRgQQ -EQIABgUCTTu6HAAKCRAL4Kr5tSzLUpb4AJ4wyu5e7kGvinJXUbXnANXbcH/D5QCe -Oi2no/312U9Ov1uviu4n9zRAmQGIRgQQEQIABgUCTdZizwAKCRClBubU3U1QiHCf -AJ49mBVagGayfiQR+xTE0aicLWbD9gCgumKTi/sFKgvwuRU4M7q72nUqUd2IRgQQ -EQgABgUCSnMJ6AAKCRD2KOuTR0MgbFzgAJ98WegyXTXWqm3GVzmPHZpPNE20qgCZ -AcGIQct3PsDafAC3sILw/evhZOSIRgQQEQgABgUCSnRCvgAKCRC89sYPboFp0tuy -AJ4s3MxNtdtcswgxVIDxdnWCkgbk1ACghxyn75kOekrp3AKbM1Sf2L5KtB2IRgQQ -EQgABgUCSnR3PwAKCRDDdqja8bzbcxMrAJ9nqkKSKNGAwfxvVhEqP6ZZoJuCIwCf -fPbff9VbLGpYl1tYGXEeglkKApWIRgQQEQgABgUCSnR32QAKCRDZRtVqCOOLvGUt -AKDUSiEpvoC8EsUbQ+v5eDDcc7HilwCeKClskjHR2eMaFHZ0UOfxw8/inKGIRgQQ -EQgABgUCSnSc2wAKCRCGvKv/HADHkESaAJ4nx27684RA0hETOoNxSzaoOTR0RwCg -hJG7fKh7J8FYxMzM+yX0PFsLqJCIRgQQEQgABgUCSnmQuwAKCRDU5e2swBQ9Lddn -AKCPJIilkuqnmEcROIo2m4fH5FH/qACbBBQ2CW+zboCBiRFvJYSzkmzjoSGIRgQQ -EQgABgUCSnwzeAAKCRA7MpidAPPP5OdmAJ4/705v6jD3TtW8ysqG3xD+8ifczwCg -rmnUJp5FKrBzfI6W0kLrCuXyAluIRgQQEQgABgUCS1azVQAKCRAU8iKaVSrZNDTM -AJ9WEZquaXFHRU9iT4jN2vPa0dxLuQCg6EjWBT+m/HepdAC1h8T2T3D0ViyIRgQQ -EQgABgUCS11JcwAKCRAGMraGigSbIVNGAJ9djkI7VVdg1M+RZJYbF8a7cIrJAgCe -IJQwBI40fZTH/p/YrLBTPodQujKIRgQQEQgABgUCTTvTzQAKCRA5YGZPleoj3b2I -AKCG6aBL+1aDPJA0oSCDdieDfBak5QCcDaiFdV9//+1emN75rHb8WYzCmjeIRgQQ -EQoABgUCSoIM+AAKCRBRlIML7BmzeNY0AKCV7Z/yISJ0XMANhwyOFXdODIE+owCe -It7jjFU7vilJg8aCoBuwXXz3FcOIRgQQEQoABgUCSolsygAKCRDcNVLoNudWBGmb -AJ4uVNfXWbM+7KOArqEkAWn5X6yugwCfXTKJ7vdWpS8bpibzguXzCVUcTTmIRgQQ -EQoABgUCSrT0UgAKCRBBIcdDMXDr6cyTAKCInc82UgutF2JpAdWlseIcISFKGgCf -VE+vn+PgU08HbTzRTOfXycKjGqOIRgQSEQgABgUCSneG0AAKCRDa2nnNeIo/THsE -AJwJoHhSH96oyrIc89nBFR2Pb0BnSgCgq2QjcxU4AsZCX7+Bid31lw9uEdCIRgQT -EQIABgUCSmyZvAAKCRAS23nuxHY7pWM3AJ40fu9gNYS3X5qnjbqAa6lxi1IwZgCg -iz/bleK/ZLnAL8E6JlsPhGXqpgOIRgQTEQIABgUCSnFnDAAKCRBvF6WvwfJOpILl -AJwPJLT+TJ/cC1tzWzixbkRhJwUEhACfR6QWoraChMTpVNjO0jdWy6muwCSIRgQT -EQgABgUCSnTMxwAKCRB8O3lwiMfB9z05AKCnlM80cvrWJYOgKGLVNIeL0Eac2wCd -EVB5Vn+tC+8husF9WwkfzCtY7SOIVgQQEQsABgUCTLrM4gAKCRDyrYWsHkKzZxea -AN9oekFaGzgUF5hfVHQOxqToXVywlXCJdpOA6ALFAOCJ+Ir4U96f+AdA7X5hdN5+ -FuLbD7F3oQnfSmnRiNwEEAECAAYFAktWmG8ACgkQwqNdSiQ6EykgdAX/Y0fLbNbx -Lujqlzv1fxcfsFPl1xcW2cbSX1N8sJ7lJCO4qzxosg/uPfjnBWfF4FHQHNsfLjHK -9OkT+deE4t1JueyvOxrg8eHpZcSPZTeGBngXoshxoO5noZz4dAWlncNPHbKYu0l4 -zGlVWEnYr+HVgRo+Sb8/9xmra6qvgNiGCPO0bK25BNs0fjwMiPULBD1sc5BKt4G0 -k0jY/tMaHHQidH5OKK6BDws9LwVhh0024Vu+GQQYXtMN4sU6OTIy6GXRiQEcBBAB -AgAGBQJKdzLEAAoJEJaA6+SpkZPigAgH/RIyDHCAwVIM6B+5hxyssGm/Xho7cxFa -Bxm8+mKG7/v2rNGzdMlNDaHmn+CspF3JQdHaDuhMZysVmWg6dmFV6DMlPiya6d3i -l2kWRfD7lpEnAS7zGWsVmbaNyM2UWJOm9Z9sClwJIpfeb0nNBE57zpcFGKU9mnsV -pUcyzpRvQzOdWAPxCtv2sUK5LCUGfYIe9jz/B/mi7pBPZ4iFNTpxVWnGN+LjSZSl -dKq+EIAPgVkl7l4J8YzWsbqCyPGNSawBNfSRqWK0z4BwptNjTKrVoBujv9DYRk7b -AtP4aUxB6No5TprhE3SJ2sy63m2ElxtBL4hgz9nBPcVQ5lYCQvKpbPKJARwEEAEC -AAYFAkp3MtEACgkQMfzn590HlGEfxwf/TsRH7m0OFTmrgf5dgEpLMWfs7ICfXi5X -+icHtmuwqzCGTW41VwxuNgY1azeDSsaZqXYD9ChWz+ODArYlslrZnrDOb8dSiseB -AK13rYLTGWI0x/7NxOclgv4vDmXM5XLeQKsTnipSJe9QWWgX1TQMpX88ZFVKhkpo -+LcocBj+G5DmwRLPEjxgMtLNZV3XgNX1DaZrDgp83n5e5sBe9eIaYlx40RLWV3fv -9Z9tBw+L21vfCSLG1sdwX7CFx0lO4mww88oIuzods7fKpI2XcccZ5G+k8D8X+NNR -nO1P3HLtEmSSIoHmY2QG+3eR01Bhh3TqflwmQE4sRGOOOJsKEqkwgYkBHAQQAQgA -BgUCSnQwaQAKCRDz5BIvHYwmGqGwB/0bWjHmG8Ll7n+SJhPoqxtwmTL74VjY6KrC -xsYPNue8/BulRGsGAWO8n+85hxchAjF1ctO6cOgUcJByBQpP3dtfpQCw9IkgQo9y -SaiWhvtmFQ0jyzpeqnlRNA2uukZ2AIqIs3fRe42hBYFvz+3w2gN6l7wCZCCLrN1M -EJ6zsU1+YiVMpdezEXGa2LHs/49u7QiiCAJevShOtpy6vd8ZDTQWx/JTPVbHeSun -1v2Jz7nOnWvZxRJA8YNM0XTzABvCak7Ebvo8A1W64NjohfLks+GUC3ZLdiCQ0Zd1 -WmrRZqM4S9wlf4eTKV4cENTXk3P1+8EC1x1t/1/ZHMEvycQ6uNF/iQEcBBABCAAG -BQJKeV/XAAoJEJaA6+SpkZPiKb4H/3EE1HlZbzaLR3Kr7h6siDLrko5W6u/Rxl+7 -Fby9aVZtVq6z/svujy9vNo1r6QmTU/1lU6muv5NElYyh0KBs/61cG3VoRITSSO1k -7tvRlHeyDCBkvDYLWS2j07qjWltbAxgsJYz6Plz8PjHN2nk7vYT12Mqh1V+AVUGZ -VdaKoNgilnGJqx4/XlMIPYpkf9RNR2pjVo/H5D66tgeoxWfLd7uSjPdg2tukbdx0 -ulk2kIdAT0ey48Y+qXlOxq1t05ZEKyrH40Q6U0wEa4g0xAWFLOmBkwhKt3OOTspx -Fm2p/7Y2fq/2as8n+M/WZREtt5OKOzdyNQH6ftrEx8y80HUllvSJARwEEAEIAAYF -Akp5X9wACgkQMfzn590HlGFBdQf+M1GILkp36AaOPqDRw1PrUye2zrxNzi6UPDZh -6ml4fAFNPpef0jO7dOea2R7arrK/nqa5fx47DgDE/K9tVX1264Tjr3+MsuXInjVO -8oIbRNDmi23R4LlwDLcHvOo2kbJoIra++WleBN0eLlyzgjBuO5P/bxNT6MSKwBTy -L7a+biRq1epKW9exHtgwxaY6jxfLd+bsIGCnRJ/wmhTq6PrXxYbz2+2Ts4y4dRxR -YrSpUs4Zrs6MVX8lJn/gaicPHhp3EX9EBVBnVApXGwpHdW6M3bHurP+vaN80JLmc -Ygz5I9EVTWWCgG0v+e+bgF9xTula6fvnVsyYuGllsLiaSCWMO4kBHAQQAQgABgUC -TTu6HgAKCRD37mFu4MIM//5oB/44yFSHjtgXhf8scTN4WRdpuy9RVcyciEl272IJ -sQRleA5FpZvKIb1ETgVIu6xdtHILI8gvA9H8mAsYJuD+SzS0oiXC7JFELpdTxYNX -AV1WMR36zzyt1vQUtaUJwje7ImWiTeF6zM8OVSmEkZStHrRudFpeXoBpBU+A/2FO -P//hwBNxgVs5Ey5AIVLirgmRMgp71QpXLG1zbj86P7Uy9XRZqVQS8iBu7bdJXBHC -madDra5lR63+8QHcp8e2S6ERajmmXmFLXRCm+Zs6PxJiwgdPXKYULtA7Mm4FnkQM -fUIT8AaD9kgbGu1nbHRkBEugdu0FL98p7URgtLHR1fL1i6uYiQIcBBABAgAGBQJK -bhh2AAoJEKLT589SE0a0a2gP/jPBA/tbnwQiUwU/H72WS26wDj5jpwXEyjjD08e+ -ln2K+DKNDNrewOdrKl6YmN+P6B8b9txwC1wW+zzKN42QkowdUIa2TLdwLEKLo+hd -wZcG8aWa3Oaonc5LdJpvU2IKrGdreBNkI8HWnHT3PonULxl7uCSRrA+dDKozJzqg -A9BBwuYpcFpBKjLlYUQr7KXNNwRjjZ+SBvtcKUK7p+D0oncOE1dl6zpHw/7JTUoW -aNlVrHKj4EGvOyHJlUpG0eAoxWSbCbRhlb5VlE9b53gW8Y5dOJuXygsIx1razwLQ -7LDEq/n+U/7vV85/JgAI91eb5odbe/iDLKFTM2R7pd8f1pLlD7doPhgYNpcuqMYz -CA0REMLW/1Dn8fOFvzLy8hvGBuGnqh2ouZzAqZjxrI+b/V3DobLz0I+TqukMue8S -Igh7IJL0pWJBlx/3lAtPNbldvS9PwcEjlREy4kJSuxkAYbmiLim/9adEzmHRxZWS -mn55aFeXwlEvxtfp7mRqsNGGYTcjXNRUg8KMF5VXP8ZZvdeScwVjE4B1v8/Pwe7b -+i4ts8zW3vUBw69WpjnqTayXKxQKcyPWLMLyhb1vLu5QriuRiUfB8owdABCWYib+ -QOzDzXjxxWcMPUd2JjbPIXeq3oLaiM3gCxfAe1ce1QFG27bIaPYpuY2+SJ9cwYAN -T4eTiQIcBBABAgAGBQJKcJfwAAoJECx8MUbBoAEhgtgQAIjdbbh2MYdM+RD/iSy5 -wSaY05DSlJYgc7xG/RRkWoCdXeq+LdqZzjM1w70Me3sP59QhxutICbMKW+ij0tA5 -Y2y5hl7rHUO9mEJ9mLEjR26Zk25+UHjMv/EmR/u+jQSeYKnYsxUu+UOyvX34roaK -ZLf/ETWVXr3PA8uHlquZcDSFV4ieqYYkRe+kqc8MOgJRPmiGpGraTgjZj+ZeAYMT -vEsJOT5LfX//a0XsGvYzT6JQSnOJFoffyfS/XIdLoFuHqRj20TBFacNeeKCtNEYH -PoiGygNp5uVtXgDZxTUrT2C7etpQ1N64j4kiRP+PT2UvyB0tcntq4l9VrVs/zOp2 -TRUz7ScQX+XfX5fk2pat5DaPuWT3bdZREGKah1TqVPkbl6FowSk5sCld53fzXX9i -DVCXc70a1As+dfHLIhdt6dQ8BQ9NwMF/6evKm8qIYc6QMfCc9tosg8y0OD3VUFQE -4wYjrDbZdcKBhe4zL4yxCXrmm6+g9O5wutf6akrVggt76oOQtcTjq7bXjj9Tcr1v -islJ415Gyg2ktAJCDo5VlSZ1DXLcdGZ3EIFv3ko1+gqZKECr+ggdH5QdYrkdSrV+ -AQzlPT+YHLUoZRi4HjWsOkY/HrQaCTPq90MretTc6nZgG2Z6C4h4GGep19mhlFkJ -E1bKrY20k3ppcmGNVPPOPQu7iQIcBBABAgAGBQJKcJsnAAoJEO2GBLwG9akf8JAQ -AKPpJbOdi0WcRL2Z36JpsNbD/nUO+1adXw4qMIW3hsoNeWGPKPzjxITtqRNa+gka -C1oBTTGPwvm/yaXiR6pBicnx6cBhTArieJmu2DCvGEGnsBnKkIjrksq6pwITVEAP -oMo6cyhEmcIWBVBfMYzd9Dx5TGoa275G7GTb5sKMWKaKOuZHFKivnmautn+3XBH+ -AOgdz4O58GpGkSl/A/enczaS2/IlYTC+g0+/MPg8OEAO6PATe7RFrWqPpHLCDt6a -D5yrZEq6jvk42365LHS3++wuHOUIJuGmAKMw/z+nAC49eCwhEbIXKHPBZ5nfrvxN -DVOKcamXTzlnmXKfIOdh5jav0nLcu1tsAv6oSVOEgx0E9IVviL9XrmjcbY802avF -xNJrGNXxAXlLp/CZET8nFkUw2s8Lh1IfxpWT3L9bqZ+p6jIs52hzr1UNKIcWtVgn -0FOHOWiTD8HzHr+mqEe0DDYf0NEtumfc+gKe2JhpRajix6HMJ8A+tQaYbapIBGEn -3Yzh7uZAOVdQCTuQVhNjmFR4aHMaChvUb+00e942E8mZGmBWKq//ss18wu71UPYB -JCcKI7fKX0kd+1LoJB9EFJ1NyXcFVCCGY7W9EKwT0Kj86ZEDCscoDDpg3wbzxfv1 -GreqJPKVtQisLagI6SEuDlJj5eH7IMNDQV14p2ZqkmNciQIcBBABAgAGBQJKceUh -AAoJEMx5hm+ZQJCE52EP/25QU+gHFkW4ccOyAyAIUYmuwnxvjUV9tjoErxczGcsP -Q/bY2T3ugbUI3bRSx9PpYtk3rwYeOePOyXF36951+Iyos03huVEHq09ZyoSyh4In -QtkPsq6/c5bt+zVP5eE9sBMFeH80bw2Fv/fCl5y5QXKWWKnaadUu7hnNJ++tgnwd -mAFtvtXCBpKI7zWU3TzR6gPWhubqdZDOGw/E0++98EtVcGEA2kbjX0OUkzrHaJmy -6lHNGqphs0v3QujVAC/OOMSYkYEZAjuB/4mkwBfSSnrNcZIn0VuasceNamNghdi5 -7LvhYzpsQeRVskExZ68S9HQVXTozl7yLCNOlqA0sMP0CRRxoDBmXHlBmvuJHW1mG -EzkIIh50xMpZmA1I9hNNOuuVSUBqxXO9EQUSUwdLjPKEh4xJB8OVcXtmISOu/O1/ -Syrp8wHhopv42rehNqkBmEvcjQZYHIlpNEKnoK3Br3yq3bW/vrkUHKR6VUSS3xNd -8VitQpt+YyWdmQzQSA0QxCyncjxhoFbc/iphIMW0YM8YrjQBB5JavAfiAt7zaWUu -M5tYeBim3IL7HRAN2PvrFp9VfOs3OzTazcjo3vVpmiKJ7W2Sl1LYRRdP8o+gW9Sx -LIaR4L2P4V86e1ZRsejuO9Dk+3lC0Z31q7pE8IAtVK8LLrM4gihbM9yRWHd5VVB8 -iQIcBBABAgAGBQJKcrYaAAoJEIcvcCxNbiWogKQQAIi8jmGD1l1gGP8AbKrB9fKA -T1VVPSU6C12yl+MZM4KXCDDIRK5JCtowYFk4lhX78+u7+S+qjRoqAlwSgM8caC4R -cJEMrWTBvba1L7T2ZF9JagXlhL6pEKdOl1u8bep86h4rBCGCj7b101mVa21GTPvw -Bij/bRU5NuIGrBHdNn+fHhTPF202/8cRGm+VQNp59aalV8VqVP0cOcqmz5CFUPOD -s4dPNj6F34nOKI/G39M1ew0bCcWugE1MazwOmPU5U/9UW6KwtVQxo6a9HBTKalfH -eh076i64R9MOCIPP5lEtudigmEWngDmwnl9nv+C70exlU197Z01kC4/HzwmZUwtv -lFb6IIeNq6k/GYyygibj6jKGlOLvrRYsCCw5wMV2CeKdhhmoD4J/L4tLb+5aIZf9 -aws8pFeJ3OdYGhai3Ecg2DTCtnj6dUPQ+ZN3auS1Fb6XVmQZepWc3rV52apnSgey -OJEGVhBz4FBnPgfClMgdmHeHenXU2ym5xWLy8SNHF3fQGDxvovMpD9I1vXlbA7ak -f4d4Mn+IKG0pSertrub3yrOqjSW2mBeRdSQLRk5WabJaK7+Eu37fSFZpQ/FwPx1z -MeuG3swPht8GsdbN91E4St9TDwgq+Oyxx8phYxaoTqsSoT6h7cGPr0TFzQ01uVXI -etoPat0fuV3Yn6oq9QOIiQIcBBABAgAGBQJKdVVQAAoJEB0hyD3EUuD83usP/1oQ -RTqRJ/4Lt/x3nprg48fWHg0cv2yogdHNThLygcxYctVqpkWenLlcRkJV4DxNOGdw -6ooVnmRNlAtO8Y+8gbZn39qjuBMQfgtYIrtfGcumkODMgtiDBizpYVnPXywfV7dV -Z07CiuO28YPPD3rq6rWIAmVEKAcej4e9CeTzYz5OWkDOBfwALC39FVqQ5MKB9PBM -65hi4W5wgenXvpPEj0iiZz51+U85+FDxWPTnMY4Y6G7zFJ67+NJ8j/Qky7+bfqeB -p0pk3htxqQnj6MLlvfzxAPcXcBxGJ8/A4Wp2kNg61MEmzooKPyT9HNRinuzdQ8iU -G40TtPDCfOn1BCCt9eVh94XITg/Et7R44+ioMcuDnioY/l3vfI9al9acRlFqLfCH -0ho/0JqTRhUpvMUbqWvAZiwiTQ8xoNUKxeQS3absj8zkaUi2HzmolU32g5MPzyAM -G8F+EY8q9rBcWyyWj4qLXzjDtKDPNLT6cJAE1Iu8d1XQ4MpHLA/cChyDpa6UU1ys -Rn2bNRMxrrbkQXtnQC3QfW/TV9Cz7XvRYPnBHfe8d3gAd0ELJurQZaC7wTKlOYAJ -uJV8ZgNYO/UaiVaNetpQoQt1K8mV03I4l4/xN2YZ9sr+AngoTZoA46uBWDBgG67C -p6eMLcu8+ZUB/468BXlOFVVm2mT+Ym5Y7mjX1KfSiQIcBBABAgAGBQJKdXd8AAoJ -EPZuPkGfhPTe4IwP/2foWdRm5l7KQAAHLGVq8SaENQFKWyjVCdJY9FcizXqqgKP2 -XQ1cWWrP7l7//Mb7wSyb4S0v3CGisyIdVM5+/i7Owk0C5tRGSGjlfPzw15jYSIxR -XXdclMTo7UDpANfTfPtauCVQ0ImFmc7MeU/DOiA6yePJ8ZXohnL7G5aNVeI+g6Qw -+qzpb+m6YnXyYDA28RkmiKUJslOX57x6bG6iTE9i+vGjPu6D2EkKBrgxtliohz45 -HNMkffV6JKilH0zj4RzOjj4upN4nLA56s/VbmF4cDWqQTwEhQc42AmWcKnpPcOap -oPUUnkQvS8ZYpPBILkpPwF5eOrYAAjikw+E0ufvAOENVNO4O5PVQnx+KG9NRU2Xw -Ni6IKBjAZdeqeKcHiqXaaYSSwxTQX58vccgdAY6RrYYTnLnmrYZnzhhKotJPV1ZY -h8GNWpEMY8wNdOCNxmxXkJopHuLCJWGuEPEXrRgcnVq4omZsD2RyGyniuSdv8Dpz -dIGmaNeVNpmnD4gJxJU8y3cw3d3K24mYO4uEgiAofXfsQxbor87lZgGeCxDQ4dc9 -ltvWNuo9xnzcTG3gqMwETNts1LjbwCe6+R8CHwnUCOgk2G0zk10O5f7bthUHxokg -+N9WZVXUk7ivX4l9pJhOX+kxhKnvhZgwaOU4WTS6iUVgryGgrJCv3L/gZRq+iQIc -BBABAgAGBQJKdx66AAoJEAxwVXtaBlE+nP0P/1k+Pzm0ObBj9JHF7dmCq6uFVK6y -OFycWJYePk9YQjztcAJ1zeZM4+SSZoIu2jI0kQih0NNieDUhBHKsvqBEPBAcUcD+ -+WoCbFKrfvQhPDQ4GYabclaTOT6ucasiouHvWTIB7aGtZ6OIt+aNbWWwszUf3wLa -XhNywV6sroviWkmue4GDeKSbnwRQVuvi8p/r9x1SJLjtRD2Na1gNFA+TsyQLcTHN -9SDSJQ0hU8jQEBQqVqot9Tb9dqcaYwSCd9E1b34X1nFmiHW983FUwCMmzhuACieF -p/9Ns2BKbTaucDFrPu/h3XaLY+LlwpvnDoiS552DZa5lfiVQLRdDpzC8H2gmJeqf -nYVYa20TqQjKZ6AEPoQibCrHFlEgklzNl0L28X7W3n/L1PNeHF3Xbw7IEYU9fGm9 -GdkR8TksLlHbXtspQCjYU/9o9EPce/twy84tLXbwVDs2D07+ShlQOtqeti+GW6mU -AQdoe0D2q4siI3PkTcDzq4z9ZK3De5XNqAmoS7y2vHe63o7abxJNIvCTIQQl08x/ -ed/AetVNxLpwGQmC/J6A9eogYGA6c8l/qVPuUwionQN7H9qyzX51u4YKUh6hSWUt -qTY1NE35uV5iAQUiLPXjkCdZ9PkkPrMcdnjceq+D3t3U93sIilEkOmGSRKzVqJ+j -a1kQIzjsXW05MvkFiQIcBBABAgAGBQJKd9NoAAoJEIcvcCxNbiWollwQAMlL3AMl -K7E7XZ3lHQeEfAXYmVd2c4ChH/wXz9DOgHo0XQbslR70Q0PqnCLdIdFnP+WNuNxx -SG3AL41rSHnG/bNaxHzinbKnJhk9hiJ7h9HM9UMXyj5YMcG4EeQUqMkam6U7k+hj -jN8utIUUyB/dH5A1y/5x3/QdxOOVD9DB3fSrTlF9WFHHPMyMyYMXcqo/ZoHxeA5o -dYpqp8atyHNEKYh4SB9hpKt/hkhn+r7x9DWHKbpbjIuLnEqeipj4E3sX1Oo1iqW2 -pEeBIMeVlZFqCCSmCKihNhWV3857DozTPIjONbFoUuhpMcsOPb6MFROBnkq9un97 -yrd8PT2lk3hDw8WT54NcToAVK1swus9uJJlqm59ZPteoOQ1eUpvaJr2xXZ0Lyhd4 -CRv7+5B/bwCadogXeZIonmT7VHPnTNb+/mp/+RE0GgzjR1VvXQNClAwVeJYN+kPO -pGSyemzE2CcXm6KaA1InAmfAN443rSQWgl+UKGcdEARj7Gor9w5jsaPyKO11H6Gp -6Fd3U60gYsNS4nV3WvXZL8H3OONgAk7OJU5SaqvN1PKeVs1QVpq9wDVnX7BLfz11 -nqKo+3322dPmc7Kb1zNb5lV3MW7ASmFEwHaCFBN01Hv+eE4ePWEc5pqf4jquIREM -6Lj5ysji2Ndl1YsVDjYfrr92dLF8Cn3QDX7giQIcBBABAgAGBQJKguopAAoJEDIk -f7tArR+mVqEP/3RsEDpP94rlLQQ/nVWvdIfXuyKHB08Sa9NXJRZ3IaZ3JOJlo5dO -4iUInGuWpw2KBgi+KJ9ECRD393YZzM00GFgSLXQQVk4fu5AgizMijdok1hpN5949 -slYzcD4r5GmdQFC2i/KKz6F+UNDuP/RQ/473nVrWL6toZNu84Xsg7bVJ4qBJ7x1U -jSLCW+GQTSIF+LbVNBmIUC8YpcNT/g4qQHRrcy3s02haUKC/1vYiEpeK2bClKx7e -ngFz7uiufu81nReILWKHyV18meRaZiyx6x3uvNoVZCwLHXAS12aWI1RJdbp7owBo -GweL8zJ4ow74hEseJDN2Jf/7BkXgFzDgN/T1sTBXWh+CeCfaT2WMaoR09Sq2YAoE -/faWAqAXTrFdaIiMvKmn/LyJqdbxI7sW3yJ4VfB6P54UeUvEvCjkIjwVblzwVyMF -9ErX/D9qL1wX8nU5z+MACCrPAqL4DgM9zj8AD1keW0yfePfdPftW8OG3hzNx5Ka7 -JvTNb1oscAkCkHbxQNPrcNvML8fbZYGzoI6UESx3OmIgIIT8GxA6IfhznMwq9re8 -WJQPaEn1imLK9KapoAfPAI2FgZXtNBYKXxU6oQmFRu3IwbjO2DkU9u8ZGAYN2AIZ -q6szloca8OiSOgCXeNzoExZfPpNiixZvT60lmgCXWK4+F4wjQhfIZoxdiQIcBBAB -AgAGBQJKg62MAAoJEFeTDasLhrBnYxsQAI6hFAydahi0Rj8O6JDSA/vDgFZUzAg0 -kT6g8vrLQi0Q4SW6OnBn/qGOUONTGOlBeTASLOSjoyowsQcyKn1gQxFF+iw3L0Nj -Pi0eCzqccs6bDppG5OkH0mkCazUi1F01uJlA3VM+Exa7jL0PRINdcUL5Jwl9695K -3McgX+edAIevyjJRYijYcJOerXqAgW21PeFDbc/iGSBaRy5fThZt1Tae7FWY7/3C -qwCF16kVL2IZL2JdePTHyLrC6ssJT3wzsSEIgpA9DprXuxyTZE+Uo5Rzx2pCQ6b6 -mMlQk9VxiANiKxZprUCDvZatXjEJrUQr9vqMadMTlwDQQN6RFXyUAYAvxgWHi3oz -I9Nca+9BOTUjRdlAfOXh04yeDT1VmlIKeV4JngWGrc6CIZ/E1dY/mipvGnUJ9/Ws -i37YgUTpVQ2RHojvdQIbBeO4qsjc86mk57+2AS+kyI3vciD31mYctqzoyeA/8pU9 -lzDHMGV4mrbPmZ2zJub5bdrIqFVznfN4OFSP7+QKJyFy+vVQhs6xTYOrCeXVsnEb -z6se33J9D/AX0Dfam8GdS88/a+B06yuqNv/+T6ZTbgZzUr3mYsoV1MApxIQs6I4a -xcrOwQl6QW2QMcxiX/anam8aQJzcRsK2aWSGkPdCID4ljBv7OOG/qR8uJkbTFfkw -UL7NFPDtowN9iQIcBBABAgAGBQJKpDKBAAoJEC/5zVlhJha150sP/2UThDpK2BRM -JPG9ufG+IqGSIAqXrxjRJgBlJpCf44TPHXP+CXeN9YyU0ZHHShNevcCRDihoAo0s -/qEbNj/9RSh/MVuIBVaEUbVOw7nE4/lsrVBXigt0P8YKe5AzjTZztnwr3UmYLOOx -+WMg548evIEifZTm5qqn8Ej56U86F7HBB52ObugmhFUEVOIcZgfVt9nw1CfH3WTk -I+oTjMsj+h/VkufVvuEg9w7DWCMgKnH+HRCOaGeLqw2830msNC2dNOIWsEwllmnA -uhciz/vhN7M4L4OhxWFQF9Gs8/GkVNB3H1Wi8K/9w7hQ0KexZIOlj1oYBlY8ZKv9 -BGLDxr9TCfBJeFthZCki4D9wPxMUJilKsjJCsq3wTX3BgEizS1RPxaiJAY2oGCsz -UKm3qNOIsCedvoEitzQu4PjdaOvXV9wB/5ZKOUg1Oj6Ug3nuB31JuGGQ1GQKTIS9 -RvcxlXFElsCGjREwW0/ptolGVSUd/qT+nyRUmNnVpn0T0DeSCyDxldtipx5tngm9 -79oJBBZ4NvagIFEF0V8eOiEhly2xzBckzpPMUoHOcbQCmpgivFV1h8x7GthOsblY -y5N9aa/4DWkHVEkbyOfZEeSPNsH5GwuP7SFT94qRm0NFtElsJ/F463UkhEtQigY1 -zCcy8mc3SUeJv10OAvxicm40ZBFIqAZSiQIcBBABAgAGBQJKq7kCAAoJEOtw/vPN -/G5PYcsQAJA1iz9YPABZGb7zTDT4eIrE++zZjqYmZpwJvjCRPDqxKgyE15A9JRiS -bdwHW73IqQazzkSr1eSymxcUFZhKcuQ7inssd9DR/eAQ4lhUnUeO4iIc5hJ6Yz3z -MeKAwrRuQH6BqIUH0OL3WYc5hOAonHbEgvZbjsoynVu94yiH6HYi3R54I2GOdtXh -qbNX7ZDhfOIpFK/zlKrgQ+++U9Y2qGlj181XkRge2ItCAgJ22rHgnL3DHCJy/qLA -yL5o5mEe1w1sHLvcW2IKLz9EvHNi4H5YKSGvnyGRIixoeeX3251XPbaz0C9EIH95 -GSUUveDb+ItEBDfYStltMtt77p3FgwE+GiDGEQEuqWKkVQo3OYcYMeN8eNh2z97H -EqX3/EjwBRB0v2iR0ymB2v+WS6J+NM+OOSOmCxzdfOsvFELWjdROlCJCA5dydcie -+91lg+9hegLUnMyaeFKa5Hh1KRVWV8X4YEYHdopoABasZAJL+Qm2CD5V8Ju1cuYk -F/lcu9ugft6VoKqsV4Ru3BaX1OhCT9xKwRa/2oIUE4ETA/pP7e4bdFvgihcxNaIR -IMX/Q86ySMsG1vuEqFKJnZRoEG5LKKbfZt+oI+FSTwHhcMeuAQ+Z2ZsklrdvZp4A -U6R5lzx3VbKreBFpgKExh96v14FaMeZEyfZUfsrwrgCKSFcqOuhyiQIcBBABAgAG -BQJK5dFoAAoJEBgUewc7rSsHqusQAKU35eP2lIB9WFRwE6LhFi2qxBXYngEbr9im -wB0NFOrsruUxaL09TIOSyPl05BnJow3TEPhxgdOC2EULm3AU9uICq4a5JGFnax52 -jINEjJbL+zXXp9vii/JaXu2usqQ1bAAgExn3RAfJC0wS/8gqGrFfVuR/7WP6Z16e -ShA87L7QjFrG8bazmHY94faMhIctEe2tU/oAtnGWN94iCOqNuI0X8NLBi6BsChXc -7k8Spp7hBas8dsLGAdmqZBcG6jgSfQ79Pij1b8fqhpZIT5/iN7Ufdmsf6wE68aFv -P8VGewiEu0z9NfwI8gMXgD+LziA6FCje4FrVukcW10TC/3QMvbrUg32n/eYoCQrC -FyFOsDh+MPpuFQn49LuZtPLJ29l/W4TAHRYY5cNanigCcTzd2hw5GaDeRGVRt918 -cJRYOOhBBLRc17ivNbvegNta5rCQWKwQAe9vh24/lUZyUE5RnKYKgYc4F3qvG0DZ -xB/Dy9tfZtuRWMGOSNe+/kROZ3CTWZCbh4j/cD0f9twAviAtZANYro39XtgZVG+/ -SwNactDJ9fv7G6g0EHhkHtlnSoU5/GX68NIZeuapcD1fY137f/8EbIh1tOsqX7U3 -1wHRM33+7xLc0ycWmmREQomPnTb5XwdrPBq6ydEi5NTojkBgfwc2R57ZXakzC55a -JVCjIahviQIcBBABAgAGBQJLUV0BAAoJECGfa2Cyu/z8busQAKCRBdV/WEPcgfKP -IsVLI1P4L4SEP6iy4YwQa6A6OqHCjYtY1nKq7DYXlAOPNPUk5Fg5x8DcBMy9P56O -y0CDgH5rQHYSsfPu1+u8SZ19nyVN1QDorXPQ2rtu0G9/s9VDzO+0eS4Beh2YTJjl -ZYFK3RsaIP7dncbVTqIUv+x/Uah3L7oyx+bpmPVY7KTOLJCbQMVR5eZ+A9qgRlrr -eNc+51yxr5FcCCrcS9NO6OF8ACJW2H86bVasoV8mWx3NOUClHF856P2XjIhiOC9v -FaSYTWKUCvbfD3Q7xPCkvFG/wWDpM/izbZJil8Hss/V0jA53tElc/Kke9ny9wgS0 -ES+UOkKjvhbgk/1HhlNsfz2HiX8MwDb2h1pFiioW1zj8Tq5ow6Ee6PIf3oirxoiQ -pMSvpmDWs52t1FCRb6TpHOR60ui+ioPk7YI2e9Zw0W5PThMXmaHB/UDW7TEo3tsW -G+diJ+TsLVNnAezpeqUB+QzW8BlUYHQsOfYtXby7rxluoWKymI41V7Y1v/F8zsRn -0sgoDrX5bDReGwe4Knn5j2zkW4YOH1/TDD93ZrUaYx17CzLcjH3CYxdoZ42tTQqB -I+fB9jDqyoWM2yxvEIjcsDB4znnTCUXtNN0xk7TNZp1hZzL5aiVtlPrE+va8TzIJ -MKOEhIl8GdCvcoQ4FBG9dQdHrrrqiQIcBBABAgAGBQJLV5npAAoJEHwgX/EmwTcj -zNMP/15CslpAKkZrcyjeX4aaXqcnt3MWFuZDPhz21P8mIP591mCLao9XFBKMz96R -Fym4j/Ie11fPiItIAmPQO+NJYdVLvAu6Knlt8oRinuARP9C0D3fwdL6wiD2/RAjx -1pvjhT8e32wQAhZwoY5wg7HeR8lz5s/f2DW4l9S1PRc4i5eq0YYHawdBioUWI0sF -mP1o1cHMefrtnG20/UlgFMxRSmOs1faZFtuX4Yb2hkqcoWRCWURMRM6bKlsm/Usi -fOlETrpIryoB50b9TuGbgzfP2CTuafkpJmdQAoHm6P5Uzj2zYoZzm2XWfF+si8dI -eyfS+OtYjRqHSYyFW8bD38fU7sBLSaRXvF35FrETt+6Yto4o9O/Wtl+McCCsbGhM -LQQgzw+7INLjOUVW2/YjieHC2PTyba1e4vkDfiauiQHJGNzMEvt+CULzxv+MvRH2 -l4+d0/9pUl7/1X8Rf2lqJhr+n8+F32xpti/5vcJGrQ1fGFT4t4W1BQzDNlGcwDoO -Mdtx6AFYbbkvmoPfDUNsV1UKiux65j6jGHpW+NBb739eT0I9Q/Oz8a808B4W3elH -iaxxL/SphjMPMSqODxJCEzVUu5ErNT/bpNi+2ILdjf2/kMg0nHnH59mgzmc0tfcu -Uxuio/MIf8m8e1Dsr8bynn8ToOj0A3edMA7PHGj0NESa33hgiQIcBBABAgAGBQJL -V+tvAAoJEPI3Izb+w5dFsB4QAO8Nln3vz5El9hxQCsqTinrau6DeFNZTkak6Tmbx -MkIEnSjqar1wjTMD97FkIthc167lkbk8D4n6+o4aE7/jG2tGbrfsYL5uYORHFCTx -9H202QWngZMXQiKC0NJpiqTIi+hGZDp/669v0jQQi0ODi1IP3zGParGfARxX9J28 -PHrdh1eAQ/xFLk39k75ZBZJMxGRZ/fM6KZrsGpzI9UWdKg0nqV/IFuXbV1sTTO1h -WcSy13YUxSkEHMoYYKv8EnF71lnQ/A0s2ip/tSzzvXcLY0378MLreI0ZBgfqqx6c -M31R6X9ZyzIkF6rp6+9+wACV+CfpMcawdHwKbI65QgQ3p5GsbryPr6k7PjtLkphN -EQNsCC+RQVPqaSvfe1W7qpIvfokbz8MqfOJRVIqNDtui/bb2FLqUbuWtunYovlhM -G08JPUxff9VwVOPfm3+ZnXEHcJbHVXHCrpakpDwV3d6P0QTssOC42VMZvLhN5mV3 -jJ+bmHlAf7yyaYJeWGX3zuQXtG/1vBg1/VeavTMXJwZAfQPzT8iBt+BKN3uymiXl -ns4ebtgSQBnVXVHjfzQT8k1D0IkOOU2WHhsSAKgW+HUj2zBQSW0ZsTKKGpC14ck4 -QW9Y6bLdkjtOuQ1azID6qcf3+LYcPQiXgF32sBUTZKV6d7pTko+T8GO9T8BtR3l9 -dQTFiQIcBBABAgAGBQJLZeE/AAoJEIoRG1yuQmlEMDwP/jxMgwpGEZnF5Bs/JEDA -TDCXAYkayqVX5nTv22J5Rk4C96rtau/1gBSVeHKbH8EhcDdJpVxOOjfhC92ToWJh -0t6wiMZSrzCeQklBWkPzeGmjPd9Xvxjga57xeTbPFkrwW0Q45qcuqCln/SSvuUKz -mQfWv1h+vI4Z0jit0lyj85HaLL/r5rs+aj88pPS0YxRQn+HgdR2A4OGpCH9inxB0 -zZFb6xN+VtFzzcGvFwf4c92GXxW9PpcXH/+JrCTnD4o6pQIwY8gIQL+pJXu5hAfd -Xe40JfM6rzqlXvbtzX2k2NkEHOOnZO3EzLCSiKEKXu/FdhK5aRTRbJsXYmlbkcHm -LAQPiu0BnWLJh0jOF4XTedKxn51tmN8Ixj7VZf6BRbUutwsJeML6jORUdW6/RHUZ -45vWWswIKNc/nXp8stQYPyIhJnX7w356dQVNP3yJF8BXve9w4iFnTV4eXZmZtHRJ -kdQbuv4zvztKRR6PWl27Zs716E8LIEAZBhJ2Ua41wPpn0sXLS1cjVsNKjA/WSK7K -XEZGteGt68EaXEQ2bx+/Wj0wrd5k51NjtwojQZktJUIxf72UNhvtNmgrR5jux+9H -dcrB1P5AZffwmlD4qCotyZpUgA9FiI/Xu7Y1s2yGzDlUmi7Tvw7C5NHvvTAddfTt -hiIJbx/i2PDfiN1niuFi0VAxiQIcBBABAgAGBQJLbpNwAAoJEDH7Hm4SWfKPz9AP -/0P2BT8jt2gy1kfblYQvJ9LJjJi2nU6a+zaeUpi8BS4XL5jAyOyfBjUx0IBFfvQD -ppvvr6ggcont/yDDHA4iVSRxqCQTRL15I9CTMucIwnY9iLd9jWeOGCuOJtNubKXR -tiygC+ADXer4mB6/98aZEIOE2CqWySZwICygrYOdZ1wxwZzubM0cEs5SlZY0i09B -tXvcZ/4u16xXc+3BEX8JyxuEUFDBp4dyW/PsMW7kKIAR/RJp81nHK0udYKBUL18U -UGNpRaiORDRqYgZ2/VS5vafLoOi4jEtitZ56N60ShvowqIjkr1TcgxpDFqsxmUxD -dmwcntsiktAGAcntSd1LGXwUE1rOXubW/SkpZvBrg2JHc4dvua1M5HeCMrQ0tPQR -zgzyYhXj8T/Piu56E9CHONdAoLUcURQI8gIYG3N99EUjnBVgLptYLkbXFCav7lL5 -D+lclfGZK5+4v4DTqksiSX/PuV7MwV+UeS5MTC+4bW8KxsVq069Usa0ZyiNsbEbu -uLPRLkr+T6IilX5pMc1bWEmUXyZ/onDMMhrF3rlwRMImVbNriuExEtEuUO0w7AUN -dV79DHeRF8IHJT5eE4F8Pq5K6DNkGzsqd6qHBbXC/9Bo7jDCbnI7lBiRwRpNmZeo -I12FOljEuXULpZx+jnQYQPbj6JUq5ldzgFLfT/d+5fnIiQIcBBABAgAGBQJLe1AP -AAoJECitMrIYzLj+TmQP/1KdrXmkTrCT+ft9NvbkFl3WrdLF3+4FMhzGSJntRgmU -onFvraTHePlMVr53bZmeAf4RDUp+viRyYeHTySMXWtXY5yH1VIxR3tmEV2vSSxB0 -aSRC4knP6v5wuYbSH8iKm8zc+Jsaz1ZOz3b4AsypV/dg7p8SRVrJbeHgXDoZyX0G -fLLXyGMRvlBm1+U+PIvkcj2GoBdQkg36qhV2tFsme4cKoFhUNhi9IQdf4Pl+DJ1X -4zZUro7z1pgQbpGbFcRqF5tVjK1CR90WEy7rdtBOli1v9nM6qY8di10FK6pPw5QO -Jut5/EjtMO9E3LWL1O8Qh5mcK90L64miQrBRRUHlUj3MAiR+ZsMNR5X1cBcQGMPU -9qHL7ni75XRsIAZfcTPgbHF+4Eh1T3X916ZCf+hyIRX/3Yrq5GGEdiA2Il9rw7le -OtesLQNmUsfNj0i2ct7NosfhAcUO8PtauLAO2eXChlRwa5ufrzFm60Vxp4PWOnNf -+RlWa16HQJnlmFoJ31j50nGMLG9awH3pHx9sIxVwtGijeTzK5z2jNqLyPqsI5NkI -uFV8IWLk4e4Ad7/SdJoQ/qmjsyArE89VA+CIX2Dg6vA4AV78kHxBQfZB//5JoDCs -6phm5gfh/X3WyYYENjRHFKRjcrpr8D+UHiWIIIF7bUFU/ydiXgf6AGC0iEtBAYR2 -iQIcBBABAgAGBQJMaupWAAoJEDOWFYjhwhhFmC8P/3XHweTqkZ9kWdv/+PAaSt69 -MRRu1cZ7Ew3cBMtenH4KstSfdUAh3gYZ7mf/AwrkNA+MrHsRgHNFWzLMB5QvJZy5 -3MFiLj5yd4CsgB2reJZr4ssHJE9Fzv2J/ykBqgK6NnAty8MqWXNE1qAQV/BBEE4q -VioOZRJ/pLEE165j8uW9r1IMD8HHaTbRBP04M7HI2ITV6+iiR7GfOjg41/W26qoL -LkRSE1GDOMmISMBLMQhYEli2Vc8ViMAGkqL4IRgRJ+5iOhsSs20v9OUBdeQ5Os+L -g6xiYEPXhA+gX8sHtI4RlHmQbmbWf/bfKICgG7JrS5SN81jDTUJjqjsZb4oKg4KO -oAWMAkkcORhXE1fwDQnX8N49CoQZq6E3AOzNKFhVQpjqm4hrSgmqJKlESdN3wrgp -k7Uoq8uwTYOF/HqsMHE7q8IjJNC+n8Hb7+HT13WdaWqRsL+K42b79L7clUJGe8St -bY1dyXVEJc14HNTjzjDE73rZZ5vfWspLD9e/B+vczUBLclm9QpQv+OrjZRsJtOGu -Qxc665iHl8Mrs7DRuSe5fY+Z47yXPXQH+3jVuCkdueV0E+4B+pcysSWOSlMaADB2 -aguIaZnl3UIRors2LCLnZ/YK6sY03rnA+UhOjfo1fiZSQMgisZazH4rpVNO5fuPO -t1Zq8iTagFtla6SdBhsliQIcBBABAgAGBQJN1mGeAAoJEIN77/kM2/kc6ZYP/1yc -eR7SnYV3lazAcpyO/yvEvLPAmHIjXXYSRevzp9X4osCXekUfU/tRlMSoAM0s65+2 -DfcVdygJlW9XHnm5XL10psqSrf758+95oXoj1f5rS8/IlZNhPvdf8SQePWhMBFJu -Nix13q7TCkNpdWwuiqKvg77XqUk0mb/M2MATgYCoadCxyMOlgpNOQA6ViJ8dqEyJ -hYjXS85CyPJhz8OjsBQ2fPoX/RQqglHf3tbaKW8W9sG5FFGaAODfnRVgWyM1WjSJ -UHE/VPufqrDClXbj0WwOdTz/D1hysIBPV2ep2+toeRVBbNcOuMWfRQCWay2uaN8Q -W/wMAP8queTqdlKaiV/OANsGa1NpWXc+3Pigb6JSc2RSYzgDCiOoc5xcM78KGnr6 -ey8dzWfs5sipbEPSN9QPJdqH/rhADv7WC88/er6b6QXbEa/57SQtu1FVqsY7JOHD -xLEIzvImthqgTnYhxr0PeTXDh6jhuGW9GrU9CbUczKZWV9t1i3uKI7FHdOcwHi1C -iPYTs6+Ilo8+/upMyMTrWKDVUHVkeaXoqrNb3eTqo0gaKP/UCjzYmHScxAj6GQig -fdy/O9Ivz1A8aC/w0/2nU/fM3z1NuQbMm8WktBA7bFXxsUSidNaMH1NqqilRQsNZ -NmVqZWBErflrOyQTeY1k5jGunkOyastXfOVqXqcEiQIcBBABAgAGBQJN1mSCAAoJ -ELXjmZ++7mdkH3UP/iCpWZDxPuXgyhvDekWazWjrLmDaDm85xmmJQqJxIC+TUTcp -czrF2Z4gnlV3VRMGJgKp5PnZo/jDiAdnsadH6PHIGMyd53o7eZ27D0qXH9pgQD95 -1w9/SU7GJy0L3sx9z9PgUtce7VhHytHgHlrnD6MJizEeXlKI3l2jhWfG7QkEw+Ka -4NShZRTNylP2OaO58odB1WZYXsGWTvTf+T31mDA7X3urBRihbFKvy9OzZKuzDF2z -9SmGbNy47iRJ+Cz1zljkx9WpQRCUUJxQdH9jTPnreaPPWRxyjNVy+SAzKrSenxLB -j2wSDMGIsHsBpQ13Hbr4nBznssG/Cz6OGLk1IVh8fYvVjpo3UWLB3SU32x2FklOK -Y+BtZIKM0XPQM5TqISVQIu8ZczTd51tdKeIvdaR5XjMbrbuZ1KRMMGEULKsEMQen -zFRmJJMf0Re5Gt8a1n/tr0zzh/wr5OEz9BhRWcCRHIdPvu9xuIhttcM0JxHsZVUW -jhFJzpCk9uLGhzrE+mryX3oXl/2o/51M12FSN6y7ElgZXZhoSEO3FH39vDtGb3PD -hdEgZYrkqcNdvGZA03EaZxcR1KwfNE6AjxX1pCXxqo228pcBkNJrV0mmTjni8YZB -ESzujm7tNofeQVfyh7yHVkBLMggnE/WzKz8/WX5gq2oE+CLah/0h9UdYIKMoiQIc -BBABCAAGBQJKcb37AAoJEPGmm+QpwP/uM78P/jaUSNj4/X9mYFygCROzfDejkB5M -RSafqvjWcSi3DRMzQ+y9Q/uv9eZ0+MXI2dDaMI0ra3PSJ8vJCOmoJeWoJXnefeo9 -zB5LjG/uMbd1SEXsAADnego7nv8aB5WRlCql8fyNbGWal13zZKAtvu1899G3Y90f -02lpws2xS0NbZYSD6xFnijcwj253hBSSFNWxV9mvIrinYoBvL+ioM+vdYE6WgpeK -LlrKLovI3fScZ0g99qQVfQ/6P0UZWkawFV7OJzk7FTXOD0IhvrLlx3Mg/XvYiZDD -ypQMB6UEqMXibbMFoaZcQJv0T7nycLsn7Am/m6hIaJ9GiLH+YFkgqgsAzl8fFONl -x96Qj8mtruBb9SLIjnC0nQzqb3bo/wodsC82hdwTcXkgdrkucBig8aozxGl83Kyh -1+Kb0DZArhNTe8wuGZ/d318t9aaGdSe35rei2/QdzFdNDCnx2K7u9o5Wwn9t5UIi -nDG7qh3a1EQpfghBB0LKAO7kdQSwr9j8c1JY/wmAxdiRO7bcTuJo2LSmwL7/SbbE -H80CFYDc5f6DgZv3TCb0yzBNv00eqEGhHadzLZIH6Bh0YsKvy4vH39K0c3rIgDBX -MO7hkbM/bStz/Wb9/jSYXG3GaAm0WT7RTXfG7dVoPutnbGZAno/Ym0U0uh2MYgez -HJvCGcIg8mHhUmMkiQIcBBABCAAGBQJKcwnqAAoJED2QirPw+/UfdoIQAKn4hD2G -TCR8eynwrBTQDfppTrgN4NeyriX8ZZtk2duehm6TocfJMQX2mo4dT/s5nKDrwO3u -mUBg6OsWni0Ipw2s0sl/KmT4dWWI9mSqjiMCcwXfyFQYsCJBjhxz4YbjzCx5Hm1r -E+8joEwECQP8IgR+kkP+Xkr534+z/5FagSYnQO+VwuO9sYgZXnAa1qs6GocScNLS -UOyjgdwrbFu31Bgka3FjOkrkVbt0l4t7EXpTIilLEMLnjvox4y/MffVLABzTn26k -PTlCUISLux5nL8OtjiUJR55Mxi9tXyA4kCT++RoLQCLJsTXEVPUZFZ0XZ3sWV5be -G+u3Osg7Zfvie8BHRqh8E6OAsRZw509r+kVFcgbsQqcBwwIbGnxnoI2mRTTOeizc -9dsw7WJhk6/3Gm/KW+v46ANC97mjPEgFUtMzlLbTV1wNPYDa9suqlRsQ7Etq/n/k -cc1uWX+vaezoWuRSVWYx3vJdOhZvpEIJwamQBGpvcUrUx6KyjVpZCWCTD0qwnTwR -wsPNVyC5IKtSYZzl7m40bz8KWltq7VjrMYzOmaJGz9Xl1a3lqvRNTW7IZcrc/gls -ziwGOg7Lneo/tz61tX5P58lOxJl23jT/RLqOhUln6Xu3ZdBGHulWboOPjp5NP8w/ -jsOaRrxq6G46tc8PTR3YzXVGIODwaiRZIYIqiQIcBBABCAAGBQJKdEJsAAoJEOYZ -BF3yrHKa5ywP/06FPhWmTq/eMTCDiX73oJLgH40Ust1CR5SFuJILF7dXNklvwCeW -MQeTvOifxw804F/4teeuw3hp7fOjwmWIl/EYi+8J/XvViXeysgXShoPMvJ+z/IfQ -/1G3GFn65r0mLJXb+7O5kQOQcJBaUexVaYhJBV2XZrdPqwGxC301bBQriRMjQhNI -ATrv8xrRIyc2w3XtudONO75OJdGpfvuroLIz4XbVBxvXRVovhwQWGyFaTis1HieE -4zKt1AEgoidEMNy1wMZYWtYqi+c3TKmsRAb5GvW9Nde9zsdhrgTpcPo99Rl+79hr -iFj+yHOJR0hP36Y+n7VTT6pmhgssfUZhDZSfGa412mNZyRrpQbTIiEp3hitTKu5R -M6k7/+P2vW9fLGpXHqrDMtrSnWNH8QDlmOMX2+cAebbFLMC+4jdDgVO0oPwgsOQ9 -qsKXDAedGPT2XxXMIIs4h0PtYiwEiPZga8sDePjD7/248qUbSbZ5sKPGB60nAefc -hDFI+7TbOieuoRH2pZn8VBXXwBql5AcT7a1ZKTFjji2f+1hiIZ57CmHl5JUEp0Gs -H4UPH7TQYzAi+ELXERqECkqtgni4djsDqkjmNAYIZ6R4aEnkeJFoZHkHaU8unUoP -o6lW/tvwAh8yQWlIppEfpPrj6uDWQ6eUowr80zwQsYfb6llEdoZBlInsiQIcBBAB -CAAGBQJKdHfNAAoJEN/3OMLRbPuiiJ4P/iDNJRh4YkIPRslCxqIiZGe/IkFEX9/x -5WKqrIy6Aw0+JZjkY7f68c9QEItHc9okCqPb70F575dSMifJWOg0W3YSKmNsP3K+ -aQSKpc4bli66W1eE1bGvta/yy8yiOfOuOok5+NY28LVnHic7y0rRX3TyI4EXYr35 -pYoLEtxrfgUcRH6a2dXZnYoMJrjtS+KQQVHp2yCF0WMw1bpNoUmQRoarg5Xvn6Ab -nDToE8IcA/P8U43Ua+irOXv1Pdv6ldLjQgBtChLxkD7LzDv+agbeevmcnDisuniF -wwxLkxw2o2Bi6fLopLIxXl+8bGsnlBMHZY+Lsa50aV8XzZ+vTl9dWjm7YsTOK4pH -Y2/h9dWOjgeIw/N/NB1B9KHdZODOpkUfRSdCfAVonnRlqParWiGGVSbfasNTS0fM -Z9BKSZrh6icEFWDF61dZ2CCBIDHZdoZycyYhsqEdx9fUuHaSu2WSiTfSnoOuzPLR -1duMtFv+oT0zyVOMsE1WRcmVXqSs2hkFJGGyBzTX2v4s4khf868QMAXTYWe9GmMS -LB0AvgNSiqSjs2+FeivjmQm36QjuNpedyn5mFprhuflemjkBTDTJa9GSR+oZ7EEF -N5EV38KI1LsYKPK/Ftrw/2isGFJvPLgnQBvSBclRCGltwQXdQsFdb3htNwFUdnog -gekUHgkc1RJfiQIcBBABCAAGBQJKdHgNAAoJELqceAYd3YybDR0P/iefgD0h3+Uw -SlRtblkYwb54R2krvtsSCX2u5Ns0dAVBQmTUOjSaw6+9L49+9GqljW/pV+L95Yzt -g7s7rv93QFu/yLx87if0v080KlVRCh03W9mVfMRRP1kxVwpgvybqfjJA2PAjQU/8 -GIgCMrGybbiOYq+3fG0pv8dyWJanPaC3jpCz4ifdQXitH69gPmqTfPnNQY++vihm -0RGDIeO8AC/Ea2gxsgj4LCV7zl/DvwaG0WG4283kKpSkAawhhZ8dQMwAXaEAqAAg -Y5LxoapvNrRx6xtmG+NJlvWwHE024bmuGBs3NSWFoI2q4GpjWlxGsmptuRM+VDOv -H9G7yILwuGrCtmfCg85HCxsK267RMhFoju5MICgMp6BcxbL6lufwttm12tRECTkg -JVsBltw4e0yTLY0HDOCEhxLVP4kK2KClNoLuKI+5H6Njcysn2ntGq2upkMNMyhIX -ANvCtsaSlf7k/mcenhPBd+EZkwJ+r7W5yG4Ng8ZWqmuO38hWpQd9FSmsPMf64CTm -daxRlhjf2B+LNpVmc7VeRbwKMNai+n0ejkdp4kMnBzppz0aXgWKE57rQqLIML+TH -6ORCIR1xvUNhn58FWWaOpxG+cpJNr3aTVA21pV7A6LVXdyshIPTzK+JxdD5ERU4e -Hmx0L9QB8/+P7y/0tKjrsT8YGI9uMa3yiQIcBBABCAAGBQJKdIqqAAoJEKLT589S -E0a0ZMUP/0U7qlaCC7kDpg60/9UyUb937Fv5fhMEvcC6m7J3KHXTQLUmvNKYdxDO -tuEkPS9NwMmHDwsAWC7SdLyQoXyVaKva6w1d+dUj37I1+4TmXrLpY7q+yjb8Xcw4 -37dC4YVZHP5aMqw2TJD9TJ0hFGMiIW5FheDXe5L7Sa+igsq3pQeqdoZMDuHwaU9S -4ibotiFdeXnM3q/L+ki9cl1lnqzvlzfuqWuW0jhf3nKLjI2B3Oyeu78ivBTirgW8 -iNohch4juYndnIItzBCiyum+G6PYjPFb6Fu2pd+m8EqYtBZbqyTCBpYfDCf6l+F8 -QAICsw2AImb/d96qkFxjlLIQCpKwWPUsXDjeroD3JiAMz36wZdMvlcT9oZac+OqM -1iY7kHfxwt7z7iu8Ox2gu3W9WiYHCVPz9El/i4HApIfvMEUqRoGblqFqQmKb6jvn -NQjLonAz0IcTPpaAiAQVBFKk4Cf0epk8jDj0nuG7G7RtXArvp6/4Rkofl/jQld9w -8x4FdZjtpG9OVQhG/o+GcS4VEwrb2bx8HlHtAtWvkE5lqCmJpj4VXo+OZhP2fNRS -Gv7AN5KzSStMoaUj6XbAlpo71KdkNezqpD5QUYIPCPThZMBBIXMCgVrs8Du5jdwP -k5DDP0g7v5k7v7yTYlHO2ZlLf1bvo84E9Vhh80+qdkz3U30VKhzViQIcBBABCAAG -BQJKdJzeAAoJEDO+GgqMLtj/RpkP/2g3Qjl5Wi2R4QfUftexzDZ6BzvbZkc1smiS -KTTI2f30dRrKrp31LNwc5gbhUje5yZZceS+ExC+x8oMePCqr/g3QN27aeafzeq26 -xMsLCcz7NTaZTaYbDSg8RqSg2JFwY3QcJjPJTulqolj4I0QRrAU9qXr92PS3IXc/ -4rQwJaUaVD5JLHHESpdnp9kpE8rr2IaU55tvuZhaV8zy5Dc+cOj85R4a6930rnl/ -055mPaN7a2NQTyeyzHjjgEqtw7WsVcjuHtzMVmk392gslLIYipxG/IRRy/SqfuRE -b0+3ob1/XoK79WZzyyPIx5Dxoha1Lf0CpVWVGNwB55FeBSiZn02KSiIARAM23Zo0 -tRHY7SKb0fgYqQFTIz3y/N4QXqiShdI0dGYF9p1P2tsZQK6tPmzx/mzTcDWXlrlI -rivJru03dvUXm9jAQZZytXSnydyC3zA2s1zroRxToBgxy1aXz96hW02Vnvsh8nWx -zaQ37B7UiyMaDKY5VEkgClMaKu79GZft8KtrTGRpdq3Sd8ix63aBMF65DVIQCKg5 -2uiDpYOzJ7Q30jXk/if5QMkArSONbcx3+0fruYTzhsDXyGuqoWUjgthOtv3LY4RW -q25siCQi1lrtr9eSystzmY+FNpjGc7WDyruxmF9Ht2j9Sd2tTDBaI2lmxG3H68eS -WuBuePoNiQIcBBABCAAGBQJKdw8lAAoJELkRIFNthRKNtooP/1ox0R5m/U6SlU+F -fxVDOInivqwty1134viCqK3LXwcqguDq+rMxadv9W4AMfZkqbhjBknTudKipsVsQ -ubpxEMusSxiAkvig0DwpTehrJvAMCULRt3GnzRd7Q1xF/eXalN4Yon47nuYQ4nkt -0rF5KqKzhAebi23Q0dlzdx99127qYL4bM4kwZ8JYiw6UdxStmJKBTEo/FZUz+v/2 -sRphzZS1auK5EFbqdwXcvbQKlZ7ErhubwgwWerdGBWqFsyG085TQZzhJ9Vi0/yan -VPvWUTvlOrp6IqGFVcj/jtdqAwukr2jv3yaLBULshH2CEc2c5bgRTh1hzP6fi2TZ -g71eGhBT+Urzyi4uK4L8NVLoISTXeSGXyKPJwa0WeNaLPqTOAS3ZGAmR0uV+mAQD -stuHEw2iW9c5WohiY2aTIjm8vuO22qbO+Z98Z66fQCx1tOw2kGMZF3jAcQFohLwv -zfypR2wxCy1D3eaisPz6xwU3acE+4a9qEJSvrSaV4DRw4jYWEW8a06PBzp92PHra -Ta8L/wrXRZQ2YtokGRYQXTqFmOoR8y9juitQ2IuFCupHN3dthH7nuy4pHKdYT6Aw -p+SSq2evqZbihsruxJzVxn4OH9U+1UhLOYI4veq6kqADdYSf8r3PZA6O+3GnJV4X -ilj7+2oh8kILiLvaO2F0OXx9TyToiQIcBBABCAAGBQJKd01iAAoJECx8MUbBoAEh -jVQP/3KfQhyseJgi5q4sn2msivjgJowlxYcrB4OmYQo08mak15TanENc22XekxxT -wvL5SLuK08Ms/dFYqwirJWOhTZxz7KpdhajxJoYSDY54pNWyxttk3UxlutCjUalD -+lyfFmIfCNiyyzytk6MMvPygKrPkurcc0BVs521Dk2jfTYWjkgebyAgEFe63Khll -WrBqtS1jmOkdlgR5UTh3fk6I8bTxMq+mU/EpLWdHbTgsvc2/8Iryfsk2TNsk1Q58 -JS5hLl3z06/3HbflTBac6d6YQ04HzBV6nIvcNqWjEw7d6DWJ6lG+Ior6/+R63UCq -TxSxtv7FvmUfbudbC2JQU8qDHIl7Ywy/aezyygygQ7+ry49tOiyvgBUE6i372AAi -AcLeO6KPvRhz3EPEUpw/vGvu6vKj0VSZPQt5w5bW+2moPgINTxCpDTf6jeFxYRqV -EGiWl2vfFFutNx+wHjb9qkCc2WLS6zJnto8EZM8doIQZqEBBjkrLoAAhXRB7G4qS -CYgzGpryHU3oixYVATNdJmrYddyN4zbckUVXDu9qoOm4cIcZmfLcWvQPYL+ydIJa -cAnvQHx/t0iOLwDlaMkDqsonFopSouFroKAOB03QqBgOqeYdF1FTE7mpLe0RSeE+ -YKG0aNQlpOq4QttTiYv5CeDIKjbVLpiLmRCl3zy/lnJ95E8UiQIcBBABCAAGBQJK -d2DKAAoJEAxwVXtaBlE+pw8P/0RsGBA3bakYCXovlh34fsA/U7Nm5eIY9LIDPeMz -61ygs4N5UzSIwcwkR8/CGtpE7DZAtJUAVtYFelfroKu5Xgwt+8ov6iPsVAyGblZc -UrJYILXfFLE5d8+W2GZrbaiVIoaAUk3eObwNo8D+3etJ5GS00ZJpISjp9SmXHpqu -+88ZQdRUUiGOVC4YlGsqWN2hULCPcL6zjkpWkuotbWDpupr8m9TBS0p9MZEeP1qz -kzeyCg8jKMOuMGv3wHWA/0bIdlva+Km2p5GSeRkV3fPESunxn7dqzFrtdWJ5d1L6 -A0tLMeG7j6xvZw3xxp4Q1tDR3ZnF4ri1BtsJ6QJcPk6PdPMkRk8bhcVo/ulGcdjr -iPJQhdM1GYMZtcxDdW+k2QS2Y9VhCKQBmsSakbow+zpm2V2yEopD1In1uPCxIaKF -4P4hcrP1JN9iPckINH1zEGzWdF85dooA00zS1PvyDVwghBpkPf17/DVlQm9dW9st -xGyubAleaSm7Mqj6c+hWaZQOP++J9hGS+hZazBcOOE1CcaUDc3H69SzOqhSLRE5r -oEWN+AhxwbZQubJK1jQNgNDE0NHI/vSr2NfXXVc47/rLCjJIc8Sw/yHGZiddxRsm -AN8HtUj2fi2uOXfvtK6JJZiy5XapcZOEg8Rv5YqHuvzEVy7M3cZ9EB3Baks4BJzb -/rSziQIcBBABCAAGBQJKeZC/AAoJEIcvcCxNbiWoykoP/RWWb4CUnvIFsCtWK9Pj -ufn5+v3LvqpQt7wsL6e93eXz9M8QUOgTzb+gig6869NI7XMbJaOBt1FXuoZAdyQh -lQwYfiZ1x8CeUk9hUGfJrTxlTJY/qissL9ozNefb17n2z4m8EwZ1ClZ/8a22MlsM -cNRFmUV5cyRC7WXXNbh5fadTuez71VCqkuUQRh4segYdcNZHG49UwFc0nQLW7fJV -punsaudhVdyISbd+6X/W2tXXQ+vfoQ3U41rImwqpg4X1fWhUuN0t7V3UWOR6JaEN -4oHORy1JyTQNSkg+beoLPxzd6QUL3g29+T7PeXHpe2B54mP9rjHDa7mlM/IWjhUV -BMgT/HsIKoht0TdSUX8jAejAUvzeufTvPOES5j2l1tQRuRdbRPDJjk6IQ3T+1dBq -sPHNuhSbR9nFD677QotXqMs5BzcTeQj0zPDOqwX9RG+OxA8CAZ4rZLRRUSuk132H -8OY+9iQ6LlNbHsc62BEvVnGT+NjnrGhi4jNrv4fVTqGqpz7hU/BsgJGzrc6Fs7d/ -JHP7ncXDnXWCaf1BAsScwjXGFF1/aV4g1Mjn0B1vUWkjMMbTV/dbjIgPh0YdTI+8 -EZm/WcoJRLHXg3faWSBW5BKWXMB0to7EAYiXjGoKPN9dVm4/Ty0/SEvLruh99dpY -k5+Fb7S0oXS9vYG/exF00HpbiQIcBBABCAAGBQJKfDOOAAoJELs6aAGGSaoGNNoP -/1TyKz+zKsLheN95KhnMUJoup1YzC4UIgnel1E8GySh7D1mQfsdWJjbrmWxoCzF9 -bGN9htCTJNOO5QbzBUJciHCum2Vvu+2w/U9F3j+x0KC+D0vVATaPwhWGu7ONpckm -NMmwQBBtERtn20aWxYkX2tw8Tpq8j1tv/qBbkEoaBQfZQ1LQmOELJfmqLQteV2OM -8zGFM/xUE2YtJPvGN+ITMUYdNl6myzewbDKaUoPMdHlJjbI1xGHRtPmPKHyJkoMH -N/4v3wdwqrZ8YZYBz0mM7dc7WtIi2BGEJC/ZkO1u4ldlADxM5Nay63ANwLNWQuWF -XEn42o4trh8n0SAiU53uje5Yj0x/kkfKZsXWQrxbsdapalHSNkcB9Rxhk3TFcqa0 -d/UNttCRfCSrXntNzWM3tyCfKjD2tLsR3ySJie6aH5NOji3wPcC4rteRt7uTJN+/ -Do3rbQ9HNrGC0wDJVnwyefvWHiCk1FXELoSMv1xEGIDm7C3rdvSxNmYFq02XjxMh -qaKaifof8yp7m2s7zX7j57G1Hu0zz2+qtYzq9Tasy1D9aai8Q32AL6tBpYbdtuzP -GvFjwRqL+/yxN+LJf5d9ilngTUgyzotVyybQW7cWcvA3fQby5HUxcIeei3q+e3xK -xDIpHWrrMEocJbRQnW0nOHA2QNZg1Nj+QH9k5T43Qc/+iQIcBBABCAAGBQJKfTTg -AAoJEEnDv4knVT0uCS8P/jdN4VIZafYeAZdlveKj69Meik7SXOwSbSTuUO2WZa2l -vsLKVf4hpnrmxqRf7VqRkU2tD15FXMSfcS4/3pYn+gyCWY+ZJJjOTCWlLVkcMvAu -EZnZJtcxpQKALYk2gSTiBKjCBrNicupxKCXdGTsu381rqvaDsC0mL/oxpGKZrJ81 -HtO8TsgiPmOlsJqeD79u3/ut5b9yBXTzxwufbjVs3H5WQYbFaME7oQ18dgHIL6cV -Qn3uldj5dvtyAb+LVQjo693wXCseQois9m+iMD68h+1ntckyHGoWGkQN58tUAhys -WQdvjU54lI98xTL977nULzw9qI8qzNFtXIJLkcvt2rEVwkLIGK4xoYwkboGCIJYF -zVr0ALnsgNMsu286f59JEOlP1JBdAMZmWod1L+5bto8cYVEbXcoxCizia4C09gyT -EpdBsWpk5XlmXubk8kCbWMOAj3AipyTSPC2QUjNvmRyo/w+p0u/IFAf951YYGgwB -Dri74BaLJi8ngm+hr9u2CuDvlBx+6axfeTsn5ZYpXKcGqwqHkAYZLyct4kyC1OPZ -tu2T0OzCT/ex7AbA61qGLBQNHkkuzbpkAjWbiHG/mCIn0BdojrCHyZSBXAtg53rl -si4LsN0tkhV2UosdwbPjDYum6HA9+u/F8wGdOrxz0WyiWaBCMU82ME0Km6vldNSM -iQIcBBABCAAGBQJK8YONAAoJEE0knZsj5vw62J8P/11o3yhk8PuXauEDeDGb7ukv -s+Bift4PGy9ijiL6ZzIfKGaEG6wAvofFCshXB9M+ldPxfPYSpESOVen6tx2hc+lG -I2nZPq2fQ2Z+cg1tbqOV5VjaVa0I6wB7ZCKTUfkn91nPNE/V/HbqQYms82D56fIg -sfHVF2EhmlE4uctw3Wh4n1ynGzotwdmLDjC2T88P9a6yWlQF9kbAWRWY84cp5PCK -pN0AiEhKhW4eLJ7W6077D5ft+k3LDKxCNzzQDfUP/FjgEaWkdFZSunciGw9hjeR9 -+w+VQ8GZOhoNy9ne5XRZJphjOyVpa9xkUhhQUWEGruRfZexD73ySM+VSXhaRERoZ -EOK5QIb6G0aBiE9KP9eVfvFmYFM7fCE7qklNZR8ouK0d41xFpeaFKA8WVVM5mvMt -xeMCWxTM5GBMp2de81/u+o5PSOPLZPp1+awbmlxgKpiZMx9IFvB92lSb9EXYcHkZ -i0hGI/L+LzUVAJtOcWzdY/QR1x1UaiBcTE/fHTqOrHtVCOW6Ij5k4nyktrqcMHle -eDcjq0aMqXXxGgu8i/C0my6BksC5GsTytz19jpyqMIXsU7gk2yprze2pZOLHHPle -v3e8XE5jTxE1aoGyKAqOEc8VaTLDRSMzPxQ+wzHKWVrUHC+Q7/CKZeeoiiLB50AL -wWg0zs1bxUIanbY/UBAEiQIcBBABCAAGBQJLVsi1AAoJEDlmokvsTXnnQwYP/0II -SS7frL7MI1dCvy6bbQVcAJNXG0eb2jhrBGGEAKc1zQaGbz3pHPj5ECO1aDDLQZJG -YgC8qUyirLJx2z53lWKpaJlEcDW66R8gxOHCoQQUMPLAFEmq0tGlla9Z+uPHmHEt -bldhMIvly1QXceQOP4BL4Plu9fiaHVHUTK4bQxg/esXrubOPjSMXWi2ttDrAQAOR -DmziM8yThTBRnplwhvBkW9fC1PIBX4FDXOQl0ZEbwFXQiaFf40e8VDpdi35LaLVp -Fv0zqNsxUcpawsfAWzFp0Lq6Wock8X395bYcZv1lOTw2BD02CpBHRsipTFDD1UGI -eWHoiKbFsD+RwI609EI+kQ223XIWSb40Y8CiBMhwUeo2uLTefzTvT8Hx1cnn5p4E -AJoHDGrpolAb6S78oy4qtgV3fY/82j6kiBqdV27q6vTGsQHI0MRetSYUiDgptYsW -ES/1QV2TzioFqv9c6nZKhQGMto1OTlWMeAhu/4B6k3UMl4n/hV9tVHhY31TCISyx -hRsJofv2RPGNJx7y2Fv1NRYhcldNfChzuTavQz24NYNGxJFT+s2J7MWGTzU5yKpV -l/fFnDHy7OuoSnV8D3wPvdV82cxUo2dntqxkC7UxrGOBUjlmy8TDGrnifvVW32Af -GkBQ3LCcuzyHpkSku6+ZpcHaxFgCrRMw0mLM9v/UiQIcBBABCAAGBQJLXBRTAAoJ -EBYoHy4AfJjR+esQAKhCoy88SjhZqrxOeh9z1bZgIUM73qvTH9Lqv6nbVrTUtVkE -OtiNCZuSnNKn6tsj1RRwds6bvSORbOVq1/oabAi4hO6UMlANgl64O1k7G1Rx6ROR -E/yJmn3L0tuFHIp/HLKz2fG/pS8gRn7KCP7d2ZtwMxDN08PwSdVsbU8NjkC+1rCm -v8Eb9RzklDPxVLoPxbBSwCGTMBAgYIWq2V0wFEWK6xh/QjaRHnK1NTM0h9MEIQN2 -BNaCDKcavU62m8+o9XACu3ROQQnr0qylTuxqMfqqF3LRvPK0ocN3aVRxlZlzpXJh -hL6s4phNDLwv6+xxc3EK4v7bWV3zWJe8AzCnQDjeuJRw0qQS5pOqDgTlYyehvBIm -w5NEZXyG6gfd/+LG+PktAX/cb0ceA/crsJXqCKqJ85WMjtLnHnwipQZFgYARGOHu -lpxBFG8j2DQUvDtme8shyDCzqeXmaaIsz2mBFaSxCcYznNMI1RbN9d3pcwPhZm23 -xNdcK4fKC5UZP9bmtdniSPQAjNazMwy6WJ+PDJQmq6QD0EU17m54z1U2Kj7WdHXV -CJ6kQzTIMcyf4fgINB2XwUmc+4TDcKxEXI9qII+m02SCQcQNEkW07Rh6M4c9k4WJ -eyEjIW5EWbc4G4S4Zo6WfV1fXeZ6nNkrFen9ve3AI4vzrWs0xWCY/EMKHc0yiQIc -BBABCAAGBQJLXUl4AAoJEFCucIXGvfbwGE4QAI+ISTTiHOjues7M0Fl3tQRSlmpP -EESVtkKZ+bbFtb4F34LW6Uw0+Wnc9PsMiJy+3i1R5IMP70ZdidwfsFkp/aE44pu9 -6Cbs8iYxfNO6UjM5T1Rq3wv8ye8uf0oSB5aY8uuYd0efwrnCk5u/YZ/LP8n4RLqo -/u6uxz3KMiQsXsmlU5IA+FIuZv4Xjvbdw/NMy1xQfV+izmZ7pmaLULhSOBm38fxt -sp4hRhQhgUPnrI7piMnUg34Y6pFO/E4fjsU+rE6BQlYSNJfl/HETliBqeRSteKTu -fiRletxN3zEBSIATdBveK8imdrz4EzCoZIWY8RipQcRfAIMYVUlDkdm6uuZu/rK1 -KubFcAM0VuYch56XQ9JWdn2kZKQBQG8jw8I6A8dJk5hH58+z3qgeebewuaY9Qhhw -XzBDEYIUhqTbWhJOsk58kz72p8R8jmOoGYTJMpXUXXNS7LnLf4eKZ2HI9eh5het1 -VfATZzvs+J02fkd0FcgZeLa4MtbtBsAmx/ysD6ZIbTpsU6vqGa7i05LLZppey0OP -0htZ6QpzA+2LAo2j16m5gK4B9de6TdJrgqR/6HTX9sLywrfoLZ+d831H0pAjGIRT -RKy3dhh2pqjBybPV1bNCJ+r/Wp/5lbUiw3FXw5HpHcMRUxhqkg7uI8J0N27KyvOj -fF3QqaytRDvGqVMUiQIcBBABCAAGBQJMYhIhAAoJEOVivZS/A0Re0n4P/3JFagSU -+qyNL2KrEzdLrBcuA94DzFearpeVZH9+jrpPPlplPFa84s569qWw0zwrQw/zw042 -4RRlixS3QHJEqBATDNeLN9/QOKOOYCazYCuRPi1iITxFngMDUYYzNAbnnE90nI00 -bA+ls0YE5XnE7m/Vm5dyXYyI9UXJ0Cygl6MbFZp1ymHV3Ii6p2qk+y81BIyRBg1S -MzboqD3mtt1VOS71x4004jcOMQh9PEIltRrCsRSPm7wzdCp96Pq+69/RGlqTNFOM -7zxbydtvs9unfsRAaq1ztk8DLd6EDfCandoUV+qxedJaxkqfslSneFrnsIInVejF -HlUz2BUvlLWEaqUtMeGHhG+bzMgmJlemVcIb/EsmgJRdcP4C9RigDlFTKMNqeI49 -P0BjogWUNRNf01CwSX6pPaPohikFydRBLxHmEXPB0bCjUpB+04CtzjPHtzLjoA0Z -6vUm4MaiO29EOUDDSjd66pNcp76HCN+57GJp50RcQ11J00SIFa60l8tgt2At+lqv -7YwqCaqkNHK4xLBJKCYwWtlIP9lDWoV4ysorKofvAor+Fh7doe9aZMhOXo10M4jB -eyv3PN7UIqaThqfu/rN5pGfS0Y3Y8sZavt9SJIR4WloyO4I0EcflviFDWimXp/52 -I+jEBMNJiLrigQJ+bl7NMFsbvtBPIEU4CdT/iQIcBBABCgAGBQJKdaJKAAoJEPZu -PkGfhPTeQ9EP/3jVNSV//Ya2oGOD18+Jo8qMJPd3DtGCEwUry68CSdikgjVXNN8u -BGWyLViw2nxvUIBI39qWl5h7LclWtCsgxPAotekLVcW0w+2Z/FX61/wa1ijfPMa5 -aNJhvfbKG/XP8cVsW/ae8ywCdvHJZkT3rs225e7ttDcu5VZKku5D4QFHzylpDPwZ -mApK/VrzDqLI8LztX5gn6TxgamtCFU+qXM/ykYA7dt/gPw/nkMoCUL/zTOt/5Ft4 -ubf6ParCtz3wJ0mNh5sORfr2rzucyCNF5ArvZKF4VvLVgnVxXVWw8DLJpOCeHIos -3YIQiDp7+xXh7LQOppewEW4B2YqokANAfoJwMPMSiEUEm6UCjLoFe2OI9+bmxl0+ -DncDo2/J1C4/5DFZGsED1P5KTJzL4KPq9R5tjSErtgN7fwdqh1K2OL8oYmOH0sFu -wB7qDe5So9uMbx09w2oWng6j49kiVW93GOxFwIywAe03CFqZqZvyy7oXrJRWIY1W -faBcju8rDZYFx+aWBl5KWMLvkDcWkMBqhRlUO+M9oaN7sy7CoxERUZnNRnId+4Ym -HJDJmDJGN/F78PDNYAC8+uODTTV/S9gr3HAtPaJjkRr2MpfdIdUhoLercfCRXJml -lHvJT5mZBndbamI4NVcNsennSultG5yguFRxXPFxc3t6kEc7SUtObte/iQIcBBAB -CgAGBQJKghCBAAoJEBNunPF7XTQtwroP/AmfWzdtLUsCVHIQBPuvZ9SC9TbN6vI9 -ycsi5KoF/w3jm7+aQS7Vm6MmxbdZI0PdzyQmINBLdRO/T4I+BHWGgNa6alTdapq/ -B/9gHN4IL+FGCET4YuBGuuRn8aL0159A0430Qra3vancpHeYxozPvMc8+lH/d+iA -Ut8FSZ/M9+Jzdvx80H5qokMQHCRr7lMew5rjMwt71qS+c+7g1iewOUy8SY6Nl1FT -lNVZj4ulGvH9e+T6btxC8Jzo3XJXc/jogUA0SZmfI3owmcGFaaTm5Sj38ZtmDfuG -Et7NcGFu5Bez4ddGiO6CyI5w4QO0ULNznktHXWWoGSB556y0YfzO6ISsgR2XBQDg -jsy8JbM6hXxKqDF5Piw6MJ7N295wJMaZ6WqxRMsdXJC4iLnb3KZ9x1gQ+OFSZJTC -A83yv6v4EhO17wsYrCZNfQ+3XxeYgPfPzfDIPBUBb8gpA67tiFOWiYqskZzunBW3 -46XRd/wZyDwvDClIA713yQE9QEBmzUnTR4rhVkLKs1frewXXlw3sQfrI1+MCdAFu -bS/qa0rhgCJ2sif2HBu214/Iw7KLSmRWrMMN67ZcqVgfau3+DLuVOYXWqkF5Ce7v -CeXChUMj3+QiJd8N3QlX31TsZOQZqS8MoNLKfAB3EGS2lAWFkgmEqNtqK5EFi8Y7 -HYc8u3d8OZrfiQIcBBABCgAGBQJKiWzWAAoJEJwnsxNCt1EdEZQP/jRi5Bw6ET5c -jiSbGgzdp7ZOtazqLYpPVq4iOEIgpyCrjqnlWD5Yz6ciAeQIW9L12Vg4083f5X53 -jmlxC57RDxmAvzWEb6b2s4wIhimbyXJQ9qiT/9PSGzGlNiHy5py1GkZxyzU5FTx4 -X4BMmgFqHhdDc2APKWVZ+LZyGcjpbpRKnxRlNS8SaEYQYGEGve1cuNjhgWtJr5Df -2hmMf1E0fS4AG4tIkKIIZAssMnPRrPI+k2DY/0CYMSanIrrAukzwgvUWhPu9VjgJ -2CYi/whzUJQlOjhgmMQAf2iwrToNIKXSOsm8p6S1lgRnHiVNaR/s8TEIvxz2otqO -9iVX2XdQ5coTNP5RKOUY63ABjJv+Iz3sPG8P3N63QLh66WuGaHHtbaBrPEihWdgY -N9v2NcfK59YRUjnwnKMLm7Y6cpKrWZrojXpEnqtNhWvrmiGFewl41mrGnQwT3lfz -vgergQC8MBhwKjnXkA27TetRI3ohVFMTNSyrB5vReRxbaM9eMK9naTBmSf9nPJeH -e2oLouhrUacp78u76H4nup1wJ9HaZyGKhgMgJ0aVHpJi87pdxvxOfd7HehIiNBtr -Sv85QIe3wd69dyRfMH+7zIoZx2vNgMxls+N9i13y4wdXOeU5c0a4V//aMa5fC80D -PRtV1DpdgA5ZgPvhdCnBqL7wfStF2QpgiQIcBBABCgAGBQJLZGBMAAoJEIy/mjIo -YaeQuxYQAIo6HkZ5wbhk/OuoAr6Wk/Kh8tQGWXgDT1RRUYImgu9HulFovvbMF6Bi -pSlYBfHu4lTYg3pE8icT6CYAoKaBBfSI/29ferenQKDTyt7rCktgf1pRSy8EPkud -NRF9Aftb4fStQdhmbB04elupOiDxfqxP4aM/5ampNu+lEgj8f5g71wDfWuWrsYz1 -fbXj/B54nfDMpEPvGij5Zd24jufmDGh62DFhQQtarEjsFw9l3+yIa0Dju1xlFufp -LP0hILE7HfGXwa/VgCjOPIWC2XcpLZjeKOUnikzEzKZi5V1w+YnsqOwB/xTYrF62 -E3mTGVAnFWDZv8cOZYyP16m4BrnlBs5WXC3W1stNUHp++i2QcCkXdFAMe8XWGZgt -Fqmh9B8XvmtpkHStQFVwj850QPPYWj8zfxwM7LVdpAxys92vb1ONlyUkPUFx5vCR -6VbcTem1FIV816THiZXFbc4q3lgU4o3Brpdf7YLboGX1bBRaiJDiwOvPoU2xjZKi -BF8l0hpj6qQi4ahO/57QSQ6KAVVpzVIZ0rvQ4Iixjea/k4pob6tWA7u13UJKYi1k -L1egKNxgB10xTq+7RewZvxpWJUH0GDw4M9U5Pd5dGrns5WcFc5bssG6ZqrSQzjCC -thQxfOxIl8/QbT7HVe069Hkf7uSDSylkhGdnhB35wjKt/lBxTYNViQIcBBIBCAAG -BQJKd4a7AAoJEMkQ2SIlEuPHw28QAJlYfvPRMDBumOEp1qxlxnG5xwMPIhnpykar -oJDCiAkDVWvYUg7rVUy/0SDWy0x3j5a7gckQ08hoHESEU1Qfxd+haUPpGkuDulWL -EgUY2tWpMJ+PsymZUX768aJ+3a4lz+vNrKJhYKFZC4teBkZli6bQrWtOQBO/7wEJ -yBf2sr0sv5vxMXUI3et1KKCajUPn5+6jK1GVVN8rv3BBKT+S2aXWlsd/vKJSBfGz -O3+nMN0DQj5AGPunoUvmc3wEPSZ76xxSWcYCmf/J9fgU0P+MgfyzQKJdkSdgpQpg -eNtrEaXS2i9oFokhAhV803vVuotatFpUY9AEdH94NsIjupiNXwQBOVFD8D9UQJGP -nmj1lp2bffL64NrwB4Urhl/PkoDs4yDmunFJkOCotsqOi8rZSFbWReXkAWLk3K7Y -f5tOgu/4ejk4c2wuP4ytnIZipZuYAMYt1Hi6RkvdStTrGxvkEE+pJZvkgCH6C+ay -zo+gBwx3qIO97WoO+KapgkyWDRYuShEZlVDz8neX58dTnAC0PvfInQc/lzXSkmxQ -z3nOxY3pDU3REbCB9GYE8+dZHNJnqkh4aYVyjV4KQore0+0W2gGYaKGMUNRfNBHw -HpCUPUKrFX7vX+bIgfixUAxAhHGaZ5/fntrbA+MW17d68gm9V9TxheuUquu/GgDs -vjPZghopiQIcBBIBCgAGBQJKecLVAAoJEPU2qnARHVcW2KwP/04JJwBvD4NrQ663 -XYtEb/TcVqef/qtd0NZ26Hs+sonUIG0RIefgbIwDpaIDmlurajwq4DcItJgwIimH -P33w7mCFjBWL4I0RhrUCEQefveDxzkQuzCeTOnTHz10Ar22SmREqlYmtGNPBQ+In -BzYrwmSlnUvBbUwDW5NtkcGfZ1HIp2xOdHxfWo7nMNX7BDevdtOIkL2CombBhUO8 -8eHXj5zjDMzQpb91hv8eeF6wDde3JkPG9QLVclA2IxfNjqZikJ7qLTYOJ/+8zMal -yVtP9tFzwX4zJ9G7g8rUg01jHwD5h5HMBRtvC2z3Bv9VO1qhRecrCOF3aqcaogjF -yXiqx8t/m3maaQKBWKfhe94093uY1/enRMXsNLvh6fwvA7XDb/2z3URpHHrEYii7 -WWzfD23lcofwFmi2WNuAaz4xIslJ6eh8mCf33sglFx0nW5NGd9GlBEwPaG6JBs+J -65pMpzjIsQhAAQGvYPeSHtni+gLt7Y0YhmqpKsaAANdWxgEsDc0HyxZjY/bj4gcY -1u/6IQGdg2NhI7FkA0+mztLTjNAndiivCU/2iYxBpsASpmcV/SvfDCxFksYqFz9r -d2ofnOxAIYx5jf3M0FGyxZV806fUp0SoMyHPOagM+glM1DwepE1za7Smf2T/976F -4+yTShKzc6iTm5VDyvkTuRHabGRPiQIcBBMBAgAGBQJKcWbpAAoJEMaHXzVBzv3g -8C0P/jZ7p55nh/kedKrjfCQ4nQ67iH4y5/fME2yUTdAot1umqLGeLAyxo2S6LuNl -W3OZ6HaJQc3xaVdCDM1gZPbmb6uuvSpFdrgxxc4XSFP+zVZac/tUULgRpjyqepzi -9m24F0MsL8BcljVDEMlT6PCVKaqkvJvFSGdHuNdVKLj6uFq94GSF3GrgL16uCkTg -0fxmug/GPFf8sdtSCffAHWTThXfBBfI+VMzmIF9s55Fwtwwts4SKq2cF6blvobm7 -Ei2gOKeIjnKt153w72m1GqvjrNvF+T72dGzJIbfxMbc4LJkF+DuTq+3Ih1MLuAZ9 -GerGbcM8FgM+odINd5f5FSSEDPkAParAeT7n736tg2bQFaX5xHm9yocuV6a30bw1 -7PBArbUvYWj2gxuF9T3VsXJ4D3dxZPY6duvOnr9Gj3dmHe1YIRfhS7NOT82merj8 -LnLwrl0hr4G4CckCTGXZQXMVkvzcCRGMkXJzpbAvKB5M7CTmEzjX5To+hJOruK+C -1lix6ZBETvgP7zyG5Rg5eHRxK7hgvMihuH15ZJv67dLfQY+pRo3nwVveWxuf7VfO -8jPaQFu71l9oMNFKqBzrCD6pFzlpmkc6Rif5qvwnkA0wFuVlkEcc8TU7W3xxiYVw -THSHsF1WrwkkKX+xRgOKlkzpTD0C6n0HX5mIDMdHInD5vN/1iQIcBBMBAgAGBQJK -dIUvAAoJEM1LKvOgoKqqVh8P/jXbCeAUzifwlhQRsT7q4MPTTBuEj8M28jzUgsYT -J9j9T5LGIH5hEoy/ZwUtqmN8K2mxo2LX5HgJMYte/EX8yxpXLW54UjvVoJtsiHgX -W623KUNr4Cbbq3EKB/5no83uBEK6crfhTukhZ2yUneeqMSHvHyTLjbYNf9GmMBVS -AZSp5oq6LrFbuikTq4T27mRAiDQ9Rdn6Cb3AXZPDHzZSXeW/EZ07kl5nswoClTmQ -nk/rqlUUtTUWYZq1/ptgmmiBysYmBmaOxO3bp0z3embvajzg0Tcar9vX1GEo0ZBn -6gWBvWiWEffoXnz0X0pnCiiNAQ/4Hr2BvwMa6ObJCVOx6UXkn6+A21sJMJixm2fT -Kyy7Sw66A0trYvDkhiYnfMfIQoYTdnlanQFLB6Faj0SMcVkmdp0KgskDliHaW9Uv -flO/tVpp9+W9wn4owIIPSbO2W1/6Osbsd34WdhxMArzBcPHBkdtXD+NpwPjzBmAk -/XVCD7bAE+hW9KuND4mbzB5CDUSKW9YaVX2jvJzFBNjQkDfUnreQ/EIRwEQEP7jl -y41MDnS5gYGZpfQQLJnP8c6qwZtUJOZCcUKugI3dxcqNCueMxxbX7L+dhTodHmqs -VSSLjs9GDINdiqmZyegT+4s9TjsKXAsa2khjOS+k+Iz2b/XJLuDZD27macjsCS2U -fVQxiQIcBBMBCAAGBQJKdMnmAAoJEFh5eVc0QmhO4ssQAJ3oYkwgBj3cDXXF2H5i -7rS1zvXC85vPN6nIImbyIlc0zGpClDJpPGiGShcnYhItL/cTPSz1UNYrCzGJBubL -POjKPgt5vlw1xHCFXAqKyUhHqyLcOKe2JEYFltEPJwAf1MN5BwxqWpeJvsWXFPry -z3BXo9Uwzr6cHuUosnN7Uw+e+vt2LXEopM+z2N8PmbazrG994jsKYJjCpAdTm4IT -CcQkmpiz24110Nb9Qj0n2Wi1+hbLGq1EvA3/zo+DINNrF+IVbp/GNi/GsffyURlX -LFcmqo9EpxACFCYXUlXA6n8Zeq2UCZ6wjRAGD0wOLu2YJiipxVFPK6wxfxQ+QVtp -UMsKLZ3pYIWxiTtvkIgAnO73mHmNnkNqt5AE0iwu5UVSaVw+vHIZAibKHoy+9+4u -Ap6gWYVdFqYe3ENxnsdzvhuBlKiAvN+M8X+naIGAi6VlNl7cWE37HpSblpAGsxe/ -M173/dgXSP7eqKQt4ggmPLXYafYAJDl8oQ8yB+bL1AyogLkArNxZuYoWpB1iU+l+ -V20G1EBeD6jK5DJSC0y7dcR9AALxeqyL7090o51u6Jn2qGQmV3IV5qTnWTTDwoy1 -VsiJx9Js5fe0RKbSL6vj4MuoXSbGYKkptQpFDlcqZ2uKhzgoaFeE7ybQydPSZ+Rg -CkW1Ytd5NBFALAYrXR6/54RCiQIcBBMBCgAGBQJKfYzdAAoJEM1LKvOgoKqqijoQ -AL7xctd1R9cS0xUt6ss5tT/+Z0BLPcZNUc8zA3xGnIrdkyY8+ov5LMXk0ySzmdQZ -8cTSBSvUTxpaVCgl6/ifSC+CvWDKwJKrAzeXI9Z2n1VnrY1aJ4H0EqB1MK8PQk0H -1kIVSF0ZbaBNOVkDL16CZ1WRlQ/vYR0QPreTGzYxNaIjeNnDKYzF92NflN/LfAEI -8j+J93zoqqwW/X1Lw2yDyigmUGoKteT+mzG8IAGoOK88ra+0U2S+6vc/Z1gI2Cc8 -daarGAKKEvKbxym1aYoRCZYe2YXdq+iBmLUfFL6S7XWrJF/DJvt0xKK/G02uc7qs -7VpCH8UV7Lz7KysfUJKHsxZcHa4ENvNdb1tUUsymwqQwuWuM8g/upc+dj7xRvE8j -ETvngz0+xfMCE1XPT+TfwnU1Uk2TkRTPinP+9trdIc0h0WhDazOXzXlN1RpEn6eq -W1uWO9xok3d/JZhNbmtkHPrKxRzSsvPmDdKnLCnO2EHvksmmULI9ewqx4vIf++OD -8asmM4bhmZdjV3H/2jjInKhYa+ApWZaua7ZojmMGg3+lYU0SAq30BrsuyKlNP2HN -3+HqSoCAF822/3Cq1HM7MMavvS/3sVz8npaTguuVDJtN7QSQK2xsJmZNQG4TvA+/ -+XoqjrRBNs8+itnQWYreSiUtqhn4nD3w3ZQH0hlDUkaCiQI3BBMBCAAhBQJKUMQk -AhsDBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEACAbyvXKaRXoIwQAIP+d4qY -UJu3LFrYlPfqPOT3woj821RSsxJpdeqrRVgtuz0qMSGwztuyU6QTFLALaGxO/3Vy -krHCMa/ipvziBjc2ueWH9XB+pkfohLdyaa7CUnGIUPgw614Fuds5c6NtLIrSDgfo -gAW/4UPXGQU2jyiGKrPwLN/JX5yefNnDaSHOe7uvSPvAq7BSv2mlUuvwF1tMOj0/ -Y3K/ZaNVybLsRZbaktOta45VJnCtYlRqXfwjoLodT7KLl80743LhMuCui36Z+3Bv -/zv5ZZHuRe9B+H/wVCEB9RDGqpDbk07xq81MlDMl8lfmEse9+46JOXsro2mjufas -oCpJdTBikq/PlM4wqNvjZiebsUmO1XsfqNaf3qXumZ6WRHLJohenRoMgvLCZyDGh -wB94j7XB37MRXMrhJ6QbH+nLNvYoNdInekOGra+eUWCRKNv1lWRI5ZO6R1Gd0xKe -qWMV9X4/EnJWwHBNggVCENmZBmXEHeAxvwRKjvPSXN361TK/jX3Y6Ae1EsOE+vMT -dsLQEIRNfQaVTjxg3ElFeL90bF0weIQ0sCB4jDSZ2WpuxP3KoVAfUU512HVEApRa -zZMtGmCqE5Wx7Fi+Jpu0S2bRwUo4RkwVpBi4s+Ln1alxuZ6PXAwlKrr4zL36G0u6 -bD8QjeY9YG5Rm0Js4CDiX6uuFQ0nzKXDGy+siEYEEBECAAYFAk+F6yAACgkQVuf/ -iihAxwhIPQCg1gdcW+eCM+ZL59s+tFJxW/zqLeMAn2SvehCqBPa2KKgputYk9j27 -8PrniEYEEBECAAYFAk+F6yAACgkQY0Ly7Lxa9rlIPQCePAeEXTadK0tXOCze4ksv -rQ1uiGUAnjwRVr6jHADGP9TZuPbfgW3zNxXpiQEcBBABAgAGBQJPhesgAAoJEGfn -cvCDUeCvidYH/2nnNVOTBAjmq9usF7ZK2wDsvNEtRKiryS2KKZpf0iXufrUpG5uC -zfv4XS+pGDWu8ixVdJTv7k4QEXsMpYk+bVyQ3WnM2OFiarKFaNNoxYVzM/ChLA+j -Oo6L5GWEs3QlyVwLMeEIbxuTrm6ekjKyBSqnEczjHud3DjXjr3qym7BuDI1snynQ -JT2DzuRmKoIgIJZSzOnY8FDeL4ugAzXq9D6e8vaPEnspdlxEfBZFMux9BvSyQYhO -1G5RzTf8962uM2i9stu7rafaOs0lkToFsyOxcBXbBM+4lwMKJAozF0CKSj4u4mKs -fSKD/heAxktFo1F/MncuIqy/tJSVNCdA1PiJARwEEAECAAYFAk+F6yAACgkQqM4o -pgENbzqJ1ggAvOnshREVP1v2OahOnse4qmkLAEfT026ov9hZA/51X6oMm7Y305Xl -1kHcNXbHLM2E8oML6LUESxU0NXgAwG0phqaWIFy/UU75qSWQDgmxHUZg4wyUsQv5 -2LzyZXVlZBr9D/vNDOJKtywqtHCFcntJf9i6AEhw9+4QYi4Tt1CCHA0bbBNN66VU -VWe6ydpKz4npJn6wqsrkFPsQe8DaKbkyjOw5Q6ntvsQEGjbwfsPKwbzoEjNZssYY -J7e8yPIFcHYvIpum8GuEuE3jIoupE+U2V6Pu88/aIA+s94kpNXPwaCKRFOWxnqv4 -i5svpzRiLKa5dwn3JFC/80/Gy47E2HV7+YkBHAQQAQgABgUCUCA8vAAKCRAmBz78 -2sV25u20CACWddBJiEeEy2rDVI2Tk+M26FJpAonE5fyMrAWDuuj61eCSDFlqeJxw -B4AxZRlcHfVwdn/OVp26+bY462Rv/SqMllqlZV9wUZoyHv8DeISJGxlXKPwe68Le -Voicb73uijSnh6TFy+jhXYBNVBD3uzeW4PT5eBNWenbrc8eYdT12XGGZbEMo8V06 -nszM33GiiOpImgDifD2n0abqNgsEFhQw1Rt/6u1mSSBCqHhBRSG2JTnxKi+mPjyx -LU0iWmG5jM3rG+rKt6NY5pu47PUSCBEzbduGEIkYt7+P47kfB4SCqVoISGC5uEtY -fF49TiAkOeZdaLr033ke+O/guDwa8vhOiQIcBBABAgAGBQJQgyrPAAoJENcA+kgT -sobZ/6MQAMw3RcxpHE6j/xOoWbYhXM+LtEm3hTIuabF0lKYTwXURhjiwVHVEFDRg -OrfwMfQF3qR8l0DORp98fnIJwFBwpo3gmm8YQanQmYk3ECHvYWUSKuSg5Yca0lCA -RGlNjZaQHy/1wEW0gZlnVqCEHzHZqJVd1f27HbcZEgP07Jw3FuLrQmQp/93qVEhC -M/kXotxWlFgmsCDNscausjIyL0Co28sJ/DkBvNoINh7fqva/4LXLsGVB0RhuYXEJ -jiuN+NxCbA87vVAsaC0Fom7nMD5Z6LbQAg7/RLXl6OBKX7Vkf6MKVKM8e1lp2YQv -mLJIJu7Bs021rImCDxPDotmyTQmWcxNXBfbMektHhUOqfm7H6gte+5FLPz9AtUH3 -fJXU+6RT1weqUu45bFYpm8LVfzMzpiNFBS+3idA5VB9+sMNl8KGoKDecfowk+GFB -3RZDppyhUjJumVDWw0Kpy3O4x4Y+/XdjVsVzNb4iTWabz5kDpfIV8jiSR3FcYQJx -KkevEmvXtnNMPZ0UsrU9cNFFrEvftwCp85yd+TnZXI52iuWXAOKzDZP7QbXeyBYj -0V7hX9WhvV8yht6yUGYxhmh/eLjUyV6BNX8JkFQpVyDk2paHv+mPwTdXi54/7FJr -KNn8yi6PRKgKQQN3VBgEfJq2dlokpr0ihvlfNHQZumIoWU1OnPOjiQIcBBABAgAG -BQJQg/cgAAoJEHtYWzCAfCqHNhAP/jUgjjQUNw4VDITkq4yi34vGlvg7kEUCjYVP -rzzGUT7e9hKG6sIP3+d3pdmcHTd8d2ZMnCSs49Eba4TCnNZkbyO3QRt0z8Kff80o -/YK7JvlYUCSo/xVj9oDQbx9wnf6hJvs458fJBlMmvpXm4zq/bWlyIftUTJ2q3QZx -/i61thP1dQ5nWhrKbrTq6LzJ/YABiNjAwfOviVxAMP7hDi7pzGL8JPVSV2CQKtB0 -2WCq5uJuVIucbOsYm0UxzEGXZPegcZQ/9dCHVwmTYajA1xdCtnc4NYn4rdd4R0b+ -PW1aZ2djYMsivChx/bqHOR0T2Qixh2Qkpt9QN+uh9tZOzWv652SwzPPjdPEyQXvF -mLYxw84E++V7dRrRGyVr90GEHr8pj9XAKejP/NfUHbnmuQFURz84IcUGfDpLxF2v -5hBQJA7aLPafrs4qTwNTwNLjQpE9jxPbTrErgvZcp4pcbIFJxnjjtGq90YwPAI5D -kvON3iq4YpCr+qZghfhtth2ngeJqsNpFBRjo5vQQjto49QY2X/Hl0flVx6KPYNlP -XBZu3gwc58K/QLlTT9dE85vMeEY48EqP8zeMXOvwDAKRMs6T4Dy4e/y1jvEQgNy3 -h0bVJBvgqHyRKGeiTG15ZkYtA40tcNI9n0mMgBDmAQdwC9/EylWuEreu8mWL8aJ9 -Cp7viG/OiQIcBBABAgAGBQJQmp1PAAoJEFlmowpT/oIwCkMQAIoxeKak45EniP5M -GbW849sLpktA0Uo2PmXT81nfMyZ0AHWoGKakRtAtbq4yXukCmlDOCDYrk1z8y0iC -3IBvO+tJYkj+4tHu9f649PRYNyhDdndHdKQPubmSA1atMIAE8vopLi7d7tIHhuNh -7YntCR8n63qket9faUd0GwS7t3lK7fDM3+immcktvTtDNJSmayigZMCbwczJsq8C -fTJy0wxmu2hzh7887ih6uhcyWgqeIQ9pk6yfqLUbaYoD2fSfmkQ7YZmPbHijbouT -4wfZ69AJ5/y4g0Ah4tfNNAWXpGLggBBdhEpKrGWlAoWmj0xRUgf+yAs5+2JKoscC -CiwIkfUQIDxrTlvOY0qXikPll+sh81qKVmi7hDcaPjk5sSztAGDUzyZGa3dvSugV -FzyC38p4bJ2jbDMkRGU4GKIWTZemxfgS4H6twI7Lpf3L1FK9z31YUlDLS/4t8xVo -PAV4C3FuizYL8xTlUwoSujUq98tW8DetErTLobOEluI99jwxVsVSbDV6+P36MtXh -KE7el8g/T0n/gWA02RcvG8J1u8lbuBQnablJ1gLRPOM3BwOhovT0p4YM6CsZ2m2+ -xeGB5DFz4lSjk1NQQd8rbGD1qVRUsaEwm1E6MjyEEoHeaJs2rww6eeAgU6iVhMvX -0ZKzO0ZyXWPC4HgM3ssibtwQF3y9iQIcBBABAgAGBQJQrq27AAoJEEk7PGDerpAW -AEAP+QF0gnqTbOfe95yLixy0/mj9LQWU52ls6EHsfEhon/dg7pCUf+0lKgKPlroW -hJPaOhB7iT5/mbIJM2+y+ythxqgmrhIvBFZdeFcSu/n5ZQGCrcg0z68P+3fOKzm7 -3K9+7JI/0yiEoMDaOxlJntoqTiAJ2H9+GuPqVyzSMAscXy81gquRnLvi6G2NaUbQ -bc4gGXKJMlZKLV/I7Gs1FaShXEkggO13A24WPg0/QnwB9WPSTqNli09EFIsJdeFd -nol6JM9Pvof4lGHu/FwC/idK8M5Vin1tNZoGBiIra6fApul5716iC2gAb9DVeAkj -FnqKTa0HgXD49QX5kOnLgFztKZEMQ+9ghyeNRXXWbIkWfk865C4+VOuIsSGcFpq7 -NcmY9+/HtQHFqFUrpYHsvf8lZA6+0ynpcdRuxTnIsFirSrShmQT73d1+7ntqh02i -Zy536IZR7MQd+h3d6GSj2WzyMzqEWNocwpeMJAOS8GzYNsTz57XUcwr5qppN5qDs -XN8UOF5Ad+FMNpYmGn/ZJtT/ETPfi9i8Ynx/2laZ0bcm3Ur9P/+mFGnytOoS4RyH -xiSDVgbkzg6aZEFXB4eKX/vQ3xElY9Oy8N/KC2c9BLqZb+2wmPkjnoK28kaHIjPJ -Hv6qMvRh/EUZmojVH/u3Nl1Wu61pH7yo9EpYe69eMhz38pj6iQIcBBABCAAGBQJR -1uRkAAoJEMr7O60Kdch3itMQALDx5Zd0c8DrT+mf9mrdbN9q14rVYicYhCWDb9XQ -0D7Mlt4mI6YgozbDNEbjmDEyyHngkjbuUeB1az3+mwJG4sLVPnYXUJiJAlhShZeR -JBTlU1Ikf7hqmebGtAtgukI+WKnU8AymwyHbaM3fF08j1uVet47JRD6xG8ZGCCGt -twue7wHO7WSWgrDGuhY748qTYTqh/yGG+1SisY+M0DvgBPok/47Yh57VQ1u/PX7n -GVS2hscufrL6JB5AbJoktqcsc8Z7zfGTYdVwWcRX6rXn0v+UPu9b1RLeOSKVl3qy -gj8XW9+nob8dCLGgs1XXmWMUi7p4jRZj2Qb5+BNFYnA9KuC6Xo2nsJxFr45YIwtA -ioJC+Lw2c/RhCDM/nGOaWSeVJtUjxNSvjpJVcef2db62FCQ9LS9YySndpO/h6eb5 -D7DjzqG+gYvB+K6Dz60hlqtW79JvO+HV+MwVQPSINXiCCYcJDkzFYGxbSC43TjSd -HIbMrHUQ5Mlh6U/dYPROB8zbc3m4VO17llkV+HrdXJ/5RvZVRWeBnfbiRo3D34sG -JHJ5WwFu+Jc3URpX7Dcel3NR2E4epEvkISEBw/b2e20I/hhSHaFNFOTQXvor+UxB -/1AxkmcnZqSb+zByFlEoO1F2d3H/4+OOFRIPtIK+051iL7gpLfdA4svOcjsVc9kK -4qgyiQIcBBMBAgAGBQJSE7C8AAoJEM7JlC23rbhvwHgP/32bz+/cH+es3oGWjTu8 -7cK0AUiVv73Tf+MQyycHmWi/qSjnedO3tteShlLCf6Bwf/mRTze/QTwecs045Ycy -GBw4hzPafRM8tm5WhsTpynUd/1EA069SAjRTLfqSE7q+7CcjcTvzdvjEFDqS97Wz -GYac5OrF08hsXL3iDtiGFgGCtnPuNHa4dSnv157nmHciHJzT0ossWgGIjy1Wk9I/ -9KOYZBWV2xvB94oH35WKgGA3tPmBQriOF5SLAjdCvDOOu98B/k1Ftq/xkWmLl39h -CtD1z3qDwLTdAkXCv60O0CoEU1QwgjArDBO4g/NT0vCjpjnermMN2xIqJ5fuqWkr -F0Lb/woZnxcEREChFFW0XhR3PCsIpr0WXjx6KX/W8kabCgM3Rga43+wqskKhhSfP -4UAbwM+AspNYb2/6NK4STlks5Fgzrc8Y5ham2glK3S7mVOCQyofC1gL6n8r7CcVG -uEM95CVIx6Caz1f3JMAmwgMJDNYs49gky6SfswXHNTSLCM+lXNy9DarjYiQOOiLw -SvQI/eT5bS6vhX0HBuGqSeG5WsHTcP9zsk2n4jG87qE7KQbJzBKBVOWPLSYCOF+1 -X4VqHaBH/LH6pr9T7Hp7rw+qtAAzixKkGz/lVoIrvhsiFGABJvEB9rFMsFwSp5qx -9XwSjwxoqTC2T9B4oCRsrWLTiQIcBBMBCgAGBQJSaJneAAoJEI2G5/rl6wwQYjQP -/AgLCNcLLVshO4n2fWFaFDWleer5AyoPhkZWy0Liu44ydVKXq0ew1PN/FXj/Co2+ -SZZ8OowOwIWKXxNP7/C7Igo0TiSykPMQyC3jNSBru3Zej5ykMeREKhP1hcjXcwCN -Uu5LN0IRA8bII20cXX069aOxRZ6rRkrPOV0uTn67RHCc9frU2/9HtgCUYKxozulO -7knwkE0g2hdQcjADsPojCp7U0+qmzGd4kxxtdljcFeB73tYRq3GX6jXb/h8zXNdm -P8uzw2jhCjXIn1Rja0eqn7LkUDXDHPO0HQcbF47DN0lk/xUQl6pgB7xUulKkiIY7 -G9hsuVMIIQcCjjhZx3mpWzNNFZGQXxBEwsKCvbVuPThcL/FwWDpSy0nx6btiDi8B -wj6DreEVruFkN6EgpHwpPqFImXDe6MFdKEnmWAByQZQlOmwhSDO0nNg8PQhYeyhO -WuSEWoyCDcDwG1LETR0PTQqnF6mO80TTC3zdoALxcNGEYWPqe8tP6fkpCjrbhjn+ -CLqg/6Z6g2Wqrf1OEvPpiFgGKoGN6uFVAIvWGB8bGZoVm+iyouyLaPAtacHj4AhM -7jJmV1Ck0XYxUR55J884dMUAwtwzxH0CNgzyl3lrHtTCo+FCgiVLhkQComYeYc+A -G0xbMSqCiMkScafVIDUXReb+PPVzj+QdGbDZjSMrZY+kiQEcBBABCAAGBQJSkO3Z -AAoJEHgZsCfxsF41vl8H/i5JT04IPGw2oJqmhnB7bVHtLpZ5G6kyLwGDO9E0hFFo -0KyELZXrYhBNK3EWJzmfLV3n2vrf3gdP8cij7XDuV6O5309ui4yW1lVaNOVtnPi5 -jr7Vzq1zIyKqQ6Tvf3JwCvLMF1TZ3o9DJCn5eC3zaX24kGTLv3i9taK0rNVSn8VI -RLtdDAa3R+6uulzVgCpc4jD6IXcQrReFYmgsDcYopPna6HEJGqxXTEmEO1y8Y3+3 -1DJFyvKl7jY3ZFyuznufyQnzmZHqp99PK8OvkFmLCKdPkTwrGTrOrP1MoDkQyx/l -u1m95d1FdIaq9UgGAVHIHJcT2J9dHBrRCbmDN0rNMxKJAhwEEAEIAAYFAlKQ7fMA -CgkQZesqsS8Ox4ojBw//SdoYZUTGqfQuBONtfDz3gT+NxItxP3lbQPju4lYvU5cL -rVgOnYTfnK6kL1d5pilSseP774RRv7CcGLO8IO3InKNnChb55byMSs8tSXEMQf5T -PFxZX95MTpSPR0CtQEpqGnyD1Ajncgwq14fMXBs1xJcOlVPhUZ125HuM4upaYULJ -tIhdZp3qocWeZ/paoy2RWELSImijfrNs/G34bbzyFNIHdcazy+A8lGM/KusWdwSA -W/WxiKYSAkesXjD8BW7HfrfP8uWOFYHYsJIRyhjyLsKsnAH9Shp6AFuJty08M9mc -Utnzt2wUFCWxLeCHfhjLCR2bHnHw9hs0RPeW3a/fkw4GQAUwu5kBXsbzuZDl2iMj -1eT6Yq0EZtXFLKCRhpjw80XtG4DjWetZRUYbBAQpcnpuUSN+Rsoro/XTr6xeZTK3 -en4nUDT5wEapJxTRoYO/2UHZwunNSGii+ExIwwmgr0Pzf9gbfI9QcN3+bZE5PrX5 -8/w08plj8eqMRtELBkebQPc3P/2FVkUW7CqZrmO5RNvjLVWA94EH8V1EcnBXj2zP -w/D/r77nXkicCeuarMYmpA+iSYd7qiSykfIvJdwxZ+AdMsbpdeqiCTHFbPkdc4dj -1EcscdJ6MV3Va3hoXyFGnETILwfhb0EB1RT0bEUyRKPhC5WwtCArU15DeujFvmeJ -AhwEEAEIAAYFAlKQ7h8ACgkQgZ0nig5vmSrzPw//UC19n/VLhrW+GSuAKSI4mqN6 -i6aYbx+X+o2K2cj6NvjeFfiROsp7c/P69naYbimRrZaAccmyAiATjPwTRaKWw22J -R26Lwi5t3P5uWHxL7aJtpqnljPTlGHDAHPoDHzg4CfFb4lVCyXK+9DBg/FIrq9Ep -R3GJhOhOueF4fKQkcjmi2yM2MevHYbh68er4beUL77hm0q/P4cMMaAhsMhAnSuIt -ZAdzgbMH0nw8gFg/OYnUJb0Mrx4F+d4ni40F5rKNHd4oPB3XU6o/ao4tN6XQWCAb -ReUVBNoXC0lal41Sv0C+vhqQMkEUPtZ9RynkXt4BQp9gfwQ4lYSd86SGgE7z0Uwx -YdniJ0082ZzFTkmgVaioa27mVw2vrw/Lmla/IG1LqGcyeqDg1GLP5jDz4FeDojog -vgke30+o1KYVy7cR+Uy2pmnK3jH0yuPV1foHMI/2C+tR/cRXr7VNqZ42BVKVmxCs -M0i0JGCrsTF1HrFB3rQURXFoDrJOrCeMUN4UkTCbs06e0bEnqVPUSmD1pIcxS0Cu -ucJ1q206E7jTmVP7SaaxMV2/wt+mQ7oVQZka8TEVLhdGEPYoMCofNhpqECa+VS2Q -8DspcuqHYvq2F1S/cABjIZ4ARgkgKPORLkttGWBRzzkoJftBqRl6n4YpVEUznlu6 -qjMLlk4xqGxOtNtr4qG0JUplbG1lciBWZXJub29paiA8amVsbWVyQG5sLmxpbnV4 -Lm9yZz6IRgQQEQIABgUCSlEkEQAKCRA9r1SiHu9SdmAyAJ0RTMJOOgeGWjt4ScIV -yZ6WmYAuAgCcDsss2qx0G7/trk4sHfm2/SjwfYKIRgQQEQIABgUCSnK2BgAKCRDU -5e2swBQ9LRDcAJ9JZfLaCnL1TUweVsHEsETpi4RGkACgrObkpUR6w9UOS7l/XWiX -fB9h5H2IRgQQEQIABgUCSnVzuwAKCRBp0qYd4mP81K1sAJ9OJF+e/HnkjIwM/sFv -gXDOO0BpDgCghYiSVrIVUwm0YBceSLILF3cgPNaIRgQQEQIABgUCSnhdNQAKCRDV -ypsE8sQjvMTRAKDJJfbgsz6A8ljM12ZUW3tWwDa8DACgmf8icCcTbSEdVfgmH3PC -mUT8HeyIRgQQEQIABgUCSn0wjwAKCRCEibFNiAdSm7H9AJ462L1LN6v2rvevf+iV -8eHf3JZOtwCcD+mAfE2jJ12WnVRUSN7dCVrxF6SIRgQQEQIABgUCSoKbhAAKCRDj -IZO2xCm+L8QXAJ427tN8YX0LqOllIDsNOYd8HRRRmgCfRKckRp+ZV7SStg03irdD -e4i2d1iIRgQQEQIABgUCSoLqBwAKCRBBIcdDMXDr6QiPAKCJ85bVWSEQBJ5MA3Gx -8tvg3ORcaACdGD/ltU8N1gCQ1308q06lOj897/eIRgQQEQIABgUCSqQySwAKCRDA -nh2JlZMO3nE5AJ9dMpc+3RBo+T9teLkHKeA4NfnSygCfQTZHTBYbcHSTBuBLIu5B -QBNPD/+IRgQQEQIABgUCSqQyeAAKCRAEBGuFSi4WK6NmAJ4tuajshxrGrirvs10q -AudYQSNp4ACcDUbV8ASNFBoHZSUzq0ef7bUjFNyIRgQQEQIABgUCS1aYcAAKCRB3 -AbuFiXrzo1YoAJ9ba76a1CtngTITlHIx5dKIWU3FlwCfbfG2kEfgzjTkNcnYwGI8 -9KBtl+iIRgQQEQIABgUCS1e1sAAKCRD9H9kjU/RdMofoAJ9r/MXjkBpxvKPSj71H -CSaZwu+4AwCcCMiA4JD6AUOxwSvS26NltCXF51WIRgQQEQIABgUCS2YBngAKCRAC -vEK3Q+JdHrctAJ9QBX3+FvPTXnhvg50g/OYRcu4hPACfT8mM+bIWbsXDaqxAVyis -qwn4qMiIRgQQEQIABgUCTE3s/wAKCRBlHfNSPSPyXSt3AKDKXoAKuCvAoJSbc1sq -T9zxzv+7JACgvaTskR/lsvwFAMZNucukwrxgXMOIRgQQEQIABgUCTE35RgAKCRC/ -YHCLSEJsfstLAJ4vxzUmIpWUDGKuT1IpPfyXzwpO2gCgkJeFK8iC5b+w+s12w67e -s5sd7WSIRgQQEQIABgUCTRy+lwAKCRCnGmt/a4UvN1R2AJ9FCT6mtvtpOOh65Yg2 -ftoYAtCdyQCeMzrNPAOqihlTh4k0Qmw/tK1MMu6IRgQQEQgABgUCSnMJ6AAKCRD2 -KOuTR0MgbCjCAJ9kW1xLt0eATdeUxKzwwKf2WI7n2QCgqV7D+WhK+oVgwkvTujml -wgk29WuIRgQQEQgABgUCSnRCvgAKCRC89sYPboFp0nUuAJ0TZNuPkuxv7pyXOkDW -LfAvpHSyBQCcDWh86tTAl1Fhm5Rj3UBl6di3/dOIRgQQEQgABgUCSnR3PwAKCRDD -dqja8bzbc8jQAJ49MIC9z7Q7Bmk5iVJS8+j0Ff7NLwCfRySIzYCWuc1aKRZGFvQ3 -1HyimwSIRgQQEQgABgUCSnR32QAKCRDZRtVqCOOLvMbjAKDUEurHHpo4J7b8yCdZ -KMld343ZhwCfQZxW91GaQ5SNfNBAFIhvkbw6ZVaIRgQQEQgABgUCSnmQuwAKCRDU -5e2swBQ9LaVAAJ0Z7guXkJsWotvf0TGOkeCkj9NQqwCgrbt7598eLLXxJv8YwYvS -ck7QolOIRgQQEQgABgUCSnwzeAAKCRA7MpidAPPP5IDLAJ4u5bBAnuWnt2JFsgkF -tZHrcCUV/QCfTpcVAgggHNot+42J4XEJ40g61ouIRgQQEQgABgUCS11JcwAKCRAG -MraGigSbIf+QAJ9kV5iYzanaxWvsGF7DD5hHmTl3ZACgiI7B9T8IC7jNV0OuN6vT -zUOu/yqIRgQQEQoABgUCSoIM+AAKCRBRlIML7BmzeEjDAJ9I14JbEiPKYUob7MAB -gkxzQMtO7QCfdqppQddc0Z6FcPJiOwanBS/U6IaIRgQQEQoABgUCSolsygAKCRDc -NVLoNudWBCt3AJ9oN0SxVgsLb6/gOU82N4aozoNGTQCgrjEUoT1E6JskhT1Li9wu -LjMFz+iIRgQQEQoABgUCSrT0UgAKCRBBIcdDMXDr6XB/AKCdVmbA5ZrCzjoc1vV8 -ec55LZ8aVQCgpKYmlkZ8pwgkhkECvLK0MyPfhF+IRgQSEQgABgUCSneG0AAKCRDa -2nnNeIo/TAEtAJsHLmr2llIfirMyo9gKwRGklygWbgCgjWki/GbWy8etq7q4wYBg -Yj3KSluIRgQTEQIABgUCSmyZvAAKCRAS23nuxHY7pQBbAJ4z4bvApi+xAQcjpprT -n47oCfEWUwCfYsI5oCsitRcEDX5MWCAzvItnUBWIRgQTEQIABgUCSnFnDAAKCRBv -F6WvwfJOpGyOAKCkANmIsMQ3dFaZKMM2DtPEv6+EeACfZfCob9oUYjsYuufiGndk -FTI0Fg2IRgQTEQgABgUCSnTMygAKCRB8O3lwiMfB94PRAJ9Mbhr2vUi1T7bikshH -+/Di1rhegwCdHB+B1IsQj6XCXSTORaPH+14rJQ2IVgQQEQsABgUCTLrM4gAKCRDy -rYWsHkKzZ3ViAOCcEYbr2ODcxpdTJ634wyAVsJ545OkmfIqyekWvAN0d/FiaIBlM -lsHGfWS+qftWWwB4g4aEUGs2LVzAiNwEEAECAAYFAktWmG8ACgkQwqNdSiQ6Eylp -aAX+LWkiMGB9ptr2iwIeCxUaebKMCYppOKEVH9nxF0MLfGLWXQ/8Lkxx9Xx7iGuW -ejkmVcPsf/gq2Pn3Qgflo5IjEi1SgwfxpbohJBi64AmQfMKukkotA4i39mlUu1u6 -/SnIidZZHARS6lSAYPkGfXclTPlsCg3dQdckx/kzkusmXS9qrDXqTcK3m6otgBLr -G72j3b+cTp58auzjFEt4XW1oXUr502eWbT3ScoSU0kY9LdA0mXgV6FBokhYQjqeM -K2PmiQEcBBABAgAGBQJKdzLEAAoJEJaA6+SpkZPiIXIH/iOPaDOqdUYTseD++iCI -2VKTt9GpzTDBbAdZ0miH9DANfFq3QyeJx7lGsq6WJyvh+aHXnWyUlfuwz94ExzN9 -u4q8oqZ8EwNJfXJCA80tAyOJE1kA2Gg/+m70uASAIPWyiNgsdsD7D8PiMfHDVjfb -QisiO0i71iRnTp9ShyjMcvUsCbyH1rEJ+m0BZdHnc1MNiSFV3Oiq6CJCr+rzNSn9 -uBKGFkhOPPUzv9m93RV6VCKBof+Brcy9T/FmSujuvJKmJdRstCaAnQIRPrzO2ost -mvopMR0keA4HwIYQH1r+B7uQLM/UidYacziYnDEtAavIaoNMT/YUk2PwtdjLLFft -V0CJARwEEAECAAYFAkp3MtEACgkQMfzn590HlGHMKwf/Yn1wIx0fSNT9lGOF7QJ1 -2nNxFazoTfXCLbf4UTgEl3Z/OH4xP9HaYsr3YEPy0KwCIjunPkgu4GuPlZctVAzh -aCGYFPSS8D+PqIDtT1Ro1k1AuJSEZxvfac0EUBzsaipV2JTS3nYw+7+RYEBldcyt -I8kXHZpL2Ngq74HbAg81f90c4F5yrkQy1m0qrDMgwKJxMd4/Y9OLUW96cgGULZJ2 -mN41NpDgyxXxl6oI+z7XsB9HX2AabJNFU/vJQqBg4hBw5SuvPAXZDVQGk59Fp9tX -ApbqrhZTYq2euv9ZQYReMF0AnJJcMJlzdFkHyLGJJ3xNp9Yk9C6+/ZxajKQUTIiX -34kBHAQQAQgABgUCSnQwaQAKCRDz5BIvHYwmGiMpB/95A5yvxDbm0McKtRzGwvyV -OqzZFPw5RFyG0OlYziif3jVdMdkaeTP/LBbM0xB6gUGFeUa/mvSSvuTApIEORAQH -KjOTe2bo0OPYPZv5yOkVNfq2eqSGOSZN9dXRdZ5jtNFS9mrfgGFKBvMZrAt5JZDd -eEDjKa+HphIPeo8lULuI623CTRWk48yDH/XPtEKOGeULhxreIp7PTxGWcXnznuh/ -qP1G5G4oVCXpT0sVtql0htazNCVaWU7TmneN7S0q1pM/7fUV3QvkTt6RS+XfBDq0 -z8qPuBs56s+oqfWwst4o29YfWXCgnivA7kur6RNW/31FvZOfJDLBLHUckuwxhKa/ -iQEcBBABCAAGBQJKeV/XAAoJEJaA6+SpkZPig4oH/3DQt7PACvSnnAVTzNn3hkQM -zWbyHCBhUrQWWVkiZgRyp31tpUn32xVqIJmZvi1MsyBnrTMGYAUSr8GYZO62oM7/ -FF9xLrkyhjIpCt1plfhBPUxRsEcLhQC6d1yXxZwSudYlqMBCfDEeefsYy2cL5oLd -DSrWA3dopfC8w3E5cXObKffzYvV0V7wPQfwSo6j6BdZch4huHKgXAyYyCp+2V+tj -H5tXDzR64oFLODloJuO5D7Ekcjv/A9IhEQOPblSEIdrrQ7R+Ab5yiMMoYbZFtNCD -2IJFm2too7BKbJQIg2xPwlTnBpWiIBlJCbeq5joO4Q5JY9494vQDENrRqyNkPoWJ -ARwEEAEIAAYFAkp5X9wACgkQMfzn590HlGHu+wf+PfY36tPi4+oQhdbFtLQUMgWG -yuBqlox6VCXFgVBfMauZ3k0bW3QilnT5k9eNpzySQzYqE9v18kNemU2+GqhPhTSq -SiKdK0SWzkLmSXG+M2TThCIMxwQOhR/XCau1EMCXYibYVfGrE77QMjHs6LTkqZAV -1vI7NyGFlApRJYkYDBaxFSNZvxfRaLqEvXLRMZ/s2M7w7VHz9e1VzB3gSrCGSK8e -tQbxflLD2tLwz7VEgJXDG/ILYY2d94VI0rNCuk0j25mmQoGNyPpkRrhqTr/OhFuF -FNUSdxD0+RgqPKUOpq2B5y+SsJrQWeI/3UCJEOr1fXuLKWRk72ANIeSWO+bapIkC -HAQQAQIABgUCSm4YdgAKCRCi0+fPUhNGtNEvD/92AIodOCoMbcZnT8RMtL4x7yuv -szvizzXB8liNQja4WbhImmTCX7yuVHFW7jHBvWEELfdsowkJN66LUr5cjSJIxb3I -5Q6PUYvjmNbgu6aak1bZUbwIaQK4g3d9SjLefnmYbkfqzJk0Mr/vtRChyFm/yvmk -6Vi0WHvUSjsN9TWDFWKMuwnMH5LslDOZ0kD8Ndr2t775Dog1THQZ5RW4gDUpGLUk -0+kwnZpqLbKJiYKLt9z8oynzetcQgqsDBDRAnFGaiEF5nwsVWKt5SW6skkqMupgR -N6fJcXuKJGOpaRhxckqT+j122Ky402UQ4t2tRcQPwBD10MYkRfrQIR021pWc5/Wt -VKBN/ZeQJKIRu/nKIuRY8YyHWl12rzDBr3J1s16R1zks6GhEYKOZsHZ7pPSbZm34 -0U61IvmGR6SGp690U2wp02qOb9njLGXjtAaJj3YUOFXA6a6nJskHvvPJQByd1mtc -Az0BPgvraj82dXboXbr4ZL0bJRZ+0oE6SUzu3cksjxV7RW4N1XKlsLJfVTF3iOeU -/gQcYgqpFfdLLvmX40ZWJNdbF4/gwBdI5w2G/3Wr57VDSxzLwSpDMpa3V+VEMNgb -fz+/q/33qeHXAR3H5SEjUWvJ8AgkY52mPkFh4GJjZmo7H980g3lYJPdw/iQTgZO4 -NCdusnl5FeAT7pLQMYkCHAQQAQIABgUCSnCX8AAKCRAsfDFGwaABIUECD/4iXzO6 -G0LqSFBAv2TtlRT1MfTXoOIsV0FP/HbcJVPm40cAaMxi8ydYEsCnsdivn2NNE9RE -pjDs+dtev6dzRNkUASg3aueyMJ+r+rKIbhusdxTz2llFl3Mmc8bHMFduyCf+q4Sy -jpiYucPrCqWnci1Cp3PzoEvhpALV4nH/SEnqIp0unFwQHaz9yyC0HO2E+1WPcNBr -rNpZUvs31nidIzp1NXlUYWXUAyv8eYkIqbrqXFf65eWuBrT2BqoMbqgdBBZYJWmG -FIZOgK7lz+SGA7givpqkuSLuXUbc6c5NDdpzhMDJ9wMprRpE6QwUrfrMYwVTbNfU -pG1YyiK5S/cY6uK3xsOgqIEg3aPmlKVAMx8NV4M6xAE9zP0KlcQInFtLuT5xn0Gt -3yuPoxm6ctpRrCen7zT2VsZP+LFfytSsHUVc1MS3vQQ7j4xdUprP4855U2dLKqO+ -8kESbD/HcgsiipxnJB1FWNSo3AusEJXOG+qi92YnYJnObvshncsDy7a2rJCcK6Gs -w32cP0DuNkL1d7SsYvPXzz+45+jsDNnWqGnY1cjyAm2WVZQJWpq5LDPBbvdahqA1 -rRSdlavzox8YEMxQkdXeyVC5TChWMhNCW1xA9pajEn9RRI58MCFtV744hYG9Kj91 -0SEcxOt3I+kc9wCOGTC+wiqp1WmA1Vuh52SKHokCHAQQAQIABgUCSnCbJwAKCRDt -hgS8BvWpH6BwD/4nuV2easCh+VPWGqGNMwEGUxa8Pwo9zjHRNyzJAnPkw+H0A1JF -7riIj87jsox3+wR4V4YXE5c2R/er6AW+LzYJmXRw7xwRW1RcYEA/Yf6lgsFMyLmU -BlyG9JsdT5wCvaqnHlb6IdpNBj+XLaPyad57SOc1R8+Lwhu4F10xbszv7oXdN5xP -/NAhhIra5b32lTMMLk8ZbY/AwtYSi2Q/UAIMIIQYDugtT604q+L0wqE88e/TTutd -/dDmgbLOhYgq6A+q46ZAyeb2YB2my4KrUh4o5/AvnyLO74R/+A0SALiY85L6q2pR -Ao9V/NcrOLlGu3zRV/4cLi5J+KLG4xs9h9Q6ykF+Z/CuMKDtLrUN6V0ayKGgQzc3 -fR2T+vuyfnFLe1bV3vx2c7wdC5ptU5wqE6kiX/akZo+PRN4epoUh+iQ0Tej61mOt -lR+PorRJhJoMggVZhF/Cu+ZgUDom+rBNXcVx2Fws9tOLLECqyVX34ueOxej/ZhFL -j7DP0Gr17UM/5GjI+LX0N5ygb95AvjqN5Eh4mC1Sczv/yYbfCDZkNISiWQGi0Alb -50ejMUXmqnNCOe8iiTVbpLTr50xOVlSbYOKQ4vI9W4hx0yq2bnqAAZfSivSg/jya -m27olWqbj8K8Di+j2amzExT/oqAjHCmuSqZO4DkD/b4pMt2dCkrzZG1hI4kCHAQQ -AQIABgUCSnHlIQAKCRDMeYZvmUCQhD/tD/wOVBXa40wCnoZAMPHwCPGWeNBOL0PJ -gIYHnmADPS+KA8mbgAuOWRT39IvgEXAvaFNikx7JNjddpRXLzldBL7qXLuM5sXLN -4BfXRTWEQanUOSEDRLaBjAJNiRvt4CAPJBxY1B6BLJHL550hmmjyDkSvpBTt9Y/8 -I1V226ktuVyKaUpLYOhzoZAt2/BvL/qUO7J3haPoZHwrqR33loZLQrPPbTTNtFys -yB/mKeOy2qZcRaMLUE/UhFUiNrzYjQ/5MLfv0pYZLJZVqDhfM1wG4Dr5zyEjtJhu -8EQ0JF2Z0vshfuBr781WHENM5oCCxVL/POSiPrbNISSVGKRJ664hLXS4t4sbtXPO -SGcI+PSQwE8Op8CAyJwlH7OX8EPKlZMgwUW+BMXj0LKbrpJjLZXVRDBaHosEkv/1 -T1Jrka6ibKc31DA+Pwrk61W4glTueZqxyB9aJMokMoQPdMwRS5Yab9YGagyzdhx3 -7uqaopRcxDaxr/ZgQ7Suz4ibGV3I8ICNj0n/N3ZI6gW9mEAm1RPjIzDHVQkR7YFp -CNUg870uSTIstyDPPWaCgrS6HXmIfRHWHpz2AzRts8y/GSBpjeweb6jc/lCTcRH/ -5mzF880MYsgUbZDTK57RIh8yHjM6SCzwnkfXZLo/0kocfv6uPoelquAdChZjSsj+ -QO6cj/VSbO5A6IkCHAQQAQIABgUCSnK2GgAKCRCHL3AsTW4lqKPAEACS9hR8vpgx -AcEmikrMaJl6zStNZEY5ItskurFIOuBzfRIeXAAH/niFlMbZzVQdwM4RtbfeHhWL -xRJ5qfSjnRJNHse73jri3ElgYJGrBs/d+5nKPRwfM6FZlp/YJY+LiOt9IiyzoIfQ -QaWyF76nl9PzkXbUzijb8POyx9eTE+C6YLoDULsrvupsCNAgrbmpdRSd2ryHT4sY -jy/cKDwfOAPGEGXFMWMg6DyKrVfpFgkvQWjBnrVhd6IT5ZHQnWCVY0uTya4qwW/G -Kzp250k7CJHsRZiL//69G8w7L8R7qqdfWAD6KvwJZ2DebYuYNS0HWtYlUAng24eh -zfo6I8qFYQ7Pl7QYNzqCatc8Vo6DbMnAckkxODM8k7j8xTPZNEBqFc1j1nRfOwRi -fX6g7HnRCn92v79aRNJecODlYuuDC73bKGIN3kKy3oxC28iLiUdfsVaufyoaBUla -x6jIatKwHxaYYgPlOKsVQ4FTZcsqdpV2poCSOxu8GX5R5EHUy/vUqEvbyxfxeAFk -I+oYk0/bMaqAib73uaS1Z8G4AGUXfN5zhlI1Nbid5RrisCayhSsCvAh59UAeC2q4 -340cMNzQlAVPzKk/k3ALTPD9rt+m1dAAtP1kkvCGPr9r0tmh1+j1tbomccpJi39c -7qSQDsoTgf+T1AWUbYH9mn/9lh4wnWBGCokCHAQQAQIABgUCSnVVUAAKCRAdIcg9 -xFLg/BZVD/9yDxjXag1g2nS4661xgTxYy3u+RGCjXMrJd4WHQI5M2PLMZAzW0f3I -fhV+Q1Y0ahUDSvY3lx9vFlFdivnlqYw13fb88aiGMRWnTzV0CRj2kdwSStGLxx4n -cHmTZaNHEF/2mBQIq5TZ2nfOVlIu7RSNVmKsfCv55Js2m9WFdnZhIEVDZdjYIig/ -IV06DFr/afx2hczU4Ra67TyeFSCjIl0MM77WL39kvNSyxyQSeSe7k3KzQAN1IxAc -bjutgIi7WNeVlzuxH5pGtPLc1rRq0RlB5HH3S86Kafv3Yp6SUKJExKzVhdtDmigQ -oJrCL6wFuOKCxcBLP9H2gtIuj2dkaFha1DESuDEDFmjMYsw+Ns4gNCSNWRELaLIR -mF5OfJyQlulP2kKV9qUxoqldD/vVcSnZ2QUkeU8vxk1s9YrXjyvmKlNzMAj7WD/s -ipb+DD05LLARJdiYX3hTESSWhMPpClZQGV3mWbYR4GUeMSgxj4qEotwCDqbTMVzE -YITjlRgv0s6KsznXOwgHuG3YQ3kgXp8MAt0gpgHL9NSa8eIC0BMJ3L1FOiUdDQUn -KGGJ9qbNTLkohPvjQAdzsaEDcNCLCFvCuwBcRr9/w7kn23+Fm7cTvOFxtZK0kgAb -2Nj+4aewPhM4tQ9IhvGDdPM82GxlNq1QWqTXRKPXcRjbbLr/TcpCK4kCHAQQAQIA -BgUCSnV3fAAKCRD2bj5Bn4T03kqvD/9dQR+gw2WQ+Aa7mmYGpJj8hgPTgKR313yh -Ye57xSG9tp9GjY/ThiuSUpviGoZn61Y/SqkOBWWiz6f2kIm86b7XDey+rzyM7P1b -EcARtKkEyS46MilC9wGJO+6tn3xpWcurzpCqvfe98aA31PuyVD9PIahypd1h7S3r -JYeL/epm/C5bEH4hnQ0iiOnbMEp6EQ4vLrPJN3hlsGE2k2xo7ZN9nz5PE+WMS8Pg -183iFX3KE3jpK3eeq4bDEQn2JxSfvvbEfZOPF3CMt04QEDTBfF3u/SrP4qv1UbmJ -gyZV4hBw9LJqN1sEn0cPocFG8JucmDJUox9m8/CDpipCewhq3ZTsrvmZvoQX3+Tw -6TvKpMS5Vf/VyocchzeBggUeZ3i4UXwnOm1tJF0v1TjLH3V5aHYLH4xAmpV3UFLq -eQZ4C0Hul3pWLg+f9qm6WiLCcdAnVq6xWGGat83kFAlehQ5uJhok1BEZ1pZuc0j7 -dcnhCe3YlYUCe2bWGonZAsCPTIkbudAwkv+yi2VT6xyRO93oDQVwj52BApDslQvP -sQ5fKKy98A+ON7PkpKJpndRDwGEzRP4RcFYtsUv70Drz2KOsr7Qfs8wN7A5iUrsk -GQGwRiduZjS6sBvvVs0dBzT58Jkyn1+4+6OL/gVidpawDX5jYvvFR6D7ff+6+lVA -/PkEVsRZkYkCHAQQAQIABgUCSnceugAKCRAMcFV7WgZRPqVHD/9SQmmBOuYxUU8l -OsU+izISCfb1Bzyr/uRe/rVdxs50wEpSik4+/ylEGnrX5n4o1M+chontfjJkTz9W -cHcMwI4Ssv3YegOJSjhS1dszt1qgwybYLlGC6byXVIf5OtrRONnvhC9OwGRjKuu+ -4ii7ANJ2RXyl9cFuSmMuhbfKs0s8v7Zq3C5d6Vf66YwgjxfSzFTky3cXJ3wRiYie -VNjKDsCgCxHREIyX75cdF2upLHiYRY9Y8BiP8w77WCYyIsLHD2QWcT8fkN6a9FED -AAUqB8cRyGPivKwDYCV6wCs7PZcsSCBtgiGWdWcnbicSQr/SAZCa3ND+3ukjqMzF -sX0B4cax3oB7iVPn+uQYdcj5r/DUZcj10RnjxMhNdAGgMhUwfQPETjs1VII95+A/ -+M/TjA0SerfyaD7oGly4puhmDc+e2EmSeYEwFCiPgem0SmBI5OWQcaxV7lo5nDEV -PLQTduMimNjaC/StMQVkNq1ZmEY29eh3pVO01EH/eTgDPCTxw7VIo5tPYp9oPbbg -0aPTq0tjVXIz9bgUAxmKD7RzlAYDJCoLrZp4RsY9F+Crxbtz5zXocmkKyT4O8pPt -TDB1GSSdI3ctQB5ARw+bXzJznA36b2QTTHhS1YFK7vICAsKy0zIDPZKn6/bdJHu6 -evoXqOekoig7ZU5+A2R81VRS+CGqZ4kCHAQQAQIABgUCSnfTaAAKCRCHL3AsTW4l -qKz6D/4uJmT7wfYooVELc8HXMWOozt2fkResvYi8Wnj3sixjwxDloEzJ0hebDdsj -uu3HQYWRMZhU3o5ojXgBdgN0++3BpERFpcVN9iPimI2Je5ROeswayGo00t1N8utS -Njp8jeImIXCThGBO8iA8yGXeIHIm2NTC+QfO/X3FnR3iOB/HvueXQxx+3CENa+t9 -tiso83xY6mz/d3Jsis/kDknQanmHie3TAJ1syTtl1+K8DlepyRAUO8+dAFkaYVAQ -TWh9uHQcfvR15a1CYetzdDBUuup7wB03Toi1BhP9WZlWktJiOmqSlM6/0jwVLyqm -ICEPVxvbKQUHKEaUw02PX7vYMDPupmo5BSxzKHmH+OqHEtk8kKHDLPqKRUQgT2xp -sXQR9qGGHPcUFGM8Y8aZxWYTn3+0PGqhmnWrTBFFR1q+YC/sUwsAV96Xz3yulaZV -ojlsD9yLtSll3JtzU/8FaOZJyxS7lhxWld7vXzsni7eQexU4hSNn4OkULv5fuOGw -nr9uwM4SSIBvJT+HqWVnDWJhfId1KT/rVDiQKpkfwLuDg2ILI0zTMrOs0CKRqup4 -TgQkPcPX1ao8sle0je6Eci5IlVYyKG5IillfEiA9Y/EnXrgMhXPf2LWPqxIYu5SI -31QxH6A2ZTTpNyJnpyOPqRKrmlLsQMvSHdiu1kMwb5sYUwXjeYkCHAQQAQIABgUC -SoLqKQAKCRAyJH+7QK0fpu2MD/9XuKhhVQ71mnP5EFmq24CoOO8MzsZWWuk+aW3f -TD2iyMfjPrB2WOj10kQSygejuryiW8nE4XasoRyFAAtAS6CkKuAl2OFo6VdnRt48 -5r4nK2n/pjkZ2h1N7qITFBvAr3HxpzD0VwegV81hkmQyoHLGHJHyrc7be4ZWkA19 -pHDtZTdpbGbYyA+zu3gZDrmyZRdFowCciSqYfmNSXkZTFGvjVRF6J/N4ZEtL5/Ct -SOv3ft/piFhQbHkk619k6Vm1+m0e/moH/BIWouVCuho/ICvYVEnH3ZTnkpJUYocI -kGBWJUwtli8Ylv/Z9nUNcSyX8x4z54vPCLyh5mwS+Pi4y/y6l2TU2HVZAo0SMXZI -0u7vfiCjksYscWMOgfjv9uuwSvw42bhkp2w0zSlXk1/H+B/mr1I0LTKR87HBSn46 -I6K0Yn3KHPq7rxAdJb/OVoLUhaNGrdZMYWPtuhLprHsFi3mgNSHEGKbTrO1CCvna -nZi5gHza1PnpxItH0m3miR0ZriEeyxOJh1wBx/UTWsQ7aRoCU40j1gX93yNn44oy -mvRA3zSIonUxlI6uW5nomlmiatvYrM6QtAafCGfU2ERcmMOR2widMkjE3QSA+AbB -DLHc0WaNcE/bFJZ3T5VeKPfZYUNNft2Hy1zOxWOW3c4kPFu4AumhEwVkLaKbq8Iw -kDSw9okCHAQQAQIABgUCSoOtjAAKCRBXkw2rC4awZ2X1EACDsUxRFz1ogYhFiVcE -ZOnl2d91nyS+JI4lYh9NtiZPlpzBXJpSJUnIPMiRqtUFQ2rOtCEO/KJaBCo95Cpz -1nbWo+oBH+E/a26V4959fHz8/FD9rzZGFGW6EXjMZQfFv9ump6HmIMTaFSTNe0U3 -j76WH8SMMJ2MqmV1trzuaiG+aZHxEGMSZOtKIxvocoT5nissr9Y5yBJK+zBmhHdC -yajOLaxKAqtU17twcmoAcEcNZegij929T9agffeoxcGsiM66Lfe5mcPHFSw7jzAn -kI2AesP9q15b6K2gZk3KWBzUDsVNsTUnyZxuhWL/aO/inqPiBGTws7rdc0Ygw6wV -5aJRwjz9+ZP8l9wy1CO9aThC4jawrrQMXHGc8uJPTXG6aYwu/YG+XdgiL6mwt7Y6 -AS0khLa19OuiSvrndNTnSGoXvBmZsdUM+t25MS6nj1+fGEB2POmvcjI9R3JZFYbj -RWtERvl8JdlFT9Tn4yt44TWsysquZeIM2Zn7QQl0wbFor80zuMU0wwAkODErsvFc -SM/UdtvUHQH6zdDLYzH5SlFk1Amj+X31ZOOOSp3ggx11n5zL7+6VDdxEsOFOHYcW -UXBxjb3vUrzKbzxvaVIDaqX411e+e5tIQaLoZ89sU2XkaRP9gG28WmTU2FM8X+EK -XRVxY9FxUzzThM9OuwcM7BDCDYkCHAQQAQIABgUCSqQygQAKCRAv+c1ZYSYWtXF1 -D/oCmzfksGqfkvOcPzjOQYeVm8rcT9AdQA5992qCIoQE1BuePghP+WDQcIU6x2+W -0UGAwiJX/sjE4uRJa43i9b1+lQXIXqxpa5JNZci1KrIoVBFiNP1hNfIGEj1Dd+tE -shQPEbh32A5/ihySbcteopY5Dp48aG5Ux1V/287dGAIWpR00X8LA5leVQwf5ffTB -2De3UmckByjHjvhYB7c8aTwW2pDI70bd0j42Ex0ol6LqBvTKOBj9bck5o+4oH4NF -jsR2erNN+VzVjeqhUtewJLRD90HFtE4fayf/mRksTpNHwbGEVmikUsY+lEXp3EP5 -ticjLkCtOPwbJJOBtKifJXxZ3CIEy2IJk9J32cln5Odla/N6bqvR3nrLOgYZX9Cc -kbM5+yKGJlQ0ikzVrfEYimFTs0gz6sUhRKkzo0hspzlb8thTE3F59h3SJVRlo53m -rSl5narg89KamoF1abndmdMQRXDpFpeZra6nkTNIaNR68UJmvYUQemZ8IuGIQ3Aj -aMDm6ReOU96edqmizWMvGnV2lbphAXOhpkmzjI90eszdKyFhXQxc/gzUKTCKeKZ1 -mXECxkaUpfvFmH/Z/H1u7gVd8fRCQclDmkY2k3KpknMPz41haR4KRFRQi47U9imj -nsqQjYrCfG2Cs/v5A49+yCso3P4DQwfD6Zd4LKvNRq8ov4kCHAQQAQIABgUCSqu5 -AgAKCRDrcP7zzfxuT/YgD/99d9KosYWZnF3Pavbp+Y6T6rYXQXlmE6NhfxmLjZxY -1RBZbwkvOIxP0WpGE3Y+US1yy6VH8ICwKfz2yAjApHMOqVeHbLbfuOJ5DzAud924 -xbXyCbWgWp0bG9NUKXdiLe38L2GOoIFiXuppHmbj0ZXRD4MggERiTknEyypIaoC8 -r/4k6CGjo3URGHA+9q1sbtFfr5zTqC/SEoSflRIvBcJGyMSWMq9aZl9SFSnX8pRF -+uY65FMKRHlMtzcN2ZCfZ6FL7lmKXZ6q4lR9pvcmNHxmpOY2IL2SBkhErNzGiJgj -6bsZCB/mzeFPvlsSvgL3JZbqaio3ymJTcK5a4K6kKRGTP0knNt9urfSYrF/0D7Ca -Zjo17dRqLCj8GUd4Hh4SQoYsMl8QzsS6KxSHjqBhcaf7K9nvtby51GFirIyrR8QN -rSEKJLcqQCzbCaiijvfZ9akszfE+zm5QAgK6uIkLM1cvK0zPvgq3/Id+Q5taU7vD -Z1E7ogdbYWnt/huaJf3jwQI0WXHzYR0qj/RYuBCFhotGemkqXO4OwV2fFWAHR/O/ -YzJMkhOntymuSoGqxAgdy5cv+e5XtCzpjifAkBl8RohESOPirjiUXoHEWEKsBjEw -DvrmMgiRYqLhxTff8qQXZBWMYB7H1VfJYmTuQaLYWYuAu65LieiorMXF+xjfD+X6 -BIkCHAQQAQIABgUCSuXRaAAKCRAYFHsHO60rB94PEADJRvedVCzUGOKZtHMBBqlB -b5xB7Cr8PfQRnTN7W8uOXdwVBQCS5icjM4y8Um9eLK8P8JVDQwN/YLev53cs3tzx -Uk5QBlhRNmfOdMURfZBLvwjNQhJAr4NDikipHJpFAts4fgtB1DFARDOLoQS60R/V -ml83hZ4lILbviirYvW23biX+9j7PYwFQxaZd9cN9pTVDQ9rJF+vkZz6Mr4fn9zWP -ITxfyDbBL/rIZcyvevdivb8k9Bt+4nwhlKcVEWU0c5Gm55dZkJV60wqBrzBvLmMi -7/lz6XKbvITKXeWC6YTq6SEmnUxyaXkTvWN0N2t3GHg2zIOkORRaiXNPlYR60hT1 -SYvLfA1IBYW5Kg6TfbWh5PmacIzM8rr70EbZIJbAm/q2Rb3QdQeISdZ81dJOsvY7 -+xYoeeUGsSI2kcBtK98WXCTcp22L4lYQS/ZTKC0PpDo5a1W7Z3bzpvs7771ElC8Y -3B5CygcqyaZW8KlBK226fRCel1u2DgI7uPVndCOFbIqkmx85gFi0clc/yBIrBDh4 -9ftNDJf5m/1sz4lnTn4rFljhxqMTxw96UTAGgRmyKk+0ZFt8ErEpOk8w14yV912g -p1lJfsDK3TKvrStl7W4WJgLuS6LoIRTK1KxhAGqQh2dFCqBW57slnIOprxUdQ9J/ -/7RjJJIY4QnCOqMtguachokCHAQQAQIABgUCS1FdAQAKCRAhn2tgsrv8/Pa0EACS -JooSwtd57MjnOQIV5BcQEOo5GsFIQKxzSs2j8WyZBX3kI1aY2mw4tLvYOLTkUdXR -o+ssGj7mQQ4Pie6zIKlsEQtz41kO7u2xLftcKNmycDS5lnPyiWUSWKGCSrYnYIyq -pjsJrSeP0NumEe7y238r0BnKnaNB6IZgjgZWrA8gfvNUTldc1S01hx36O0K5U1uq -NIjtM3Kd4FwjqOGIf26GJO3MtKFAfuOxnraLNqEvsA1Oi4pMF6PdlWhUgBgho5X0 -JyXgxnnkZ6a7yWqXYxjQ1UZiNe2uI9OrHrqBWBAJsltXgOuuHvugnrXhpEcsXEA0 -BJ71jDtAU/CGRZNsIXYHLnOtTCgik7JoQ0YWC3IG+GMStKPn8ub7RUn+4TRVxIuC -8TSmBc172EIG9ouQ5hwlT58qO+A/98XiFt67V0pnMVrbaYZ4Mws7hH5OdHIsJN51 -7hXz5uQ4wLRoZC0vUoLXztemsI6MZXBWlXNyCZU1g8GIN18F119KNfXCrSTcX+yH -/ZoWn9JVy9fCLQQABUz0Iwe8BBajoajvbofZOOsQMZ6a9/l+NF7mEFQfHk8s8RzL -kDl4o8ryhEsrxlrvAYBx5PzU5ubxitxXmgYhjrYCrItNpVcHYJkN/+Jqw9ekJnKV -oerdlOjJQLTcGzBn3DkXKmtFqGB8j/c31UnHW0/xX4kCHAQQAQIABgUCS1frbwAK -CRDyNyM2/sOXRYNuEACorKlR2v/6ACpxQgsl6MX10MI32e33nhXumTQs8daCmh3v -53JpXFV38SBrMt+ktHiHNO/tSHJd0UQZDkVcmX3GgziPTk5oD5/brm5Ey9yPJg1l -/IH6g0DUTbqBOBLRxJyzDbva3CmQf/MoWuFbunYBJt3+syMWuf4W5QtsJiNAzr6h -XfsAI7vOx8VkV6gUKItb/51oG3AhUmkJ4SYyPzMqTZZSxRvlpXhC9fwn9F70UlDo -4dpZeHJ4lNYO1meAy7kYrGq9bOREl2AcHEmVR725Mtg84/JcRmRZKguDPPIeKY5U -mDJrJ7KDhx4ayomCvdPbImsOdP8ZNatqmL0QWhWNrlAsyrVXetAqjoKhqW/QKXQN -0QxWR1mrrCWrqDzaLW0Gho3NMyPeyj/lzRTd1+zj4Dohn4OYf+bNdGFC2/IC4iV7 -6OanNnsLb05uvdnVModYoYIJNWoMlOOwcHtQTsG/bBnAIu1iKQV3aZ2ZwUOjptoa -uXC9VKBce9l4goY9Tx9Ob4cGsxrmEzEzTXPvYm4kS0Sr8ioOfi2ElusWteM+/lS6 -5FpMpiF2yiErkLvh7qPeudhzHYN1rIBw0HYuYkAW+BT54rH6SZx2uziPZ2S6b8Ca -m60RSxSGYL08iwKy9ScErvWAo4MFQCQpmQ9/YQQvrLlDZLx4UukOYS6CW7P+V4kC -HAQQAQIABgUCS2XhPwAKCRCKERtcrkJpRD5IEACAWRnlyGRuceCPH6qeGMZ9+CGM -RAns7UDhgJMiDVUIbIuraIfIjzMzrmXvK/jsY0By/bVD3q4CHhNBdVHFVgfCnOFi -fQFpYMfXwkbhCyBokxB6WkP+2AowKr8/rdodKOPFLMm+YlfVa+dMa8nYlwFG6Cm4 -fZBivf+CmgkvrxAn4HeGgw5SaZlP7dec+I8iVvq2r+aWXE0yflJuuZ/w6bBpI+iD -wmgNs9pxll7g4c5QZtwh/2IU+1b7pM64MKxDBTZJl2lpBfOqctjCxttPgfjTpIxw -jw+yp5WwMQbGuUsEf1w/hvWLdYCSOuSI4twcDyCdyickpvgkJTq0QAaQ4NsQr75j -xEIGdPA19l2VF8sBfrGnXJZyWn61/g9O85VCQxAvdgHrbILxEHT/I8+hXR+rmhIO -RGpDPqTmDSBXEh/+Ew1NvWJmV88hzkEHos3xGxLVzm2iM7fyZ463zgmYiQWIr0Ty -OvqLTmamcJN4JDF9q9dxSiX0BrTC3i+mlj/O6B6X9VtbGcFs6xLTrgFZAo11jSAX -Fe6G8/l/CW+A8aoToocxzCJsIrru9aXLcRdQ0GJ9zoFEMRztAfuucSY5DO09pwW7 -8klfs9+2558RBOv/jRRgXQVF0Qfze7JNWPSyagRauXrHGyYcQqhMHocMrQjFAq0q -H9qPO9ADQHa9NCbQ04kCHAQQAQIABgUCS3tQDwAKCRAorTKyGMy4/oqgEACXcFe0 -JGVgwN7rUBxfbMYss1FeH/mCm5+TpoSEh2w1nLSBWmkUQD/u8gsH9cVILqbQ+4Gm -Mru4kOsQzI+oNNBUUxDzPlcQ2BFMrIA8t+RvJGsK022lD0+BYzx/4bR5vtwEbc/e -ZbAkCVGTyYEPZ+eb79zFWHheXtzNb5mKN7JXntdGkTzzzSpayPfpHoEYPQRv4477 -euPaNod0RKd+TgDzsaPzC942U6H5PmryiAc9HNprvPZ+fArMIke4obeLeHzrcnTQ -9fE2XwQcJ5zZ6UpftiMu7dSdCnqxRSNhCNkZSP+Kxyc0U0VFaymrPxGF3pGzDyXt -u1s76shCzX+VBMp/ZO+BMmFi4mXfLA5lyd1ZGHrc5i/augTl+0MRmbbHQVXZLSUW -LH+WftWCwppsU4xkWBeUorzfZFx+qBnaGSQgjDGTezfpdeeQLJ5RGotXEvCPiVAB -c+DTIPQ56KKerCOv3wrCS6p8xbSRoZHWtxJBOjuJlj+Mdn+KOHAlvG3I9swVxlgi -KURgeZ9MyqhMY0/l0I/ARvEyGQxrDO9KPSrJf8nj2jP6cKgPOR25T8flE264s6bE -vfcBw4SLieeILV7wyJv1hBvPbs9GLevQ+yMTS2gY5bay+Ra7iTJKOZ8bgw7t1X3s -tfuH47GteU1cFmxgW71eRd2e1eaHv0PQhqYouIkCHAQQAQgABgUCSnG9/AAKCRDx -ppvkKcD/7sldD/0dIy0elQihHJKK/BWfoVFQmflsmay1GeoNLnyA+5WtAxU7KEW4 -5njaExDZOGKzgeQWXltWa7BjZMKdH7GLpK34SElSZWqTu4IzBv4m3bvI9iXuHMoA -F2xREagxGXYxZyL6wtr9lDzwT+FyZmojqFzxfCGdta9q8T/Ck5dgzZnNtOr7QXJu -PpCTB8vGrHlajNzA1SvLy7Qmh+DkTURB74SAXy5s6COlQPWk31eTrBlSHiQwN82B -+nzIHZR2RGFpYLRt3DQsYvUsyQXQ86PKmepFMMNqZEUF7yhbxu7sB4lRL/6wEblu -HRNmMf4eKgxBAtIHdQ784gJHTJWdfPljgDvrRo3+ou8+zoKwZLadSDOLE//Q6O3l -Pqv4v/motVJ/MGbBxjk4Lvox2PLgYjdazGYzygelnsPWSEsQ9OMpcNEkfxO6jQlv -XSc2myfxKhpoOxvWU9CRx92uIqwu/OoFGAphNcVmBCANU8TuC0avAJM24iJ7gvEM -+vS9UkpAdd4g5qTE5pItqe9Kgh0g0XF/yomRsklBCpiGbPeUSciqKS52XyTnkzEX -OoUmp2hTX2pD4vQSGMhgAS7qTjG7xBZD8S5if2BQA67V5oX3y1X31M+u8MsQxEAC -Mi1MJRFzA4jJEA6bmYJGclwghQKa7ARhiLStBZ/+t1QAWLmKzt1fw0bX04kCHAQQ -AQgABgUCSnMJ6gAKCRA9kIqz8Pv1H0QpD/0QtYm5KOKsDjjbENrI7llLLDugWHxW -QJ090NLKbBcxIQLwXzwhuMV0kkn+jPqTwqskqQAYdPqoTheD8ljiCxMPFaKJlohr -7HBp7qSTuKSWUGfW6P8emzzZ8ynZM79vwMTNgR0pIEu5HMpophYKl3YjdSVkHwGT -uIokEJhNOT4hvisFqLJI+OaAEBRZGOa3LHVWEk1ELCvX1pFvp27Gl4abgqynnkQu -o8zdCNgXRCx9UMZH2Kt4Ej2FoA5vlfL/OLUxWnuWMB3MIusr+a1gu9CFaFoy/73A -ZGUYEt6ybAts44Sj2fV0d4w57n5Sf8tWNtAZLEetn/UosqufiY/E3LywzGEgHapk -G7MUcAVRexIt8UQOyOwgGFhYC04/CQ/NNX2GX4Nt5A2r5rSs9DCpPcvATTBL45eV -ugIaX0rwGElobAoT1DCrURS9J5fNNYeEN9eVWKhr0WCZcMe/zosm4iOA47G6+Aw4 -95fd9XwfF7ybAR3lomcNlOqmKlRSDLu2ql4lo8oWJnfzGfURVQf2PQ0FnZxmIFmR -W5lLBDF76luUQMcB9a5XhauyB7fP1QdhVsORteoVL/lB1eyF0QQvddT5JOSQ7fOc -3fIEbPZCU11eJNjk7MVfqGPdM92oByO2nzvEHqG9sdP2x3PM9hBtqwEb42vqLH+p -3JTdrJkjOCQo4okCHAQQAQgABgUCSnRCbAAKCRDmGQRd8qxymkQPD/42PPLiMYhD -eiAN1l8bpNvm4U7aiWPfJ8R7zJvB3GwuuwYxTXRGanOfqHfSQKzslFJogtbCgvLs -qgl6HVf52SINGxejy9m0woSrkYnytgdpyg1+GrAkRo4cC8VOmowGSzXIR7k3/Inb -V6vAAeLYACo2hiaorQ57MtqIf/iiMpayNJB9kOKVrZgVoTl03+/CbPDMiwoxXtSM -mFxDNGh1p4c704b5tjesY5UHB689PA0lXQBLP9UqRiV+DdJLe+YXVjrvSTXDEatw -/mqPZOEObFd+ZkE/3cXo6Dwy2zXOxwusSshTZznfilWtSqWmqs61MT4SFxgTWxxs -F9K0P6uBUk8HTzIepRf6rjv1inMzQtMuTDfRef+IZiyGLZQnqfyBpmQCXaHJEjNu -ziidjM10jUizqYHdkdypZYP+rxZxaTQSROBiWyeaKX5FEemAPvqHZH7eqqLzsgb8 -gNH3liMmpCPLWiJWeIQq7DJgNkPII2LKhmuxsghD35HJxZYbPxljzq50AaC+AE3W -SvrU8dFEqyk8TvW2RYQy/8LsrMs0qEMij+29nyJ7D1wkKlAVO3BJRN+LUabORh16 -rBWeKZ9A2r2XKIHBT44W63YRMiXS6WBl78VMCS0F6LtH34jBdAZ22PjWG8Ws0xuC -Mvok+lOmE5G5GRfoSurdQfCEhKI5sqT9UIkCHAQQAQgABgUCSnR3zQAKCRDf9zjC -0Wz7ou4sD/wK6sOGmGTEawEy+KeUlz33PyVvPDmNS3zvj4ZMuIAOxS1UE2RdK41g -nljVe+7Z6Rshh6KmEQAKEWrCDC6ZOePDgH/y79HPhSEry9M0m6tvDhj9UaeXr6JG -i6Ltkg9E7ZI5MVQpg/hp9iNxtbtuViRt7NaseVIwFsvJar4rUWvAUR/Nr8j58AzY -B0/aAeLInfALzplmzcn1s5TT4EC2tJx1y5imeP/S0uva0IBbHyA1vTm1s9kygGL9 -x3flj8SHEq0+kIePN1yYIhYvfmYET930PqrawJmqEfjyvsbR+dkVGZnoTAvl+5jT -dOmR+/TOcr8OnkPIQMaeTgZsfO+vkwesYx8VhRVZcXJb7QjxdvTSDM2qtj2Mb28M -X+Rytk5Ux+WXVSPIk9Psd96gsybztj/IPXeIMDQ039AOHwJBT2cItsWcjabR7vuV -UthpUXpv7iKyKfOBAhFXX3jJt/4uJ5kRkiNd4ONPEtJnzQNqGH3jOnkT8kIBca8n -kBvBFqMR22/1jxgGQM1haYjpM8H0e9oHofYx8mLJgYFpMSAaXsE4Fxv6XAIvIaei -L/0lZZLJo8hrco1BcM7rlpUiuzOV/zzPAZ6lLB2OaUS3xodlnZvDazSg6+zvZXzE -CGisN4i55TtT/VAeGsl86u+0ezudTvk/n/O+ixlC8HbRfb2gBtjWQYkCHAQQAQgA -BgUCSnR4DQAKCRC6nHgGHd2Mm0I/EACbl4tdAFh+B8bMqYEVdCQ0tBxlMWqn+v3a -qciz8ibmNFwL3qYeS0LdNVPpfuS2seSymUmPpxtnr6uKHt5Laj0mcolI48bZu9ov -3SJmdP5dxfv4ZRp2Kqa33RCg9f44uxFdHJvYmzL4W+oY7wUEAntlZ6et3DsNJuo3 -erdxTWyv7WmRRDjjpWb6U1QkZGOoKuktCOmRYdAc+U4aMD/GNIS+/X9eCbXWNead -5Cnj4Vh9QSFmt2U3Dtcub5qbrhIxs6UVxAkSwDCOtAsj+a18ETnXqkIYSiE9aHCt -dGsfRgBUlQo/n4dX6cBlWl5mWZi31qH34YL6ArY1zNVOtpqKMXylCcbIFMFQq6HE -eM4Pa+taGLclEbcIeaqr/2TpP+oRDGqhU8hoXFGWS7ON9zyplRZfnhYE6TXHbPQ+ -O6qsI1DZATtYl9AsmonTED3LT+d52R2pXWmTavV6Er3LRWJvvSOjMf0jEYf8pR27 -ZdPQ6Yb+aKA3uZe9yrWD2M322B1ZW0pQob1/Pt/BA/t47f/W+2YalbygnOqBTQ+3 -mHiku0WYPwcO4oR/m4P1/LTExSmeDfWVTeWnJTY9h0nRFMONrrHAIpZlvEljMmFS -aHBKrNDN+yES3XaLsXb5hns26Sjcva4W81qYJbZaGuzPusJ5P+Sqxry5f0vtOl0D -udOENM1gTYkCHAQQAQgABgUCSnSKqgAKCRCi0+fPUhNGtNKLEADTSoggH7QXdfXx -c68PDaO6yYOCuv+AHO8zvzNZbIYUJ7PlLDqh/1DnZikivX19mfwhI/daUKtJHULX -fDDVMezM8R4vw079ODl0JTVCWbU1E6BtGyVzL5iKvcf5wKlaJCtAhHn14sNH70ts -ao7PTMEZn+r0jP8npEh+DuKFIQDk8lgnqZbdxRBYl8JurrYlTLhgIZg3mEfcezeM -qt8FA5FHAf2QsbSi7osQdhuRg/1gfqvWMXRRf3oDrSPRANpOAkOtDoLYEdqjWbNV -54lOAX2NhCOMHOu1sT10AJd/hAnHdztzMoYqM+X+dQvTnC3fOTt9uKtxCbnw8I+Q -Fn8d+CAbV0K2xRtmS94TEotgF7F9fRtG5Sjjlaj3q+QowlJGyn8lIkGahbrONqof -dIPpG2yU/p3tprfdg2OyY7WsLH0vpDzCpM7cTxz5d8vWSUC4euqsZNAv/RulEzTe -j6PBaAhRNWymMNFj4YwONPRXhoQDOa9UtrvUi5U9VXWn5G79GDeG9HVx6dUxNx2X -DvZhiq93xIvFBua1zEAB2+lw51BObXsIYbfEk6294TAmO6VOIThG5oP2sHkQSg5h -XnD1HpdcmaRXnw2AHpQlEC+pDzfr0xsREcDh8NoEs3aHwNvVENq5Qxd6R1N3jhgA -6P+ml4f8vVSoPHf9TwAsLcKkukqPl4kCHAQQAQgABgUCSncPJQAKCRC5ESBTbYUS -jTNMEACWsxgd6NOSe/YYo8Ywr0VH49M0myR2sSoa8kiL5B3QGANiCjzCInDtKSxj -u8Dt8Db9ODCIHqXep+tvIgV60HV4Zr0JZx9nEyHSCq9Vnfb8W8PWqiasIeb9EWzV -oi9a+Ng+eZBUr4YK34n0kQX1lvJEeAGgUe8vAuzJE2Z53O+B61GOZ70Txki06HD0 -xQ42oDPPeUHAqaQVW21bTxsIvjMGt1PQEtgCT39035daCV+nSlNj3245oflk5v/i -m8aBWm4hVnfWvAKCI999+CgOdIQYMVNoWg6JZlAgDZi3DOtlr7ozRuy7sCwBgVjQ -qMiucJhs4YY9gkSBggVBUzfj7L6nd8yCeFwYOK8uX9a16DUE5bO0As3LKB/LydEt -KXaUZDvkyEVgaJ3CKs6/4ViEiI952dTVxZSmcGInEwaDgwva6pr2FSq7wFnSe7RY -ZAwGQH7mZjwVMMBdES6Zmv2cFGlW6FZ/WMXeHN4O0JkfjWyKFLlW5T7u6/yuRyE+ -kx/VIPqFm8bneunVvrobptbehudCBgHlpUBUFOwIIa+iXBBwTEYW5B/fk2Lcqi0V -UiBmmoulZomnYwNFXMIW6iqbUm4qP5fp4LH1AmgL6lBeL9TBQyrA/Lo5CpRomfuz -/J+gi9Yk9s8OFfu38aBWOaANtRXFrXCRAeqmLG/m6+3Llfq2IYkCHAQQAQgABgUC -SndNYgAKCRAsfDFGwaABIYR0D/0Y2Ejt0AZGz/JNPynb4t48TOO3CTqIbiIx86uE -SmUK0TUs77+rv4cKSzh2ABFECSoQiOcQekxD+xD4tNQp6TwR3zKq6qKh2UK7va13 -CPCTCgcx5oXnB0hoc5reykzkg+XgwzvmEqZyQDuQdPYpJBFFnwHJfNBm2IXmw+Vu -0zQVPX3r5SGqWiI583ECPVOWY/QmNE8XYiTaECol2mC8jMwFK9vC734QBpcdvI37 -mdlyN73fKYsA3RiejL4BepFBk7LWjTOPScZOhnMQR1AhaL+al9eYK8aF7GT2PARm -fCRT4ZW9qnOfoVlW1gLp9hCClRxhkkcR4zO4Hh9drwrg9hEBei4u/GbjcTgbCsjF -JcuvpitR81NlUNqYRKamoNm5LCG3kykl4nTtNkRFy5nYxdTKTH/sgcdOhwlHBuGy -BMZqIgMbjtiDng9qh9pfE919cX9mNC9IuezEyduKsovHv7vJOdQWi6uvHZ3uKioV -nBchgniCSYRI6Bkn9HgoxjRU1xspTeEZAoBB6XcMYLTXsNtBN0cptYxVjFQ4OIFf -+MRvGFBTgOiMYmufizwwtrBXtljaYrZdhyHS/I57H1FnZeK58FZThLCt4lHyOoyV -YjhPnHqav2S3cAWwOxAeF3XZbsy9xTMNsuPhwoA5Yely+an581jtDoZ+2/kVu0eE -4b+EQYkCHAQQAQgABgUCSndgygAKCRAMcFV7WgZRPse7D/sHmHpUuFvVrqp4l/yl -5PHl8Ta6tS/KK1Q/GAH2n37lYDbhg5eI3mscKigQBpe1LMvmTdz7IWjH6pxAmmzT -Tz8/lu3x15CwrGnsDc6KmD0Vpkml8T3ONeT9xa+NdbysMnevbijZEib86yU13hGH -t/YZc0Zi+BwkDwO4FElEjwu9DBT1p0APeHG1mQl5wQJLFcDukKEZr9J0RqJ+JzXa -z0ha93zipsONYxlGhPGc2TH/faQB+KEuf5c2zoTt8JHS6ym7GZVikhGn6WqHPsRk -jvujv0pjpJGFYVwIgFo7S/TNm36DjkuDJulKHzo/Dc2wqTj/1sfhhDPL3lM5xL6b -usu12ZU3s34z39tOtR+Aika5+hot9+ienvcgSDgWoJTNIpebo0bPlJqvmNb1O1/z -PevxLzDTOBJBtoFlx+Jtt3KsPeZDlgBPAa79G1d5lSsaKvplILjbORCZlRs8wq2u -8q+hzB2M4z30A8D2t4B5zrujtPGW4VwUY2brjKfNZnbwgldxj/K/qr70lNn8IuYG -Rp4jSFO5aNZyDSKpCi+bSQEP5EhU5JGUPlaD8OgABd2+JhqzjtZCZLonweFMxNzd -mcYPlRZKKSobRibSyVUkS5UiGtcAuW7YFJCHFvJpYEE58jXRC52XfVQwufldOEZA -uoR56LL3Rs17jyC2R9UIAL3IyYkCHAQQAQgABgUCSnmQvwAKCRCHL3AsTW4lqLUk -D/4nzV32AonuxbO6uFQOP37WBZ4kTjF2xwWr8Hy/uf67l0Yoc2GVUjWrzAhtYw7J -RRur/t7KlWLznwFIfn7ZA5Fba38pBrN2PJoYciuoHGL0n2lXaMg3zcoArzF81ja5 -4haR412DJY1QDx0phwKv27EQiAcxpseRwZqKoYsKO49p6WdbCwrde4fwS1mbyPsY -qXCCYRBOIzSm+k4gXdD1jnHLhbumPTf1rksDv/TZdukMD/Rt1J1whzj99QfDhxgP -EYlBIAb9zSMD9u3vYPelF+AINsPsqhmKf0qJ07MQJc0icfUK8E7Yo0QPIFkEhLBx -4oRIlU5+rj/u1i4t9cJsG+VKRxZnkCkAuMbU3Cqx+oUA63wBTNyHIzCJwdZf16Vo -fCpehx8XzHVEsBD8EKUKkD3kEGjfU5wz+hx9AYYWYDTxSJym3ATGA8qf+NsfJ2WR -3y+vIISb8AMHbpvJOmTiUMsBW2v98IIjumZuxggsKYj2zY7aUvcx+YM1YKppRgft -MTE5Ioq3YolTdkpuuQEIENspbNjZ/I73H6wzDIjJ5q3wTtIDCQP12NgTxeq03hMr -qc54wasyZwxlhOqNGKBoF3WBT86bvvLeCA1j4uNknz2VHPVgq23LULOzJ18Ak+QO -QbF4ZCq0RJDfTlVhhCfPCOdN2k51/Tzrg/PcZoapJGU5m4kCHAQQAQgABgUCSnwz -jgAKCRC7OmgBhkmqBiH0D/wPg8zhV9RRTDn4SklER3RYG/Gtgv6L1IFbhLFLFWVL -EfwIN6QSN92vHsMrMCLvWZ4X4jmF1rt/NvELgDWorTXsSjXxBG5vX1PX2maKZ/zt -D/pMt3qFiOjy/MWxa8+0wI4TvRlUcPHRMaF+SZgWD3PKEmXn3phEJ5Wn+EEbrHIJ -6JVu8Z2tH4u6yGx+yCke5TOGAmj9Rgbhs/I0+L9fDqWFt7SFPqgWShtHvZbh/x5H -SvHdrP4MgU6AzEr5yEH4loyVNdrHZMxD0wUsy56Zd3aKhNcfkm0SUlnPGFeNDfe9 -C7bgqEgKvoqOpQR2m2KZpI6BhcGmUtLXw9HjY+W4kQx4YdGaYpEwjEFg5ueVUYmn -KE9/ckGd7p108Z1cTWjuSUNaLbp3ylZkxnS0yppTCZ8DGuTMb3REEEn+MEDfirvk -NL4pMN8zD/buyMTwMoXYkWZmqbXUqV7fpm2N8tlkFlbKIiGlDyQUkF67S51l65oO -9GUojz5fTAOjIRP4Xg2UIaVlH4n1Z0SB5WNlDnzUa2ivwNV37iYKS2yfSIbT/vI5 -gdD5yI4DtgWpz29eyyVnAKL9fZ4MI0yRTzaFVTYhURDFkyWe1rZXfI3w4n5hrZBV -5lSLeVcmsEsuFTAvf1X8fSD3RwjAti6GbVVQKMIZXzbeIUe5jZ2pZCV8MacSGn0a -tIkCHAQQAQgABgUCSn004AAKCRBJw7+JJ1U9Lj0fEACw3cIdCMSj0k321bUyowi0 -V3W1Q3hO68Ts1w5a6oXvzLr2MpWSjqaglsGq1Toxib9Xvyhky/mO7+WkK9dagzy+ -1k3cNnPD6O7MVsecdgBviLKJJVEK3ByHdvzZXAkcMOksK40/+aNJxwDAuhaTb3Td -IFCZ3jQH33dr/ZeGJk+yK66ERvjYF7uf+9dul2b0jDjWgof7w3hdjWjvHdRfkC8T -dsttPSsMPaeUefWxrjaTqJzFxG5w9RrkEx0MTKd57UKu/yVAO09M9F8QWWP8V9e1 -+iDagCxUGaF0MpKI+/2TAgQMiNi5Rhm7zgc5Q2MDUK/SUTaDftSkVbSqLo3Lcq1D -EtFTZXOT4B9xmcroeZgdicNGNwcSn9a375/Rehgm2NamC25hGFRoA9T+Cz4uNH5z -x3mbitmnnWc1jc4Je/kelKOXiFqP9vaaMyfrFFF6EcKpw6yPSn/ExU3ahKFJzPWp -EBE96Q97P/HMUNn47LaQcQbkEUYJlvdKMFhSnBiRKWv1fLEuagwyXlIR+5trhKgY -D0hvSQ8GBSgURgaDM1EL63Qn/3Lvffr7g9b6ngwgEi+jUOSBS7CyVj+QiCtA34L/ -rRaJ+TKmusbBfiAXcs9VEknQhQ7E1+47ftRKQQ6SyYt6wE8VAkzFazgbae/M/yfW -gxxCzEVebYtVE+FAYHn+HYkCHAQQAQgABgUCSvGDjQAKCRBNJJ2bI+b8OhkWEACe -Q4n6H/W5L5/snnJAAujBQ+gl7uvCkSru89uy7mEkWOSGMM6UU8BRP9lqmoBPqtVB -pKgTFjk8QMNnle5OwCRTuRxHxbfyWKhq+FXpux1uQZyqXSNHhdxciENpLPDjVk8P -Ldxu+h0OcOFvM2MxSagjgdVsJHEnbI0YLOcwAfffG/ml8ajMcu+trxze7sF0v/rF -pRXPZe10Am4OX9T4TXeGdkq7OLx+gG23KkHwq9dXT43BwKL/l8EbIOTi3lmHFkmE -BBdtyl9y5UDg3IMXN3Z2APJQRPFyBmuE9m5GuUMBgL+rAf+lKs+gRQoij7iPahY5 -kN0CYUvXHf6QBhB+tXUvNAaV5rWOzAGbM+rnRTNrtiZlTkuPb4kJbHLWGA8w2zI9 -J85v7+D/hlduuMYZvm4w3QW6sYRarLD9ttOlshEbGJLb6ZJwctjUjeJTIS2iqn1d -ZpFhTTh1GRukmUrrnCQNxtiD0vkPo6jqEvKfZhx5wiqqNzfhCXIJZuJFiJyNgF+a -3H9L9Aeec+BBDC0H7vZMbdrAwq+ogbnIIeB4mf1bfMWWfFKiI9uI4qbMlAoDJgoF -8zeFu0xMQJYyyGCjEJMUTZ3OcKioBb108ol3iptlqJ3VCjSXAAmb4xJ9NyFXx0gY -tz2YOBVCkvNAiHxlLi3WedwVgfihK0EE4EMg2M3njIkCHAQQAQgABgUCS1wUUwAK -CRAWKB8uAHyY0f/DEACbw7fDaAh+w7QgsEGnXjaF46ILKoByv0dzhjVkaYNVkDv0 -p0y05KtR9Exd9ADeFTENwnnk0m0aZ/m4vy1QbXXoOGJpgpjmXv1j083bLwZgzCbJ -KLRKQLi7Hr0Aj3QjjOktY2ejg9in+ne392dTA1rVaQR1ZtYRF22eYMOu0cqHSMzd -L/8wLpwftL7WpFKrtUP4dcNNw2as5iewUmBL07PH2lI+TjmOxrQa/F9D2jyStHds -Xv3hkGg/VZoMJVSa075ZRXIcVCQE2h6mwI0CVYJDkV5WsFAnHTF5TVAcWAIr2UbD -jULP9NUuLJHT4jwpBj8tGL+nv4M53g8BL9aUc8NgG9D2ueoF4yL9FuwKgbdBFmV1 -3oH5A3S4hFfZymi0Q6CYKhINubQ+27LZ0qiCtlImP66BluEFlp4zjbJQxt2YudKz -qHvAc600QIK1uMXB74KoPJ4JeD41voLZhvB2G5rxwBaeVunL7EvLDW0W+PpCuQFp -C8tIv1LTrOg9/sXxPT59d8j2XZIwgvDCEuIrtp6Ey1bO1nLFUAz0oo9O56MnNlQj -+P9rt3sdjjIYIIISWFgvlvG587riT6X8w7qAw4YX9iWUrO/QXEqLmfDmPsnPOZVC -vAFBbu9Zb9trrSVV220YA3VPp8C8vB8ls91ctrnECCqbO1QBkuEbUqmYk9RQdokC -HAQQAQgABgUCS11JeAAKCRBQrnCFxr328KvFD/9bSV1+Fn6gBgrp3ChETIFSDnKl -79CBT6KdxgQVt1aOpNWvkZJb7In0j8dc6NedxHh8M2Tf8QRHRGu336w9I8U5OXig -MF0iX3bR5ZxC/yy9txyJo+1pEW/7pFPxRRmdw5ojej6NacumNkHcnEDD4L9EFbwm -YTCYLdiWVXCg4VM84/3GH0xGVdmMuFJxhG0yOVzf0idb49JYvi7UkUPVxcB6AHFm -+ofl30ugYMD1o6JTzW/prQ++Oj1og+WxfmRWMn4lFHEgOxIoXdlvXGuv+7EcQ8ef -PnJbEAslwkJ6yUvN8KZ5iurYPEzaZ8ai2ADqTYzWfRWFr93/wS6zRZA+RhRENATV -IDrDwiBpw3YrCG8X0nk+R6bheUQ8Xb977JiUcOqhnapZZgDRgSce1rUUeyggiGke -yeTp7HoZc2D9LSskaG1r7Yt48SXh2b2yPL53bgUCLeqyzRZCx0m/GcisOtvVtbLL -JlORYjZyA3kLjvvmkDRpd22+Y2hJ3+Bh94q+hd1E+JJ4vEwIGQsbDcn71CLRk98e -hXfMydUZuGFxiLFP0uZEYPK8R4OWrPnqBsoc/BvzogCPUFS+HjaB13SdzyefCxsP -y4xVTg1wITXZTq2ZRH/h4J2ph2D/mlfB5r5JOh58Sdb4vihj6W9BBq221jxIw2da -nUmmwGR4mfvrp09QPokCHAQQAQgABgUCTGISIQAKCRDlYr2UvwNEXsknD/wK8T5K -3rGzGAB5WPusM1WoioNUkO3pID26J3HNuG/fBgu0oGgxXEgwd/RGITtqy+vgWf7G -hBBMx+xUnbZVNP4pAuYgezJYp/8Mw7RpiF68s/Zcq8xZexZmYW3EuJRRTUFWlYix -r2R6WLo33UCXJGicC9B9wYXWel/Yz1wTDYddby1dac+GAqiyeqo/vU2mZTUyWJaF -iBjsEmsA8B/To4T00qn0SRnGkM2BIhGDslKpeNxgCkikAZztxKCeFdU76PAtaTxK -IZzxP2AIbrfySxyAm2EZ5K7G2EGbY80z6p2HokMpck9FqWeBp2FOTaJpEJA3vLkN -RkTBaZhWKu7uG3/RSGnVbmjb+wspq4lLGdDkwErMqumHpXriK8kEPtY+8oWPJ2G4 -EtM0dU/wuFs40ymougVFZKuasrURCHNPriXEGSK7MKsKlRPqN3XWX8iDTgeOqWyz -UIAPUL8wRCK0n+tGpsatlo3MGiaW9IYqazI5Gp1J5roVsqW3L2ty18EtSj6BXLFM -HL+qrDijLctRL7SOQjh0MA1H2e5k679QZ/NZGahBkZlA5VSAgPW1xZ8jbDfabDw/ -Yab1HDoDlwbHcRHH7+XWEOkSQmCKkN82Rn326SCJlNzXgAgPdrcmU8g/nC8T4ibq -B6IR+u+L+zfM5ZNXciSUl3LMHCFhtehHzyznJIkCHAQQAQoABgUCSnWiSgAKCRD2 -bj5Bn4T03o3fD/46qLH/fzYXwNtDEoGU9DYAbdJ8v19cwhyY/ZlTc3PfDYrOlen2 -c0Oi0CfFNGKVBWWCYXComnKac1lzTWl7Obpwwo4Tbz9WVF3ZEzZUgG3MGRHq1nlI -eoFylSVKGkiScdqzjsCVYY9F/+SBM7I3tho4k2OR6yKHJIGyUosIcmFLdNAsjrVV -kX1f81CgPsMllT0HXcV+AzbXt47wGsbahPmAX6z21Kief1PGJ6QhcN6bFNZfU6z/ -WbGY/dVCxpYwpyRk4hERRlhT1bZAWOOGIPEh9IUnfCIXOqXuT7Hhnw5llygJqHJO -BW/zw/HB3cTJo2rGCF7ZRaStpMQvUjr9kcBhfWi7mabfCj+75WQTKHv88xybxmju -cCiZbRWnWXWd9+b1SPjIeRAcH5boN9Xj337wlllSkoVd0d/rqov/eIknjsYE5IP0 -c0te4QyYApTfCGBvea5+pVVMUDcsMQE2MsZ/bsODnhj5n2obhJEtAp9Q2JjEDeMF -eTYKzT9+x/5mbeaZSxerNaGLASMldBTazthaiY3IK0lR/thBBj0rDrYSMtrwRNq3 -a74P3I5U3KM5gTV6mwczDbpGHrKn4O//et6y3re3u/9Z7wmytT11s+eKBaF5xGdZ -R/NBl3V/2k09VyaPwIZ8Es7NrYl/b+fdh6n05PHgvUsXcV678khns4cXE4kCHAQQ -AQoABgUCSoIQgQAKCRATbpzxe100LUmjD/93BiOeX4mqP7zwUuyRx0WlvzdVvjd3 -kMdQNaYp3R55u32D5EiElt1qiK0maD4rmYvDpA52fIh906CEkbqngSxC2rSJSoOL -aNtod6mvP6iO+AXFugHkw4KtVzg2tziljlZN50PB84C/6ZuuqeJs3RAg9a6+6ZhW -Je1bTPvnL8nD8091Kw2SOruBbZBUcOkglo6RrGTvUlfqLIysFT6h5wEX8VgGCykY -B7cAO1FqeByADadH3LQ6Vj+ILIqGkkYJnAKJ85GGNR55yh8ItP3/jBBL2bZlMSWB -vjDLMhhRM3/a96W0mmKL4+vyCpxybvKJGIadLH44+gK9aqHegvzojH7gipKKVK4K -ibMcgxA1S4yKFReYzVV+779sEtcP6XFcvXHQ2ZsHnqyDEJzaroxDnnotz5njuug8 -2tc9FnvJRkvm8Me/LDYXuUffS9zUGYlJAd8OuzKWZzmRwuko3iQ7ARG2IsT/KJ+B -DFu4jmmaSvZaqlgY14TbNKatVRvbMTRd11wDg6dUNRXvM3bz/QQeGvyvJ9nx3j5/ -1c0eL8BTDYyGwTmQyRlAn9mfW9Kz9CnCBVojyMiNIXAN8LFZAfollXC/M5HQ2Fr4 -G4Ht3Izu1+LDpCmYP7WPHlvnYrOMswlL/5TNarMQioLWApKAYcimqgrStLnTZZ5z -12gOE6tLkPyr54kCHAQQAQoABgUCSols1gAKCRCcJ7MTQrdRHS5lEACXD+loU34B -/V2KiZhfletaIg4p4as5srHlv9Da1vlIcymtcScp9uR88HhSO0cXDyHD5zgBWGUh -Ek+ioVf7NDO94VPcte0nDN63aJ/mef/6xveuCfwSE6C7G6TMew/A9rexP1ZhIw+L -vQ+dWGCesKWpm6eBs1ugWIET7NRlhYU1fzroIHChsEBYNIeCLNp4Hza7O+L9DgUY -x5Vv7zzzE3LrER2wXCpaXHgyKPLJYK/mcaPX0bkjK34+FRCAV7NqVHtElh/TW1rW -olnFj6KWC+gbvol9K4vLdyypqIMtrd+bj4PAZliO30t+iiDZIXWQn2Z1l/koNvxk -UrB9mVE7bgP6BOtvkGXibYNAGqn0GhAzFNYEZt6vYuzf+TQ9CHlLhjHGEbuKA+Vw -J036SI6N9b5XvUd1uyQiDQ9R3VW1XmeJGKNdk7mhLVRiHArvoXTOff1UAju2MJ7i -GxvTrQH7GX42D6kuFEnbVuyZuGO6tXRUMXj205F5tMR2zoT873geAIoFgqfR0yiL -mvoJiAhlNkvKm80GlH+cEdMo/ZquCYD48nrVl2jP8NSuWA3P0quONTIsktTYDfRl -6zfoFz2h6mBlnyRqtqoqDkpeGyF9a6tiYV2gp6nar+3OMsuP1nvCYvcWONQ5GfcN -gwkEg3IvAsic0ijBHIVQ/S3mb16XPJpblYkCHAQQAQoABgUCSrT+DAAKCRAyJH+7 -QK0fpnHjD/0QUn30P8UOoZO7Pm7FMGb4yyndiMIovfTJs74ENSycd9VmU+5jo8zv -tms7HnyBEVZ11bHiznojhZetw6cOZN/m/bQlS+mvwRl8ahmdF7aB6CyPQyel8q0+ -MViTKkj7YgVyDqvZOR1eOHdvX3SapcPjGT+DVQFwiiBIc9D03RElKaWCVXmhC1t9 -BYHZr4LEiqitP66m3uJ4+kNk+Mlgtt3fPWkuNkFTM1WP/7GibseoyBLtsrLzBgJo -xJAfhTGZNqxTRusnlFdtNSl1X+w0J5/hRbyyQeO6RBa5MfRnypq8y2JOtbKLuy5Q -eecmSyM9S/BxAZvankShrKgfoS+3kwkLlGoerUBZc3ArR88cWdBT2oZCR15IljB5 -4awKPYeXgpM//OAoIorWb3qTcHMHjP3l+8KUiOYFnKzjm5wKocupBrkGeECXg016 -/33OQiyqiTuevKBp2+g3UYmv/6zCleCJ0wI36GY/DjLb8saNt6UrO0iN54KgDBtk -ZdDwLNQYvOt/VlhJ0GOnZ9RdaBZrCzh+5joUmdlKDBUHKUmspfWb4Hux9CoVgI32 -kWUwNBnQjPPJCphCPqdD3TYYkTf0ZulDLBhBchAMqMAZfM6cBvu9Yox+P+++pY8T -BZJxzWZrxcBVJ7urKtQ0fd+IFmGtuq1rcE8sAhUbhjDd7am2KMbl2okCHAQQAQoA -BgUCS2RgTAAKCRCMv5oyKGGnkHuuD/9RaGOfMXEyfTwylUHFsLeWpeOUKt8UX8dZ -ZEAS7gOvPeDLjXH/n9/wTfKYJoArXI6ClYyeV3TCg+xjZouy88PgpnyVNdFixr1P -ghsrQUCb/K7q5XTQl3Qqc9ZADwRg6qu6lHqqpVaNzOmnChfqaOSDYa56RSZTbRzN -17yertTk086rsnNvks0/FHBbKlZB0LEwcf3LJH+5x+++4jtNre5/KBfnIE+Hh5tC -SUNJSW+ycvaRbke4xiy6Q+JkI5rkpyc3hjUXbP6V6yRx5bi2c54oN+yRn9YLK350 -oDAmYSdS5fC7/2baA8ZfKKDmdbhG3duphaVbye5ME/r3lIDjOxgjFcnsshup93ZY -ul39S7D2zN6IvvFa6ZtBUe4Kb/Wh6F3Y1ATas1WI1jlqQzJ3OuqJ50LcN44MtwRQ -vrnfoaGW+UIfbqUYJxoHQwXr3Uf/173cnyLFWToCg5hUkhUG3pgeuIZB/f1bDmkI -GITD7xQ8Rl6GUwST3KSPycSIFAk3MBYjA8syvYnSg+JIjjnIf2hpMx22vVPBGLl7 -310zgmeaW5hWzwY8i4qd89WsLo6PVV6L721DII9bp3ZYYZwjF4+AK2oi8Ryd97N8 -QZ7KKctDoNZQeufn0jXLN5/iEalt/ET1BuhZHHMN3vUWUQb8XszAb++i2bpRUpX/ -Ie9edkhRd4kCHAQSAQgABgUCSneGuwAKCRDJENkiJRLjx4UGD/4j590c8XFPpyA0 -VrsYfhgpPDlc4c/ffVgFNEmZwAUoMK9kA2LYboryGG4LSuWPWME5ALaycMbhQgFU -frGmURYpOm1WGkKg7XidhVXPktaE9nwyA4pWhbJIibdFOkFR1dSytLbfvCWfQNwT -8JuOzs28AoM6S0HctbVWf4tpPTRzpzZReHOKceY+RQjU/e6m96vgMzDwbDEdwSmD -X1UGT08EuuOcPeGNP5qoM0eHlvgbtwewS+f5zEjSjVeahygqETEBegbwfLcxR8mZ -SK2e3eUOOXi+fE3YKgRDHd8yDztoDqIRdk9nrN7lrH5wMtccIK/TJ9zGYqYWdrN7 -HVT/MOEcP41JXk7bVGSH13use4yU1ZrqecwtkYpZWhIB2NIijo8BCnaZ2fv6cEYt -8Xo1lLYAIToR9PEAqtft4Z35zB2ws/zXLIXgDkELmhYtEBnZHX8M/S4irIGJgzT/ -kVs3OJT9oe8OzwWB+83FQ4oqh4AxNwVLDM8KCwjzPf8epu8tMnI9abhsbyTvypw4 -KSy2A1/Z2BTvIeMoa/KJW+FNvPooTntMXiE5ttiBbdwp1g0oEjH6YlFiaNZ4RDKe -uHlKBooSOa2oAO//ZBVIHTHUf8SVZSEQ9qC6/I2nRkJ9fLLUtl+VNaYQO5yKXEG9 -GqxtOa0TpzQNmuG8QQf1bgivtWNqyIkCHAQSAQoABgUCSnnC1QAKCRD1NqpwER1X -Fi4lD/9PSqMsokk+glRCbftL444/rS8gIGHnwQybZ7GaO/h9rjUXASMSJTWgcZsi -lh+tGpBjz/PpcBfKSRVLbLmSmo/0vgkuDfGiIHW+wl/VncDVW5NaPK4hsmWQerDQ -vv08rlLCJ7M4ldEAM/50sB7nwABK3/seozM4cI2LU+IJCV16Won7ZWhGrPktkbDx -BeZk4s9b/ntsKxaT18ZebU1c9kpzhucg1hpngIYG8IHMTV1DTW+NeURNTmO0WsSm -9PEVNi1FJT6WtkJmcRtCoYBYGW0S9cTjrBY8s9ubL6UWVwE4jifRtUq/QLZ4abe2 -8GQuxmyCWZjO8xxrEgg/VANhQjMzPE9jqovsxRTCNb40eYzk7W8EZGWnEV6czpsk -xUQvkEMQUOXgN6qhDp9eBRfeEhT+JAEhI3kiBIHEwczK+tsWCmQdM8DegLmhdBbH -i5q5zqccdlJi52WPpWmR5FXdd8Lfq5DqI3aipC3JSQGDDHp9ZL+SKiYh+x31xNdq -vQeXPLQZeaBKXcoJ7TA2P0E8VJ7adoiPbQQwvqK5JhgC7ACypLo7orPIHeI1RGs/ -16miJpxLRc7AWegb89ljma/JJ9F5Ev0VHa03ZeD1wDUYzQaP+ZejC3frWo+J0ENo -KR6GkTU/pvLi5K+u410lhDb3LFTJ1ac9KY4mF68PGkB2m8qjrokCHAQTAQIABgUC -SnFm6QAKCRDGh181Qc794I+7D/wMOiqV6hvmUkgxB46OqTs1trjC9ZgZ4o3zGd5c -6OQt0ia/8fW+5veEYJriS9MBA8RbL1QZ0xdGc6QshMjB8tkqcUFtTEcjiOKvVxnS -DeghOa02KPcFtHKIGV+ZhRRiUSq3A+soUmpIowPNukDZGy6rSg3TygM5npXX9wHN -lIwdKah2HSCFgEStBFmoztEMz1BdtXV9Z1Nmp827RO+5hp384mjXJPK7rjCDnqC0 -dHL15Rxt5pRMKAfTi8SThLP/t63HUFdKY2uwdagwyAV0z7Hj8U7Y7Ni2kfhPl+C/ -SevaBQLlUmo4iDUczirnCCE3oFkt1FxfucTo9cFI7e8TdnUZ9WX5rUGaymj5nLrc -2CUmhj91VFiuFk2fGdkWvCDwoo8dW+vDsjXZRavDZ2RRsFSXs/+lJmhfc0oFt2u9 -REOz7Kvi93Anm6ewiaEd/u/l9V2Zr/ZLldflMIOoX8fnlLcxUJBgzluqINCdwFBH -YUceIGXovJ0GeNu0kZM4W9iQoRmny82PxZ243U9Ukfshb5BFDaAuw+IKJgkyDfHY -h4QRQoIMh0MQhMOp2wObDpyfFFdWqABwR7mvOnOjNEx4jHI6+7SvoWcksHSzdhAx -4EjbetdpwrNSAX8BsGIUj7RrBvQzeR+tpXkDDxHcMq4FxRoz90tki1oMHm9WYKFh -pka4K4kCHAQTAQIABgUCSnSFLwAKCRDNSyrzoKCqqtRQEACkegCsO3UfkDDTlznm -A8cOIonanegt2J870Ugmjq1wZ1tu3s8LQ7h5OELlDzqe25l7mPRgjzBYZqGF/k+D -OsjCqrNoJZicE3MHUiWI9HaLDpp36GBcnLcGMm/fu3fgSX3elFaMiVRRA+/ooNUC -EdAqaXR9WeQ0iT2TQMvZkHMBl0KkwjMpumlZcPJzibuIlIDqwKoNl5dCPN5W0c5g -vBYFewnmLEChdrDXzOWpH79nQOzqPBMPNWWzxmV6KMIQrQdBX5MFxdB+b+C44LKS -rKcqf6k4MHd7htXSvR8i3fflh7wKg6UURpS3zI7Gs4k1d2P/tjyKWtgmoYZNdOOz -7JmluHBpvyzKAyEv8zHBwfM17ZlhWlGoQV+56hng1odi3rPPE7GfnME/vqPFOBPJ -KimDYvf2OCu306lLBJzF6DpAcl0V4vePJd91apIT4CRcBJdX7P1YWf9MYHATQrAG -4BDMXFt4siYZDCMn7xuOrIYCb6FKAA+3I0oaMycey+frHlKHBegzox34gfAbcvj9 -o6qvuNc90VeyS1pEJ19HsBkcnIjIeZgUIUxre7l9U5P9YG7JPi4yzRBZH6MIt+RP -F1t0ZPfuCTLCzvwqbyiQe8/Ftcwy5u6RYInezOET+WuncMhSBtubBY4T+LujSJnL -mr4YeAWrswk0XnNvCKbOM5Ab0okCHAQTAQgABgUCSnTJ6AAKCRBYeXlXNEJoTrI7 -EACe6HhXVaVQAXDLYovUamageV9/QYgrIHRz9VYR/ExrV8Kym/8u50/HmfM+ia0R -y+MmH2VnbTIZTEZQzJYT5gnnUaAlknGpM/F/tADZdtTESugO+mRKiVEQ+5uUT1s2 -qJmNaj6BsKyojAYMYyl9lEplUEQ01olaEXm4aA5yHNlwIfJ2jgcOBky7IDIja7Uy -isq9rBqrxPl5o7EMLqL8c5UXeVLUI4pcK5JxsaGVKOPOY0l7J6DvPoYGd3FmK6e3 -tmLzs4bQdcZPoMgYcuA/PAc+E4KGw5VrKKiE/JUnAWcgWtvEtJsmGqWlnQmTZTXg -YpkpEAaWVxxytH8Pv/ZmFSEILO15HE2BEPnsJPCF+wyLckRiNDBIJZd7NJ02+iyh -rDWKNoUSG5JmPhStws/5rxbig18Rf9xtVyAcyLRNZvfSNCIo1eVaUxTZqCKQ84Qd -pUqUj2Sqf/M5l1jMfasJ1h8M51Ua3K/XmDH6EzSFgRTpxTwRw3B0L7ofUMmIVncY -7Fv8r1n9ozaIIhNjtimk7AYvB8kRHzt6A9u8DUmVhZyQo9zmnT4PS6ElJDXxHl8e -VP02n8A7uRxrv7jwbCTZ6u4mH13z437PkzYo1uSLmzr8usp/ZxWSoxlSNGHBsGC0 -l2+B9BvPJNdC+Z5F0k/K/c6xS2FIr5fIptJiuYQjF3G8lokCHAQTAQoABgUCSn2M -3QAKCRDNSyrzoKCqql7lEADCMOpCZw1vi+j5GOJS56yYAlNw0xIR49BILqi2loR/ -zYVD6U5tbsXqoICu091Z5pBMP1VtLhjGfQo8QshJmMCgxhdJHLfMrcfbMRLJutWc -1sTrkVKaBZVyLIOTUdhcv5lJG8bp6JAPl0kjTx7oSxqmrbbUyr30LhaUx5t5SQL8 -iMCLZiVfMru/SVDymN8xAm8vroFFFzEsxxLWcxvh9p+r5XELTgonuuwdhNSOngF8 -zlVA31Us6FIntkVuAL37sB+xzEw+nKw5T0OArAvLi/Oh8FpKe6XmC3rs6OFw1wck -M0vuosqLr4Ye7QGgkg9tT+aIYY9bPl+dDTkNGnp1L1MUAbYaQcVCrnNqqdsBetXi -uaU78hLB+KP2k3QRe6mYrxZgspu8uRsapfa9zfBvVnZEitaqUkELPorKmpquu2Kz -WS/JJLSK0dM+7IDoDoLPlcOCwRmgVrwD2ZMKk4eSMrwaMHaev4UeJXg8fjD4PS69 -+McVqbdjBm5dQO3mYpYTgmDkg+stt2PUniKjYRkGzKyoNn9R4zrmmepnbizVu/S+ -6eTH5dFGNjdLYjHTyUZlaWUelOrwiHH5TPDuO8UBNc1+9Xt5IWw791c9v9XJ5tG9 -cW6Le0k3YzRwCxsZwLeNmg++6tM/j4PNEEgKstZaJlM93lJH89NM8tj9knXtGdl8 -TYkCNwQTAQgAIQUCSlDFIQIbAwULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRAA -gG8r1ymkV+CZD/0fk2SxC7iM6aKx06a4ewVusgMcs+M7Kb1HEJjZSYiFCi6+tIfc -/dHaY+T7/BudrOKNFkPaeubOjwpeR4veRNJux/W8++iQEoY11/mW+xPTdAiskGnL -pGBD7nLRvjabWtLKjJgyfRL8+72DxiKUYdk/OSV5NYEP9hx0Hd49jXSdJTPH6bL7 -WjjcOmNtpPEeuA32KpRus53Ajnx+STvbwHahlnyshYQaRPT7y/pRB3jwizU8gAGv -+subMHSRHS6aCbbqE58LbQrI4TPuN3ZJJ2vW6uzkYonLg8yJ7/okx3jevLI3sWL4 -ieljvnoTpbMmVmrK5noTKa4UmyXntY56B4cU2x/bQEAFyqYlsvIk0RtZ4yEHlbua -hIuON2f1zMDTHkjTPAa99V+/l1GdkinCOMhiotSy6kqUSeCMhA3QYn2FWmssI0Am -dm2D/HFYOx94ZLvJDHgw3+on2MMk4Lk2LDCMMr6WMSJTzi7ngOyK7GA2292YZ21F -b5n5030rAzKXp9xLuCXeOtpbJwA2SXTr6Br18ZI9HDYFF3crEPAtjuHkEe48om/s -czm/nROYxezJO2ClxgiG7uw8phVu1OpLLZrkzw8FPzKsjNBJg+6M5Qhq1+9GUVL1 -wDugwYXuhOQC5CITuFQwSU+bsaDBA4Sa1TFcNgrojTQhyHVxHsEmpNPYK4kCPAQw -AQIAJgUCTW+0Uh8dIG5sLmxpbnV4Lm9yZyBubyBsb25nZXIgZXhpc3RzAAoJEACA -byvXKaRXDjsP/1UZRR4Bnn0iJMYN/NcKPLd/IOzV2y8BrY8byY568J8UElZ3rBsa -WR4MqP06tC8vjz2LT8z1F9q21cPdCceoA9mJeyOq52KDbqzYSahrBkSMgkNdVHSp -g7uY4DxLqxI8nMWStVDpKp361Xgrmw4pRTYE7rzkGgta8OdTNNLKr//Cr5IVhOGR -pL4Dnhii3uTW8TbgU4jzskSFViQ9quNiwmmsmK3DP9XCGZOeXTHj4kgh5Ay4NBmD -HZG4bKIRPGAa9z1pua+ixiNeYoZuN2GsDOJFXYEm7wweZJ9dYzbjnjC6lU/FQn4T -fFfSf6D33fJ+BGXIb8IrqRgovIkebsFiIOFjkC/HU/7rgjzNH2XmWFOaMXL45k+P -BUk82Rzn14PB4c4ZTWqeLUEvnLx5XlJg7yJl6Pw5/S9tL+ToMAbv1qvJURAVWY7h -FMaeGvhvb+kjIbWJBXrVpwLwrotwzkCpZoi+hiqVDsd2W1wXvbkrIzLGoYYR48nO -DEbBY+AXMQnNyDCv6fEVr665d5fWyZUK0bMVm0/eX1T51CKhuzvvE7cZfqCLrm5y -gZ952fJVgW4FmzOz3lRyTcn1fdSkKf8Wh1dLFi5cC5jdEuRgLqoTe4MjzB3QoHy7 -lDeNogJFKnoVUnychV9KgmqKlIiHiw67QtX+Y5m0eJmoiLrlJr/6er6rtCZKZWxt -ZXIgVmVybm9vaWogPGplbG1lckBjYW5vbmljYWwuY29tPokCHwQwAQIACQUCUJUi -wQIdIAAKCRAAgG8r1ymkV0JXD/wNYlJtheiH9/VZomFGt2QPREotwAbV3PTmrZSs -xZcP+Jy+EFTgpvU/otEjvFZuyCDL++gSFICKPic5E6atCuxz/N6y0x48is5EJZDD -CE12JbczoXyxmR4KpSWZ9RjVpBLkCtIIK5KiWyFkVWHqXfCIDxoCtQ5btcvgUkkZ -Hh0fdOp9jmrCCCE4Mkm7JBFI3/l7pAdjvb801UvM09WN51JQopukUcITwTDyq8Eq -wrLAq69uC64uoMMq4WW9xWl1ZprxwuDzY3+41B3clKaexQGdr+YIUHPTgly3pTtE -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 -qkvTpajivF9cziJ6kUL29YjMaJcj0oAuNt8zIp75AAUtOJXWPuHmKLIV+Sn9TIun -2/6FIld2pe8/pN+tdyJVe4llFMPPDQ2tXNVNBGE2pBb2RfCaFJNbf57N9Wa1rlvj -1IAU9epSLMOis1l0gkGt0et0HTp8Uyn+I0PMRAHtqZEY4fygA3tEM+N6i3QZoWPg -uvqcyF/L8BYl5DqURsrWwo8M9+xTsOQpDriH6LU1/PD8p6NNR4HnyeLrrTXSB0EH -6d2y+Y/N8wRiGJthUrLL6gXAIPtWtFSM3uulddkyXQbsxya0Jy5A1dBzDbQXzr0G -mzF64YCW3TWelQgbAXtP9dwlafy8NAxX4KD+bWtBKmw6CrTJANndfA7kUbwL4ASy -4sntLA0NoHN8bKLpAmoIYkx9ndNk/mT3H+mKU4nmVxJnPI5TTC0Isr0LkqK8kmPs -0bOJAhwEEAECAAYFAkqkMoEACgkQL/nNWWEmFrUpXxAAy/9gPSjCfNDfokoj0+03 -BlOsNhEmqUV7GvUC+b8knqvb7+GPojOYS5ILg2C5jfZEaIqcfCx/YUb9MjuDt5K/ -9gw0QYya7qT2QJpcTg3QuhH5M93gox8V4/faSfHdSWYMa1FISYwV+wX83mkj8Y8j -Ud/DS2T3qvXmrBlschBgTxHhA+97TLKySQ4bIz1D0JEQok4tvG1srVb6nuAinzwE -PY1kU+MBwRDMmkFIqUe6BO2msXJfISV5Zca543pRkbQsN9MTHhYxOVg6CGJdzXQW -V66M1gQ2TDaIKbPEV4jWYuoWOhukyl+PZfqPwNcOg/rKumhU6HX2uFMvq7g10kqC -pae8dTrPeBl9FaRYXXgyeYIEvFkOA4tvl9JFocXmc8lhJdZcXxnyw0bH+wzt1KLX -9gJ8cAUcTzepr7ZvpYO8MeZQw8uJO2kq4fI2Hid3zu92s4Fwl2/1WjeHzuDWKJzu -8ZI1tVTuNrbLVMLK/CnTvMrwlvVZzo4Sy8qJzWDzmrS45rmZGzZRApvMkzCsyx64 -EmnHpSlBFmY3XKEY96GyPYyzLWfvOe6v4itsb8uEqpaggDp3QCpUdcYPlq8mDOAo -dQnGPWwzkRcBT+7ueBlm7L4h8Q9sOngeRxxUeba8RFQPl9eXRsHi/TbiB+yMxM3m -V7gI3u1TAcTm2uDQ8iVJh9iJAhwEEAECAAYFAktRXQEACgkQIZ9rYLK7/Py/dBAA -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 -pbD/TAg2ZM4= -=QlRH +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 +fLaRrSoNorBK3OcDxz316cLaYMLiy1B0p5JULBYCHYq9PtkaLjI83UynHUKmUoP6 +W4yjbcUWULoiafkp3Lozw2GMvtcvjh4CjrkTtlr13JS9hbFI0g0C6PMJUIaH3nDp +isfhpegtAGdUkL5PFFyqjhuuo5JyKv4ivYmNBvFLxybffOgVznKjR5tXht04QX5C +7HknwS/XWpbHOy710I/W4E7kHD4ISnrn8ZWXUsiJAiUEGAEKAA8CGyAFAldkVmUF +CQZHEGYACgkQAIBvK9cppFcoZRAAhMFFpenuSqN81XQ5E23sCPoVNBvt+7lFZMIb +YcpY2JJDB+TxtQkHfJb7IfMo/jnmu1moE2RNnj6ydW7NnA6PCeoiTcVsvBOEZiuV +AM6nEuOpYVhr4caX97/wppUpcdm7+DEHXZPupxVbFh/oBtCjH2K9T9+VpA0wHqgG +a0YLyNku6M3YtaxKh57ZJw5+bAKCHvDcQu18dWgqLjxyKJR0lciDq5kvkmial9sC +BIRUlXDRz8J5kedSCyRBmXMSSCfhv6pmKr7mRfXelnCCmmr8sMDm+saLgHw39apO +scaub8RHgV055ys0wPTk5ICSAviSAbAN8sXQ0ohma7pyvPFYRaFlUH6J9SyfUDSU +/eKkB3UsQsBFSzCkxEikwaHde720secbuUYXjO+sIvxM+5yBl09QwPFR1GfYAkdD +PU74W0JgaJ4kYCIi3zyUxkZ+ZrKNsUDIiiI93FRuS17Ol2BJa5LDxvFQdjB6Z+F2 +DPYzP9y1+5FVvA+vRXuDm6e0ib5W840UTab2wLrWXeFofMl1eQOTslRzFbQkjf3o +ZFL7iRdv/6jkSLKZ4AcMmegVWK6rShRqgqF3Xsw786h/n5eo+KAzWgwosJUHEDvf +4F30pDOnGmOH/LCYvuwz4p7r0uD/MTmQjXtoz8rqD70rOBBxToa9jZvwdV4qBHlO +BWhb00e5AQ0EVN+uMgEIAM1D0cNCObwmAE9syUN5KDGOl+4N2ZKIzzTiVWDSl6HH +UFrpMpNS442IDDroe9qlCnR4YnJ7FnjgJYmv/TAuA43ereUNSq/xTQtmuf3bh9Eg +6atYsuINjO8zfaEq3fh8OoaAFhaI7rCvs4GyvYQoj+efXE35nm6WayyO1jM0vTOl +QAy4sHR5AQAHDLBarZ/4L3XzoVuzuG+keYwpIZ2kXrzN7S8PXGYq/+1pryNe5gSa +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 +=B2zL -----END PGP PUBLIC KEY BLOCK----- diff -Nru dulwich-0.12.0/docs/conf.py dulwich-0.18.5/docs/conf.py --- dulwich-0.12.0/docs/conf.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/docs/conf.py 2017-10-07 15:45:17.000000000 +0000 @@ -3,7 +3,8 @@ # dulwich documentation build configuration file, created by # sphinx-quickstart on Thu Feb 18 23:18:28 2010. # -# This file is execfile()d with the current directory set to its containing dir. +# This file is execfile()d with the current directory set to its containing +# dir. # # Note that not all possible configuration values are present in this # autogenerated file. @@ -11,7 +12,8 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os +import os +import sys # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -20,10 +22,10 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__)))) dulwich = __import__('dulwich') -# -- General configuration ----------------------------------------------------- +# -- General configuration ---------------------------------------------------- -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc'] try: import rst2pdf @@ -42,7 +44,7 @@ source_suffix = '.txt' # The encoding of source files. -#source_encoding = 'utf-8' +# source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' @@ -62,72 +64,73 @@ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. -#unused_docs = [] +# unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = ['build'] -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# The reST default role (used for this markup: `text`) to use for all +# documents. +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] -# -- Options for HTML output --------------------------------------------------- +# -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. -#html_theme = 'default' +# html_theme = 'default' html_theme = 'nature' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. html_theme_path = ['theme'] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -136,53 +139,54 @@ # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_use_modindex = True +# html_use_modindex = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' +# html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'dulwichdoc' -# -- Options for LaTeX output -------------------------------------------------- +# -- Options for LaTeX output ------------------------------------------------ # The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' +# latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' +# latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). +# (source start file, target name, title, author, documentclass +# [howto/manual]). latex_documents = [ ('index', 'dulwich.tex', u'dulwich Documentation', u'Jelmer Vernooij', 'manual'), @@ -190,26 +194,25 @@ # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # Additional stuff for the LaTeX preamble. -#latex_preamble = '' +# latex_preamble = '' # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_use_modindex = True +# latex_use_modindex = True pdf_documents = [ ('index', u'dulwich', u'Documentation for dulwich', u'Jelmer Vernooij'), ] -pdf_stylesheets = ['sphinx','kerning','a4'] +pdf_stylesheets = ['sphinx', 'kerning', 'a4'] pdf_break_level = 2 pdf_inline_footnotes = True - diff -Nru dulwich-0.12.0/docs/protocol.txt dulwich-0.18.5/docs/protocol.txt --- dulwich-0.12.0/docs/protocol.txt 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/docs/protocol.txt 2017-07-29 00:14:28.000000000 +0000 @@ -24,7 +24,7 @@ A common way of sending a unit of information is a pkt_line. This is a 4 byte size as human encoded hex (i.e. totally underusing the 4 bytes...) that tells you the size of the payload, followed by the payload. The size includes the 4 -byes used by the size itself. +bytes used by the size itself. 0009ABCD\n @@ -51,7 +51,7 @@ SHA1's it would like. It then starts to report which SHA1's it has. The server ACKs these allowing the client to work out when to stop sending SHA1's. This saves a lot of transfer because the client can make decisions like "well if it -has this SHA, then it has all its parents so i dont need to care about those". +has this SHA, then it has all its parents so I don't need to care about those". When the client stops sending shas, the server can work out an optimal pack and then send it to the client. diff -Nru dulwich-0.12.0/docs/tutorial/encoding.txt dulwich-0.18.5/docs/tutorial/encoding.txt --- dulwich-0.12.0/docs/tutorial/encoding.txt 1970-01-01 00:00:00.000000000 +0000 +++ dulwich-0.18.5/docs/tutorial/encoding.txt 2017-07-29 00:14:28.000000000 +0000 @@ -0,0 +1,26 @@ +Encoding +======== + +You will notice that all lower-level functions in Dulwich take byte strings +rather than unicode strings. This is intentional. + +Although `C git`_ recommends the use of UTF-8 for encoding, this is not +strictly enforced and C git treats filenames as sequences of non-NUL bytes. +There are repositories in the wild that use non-UTF-8 encoding for filenames +and commit messages. + +.. _C git: https://github.com/git/git/blob/master/Documentation/i18n.txt + +The library should be able to read *all* existing git repositories, +irregardless of what encoding they use. This is the main reason why Dulwich +does not convert paths to unicode strings. + +A further consideration is that converting back and forth to unicode +is an extra performance penalty. E.g. if you are just iterating over file +contents, there is no need to consider encoded strings. Users of the library +may have specific assumptions they can make about the encoding - e.g. they +could just decide that all their data is latin-1, or the default Python +encoding. + +Higher level functions, such as the porcelain in dulwich.porcelain, will +automatically convert unicode strings to UTF-8 bytestrings. diff -Nru dulwich-0.12.0/docs/tutorial/file-format.txt dulwich-0.18.5/docs/tutorial/file-format.txt --- dulwich-0.12.0/docs/tutorial/file-format.txt 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/docs/tutorial/file-format.txt 2017-10-07 15:45:17.000000000 +0000 @@ -33,10 +33,10 @@ [parent if several parents from merges] author committer - + -But where are the changes you commited? The commit contains a reference to a +But where are the changes you committed? The commit contains a reference to a tree. The Tree @@ -63,7 +63,7 @@ The Blob -------- -A blob is simply the content of files you are versionning. +A blob is simply the content of files you are versioning. A blob file looks like this:: @@ -90,7 +90,7 @@ per object) or in a ``pack`` file, which is a container for a number of these objects. -The is also an index of the current state of the working copy in the +There is also an index of the current state of the working copy in the repository as well as files to track the existing branches and tags. For a more detailed explanation of object formats and SHA-1 digests, see: diff -Nru dulwich-0.12.0/docs/tutorial/index.txt dulwich-0.18.5/docs/tutorial/index.txt --- dulwich-0.12.0/docs/tutorial/index.txt 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/docs/tutorial/index.txt 2017-07-29 00:14:28.000000000 +0000 @@ -8,6 +8,7 @@ :maxdepth: 2 introduction + encoding file-format repo object-store diff -Nru dulwich-0.12.0/docs/tutorial/object-store.txt dulwich-0.18.5/docs/tutorial/object-store.txt --- dulwich-0.12.0/docs/tutorial/object-store.txt 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/docs/tutorial/object-store.txt 2017-10-07 15:45:17.000000000 +0000 @@ -22,7 +22,7 @@ Of course you could create a blob from an existing file using ``from_file`` instead. -As said in the introduction, file content is separed from file name. Let's +As said in the introduction, file content is separated from file name. Let's give this content a name:: >>> from dulwich.objects import Tree @@ -175,7 +175,7 @@ index c55063a..16ee268 100644 --- a/spam +++ b/spam - @@ -1,1 +1,1 @@ + @@ -1 +1 @@ -My file content +My new file content diff -Nru dulwich-0.12.0/docs/tutorial/porcelain.txt dulwich-0.18.5/docs/tutorial/porcelain.txt --- dulwich-0.12.0/docs/tutorial/porcelain.txt 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/docs/tutorial/porcelain.txt 2017-10-07 15:45:17.000000000 +0000 @@ -1,15 +1,15 @@ Porcelain ========= -The ``porcelain'' is the higher level interface, built on top of the lower +The ``porcelain`` is the higher level interface, built on top of the lower level implementation covered in previous chapters of this tutorial. The -``dulwich.porcelain'' module in Dulwich is aimed to closely resemble +``dulwich.porcelain`` module in Dulwich is aimed to closely resemble the Git command-line API that you are familiar with. Basic concepts -------------- The porcelain operations are implemented as top-level functions in the -``dulwich.porcelain'' module. Most arguments can either be strings or +``dulwich.porcelain`` module. Most arguments can either be strings or more complex Dulwich objects; e.g. a repository argument will either take a string with a path to the repository or an instance of a ``Repo`` object. @@ -30,5 +30,11 @@ >>> r = porcelain.init("testrepo") >>> open("testrepo/testfile", "w").write("data") - >>> porcelain.add(r, "testrepo/testfile") - >>> porcelain.commit(r, "A sample commit") + >>> porcelain.add(r, "testfile") + >>> porcelain.commit(r, b"A sample commit") + +Push changes +------------ + + >>> tr = porcelain.init("targetrepo") + >>> r = porcelain.push("testrepo", "targetrepo", "master") diff -Nru dulwich-0.12.0/docs/tutorial/remote.txt dulwich-0.18.5/docs/tutorial/remote.txt --- dulwich-0.12.0/docs/tutorial/remote.txt 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/docs/tutorial/remote.txt 2017-10-07 15:45:17.000000000 +0000 @@ -28,7 +28,7 @@ SSH (git+ssh://) or tunneled over HTTP (http://). Dulwich provides support for accessing remote repositories in -``dulwich.client``. To create a new client, you can either construct +``dulwich.client``. To create a new client, you can construct one manually:: >>> from dulwich.client import TCPGitClient @@ -60,7 +60,7 @@ >>> from io import BytesIO >>> f = BytesIO() - >>> remote_refs = client.fetch_pack(b"/", determine_wants, + >>> result = client.fetch_pack(b"/", determine_wants, ... DummyGraphWalker(), pack_data=f.write) ``f`` will now contain a full pack file:: diff -Nru dulwich-0.12.0/docs/tutorial/repo.txt dulwich-0.18.5/docs/tutorial/repo.txt --- dulwich-0.12.0/docs/tutorial/repo.txt 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/docs/tutorial/repo.txt 2017-07-29 00:14:28.000000000 +0000 @@ -30,7 +30,7 @@ >>> repo -You can already look a the structure of the "myrepo/.git" folder, though it +You can already look at the structure of the "myrepo/.git" folder, though it is mostly empty for now. Opening an existing repository diff -Nru dulwich-0.12.0/dulwich/archive.py dulwich-0.18.5/dulwich/archive.py --- dulwich-0.12.0/dulwich/archive.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/archive.py 2017-10-07 15:45:17.000000000 +0000 @@ -2,20 +2,22 @@ # Copyright (C) 2015 Jonas Haag # Copyright (C) 2015 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Generates tarballs for Git trees. @@ -32,9 +34,11 @@ """Turn a list of bytestrings into a file-like object. This is similar to creating a `BytesIO` from a concatenation of the - bytestring list, but saves memory by NOT creating one giant bytestring first:: + bytestring list, but saves memory by NOT creating one giant bytestring + first:: - BytesIO(b''.join(list_of_bytestrings)) =~= ChunkedBytesIO(list_of_bytestrings) + BytesIO(b''.join(list_of_bytestrings)) =~= ChunkedBytesIO( + list_of_bytestrings) """ def __init__(self, contents): self.contents = contents @@ -82,12 +86,14 @@ try: blob = store[entry.sha] except KeyError: - # Entry probably refers to a submodule, which we don't yet support. + # Entry probably refers to a submodule, which we don't yet + # support. continue data = ChunkedBytesIO(blob.chunked) info = tarfile.TarInfo() - info.name = entry_abspath.decode('ascii') # tarfile only works with ascii. + # tarfile only works with ascii. + info.name = entry_abspath.decode('ascii') info.size = blob.raw_length() info.mode = entry.mode info.mtime = mtime diff -Nru dulwich-0.12.0/dulwich/client.py dulwich-0.18.5/dulwich/client.py --- dulwich-0.12.0/dulwich/client.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/client.py 2017-10-29 14:58:25.000000000 +0000 @@ -1,20 +1,22 @@ # client.py -- Implementation of the client side git protocols # Copyright (C) 2008-2013 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Client side support for the Git protocol. @@ -36,23 +38,29 @@ * include-tag """ -__docformat__ = 'restructuredText' - from contextlib import closing from io import BytesIO, BufferedReader -import dulwich +import gzip import select import socket import subprocess import sys try: + from urllib import quote as urlquote + from urllib import unquote as urlunquote +except ImportError: + from urllib.parse import quote as urlquote + from urllib.parse import unquote as urlunquote + +try: import urllib2 import urlparse except ImportError: import urllib.request as urllib2 import urllib.parse as urlparse +import dulwich from dulwich.errors import ( GitProtocolError, NotGitRepository, @@ -61,15 +69,22 @@ ) from dulwich.protocol import ( _RBUFSIZE, + agent_string, capability_agent, + extract_capability_names, + CAPABILITY_AGENT, CAPABILITY_DELETE_REFS, CAPABILITY_MULTI_ACK, CAPABILITY_MULTI_ACK_DETAILED, CAPABILITY_OFS_DELTA, CAPABILITY_QUIET, CAPABILITY_REPORT_STATUS, + CAPABILITY_SYMREF, CAPABILITY_SIDE_BAND_64K, CAPABILITY_THIN_PACK, + CAPABILITIES_REF, + KNOWN_RECEIVE_CAPABILITIES, + KNOWN_UPLOAD_CAPABILITIES, COMMAND_DONE, COMMAND_HAVE, COMMAND_WANT, @@ -82,6 +97,7 @@ TCP_GIT_PORT, ZERO_SHA, extract_capabilities, + parse_capability, ) from dulwich.pack import ( write_pack_objects, @@ -96,11 +112,23 @@ return len(select.select([fileno], [], [], 0)[0]) > 0 +def _win32_peek_avail(handle): + """Wrapper around PeekNamedPipe to check how many bytes are available.""" + from ctypes import byref, wintypes, windll + c_avail = wintypes.DWORD() + c_message = wintypes.DWORD() + success = windll.kernel32.PeekNamedPipe( + handle, None, 0, None, byref(c_avail), + byref(c_message)) + if not success: + raise OSError(wintypes.GetLastError()) + return c_avail.value + + COMMON_CAPABILITIES = [CAPABILITY_OFS_DELTA, CAPABILITY_SIDE_BAND_64K] -FETCH_CAPABILITIES = ([CAPABILITY_THIN_PACK, CAPABILITY_MULTI_ACK, - CAPABILITY_MULTI_ACK_DETAILED] + - COMMON_CAPABILITIES) -SEND_CAPABILITIES = [CAPABILITY_REPORT_STATUS] + COMMON_CAPABILITIES +UPLOAD_CAPABILITIES = ([CAPABILITY_THIN_PACK, CAPABILITY_MULTI_ACK, + CAPABILITY_MULTI_ACK_DETAILED] + COMMON_CAPABILITIES) +RECEIVE_CAPABILITIES = [CAPABILITY_REPORT_STATUS] + COMMON_CAPABILITIES class ReportStatusParser(object): @@ -138,7 +166,8 @@ ref_status[ref] = status # TODO(jelmer): don't assume encoding of refs is ascii. raise UpdateRefsError(', '.join([ - ref.decode('ascii') for ref in ref_status if ref not in ok]) + + refname.decode('ascii') for refname in ref_status + if refname not in ok]) + ' failed to update', ref_status=ref_status) def handle_packet(self, pkt): @@ -175,9 +204,67 @@ if len(refs) == 0: return None, set([]) + if refs == {CAPABILITIES_REF: ZERO_SHA}: + refs = {} return refs, set(server_capabilities) +class FetchPackResult(object): + """Result of a fetch-pack operation. + + :var refs: Dictionary with all remote refs + :var symrefs: Dictionary with remote symrefs + :var agent: User agent string + """ + + _FORWARDED_ATTRS = [ + 'clear', 'copy', 'fromkeys', 'get', 'has_key', 'items', + 'iteritems', 'iterkeys', 'itervalues', 'keys', 'pop', 'popitem', + 'setdefault', 'update', 'values', 'viewitems', 'viewkeys', + 'viewvalues'] + + def __init__(self, refs, symrefs, agent): + self.refs = refs + self.symrefs = symrefs + self.agent = agent + + def _warn_deprecated(self): + import warnings + warnings.warn( + "Use FetchPackResult.refs instead.", + DeprecationWarning, stacklevel=3) + + def __eq__(self, other): + if isinstance(other, dict): + self._warn_deprecated() + return (self.refs == other) + return (self.refs == other.refs and + self.symrefs == other.symrefs and + self.agent == other.agent) + + def __contains__(self, name): + self._warn_deprecated() + return name in self.refs + + def __getitem__(self, name): + self._warn_deprecated() + return self.refs[name] + + def __len__(self): + self._warn_deprecated() + return len(self.refs) + + def __iter__(self): + self._warn_deprecated() + return iter(self.refs) + + def __getattribute__(self, name): + if name in type(self)._FORWARDED_ATTRS: + self._warn_deprecated() + return getattr(self.refs, name) + return super(FetchPackResult, self).__getattribute__(name) + + # TODO(durin42): this doesn't correctly degrade if the server doesn't # support some capabilities. This should work properly with servers # that don't support multi_ack. @@ -195,20 +282,40 @@ """ self._report_activity = report_activity self._report_status_parser = None - self._fetch_capabilities = set(FETCH_CAPABILITIES) + self._fetch_capabilities = set(UPLOAD_CAPABILITIES) self._fetch_capabilities.add(capability_agent()) - self._send_capabilities = set(SEND_CAPABILITIES) + self._send_capabilities = set(RECEIVE_CAPABILITIES) self._send_capabilities.add(capability_agent()) if quiet: self._send_capabilities.add(CAPABILITY_QUIET) if not thin_packs: self._fetch_capabilities.remove(CAPABILITY_THIN_PACK) - def send_pack(self, path, determine_wants, generate_pack_contents, + def get_url(self, path): + """Retrieves full url to given path. + + :param path: Repository path (as string) + :return: Url to path (as string) + """ + raise NotImplementedError(self.get_url) + + @classmethod + def from_parsedurl(cls, parsedurl, **kwargs): + """Create an instance of this client from a urlparse.parsed object. + + :param parsedurl: Result of urlparse.urlparse() + :return: A `GitClient` object + """ + raise NotImplementedError(cls.from_parsedurl) + + def send_pack(self, path, update_refs, generate_pack_contents, progress=None, write_pack=write_pack_objects): """Upload a pack to a remote repository. :param path: Repository path (as bytestring) + :param update_refs: Function to determine changes to remote refs. + Receive dict with existing remote refs, returns dict with + changed refs (name -> sha, where sha=ZERO_SHA for deletions) :param generate_pack_contents: Function that can return a sequence of the shas of the objects to upload. :param progress: Optional progress function @@ -218,6 +325,8 @@ :raises SendPackError: if server rejects the pack data :raises UpdateRefsError: if the server supports report-status and rejects ref updates + :return: new_refs dictionary containing the changes that were made + {refname: new_ref}, including deleted refs. """ raise NotImplementedError(self.send_pack) @@ -227,22 +336,27 @@ :param path: Path to fetch from (as bytestring) :param target: Target repository to fetch into :param determine_wants: Optional function to determine what refs - to fetch + to fetch. Receives dictionary of name->sha, should return + list of shas to fetch. Defaults to all shas. :param progress: Optional progress function :return: Dictionary with all remote refs (not just those fetched) """ if determine_wants is None: determine_wants = target.object_store.determine_wants_all if CAPABILITY_THIN_PACK in self._fetch_capabilities: - # TODO(jelmer): Avoid reading entire file into memory and - # only processing it after the whole file has been fetched. - f = BytesIO() - def commit(): - if f.tell(): - f.seek(0) - target.object_store.add_thin_pack(f.read, None) + # TODO(jelmer): Avoid reading entire file into memory and + # only processing it after the whole file has been fetched. + f = BytesIO() + + def commit(): + if f.tell(): + f.seek(0) + target.object_store.add_thin_pack(f.read, None) + + def abort(): + pass else: - f, commit, abort = target.object_store.add_pack() + f, commit, abort = target.object_store.add_pack() try: result = self.fetch_pack( path, determine_wants, target.get_graph_walker(), f.write, @@ -258,11 +372,14 @@ progress=None): """Retrieve a pack from a git smart server. - :param determine_wants: Callback that returns list of commits to fetch + :param path: Remote path to fetch from + :param determine_wants: Function determine what refs + to fetch. Receives dictionary of name->sha, should return + list of shas to fetch. :param graph_walker: Object with next() and ack(). :param pack_data: Callback called for each bit of data in the pack :param progress: Callback for progress reports (strings) - :return: Dictionary with all remote refs (not just those fetched) + :return: FetchPackResult object """ raise NotImplementedError(self.fetch_pack) @@ -306,10 +423,9 @@ else: ok.add(ref) ref_status[ref] = status - raise UpdateRefsError(', '.join([ref for ref in ref_status - if ref not in ok]) + - b' failed to update', - ref_status=ref_status) + raise UpdateRefsError(', '.join([ + refname for refname in ref_status if refname not in ok]) + + b' failed to update', ref_status=ref_status) def _read_side_band64k_data(self, proto, channel_callbacks): """Read per-channel data. @@ -338,21 +454,29 @@ :param proto: Protocol object to read from :param capabilities: List of negotiated capabilities :param old_refs: Old refs, as received from the server - :param new_refs: New refs + :param new_refs: Refs to change :return: (have, want) tuple """ want = [] have = [x for x in old_refs.values() if not x == ZERO_SHA] sent_capabilities = False - all_refs = set(new_refs.keys()).union(set(old_refs.keys())) - for refname in all_refs: + for refname in new_refs: + if not isinstance(refname, bytes): + raise TypeError('refname is not a bytestring: %r' % refname) old_sha1 = old_refs.get(refname, ZERO_SHA) + if not isinstance(old_sha1, bytes): + raise TypeError('old sha1 for %s is not a bytestring: %r' % + (refname, old_sha1)) new_sha1 = new_refs.get(refname, ZERO_SHA) + if not isinstance(new_sha1, bytes): + raise TypeError('old sha1 for %s is not a bytestring %r' % + (refname, new_sha1)) if old_sha1 != new_sha1: if sent_capabilities: - proto.write_pkt_line(old_sha1 + b' ' + new_sha1 + b' ' + refname) + proto.write_pkt_line(old_sha1 + b' ' + new_sha1 + b' ' + + refname) else: proto.write_pkt_line( old_sha1 + b' ' + new_sha1 + b' ' + refname + b'\0' + @@ -363,6 +487,15 @@ proto.write_pkt_line(None) return (have, want) + def _negotiate_receive_pack_capabilities(self, server_capabilities): + negotiated_capabilities = ( + self._send_capabilities & server_capabilities) + unknown_capabilities = ( # noqa: F841 + extract_capability_names(server_capabilities) - + KNOWN_RECEIVE_CAPABILITIES) + # TODO(jelmer): warn about unknown capabilities + return negotiated_capabilities + def _handle_receive_pack_tail(self, proto, capabilities, progress=None): """Handle the tail of a 'git-receive-pack' request. @@ -370,9 +503,10 @@ :param capabilities: List of negotiated capabilities :param progress: Optional progress reporting function """ - if b"side-band-64k" in capabilities: + if CAPABILITY_SIDE_BAND_64K in capabilities: if progress is None: - progress = lambda x: None + def progress(x): + pass channel_callbacks = {2: progress} if CAPABILITY_REPORT_STATUS in capabilities: channel_callbacks[1] = PktLineParser( @@ -385,6 +519,25 @@ if self._report_status_parser is not None: self._report_status_parser.check() + def _negotiate_upload_pack_capabilities(self, server_capabilities): + unknown_capabilities = ( # noqa: F841 + extract_capability_names(server_capabilities) - + KNOWN_UPLOAD_CAPABILITIES) + # TODO(jelmer): warn about unknown capabilities + symrefs = {} + agent = None + for capability in server_capabilities: + k, v = parse_capability(capability) + if k == CAPABILITY_SYMREF: + (src, dst) = v.split(b':', 1) + symrefs[src] = dst + if k == CAPABILITY_AGENT: + agent = v + + negotiated_capabilities = ( + self._fetch_capabilities & server_capabilities) + return (negotiated_capabilities, symrefs, agent) + def _handle_upload_pack_head(self, proto, capabilities, graph_walker, wants, can_read): """Handle the head of a 'git-upload-pack' request. @@ -397,7 +550,8 @@ whether there is extra graph data to read on proto """ assert isinstance(wants, list) and isinstance(wants[0], bytes) - proto.write_pkt_line(COMMAND_WANT + b' ' + wants[0] + b' ' + b' '.join(capabilities) + b'\n') + proto.write_pkt_line(COMMAND_WANT + b' ' + wants[0] + b' ' + + b' '.join(capabilities) + b'\n') for want in wants[1:]: proto.write_pkt_line(COMMAND_WANT + b' ' + want + b'\n') proto.write_pkt_line(None) @@ -443,7 +597,9 @@ if CAPABILITY_SIDE_BAND_64K in capabilities: if progress is None: # Just ignore progress data - progress = lambda x: None + + def progress(x): + pass self._read_side_band64k_data(proto, { SIDE_BAND_CHANNEL_DATA: pack_data, SIDE_BAND_CHANNEL_PROGRESS: progress} @@ -459,6 +615,12 @@ class TraditionalGitClient(GitClient): """Traditional Git client.""" + DEFAULT_ENCODING = 'utf-8' + + def __init__(self, path_encoding=DEFAULT_ENCODING, **kwargs): + self._remote_path_encoding = path_encoding + super(TraditionalGitClient, self).__init__(**kwargs) + def _connect(self, cmd, path): """Create a connection to the server. @@ -473,11 +635,14 @@ """ raise NotImplementedError() - def send_pack(self, path, determine_wants, generate_pack_contents, + def send_pack(self, path, update_refs, generate_pack_contents, progress=None, write_pack=write_pack_objects): """Upload a pack to a remote repository. :param path: Repository path (as bytestring) + :param update_refs: Function to determine changes to remote refs. + Receive dict with existing remote refs, returns dict with + changed refs (name -> sha, where sha=ZERO_SHA for deletions) :param generate_pack_contents: Function that can return a sequence of the shas of the objects to upload. :param progress: Optional callback called with progress updates @@ -487,30 +652,33 @@ :raises SendPackError: if server rejects the pack data :raises UpdateRefsError: if the server supports report-status and rejects ref updates + :return: new_refs dictionary containing the changes that were made + {refname: new_ref}, including deleted refs. """ proto, unused_can_read = self._connect(b'receive-pack', path) with proto: old_refs, server_capabilities = read_pkt_refs(proto) - negotiated_capabilities = self._send_capabilities & server_capabilities - + negotiated_capabilities = \ + self._negotiate_receive_pack_capabilities(server_capabilities) if CAPABILITY_REPORT_STATUS in negotiated_capabilities: self._report_status_parser = ReportStatusParser() report_status_parser = self._report_status_parser try: - new_refs = orig_new_refs = determine_wants(dict(old_refs)) + new_refs = orig_new_refs = update_refs(dict(old_refs)) except: proto.write_pkt_line(None) raise - if not CAPABILITY_DELETE_REFS in server_capabilities: + if CAPABILITY_DELETE_REFS not in server_capabilities: # Server does not support deletions. Fail later. new_refs = dict(orig_new_refs) for ref, sha in orig_new_refs.items(): if sha == ZERO_SHA: if CAPABILITY_REPORT_STATUS in negotiated_capabilities: report_status_parser._ref_statuses.append( - b'ng ' + sha + b' remote does not support deleting refs') + b'ng ' + sha + + b' remote does not support deleting refs') report_status_parser._ref_status_ok = False del new_refs[ref] @@ -527,7 +695,8 @@ (have, want) = self._handle_receive_pack_head( proto, negotiated_capabilities, old_refs, new_refs) - if not want and old_refs == new_refs: + if (not want and + set(new_refs.items()).issubset(set(old_refs.items()))): return new_refs objects = generate_pack_contents(have, want) @@ -546,21 +715,25 @@ progress=None): """Retrieve a pack from a git smart server. - :param determine_wants: Callback that returns list of commits to fetch + :param path: Remote path to fetch from + :param determine_wants: Function determine what refs + to fetch. Receives dictionary of name->sha, should return + list of shas to fetch. :param graph_walker: Object with next() and ack(). :param pack_data: Callback called for each bit of data in the pack :param progress: Callback for progress reports (strings) - :return: Dictionary with all remote refs (not just those fetched) + :return: FetchPackResult object """ proto, can_read = self._connect(b'upload-pack', path) with proto: refs, server_capabilities = read_pkt_refs(proto) - negotiated_capabilities = ( - self._fetch_capabilities & server_capabilities) + negotiated_capabilities, symrefs, agent = ( + self._negotiate_upload_pack_capabilities( + server_capabilities)) if refs is None: proto.write_pkt_line(None) - return refs + return FetchPackResult(refs, symrefs, agent) try: wants = determine_wants(refs) @@ -571,12 +744,13 @@ wants = [cid for cid in wants if cid != ZERO_SHA] if not wants: proto.write_pkt_line(None) - return refs + return FetchPackResult(refs, symrefs, agent) self._handle_upload_pack_head( proto, negotiated_capabilities, graph_walker, wants, can_read) self._handle_upload_pack_tail( - proto, negotiated_capabilities, graph_walker, pack_data, progress) - return refs + proto, negotiated_capabilities, graph_walker, pack_data, + progress) + return FetchPackResult(refs, symrefs, agent) def get_refs(self, path): """Retrieve the current refs from a git smart server.""" @@ -584,6 +758,7 @@ proto, _ = self._connect(b'upload-pack', path) with proto: refs, _ = read_pkt_refs(proto) + proto.write_pkt_line(None) return refs def archive(self, path, committish, write_data, progress=None, @@ -618,13 +793,23 @@ port = TCP_GIT_PORT self._host = host self._port = port - TraditionalGitClient.__init__(self, **kwargs) + super(TCPGitClient, self).__init__(**kwargs) + + @classmethod + def from_parsedurl(cls, parsedurl, **kwargs): + return cls(parsedurl.hostname, port=parsedurl.port, **kwargs) + + def get_url(self, path): + netloc = self._host + if self._port is not None and self._port != TCP_GIT_PORT: + netloc += ":%d" % self._port + return urlparse.urlunsplit(("git", netloc, path, '', '')) def _connect(self, cmd, path): - if type(cmd) is not bytes: - raise TypeError(path) - if type(path) is not bytes: - raise TypeError(path) + if not isinstance(cmd, bytes): + raise TypeError(cmd) + if not isinstance(path, bytes): + path = path.encode(self._remote_path_encoding) sockaddrs = socket.getaddrinfo( self._host, self._port, socket.AF_UNSPEC, socket.SOCK_STREAM) s = None @@ -645,6 +830,7 @@ rfile = s.makefile('rb', -1) # 0 means unbuffered wfile = s.makefile('wb', 0) + def close(): rfile.close() wfile.close() @@ -655,7 +841,8 @@ if path.startswith(b"/~"): path = path[1:] # TODO(jelmer): Alternative to ascii? - proto.send_cmd(b'git-' + cmd, path, b'host=' + self._host.encode('ascii')) + proto.send_cmd( + b'git-' + cmd, path, b'host=' + self._host.encode('ascii')) return proto, lambda: _fileno_can_read(s) @@ -673,10 +860,8 @@ def can_read(self): if sys.platform == 'win32': from msvcrt import get_osfhandle - from win32pipe import PeekNamedPipe handle = get_osfhandle(self.proc.stdout.fileno()) - data, total_bytes_avail, msg_bytes_left = PeekNamedPipe(handle, 0) - return total_bytes_avail != 0 + return _win32_peek_avail(handle) != 0 else: return _fileno_can_read(self.proc.stdout.fileno()) @@ -691,10 +876,10 @@ def find_git_command(): """Find command to run for system Git (usually C Git). """ - if sys.platform == 'win32': # support .exe, .bat and .cmd - try: # to avoid overhead + if sys.platform == 'win32': # support .exe, .bat and .cmd + try: # to avoid overhead import win32api - except ImportError: # run through cmd.exe with some overhead + except ImportError: # run through cmd.exe with some overhead return ['cmd', '/c', 'git'] else: status, git = win32api.FindExecutable('git') @@ -712,15 +897,19 @@ self._stderr = kwargs.get('stderr') if 'stderr' in kwargs: del kwargs['stderr'] - TraditionalGitClient.__init__(self, **kwargs) + super(SubprocessGitClient, self).__init__(**kwargs) + + @classmethod + def from_parsedurl(cls, parsedurl, **kwargs): + return cls(**kwargs) git_command = None def _connect(self, service, path): - if type(service) is not bytes: - raise TypeError(path) - if type(path) is not bytes: - raise TypeError(path) + if not isinstance(service, bytes): + raise TypeError(service) + if isinstance(path, bytes): + path = path.decode(self._remote_path_encoding) if self.git_command is None: git_command = find_git_command() argv = git_command + [service.decode('ascii'), path] @@ -735,7 +924,7 @@ class LocalGitClient(GitClient): """Git Client that just uses a local Repo.""" - def __init__(self, thin_packs=True, report_activity=None): + def __init__(self, thin_packs=True, report_activity=None, config=None): """Create a new LocalGitClient instance. :param thin_packs: Whether or not thin packs should be retrieved @@ -745,11 +934,28 @@ self._report_activity = report_activity # Ignore the thin_packs argument - def send_pack(self, path, determine_wants, generate_pack_contents, + def get_url(self, path): + return urlparse.urlunsplit(('file', '', path, '', '')) + + @classmethod + def from_parsedurl(cls, parsedurl, **kwargs): + return cls(**kwargs) + + @classmethod + def _open_repo(cls, path): + from dulwich.repo import Repo + if not isinstance(path, str): + path = path.decode(sys.getfilesystemencoding()) + return closing(Repo(path)) + + def send_pack(self, path, update_refs, generate_pack_contents, progress=None, write_pack=write_pack_objects): """Upload a pack to a remote repository. :param path: Repository path (as bytestring) + :param update_refs: Function to determine changes to remote refs. + Receive dict with existing remote refs, returns dict with + changed refs (name -> sha, where sha=ZERO_SHA for deletions) :param generate_pack_contents: Function that can return a sequence of the shas of the objects to upload. :param progress: Optional progress function @@ -759,29 +965,41 @@ :raises SendPackError: if server rejects the pack data :raises UpdateRefsError: if the server supports report-status and rejects ref updates + :return: new_refs dictionary containing the changes that were made + {refname: new_ref}, including deleted refs. """ - from dulwich.repo import Repo + if not progress: + def progress(x): + pass - with closing(Repo(path)) as target: + with self._open_repo(path) as target: old_refs = target.get_refs() - new_refs = determine_wants(dict(old_refs)) + new_refs = update_refs(dict(old_refs)) have = [sha1 for sha1 in old_refs.values() if sha1 != ZERO_SHA] want = [] - all_refs = set(new_refs.keys()).union(set(old_refs.keys())) - for refname in all_refs: - old_sha1 = old_refs.get(refname, ZERO_SHA) - new_sha1 = new_refs.get(refname, ZERO_SHA) - if new_sha1 not in have and new_sha1 != ZERO_SHA: + for refname, new_sha1 in new_refs.items(): + if (new_sha1 not in have and + new_sha1 not in want and + new_sha1 != ZERO_SHA): want.append(new_sha1) - if not want and old_refs == new_refs: + if (not want and + set(new_refs.items()).issubset(set(old_refs.items()))): return new_refs target.object_store.add_objects(generate_pack_contents(have, want)) - for name, sha in new_refs.items(): - target.refs[name] = sha + for refname, new_sha1 in new_refs.items(): + old_sha1 = old_refs.get(refname, ZERO_SHA) + if new_sha1 != ZERO_SHA: + if not target.refs.set_if_equals( + refname, old_sha1, new_sha1): + progress('unable to set %s to %s' % + (refname, new_sha1)) + else: + if not target.refs.remove_if_equals(refname, old_sha1): + progress('unable to remove %s' % refname) return new_refs @@ -790,13 +1008,13 @@ :param path: Path to fetch from (as bytestring) :param target: Target repository to fetch into - :param determine_wants: Optional function to determine what refs - to fetch + :param determine_wants: Optional function determine what refs + to fetch. Receives dictionary of name->sha, should return + list of shas to fetch. Defaults to all shas. :param progress: Optional progress function :return: Dictionary with all remote refs (not just those fetched) """ - from dulwich.repo import Repo - with closing(Repo(path)) as r: + with self._open_repo(path) as r: return r.fetch(target, determine_wants=determine_wants, progress=progress) @@ -804,27 +1022,32 @@ progress=None): """Retrieve a pack from a git smart server. - :param determine_wants: Callback that returns list of commits to fetch + :param path: Remote path to fetch from + :param determine_wants: Function determine what refs + to fetch. Receives dictionary of name->sha, should return + list of shas to fetch. :param graph_walker: Object with next() and ack(). :param pack_data: Callback called for each bit of data in the pack :param progress: Callback for progress reports (strings) - :return: Dictionary with all remote refs (not just those fetched) + :return: FetchPackResult object """ - from dulwich.repo import Repo - with closing(Repo(path)) as r: - objects_iter = r.fetch_objects(determine_wants, graph_walker, progress) + with self._open_repo(path) as r: + objects_iter = r.fetch_objects( + determine_wants, graph_walker, progress) + symrefs = r.refs.get_symrefs() + agent = agent_string() - # Did the process short-circuit (e.g. in a stateless RPC call)? Note - # that the client still expects a 0-object pack in most cases. + # Did the process short-circuit (e.g. in a stateless RPC call)? + # Note that the client still expects a 0-object pack in most cases. if objects_iter is None: - return + return FetchPackResult(None, symrefs, agent) write_pack_objects(ProtocolFile(None, pack_data), objects_iter) + return FetchPackResult(r.get_refs(), symrefs, agent) def get_refs(self, path): """Retrieve the current refs from a git smart server.""" - from dulwich.repo import Repo - with closing(Repo(path)) as target: + with self._open_repo(path) as target: return target.get_refs() @@ -836,6 +1059,7 @@ """A client side SSH implementation.""" def connect_ssh(self, host, command, username=None, port=None): + # This function was deprecated in 0.9.1 import warnings warnings.warn( "SSHVendor.connect_ssh has been renamed to SSHVendor.run_command", @@ -856,21 +1080,27 @@ raise NotImplementedError(self.run_command) +class StrangeHostname(Exception): + """Refusing to connect to strange SSH hostname.""" + + def __init__(self, hostname): + super(StrangeHostname, self).__init__(hostname) + + class SubprocessSSHVendor(SSHVendor): """SSH vendor that shells out to the local 'ssh' command.""" def run_command(self, host, command, username=None, port=None): - if not isinstance(command, bytes): - raise TypeError(command) - - #FIXME: This has no way to deal with passwords.. + # FIXME: This has no way to deal with passwords.. args = ['ssh', '-x'] if port is not None: args.extend(['-p', str(port)]) if username is not None: host = '%s@%s' % (username, host) + if host.startswith('-'): + raise StrangeHostname(hostname=host) args.append(host) - proc = subprocess.Popen(args + [command], + proc = subprocess.Popen(args + [command], bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE) return SubprocessWrapper(proc) @@ -891,30 +1121,47 @@ class SSHGitClient(TraditionalGitClient): - def __init__(self, host, port=None, username=None, vendor=None, **kwargs): + def __init__(self, host, port=None, username=None, vendor=None, + config=None, **kwargs): self.host = host self.port = port self.username = username - TraditionalGitClient.__init__(self, **kwargs) + super(SSHGitClient, self).__init__(**kwargs) self.alternative_paths = {} if vendor is not None: self.ssh_vendor = vendor else: self.ssh_vendor = get_ssh_vendor() + def get_url(self, path): + netloc = self.host + if self.port is not None: + netloc += ":%d" % self.port + + if self.username is not None: + netloc = urlquote(self.username, '@/:') + "@" + netloc + + return urlparse.urlunsplit(('ssh', netloc, path, '', '')) + + @classmethod + def from_parsedurl(cls, parsedurl, **kwargs): + return cls(host=parsedurl.hostname, port=parsedurl.port, + username=parsedurl.username, **kwargs) + def _get_cmd_path(self, cmd): cmd = self.alternative_paths.get(cmd, b'git-' + cmd) assert isinstance(cmd, bytes) return cmd def _connect(self, cmd, path): - if type(cmd) is not bytes: - raise TypeError(path) - if type(path) is not bytes: - raise TypeError(path) - if path.startswith(b"/~"): + if not isinstance(cmd, bytes): + raise TypeError(cmd) + if isinstance(path, bytes): + path = path.decode(self._remote_path_encoding) + if path.startswith("/~"): path = path[1:] - argv = self._get_cmd_path(cmd) + b" '" + path + b"'" + argv = (self._get_cmd_path(cmd).decode(self._remote_path_encoding) + + " '" + path + "'") con = self.ssh_vendor.run_command( self.host, argv, port=self.port, username=self.username) return (Protocol(con.read, con.write, con.close, @@ -923,12 +1170,17 @@ def default_user_agent_string(): - return "dulwich/%s" % ".".join([str(x) for x in dulwich.__version__]) + # Start user agent with "git/", because GitHub requires this. :-( See + # https://github.com/jelmer/dulwich/issues/562 for details. + return "git/dulwich/%s" % ".".join([str(x) for x in dulwich.__version__]) def default_urllib2_opener(config): if config is not None: - proxy_server = config.get("http", "proxy") + try: + proxy_server = config.get(b"http", b"proxy") + except KeyError: + proxy_server = None else: proxy_server = None handlers = [] @@ -936,7 +1188,10 @@ handlers.append(urllib2.ProxyHandler({"http": proxy_server})) opener = urllib2.build_opener(*handlers) if config is not None: - user_agent = config.get("http", "useragent") + try: + user_agent = config.get(b"http", b"useragent") + except KeyError: + user_agent = None else: user_agent = None if user_agent is None: @@ -947,22 +1202,60 @@ class HttpGitClient(GitClient): - def __init__(self, base_url, dumb=None, opener=None, config=None, **kwargs): - self.base_url = base_url.rstrip("/") + "/" + def __init__(self, base_url, dumb=None, opener=None, config=None, + username=None, password=None, **kwargs): + self._base_url = base_url.rstrip("/") + "/" + self._username = username + self._password = password self.dumb = dumb if opener is None: self.opener = default_urllib2_opener(config) else: self.opener = opener + if username is not None: + pass_man = urllib2.HTTPPasswordMgrWithDefaultRealm() + pass_man.add_password(None, base_url, username, password) + self.opener.add_handler(urllib2.HTTPBasicAuthHandler(pass_man)) GitClient.__init__(self, **kwargs) + def get_url(self, path): + return self._get_url(path).rstrip("/") + + @classmethod + def from_parsedurl(cls, parsedurl, **kwargs): + auth, host = urllib2.splituser(parsedurl.netloc) + password = parsedurl.password + if password is not None: + password = urlunquote(password) + username = parsedurl.username + if username is not None: + username = urlunquote(username) + # TODO(jelmer): This also strips the username + parsedurl = parsedurl._replace(netloc=host) + return cls(urlparse.urlunparse(parsedurl), + password=password, username=username, **kwargs) + def __repr__(self): - return "%s(%r, dumb=%r)" % (type(self).__name__, self.base_url, self.dumb) + return "%s(%r, dumb=%r)" % ( + type(self).__name__, self._base_url, self.dumb) def _get_url(self, path): - return urlparse.urljoin(self.base_url, path).rstrip("/") + "/" - - def _http_request(self, url, headers={}, data=None): + if not isinstance(path, str): + # TODO(jelmer): this is unrelated to the local filesystem; + # This is not necessarily the right encoding to decode the path + # with. + path = path.decode(sys.getfilesystemencoding()) + return urlparse.urljoin(self._base_url, path).rstrip("/") + "/" + + def _http_request(self, url, headers={}, data=None, + allow_compression=False): + if headers is None: + headers = dict(headers.items()) + headers["Pragma"] = "no-cache" + if allow_compression: + headers["Accept-Encoding"] = "gzip" + else: + headers["Accept-Encoding"] = "identity" req = urllib2.Request(url, headers=headers, data=data) try: resp = self.opener.open(req) @@ -970,47 +1263,80 @@ if e.code == 404: raise NotGitRepository() if e.code != 200: - raise GitProtocolError("unexpected http response %d" % e.code) - return resp + raise GitProtocolError("unexpected http response %d for %s" % + (e.code, url)) + if resp.info().get('Content-Encoding') == 'gzip': + read = gzip.GzipFile(fileobj=BytesIO(resp.read())).read + else: + read = resp.read - def _discover_references(self, service, url): - assert url[-1] == "/" - url = urlparse.urljoin(url, "info/refs") - headers = {} + return resp, read + + def _discover_references(self, service, base_url): + assert base_url[-1] == "/" + tail = "info/refs" + headers = {"Accept": "*/*"} if self.dumb is not False: - url += "?service=%s" % service - headers["Content-Type"] = "application/x-%s-request" % service - resp = self._http_request(url, headers) + tail += "?service=%s" % service.decode('ascii') + url = urlparse.urljoin(base_url, tail) + resp, read = self._http_request(url, headers, allow_compression=True) + + if url != resp.geturl(): + # Something changed (redirect!), so let's update the base URL + if not resp.geturl().endswith(tail): + raise GitProtocolError( + "Redirected from URL %s to URL %s without %s" % ( + url, resp.geturl(), tail)) + base_url = resp.geturl()[:-len(tail)] + try: - self.dumb = (not resp.info().gettype().startswith("application/x-git-")) + content_type = resp.info().gettype() + except AttributeError: + content_type = resp.info().get_content_type() + try: + self.dumb = (not content_type.startswith("application/x-git-")) if not self.dumb: - proto = Protocol(resp.read, None) + proto = Protocol(read, None) # The first line should mention the service - pkts = list(proto.read_pkt_seq()) - if pkts != [('# service=%s\n' % service)]: + try: + [pkt] = list(proto.read_pkt_seq()) + except ValueError: + raise GitProtocolError( + "unexpected number of packets received") + if pkt.rstrip(b'\n') != (b'# service=' + service): raise GitProtocolError( - "unexpected first line %r from smart server" % pkts) - return read_pkt_refs(proto) + "unexpected first line %r from smart server" % pkt) + return read_pkt_refs(proto) + (base_url, ) else: - return read_info_refs(resp), set() + return read_info_refs(resp), set(), base_url finally: resp.close() def _smart_request(self, service, url, data): assert url[-1] == "/" url = urlparse.urljoin(url, service) - headers = {"Content-Type": "application/x-%s-request" % service} - resp = self._http_request(url, headers, data) - if resp.info().gettype() != ("application/x-%s-result" % service): + headers = { + "Content-Type": "application/x-%s-request" % service + } + resp, read = self._http_request(url, headers, data) + try: + content_type = resp.info().gettype() + except AttributeError: + content_type = resp.info().get_content_type() + if content_type != ( + "application/x-%s-result" % service): raise GitProtocolError("Invalid content-type from server: %s" - % resp.info().gettype()) - return resp + % content_type) + return resp, read - def send_pack(self, path, determine_wants, generate_pack_contents, + def send_pack(self, path, update_refs, generate_pack_contents, progress=None, write_pack=write_pack_objects): """Upload a pack to a remote repository. :param path: Repository path (as bytestring) + :param update_refs: Function to determine changes to remote refs. + Receive dict with existing remote refs, returns dict with + changed refs (name -> sha, where sha=ZERO_SHA for deletions) :param generate_pack_contents: Function that can return a sequence of the shas of the objects to upload. :param progress: Optional progress function @@ -1020,17 +1346,21 @@ :raises SendPackError: if server rejects the pack data :raises UpdateRefsError: if the server supports report-status and rejects ref updates + :return: new_refs dictionary containing the changes that were made + {refname: new_ref}, including deleted refs. """ url = self._get_url(path) - old_refs, server_capabilities = self._discover_references( + old_refs, server_capabilities, url = self._discover_references( b"git-receive-pack", url) - negotiated_capabilities = self._send_capabilities & server_capabilities + negotiated_capabilities = self._negotiate_receive_pack_capabilities( + server_capabilities) if CAPABILITY_REPORT_STATUS in negotiated_capabilities: self._report_status_parser = ReportStatusParser() - new_refs = determine_wants(dict(old_refs)) + new_refs = update_refs(dict(old_refs)) if new_refs is None: + # Determine wants function is aborting the push. return old_refs if self.dumb: raise NotImplementedError(self.fetch_pack) @@ -1038,22 +1368,21 @@ req_proto = Protocol(None, req_data.write) (have, want) = self._handle_receive_pack_head( req_proto, negotiated_capabilities, old_refs, new_refs) - if not want and old_refs == new_refs: + if not want and set(new_refs.items()).issubset(set(old_refs.items())): return new_refs objects = generate_pack_contents(have, want) if len(objects) > 0: write_pack(req_proto.write_file(), objects) - resp = self._smart_request(b"git-receive-pack", url, - data=req_data.getvalue()) + resp, read = self._smart_request("git-receive-pack", url, + data=req_data.getvalue()) try: resp_proto = Protocol(resp.read, None) - self._handle_receive_pack_tail(resp_proto, negotiated_capabilities, - progress) + self._handle_receive_pack_tail( + resp_proto, negotiated_capabilities, progress) return new_refs finally: resp.close() - def fetch_pack(self, path, determine_wants, graph_walker, pack_data, progress=None): """Retrieve a pack from a git smart server. @@ -1062,38 +1391,41 @@ :param graph_walker: Object with next() and ack(). :param pack_data: Callback called for each bit of data in the pack :param progress: Callback for progress reports (strings) - :return: Dictionary with all remote refs (not just those fetched) + :return: FetchPackResult object """ url = self._get_url(path) - refs, server_capabilities = self._discover_references( + refs, server_capabilities, url = self._discover_references( b"git-upload-pack", url) - negotiated_capabilities = self._fetch_capabilities & server_capabilities + negotiated_capabilities, symrefs, agent = ( + self._negotiate_upload_pack_capabilities( + server_capabilities)) wants = determine_wants(refs) if wants is not None: wants = [cid for cid in wants if cid != ZERO_SHA] if not wants: - return refs + return FetchPackResult(refs, symrefs, agent) if self.dumb: raise NotImplementedError(self.send_pack) req_data = BytesIO() req_proto = Protocol(None, req_data.write) self._handle_upload_pack_head( - req_proto, negotiated_capabilities, graph_walker, wants, - lambda: False) - resp = self._smart_request( - b"git-upload-pack", url, data=req_data.getvalue()) + req_proto, negotiated_capabilities, graph_walker, wants, + lambda: False) + resp, read = self._smart_request( + "git-upload-pack", url, data=req_data.getvalue()) try: - resp_proto = Protocol(resp.read, None) - self._handle_upload_pack_tail(resp_proto, negotiated_capabilities, - graph_walker, pack_data, progress) - return refs + resp_proto = Protocol(read, None) + self._handle_upload_pack_tail( + resp_proto, negotiated_capabilities, graph_walker, pack_data, + progress) + return FetchPackResult(refs, symrefs, agent) finally: resp.close() def get_refs(self, path): """Retrieve the current refs from a git smart server.""" url = self._get_url(path) - refs, _ = self._discover_references( + refs, _, _ = self._discover_references( b"git-upload-pack", url) return refs @@ -1110,19 +1442,16 @@ """ parsed = urlparse.urlparse(url) if parsed.scheme == 'git': - return (TCPGitClient(parsed.hostname, port=parsed.port, **kwargs), + return (TCPGitClient.from_parsedurl(parsed, **kwargs), parsed.path) - elif parsed.scheme == 'git+ssh': - path = parsed.path - if path.startswith('/'): - path = parsed.path[1:] - return SSHGitClient(parsed.hostname, port=parsed.port, - username=parsed.username, **kwargs), path + elif parsed.scheme in ('git+ssh', 'ssh'): + return SSHGitClient.from_parsedurl(parsed, **kwargs), parsed.path elif parsed.scheme in ('http', 'https'): - return HttpGitClient(urlparse.urlunparse(parsed), config=config, - **kwargs), parsed.path + return HttpGitClient.from_parsedurl( + parsed, config=config, **kwargs), parsed.path elif parsed.scheme == 'file': - return default_local_git_client_cls(**kwargs), parsed.path + return default_local_git_client_cls.from_parsedurl( + parsed, **kwargs), parsed.path raise ValueError("unknown scheme '%s'" % parsed.scheme) @@ -1148,14 +1477,18 @@ # Windows local path return default_local_git_client_cls(**kwargs), location - if ':' in location and not '@' in location: + if ':' in location and '@' not in location: # SSH with no user@, zero or one leading slash. - (hostname, path) = location.split(':') + (hostname, path) = location.split(':', 1) return SSHGitClient(hostname, **kwargs), path - elif '@' in location and ':' in location: + elif ':' in location: # SSH with user@host:foo. - user_host, path = location.split(':') - user, host = user_host.rsplit('@') + user_host, path = location.split(':', 1) + if '@' in user_host: + user, host = user_host.rsplit('@', 1) + else: + user = None + host = user_host return SSHGitClient(host, username=user, **kwargs), path # Otherwise, assume it's a local path. diff -Nru dulwich-0.12.0/dulwich/_compat.py dulwich-0.18.5/dulwich/_compat.py --- dulwich-0.12.0/dulwich/_compat.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/_compat.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,963 +0,0 @@ -# _compat.py -- For dealing with python2.6 oddness -# Copyright (C) 2012-2014 Jelmer Vernooij and others. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) a later version of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. - -"""Misc utilities to work with python <2.7. - -These utilities can all be deleted when dulwich decides it wants to stop -support for python <2.7. -""" - -# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and -# pypy. Passes Python2.7's test suite and incorporates all the latest updates. -# Copyright (C) Raymond Hettinger, MIT license - -try: - from thread import get_ident as _get_ident -except ImportError: - from dummy_thread import get_ident as _get_ident - -try: - from _abcoll import KeysView, ValuesView, ItemsView -except ImportError: - pass - -class OrderedDict(dict): - 'Dictionary that remembers insertion order' - # An inherited dict maps keys to values. - # The inherited dict provides __getitem__, __len__, __contains__, and get. - # The remaining methods are order-aware. - # Big-O running times for all methods are the same as for regular - # dictionaries. - - # The internal self.__map dictionary maps keys to links in a doubly linked - # list. The circular doubly linked list starts and ends with a sentinel - # element. The sentinel element never gets deleted (this simplifies the - # algorithm). Each link is stored as a list of length three: [PREV, NEXT, - # KEY]. - - def __init__(self, *args, **kwds): - '''Initialize an ordered dictionary. Signature is the same as for - regular dictionaries, but keyword arguments are not recommended - because their insertion order is arbitrary. - - ''' - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - try: - self.__root - except AttributeError: - self.__root = root = [] # sentinel node - root[:] = [root, root, None] - self.__map = {} - self.__update(*args, **kwds) - - def __setitem__(self, key, value, dict_setitem=dict.__setitem__): - 'od.__setitem__(i, y) <==> od[i]=y' - # Setting a new item creates a new link which goes at the end of the - # linked list, and the inherited dictionary is updated with the new - # key/value pair. - if key not in self: - root = self.__root - last = root[0] - last[1] = root[0] = self.__map[key] = [last, root, key] - dict_setitem(self, key, value) - - def __delitem__(self, key, dict_delitem=dict.__delitem__): - 'od.__delitem__(y) <==> del od[y]' - # Deleting an existing item uses self.__map to find the link which is - # then removed by updating the links in the predecessor and successor - # nodes. - dict_delitem(self, key) - link_prev, link_next, key = self.__map.pop(key) - link_prev[1] = link_next - link_next[0] = link_prev - - def __iter__(self): - 'od.__iter__() <==> iter(od)' - root = self.__root - curr = root[1] - while curr is not root: - yield curr[2] - curr = curr[1] - - def __reversed__(self): - 'od.__reversed__() <==> reversed(od)' - root = self.__root - curr = root[0] - while curr is not root: - yield curr[2] - curr = curr[0] - - def clear(self): - 'od.clear() -> None. Remove all items from od.' - try: - for node in self.__map.itervalues(): - del node[:] - root = self.__root - root[:] = [root, root, None] - self.__map.clear() - except AttributeError: - pass - dict.clear(self) - - def popitem(self, last=True): - """od.popitem() -> (k, v), return and remove a (key, value) pair. - Pairs are returned in LIFO order if last is true or FIFO order if false. - - """ - if not self: - raise KeyError('dictionary is empty') - root = self.__root - if last: - link = root[0] - link_prev = link[0] - link_prev[1] = root - root[0] = link_prev - else: - link = root[1] - link_next = link[1] - root[1] = link_next - link_next[0] = root - key = link[2] - del self.__map[key] - value = dict.pop(self, key) - return key, value - - # -- the following methods do not depend on the internal structure -- - - def keys(self): - """'od.keys() -> list of keys in od""" - return list(self) - - def values(self): - """od.values() -> list of values in od""" - return [self[key] for key in self] - - def items(self): - """od.items() -> list of (key, value) pairs in od""" - return [(key, self[key]) for key in self] - - def iterkeys(self): - """od.iterkeys() -> an iterator over the keys in od""" - return iter(self) - - def itervalues(self): - """od.itervalues -> an iterator over the values in od""" - for k in self: - yield self[k] - - def iteritems(self): - """od.iteritems -> an iterator over the (key, value) items in od""" - for k in self: - yield (k, self[k]) - - def update(*args, **kwds): - """od.update(E, F) -> None. Update od from dict/iterable E and F. - - If E is a dict instance, does: for k in E: od[k] = E[k] - If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] - Or if E is an iterable of items, does: for k, v in E: od[k] = v - In either case, this is followed by: for k, v in F.items(): od[k] = v - """ - if len(args) > 2: - raise TypeError('update() takes at most 2 positional ' - 'arguments (%d given)' % (len(args),)) - elif not args: - raise TypeError('update() takes at least 1 argument (0 given)') - self = args[0] - # Make progressively weaker assumptions about "other" - other = () - if len(args) == 2: - other = args[1] - if isinstance(other, dict): - for key in other: - self[key] = other[key] - elif hasattr(other, 'keys'): - for key in other.keys(): - self[key] = other[key] - else: - for key, value in other: - self[key] = value - for key, value in kwds.items(): - self[key] = value - - __update = update # let subclasses override update without breaking - # __init__ - - __marker = object() - - def pop(self, key, default=__marker): - """od.pop(k[,d]) -> v, remove specified key and return the corresponding value. - If key is not found, d is returned if given, otherwise KeyError is raised. - - """ - if key in self: - result = self[key] - del self[key] - return result - if default is self.__marker: - raise KeyError(key) - return default - - def setdefault(self, key, default=None): - 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' - if key in self: - return self[key] - self[key] = default - return default - - def __repr__(self, _repr_running={}): - 'od.__repr__() <==> repr(od)' - call_key = id(self), _get_ident() - if call_key in _repr_running: - return '...' - _repr_running[call_key] = 1 - try: - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, self.items()) - finally: - del _repr_running[call_key] - - def __reduce__(self): - 'Return state information for pickling' - items = [[k, self[k]] for k in self] - inst_dict = vars(self).copy() - for k in vars(OrderedDict()): - inst_dict.pop(k, None) - if inst_dict: - return (self.__class__, (items,), inst_dict) - return self.__class__, (items,) - - def copy(self): - 'od.copy() -> a shallow copy of od' - return self.__class__(self) - - @classmethod - def fromkeys(cls, iterable, value=None): - '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S - and values equal to v (which defaults to None). - - ''' - d = cls() - for key in iterable: - d[key] = value - return d - - def __eq__(self, other): - '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive - while comparison to a regular mapping is order-insensitive. - - ''' - if isinstance(other, OrderedDict): - return len(self)==len(other) and self.items() == other.items() - return dict.__eq__(self, other) - - def __ne__(self, other): - return not self == other - - # -- the following methods are only used in Python 2.7 -- - - def viewkeys(self): - "od.viewkeys() -> a set-like object providing a view on od's keys" - return KeysView(self) - - def viewvalues(self): - "od.viewvalues() -> an object providing a view on od's values" - return ValuesView(self) - - def viewitems(self): - "od.viewitems() -> a set-like object providing a view on od's items" - return ItemsView(self) - - -# Copyright 2007 Google, Inc. All Rights Reserved. -# Licensed to PSF under a Contributor Agreement. - -from abc import ABCMeta, abstractmethod -import sys - -### ONE-TRICK PONIES ### - -def _hasattr(C, attr): - try: - return any(attr in B.__dict__ for B in C.__mro__) - except AttributeError: - # Old-style class - return hasattr(C, attr) - - -class Hashable: - __metaclass__ = ABCMeta - - @abstractmethod - def __hash__(self): - return 0 - - @classmethod - def __subclasshook__(cls, C): - if cls is Hashable: - try: - for B in C.__mro__: - if "__hash__" in B.__dict__: - if B.__dict__["__hash__"]: - return True - break - except AttributeError: - # Old-style class - if getattr(C, "__hash__", None): - return True - return NotImplemented - - -class Iterable: - __metaclass__ = ABCMeta - - @abstractmethod - def __iter__(self): - while False: - yield None - - @classmethod - def __subclasshook__(cls, C): - if cls is Iterable: - if _hasattr(C, "__iter__"): - return True - return NotImplemented - -Iterable.register(str) - - -class Iterator(Iterable): - - @abstractmethod - def next(self): - 'Return the next item from the iterator. When exhausted, raise StopIteration' - raise StopIteration - - def __iter__(self): - return self - - @classmethod - def __subclasshook__(cls, C): - if cls is Iterator: - if _hasattr(C, "next") and _hasattr(C, "__iter__"): - return True - return NotImplemented - - -class Sized: - __metaclass__ = ABCMeta - - @abstractmethod - def __len__(self): - return 0 - - @classmethod - def __subclasshook__(cls, C): - if cls is Sized: - if _hasattr(C, "__len__"): - return True - return NotImplemented - - -class Container: - __metaclass__ = ABCMeta - - @abstractmethod - def __contains__(self, x): - return False - - @classmethod - def __subclasshook__(cls, C): - if cls is Container: - if _hasattr(C, "__contains__"): - return True - return NotImplemented - - -class Callable: - __metaclass__ = ABCMeta - - @abstractmethod - def __call__(self, *args, **kwds): - return False - - @classmethod - def __subclasshook__(cls, C): - if cls is Callable: - if _hasattr(C, "__call__"): - return True - return NotImplemented - - -### SETS ### - - -class Set(Sized, Iterable, Container): - """A set is a finite, iterable container. - - This class provides concrete generic implementations of all - methods except for __contains__, __iter__ and __len__. - - To override the comparisons (presumably for speed, as the - semantics are fixed), all you have to do is redefine __le__ and - then the other operations will automatically follow suit. - """ - - def __le__(self, other): - if not isinstance(other, Set): - return NotImplemented - if len(self) > len(other): - return False - for elem in self: - if elem not in other: - return False - return True - - def __lt__(self, other): - if not isinstance(other, Set): - return NotImplemented - return len(self) < len(other) and self.__le__(other) - - def __gt__(self, other): - if not isinstance(other, Set): - return NotImplemented - return len(self) > len(other) and self.__ge__(other) - - def __ge__(self, other): - if not isinstance(other, Set): - return NotImplemented - if len(self) < len(other): - return False - for elem in other: - if elem not in self: - return False - return True - - def __eq__(self, other): - if not isinstance(other, Set): - return NotImplemented - return len(self) == len(other) and self.__le__(other) - - def __ne__(self, other): - return not (self == other) - - @classmethod - def _from_iterable(cls, it): - '''Construct an instance of the class from any iterable input. - - Must override this method if the class constructor signature - does not accept an iterable for an input. - ''' - return cls(it) - - def __and__(self, other): - if not isinstance(other, Iterable): - return NotImplemented - return self._from_iterable(value for value in other if value in self) - - __rand__ = __and__ - - def isdisjoint(self, other): - 'Return True if two sets have a null intersection.' - for value in other: - if value in self: - return False - return True - - def __or__(self, other): - if not isinstance(other, Iterable): - return NotImplemented - chain = (e for s in (self, other) for e in s) - return self._from_iterable(chain) - - __ror__ = __or__ - - def __sub__(self, other): - if not isinstance(other, Set): - if not isinstance(other, Iterable): - return NotImplemented - other = self._from_iterable(other) - return self._from_iterable(value for value in self - if value not in other) - - def __rsub__(self, other): - if not isinstance(other, Set): - if not isinstance(other, Iterable): - return NotImplemented - other = self._from_iterable(other) - return self._from_iterable(value for value in other - if value not in self) - - def __xor__(self, other): - if not isinstance(other, Set): - if not isinstance(other, Iterable): - return NotImplemented - other = self._from_iterable(other) - return (self - other) | (other - self) - - __rxor__ = __xor__ - - # Sets are not hashable by default, but subclasses can change this - __hash__ = None - - def _hash(self): - """Compute the hash value of a set. - - Note that we don't define __hash__: not all sets are hashable. - But if you define a hashable set type, its __hash__ should - call this function. - - This must be compatible __eq__. - - All sets ought to compare equal if they contain the same - elements, regardless of how they are implemented, and - regardless of the order of the elements; so there's not much - freedom for __eq__ or __hash__. We match the algorithm used - by the built-in frozenset type. - """ - MAX = sys.maxint - MASK = 2 * MAX + 1 - n = len(self) - h = 1927868237 * (n + 1) - h &= MASK - for x in self: - hx = hash(x) - h ^= (hx ^ (hx << 16) ^ 89869747) * 3644798167 - h &= MASK - h = h * 69069 + 907133923 - h &= MASK - if h > MAX: - h -= MASK + 1 - if h == -1: - h = 590923713 - return h - -Set.register(frozenset) - - -class MutableSet(Set): - """A mutable set is a finite, iterable container. - - This class provides concrete generic implementations of all - methods except for __contains__, __iter__, __len__, - add(), and discard(). - - To override the comparisons (presumably for speed, as the - semantics are fixed), all you have to do is redefine __le__ and - then the other operations will automatically follow suit. - """ - - @abstractmethod - def add(self, value): - """Add an element.""" - raise NotImplementedError - - @abstractmethod - def discard(self, value): - """Remove an element. Do not raise an exception if absent.""" - raise NotImplementedError - - def remove(self, value): - """Remove an element. If not a member, raise a KeyError.""" - if value not in self: - raise KeyError(value) - self.discard(value) - - def pop(self): - """Return the popped value. Raise KeyError if empty.""" - it = iter(self) - try: - value = next(it) - except StopIteration: - raise KeyError - self.discard(value) - return value - - def clear(self): - """This is slow (creates N new iterators!) but effective.""" - try: - while True: - self.pop() - except KeyError: - pass - - def __ior__(self, it): - for value in it: - self.add(value) - return self - - def __iand__(self, it): - for value in (self - it): - self.discard(value) - return self - - def __ixor__(self, it): - if it is self: - self.clear() - else: - if not isinstance(it, Set): - it = self._from_iterable(it) - for value in it: - if value in self: - self.discard(value) - else: - self.add(value) - return self - - def __isub__(self, it): - if it is self: - self.clear() - else: - for value in it: - self.discard(value) - return self - -MutableSet.register(set) - - -### MAPPINGS ### - - -class Mapping(Sized, Iterable, Container): - - """A Mapping is a generic container for associating key/value - pairs. - - This class provides concrete generic implementations of all - methods except for __getitem__, __iter__, and __len__. - - """ - - @abstractmethod - def __getitem__(self, key): - raise KeyError - - def get(self, key, default=None): - 'D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.' - try: - return self[key] - except KeyError: - return default - - def __contains__(self, key): - try: - self[key] - except KeyError: - return False - else: - return True - - def iterkeys(self): - 'D.iterkeys() -> an iterator over the keys of D' - return iter(self) - - def itervalues(self): - 'D.itervalues() -> an iterator over the values of D' - for key in self: - yield self[key] - - def iteritems(self): - 'D.iteritems() -> an iterator over the (key, value) items of D' - for key in self: - yield (key, self[key]) - - def keys(self): - "D.keys() -> list of D's keys" - return list(self) - - def items(self): - "D.items() -> list of D's (key, value) pairs, as 2-tuples" - return [(key, self[key]) for key in self] - - def values(self): - "D.values() -> list of D's values" - return [self[key] for key in self] - - # Mappings are not hashable by default, but subclasses can change this - __hash__ = None - - def __eq__(self, other): - if not isinstance(other, Mapping): - return NotImplemented - return dict(self.items()) == dict(other.items()) - - def __ne__(self, other): - return not (self == other) - -class MappingView(Sized): - - def __init__(self, mapping): - self._mapping = mapping - - def __len__(self): - return len(self._mapping) - - def __repr__(self): - return '{0.__class__.__name__}({0._mapping!r})'.format(self) - - -class KeysView(MappingView, Set): - - @classmethod - def _from_iterable(self, it): - return set(it) - - def __contains__(self, key): - return key in self._mapping - - def __iter__(self): - for key in self._mapping: - yield key - - -class ItemsView(MappingView, Set): - - @classmethod - def _from_iterable(self, it): - return set(it) - - def __contains__(self, item): - key, value = item - try: - v = self._mapping[key] - except KeyError: - return False - else: - return v == value - - def __iter__(self): - for key in self._mapping: - yield (key, self._mapping[key]) - - -class ValuesView(MappingView): - - def __contains__(self, value): - for key in self._mapping: - if value == self._mapping[key]: - return True - return False - - def __iter__(self): - for key in self._mapping: - yield self._mapping[key] - - -class MutableMapping(Mapping): - - """A MutableMapping is a generic container for associating - key/value pairs. - - This class provides concrete generic implementations of all - methods except for __getitem__, __setitem__, __delitem__, - __iter__, and __len__. - - """ - - @abstractmethod - def __setitem__(self, key, value): - raise KeyError - - @abstractmethod - def __delitem__(self, key): - raise KeyError - - __marker = object() - - def pop(self, key, default=__marker): - '''D.pop(k[,d]) -> v, remove specified key and return the corresponding value. - If key is not found, d is returned if given, otherwise KeyError is raised. - ''' - try: - value = self[key] - except KeyError: - if default is self.__marker: - raise - return default - else: - del self[key] - return value - - def popitem(self): - '''D.popitem() -> (k, v), remove and return some (key, value) pair - as a 2-tuple; but raise KeyError if D is empty. - ''' - try: - key = next(iter(self)) - except StopIteration: - raise KeyError - value = self[key] - del self[key] - return key, value - - def clear(self): - 'D.clear() -> None. Remove all items from D.' - try: - while True: - self.popitem() - except KeyError: - pass - - def update(*args, **kwds): - ''' D.update([E, ]**F) -> None. Update D from mapping/iterable E and F. - If E present and has a .keys() method, does: for k in E: D[k] = E[k] - If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v - In either case, this is followed by: for k, v in F.items(): D[k] = v - ''' - if len(args) > 2: - raise TypeError("update() takes at most 2 positional " - "arguments ({} given)".format(len(args))) - elif not args: - raise TypeError("update() takes at least 1 argument (0 given)") - self = args[0] - other = args[1] if len(args) >= 2 else () - - if isinstance(other, Mapping): - for key in other: - self[key] = other[key] - elif hasattr(other, "keys"): - for key in other.keys(): - self[key] = other[key] - else: - for key, value in other: - self[key] = value - for key, value in kwds.items(): - self[key] = value - - def setdefault(self, key, default=None): - 'D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D' - try: - return self[key] - except KeyError: - self[key] = default - return default - -MutableMapping.register(dict) - - -### SEQUENCES ### - - -class Sequence(Sized, Iterable, Container): - """All the operations on a read-only sequence. - - Concrete subclasses must override __new__ or __init__, - __getitem__, and __len__. - """ - - @abstractmethod - def __getitem__(self, index): - raise IndexError - - def __iter__(self): - i = 0 - try: - while True: - v = self[i] - yield v - i += 1 - except IndexError: - return - - def __contains__(self, value): - for v in self: - if v == value: - return True - return False - - def __reversed__(self): - for i in reversed(range(len(self))): - yield self[i] - - def index(self, value): - '''S.index(value) -> integer -- return first index of value. - Raises ValueError if the value is not present. - ''' - for i, v in enumerate(self): - if v == value: - return i - raise ValueError - - def count(self, value): - 'S.count(value) -> integer -- return number of occurrences of value' - return sum(1 for v in self if v == value) - -Sequence.register(tuple) -Sequence.register(basestring) -Sequence.register(buffer) -Sequence.register(xrange) - - -class MutableSequence(Sequence): - - """All the operations on a read-only sequence. - - Concrete subclasses must provide __new__ or __init__, - __getitem__, __setitem__, __delitem__, __len__, and insert(). - - """ - - @abstractmethod - def __setitem__(self, index, value): - raise IndexError - - @abstractmethod - def __delitem__(self, index): - raise IndexError - - @abstractmethod - def insert(self, index, value): - 'S.insert(index, object) -- insert object before index' - raise IndexError - - def append(self, value): - 'S.append(object) -- append object to the end of the sequence' - self.insert(len(self), value) - - def reverse(self): - 'S.reverse() -- reverse *IN PLACE*' - n = len(self) - for i in range(n//2): - self[i], self[n-i-1] = self[n-i-1], self[i] - - def extend(self, values): - 'S.extend(iterable) -- extend sequence by appending elements from the iterable' - for v in values: - self.append(v) - - def pop(self, index=-1): - '''S.pop([index]) -> item -- remove and return item at index (default last). - Raise IndexError if list is empty or index is out of range. - ''' - v = self[index] - del self[index] - return v - - def remove(self, value): - '''S.remove(value) -- remove first occurrence of value. - Raise ValueError if the value is not present. - ''' - del self[self.index(value)] - - def __iadd__(self, values): - self.extend(values) - return self - -MutableSequence.register(list) diff -Nru dulwich-0.12.0/dulwich/config.py dulwich-0.18.5/dulwich/config.py --- dulwich-0.12.0/dulwich/config.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/config.py 2017-10-07 15:45:17.000000000 +0000 @@ -1,20 +1,22 @@ # config.py - Reading and writing Git config files # Copyright (C) 2011-2013 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) a later version. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Reading and writing Git configuration files. @@ -27,16 +29,10 @@ import errno import os -try: - from collections import ( - OrderedDict, - MutableMapping, - ) -except ImportError: - from dulwich._compat import ( - OrderedDict, - MutableMapping - ) +from collections import ( + OrderedDict, + MutableMapping, + ) from dulwich.file import GitFile @@ -99,6 +95,14 @@ """ raise NotImplementedError(self.itersections) + def has_section(self, name): + """Check if a specified section exists. + + :param name: Name of section to check for + :return: boolean indicating whether the section exists + """ + return (name in self.itersections()) + class ConfigDict(Config, MutableMapping): """Git configuration stored in a dictionary.""" @@ -153,6 +157,10 @@ def set(self, section, name, value): if not isinstance(section, tuple): section = (section, ) + if not isinstance(name, bytes): + raise TypeError(name) + if type(value) not in (bool, bytes): + raise TypeError(value) self._values.setdefault(section, OrderedDict())[name] = value def iteritems(self, section): @@ -164,11 +172,13 @@ def _format_string(value): if (value.startswith(b" ") or - value.startswith(b"\t") or - value.endswith(b" ") or - value.endswith(b"\t")): + value.startswith(b"\t") or + value.endswith(b" ") or + b'#' in value or + value.endswith(b"\t")): return b'"' + _escape_value(value) + b'"' - return _escape_value(value) + else: + return _escape_value(value) _ESCAPE_TABLE = { @@ -181,6 +191,7 @@ _COMMENT_CHARS = [ord(b"#"), ord(b";")] _WHITESPACE_CHARS = [ord(b"\t"), ord(b" ")] + def _parse_string(value): value = bytearray(value.strip()) ret = bytearray() @@ -199,8 +210,8 @@ (value, i)) except KeyError: raise ValueError( - "escape character followed by unknown character %s at %d in %r" % - (value[i], i, value)) + "escape character followed by unknown character " + "%s at %d in %r" % (value[i], i, value)) if whitespace: ret.extend(whitespace) whitespace = bytearray() @@ -225,17 +236,13 @@ return bytes(ret) -def _unescape_value(value): - """Unescape a value.""" - ret = bytearray() - i = 0 - - return ret - - def _escape_value(value): """Escape a value.""" - return value.replace(b"\\", b"\\\\").replace(b"\n", b"\\n").replace(b"\t", b"\\t").replace(b"\"", b"\\\"") + value = value.replace(b"\\", b"\\\\") + value = value.replace(b"\n", b"\\n") + value = value.replace(b"\t", b"\\t") + value = value.replace(b"\"", b"\\\"") + return value def _check_variable_name(name): @@ -294,8 +301,8 @@ section = (pts[0], pts[1]) else: if not _check_section_name(pts[0]): - raise ValueError("invalid section name %r" % - pts[0]) + raise ValueError( + "invalid section name %r" % pts[0]) pts = pts[0].split(b".", 1) if len(pts) == 2: section = (pts[0], pts[1]) @@ -315,23 +322,20 @@ if not _check_variable_name(setting): raise ValueError("invalid variable name %s" % setting) if value.endswith(b"\\\n"): - value = value[:-2] - continuation = True + continuation = value[:-2] else: - continuation = False - value = _parse_string(value) - ret._values[section][setting] = value - if not continuation: + continuation = None + value = _parse_string(value) + ret._values[section][setting] = value setting = None else: # continuation line if line.endswith(b"\\\n"): - line = line[:-2] - continuation = True + continuation += line[:-2] else: - continuation = False - value = _parse_string(line) - ret._values[section][setting] += value - if not continuation: + continuation += line + value = _parse_string(continuation) + ret._values[section][setting] = value + continuation = None setting = None return ret @@ -361,14 +365,15 @@ if subsection_name is None: f.write(b"[" + section_name + b"]\n") else: - f.write(b"[" + section_name + b" \"" + subsection_name + b"\"]\n") + f.write(b"[" + section_name + + b" \"" + subsection_name + b"\"]\n") for key, value in values.items(): if value is True: value = b"true" elif value is False: value = b"false" else: - value = _escape_value(value) + value = _format_string(value) f.write(b"\t" + key + b" = " + value + b"\n") @@ -383,15 +388,26 @@ return "<%s for %r>" % (self.__class__.__name__, self.backends) @classmethod + def default(cls): + return cls(cls.default_backends()) + + @classmethod def default_backends(cls): """Retrieve the default configuration. - This will look in the users' home directory and the system - configuration. + See git-config(1) for details on the files searched. """ paths = [] paths.append(os.path.expanduser("~/.gitconfig")) - paths.append("/etc/gitconfig") + + xdg_config_home = os.environ.get( + "XDG_CONFIG_HOME", os.path.expanduser("~/.config/"), + ) + paths.append(os.path.join(xdg_config_home, "git", "config")) + + if "GIT_CONFIG_NOSYSTEM" not in os.environ: + paths.append("/etc/gitconfig") + backends = [] for path in paths: try: @@ -416,3 +432,18 @@ if self.writable is None: raise NotImplementedError(self.set) return self.writable.set(section, name, value) + + +def parse_submodules(config): + """Parse a gitmodules GitConfig file, returning submodules. + + :param config: A `ConfigFile` + :return: list of tuples (submodule path, url, name), + where name is quoted part of the section's name. + """ + for section in config.keys(): + section_kind, section_name = section + if section_kind == b'submodule': + sm_path = config.get(section, b'path') + sm_url = config.get(section, b'url') + yield (sm_path, sm_url, section_name) diff -Nru dulwich-0.12.0/dulwich/contrib/__init__.py dulwich-0.18.5/dulwich/contrib/__init__.py --- dulwich-0.12.0/dulwich/contrib/__init__.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/contrib/__init__.py 2017-10-07 15:45:17.000000000 +0000 @@ -1,26 +1,28 @@ # __init__.py -- Contrib module for Dulwich # Copyright (C) 2014 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. def test_suite(): import unittest names = [ + 'release_robot', 'swift', ] module_names = ['dulwich.contrib.test_' + name for name in names] diff -Nru dulwich-0.12.0/dulwich/contrib/paramiko_vendor.py dulwich-0.18.5/dulwich/contrib/paramiko_vendor.py --- dulwich-0.12.0/dulwich/contrib/paramiko_vendor.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/contrib/paramiko_vendor.py 2017-10-07 15:45:17.000000000 +0000 @@ -1,20 +1,22 @@ # paramiko_vendor.py -- paramiko implementation of the SSHVendor interface # Copyright (C) 2013 Aaron O'Mullan # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Paramiko SSH support for Dulwich. @@ -30,9 +32,9 @@ import paramiko import paramiko.client -import subprocess import threading + class _ParamikoWrapper(object): STDERR_READ_N = 2048 # 2k @@ -115,8 +117,6 @@ def run_command(self, host, command, username=None, port=None, progress_stderr=None): - if not isinstance(command, bytes): - raise TypeError(command) # Paramiko needs an explicit port. None is not valid if port is None: port = 22 diff -Nru dulwich-0.12.0/dulwich/contrib/release_robot.py dulwich-0.18.5/dulwich/contrib/release_robot.py --- dulwich-0.12.0/dulwich/contrib/release_robot.py 1970-01-01 00:00:00.000000000 +0000 +++ dulwich-0.18.5/dulwich/contrib/release_robot.py 2017-10-07 15:45:17.000000000 +0000 @@ -0,0 +1,143 @@ +# release_robot.py +# +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. +# + +"""Determine last version string from tags. + +Alternate to `Versioneer `_ using +`Dulwich `_ to sort tags by time from +newest to oldest. + +Copy the following into the package ``__init__.py`` module:: + + from dulwich.contrib.release_robot import get_current_version + __version__ = get_current_version() + +This example assumes the tags have a leading "v" like "v0.3", and that the +``.git`` folder is in a project folder that containts the package folder. + +EG:: + + * project + | + * .git + | + +-* package + | + * __init__.py <-- put __version__ here + + +""" + +import datetime +import re +import sys +import time + +from dulwich.repo import Repo + +# CONSTANTS +PROJDIR = '.' +PATTERN = r'[ a-zA-Z_\-]*([\d\.]+[\-\w\.]*)' + + +def get_recent_tags(projdir=PROJDIR): + """Get list of tags in order from newest to oldest and their datetimes. + + :param projdir: path to ``.git`` + :returns: list of tags sorted by commit time from newest to oldest + + Each tag in the list contains the tag name, commit time, commit id, author + and any tag meta. If a tag isn't annotated, then its tag meta is ``None``. + Otherwise the tag meta is a tuple containing the tag time, tag id and tag + name. Time is in UTC. + """ + with Repo(projdir) as project: # dulwich repository object + refs = project.get_refs() # dictionary of refs and their SHA-1 values + tags = {} # empty dictionary to hold tags, commits and datetimes + # iterate over refs in repository + for key, value in refs.items(): + key = key.decode('utf-8') # compatible with Python-3 + obj = project.get_object(value) # dulwich object from SHA-1 + # don't just check if object is "tag" b/c it could be a "commit" + # instead check if "tags" is in the ref-name + if u'tags' not in key: + # skip ref if not a tag + continue + # strip the leading text from refs to get "tag name" + _, tag = key.rsplit(u'/', 1) + # check if tag object is "commit" or "tag" pointing to a "commit" + try: + commit = obj.object # a tuple (commit class, commit id) + except AttributeError: + commit = obj + tag_meta = None + else: + tag_meta = ( + datetime.datetime(*time.gmtime(obj.tag_time)[:6]), + obj.id.decode('utf-8'), + obj.name.decode('utf-8') + ) # compatible with Python-3 + commit = project.get_object(commit[1]) # commit object + # get tag commit datetime, but dulwich returns seconds since + # beginning of epoch, so use Python time module to convert it to + # timetuple then convert to datetime + tags[tag] = [ + datetime.datetime(*time.gmtime(commit.commit_time)[:6]), + commit.id.decode('utf-8'), + commit.author.decode('utf-8'), + tag_meta + ] # compatible with Python-3 + + # return list of tags sorted by their datetimes from newest to oldest + return sorted(tags.items(), key=lambda tag: tag[1][0], reverse=True) + + +def get_current_version(projdir=PROJDIR, pattern=PATTERN, logger=None): + """Return the most recent tag, using an options regular expression pattern. + + The default pattern will strip any characters preceding the first semantic + version. *EG*: "Release-0.2.1-rc.1" will be come "0.2.1-rc.1". If no match + is found, then the most recent tag is return without modification. + + :param projdir: path to ``.git`` + :param pattern: regular expression pattern with group that matches version + :param logger: a Python logging instance to capture exception + :returns: tag matching first group in regular expression pattern + """ + tags = get_recent_tags(projdir) + try: + tag = tags[0][0] + except IndexError: + return + matches = re.match(pattern, tag) + try: + current_version = matches.group(1) + except (IndexError, AttributeError) as err: + if logger: + logger.exception(err) + return tag + return current_version + + +if __name__ == '__main__': + if len(sys.argv) > 1: + _PROJDIR = sys.argv[1] + else: + _PROJDIR = PROJDIR + print(get_current_version(projdir=_PROJDIR)) diff -Nru dulwich-0.12.0/dulwich/contrib/swift.py dulwich-0.18.5/dulwich/contrib/swift.py --- dulwich-0.12.0/dulwich/contrib/swift.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/contrib/swift.py 2017-10-07 15:45:17.000000000 +0000 @@ -3,21 +3,22 @@ # # Author: Fabien Boucher # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Repo implementation atop OpenStack SWIFT.""" @@ -31,9 +32,16 @@ import tempfile import posixpath -from urlparse import urlparse +try: + import urlparse +except ImportError: + import urllib.parse as urlparse + from io import BytesIO -from ConfigParser import ConfigParser +try: + from ConfigParser import ConfigParser +except ImportError: + from configparser import ConfigParser from geventhttpclient import HTTPClient from dulwich.greenthreads import ( @@ -164,7 +172,11 @@ """ conf = ConfigParser() if file: - conf.readfp(file) + try: + conf.read_file(file, path) + except AttributeError: + # read_file only exists in Python3 + conf.readfp(file) return conf confpath = None if not path: @@ -204,7 +216,7 @@ # Tree elif obj.type_num == Tree.type_num: shas = [(s, n, not stat.S_ISDIR(m)) for - n, m, s in obj.iteritems() if not S_ISGITLINK(m)] + n, m, s in obj.items() if not S_ISGITLINK(m)] info[obj.id] = (obj.type_num, shas) # Blob elif obj.type_num == Blob.type_num: @@ -274,8 +286,8 @@ connection_timeout=self.http_timeout, network_timeout=self.http_timeout, headers=token_header) - self.base_path = str(posixpath.join(urlparse(self.storage_url).path, - self.root)) + self.base_path = str(posixpath.join( + urlparse.urlparse(self.storage_url).path, self.root)) def swift_auth_v1(self): self.user = self.user.replace(";", ":") @@ -286,7 +298,7 @@ ) headers = {'X-Auth-User': self.user, 'X-Auth-Key': self.password} - path = urlparse(self.auth_url).path + path = urlparse.urlparse(self.auth_url).path ret = auth_httpclient.request('GET', path, headers=headers) @@ -318,7 +330,7 @@ connection_timeout=self.http_timeout, network_timeout=self.http_timeout, ) - path = urlparse(self.auth_url).path + path = urlparse.urlparse(self.auth_url).path if not path.endswith('tokens'): path = posixpath.join(path, 'tokens') ret = auth_httpclient.request('POST', path, @@ -397,7 +409,7 @@ raise SwiftException('HEAD request failed with error code %s' % ret.status_code) resp_headers = {} - for header, value in ret.iteritems(): + for header, value in ret.items(): resp_headers[header.lower()] = value return resp_headers @@ -502,7 +514,7 @@ self.pack_length = pack_length self.offset = 0 self.base_offset = 0 - self.buff = '' + self.buff = b'' self.buff_length = self.scon.chunk_length def _read(self, more=False): @@ -523,16 +535,14 @@ if self.base_offset + end > self.pack_length: data = self.buff[self.offset:] self.offset = end - return "".join(data) - try: - self.buff[end] - except IndexError: + return data + if end > len(self.buff): # Need to read more from swift self._read(more=True) return self.read(length) data = self.buff[self.offset:end] self.offset = end - return "".join(data) + return data def seek(self, offset): """Seek to a specified offset @@ -579,8 +589,6 @@ def get_object_at(self, offset): if offset in self._offset_cache: return self._offset_cache[offset] - assert isinstance(offset, long) or isinstance(offset, int),\ - 'offset was %r' % offset assert offset >= self._header_size pack_reader = SwiftPackReader(self.scon, self._filename, self.pack_length) @@ -803,7 +811,9 @@ # Move the pack in. entries.sort() pack_base_name = posixpath.join( - self.pack_dir, 'pack-' + iter_sha1(e[0] for e in entries)) + self.pack_dir, + 'pack-' + iter_sha1(e[0] for e in entries).decode( + sys.getfilesystemencoding())) self.scon.put_object(pack_base_name + '.pack', f) # Write the index. @@ -842,7 +852,7 @@ self.store = store f = self.scon.get_object(self.filename) if not f: - f = BytesIO('') + f = BytesIO(b'') super(SwiftInfoRefsContainer, self).__init__(f) def _load_check_ref(self, name, old_ref): @@ -922,6 +932,13 @@ refs = SwiftInfoRefsContainer(self.scon, object_store) BaseRepo.__init__(self, object_store, refs) + def _determine_file_mode(self): + """Probe the file-system to determine whether permissions can be trusted. + + :return: True if permissions can be trusted, False otherwise. + """ + return False + def _put_named_file(self, filename, contents): """Put an object in a Swift container @@ -944,7 +961,7 @@ scon.create_root() for obj in [posixpath.join(OBJECTDIR, PACKDIR), posixpath.join(INFODIR, 'refs')]: - scon.put_object(obj, BytesIO('')) + scon.put_object(obj, BytesIO(b'')) ret = cls(scon.root, conf) ret._init_files(True) return ret @@ -978,7 +995,7 @@ try: import gevent - import geventhttpclient + import geventhttpclient # noqa: F401 except ImportError: print("gevent and geventhttpclient libraries are mandatory " " for use the Swift backend.") @@ -1020,14 +1037,16 @@ } if len(sys.argv) < 2: - print("Usage: %s <%s> [OPTIONS...]" % (sys.argv[0], "|".join(commands.keys()))) + print("Usage: %s <%s> [OPTIONS...]" % ( + sys.argv[0], "|".join(commands.keys()))) sys.exit(1) cmd = sys.argv[1] - if not cmd in commands: + if cmd not in commands: print("No such subcommand: %s" % cmd) sys.exit(1) commands[cmd](sys.argv[2:]) + if __name__ == '__main__': main() diff -Nru dulwich-0.12.0/dulwich/contrib/test_release_robot.py dulwich-0.18.5/dulwich/contrib/test_release_robot.py --- dulwich-0.12.0/dulwich/contrib/test_release_robot.py 1970-01-01 00:00:00.000000000 +0000 +++ dulwich-0.18.5/dulwich/contrib/test_release_robot.py 2017-10-07 15:45:17.000000000 +0000 @@ -0,0 +1,127 @@ +# release_robot.py +# +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. +# + +"""Tests for release_robot.""" + +import datetime +import os +import re +import shutil +import tempfile +import time +import unittest + +from dulwich.contrib import release_robot +from dulwich.repo import Repo +from dulwich.tests.utils import make_commit, make_tag + +BASEDIR = os.path.abspath(os.path.dirname(__file__)) # this directory + + +def gmtime_to_datetime(gmt): + return datetime.datetime(*time.gmtime(gmt)[:6]) + + +class TagPatternTests(unittest.TestCase): + """test tag patterns""" + + def test_tag_pattern(self): + """test tag patterns""" + test_cases = { + '0.3': '0.3', 'v0.3': '0.3', 'release0.3': '0.3', + 'Release-0.3': '0.3', 'v0.3rc1': '0.3rc1', 'v0.3-rc1': '0.3-rc1', + 'v0.3-rc.1': '0.3-rc.1', 'version 0.3': '0.3', + 'version_0.3_rc_1': '0.3_rc_1', 'v1': '1', '0.3rc1': '0.3rc1' + } + for testcase, version in test_cases.items(): + matches = re.match(release_robot.PATTERN, testcase) + self.assertEqual(matches.group(1), version) + + +class GetRecentTagsTest(unittest.TestCase): + """test get recent tags""" + + # Git repo for dulwich project + test_repo = os.path.join(BASEDIR, 'dulwich_test_repo.zip') + committer = b"Mark Mikofski " + test_tags = [b'v0.1a', b'v0.1'] + tag_test_data = { + test_tags[0]: [1484788003, b'0' * 40, None], + test_tags[1]: [1484788314, b'1' * 40, (1484788401, b'2' * 40)] + } + + @classmethod + def setUpClass(cls): + cls.projdir = tempfile.mkdtemp() # temporary project directory + cls.repo = Repo.init(cls.projdir) # test repo + obj_store = cls.repo.object_store # test repo object store + # commit 1 ('2017-01-19T01:06:43') + cls.c1 = make_commit( + id=cls.tag_test_data[cls.test_tags[0]][1], + commit_time=cls.tag_test_data[cls.test_tags[0]][0], + message=b'unannotated tag', + author=cls.committer + ) + obj_store.add_object(cls.c1) + # tag 1: unannotated + cls.t1 = cls.test_tags[0] + cls.repo[b'refs/tags/' + cls.t1] = cls.c1.id # add unannotated tag + # commit 2 ('2017-01-19T01:11:54') + cls.c2 = make_commit( + id=cls.tag_test_data[cls.test_tags[1]][1], + commit_time=cls.tag_test_data[cls.test_tags[1]][0], + message=b'annotated tag', + parents=[cls.c1.id], + author=cls.committer + ) + obj_store.add_object(cls.c2) + # tag 2: annotated ('2017-01-19T01:13:21') + cls.t2 = make_tag( + cls.c2, + id=cls.tag_test_data[cls.test_tags[1]][2][1], + name=cls.test_tags[1], + tag_time=cls.tag_test_data[cls.test_tags[1]][2][0] + ) + obj_store.add_object(cls.t2) + cls.repo[b'refs/heads/master'] = cls.c2.id + cls.repo[b'refs/tags/' + cls.t2.name] = cls.t2.id # add annotated tag + + @classmethod + def tearDownClass(cls): + cls.repo.close() + shutil.rmtree(cls.projdir) + + def test_get_recent_tags(self): + """test get recent tags""" + tags = release_robot.get_recent_tags(self.projdir) # get test tags + for tag, metadata in tags: + tag = tag.encode('utf-8') + test_data = self.tag_test_data[tag] # test data tag + # test commit date, id and author name + self.assertEqual(metadata[0], gmtime_to_datetime(test_data[0])) + self.assertEqual(metadata[1].encode('utf-8'), test_data[1]) + self.assertEqual(metadata[2].encode('utf-8'), self.committer) + # skip unannotated tags + tag_obj = test_data[2] + if not tag_obj: + continue + # tag date, id and name + self.assertEqual(metadata[3][0], gmtime_to_datetime(tag_obj[0])) + self.assertEqual(metadata[3][1].encode('utf-8'), tag_obj[1]) + self.assertEqual(metadata[3][2].encode('utf-8'), tag) diff -Nru dulwich-0.12.0/dulwich/contrib/test_swift.py dulwich-0.18.5/dulwich/contrib/test_swift.py --- dulwich-0.12.0/dulwich/contrib/test_swift.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/contrib/test_swift.py 2017-10-07 15:45:17.000000000 +0000 @@ -3,21 +3,22 @@ # # Author: Fabien Boucher # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for dulwich.contrib.swift.""" @@ -26,9 +27,12 @@ from time import time from io import BytesIO try: - from unittest import skipIf + from StringIO import StringIO except ImportError: - from unittest2 import skipIf + from io import StringIO + +import sys +from unittest import skipIf from dulwich.tests import ( TestCase, @@ -61,12 +65,12 @@ missing_libs = [] try: - import gevent + import gevent # noqa:F401 except ImportError: missing_libs.append("gevent") try: - import geventhttpclient + import geventhttpclient # noqa:F401 except ImportError: missing_libs.append("geventhttpclient") @@ -77,6 +81,9 @@ skipmsg = "Required libraries are not installed (%r)" % missing_libs +skipIfPY3 = skipIf(sys.version_info[0] == 3, + "SWIFT module not yet ported to python3.") + if not missing_libs: from dulwich.contrib import swift @@ -122,11 +129,7 @@ return self.headers[key] def items(self): - return self.headers - - def iteritems(self): - for k, v in self.headers.iteritems(): - yield k, v + return self.headers.items() def read(self): return self.content @@ -164,41 +167,42 @@ return ret -def create_commit(data, marker='Default', blob=None): +def create_commit(data, marker=b'Default', blob=None): if not blob: - blob = Blob.from_string('The blob content %s' % marker) + blob = Blob.from_string(b'The blob content ' + marker) tree = Tree() - tree.add("thefile_%s" % marker, 0o100644, blob.id) + tree.add(b"thefile_" + marker, 0o100644, blob.id) cmt = Commit() if data: assert isinstance(data[-1], Commit) cmt.parents = [data[-1].id] cmt.tree = tree.id - author = "John Doe %s " % marker + author = b"John Doe " + marker + b" " cmt.author = cmt.committer = author - tz = parse_timezone('-0200')[0] + tz = parse_timezone(b'-0200')[0] cmt.commit_time = cmt.author_time = int(time()) cmt.commit_timezone = cmt.author_timezone = tz - cmt.encoding = "UTF-8" - cmt.message = "The commit message %s" % marker + cmt.encoding = b"UTF-8" + cmt.message = b"The commit message " + marker tag = Tag() - tag.tagger = "john@doe.net" - tag.message = "Annotated tag" - tag.tag_timezone = parse_timezone('-0200')[0] + tag.tagger = b"john@doe.net" + tag.message = b"Annotated tag" + tag.tag_timezone = parse_timezone(b'-0200')[0] tag.tag_time = cmt.author_time tag.object = (Commit, cmt.id) - tag.name = "v_%s_0.1" % marker + tag.name = b"v_" + marker + b"_0.1" return blob, tree, tag, cmt -def create_commits(length=1, marker='Default'): +def create_commits(length=1, marker=b'Default'): data = [] for i in range(0, length): - _marker = "%s_%s" % (marker, i) + _marker = ("%s_%s" % (marker, i)).encode() blob, tree, tag, cmt = create_commit(data, _marker) data.extend([blob, tree, tag, cmt]) return data + @skipIf(missing_libs, skipmsg) class FakeSwiftConnector(object): @@ -250,17 +254,18 @@ def get_object_stat(self, name): name = posixpath.join(self.root, name) - if not name in self.store: + if name not in self.store: return None return {'content-length': len(self.store[name])} @skipIf(missing_libs, skipmsg) +@skipIfPY3 class TestSwiftObjectStore(TestCase): def setUp(self): super(TestSwiftObjectStore, self).setUp() - self.conf = swift.load_conf(file=BytesIO(config_file % + self.conf = swift.load_conf(file=StringIO(config_file % def_config_file)) self.fsc = FakeSwiftConnector('fakerepo', conf=self.conf) @@ -312,7 +317,9 @@ head = odata[-1].id peeled_sha = dict([(sha.object[1], sha.id) for sha in odata if isinstance(sha, Tag)]) - get_tagged = lambda: peeled_sha + + def get_tagged(): + return peeled_sha i = sos.iter_shas(sos.find_missing_objects([], [head, ], progress=None, @@ -379,7 +386,7 @@ def setUp(self): super(TestSwiftRepo, self).setUp() - self.conf = swift.load_conf(file=BytesIO(config_file % + self.conf = swift.load_conf(file=StringIO(config_file % def_config_file)) def test_init(self): @@ -409,7 +416,7 @@ new_callable=create_swift_connector, store=store): repo = swift.SwiftRepo('fakerepo', conf=self.conf) - desc = 'Fake repo' + desc = b'Fake repo' repo._put_named_file('description', desc) self.assertEqual(repo.scon.store['fakerepo/description'], desc) @@ -426,9 +433,12 @@ @skipIf(missing_libs, skipmsg) +@skipIfPY3 class TestPackInfoLoadDump(TestCase): + def setUp(self): - conf = swift.load_conf(file=BytesIO(config_file % + super(TestPackInfoLoadDump, self).setUp() + conf = swift.load_conf(file=StringIO(config_file % def_config_file)) sos = swift.SwiftObjectStore( FakeSwiftConnector('fakerepo', conf=conf)) @@ -472,11 +482,11 @@ def setUp(self): super(TestSwiftInfoRefsContainer, self).setUp() - content = \ - "22effb216e3a82f97da599b8885a6cadb488b4c5\trefs/heads/master\n" + \ - "cca703b0e1399008b53a1a236d6b4584737649e4\trefs/heads/dev" + content = ( + b"22effb216e3a82f97da599b8885a6cadb488b4c5\trefs/heads/master\n" + b"cca703b0e1399008b53a1a236d6b4584737649e4\trefs/heads/dev") self.store = {'fakerepo/info/refs': content} - self.conf = swift.load_conf(file=BytesIO(config_file % + self.conf = swift.load_conf(file=StringIO(config_file % def_config_file)) self.fsc = FakeSwiftConnector('fakerepo', conf=self.conf) self.object_store = {} @@ -487,22 +497,22 @@ self.assertEqual(len(irc._refs), 0) self.fsc.store = self.store irc = swift.SwiftInfoRefsContainer(self.fsc, self.object_store) - self.assertIn('refs/heads/dev', irc.allkeys()) - self.assertIn('refs/heads/master', irc.allkeys()) + self.assertIn(b'refs/heads/dev', irc.allkeys()) + self.assertIn(b'refs/heads/master', irc.allkeys()) def test_set_if_equals(self): self.fsc.store = self.store irc = swift.SwiftInfoRefsContainer(self.fsc, self.object_store) - irc.set_if_equals('refs/heads/dev', - "cca703b0e1399008b53a1a236d6b4584737649e4", '1'*40) - self.assertEqual(irc['refs/heads/dev'], '1'*40) + irc.set_if_equals(b'refs/heads/dev', + b"cca703b0e1399008b53a1a236d6b4584737649e4", b'1'*40) + self.assertEqual(irc[b'refs/heads/dev'], b'1'*40) def test_remove_if_equals(self): self.fsc.store = self.store irc = swift.SwiftInfoRefsContainer(self.fsc, self.object_store) - irc.remove_if_equals('refs/heads/dev', - "cca703b0e1399008b53a1a236d6b4584737649e4") - self.assertNotIn('refs/heads/dev', irc.allkeys()) + irc.remove_if_equals(b'refs/heads/dev', + b"cca703b0e1399008b53a1a236d6b4584737649e4") + self.assertNotIn(b'refs/heads/dev', irc.allkeys()) @skipIf(missing_libs, skipmsg) @@ -510,7 +520,7 @@ def setUp(self): super(TestSwiftConnector, self).setUp() - self.conf = swift.load_conf(file=BytesIO(config_file % + self.conf = swift.load_conf(file=StringIO(config_file % def_config_file)) with patch('geventhttpclient.HTTPClient.request', fake_auth_request_v1): @@ -556,9 +566,9 @@ def test_create_root(self): with patch('dulwich.contrib.swift.SwiftConnector.test_root_exists', - lambda *args: None): + lambda *args: None): with patch('geventhttpclient.HTTPClient.request', - lambda *args: Response()): + lambda *args: Response()): self.assertEqual(self.conn.create_root(), None) def test_create_root_fails(self): @@ -594,7 +604,7 @@ def test_put_object(self): with patch('geventhttpclient.HTTPClient.request', lambda *args, **kwargs: Response()): - self.assertEqual(self.conn.put_object('a', BytesIO('content')), + self.assertEqual(self.conn.put_object('a', BytesIO(b'content')), None) def test_put_object_fails(self): @@ -602,15 +612,17 @@ lambda *args, **kwargs: Response(status=400)): self.assertRaises(swift.SwiftException, lambda: self.conn.put_object( - 'a', BytesIO('content'))) + 'a', BytesIO(b'content'))) def test_get_object(self): with patch('geventhttpclient.HTTPClient.request', - lambda *args, **kwargs: Response(content='content')): - self.assertEqual(self.conn.get_object('a').read(), 'content') + lambda *args, **kwargs: Response(content=b'content')): + self.assertEqual(self.conn.get_object('a').read(), b'content') with patch('geventhttpclient.HTTPClient.request', - lambda *args, **kwargs: Response(content='content')): - self.assertEqual(self.conn.get_object('a', range='0-6'), 'content') + lambda *args, **kwargs: Response(content=b'content')): + self.assertEqual( + self.conn.get_object('a', range='0-6'), + b'content') def test_get_object_fails(self): with patch('geventhttpclient.HTTPClient.request', @@ -638,7 +650,7 @@ def setUp(self): TestCase.setUp(self) - conf = swift.load_conf(file=BytesIO(config_file % + conf = swift.load_conf(file=StringIO(config_file % def_config_file)) fsc = FakeSwiftConnector('fakerepo', conf=conf) self.store = swift.SwiftObjectStore(fsc) diff -Nru dulwich-0.12.0/dulwich/contrib/test_swift_smoke.py dulwich-0.18.5/dulwich/contrib/test_swift_smoke.py --- dulwich-0.12.0/dulwich/contrib/test_swift_smoke.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/contrib/test_swift_smoke.py 2017-10-07 15:45:17.000000000 +0000 @@ -3,21 +3,22 @@ # # Author: Fabien Boucher # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Start functional tests @@ -41,12 +42,14 @@ from gevent import monkey monkey.patch_all() -from dulwich import server -from dulwich import repo -from dulwich import index -from dulwich import client -from dulwich import objects -from dulwich.contrib import swift +from dulwich import ( # noqa:E402 + server, + repo, + index, + client, + objects, + ) +from dulwich.contrib import swift # noqa:E402 class DulwichServer(): @@ -201,7 +204,7 @@ files = ('testfile', 'testfile2', 'dir/testfile3') i = 0 for f in files: - file(os.path.join(self.temp_d, f), 'w').write("DATA %s" % i) + open(os.path.join(self.temp_d, f), 'w').write("DATA %s" % i) i += 1 local_repo.stage(files) local_repo.do_commit('Test commit', 'fbo@localhost', @@ -251,7 +254,7 @@ files = ('testfile11', 'testfile22', 'test/testfile33') i = 0 for f in files: - file(os.path.join(self.temp_d, f), 'w').write("DATA %s" % i) + open(os.path.join(self.temp_d, f), 'w').write("DATA %s" % i) i += 1 local_repo.stage(files) local_repo.do_commit('Test commit', 'fbo@localhost', diff -Nru dulwich-0.12.0/dulwich/_diff_tree.c dulwich-0.18.5/dulwich/_diff_tree.c --- dulwich-0.12.0/dulwich/_diff_tree.c 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/_diff_tree.c 2017-10-07 15:45:17.000000000 +0000 @@ -1,20 +1,21 @@ /* * Copyright (C) 2010 Google, Inc. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License or (at your option) a later version of the License. + * Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU + * General Public License as public by the Free Software Foundation; version 2.0 + * or (at your option) any later version. You can redistribute it and/or + * modify it under the terms of either of these two licenses. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. + * You should have received a copy of the licenses; if not, see + * for a copy of the GNU General Public License + * and for a copy of the Apache + * License, Version 2.0. */ #include @@ -24,12 +25,8 @@ typedef unsigned short mode_t; #endif -#if (PY_VERSION_HEX < 0x02050000) -typedef int Py_ssize_t; -#endif - -#if (PY_VERSION_HEX < 0x02060000) -#define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size) +#if PY_MAJOR_VERSION < 3 +typedef long Py_hash_t; #endif #if PY_MAJOR_VERSION >= 3 @@ -37,7 +34,6 @@ #define PyInt_AsLong PyLong_AsLong #define PyInt_AS_LONG PyLong_AS_LONG #define PyString_AS_STRING PyBytes_AS_STRING -#define PyString_AsString PyBytes_AsString #define PyString_AsStringAndSize PyBytes_AsStringAndSize #define PyString_Check PyBytes_Check #define PyString_CheckExact PyBytes_CheckExact @@ -300,11 +296,11 @@ return result; } -static int add_hash(PyObject *get, PyObject *set, char *str, int n) +static Py_hash_t add_hash(PyObject *get, PyObject *set, char *str, int n) { PyObject *str_obj = NULL, *hash_obj = NULL, *value = NULL, *set_value = NULL; - long hash; + Py_hash_t hash; /* It would be nice to hash without copying str into a PyString, but that * isn't exposed by the API. */ diff -Nru dulwich-0.12.0/dulwich/diff_tree.py dulwich-0.18.5/dulwich/diff_tree.py --- dulwich-0.12.0/dulwich/diff_tree.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/diff_tree.py 2017-10-07 15:45:17.000000000 +0000 @@ -1,20 +1,22 @@ # diff_tree.py -- Utilities for diffing files and trees. # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Utilities for diffing files and trees.""" import sys @@ -171,10 +173,10 @@ source and target tree. """ if (rename_detector is not None and tree1_id is not None and - tree2_id is not None): + tree2_id is not None): for change in rename_detector.changes_with_renames( - tree1_id, tree2_id, want_unchanged=want_unchanged): - yield change + tree1_id, tree2_id, want_unchanged=want_unchanged): + yield change return entries = walk_trees(store, tree1_id, tree2_id, @@ -253,8 +255,11 @@ path = change.new.path changes_by_path[path][i] = change - old_sha = lambda c: c.old.sha - change_type = lambda c: c.type + def old_sha(c): + return c.old.sha + + def change_type(c): + return c.type # Yield only conflicting changes. for _, changes in sorted(changes_by_path.items()): @@ -379,9 +384,9 @@ an add/delete pair to be a rename/copy; see _similarity_score. :param max_files: The maximum number of adds and deletes to consider, or None for no limit. The detector is guaranteed to compare no more - than max_files ** 2 add/delete pairs. This limit is provided because - rename detection can be quadratic in the project size. If the limit - is exceeded, no content rename detection is attempted. + than max_files ** 2 add/delete pairs. This limit is provided + because rename detection can be quadratic in the project size. If + the limit is exceeded, no content rename detection is attempted. :param rewrite_threshold: The threshold similarity score below which a modify should be considered a delete/add, or None to not break modifies; see _similarity_score. @@ -402,7 +407,7 @@ def _should_split(self, change): if (self._rewrite_threshold is None or change.type != CHANGE_MODIFY or - change.old.sha == change.new.sha): + change.old.sha == change.new.sha): return False old_obj = self._store[change.old.sha] new_obj = self._store[change.new.sha] @@ -549,7 +554,7 @@ path = add.new.path delete = delete_map.get(path) if (delete is not None and - stat.S_IFMT(delete.old.mode) == stat.S_IFMT(add.new.mode)): + stat.S_IFMT(delete.old.mode) == stat.S_IFMT(add.new.mode)): modifies[path] = TreeChange(CHANGE_MODIFY, delete.old, add.new) self._adds = [a for a in self._adds if a.new.path not in modifies] @@ -568,7 +573,8 @@ def _prune_unchanged(self): if self._want_unchanged: return - self._deletes = [d for d in self._deletes if d.type != CHANGE_UNCHANGED] + self._deletes = [ + d for d in self._deletes if d.type != CHANGE_UNCHANGED] def changes_with_renames(self, tree1_id, tree2_id, want_unchanged=False): """Iterate TreeChanges between two tree SHAs, with rename detection.""" diff -Nru dulwich-0.12.0/dulwich/errors.py dulwich-0.18.5/dulwich/errors.py --- dulwich-0.12.0/dulwich/errors.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/errors.py 2017-10-07 15:45:17.000000000 +0000 @@ -2,20 +2,22 @@ # Copyright (C) 2007 James Westby # Copyright (C) 2009-2012 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# or (at your option) any later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Dulwich-related exception classes and utility functions.""" @@ -34,11 +36,12 @@ self.got = got self.extra = extra if self.extra is None: - Exception.__init__(self, - "Checksum mismatch: Expected %s, got %s" % (expected, got)) + Exception.__init__( + self, "Checksum mismatch: Expected %s, got %s" % + (expected, got)) else: - Exception.__init__(self, - "Checksum mismatch: Expected %s, got %s; %s" % + Exception.__init__( + self, "Checksum mismatch: Expected %s, got %s; %s" % (expected, got, extra)) @@ -134,8 +137,8 @@ """Hangup exception.""" def __init__(self): - Exception.__init__(self, - "The remote server unexpectedly closed the connection.") + Exception.__init__( + self, "The remote server unexpectedly closed the connection.") class UnexpectedCommandError(GitProtocolError): diff -Nru dulwich-0.12.0/dulwich/fastexport.py dulwich-0.18.5/dulwich/fastexport.py --- dulwich-0.12.0/dulwich/fastexport.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/fastexport.py 2017-10-07 15:45:17.000000000 +0000 @@ -1,25 +1,28 @@ # __init__.py -- Fast export/import functionality # Copyright (C) 2010-2013 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Fast export/import functionality.""" +import sys + from dulwich.index import ( commit_tree, ) @@ -27,20 +30,25 @@ Blob, Commit, Tag, + ZERO_SHA, ) -from fastimport import ( +from fastimport import __version__ as fastimport_version +if (fastimport_version <= (0, 9, 5) and + sys.version_info[0] == 3 and sys.version_info[1] < 5): + raise ImportError("Older versions of fastimport don't support python3<3.5") +from fastimport import ( # noqa: E402 commands, errors as fastimport_errors, parser, processor, ) -import stat +import stat # noqa: E402 def split_email(text): - (name, email) = text.rsplit(" <", 1) - return (name, email.rstrip(">")) + (name, email) = text.rsplit(b" <", 1) + return (name, email.rstrip(b">")) class GitFastExporter(object): @@ -53,11 +61,11 @@ self._marker_idx = 0 def print_cmd(self, cmd): - self.outf.write("%r\n" % cmd) + self.outf.write(getattr(cmd, "__bytes__", cmd.__repr__)() + b"\n") def _allocate_marker(self): - self._marker_idx+=1 - return str(self._marker_idx) + self._marker_idx += 1 + return ("%d" % (self._marker_idx,)).encode('ascii') def _export_blob(self, blob): marker = self._allocate_marker() @@ -71,7 +79,7 @@ def _iter_files(self, base_tree, new_tree): for ((old_path, new_path), (old_mode, new_mode), - (old_hexsha, new_hexsha)) in \ + (old_hexsha, new_hexsha)) in \ self.store.tree_changes(base_tree, new_tree): if new_path is None: yield commands.FileDeleteCommand(old_path) @@ -82,8 +90,10 @@ if old_path != new_path and old_path is not None: yield commands.FileRenameCommand(old_path, new_path) if old_mode != new_mode or old_hexsha != new_hexsha: - yield commands.FileModifyCommand(new_path, new_mode, marker, - None) + prefixed_marker = b':' + marker + yield commands.FileModifyCommand( + new_path, new_mode, prefixed_marker, None + ) def _export_commit(self, commit, ref, base_tree=None): file_cmds = list(self._iter_files(base_tree, commit.tree)) @@ -96,7 +106,8 @@ merges = [] author, author_email = split_email(commit.author) committer, committer_email = split_email(commit.committer) - cmd = commands.CommitCommand(ref, marker, + cmd = commands.CommitCommand( + ref, marker, (author, author_email, commit.author_time, commit.author_timezone), (committer, committer_email, commit.commit_time, commit.commit_timezone), @@ -118,7 +129,7 @@ def __init__(self, repo, params=None, verbose=False, outf=None): processor.ImportProcessor.__init__(self, params, verbose) self.repo = repo - self.last_commit = None + self.last_commit = ZERO_SHA self.markers = {} self._contents = {} @@ -148,10 +159,10 @@ (author_name, author_email, author_timestamp, author_timezone) = author (committer_name, committer_email, commit_timestamp, commit_timezone) = cmd.committer - commit.author = "%s <%s>" % (author_name, author_email) + commit.author = author_name + b" <" + author_email + b">" commit.author_timezone = author_timezone commit.author_time = int(author_timestamp) - commit.committer = "%s <%s>" % (committer_name, committer_email) + commit.committer = committer_name + b" <" + committer_email + b">" commit.commit_timezone = commit_timezone commit.commit_time = int(commit_timestamp) commit.message = cmd.message @@ -159,35 +170,40 @@ if cmd.from_: self._reset_base(cmd.from_) for filecmd in cmd.iter_files(): - if filecmd.name == "filemodify": + if filecmd.name == b"filemodify": if filecmd.data is not None: blob = Blob.from_string(filecmd.data) self.repo.object_store.add(blob) blob_id = blob.id else: - assert filecmd.dataref[0] == ":", \ - "non-marker refs not supported yet" + assert filecmd.dataref.startswith(b":"), \ + ("non-marker refs not supported yet (%r)" % + filecmd.dataref) blob_id = self.markers[filecmd.dataref[1:]] self._contents[filecmd.path] = (filecmd.mode, blob_id) - elif filecmd.name == "filedelete": + elif filecmd.name == b"filedelete": del self._contents[filecmd.path] - elif filecmd.name == "filecopy": + elif filecmd.name == b"filecopy": self._contents[filecmd.dest_path] = self._contents[ filecmd.src_path] - elif filecmd.name == "filerename": + elif filecmd.name == b"filerename": self._contents[filecmd.new_path] = self._contents[ filecmd.old_path] del self._contents[filecmd.old_path] - elif filecmd.name == "filedeleteall": + elif filecmd.name == b"filedeleteall": self._contents = {} else: raise Exception("Command %s not supported" % filecmd.name) - commit.tree = commit_tree(self.repo.object_store, + commit.tree = commit_tree( + self.repo.object_store, ((path, hexsha, mode) for (path, (mode, hexsha)) in - self._contents.iteritems())) - if self.last_commit is not None: + self._contents.items())) + if self.last_commit != ZERO_SHA: commit.parents.append(self.last_commit) - commit.parents += cmd.merges + for merge in cmd.merges: + if merge.startswith(b':'): + merge = self.markers[merge[1:]] + commit.parents.append(merge) self.repo.object_store.add_object(commit) self.repo[cmd.ref] = commit.id self.last_commit = commit.id @@ -201,17 +217,24 @@ def _reset_base(self, commit_id): if self.last_commit == commit_id: return - self.last_commit = commit_id self._contents = {} - tree_id = self.repo[commit_id].tree - for (path, mode, hexsha) in ( - self.repo.object_store.iter_tree_contents(tree_id)): - self._contents[path] = (mode, hexsha) + self.last_commit = commit_id + if commit_id != ZERO_SHA: + tree_id = self.repo[commit_id].tree + for (path, mode, hexsha) in ( + self.repo.object_store.iter_tree_contents(tree_id)): + self._contents[path] = (mode, hexsha) def reset_handler(self, cmd): """Process a ResetCommand.""" - self._reset_base(cmd.from_) - self.repo.refs[cmd.ref] = cmd.from_ + if cmd.from_ is None: + from_ = ZERO_SHA + else: + from_ = cmd.from_ + if from_.startswith(b":"): + from_ = self.markers[from_[1:]] + self._reset_base(from_) + self.repo.refs[cmd.ref] = from_ def tag_handler(self, cmd): """Process a TagCommand.""" diff -Nru dulwich-0.12.0/dulwich/file.py dulwich-0.18.5/dulwich/file.py --- dulwich-0.12.0/dulwich/file.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/file.py 2017-10-18 22:29:44.000000000 +0000 @@ -1,20 +1,22 @@ # file.py -- Safe access to git files # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Safe access to git files.""" @@ -24,6 +26,7 @@ import sys import tempfile + def ensure_dir_exists(dirname): """Ensure a directory exists, creating if necessary.""" try: @@ -87,6 +90,15 @@ return io.open(filename, mode, bufsize) +class FileLocked(Exception): + """File is already locked.""" + + def __init__(self, filename, lockfilename): + self.filename = filename + self.lockfilename = lockfilename + super(FileLocked, self).__init__(filename, lockfilename) + + class _GitFile(object): """File that follows the git locking protocol for writes. @@ -103,11 +115,19 @@ PROXY_METHODS = ('__iter__', 'flush', 'fileno', 'isatty', 'read', 'readline', 'readlines', 'seek', 'tell', 'truncate', 'write', 'writelines') + def __init__(self, filename, mode, bufsize): self._filename = filename self._lockfilename = '%s.lock' % self._filename - fd = os.open(self._lockfilename, - os.O_RDWR | os.O_CREAT | os.O_EXCL | getattr(os, "O_BINARY", 0)) + try: + fd = os.open( + self._lockfilename, + os.O_RDWR | os.O_CREAT | os.O_EXCL | + getattr(os, "O_BINARY", 0)) + except OSError as e: + if e.errno == errno.EEXIST: + raise FileLocked(filename, self._lockfilename) + raise self._file = os.fdopen(fd, mode, bufsize) self._closed = False @@ -135,22 +155,24 @@ """Close this file, saving the lockfile over the original. :note: If this method fails, it will attempt to delete the lockfile. - However, it is not guaranteed to do so (e.g. if a filesystem becomes - suddenly read-only), which will prevent future writes to this file - until the lockfile is removed manually. - :raises OSError: if the original file could not be overwritten. The lock - file is still closed, so further attempts to write to the same file - object will raise ValueError. + However, it is not guaranteed to do so (e.g. if a filesystem + becomes suddenly read-only), which will prevent future writes to + this file until the lockfile is removed manually. + :raises OSError: if the original file could not be overwritten. The + lock file is still closed, so further attempts to write to the same + file object will raise ValueError. """ if self._closed: return + os.fsync(self._file.fileno()) self._file.close() try: try: os.rename(self._lockfilename, self._filename) except OSError as e: if sys.platform == 'win32' and e.errno == errno.EEXIST: - # Windows versions prior to Vista don't support atomic renames + # Windows versions prior to Vista don't support atomic + # renames _fancy_rename(self._lockfilename, self._filename) else: raise diff -Nru dulwich-0.12.0/dulwich/greenthreads.py dulwich-0.18.5/dulwich/greenthreads.py --- dulwich-0.12.0/dulwich/greenthreads.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/greenthreads.py 2017-07-29 00:14:28.000000000 +0000 @@ -3,21 +3,22 @@ # # Author: Fabien Boucher # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Utility module for querying an ObjectStore with gevent.""" diff -Nru dulwich-0.12.0/dulwich/hooks.py dulwich-0.18.5/dulwich/hooks.py --- dulwich-0.12.0/dulwich/hooks.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/hooks.py 2017-10-07 15:45:17.000000000 +0000 @@ -1,20 +1,22 @@ # hooks.py -- for dealing with git hooks # Copyright (C) 2012-2013 Jelmer Vernooij and others. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Access to hooks.""" @@ -50,7 +52,8 @@ """ def __init__(self, name, path, numparam, - pre_exec_callback=None, post_exec_callback=None): + pre_exec_callback=None, post_exec_callback=None, + cwd=None): """Setup shell hook definition :param name: name of hook for error messages @@ -64,6 +67,7 @@ Defaults to None. Takes in a boolean for hook success and the modified argument list and returns the final hook return value if applicable + :param cwd: working directory to switch to when executing the hook """ self.name = name self.filepath = path @@ -72,6 +76,8 @@ self.pre_exec_callback = pre_exec_callback self.post_exec_callback = post_exec_callback + self.cwd = cwd + if sys.version_info[0] == 2 and sys.platform == 'win32': # Python 2 on windows does not support unicode file paths # http://bugs.python.org/issue1759845 @@ -89,7 +95,7 @@ args = self.pre_exec_callback(*args) try: - ret = subprocess.call([self.filepath] + list(args)) + ret = subprocess.call([self.filepath] + list(args), cwd=self.cwd) if ret != 0: if (self.post_exec_callback is not None): self.post_exec_callback(0, *args) @@ -108,7 +114,7 @@ def __init__(self, controldir): filepath = os.path.join(controldir, 'hooks', 'pre-commit') - ShellHook.__init__(self, 'pre-commit', filepath, 0) + ShellHook.__init__(self, 'pre-commit', filepath, 0, cwd=controldir) class PostCommitShellHook(ShellHook): @@ -117,7 +123,7 @@ def __init__(self, controldir): filepath = os.path.join(controldir, 'hooks', 'post-commit') - ShellHook.__init__(self, 'post-commit', filepath, 0) + ShellHook.__init__(self, 'post-commit', filepath, 0, cwd=controldir) class CommitMsgShellHook(ShellHook): @@ -147,4 +153,4 @@ os.unlink(args[0]) ShellHook.__init__(self, 'commit-msg', filepath, 1, - prepare_msg, clean_msg) + prepare_msg, clean_msg, controldir) diff -Nru dulwich-0.12.0/dulwich/ignore.py dulwich-0.18.5/dulwich/ignore.py --- dulwich-0.12.0/dulwich/ignore.py 1970-01-01 00:00:00.000000000 +0000 +++ dulwich-0.18.5/dulwich/ignore.py 2017-10-07 15:45:17.000000000 +0000 @@ -0,0 +1,358 @@ +# Copyright (C) 2017 Jelmer Vernooij +# +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. +# + +"""Parsing of gitignore files. + +For details for the matching rules, see https://git-scm.com/docs/gitignore +""" + +import os.path +import re +import sys + + +def _translate_segment(segment): + if segment == b"*": + return b'[^/]+' + res = b"" + i, n = 0, len(segment) + while i < n: + c = segment[i:i+1] + i = i+1 + if c == b'*': + res += b'[^/]*' + elif c == b'?': + res += b'.' + elif c == b'[': + j = i + if j < n and segment[j:j+1] == b'!': + j = j+1 + if j < n and segment[j:j+1] == b']': + j = j+1 + while j < n and segment[j:j+1] != b']': + j = j+1 + if j >= n: + res += b'\\[' + else: + stuff = segment[i:j].replace(b'\\', b'\\\\') + i = j+1 + if stuff.startswith(b'!'): + stuff = b'^' + stuff[1:] + elif stuff.startswith(b'^'): + stuff = b'\\' + stuff + res += b'[' + stuff + b']' + else: + res += re.escape(c) + return res + + +def translate(pat): + """Translate a shell PATTERN to a regular expression. + + There is no way to quote meta-characters. + + Originally copied from fnmatch in Python 2.7, but modified for Dulwich + to cope with features in Git ignore patterns. + """ + + res = b'(?ms)' + + if b'/' not in pat[:-1]: + # If there's no slash, this is a filename-based match + res += b'(.*/)?' + + if pat.startswith(b'**/'): + # Leading **/ + pat = pat[2:] + res += b'(.*/)?' + + if pat.startswith(b'/'): + pat = pat[1:] + + for i, segment in enumerate(pat.split(b'/')): + if segment == b'**': + res += b'(/.*)?' + continue + else: + res += ((re.escape(b'/') if i > 0 else b'') + + _translate_segment(segment)) + + if not pat.endswith(b'/'): + res += b'/?' + + return res + b'\Z' + + +def read_ignore_patterns(f): + """Read a git ignore file. + + :param f: File-like object to read from + :return: List of patterns + """ + + for l in f: + l = l.rstrip(b"\r\n") + + # Ignore blank lines, they're used for readability. + if not l: + continue + + if l.startswith(b'#'): + # Comment + continue + + # Trailing spaces are ignored unless they are quoted with a backslash. + while l.endswith(b' ') and not l.endswith(b'\\ '): + l = l[:-1] + l = l.replace(b'\\ ', b' ') + + yield l + + +def match_pattern(path, pattern, ignorecase=False): + """Match a gitignore-style pattern against a path. + + :param path: Path to match + :param pattern: Pattern to match + :param ignorecase: Whether to do case-sensitive matching + :return: bool indicating whether the pattern matched + """ + return Pattern(pattern, ignorecase).match(path) + + +class Pattern(object): + """A single ignore pattern.""" + + def __init__(self, pattern, ignorecase=False): + self.pattern = pattern + self.ignorecase = ignorecase + if pattern[0:1] == b'!': + self.is_exclude = False + pattern = pattern[1:] + else: + if pattern[0:1] == b'\\': + pattern = pattern[1:] + self.is_exclude = True + flags = 0 + if self.ignorecase: + flags = re.IGNORECASE + self._re = re.compile(translate(pattern), flags) + + def __bytes__(self): + return self.pattern + + def __str__(self): + return self.pattern.decode(sys.getfilesystemencoding()) + + def __eq__(self, other): + return (type(self) == type(other) and + self.pattern == other.pattern and + self.ignorecase == other.ignorecase) + + def __repr__(self): + return "%s(%s, %r)" % ( + type(self).__name__, self.pattern, self.ignorecase) + + def match(self, path): + """Try to match a path against this ignore pattern. + + :param path: Path to match (relative to ignore location) + :return: boolean + """ + return bool(self._re.match(path)) + + +class IgnoreFilter(object): + + def __init__(self, patterns, ignorecase=False): + self._patterns = [] + self._ignorecase = ignorecase + for pattern in patterns: + self.append_pattern(pattern) + + def append_pattern(self, pattern): + """Add a pattern to the set.""" + self._patterns.append(Pattern(pattern, self._ignorecase)) + + def find_matching(self, path): + """Yield all matching patterns for path. + + :param path: Path to match + :return: Iterator over iterators + """ + if not isinstance(path, bytes): + path = path.encode(sys.getfilesystemencoding()) + for pattern in self._patterns: + if pattern.match(path): + yield pattern + + def is_ignored(self, path): + """Check whether a path is ignored. + + For directories, include a trailing slash. + + :return: status is None if file is not mentioned, True if it is + included, False if it is explicitly excluded. + """ + status = None + for pattern in self.find_matching(path): + status = pattern.is_exclude + return status + + @classmethod + def from_path(cls, path, ignorecase=False): + with open(path, 'rb') as f: + ret = cls(read_ignore_patterns(f), ignorecase) + ret._path = path + return ret + + def __repr__(self): + if getattr(self, '_path', None) is None: + return "<%s>" % (type(self).__name__) + else: + return "%s.from_path(%r)" % (type(self).__name__, self._path) + + +class IgnoreFilterStack(object): + """Check for ignore status in multiple filters.""" + + def __init__(self, filters): + self._filters = filters + + def is_ignored(self, path): + """Check whether a path is explicitly included or excluded in ignores. + + :param path: Path to check + :return: None if the file is not mentioned, True if it is included, + False if it is explicitly excluded. + """ + status = None + for filter in self._filters: + status = filter.is_ignored(path) + if status is not None: + return status + return status + + +def default_user_ignore_filter_path(config): + """Return default user ignore filter path. + + :param config: A Config object + :return: Path to a global ignore file + """ + try: + return config.get(('core', ), 'excludesFile') + except KeyError: + pass + + xdg_config_home = os.environ.get( + "XDG_CONFIG_HOME", os.path.expanduser("~/.config/"), + ) + return os.path.join(xdg_config_home, 'git', 'ignore') + + +class IgnoreFilterManager(object): + """Ignore file manager.""" + + def __init__(self, top_path, global_filters, ignorecase): + self._path_filters = {} + self._top_path = top_path + self._global_filters = global_filters + self._ignorecase = ignorecase + + def __repr__(self): + return "%s(%s, %r, %r)" % ( + type(self).__name__, self._top_path, + self._global_filters, + self._ignorecase) + + def _load_path(self, path): + try: + return self._path_filters[path] + except KeyError: + pass + + p = os.path.join(self._top_path, path, '.gitignore') + try: + self._path_filters[path] = IgnoreFilter.from_path( + p, self._ignorecase) + except IOError: + self._path_filters[path] = None + return self._path_filters[path] + + def find_matching(self, path): + """Find matching patterns for path. + + Stops after the first ignore file with matches. + + :param path: Path to check + :return: Iterator over Pattern instances + """ + if os.path.isabs(path): + raise ValueError('%s is an absolute path' % path) + filters = [(0, f) for f in self._global_filters] + if os.path.sep != '/': + path = path.replace(os.path.sep, '/') + parts = path.split('/') + for i in range(len(parts)+1): + dirname = '/'.join(parts[:i]) + for s, f in filters: + relpath = '/'.join(parts[s:i]) + if i < len(parts): + # Paths leading up to the final part are all directories, + # so need a trailing slash. + relpath += '/' + matches = list(f.find_matching(relpath)) + if matches: + return iter(matches) + ignore_filter = self._load_path(dirname) + if ignore_filter is not None: + filters.insert(0, (i, ignore_filter)) + return iter([]) + + def is_ignored(self, path): + """Check whether a path is explicitly included or excluded in ignores. + + :param path: Path to check + :return: None if the file is not mentioned, True if it is included, + False if it is explicitly excluded. + """ + matches = list(self.find_matching(path)) + if matches: + return matches[-1].is_exclude + return None + + @classmethod + def from_repo(cls, repo): + """Create a IgnoreFilterManager from a repository. + + :param repo: Repository object + :return: A `IgnoreFilterManager` object + """ + global_filters = [] + for p in [ + os.path.join(repo.controldir(), 'info', 'exclude'), + default_user_ignore_filter_path(repo.get_config_stack())]: + try: + global_filters.append(IgnoreFilter.from_path(p)) + except IOError: + pass + config = repo.get_config_stack() + ignorecase = config.get_boolean((b'core'), (b'ignorecase'), False) + return cls(repo.path, global_filters, ignorecase) diff -Nru dulwich-0.12.0/dulwich/index.py dulwich-0.18.5/dulwich/index.py --- dulwich-0.12.0/dulwich/index.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/index.py 2017-10-15 15:20:08.000000000 +0000 @@ -1,20 +1,22 @@ # index.py -- File parser/writer for the git index file # Copyright (C) 2008-2013 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your opinion) any later version of the license. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Parser for the git index file format.""" @@ -122,8 +124,10 @@ (name, ctime, mtime, dev, ino, mode, uid, gid, size, sha, flags) = entry write_cache_time(f, ctime) write_cache_time(f, mtime) - flags = len(name) | (flags &~ 0x0fff) - f.write(struct.pack(b'>LLLLLL20sH', dev & 0xFFFFFFFF, ino & 0xFFFFFFFF, mode, uid, gid, size, hex_to_sha(sha), flags)) + flags = len(name) | (flags & ~0x0fff) + f.write(struct.pack( + b'>LLLLLL20sH', dev & 0xFFFFFFFF, ino & 0xFFFFFFFF, + mode, uid, gid, size, hex_to_sha(sha), flags)) f.write(name) real_size = ((f.tell() - beginoffset + 8) & ~7) f.write(b'\0' * ((beginoffset + real_size) - f.tell())) @@ -241,7 +245,8 @@ def __getitem__(self, name): """Retrieve entry by relative path. - :return: tuple with (ctime, mtime, dev, ino, mode, uid, gid, size, sha, flags) + :return: tuple with (ctime, mtime, dev, ino, mode, uid, gid, size, sha, + flags) """ return self._byname[name] @@ -271,7 +276,7 @@ assert isinstance(name, bytes) assert len(x) == 10 # Remove the old entry if any - self._byname[name] = x + self._byname[name] = IndexEntry(*x) def __delitem__(self, name): assert isinstance(name, bytes) @@ -290,13 +295,14 @@ :param object_store: Object store to use for retrieving tree contents :param tree: SHA1 of the root tree :param want_unchanged: Whether unchanged files should be reported - :return: Iterator over tuples with (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) + :return: Iterator over tuples with (oldpath, newpath), (oldmode, + newmode), (oldsha, newsha) """ def lookup_entry(path): entry = self[path] return entry.sha, entry.mode - for (name, mode, sha) in changes_from_tree(self._byname.keys(), - lookup_entry, object_store, tree, + for (name, mode, sha) in changes_from_tree( + self._byname.keys(), lookup_entry, object_store, tree, want_unchanged=want_unchanged): yield (name, mode, sha) @@ -361,7 +367,7 @@ def changes_from_tree(names, lookup_entry, object_store, tree, - want_unchanged=False): + want_unchanged=False): """Find the differences between the contents of a tree and a working copy. @@ -389,8 +395,12 @@ # Mention added files for name in other_names: - (other_sha, other_mode) = lookup_entry(name) - yield ((None, name), (None, other_mode), (None, other_sha)) + try: + (other_sha, other_mode) = lookup_entry(name) + except KeyError: + pass + else: + yield ((None, name), (None, other_mode), (None, other_sha)) def index_entry_from_stat(stat_val, hex_sha, flags, mode=None): @@ -415,26 +425,42 @@ :param target_path: Path to write to :param honor_filemode: An optional flag to honor core.filemode setting in config file, default is core.filemode=True, change executable bit + :return: stat object for the file """ + try: + oldstat = os.lstat(target_path) + except OSError as e: + if e.errno == errno.ENOENT: + oldstat = None + else: + raise + contents = blob.as_raw_string() if stat.S_ISLNK(mode): # FIXME: This will fail on Windows. What should we do instead? - src_path = blob.as_raw_string() - try: - os.symlink(src_path, target_path) - except OSError as e: - if e.errno == errno.EEXIST: - os.unlink(target_path) - os.symlink(src_path, target_path) - else: - raise + if oldstat: + os.unlink(target_path) + if sys.platform == 'win32' and sys.version_info[0] == 3: + # os.readlink on Python3 on Windows requires a unicode string. + # TODO(jelmer): Don't assume tree_encoding == fs_encoding + tree_encoding = sys.getfilesystemencoding() + contents = contents.decode(tree_encoding) + target_path = target_path.decode(tree_encoding) + os.symlink(contents, target_path) else: + if oldstat is not None and oldstat.st_size == len(contents): + with open(target_path, 'rb') as f: + if f.read() == contents: + return oldstat + with open(target_path, 'wb') as f: # Write out file - f.write(blob.as_raw_string()) + f.write(contents) if honor_filemode: os.chmod(target_path, mode) + return os.lstat(target_path) + INVALID_DOTNAMES = (b".git", b".", b"..", b"") @@ -473,8 +499,8 @@ :param object_store: Non-empty object store holding tree contents :param honor_filemode: An optional flag to honor core.filemode setting in config file, default is core.filemode=True, change executable bit - :param validate_path_element: Function to validate path elements to check out; - default just refuses .git and .. directories. + :param validate_path_element: Function to validate path elements to check + out; default just refuses .git and .. directories. :note:: existing index is wiped and contents are not merged in a working dir. Suitable only for fresh clones. @@ -492,12 +518,25 @@ if not os.path.exists(os.path.dirname(full_path)): os.makedirs(os.path.dirname(full_path)) - # FIXME: Merge new index into working tree - obj = object_store[entry.sha] - build_file_from_blob(obj, entry.mode, full_path, - honor_filemode=honor_filemode) + # TODO(jelmer): Merge new index into working tree + if S_ISGITLINK(entry.mode): + if not os.path.isdir(full_path): + os.mkdir(full_path) + st = os.lstat(full_path) + # TODO(jelmer): record and return submodule paths + else: + obj = object_store[entry.sha] + st = build_file_from_blob( + obj, entry.mode, full_path, honor_filemode=honor_filemode) # Add file to index - st = os.lstat(full_path) + if not honor_filemode or S_ISGITLINK(entry.mode): + # we can not use tuple slicing to build a new tuple, + # because on windows that will convert the times to + # longs, which causes errors further along + st_tuple = (entry.mode, st.st_ino, st.st_dev, st.st_nlink, + st.st_uid, st.st_gid, st.st_size, st.st_atime, + st.st_mtime, st.st_ctime) + st = st.__class__(st_tuple) index[entry.path] = index_entry_from_stat(st, entry.sha, 0) index.write() @@ -516,7 +555,14 @@ with open(fs_path, 'rb') as f: blob.data = f.read() else: - blob.data = os.readlink(fs_path) + if sys.platform == 'win32' and sys.version_info[0] == 3: + # os.readlink on Python3 on Windows requires a unicode string. + # TODO(jelmer): Don't assume tree_encoding == fs_encoding + tree_encoding = sys.getfilesystemencoding() + fs_path = fs_path.decode(tree_encoding) + blob.data = os.readlink(fs_path).encode(tree_encoding) + else: + blob.data = os.readlink(fs_path) return blob @@ -533,9 +579,33 @@ for tree_path, entry in index.iteritems(): full_path = _tree_to_fs_path(root_path, tree_path) - blob = blob_from_path_and_stat(full_path, os.lstat(full_path)) - if blob.id != entry.sha: + try: + blob = blob_from_path_and_stat(full_path, os.lstat(full_path)) + except OSError as e: + if e.errno != errno.ENOENT: + raise + # The file was removed, so we assume that counts as + # different from whatever file used to exist. yield tree_path + except IOError as e: + if e.errno != errno.EISDIR: + raise + # This is actually a directory + if os.path.exists(os.path.join(tree_path, '.git')): + # Submodule + from dulwich.errors import NotGitRepository + from dulwich.repo import Repo + try: + if entry.sha != Repo(tree_path).head(): + yield tree_path + except NotGitRepository: + yield tree_path + else: + # The file was changed to a directory, so consider it removed. + yield tree_path + else: + if blob.id != entry.sha: + yield tree_path os_sep_bytes = os.sep.encode('ascii') diff -Nru dulwich-0.12.0/dulwich/__init__.py dulwich-0.18.5/dulwich/__init__.py --- dulwich-0.12.0/dulwich/__init__.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/__init__.py 2017-10-07 15:45:17.000000000 +0000 @@ -2,23 +2,24 @@ # Copyright (C) 2007 James Westby # Copyright (C) 2008 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Python implementation of the Git file formats and protocols.""" -__version__ = (0, 12, 0) +__version__ = (0, 18, 5) diff -Nru dulwich-0.12.0/dulwich/log_utils.py dulwich-0.18.5/dulwich/log_utils.py --- dulwich-0.12.0/dulwich/log_utils.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/log_utils.py 2017-10-07 15:45:17.000000000 +0000 @@ -1,20 +1,22 @@ # log_utils.py -- Logging utilities for Dulwich # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, -# Boston, MA 02110-1301, USA. """Logging utilities for Dulwich. @@ -29,7 +31,8 @@ For many modules, the only function from the logging module they need is getLogger; this module exports that function for convenience. If a calling -module needs something else, it can import the standard logging module directly. +module needs something else, it can import the standard logging module +directly. """ import logging diff -Nru dulwich-0.12.0/dulwich/lru_cache.py dulwich-0.18.5/dulwich/lru_cache.py --- dulwich-0.12.0/dulwich/lru_cache.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/lru_cache.py 2017-07-29 00:14:28.000000000 +0000 @@ -1,19 +1,22 @@ # lru_cache.py -- Simple LRU cache for dulwich # Copyright (C) 2006, 2008 Canonical Ltd # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """A simple least-recently-used (LRU) cache.""" diff -Nru dulwich-0.12.0/dulwich/_objects.c dulwich-0.18.5/dulwich/_objects.c --- dulwich-0.12.0/dulwich/_objects.c 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/_objects.c 2017-10-07 15:45:17.000000000 +0000 @@ -1,30 +1,27 @@ /* * Copyright (C) 2009 Jelmer Vernooij * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License or (at your option) a later version of the License. + * Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU + * General Public License as public by the Free Software Foundation; version 2.0 + * or (at your option) any later version. You can redistribute it and/or + * modify it under the terms of either of these two licenses. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. + * You should have received a copy of the licenses; if not, see + * for a copy of the GNU General Public License + * and for a copy of the Apache + * License, Version 2.0. */ #include #include #include -#if (PY_VERSION_HEX < 0x02050000) -typedef int Py_ssize_t; -#endif - #if PY_MAJOR_VERSION >= 3 #define PyInt_Check(obj) 0 #define PyInt_CheckExact(obj) 0 @@ -64,7 +61,8 @@ static PyObject *py_parse_tree(PyObject *self, PyObject *args, PyObject *kw) { char *text, *start, *end; - int len, namelen, strict; + int len, strict; + size_t namelen; PyObject *ret, *item, *name, *sha, *py_strict = NULL; static char *kwlist[] = {"text", "strict", NULL}; @@ -146,7 +144,8 @@ { const struct tree_item *a = _a, *b = _b; const char *remain_a, *remain_b; - int ret, common; + int ret; + size_t common; if (strlen(a->name) > strlen(b->name)) { common = strlen(b->name); remain_a = a->name + common; @@ -174,9 +173,9 @@ static PyObject *py_sorted_tree_items(PyObject *self, PyObject *args) { struct tree_item *qsort_entries = NULL; - int name_order, num_entries, n = 0, i; + int name_order, n = 0, i; PyObject *entries, *py_name_order, *ret, *key, *value, *py_mode, *py_sha; - Py_ssize_t pos = 0; + Py_ssize_t pos = 0, num_entries; int (*cmp)(const void *, const void *); if (!PyArg_ParseTuple(args, "OO", &entries, &py_name_order)) diff -Nru dulwich-0.12.0/dulwich/objectspec.py dulwich-0.18.5/dulwich/objectspec.py --- dulwich-0.12.0/dulwich/objectspec.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/objectspec.py 2017-10-07 15:45:17.000000000 +0000 @@ -1,20 +1,22 @@ # objectspec.py -- Object specification # Copyright (C) 2014 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Object specification.""" @@ -37,6 +39,21 @@ return repo[objectish] +def parse_tree(repo, treeish): + """Parse a string referring to a tree. + + :param repo: A `Repo` object + :param treeish: A string referring to a tree + :return: A git object + :raise KeyError: If the object can not be found + """ + treeish = to_bytes(treeish) + o = repo[treeish] + if o.type_name == b"commit": + return repo[o.tree] + return o + + def parse_ref(container, refspec): """Parse a string referring to a reference. @@ -80,7 +97,7 @@ (lh, rh) = refspec.split(b":") else: lh = rh = refspec - if rh == b"": + if lh == b"": lh = None else: lh = parse_ref(lh_container, lh) @@ -91,7 +108,7 @@ rh = parse_ref(rh_container, rh) except KeyError: # TODO: check force? - if not b"/" in rh: + if b"/" not in rh: rh = b"refs/heads/" + rh return (lh, rh, force) @@ -155,7 +172,15 @@ :raise ValueError: If the range can not be parsed """ committish = to_bytes(committish) - return repo[committish] # For now.. + try: + return repo[committish] + except KeyError: + pass + try: + return repo[parse_ref(repo, committish)] + except KeyError: + pass + raise KeyError(committish) # TODO: parse_path_in_tree(), which handles e.g. v1.0:Documentation diff -Nru dulwich-0.12.0/dulwich/objects.py dulwich-0.18.5/dulwich/objects.py --- dulwich-0.12.0/dulwich/objects.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/objects.py 2017-10-07 15:45:17.000000000 +0000 @@ -2,20 +2,22 @@ # Copyright (C) 2007 James Westby # Copyright (C) 2008-2013 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Access to base git objects.""" @@ -134,18 +136,18 @@ def object_header(num_type, length): """Return an object header for the given numeric type and text length.""" - return object_class(num_type).type_name + b' ' + str(length).encode('ascii') + b'\0' + return (object_class(num_type).type_name + + b' ' + str(length).encode('ascii') + b'\0') def serializable_property(name, docstring=None): """A property that helps tracking whether serialization is necessary. """ def set(obj, value): - obj._ensure_parsed() setattr(obj, "_"+name, value) obj._needs_serialization = True + def get(obj): - obj._ensure_parsed() return getattr(obj, "_"+name) return property(get, set, doc=docstring) @@ -182,9 +184,9 @@ email_start = identity.find(b'<') email_end = identity.find(b'>') if (email_start < 0 or email_end < 0 or email_end <= email_start - or identity.find(b'<', email_start + 1) >= 0 - or identity.find(b'>', email_end + 1) >= 0 - or not identity.endswith(b'>')): + or identity.find(b'<', email_start + 1) >= 0 + or identity.find(b'>', email_end + 1) >= 0 + or not identity.endswith(b'>')): raise ObjectFormatException(error_msg) @@ -218,8 +220,7 @@ class ShaFile(object): """A git SHA file.""" - __slots__ = ('_needs_parsing', '_chunked_text', '_sha', - '_needs_serialization') + __slots__ = ('_chunked_text', '_sha', '_needs_serialization') @staticmethod def _parse_legacy_object_header(magic, f): @@ -272,9 +273,8 @@ :return: List of strings, not necessarily one per line """ - if self._needs_parsing: - self._ensure_parsed() - elif self._needs_serialization: + if self._needs_serialization: + self._sha = None self._chunked_text = self._serialize() self._needs_serialization = False return self._chunked_text @@ -298,13 +298,6 @@ """Return a string representing this object, fit for display.""" return self.as_raw_string() - def _ensure_parsed(self): - if self._needs_parsing: - if not self._chunked_text: - raise AssertionError("ShaFile needs chunked text") - self._deserialize(self._chunked_text) - self._needs_parsing = False - def set_raw_string(self, text, sha=None): """Set the contents of this object from a serialized string.""" if not isinstance(text, bytes): @@ -319,7 +312,6 @@ self._sha = None else: self._sha = FixedSha(sha) - self._needs_parsing = False self._needs_serialization = False @staticmethod @@ -365,7 +357,6 @@ """Don't call this directly""" self._sha = None self._chunked_text = [] - self._needs_parsing = False self._needs_serialization = True def _deserialize(self, chunks): @@ -463,13 +454,6 @@ ret += len(chunk) return ret - def _make_sha(self): - ret = sha1() - ret.update(self._header()) - for chunk in self.as_raw_chunks(): - ret.update(chunk) - return ret - def sha(self): """The SHA1 object that is the name of this object.""" if self._sha is None or self._needs_serialization: @@ -532,7 +516,7 @@ def __cmp__(self, other): if not isinstance(other, ShaFile): raise TypeError - return cmp(self.id, other.id) + return cmp(self.id, other.id) # noqa: F821 class Blob(ShaFile): @@ -546,7 +530,6 @@ def __init__(self): super(Blob, self).__init__() self._chunked_text = [] - self._needs_parsing = False self._needs_serialization = False def _get_data(self): @@ -559,21 +542,19 @@ "The text contained within the blob object.") def _get_chunked(self): - self._ensure_parsed() return self._chunked_text def _set_chunked(self, chunks): self._chunked_text = chunks def _serialize(self): - if not self._chunked_text: - self._ensure_parsed() return self._chunked_text def _deserialize(self, chunks): self._chunked_text = chunks - chunked = property(_get_chunked, _set_chunked, + chunked = property( + _get_chunked, _set_chunked, "The text within the blob object, as chunks (not necessarily lines).") @classmethod @@ -590,6 +571,33 @@ """ super(Blob, self).check() + def splitlines(self): + """Return list of lines in this blob. + + This preserves the original line endings. + """ + chunks = self.chunked + if not chunks: + return [] + if len(chunks) == 1: + return chunks[0].splitlines(True) + remaining = None + ret = [] + for chunk in chunks: + lines = chunk.splitlines(True) + if len(lines) > 1: + ret.append((remaining or b"") + lines[0]) + ret.extend(lines[1:-1]) + remaining = lines[-1] + elif len(lines) == 1: + if remaining is None: + remaining = lines.pop() + else: + remaining += lines.pop() + if remaining is not None: + ret.append(remaining) + return ret + def _parse_message(chunks): """Parse a message with a list of fields and a body. @@ -602,17 +610,45 @@ f = BytesIO(b''.join(chunks)) k = None v = "" + eof = False + + def _strip_last_newline(value): + """Strip the last newline from value""" + if value and value.endswith(b'\n'): + return value[:-1] + return value + + # Parse the headers + # + # Headers can contain newlines. The next line is indented with a space. + # We store the latest key as 'k', and the accumulated value as 'v'. for l in f: if l.startswith(b' '): + # Indented continuation of the previous line v += l[1:] else: if k is not None: - yield (k, v.rstrip(b'\n')) + # We parsed a new header, return its value + yield (k, _strip_last_newline(v)) if l == b'\n': # Empty line indicates end of headers break (k, v) = l.split(b' ', 1) - yield (None, f.read()) + + else: + # We reached end of file before the headers ended. We still need to + # return the previous header, then we need to return a None field for + # the text. + eof = True + if k is not None: + yield (k, _strip_last_newline(v)) + yield (None, None) + + if not eof: + # We didn't reach the end of file while parsing headers. We can return + # the rest of the file as a message. + yield (None, f.read()) + f.close() @@ -628,6 +664,9 @@ def __init__(self): super(Tag, self).__init__() + self._tagger = None + self._tag_time = None + self._tag_timezone = None self._tag_timezone_neg_utc = False @classmethod @@ -677,15 +716,21 @@ chunks.append(git_line(_TAGGER_HEADER, self._tagger)) else: chunks.append(git_line( - _TAGGER_HEADER, self._tagger, str(self._tag_time).encode('ascii'), - format_timezone(self._tag_timezone, self._tag_timezone_neg_utc))) - chunks.append(b'\n') # To close headers - chunks.append(self._message) + _TAGGER_HEADER, self._tagger, + str(self._tag_time).encode('ascii'), + format_timezone( + self._tag_timezone, self._tag_timezone_neg_utc))) + if self._message is not None: + chunks.append(b'\n') # To close headers + chunks.append(self._message) return chunks def _deserialize(self, chunks): """Grab the metadata attached to the tag""" self._tagger = None + self._tag_time = None + self._tag_timezone = None + self._tag_timezone_neg_utc = False for field, value in _parse_message(chunks): if field == _OBJECT_HEADER: self._object_sha = value @@ -707,10 +752,11 @@ else: self._tagger = value[0:sep+1] try: - (timetext, timezonetext) = value[sep+2:].rsplit(b' ', 1) + (timetext, timezonetext) = ( + value[sep+2:].rsplit(b' ', 1)) self._tag_time = int(timetext) - self._tag_timezone, self._tag_timezone_neg_utc = \ - parse_timezone(timezonetext) + self._tag_timezone, self._tag_timezone_neg_utc = ( + parse_timezone(timezonetext)) except ValueError as e: raise ObjectFormatException(e) elif field is None: @@ -723,26 +769,27 @@ :return: tuple of (object class, sha). """ - self._ensure_parsed() return (self._object_class, self._object_sha) def _set_object(self, value): - self._ensure_parsed() (self._object_class, self._object_sha) = value self._needs_serialization = True object = property(_get_object, _set_object) name = serializable_property("name", "The name of this tag") - tagger = serializable_property("tagger", - "Returns the name of the person who created this tag") - tag_time = serializable_property("tag_time", - "The creation timestamp of the tag. As the number of seconds " - "since the epoch") - tag_timezone = serializable_property("tag_timezone", - "The timezone that tag_time is in.") + tagger = serializable_property( + "tagger", + "Returns the name of the person who created this tag") + tag_time = serializable_property( + "tag_time", + "The creation timestamp of the tag. As the number of seconds " + "since the epoch") + tag_timezone = serializable_property( + "tag_timezone", + "The timezone that tag_time is in.") message = serializable_property( - "message", "The message attached to this tag") + "message", "The message attached to this tag") class TreeEntry(namedtuple('TreeEntry', ['path', 'mode', 'sha'])): @@ -790,7 +837,8 @@ :return: Serialized tree text as chunks """ for name, mode, hexsha in items: - yield ("%04o" % mode).encode('ascii') + b' ' + name + b'\0' + hex_to_sha(hexsha) + yield (("%04o" % mode).encode('ascii') + b' ' + name + + b'\0' + hex_to_sha(hexsha)) def sorted_tree_items(entries, name_order): @@ -828,6 +876,23 @@ return entry[0] +def pretty_format_tree_entry(name, mode, hexsha, encoding="utf-8"): + """Pretty format tree entry. + + :param name: Name of the directory entry + :param mode: Mode of entry + :param hexsha: Hexsha of the referenced object + :return: string describing the tree entry + """ + if mode & stat.S_IFDIR: + kind = "tree" + else: + kind = "blob" + return "%04o %s %s\t%s\n" % ( + mode, kind, hexsha.decode('ascii'), + name.decode(encoding, 'replace')) + + class Tree(ShaFile): """A Git tree object""" @@ -848,11 +913,9 @@ return tree def __contains__(self, name): - self._ensure_parsed() return name in self._entries def __getitem__(self, name): - self._ensure_parsed() return self._entries[name] def __setitem__(self, name, value): @@ -864,21 +927,17 @@ a string. """ mode, hexsha = value - self._ensure_parsed() self._entries[name] = (mode, hexsha) self._needs_serialization = True def __delitem__(self, name): - self._ensure_parsed() del self._entries[name] self._needs_serialization = True def __len__(self): - self._ensure_parsed() return len(self._entries) def __iter__(self): - self._ensure_parsed() return iter(self._entries) def add(self, name, mode, hexsha): @@ -894,7 +953,6 @@ warnings.warn( "Please use Tree.add(name, mode, hexsha)", category=DeprecationWarning, stacklevel=2) - self._ensure_parsed() self._entries[name] = mode, hexsha self._needs_serialization = True @@ -905,7 +963,6 @@ order. :return: Iterator over (name, mode, sha) tuples """ - self._ensure_parsed() return sorted_tree_items(self._entries, name_order) def items(self): @@ -922,7 +979,8 @@ except ValueError as e: raise ObjectFormatException(e) # TODO: list comprehension is for efficiency in the common (small) - # case; if memory efficiency in the large case is a concern, use a genexp. + # case; if memory efficiency in the large case is a concern, use a + # genexp. self._entries = dict([(n, (m, s)) for n, m, s in parsed_entries]) def check(self): @@ -959,11 +1017,7 @@ def as_pretty_string(self): text = [] for name, mode, hexsha in self.iteritems(): - if mode & stat.S_IFDIR: - kind = "tree" - else: - kind = "blob" - text.append("%04o %s %s\t%s\n" % (mode, kind, hexsha, name)) + text.append(pretty_format_tree_entry(name, mode, hexsha)) return "".join(text) def lookup_path(self, lookup_obj, path): @@ -1026,7 +1080,8 @@ offset = -offset else: sign = '+' - return ('%c%02d%02d' % (sign, offset / 3600, (offset / 60) % 60)).encode('ascii') + return ('%c%02d%02d' % + (sign, offset / 3600, (offset / 60) % 60)).encode('ascii') def parse_commit(chunks): @@ -1059,7 +1114,8 @@ elif field == _COMMITTER_HEADER: committer, timetext, timezonetext = value.rsplit(b' ', 2) commit_time = int(timetext) - commit_info = (committer, commit_time, parse_timezone(timezonetext)) + commit_info = ( + committer, commit_time, parse_timezone(timezonetext)) elif field == _ENCODING_HEADER: encoding = value elif field == _MERGETAG_HEADER: @@ -1083,8 +1139,8 @@ __slots__ = ('_parents', '_encoding', '_extra', '_author_timezone_neg_utc', '_commit_timezone_neg_utc', '_commit_time', '_author_time', '_author_timezone', '_commit_timezone', - '_author', '_committer', '_parents', '_extra', - '_encoding', '_tree', '_message', '_mergetag', '_gpgsig') + '_author', '_committer', '_tree', '_message', + '_mergetag', '_gpgsig') def __init__(self): super(Commit, self).__init__() @@ -1105,12 +1161,12 @@ def _deserialize(self, chunks): (self._tree, self._parents, author_info, commit_info, self._encoding, - self._mergetag, self._gpgsig, self._message, self._extra) = ( + self._mergetag, self._gpgsig, self._message, self._extra) = ( parse_commit(chunks)) - (self._author, self._author_time, (self._author_timezone, - self._author_timezone_neg_utc)) = author_info - (self._committer, self._commit_time, (self._commit_timezone, - self._commit_timezone_neg_utc)) = commit_info + (self._author, self._author_time, + (self._author_timezone, self._author_timezone_neg_utc)) = author_info + (self._committer, self._commit_time, + (self._commit_timezone, self._commit_timezone_neg_utc)) = commit_info def check(self): """Check this object for internal consistency. @@ -1150,16 +1206,19 @@ def _serialize(self): chunks = [] - tree_bytes = self._tree.as_raw_string() if isinstance(self._tree, Tree) else self._tree + tree_bytes = ( + self._tree.id if isinstance(self._tree, Tree) else self._tree) chunks.append(git_line(_TREE_HEADER, tree_bytes)) for p in self._parents: chunks.append(git_line(_PARENT_HEADER, p)) chunks.append(git_line( - _AUTHOR_HEADER, self._author, str(self._author_time).encode('ascii'), - format_timezone(self._author_timezone, - self._author_timezone_neg_utc))) + _AUTHOR_HEADER, self._author, + str(self._author_time).encode('ascii'), + format_timezone( + self._author_timezone, self._author_timezone_neg_utc))) chunks.append(git_line( - _COMMITTER_HEADER, self._committer, str(self._commit_time).encode('ascii'), + _COMMITTER_HEADER, self._committer, + str(self._commit_time).encode('ascii'), format_timezone(self._commit_timezone, self._commit_timezone_neg_utc))) if self.encoding: @@ -1173,7 +1232,8 @@ chunks.append(b' ' + chunk + b'\n') # No trailing empty line - chunks[-1] = chunks[-1].rstrip(b' \n') + if chunks[-1].endswith(b' \n'): + chunks[-1] = chunks[-1][:-2] for k, v in self.extra: if b'\n' in k or b'\n' in v: raise AssertionError( @@ -1193,12 +1253,10 @@ def _get_parents(self): """Return a list of parents of this commit.""" - self._ensure_parsed() return self._parents def _set_parents(self, value): """Set a list of parents of this commit.""" - self._ensure_parsed() self._needs_serialization = True self._parents = value @@ -1207,31 +1265,37 @@ def _get_extra(self): """Return extra settings of this commit.""" - self._ensure_parsed() return self._extra - extra = property(_get_extra, + extra = property( + _get_extra, doc="Extra header fields not understood (presumably added in a " "newer version of git). Kept verbatim so the object can " "be correctly reserialized. For private commit metadata, use " "pseudo-headers in Commit.message, rather than this field.") - author = serializable_property("author", + author = serializable_property( + "author", "The name of the author of the commit") - committer = serializable_property("committer", + committer = serializable_property( + "committer", "The name of the committer of the commit") message = serializable_property( "message", "The commit message") - commit_time = serializable_property("commit_time", - "The timestamp of the commit. As the number of seconds since the epoch.") + commit_time = serializable_property( + "commit_time", + "The timestamp of the commit. As the number of seconds since the " + "epoch.") - commit_timezone = serializable_property("commit_timezone", + commit_timezone = serializable_property( + "commit_timezone", "The zone the commit time is in") - author_time = serializable_property("author_time", + author_time = serializable_property( + "author_time", "The timestamp the commit was written. As the number of " "seconds since the epoch.") diff -Nru dulwich-0.12.0/dulwich/object_store.py dulwich-0.18.5/dulwich/object_store.py --- dulwich-0.12.0/dulwich/object_store.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/object_store.py 2017-10-07 15:45:17.000000000 +0000 @@ -2,25 +2,26 @@ # Copyright (C) 2008-2013 Jelmer Vernooij # and others # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Git object store interfaces and implementation.""" - from io import BytesIO import errno from itertools import chain @@ -28,6 +29,7 @@ import stat import sys import tempfile +import time from dulwich.diff_tree import ( tree_changes, @@ -72,8 +74,8 @@ def determine_wants_all(self, refs): return [sha for (ref, sha) in refs.items() - if not sha in self and not ref.endswith(b"^{}") and - not sha == ZERO_SHA] + if sha not in self and not ref.endswith(b"^{}") and + not sha == ZERO_SHA] def iter_shas(self, shas): """Iterate over the objects for the specified shas. @@ -129,7 +131,7 @@ def add_objects(self, objects): """Add a set of objects to this object store. - :param objects: Iterable over a list of objects. + :param objects: Iterable over a list of (object, path) tuples """ raise NotImplementedError(self.add_objects) @@ -171,12 +173,14 @@ :param wants: Iterable over SHAs of objects to fetch. :param progress: Simple progress function that will be called with updated progress strings. - :param get_tagged: Function that returns a dict of pointed-to sha -> tag - sha for including tags. - :param get_parents: Optional function for getting the parents of a commit. + :param get_tagged: Function that returns a dict of pointed-to sha -> + tag sha for including tags. + :param get_parents: Optional function for getting the parents of a + commit. :return: Iterator over (sha, path) pairs. """ - finder = MissingObjectFinder(self, haves, wants, progress, get_tagged, get_parents=get_parents) + finder = MissingObjectFinder(self, haves, wants, progress, get_tagged, + get_parents=get_parents) return iter(finder.next, None) def find_common_revisions(self, graphwalker): @@ -208,8 +212,8 @@ :param sha: The object SHA to peel. :return: The fully-peeled SHA1 of a tag object, after peeling all - intermediate tags; if the original ref does not point to a tag, this - will equal the original SHA1. + intermediate tags; if the original ref does not point to a tag, + this will equal the original SHA1. """ obj = self[sha] obj_class = object_class(obj.type_name) @@ -225,7 +229,8 @@ :param heads: commits to start from :param common: commits to end at, or empty set to walk repository completely - :param get_parents: Optional function for getting the parents of a commit. + :param get_parents: Optional function for getting the parents of a + commit. :return: a tuple (A, B) where A - all commits reachable from heads but not present in common, B - common (shared) elements that are directly reachable from heads @@ -288,15 +293,22 @@ """Add a newly appeared pack to the cache by path. """ - self._pack_cache[base_name] = pack + prev_pack = self._pack_cache.get(base_name) + if prev_pack is not pack: + self._pack_cache[base_name] = pack + if prev_pack: + prev_pack.close() - def close(self): + def _flush_pack_cache(self): pack_cache = self._pack_cache self._pack_cache = {} while pack_cache: (name, pack) = pack_cache.popitem() pack.close() + def close(self): + self._flush_pack_cache() + @property def packs(self): """List with pack objects.""" @@ -321,6 +333,9 @@ def _remove_loose_object(self, sha): raise NotImplementedError(self._remove_loose_object) + def _remove_pack(self, name): + raise NotImplementedError(self._remove_pack) + def pack_loose_objects(self): """Pack loose objects. @@ -334,9 +349,39 @@ self._remove_loose_object(obj.id) return len(objects) + def repack(self): + """Repack the packs in this repository. + + Note that this implementation is fairly naive and currently keeps all + objects in memory while it repacks. + """ + loose_objects = set() + for sha in self._iter_loose_objects(): + loose_objects.add(self._get_loose_object(sha)) + objects = {(obj, None) for obj in loose_objects} + old_packs = {p.name(): p for p in self.packs} + for name, pack in old_packs.items(): + objects.update((obj, None) for obj in pack.iterobjects()) + self._flush_pack_cache() + + # The name of the consolidated pack might match the name of a + # pre-existing pack. Take care not to remove the newly created + # consolidated pack. + + consolidated = self.add_objects(objects) + old_packs.pop(consolidated.name(), None) + + for obj in loose_objects: + self._remove_loose_object(obj.id) + for name, pack in old_packs.items(): + self._remove_pack(pack) + self._update_pack_cache() + return len(objects) + def __iter__(self): """Iterate over the SHAs that are present in this store.""" - iterables = list(self.packs) + [self._iter_loose_objects()] + [self._iter_alternate_objects()] + iterables = (list(self.packs) + [self._iter_loose_objects()] + + [self._iter_alternate_objects()]) return chain(*iterables) def contains_loose(self, sha): @@ -380,7 +425,8 @@ def add_objects(self, objects): """Add a set of objects to this object store. - :param objects: Iterable over objects, should support __len__. + :param objects: Iterable over (object, path) tuples, should support + __len__. :return: Pack object of the objects written. """ if len(objects) == 0: @@ -425,8 +471,7 @@ def _read_alternate_paths(self): try: - f = GitFile(os.path.join(self.path, INFODIR, "alternates"), - 'rb') + f = GitFile(os.path.join(self.path, INFODIR, "alternates"), 'rb') except (OSError, IOError) as e: if e.errno == errno.ENOENT: return @@ -439,7 +484,8 @@ if os.path.isabs(l): yield l.decode(sys.getfilesystemencoding()) else: - yield os.path.join(self.path, l).decode(sys.getfilesystemencoding()) + yield os.path.join(self.path, l).decode( + sys.getfilesystemencoding()) def add_alternate_path(self, path): """Add an alternate path to this object store. @@ -474,13 +520,17 @@ self.close() return raise - self._pack_cache_time = os.stat(self.pack_dir).st_mtime + self._pack_cache_time = max( + os.stat(self.pack_dir).st_mtime, time.time()) pack_files = set() for name in pack_dir_contents: - assert isinstance(name, basestring if sys.version_info[0] == 2 else str) - # TODO: verify that idx exists first if name.startswith("pack-") and name.endswith(".pack"): - pack_files.add(name[:-len(".pack")]) + # verify that idx exists first (otherwise the pack was not yet + # fully written) + idx_name = os.path.splitext(name)[0] + ".idx" + if idx_name in pack_dir_contents: + pack_name = name[:-len(".pack")] + pack_files.add(pack_name) # Open newly appeared pack files for f in pack_files: @@ -492,7 +542,7 @@ def _pack_cache_stale(self): try: - return os.stat(self.pack_dir).st_mtime > self._pack_cache_time + return os.stat(self.pack_dir).st_mtime >= self._pack_cache_time except OSError as e: if e.errno == errno.ENOENT: return True @@ -521,6 +571,10 @@ def _remove_loose_object(self, sha): os.remove(self._get_shafile_path(sha)) + def _remove_pack(self, pack): + os.remove(pack.data.path) + os.remove(pack.index.path) + def _get_pack_basepath(self, entries): suffix = iter_sha1(entry[0] for entry in entries) # TODO: Handle self.pack_dir being bytes @@ -567,7 +621,14 @@ # Move the pack in. entries.sort() pack_base_name = self._get_pack_basepath(entries) - os.rename(path, pack_base_name + '.pack') + if sys.platform == 'win32': + try: + os.rename(path, pack_base_name + '.pack') + except WindowsError: + os.remove(pack_base_name + '.pack') + os.rename(path, pack_base_name + '.pack') + else: + os.rename(path, pack_base_name + '.pack') # Write the index. index_file = GitFile(pack_base_name + '.idx', 'wb') @@ -586,12 +647,12 @@ def add_thin_pack(self, read_all, read_some): """Add a new thin pack to this object store. - Thin packs are packs that contain deltas with parents that exist outside - the pack. They should never be placed in the object store directly, and - always indexed and completed as they are copied. + Thin packs are packs that contain deltas with parents that exist + outside the pack. They should never be placed in the object store + directly, and always indexed and completed as they are copied. - :param read_all: Read function that blocks until the number of requested - bytes are read. + :param read_all: Read function that blocks until the number of + requested bytes are read. :param read_some: Read function that returns at least one byte, but may not return the number of bytes requested. :return: A Pack object pointing at the now-completed thin pack in the @@ -632,7 +693,9 @@ """ fd, path = tempfile.mkstemp(dir=self.pack_dir, suffix=".pack") f = os.fdopen(fd, 'wb') + def commit(): + f.flush() os.fsync(fd) f.close() if os.path.getsize(path) > 0: @@ -640,6 +703,7 @@ else: os.remove(path) return None + def abort(): f.close() os.remove(path) @@ -658,7 +722,7 @@ if e.errno != errno.EEXIST: raise if os.path.exists(path): - return # Already there, no need to write again + return # Already there, no need to write again with GitFile(path, 'wb') as f: f.write(obj.as_legacy_object()) @@ -716,7 +780,7 @@ return obj.type_num, obj.as_raw_string() def __getitem__(self, name): - return self._data[self._to_hexsha(name)] + return self._data[self._to_hexsha(name)].copy() def __delitem__(self, name): """Delete an object from this store, for testing only.""" @@ -726,15 +790,15 @@ """Add a single object to this object store. """ - self._data[obj.id] = obj + self._data[obj.id] = obj.copy() def add_objects(self, objects): """Add a set of objects to this object store. - :param objects: Iterable over a list of objects. + :param objects: Iterable over a list of (object, path) tuples """ for obj, path in objects: - self._data[obj.id] = obj + self.add_object(obj) def add_pack(self): """Add a new pack to this object store. @@ -746,11 +810,13 @@ call when the pack is finished. """ f = BytesIO() + def commit(): p = PackData.from_file(BytesIO(f.getvalue()), f.tell()) f.close() for obj in PackInflater.for_pack_data(p, self.get_raw): - self._data[obj.id] = obj + self.add_object(obj) + def abort(): pass return f, commit, abort @@ -781,19 +847,20 @@ def add_thin_pack(self, read_all, read_some): """Add a new thin pack to this object store. - Thin packs are packs that contain deltas with parents that exist outside - the pack. Because this object store doesn't support packs, we extract - and add the individual objects. + Thin packs are packs that contain deltas with parents that exist + outside the pack. Because this object store doesn't support packs, we + extract and add the individual objects. - :param read_all: Read function that blocks until the number of requested - bytes are read. + :param read_all: Read function that blocks until the number of + requested bytes are read. :param read_some: Read function that returns at least one byte, but may not return the number of bytes requested. """ f, commit, abort = self.add_pack() try: indexer = PackIndexer(f, resolve_ext_ref=self.get_raw) - copier = PackStreamCopier(read_all, read_some, f, delta_iter=indexer) + copier = PackStreamCopier(read_all, read_some, f, + delta_iter=indexer) copier.verify() self._complete_thin_pack(f, indexer) except: @@ -1041,7 +1108,8 @@ if sha in self._tagged: self.add_todo([(self._tagged[sha], None, True)]) self.sha_done.add(sha) - self.progress(("counting objects: %d\r" % len(self.sha_done)).encode('ascii')) + self.progress(("counting objects: %d\r" % + len(self.sha_done)).encode('ascii')) return (sha, name) __next__ = next @@ -1096,8 +1164,57 @@ ret = self.heads.pop() ps = self.get_parents(ret) self.parents[ret] = ps - self.heads.update([p for p in ps if not p in self.parents]) + self.heads.update( + [p for p in ps if p not in self.parents]) return ret return None __next__ = next + + +def commit_tree_changes(object_store, tree, changes): + """Commit a specified set of changes to a tree structure. + + This will apply a set of changes on top of an existing tree, storing new + objects in object_store. + + changes are a list of tuples with (path, mode, object_sha). + Paths can be both blobs and trees. See the mode and + object sha to None deletes the path. + + This method works especially well if there are only a small + number of changes to a big tree. For a large number of changes + to a large tree, use e.g. commit_tree. + + :param object_store: Object store to store new objects in + and retrieve old ones from. + :param tree: Original tree root + :param changes: changes to apply + :return: New tree root object + """ + # TODO(jelmer): Save up the objects and add them using .add_objects + # rather than with individual calls to .add_object. + nested_changes = {} + for (path, new_mode, new_sha) in changes: + try: + (dirname, subpath) = path.split(b'/', 1) + except ValueError: + if new_sha is None: + del tree[path] + else: + tree[path] = (new_mode, new_sha) + else: + nested_changes.setdefault(dirname, []).append( + (subpath, new_mode, new_sha)) + for name, subchanges in nested_changes.items(): + try: + orig_subtree = object_store[tree[name][1]] + except KeyError: + orig_subtree = Tree() + subtree = commit_tree_changes(object_store, orig_subtree, subchanges) + if len(subtree) == 0: + del tree[name] + else: + tree[name] = (stat.S_IFDIR, subtree.id) + object_store.add_object(tree) + return tree diff -Nru dulwich-0.12.0/dulwich/_pack.c dulwich-0.18.5/dulwich/_pack.c --- dulwich-0.12.0/dulwich/_pack.c 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/_pack.c 2017-10-07 15:45:17.000000000 +0000 @@ -1,20 +1,21 @@ /* * Copyright (C) 2009 Jelmer Vernooij * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License or (at your option) a later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. + * Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU + * General Public License as public by the Free Software Foundation; version 2.0 + * or (at your option) any later version. You can redistribute it and/or + * modify it under the terms of either of these two licenses. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * You should have received a copy of the licenses; if not, see + * for a copy of the GNU General Public License + * and for a copy of the Apache + * License, Version 2.0. */ #include @@ -23,7 +24,7 @@ #if PY_MAJOR_VERSION >= 3 #define PyInt_FromLong PyLong_FromLong #define PyString_AS_STRING PyBytes_AS_STRING -#define PyString_AsString PyBytes_AsString +#define PyString_AS_STRING PyBytes_AS_STRING #define PyString_Check PyBytes_Check #define PyString_CheckExact PyBytes_CheckExact #define PyString_FromStringAndSize PyBytes_FromStringAndSize @@ -47,10 +48,10 @@ } -static size_t get_delta_header_size(uint8_t *delta, int *index, int length) +static size_t get_delta_header_size(uint8_t *delta, size_t *index, size_t length) { size_t size = 0; - int i = 0; + size_t i = 0; while ((*index) < length) { uint8_t cmd = delta[*index]; (*index)++; @@ -89,10 +90,10 @@ static PyObject *py_apply_delta(PyObject *self, PyObject *args) { uint8_t *src_buf, *delta; - int src_buf_len, delta_len; + size_t src_buf_len, delta_len; size_t src_size, dest_size; size_t outindex = 0; - int index; + size_t index; uint8_t *out; PyObject *ret, *py_src_buf, *py_delta, *ret_list; @@ -110,16 +111,16 @@ } src_buf = (uint8_t *)PyString_AS_STRING(py_src_buf); - src_buf_len = PyString_GET_SIZE(py_src_buf); + src_buf_len = (size_t)PyString_GET_SIZE(py_src_buf); delta = (uint8_t *)PyString_AS_STRING(py_delta); - delta_len = PyString_GET_SIZE(py_delta); + delta_len = (size_t)PyString_GET_SIZE(py_delta); index = 0; src_size = get_delta_header_size(delta, &index, delta_len); if (src_size != src_buf_len) { PyErr_Format(PyExc_ApplyDeltaError, - "Unexpected source buffer size: %lu vs %d", src_size, src_buf_len); + "Unexpected source buffer size: %lu vs %ld", src_size, src_buf_len); Py_DECREF(py_src_buf); Py_DECREF(py_delta); return NULL; @@ -132,9 +133,9 @@ Py_DECREF(py_delta); return NULL; } - out = (uint8_t *)PyString_AsString(ret); + out = (uint8_t *)PyString_AS_STRING(ret); while (index < delta_len) { - char cmd = delta[index]; + uint8_t cmd = delta[index]; index++; if (cmd & 0x80) { size_t cp_off = 0, cp_size = 0; @@ -237,7 +238,7 @@ Py_DECREF(file_sha); return NULL; } - cmp = memcmp(PyString_AsString(file_sha), sha, 20); + cmp = memcmp(PyString_AS_STRING(file_sha), sha, 20); Py_DECREF(file_sha); if (cmp < 0) start = i + 1; @@ -263,15 +264,6 @@ PyObject *m; PyObject *errors_module; - errors_module = PyImport_ImportModule("dulwich.errors"); - if (errors_module == NULL) - return NULL; - - PyExc_ApplyDeltaError = PyObject_GetAttrString(errors_module, "ApplyDeltaError"); - Py_DECREF(errors_module); - if (PyExc_ApplyDeltaError == NULL) - return NULL; - #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, @@ -284,6 +276,18 @@ NULL, /* m_clear*/ NULL, /* m_free */ }; +#endif + + errors_module = PyImport_ImportModule("dulwich.errors"); + if (errors_module == NULL) + return NULL; + + PyExc_ApplyDeltaError = PyObject_GetAttrString(errors_module, "ApplyDeltaError"); + Py_DECREF(errors_module); + if (PyExc_ApplyDeltaError == NULL) + return NULL; + +#if PY_MAJOR_VERSION >= 3 m = PyModule_Create(&moduledef); #else m = Py_InitModule3("_pack", py_pack_methods, NULL); diff -Nru dulwich-0.12.0/dulwich/pack.py dulwich-0.18.5/dulwich/pack.py --- dulwich-0.12.0/dulwich/pack.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/pack.py 2017-10-07 15:45:17.000000000 +0000 @@ -2,20 +2,22 @@ # Copyright (C) 2007 James Westby # Copyright (C) 2008-2013 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) a later version. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Classes for dealing with packed git objects. @@ -51,6 +53,14 @@ import os import sys +from hashlib import sha1 +from os import ( + SEEK_CUR, + SEEK_END, + ) +from struct import unpack_from +import zlib + try: import mmap except ImportError: @@ -62,23 +72,15 @@ if sys.platform == 'Plan9': has_mmap = False -from hashlib import sha1 -from os import ( - SEEK_CUR, - SEEK_END, - ) -from struct import unpack_from -import zlib - -from dulwich.errors import ( +from dulwich.errors import ( # noqa: E402 ApplyDeltaError, ChecksumMismatch, ) -from dulwich.file import GitFile -from dulwich.lru_cache import ( +from dulwich.file import GitFile # noqa: E402 +from dulwich.lru_cache import ( # noqa: E402 LRUSizeCache, ) -from dulwich.objects import ( +from dulwich.objects import ( # noqa: E402 ShaFile, hex_to_sha, sha_to_hex, @@ -307,8 +309,8 @@ if contents[:4] == b'\377tOc': version = struct.unpack(b'>L', contents[4:8])[0] if version == 2: - return PackIndex2(path, file=f, contents=contents, - size=size) + return PackIndex2( + path, file=f, contents=contents, size=size) else: raise KeyError('Unknown pack index format %d' % version) else: @@ -449,7 +451,8 @@ is the end of the group that shares the same starting byte. Subtract one from the starting byte and index again to find the start of the group. The values are sorted by sha id within the group, so do the math to find - the start and end offset and then bisect in to find if the value is present. + the start and end offset and then bisect in to find if the value is + present. """ def __init__(self, filename, file=None, contents=None, size=None): @@ -470,10 +473,14 @@ else: self._contents, self._size = (contents, size) + @property + def path(self): + return self._filename + def __eq__(self, other): # Quick optimization: if (isinstance(other, FilePackIndex) and - self._fan_out_table != other._fan_out_table): + self._fan_out_table != other._fan_out_table): return False return super(FilePackIndex, self).__eq__(other) @@ -504,7 +511,8 @@ raise NotImplementedError(self._unpack_offset) def _unpack_crc32_checksum(self, i): - """Unpack the crc32 checksum for the i-th object from the index file.""" + """Unpack the crc32 checksum for the ith object from the index file. + """ raise NotImplementedError(self._unpack_crc32_checksum) def _itersha(self): @@ -523,7 +531,8 @@ def _read_fan_out_table(self, start_offset): ret = [] for i in range(0x100): - fanout_entry = self._contents[start_offset+i*4:start_offset+(i+1)*4] + fanout_entry = self._contents[ + start_offset+i*4:start_offset+(i+1)*4] ret.append(struct.unpack('>L', fanout_entry)[0]) return ret @@ -614,8 +623,8 @@ self._crc32_table_offset = self._name_table_offset + 20 * len(self) self._pack_offset_table_offset = (self._crc32_table_offset + 4 * len(self)) - self._pack_offset_largetable_offset = (self._pack_offset_table_offset + - 4 * len(self)) + self._pack_offset_largetable_offset = ( + self._pack_offset_table_offset + 4 * len(self)) def _unpack_entry(self, i): return (self._unpack_name(i), self._unpack_offset(i), @@ -629,21 +638,23 @@ offset = self._pack_offset_table_offset + i * 4 offset = unpack_from('>L', self._contents, offset)[0] if offset & (2**31): - offset = self._pack_offset_largetable_offset + (offset&(2**31-1)) * 8 + offset = ( + self._pack_offset_largetable_offset + + (offset & (2 ** 31 - 1)) * 8) offset = unpack_from('>Q', self._contents, offset)[0] return offset def _unpack_crc32_checksum(self, i): return unpack_from('>L', self._contents, - self._crc32_table_offset + i * 4)[0] + self._crc32_table_offset + i * 4)[0] def read_pack_header(read): """Read the header of a pack file. :param read: Read function - :return: Tuple of (pack version, number of objects). If no data is available - to read, returns (None, None). + :return: Tuple of (pack version, number of objects). If no data is + available to read, returns (None, None). """ header = read(12) if not header: @@ -777,7 +788,8 @@ else: to_pop = max(n + tn - 20, 0) to_add = n - self.sha.update(bytes(bytearray([self._trailer.popleft() for _ in range(to_pop)]))) + self.sha.update( + bytes(bytearray([self._trailer.popleft() for _ in range(to_pop)]))) self._trailer.extend(data[-to_add:]) # hash everything but the trailer @@ -878,8 +890,8 @@ def __init__(self, read_all, read_some, outfile, delta_iter=None): """Initialize the copier. - :param read_all: Read function that blocks until the number of requested - bytes are read. + :param read_all: Read function that blocks until the number of + requested bytes are read. :param read_some: Read function that returns at least one byte, but may not return the number of bytes requested. :param outfile: File-like object to write output through. @@ -922,7 +934,7 @@ return sha.digest() -def compute_file_sha(f, start_ofs=0, end_ofs=0, buffer_size=1<<16): +def compute_file_sha(f, start_ofs=0, end_ofs=0, buffer_size=1 << 16): """Hash a portion of a file into a new SHA. :param f: A file-like object to read from that supports seek(). @@ -979,8 +991,8 @@ def __init__(self, filename, file=None, size=None): """Create a PackData object representing the pack in the given filename. - The file must exist and stay readable until the object is disposed of. It - must also stay the same size. It will be mapped whenever needed. + The file must exist and stay readable until the object is disposed of. + It must also stay the same size. It will be mapped whenever needed. Currently there is a restriction on the size of the pack as the python mmap implementation is flawed. @@ -993,14 +1005,18 @@ else: self._file = file (version, self._num_objects) = read_pack_header(self._file.read) - self._offset_cache = LRUSizeCache(1024*1024*20, - compute_size=_compute_object_size) + self._offset_cache = LRUSizeCache( + 1024*1024*20, compute_size=_compute_object_size) self.pack = None @property def filename(self): return os.path.basename(self._filename) + @property + def path(self): + return self._filename + @classmethod def from_file(cls, file, size): return cls(str(file), file=file, size=size) @@ -1074,12 +1090,6 @@ if base_type == OFS_DELTA: (delta_offset, delta) = base_obj # TODO: clean up asserts and replace with nicer error messages - assert ( - isinstance(base_offset, int) - or isinstance(base_offset, long)) - assert ( - isinstance(delta_offset, int) - or isinstance(base_offset, long)) base_offset = base_offset - delta_offset base_type, base_obj = self.get_object_at(base_offset) assert isinstance(base_type, int) @@ -1114,7 +1124,8 @@ progress(i, self._num_objects) yield (offset, unpacked.pack_type_num, unpacked._obj(), unpacked.crc32) - self._file.seek(-len(unused), SEEK_CUR) # Back up over unused data. + # Back up over unused data. + self._file.seek(-len(unused), SEEK_CUR) def _iter_unpacked(self): # TODO(dborowitz): Merge this with iterobjects, if we can change its @@ -1130,7 +1141,8 @@ self._file.read, compute_crc32=False) unpacked.offset = offset yield unpacked - self._file.seek(-len(unused), SEEK_CUR) # Back up over unused data. + # Back up over unused data. + self._file.seek(-len(unused), SEEK_CUR) def iterentries(self, progress=None): """Yield entries summarizing the contents of this pack. @@ -1156,8 +1168,7 @@ object count :return: List of tuples with (sha, offset, crc32) """ - ret = list(self.iterentries(progress=progress)) - ret.sort() + ret = sorted(self.iterentries(progress=progress)) return ret def create_index_v1(self, filename, progress=None): @@ -1304,9 +1315,9 @@ try: type_num, chunks = self._resolve_ext_ref(base_sha) except KeyError: - # Not an external ref, but may depend on one. Either it will get - # popped via a _follow_chain call, or we will raise an error - # below. + # Not an external ref, but may depend on one. Either it will + # get popped via a _follow_chain call, or we will raise an + # error below. continue self._ext_refs.append(base_sha) self._pending_ref.pop(base_sha) @@ -1372,7 +1383,7 @@ class SHA1Reader(object): - """Wrapper around a file-like object that remembers the SHA1 of its data.""" + """Wrapper for file-like object that remembers the SHA1 of its data.""" def __init__(self, f): self.f = f @@ -1396,7 +1407,7 @@ class SHA1Writer(object): - """Wrapper around a file-like object that remembers the SHA1 of its data.""" + """Wrapper for file-like object that remembers the SHA1 of its data.""" def __init__(self, f): self.f = f @@ -1491,10 +1502,9 @@ :return: Tuple with checksum of pack file and index file """ with GitFile(filename + '.pack', 'wb') as f: - entries, data_sum = write_pack_objects(f, objects, - delta_window_size=delta_window_size, deltify=deltify) - entries = [(k, v[0], v[1]) for (k, v) in entries.items()] - entries.sort() + entries, data_sum = write_pack_objects( + f, objects, delta_window_size=delta_window_size, deltify=deltify) + entries = sorted([(k, v[0], v[1]) for (k, v) in entries.items()]) with GitFile(filename + '.idx', 'wb') as f: return data_sum, write_pack_index_v2(f, entries, data_sum) @@ -1634,6 +1644,7 @@ # 24-bit lengths in copy operations, but we always make version 2 packs. _MAX_COPY_LEN = 0xffff + def _encode_copy_operation(start, length): scratch = [] op = 0x80 @@ -1664,7 +1675,7 @@ seq = difflib.SequenceMatcher(a=base_buf, b=target_buf) for opcode, i1, i2, j1, j2 in seq.get_opcodes(): # Git patch opcodes don't care about deletes! - #if opcode == 'replace' or opcode == 'delete': + # if opcode == 'replace' or opcode == 'delete': # pass if opcode == 'equal': # If they are equal, unpacker will use data from base_buf @@ -1704,6 +1715,7 @@ out = [] index = 0 delta_length = len(delta) + def get_delta_header_size(delta, index): size = 0 i = 0 @@ -1738,8 +1750,8 @@ if cp_size == 0: cp_size = 0x10000 if (cp_off + cp_size < cp_size or - cp_off + cp_size > src_size or - cp_size > dest_size): + cp_off + cp_size > src_size or + cp_size > dest_size): break out.append(src_buf[cp_off:cp_off+cp_size]) elif cmd != 0: @@ -1945,8 +1957,8 @@ def keep(self, msg=None): """Add a .keep file for the pack, preventing git from garbage collecting it. - :param msg: A message written inside the .keep file; can be used later to - determine whether or not a .keep file is obsolete. + :param msg: A message written inside the .keep file; can be used later + to determine whether or not a .keep file is obsolete. :return: The path of the .keep file, as a string. """ keepfile_name = '%s.keep' % self._basename @@ -1958,6 +1970,6 @@ try: - from dulwich._pack import apply_delta, bisect_find_sha + from dulwich._pack import apply_delta, bisect_find_sha # noqa: F811 except ImportError: pass diff -Nru dulwich-0.12.0/dulwich/patch.py dulwich-0.18.5/dulwich/patch.py --- dulwich-0.12.0/dulwich/patch.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/patch.py 2017-10-07 15:45:17.000000000 +0000 @@ -1,20 +1,22 @@ # patch.py -- For dealing with packed-style patches. # Copyright (C) 2009-2013 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) a later version. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Classes for dealing with git am-style patches. @@ -27,6 +29,7 @@ import time from dulwich.objects import ( + Blob, Commit, S_ISGITLINK, ) @@ -34,7 +37,8 @@ FIRST_FEW_BYTES = 8000 -def write_commit_patch(f, commit, contents, progress, version=None, encoding=None): +def write_commit_patch(f, commit, contents, progress, version=None, + encoding=None): """Write a individual file patch. :param commit: Commit object @@ -42,13 +46,16 @@ :return: tuple with filename and contents """ encoding = encoding or getattr(f, "encoding", "ascii") - if type(contents) is str: + if isinstance(contents, str): contents = contents.encode(encoding) (num, total) = progress - f.write(b"From " + commit.id + b" " + time.ctime(commit.commit_time).encode(encoding) + b"\n") + f.write(b"From " + commit.id + b" " + + time.ctime(commit.commit_time).encode(encoding) + b"\n") f.write(b"From: " + commit.author + b"\n") - f.write(b"Date: " + time.strftime("%a, %d %b %Y %H:%M:%S %Z").encode(encoding) + b"\n") - f.write(("Subject: [PATCH %d/%d] " % (num, total)).encode(encoding) + commit.message + b"\n") + f.write(b"Date: " + + time.strftime("%a, %d %b %Y %H:%M:%S %Z").encode(encoding) + b"\n") + f.write(("Subject: [PATCH %d/%d] " % (num, total)).encode(encoding) + + commit.message + b"\n") f.write(b"\n") f.write(b"---\n") try: @@ -56,7 +63,7 @@ p = subprocess.Popen(["diffstat"], stdout=subprocess.PIPE, stdin=subprocess.PIPE) except (ImportError, OSError): - pass # diffstat not available? + pass # diffstat not available? else: (diffstat, _) = p.communicate(contents) f.write(diffstat) @@ -79,31 +86,63 @@ return commit.message.splitlines()[0].replace(" ", "-") -def unified_diff(a, b, fromfile, tofile, n=3): - """difflib.unified_diff that doesn't write any dates or trailing spaces. +# Unified Diff +def _format_range_unified(start, stop): + 'Convert range to the "ed" format' + # Per the diff spec at http://www.unix.org/single_unix_specification/ + beginning = start + 1 # lines start numbering with one + length = stop - start + if length == 1: + return '{}'.format(beginning) + if not length: + beginning -= 1 # empty ranges begin at line just before the range + return '{},{}'.format(beginning, length) + + +def unified_diff(a, b, fromfile='', tofile='', fromfiledate='', + tofiledate='', n=3, lineterm='\n'): + """difflib.unified_diff that can detect "No newline at end of file" as + original "git diff" does. - Based on the same function in Python2.6.5-rc2's difflib.py + Based on the same function in Python2.7 difflib.py """ started = False for group in SequenceMatcher(None, a, b).get_grouped_opcodes(n): if not started: - yield b'--- ' + fromfile + b'\n' - yield b'+++ ' + tofile + b'\n' started = True - i1, i2, j1, j2 = group[0][1], group[-1][2], group[0][3], group[-1][4] - sizes = "@@ -%d,%d +%d,%d @@\n" % (i1+1, i2-i1, j1+1, j2-j1) - yield sizes.encode('ascii') + fromdate = '\t{}'.format(fromfiledate) if fromfiledate else '' + todate = '\t{}'.format(tofiledate) if tofiledate else '' + yield '--- {}{}{}'.format( + fromfile.decode("ascii"), + fromdate, + lineterm + ).encode('ascii') + yield '+++ {}{}{}'.format( + tofile.decode("ascii"), + todate, + lineterm + ).encode('ascii') + + first, last = group[0], group[-1] + file1_range = _format_range_unified(first[1], last[2]) + file2_range = _format_range_unified(first[3], last[4]) + yield '@@ -{} +{} @@{}'.format( + file1_range, + file2_range, + lineterm + ).encode('ascii') + for tag, i1, i2, j1, j2 in group: if tag == 'equal': for line in a[i1:i2]: yield b' ' + line continue - if tag == 'replace' or tag == 'delete': + if tag in ('replace', 'delete'): for line in a[i1:i2]: if not line[-1:] == b'\n': line += b'\n\\ No newline at end of file\n' yield b'-' + line - if tag == 'replace' or tag == 'insert': + if tag in ('replace', 'insert'): for line in b[j1:j2]: if not line[-1:] == b'\n': line += b'\n\\ No newline at end of file\n' @@ -148,28 +187,31 @@ (new_path, new_mode, new_id) = new_file old_path = patch_filename(old_path, b"a") new_path = patch_filename(new_path, b"b") + def content(mode, hexsha): if hexsha is None: - return b'' + return Blob.from_string(b'') elif S_ISGITLINK(mode): - return b"Submodule commit " + hexsha + b"\n" + return Blob.from_string(b"Submodule commit " + hexsha + b"\n") else: - return store[hexsha].data + return store[hexsha] def lines(content): if not content: return [] else: - return content.splitlines(True) + return content.splitlines() f.writelines(gen_diff_header( (old_path, new_path), (old_mode, new_mode), (old_id, new_id))) old_content = content(old_mode, old_id) new_content = content(new_mode, new_id) - if not diff_binary and (is_binary(old_content) or is_binary(new_content)): - f.write(b"Binary files " + old_path + b" and " + new_path + b" differ\n") + if not diff_binary and ( + is_binary(old_content.data) or is_binary(new_content.data)): + f.write(b"Binary files " + old_path + b" and " + new_path + + b" differ\n") else: f.writelines(unified_diff(lines(old_content), lines(new_content), - old_path, new_path)) + old_path, new_path)) # TODO(jelmer): Support writing unicode, rather than bytes. @@ -211,9 +253,10 @@ (new_path, new_mode, new_blob) = new_file old_path = patch_filename(old_path, b"a") new_path = patch_filename(new_path, b"b") + def lines(blob): if blob is not None: - return blob.data.splitlines(True) + return blob.splitlines() else: return [] f.writelines(gen_diff_header( @@ -222,10 +265,9 @@ old_contents = lines(old_blob) new_contents = lines(new_blob) f.writelines(unified_diff(old_contents, new_contents, - old_path, new_path)) + old_path, new_path)) -# TODO(jelmer): Support writing unicode, rather than bytes. def write_tree_diff(f, store, old_tree, new_tree, diff_binary=False): """Write tree diff. @@ -238,8 +280,7 @@ changes = store.tree_changes(old_tree, new_tree) for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes: write_object_diff(f, store, (oldpath, oldmode, oldsha), - (newpath, newmode, newsha), - diff_binary=diff_binary) + (newpath, newmode, newsha), diff_binary=diff_binary) def git_am_patch_split(f, encoding=None): @@ -251,7 +292,8 @@ """ encoding = encoding or getattr(f, "encoding", "ascii") contents = f.read() - if type(contents) is bytes and getattr(email.parser, "BytesParser", None): + if (isinstance(contents, bytes) and + getattr(email.parser, "BytesParser", None)): parser = email.parser.BytesParser() msg = parser.parsebytes(contents) else: diff -Nru dulwich-0.12.0/dulwich/porcelain.py dulwich-0.18.5/dulwich/porcelain.py --- dulwich-0.12.0/dulwich/porcelain.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/porcelain.py 2017-10-18 22:09:30.000000000 +0000 @@ -1,20 +1,22 @@ # porcelain.py -- Porcelain-like layer on top of Dulwich # Copyright (C) 2013 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Simple wrapper that provides porcelain-like functions on top of Dulwich. @@ -22,6 +24,8 @@ * archive * add * branch{_create,_delete,_list} + * check-ignore + * checkout * clone * commit * commit-tree @@ -30,9 +34,11 @@ * fetch * init * ls-remote + * ls-tree * pull * push * rm + * remote{_add} * receive-pack * reset * rev-list @@ -46,14 +52,15 @@ Differences in behaviour are considered bugs. """ -__docformat__ = 'restructuredText' - from collections import namedtuple from contextlib import ( closing, contextmanager, ) +from io import BytesIO import os +import posixpath +import stat import sys import time @@ -63,26 +70,53 @@ from dulwich.client import ( get_transport_and_path, ) +from dulwich.config import ( + StackedConfig, + ) +from dulwich.diff_tree import ( + CHANGE_ADD, + CHANGE_DELETE, + CHANGE_MODIFY, + CHANGE_RENAME, + CHANGE_COPY, + RENAME_CHANGE_TYPES, + ) from dulwich.errors import ( SendPackError, UpdateRefsError, ) -from dulwich.index import get_unstaged_changes +from dulwich.ignore import IgnoreFilterManager +from dulwich.index import ( + blob_from_path_and_stat, + get_unstaged_changes, + ) +from dulwich.object_store import ( + tree_lookup_path, + ) from dulwich.objects import ( Commit, Tag, + format_timezone, parse_timezone, + pretty_format_tree_entry, ) from dulwich.objectspec import ( + parse_commit, parse_object, + parse_ref, parse_reftuples, + parse_tree, ) from dulwich.pack import ( write_pack_index, write_pack_objects, ) from dulwich.patch import write_tree_diff -from dulwich.protocol import Protocol +from dulwich.protocol import ( + Protocol, + ZERO_SHA, + ) +from dulwich.refs import ANNOTATED_TAG_SUFFIX from dulwich.repo import (BaseRepo, Repo) from dulwich.server import ( FileSystemBackend, @@ -101,12 +135,11 @@ default_bytes_err_stream = getattr(sys.stderr, 'buffer', sys.stderr) -def encode_path(path): - """Encode a path as bytestring.""" - # TODO(jelmer): Use something other than ascii? - if not isinstance(path, bytes): - path = path.encode('ascii') - return path +DEFAULT_ENCODING = 'utf-8' + + +class RemoteExists(Exception): + """Raised when the remote already exists.""" def open_repo(path_or_repo): @@ -132,8 +165,21 @@ return closing(Repo(path_or_repo)) -def archive(repo, committish=None, outstream=sys.stdout, - errstream=sys.stderr): +def path_to_tree_path(repopath, path): + """Convert a path to a path usable in e.g. an index. + + :param repo: Repository + :param path: A path + :return: A path formatted for use in e.g. an index + """ + os.path.relpath(path, repopath) + if os.path.sep != '/': + path = path.replace(os.path.sep, '/') + return path.encode(sys.getfilesystemencoding()) + + +def archive(repo, committish=None, outstream=default_bytes_out_stream, + errstream=default_bytes_err_stream): """Create an archive. :param repo: Path of repository for which to generate an archive. @@ -146,9 +192,9 @@ committish = "HEAD" with open_repo_closing(repo) as repo_obj: c = repo_obj[committish] - tree = c.tree - for chunk in tar_stream(repo_obj.object_store, - repo_obj.object_store[c.tree], c.commit_time): + for chunk in tar_stream( + repo_obj.object_store, repo_obj.object_store[c.tree], + c.commit_time): outstream.write(chunk) @@ -187,8 +233,7 @@ # FIXME: Support --all argument # FIXME: Support --signoff argument with open_repo_closing(repo) as r: - return r.do_commit(message=message, author=author, - committer=committer) + return r.do_commit(message=message, author=author, committer=committer) def commit_tree(repo, tree, message=None, author=None, committer=None): @@ -200,8 +245,8 @@ :param committer: Optional committer name and email """ with open_repo_closing(repo) as r: - return r.do_commit(message=message, tree=tree, committer=committer, - author=author) + return r.do_commit( + message=message, tree=tree, committer=committer, author=author) def init(path=".", bare=False): @@ -220,27 +265,34 @@ return Repo.init(path) -def clone(source, target=None, bare=False, checkout=None, errstream=default_bytes_err_stream, outstream=None): +def clone(source, target=None, bare=False, checkout=None, + errstream=default_bytes_err_stream, outstream=None, + origin=b"origin"): """Clone a local or remote git repository. :param source: Path or URL for source repository :param target: Path to target repository (optional) :param bare: Whether or not to create a bare repository + :param checkout: Whether or not to check-out HEAD after cloning :param errstream: Optional stream to write progress to :param outstream: Optional stream to write progress to (deprecated) + :param origin: Name of remote from the repository used to clone :return: The new repository """ if outstream is not None: import warnings - warnings.warn("outstream= has been deprecated in favour of errstream=.", DeprecationWarning, - stacklevel=3) + warnings.warn( + "outstream= has been deprecated in favour of errstream=.", + DeprecationWarning, stacklevel=3) errstream = outstream if checkout is None: checkout = (not bare) if checkout and bare: raise ValueError("checkout and bare are incompatible") - client, host_path = get_transport_and_path(source) + + config = StackedConfig.default() + client, host_path = get_transport_and_path(source, config=config) if target is None: target = host_path.split("/")[-1] @@ -253,11 +305,31 @@ else: r = Repo.init(target) try: - remote_refs = client.fetch(host_path, r, - determine_wants=r.object_store.determine_wants_all, + remote_refs = client.fetch( + host_path, r, determine_wants=r.object_store.determine_wants_all, progress=errstream.write) - r[b"HEAD"] = remote_refs[b"HEAD"] - if checkout: + r.refs.import_refs( + b'refs/remotes/' + origin, + {n[len(b'refs/heads/'):]: v for (n, v) in remote_refs.items() + if n.startswith(b'refs/heads/')}) + r.refs.import_refs( + b'refs/tags', + {n[len(b'refs/tags/'):]: v for (n, v) in remote_refs.items() + if n.startswith(b'refs/tags/') and + not n.endswith(ANNOTATED_TAG_SUFFIX)}) + if b"HEAD" in remote_refs and not bare: + # TODO(jelmer): Support symref capability, + # https://github.com/jelmer/dulwich/issues/485 + r[b"HEAD"] = remote_refs[b"HEAD"] + target_config = r.get_config() + if not isinstance(source, bytes): + source = source.encode(DEFAULT_ENCODING) + target_config.set((b'remote', origin), b'url', source) + target_config.set( + (b'remote', origin), b'fetch', + b'+refs/heads/*:refs/remotes/' + origin + b'/*') + target_config.write_to_path() + if checkout and b"HEAD" in r.refs: errstream.write(b'Checking out HEAD\n') r.reset_index() except: @@ -272,22 +344,29 @@ :param repo: Repository for the files :param paths: Paths to add. No value passed stages all modified files. + :return: Tuple with set of added files and ignored files """ - # FIXME: Support patterns, directories. + ignored = set() with open_repo_closing(repo) as r: + ignore_manager = IgnoreFilterManager.from_repo(r) if not paths: - # If nothing is specified, add all non-ignored files. - paths = [] - for dirpath, dirnames, filenames in os.walk(r.path): - # Skip .git and below. - if '.git' in dirnames: - dirnames.remove('.git') - for filename in filenames: - paths.append(os.path.join(dirpath[len(r.path)+1:], filename)) - r.stage(paths) + paths = list( + get_untracked_paths(os.getcwd(), r.path, r.open_index())) + relpaths = [] + if not isinstance(paths, list): + paths = [paths] + for p in paths: + relpath = os.path.relpath(p, r.path) + # FIXME: Support patterns, directories. + if ignore_manager.is_ignored(relpath): + ignored.add(relpath) + continue + relpaths.append(relpath) + r.stage(relpaths) + return (relpaths, ignored) -def rm(repo=".", paths=None): +def remove(repo=".", paths=None, cached=False): """Remove files from the staging area. :param repo: Repository for the files @@ -296,11 +375,47 @@ with open_repo_closing(repo) as r: index = r.open_index() for p in paths: - del index[p.encode(sys.getfilesystemencoding())] + full_path = os.path.abspath(p).encode(sys.getfilesystemencoding()) + tree_path = path_to_tree_path(r.path, p) + try: + index_sha = index[tree_path].sha + except KeyError: + raise Exception('%s did not match any files' % p) + + if not cached: + try: + st = os.lstat(full_path) + except OSError: + pass + else: + try: + blob = blob_from_path_and_stat(full_path, st) + except IOError: + pass + else: + try: + committed_sha = tree_lookup_path( + r.__getitem__, r[r.head()].tree, tree_path)[1] + except KeyError: + committed_sha = None + + if blob.id != index_sha and index_sha != committed_sha: + raise Exception( + 'file has staged content differing ' + 'from both the file and head: %s' % p) + + if index_sha != committed_sha: + raise Exception( + 'file has staged changes: %s' % p) + os.remove(full_path) + del index[tree_path] index.write() -def commit_decode(commit, contents, default_encoding='utf-8'): +rm = remove + + +def commit_decode(commit, contents, default_encoding=DEFAULT_ENCODING): if commit.encoding is not None: return contents.decode(commit.encoding, "replace") return contents.decode(default_encoding, "replace") @@ -315,10 +430,17 @@ outstream.write("-" * 50 + "\n") outstream.write("commit: " + commit.id.decode('ascii') + "\n") if len(commit.parents) > 1: - outstream.write("merge: " + + outstream.write( + "merge: " + "...".join([c.decode('ascii') for c in commit.parents[1:]]) + "\n") - outstream.write("author: " + decode(commit.author) + "\n") - outstream.write("committer: " + decode(commit.committer) + "\n") + outstream.write("Author: " + decode(commit.author) + "\n") + if commit.author != commit.committer: + outstream.write("Committer: " + decode(commit.committer) + "\n") + + time_tuple = time.gmtime(commit.author_time + commit.author_timezone) + time_str = time.strftime("%a %b %d %Y %H:%M:%S", time_tuple) + timezone_str = format_timezone(commit.author_timezone).decode('ascii') + outstream.write("Date: " + time_str + " " + timezone_str + "\n") outstream.write("\n") outstream.write(decode(commit.message) + "\n") outstream.write("\n") @@ -358,8 +480,19 @@ :param outstream: Stream to write to """ print_commit(commit, decode=decode, outstream=outstream) - parent_commit = repo[commit.parents[0]] - write_tree_diff(outstream, repo.object_store, parent_commit.tree, commit.tree) + if commit.parents: + parent_commit = repo[commit.parents[0]] + base_tree = parent_commit.tree + else: + base_tree = None + diffstream = BytesIO() + write_tree_diff( + diffstream, + repo.object_store, base_tree, commit.tree) + diffstream.seek(0) + outstream.write( + diffstream.getvalue().decode( + commit.encoding or DEFAULT_ENCODING, 'replace')) def show_tree(repo, tree, decode, outstream=sys.stdout): @@ -395,28 +528,69 @@ }[obj.type_name](repo, obj, decode, outstream) -def log(repo=".", outstream=sys.stdout, max_entries=None): +def print_name_status(changes): + """Print a simple status summary, listing changed files. + """ + for change in changes: + if not change: + continue + if isinstance(change, list): + change = change[0] + if change.type == CHANGE_ADD: + path1 = change.new.path + path2 = '' + kind = 'A' + elif change.type == CHANGE_DELETE: + path1 = change.old.path + path2 = '' + kind = 'D' + elif change.type == CHANGE_MODIFY: + path1 = change.new.path + path2 = '' + kind = 'M' + elif change.type in RENAME_CHANGE_TYPES: + path1 = change.old.path + path2 = change.new.path + if change.type == CHANGE_RENAME: + kind = 'R' + elif change.type == CHANGE_COPY: + kind = 'C' + yield '%-8s%-20s%-20s' % (kind, path1, path2) + + +def log(repo=".", paths=None, outstream=sys.stdout, max_entries=None, + reverse=False, name_status=False): """Write commit logs. :param repo: Path to repository + :param paths: Optional set of specific paths to print entries for :param outstream: Stream to write log output to + :param reverse: Reverse order in which entries are printed + :param name_status: Print name status :param max_entries: Optional maximum number of entries to display """ with open_repo_closing(repo) as r: - walker = r.get_walker(max_entries=max_entries) + walker = r.get_walker( + max_entries=max_entries, paths=paths, reverse=reverse) for entry in walker: - decode = lambda x: commit_decode(entry.commit, x) + def decode(x): + return commit_decode(entry.commit, x) print_commit(entry.commit, decode, outstream) + if name_status: + outstream.writelines( + [l+'\n' for l in print_name_status(entry.changes())]) # TODO(jelmer): better default for encoding? -def show(repo=".", objects=None, outstream=sys.stdout, default_encoding='utf-8'): +def show(repo=".", objects=None, outstream=sys.stdout, + default_encoding=DEFAULT_ENCODING): """Print the changes in a commit. :param repo: Path to repository :param objects: Objects to show (defaults to [HEAD]) :param outstream: Stream to write to - :param default_encoding: Default encoding to use if none is set in the commit + :param default_encoding: Default encoding to use if none is set in the + commit """ if objects is None: objects = ["HEAD"] @@ -426,9 +600,11 @@ for objectish in objects: o = parse_object(r, objectish) if isinstance(o, Commit): - decode = lambda x: commit_decode(o, x, default_encoding) + def decode(x): + return commit_decode(o, x, default_encoding) else: - decode = lambda x: x.decode(default_encoding) + def decode(x): + return x.decode(default_encoding) show_object(r, o, decode, outstream) @@ -458,11 +634,13 @@ def tag(*args, **kwargs): import warnings - warnings.warn("tag has been deprecated in favour of tag_create.", DeprecationWarning) + warnings.warn("tag has been deprecated in favour of tag_create.", + DeprecationWarning) return tag_create(*args, **kwargs) -def tag_create(repo, tag, author=None, message=None, annotated=False, +def tag_create( + repo, tag, author=None, message=None, annotated=False, objectish="HEAD", tag_time=None, tag_timezone=None): """Creates a tag in git via dulwich calls: @@ -489,9 +667,9 @@ tag_obj.message = message tag_obj.name = tag tag_obj.object = (type(object), object.id) - tag_obj.tag_time = tag_time if tag_time is None: tag_time = int(time.time()) + tag_obj.tag_time = tag_time if tag_timezone is None: # TODO(jelmer) Use current user timezone rather than UTC tag_timezone = 0 @@ -508,7 +686,8 @@ def list_tags(*args, **kwargs): import warnings - warnings.warn("list_tags has been deprecated in favour of tag_list.", DeprecationWarning) + warnings.warn("list_tags has been deprecated in favour of tag_list.", + DeprecationWarning) return tag_list(*args, **kwargs) @@ -519,8 +698,7 @@ :param outstream: Stream to write tags to """ with open_repo_closing(repo) as r: - tags = list(r.refs.as_dict(b"refs/tags")) - tags.sort() + tags = sorted(r.refs.as_dict(b"refs/tags")) return tags @@ -541,28 +719,30 @@ del r.refs[b"refs/tags/" + name] -def reset(repo, mode, committish="HEAD"): +def reset(repo, mode, treeish="HEAD"): """Reset current HEAD to the specified state. :param repo: Path to repository :param mode: Mode ("hard", "soft", "mixed") + :param treeish: Treeish to reset to """ if mode != "hard": raise ValueError("hard is the only mode currently supported") with open_repo_closing(repo) as r: - tree = r[committish].tree - r.reset_index() + tree = parse_tree(r, treeish) + r.reset_index(tree.id) -def push(repo, remote_location, refspecs=None, - outstream=sys.stdout, errstream=sys.stderr): +def push(repo, remote_location, refspecs, + outstream=default_bytes_out_stream, + errstream=default_bytes_err_stream): """Remote push with dulwich via dulwich.client :param repo: Path to repository :param remote_location: Location of the remote - :param refspecs: relative path to the refs to push to remote + :param refspecs: Refs to push to remote :param outstream: A stream file to write output :param errstream: A stream file to write errors """ @@ -571,35 +751,39 @@ with open_repo_closing(repo) as r: # Get the client and path - client, path = get_transport_and_path(remote_location) + client, path = get_transport_and_path( + remote_location, config=r.get_config_stack()) selected_refs = [] def update_refs(refs): selected_refs.extend(parse_reftuples(r.refs, refs, refspecs)) + new_refs = {} # TODO: Handle selected_refs == {None: None} for (lh, rh, force) in selected_refs: if lh is None: - del refs[rh] + new_refs[rh] = ZERO_SHA else: - refs[rh] = r.refs[lh] - return refs + new_refs[rh] = r.refs[lh] + return new_refs - err_encoding = getattr(errstream, 'encoding', 'utf-8') - remote_location_bytes = remote_location.encode(err_encoding) + err_encoding = getattr(errstream, 'encoding', None) or DEFAULT_ENCODING + remote_location_bytes = client.get_url(path).encode(err_encoding) try: - client.send_pack(path, update_refs, - r.object_store.generate_pack_contents, progress=errstream.write) - errstream.write(b"Push to " + remote_location_bytes + - b" successful.\n") + client.send_pack( + path, update_refs, r.object_store.generate_pack_contents, + progress=errstream.write) + errstream.write( + b"Push to " + remote_location_bytes + b" successful.\n") except (UpdateRefsError, SendPackError) as e: errstream.write(b"Push to " + remote_location_bytes + b" failed -> " + e.message.encode(err_encoding) + b"\n") -def pull(repo, remote_location, refspecs=None, - outstream=sys.stdout, errstream=sys.stderr): +def pull(repo, remote_location=None, refspecs=None, + outstream=default_bytes_out_stream, + errstream=default_bytes_err_stream): """Pull from remote via dulwich.client :param repo: Path to repository @@ -610,13 +794,22 @@ """ # Open the repo with open_repo_closing(repo) as r: + if remote_location is None: + # TODO(jelmer): Lookup 'remote' for current branch in config + raise NotImplementedError( + "looking up remote from branch config not supported yet") + if refspecs is None: + refspecs = [b"HEAD"] selected_refs = [] + def determine_wants(remote_refs): - selected_refs.extend(parse_reftuples(remote_refs, r.refs, refspecs)) + selected_refs.extend( + parse_reftuples(remote_refs, r.refs, refspecs)) return [remote_refs[lh] for (lh, rh, force) in selected_refs] - client, path = get_transport_and_path(remote_location) - remote_refs = client.fetch(path, r, progress=errstream.write, - determine_wants=determine_wants) + client, path = get_transport_and_path( + remote_location, config=r.get_config_stack()) + remote_refs = client.fetch( + path, r, progress=errstream.write, determine_wants=determine_wants) for (lh, rh, force) in selected_refs: r.refs[rh] = remote_refs[lh] if selected_refs: @@ -624,13 +817,14 @@ # Perform 'git checkout .' - syncs staged changes tree = r[b"HEAD"].tree - r.reset_index() + r.reset_index(tree=tree) -def status(repo="."): +def status(repo=".", ignored=False): """Returns staged, unstaged, and untracked changes relative to the HEAD. :param repo: Path to repository or repository object + :param ignored: Whether to include ignoed files in `untracked` :return: GitStatus tuple, staged - list of staged paths (diff index/HEAD) unstaged - list of unstaged paths (diff index/working-tree) @@ -640,12 +834,44 @@ # 1. Get status of staged tracked_changes = get_tree_changes(r) # 2. Get status of unstaged - unstaged_changes = list(get_unstaged_changes(r.open_index(), r.path)) - # TODO - Status of untracked - add untracked changes, need gitignore. - untracked_changes = [] + index = r.open_index() + unstaged_changes = list(get_unstaged_changes(index, r.path)) + ignore_manager = IgnoreFilterManager.from_repo(r) + untracked_paths = get_untracked_paths(r.path, r.path, index) + if ignored: + untracked_changes = list(untracked_paths) + else: + untracked_changes = [ + p for p in untracked_paths + if not ignore_manager.is_ignored(p)] return GitStatus(tracked_changes, unstaged_changes, untracked_changes) +def get_untracked_paths(frompath, basepath, index): + """Get untracked paths. + + ;param frompath: Path to walk + :param basepath: Path to compare to + :param index: Index to check against + """ + # If nothing is specified, add all non-ignored files. + for dirpath, dirnames, filenames in os.walk(frompath): + # Skip .git and below. + if '.git' in dirnames: + dirnames.remove('.git') + if dirpath != basepath: + continue + if '.git' in filenames: + filenames.remove('.git') + if dirpath != basepath: + continue + for filename in filenames: + ap = os.path.join(dirpath, filename) + ip = path_to_tree_path(basepath, ap) + if ip not in index: + yield os.path.relpath(ap, frompath) + + def get_tree_changes(repo): """Return add/delete/modify changes to tree by comparing index to HEAD. @@ -663,7 +889,12 @@ 'delete': [], 'modify': [], } - for change in index.changes_from_tree(r.object_store, r[b'HEAD'].tree): + try: + tree_id = r[b'HEAD'].tree + except KeyError: + tree_id = None + + for change in index.changes_from_tree(r.object_store, tree_id): if not change[0][0]: tracked_changes['add'].append(change[0][1]) elif not change[0][1]: @@ -720,7 +951,9 @@ outf = getattr(sys.stdout, 'buffer', sys.stdout) if inf is None: inf = getattr(sys.stdin, 'buffer', sys.stdin) + path = os.path.expanduser(path) backend = FileSystemBackend(path) + def send_fn(data): outf.write(data) outf.flush() @@ -742,7 +975,9 @@ outf = getattr(sys.stdout, 'buffer', sys.stdout) if inf is None: inf = getattr(sys.stdin, 'buffer', sys.stdin) + path = os.path.expanduser(path) backend = FileSystemBackend(path) + def send_fn(data): outf.write(data) outf.flush() @@ -779,12 +1014,6 @@ :param force: Force creation of branch, even if it already exists """ with open_repo_closing(repo) as r: - if isinstance(name, bytes): - names = [name] - elif isinstance(name, list): - names = name - else: - raise TypeError("Unexpected branch name type %r" % name) if objectish is None: objectish = "HEAD" object = parse_object(r, objectish) @@ -803,7 +1032,8 @@ return r.refs.keys(base=b"refs/heads/") -def fetch(repo, remote_location, outstream=sys.stdout, errstream=sys.stderr): +def fetch(repo, remote_location, outstream=sys.stdout, + errstream=default_bytes_err_stream): """Fetch objects from a remote server. :param repo: Path to the repository @@ -813,14 +1043,21 @@ :return: Dictionary with refs on the remote """ with open_repo_closing(repo) as r: - client, path = get_transport_and_path(remote_location) + client, path = get_transport_and_path( + remote_location, config=r.get_config_stack()) remote_refs = client.fetch(path, r, progress=errstream.write) return remote_refs def ls_remote(remote): - client, host_path = get_transport_and_path(remote) - return client.get_refs(encode_path(host_path)) + """List the refs in a remote. + + :param remote: Remote repository location + :return: Dictionary with remote refs + """ + config = StackedConfig.default() + client, host_path = get_transport_and_path(remote, config=config) + return client.get_refs(host_path) def repack(repo): @@ -848,6 +1085,97 @@ r.object_store.iter_shas((oid, None) for oid in object_ids), delta_window_size=delta_window_size) if idxf is not None: - entries = [(k, v[0], v[1]) for (k, v) in entries.items()] - entries.sort() + entries = sorted([(k, v[0], v[1]) for (k, v) in entries.items()]) write_pack_index(idxf, entries, data_sum) + + +def ls_tree(repo, treeish=b"HEAD", outstream=sys.stdout, recursive=False, + name_only=False): + """List contents of a tree. + + :param repo: Path to the repository + :param tree_ish: Tree id to list + :param outstream: Output stream (defaults to stdout) + :param recursive: Whether to recursively list files + :param name_only: Only print item name + """ + def list_tree(store, treeid, base): + for (name, mode, sha) in store[treeid].iteritems(): + if base: + name = posixpath.join(base, name) + if name_only: + outstream.write(name + b"\n") + else: + outstream.write(pretty_format_tree_entry(name, mode, sha)) + if stat.S_ISDIR(mode): + list_tree(store, sha, name) + with open_repo_closing(repo) as r: + tree = parse_tree(r, treeish) + list_tree(r.object_store, tree.id, "") + + +def remote_add(repo, name, url): + """Add a remote. + + :param repo: Path to the repository + :param name: Remote name + :param url: Remote URL + """ + if not isinstance(name, bytes): + name = name.encode(DEFAULT_ENCODING) + if not isinstance(url, bytes): + url = url.encode(DEFAULT_ENCODING) + with open_repo_closing(repo) as r: + c = r.get_config() + section = (b'remote', name) + if c.has_section(section): + raise RemoteExists(section) + c.set(section, b"url", url) + c.write_to_path() + + +def check_ignore(repo, paths, no_index=False): + """Debug gitignore files. + + :param repo: Path to the repository + :param paths: List of paths to check for + :param no_index: Don't check index + :return: List of ignored files + """ + with open_repo_closing(repo) as r: + index = r.open_index() + ignore_manager = IgnoreFilterManager.from_repo(r) + for path in paths: + if os.path.isabs(path): + path = os.path.relpath(path, r.path) + if not no_index and path_to_tree_path(r.path, path) in index: + continue + if ignore_manager.is_ignored(path): + yield path + + +def update_head(repo, target, detached=False, new_branch=None): + """Update HEAD to point at a new branch/commit. + + Note that this does not actually update the working tree. + + :param repo: Path to the repository + :param detach: Create a detached head + :param target: Branch or committish to switch to + :param new_branch: New branch to create + """ + with open_repo_closing(repo) as r: + if new_branch is not None: + to_set = b"refs/heads/" + new_branch.encode(DEFAULT_ENCODING) + else: + to_set = b"HEAD" + if detached: + # TODO(jelmer): Provide some way so that the actual ref gets + # updated rather than what it points to, so the delete isn't + # necessary. + del r.refs[to_set] + r.refs[to_set] = parse_commit(r, target).id + else: + r.refs.set_symbolic_ref(to_set, parse_ref(r, target)) + if new_branch is not None: + r.refs.set_symbolic_ref(b"HEAD", to_set) diff -Nru dulwich-0.12.0/dulwich/protocol.py dulwich-0.18.5/dulwich/protocol.py --- dulwich-0.12.0/dulwich/protocol.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/protocol.py 2017-10-07 15:45:17.000000000 +0000 @@ -2,20 +2,22 @@ # Copyright (C) 2008 John Carr # Copyright (C) 2008-2012 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# or (at your option) any later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Generic functions for talking the git smart server protocol.""" @@ -46,6 +48,9 @@ # fatal error message just before stream aborts SIDE_BAND_CHANNEL_FATAL = 3 +CAPABILITY_DEEPEN_SINCE = b'deepen-since' +CAPABILITY_DEEPEN_NOT = b'deepen-not' +CAPABILITY_DEEPEN_RELATIVE = b'deepen-relative' CAPABILITY_DELETE_REFS = b'delete-refs' CAPABILITY_INCLUDE_TAG = b'include-tag' CAPABILITY_MULTI_ACK = b'multi_ack' @@ -56,9 +61,35 @@ CAPABILITY_QUIET = b'quiet' CAPABILITY_REPORT_STATUS = b'report-status' CAPABILITY_SHALLOW = b'shallow' +CAPABILITY_SIDE_BAND = b'side-band' CAPABILITY_SIDE_BAND_64K = b'side-band-64k' CAPABILITY_THIN_PACK = b'thin-pack' CAPABILITY_AGENT = b'agent' +CAPABILITY_SYMREF = b'symref' + +# Magic ref that is used to attach capabilities to when +# there are no refs. Should always be ste to ZERO_SHA. +CAPABILITIES_REF = b'capabilities^{}' + +COMMON_CAPABILITIES = [ + CAPABILITY_OFS_DELTA, + CAPABILITY_SIDE_BAND, + CAPABILITY_SIDE_BAND_64K, + CAPABILITY_AGENT, + CAPABILITY_NO_PROGRESS] +KNOWN_UPLOAD_CAPABILITIES = set(COMMON_CAPABILITIES + [ + CAPABILITY_THIN_PACK, + CAPABILITY_MULTI_ACK, + CAPABILITY_MULTI_ACK_DETAILED, + CAPABILITY_INCLUDE_TAG, + CAPABILITY_DEEPEN_SINCE, + CAPABILITY_SYMREF, + CAPABILITY_SHALLOW, + CAPABILITY_DEEPEN_NOT, + CAPABILITY_DEEPEN_RELATIVE, + ]) +KNOWN_RECEIVE_CAPABILITIES = set(COMMON_CAPABILITIES + [ + CAPABILITY_REPORT_STATUS]) def agent_string(): @@ -69,6 +100,25 @@ return CAPABILITY_AGENT + b'=' + agent_string() +def capability_symref(from_ref, to_ref): + return CAPABILITY_SYMREF + b'=' + from_ref + b':' + to_ref + + +def extract_capability_names(capabilities): + return set(parse_capability(c)[0] for c in capabilities) + + +def parse_capability(capability): + parts = capability.split(b'=', 1) + if len(parts) == 1: + return (parts[0], None) + return tuple(parts) + + +def symref_capabilities(symrefs): + return [capability_symref(*k) for k in symrefs] + + COMMAND_DEEPEN = b'deepen' COMMAND_SHALLOW = b'shallow' COMMAND_UNSHALLOW = b'unshallow' @@ -108,8 +158,8 @@ Parts of the git wire protocol use 'pkt-lines' to communicate. A pkt-line consists of the length of the line as a 4-byte hex string, followed by the - payload data. The length includes the 4-byte header. The special line '0000' - indicates the end of a section of input and is called a 'flush-pkt'. + payload data. The length includes the 4-byte header. The special line + '0000' indicates the end of a section of input and is called a 'flush-pkt'. For details on the pkt-line format, see the cgit distribution: Documentation/technical/protocol-common.txt @@ -163,13 +213,15 @@ else: if len(pkt_contents) + 4 != size: raise GitProtocolError( - 'Length of pkt read %04x does not match length prefix %04x' % (len(pkt_contents) + 4, size)) + 'Length of pkt read %04x does not match length prefix %04x' + % (len(pkt_contents) + 4, size)) return pkt_contents def eof(self): """Test whether the protocol stream has reached EOF. - Note that this refers to the actual stream EOF and not just a flush-pkt. + Note that this refers to the actual stream EOF and not just a + flush-pkt. :return: True if the stream is at EOF, False otherwise. """ @@ -196,7 +248,8 @@ def read_pkt_seq(self): """Read a sequence of pkt-lines from the remote git process. - :return: Yields each line of data up to but not including the next flush-pkt. + :return: Yields each line of data up to but not including the next + flush-pkt. """ pkt = self.read_pkt_line() while pkt: @@ -285,9 +338,9 @@ to a read() method. If you want to read n bytes from the wire and block until exactly n bytes - (or EOF) are read, use read(n). If you want to read at most n bytes from the - wire but don't care if you get less, use recv(n). Note that recv(n) will - still block until at least one byte is read. + (or EOF) are read, use read(n). If you want to read at most n bytes from + the wire but don't care if you get less, use recv(n). Note that recv(n) + will still block until at least one byte is read. """ def __init__(self, recv, write, report_activity=None, rbufsize=_RBUFSIZE): @@ -304,7 +357,8 @@ # - seek back to start rather than 0 in case some buffer has been # consumed. # - use SEEK_END instead of the magic number. - # Copyright (c) 2001-2010 Python Software Foundation; All Rights Reserved + # Copyright (c) 2001-2010 Python Software Foundation; All Rights + # Reserved # Licensed under the Python Software Foundation License. # TODO: see if buffer is more efficient than cBytesIO. assert size > 0 @@ -353,7 +407,7 @@ buf.write(data) buf_len += n del data # explicit free - #assert buf_len == buf.tell() + # assert buf_len == buf.tell() buf.seek(start) return buf.read() @@ -387,7 +441,7 @@ :param text: String to extract from :return: Tuple with text with capabilities removed and list of capabilities """ - if not b"\0" in text: + if b"\0" not in text: return text, [] text, capabilities = text.rstrip().split(b"\0") return (text, capabilities.strip().split(b" ")) @@ -422,9 +476,9 @@ class BufferedPktLineWriter(object): """Writer that wraps its data in pkt-lines and has an independent buffer. - Consecutive calls to write() wrap the data in a pkt-line and then buffers it - until enough lines have been written such that their total length (including - length prefix) reach the buffer size. + Consecutive calls to write() wrap the data in a pkt-line and then buffers + it until enough lines have been written such that their total length + (including length prefix) reach the buffer size. """ def __init__(self, write, bufsize=65515): diff -Nru dulwich-0.12.0/dulwich/reflog.py dulwich-0.18.5/dulwich/reflog.py --- dulwich-0.12.0/dulwich/reflog.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/reflog.py 2017-10-07 15:45:17.000000000 +0000 @@ -1,20 +1,22 @@ # reflog.py -- Parsing and writing reflog files # Copyright (C) 2015 Jelmer Vernooij and others. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Utilities for reading and generating reflogs. """ @@ -27,11 +29,13 @@ ZERO_SHA, ) -Entry = collections.namedtuple('Entry', ['old_sha', 'new_sha', 'committer', - 'timestamp', 'timezone', 'message']) +Entry = collections.namedtuple( + 'Entry', ['old_sha', 'new_sha', 'committer', 'timestamp', 'timezone', + 'message']) -def format_reflog_line(old_sha, new_sha, committer, timestamp, timezone, message): +def format_reflog_line(old_sha, new_sha, committer, timestamp, timezone, + message): """Generate a single reflog line. :param old_sha: Old Commit SHA diff -Nru dulwich-0.12.0/dulwich/refs.py dulwich-0.18.5/dulwich/refs.py --- dulwich-0.12.0/dulwich/refs.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/refs.py 2017-10-07 15:45:17.000000000 +0000 @@ -1,21 +1,22 @@ # refs.py -- For dealing with git refs # Copyright (C) 2008-2013 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Ref handling. @@ -32,6 +33,7 @@ from dulwich.objects import ( git_line, valid_hexsha, + ZERO_SHA, ) from dulwich.file import ( GitFile, @@ -42,6 +44,18 @@ SYMREF = b'ref: ' LOCAL_BRANCH_PREFIX = b'refs/heads/' BAD_REF_CHARS = set(b'\177 ~^:?*[') +ANNOTATED_TAG_SUFFIX = b'^{}' + + +def parse_symref_value(contents): + """Parse a symref value. + + :param contents: Contents to parse + :return: Destination + """ + if contents.startswith(SYMREF): + return contents[len(SYMREF):].rstrip(b'\r\n') + raise ValueError(contents) def check_ref_format(refname): @@ -49,13 +63,14 @@ Implements all the same rules as git-check-ref-format[1]. - [1] http://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html + [1] + http://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html :param refname: The refname to check :return: True if refname is valid, False otherwise """ - # These could be combined into one big expression, but are listed separately - # to parallel [1]. + # These could be combined into one big expression, but are listed + # separately to parallel [1]. if b'/.' in refname or refname.startswith(b'.'): return False if b'/' not in refname: @@ -101,9 +116,9 @@ """Return the cached peeled value of a ref, if available. :param name: Name of the ref to peel - :return: The peeled value of the ref. If the ref is known not point to a - tag, this will be the SHA the ref refers to. If the ref may point to - a tag, but no cached information is available, None is returned. + :return: The peeled value of the ref. If the ref is known not point to + a tag, this will be the SHA the ref refers to. If the ref may point + to a tag, but no cached information is available, None is returned. """ return None @@ -196,23 +211,35 @@ """ raise NotImplementedError(self.read_loose_ref) - def _follow(self, name): + def follow(self, name): """Follow a reference name. - :return: a tuple of (refname, sha), where refname is the name of the - last reference in the symbolic reference chain + :return: a tuple of (refnames, sha), wheres refnames are the names of + references in the chain """ contents = SYMREF + name depth = 0 + refnames = [] while contents.startswith(SYMREF): refname = contents[len(SYMREF):] + refnames.append(refname) contents = self.read_ref(refname) if not contents: break depth += 1 if depth > 5: raise KeyError(name) - return refname, contents + return refnames, contents + + def _follow(self, name): + import warnings + warnings.warn( + "RefsContainer._follow is deprecated. Use RefsContainer.follow " + "instead.", DeprecationWarning) + refnames, contents = self.follow(name) + if not refnames: + return (None, contents) + return (refnames[-1], contents) def __contains__(self, refname): if self.read_ref(refname): @@ -224,7 +251,7 @@ This method follows all symbolic references. """ - _, sha = self._follow(name) + _, sha = self.follow(name) if sha is None: raise KeyError(name) return sha @@ -270,8 +297,8 @@ operation. :param name: The refname to delete. - :param old_ref: The old sha the refname must refer to, or None to delete - unconditionally. + :param old_ref: The old sha the refname must refer to, or None to + delete unconditionally. :return: True if the delete was successful, False otherwise. """ raise NotImplementedError(self.remove_if_equals) @@ -290,6 +317,21 @@ """ self.remove_if_equals(name, None) + def get_symrefs(self): + """Get a dict with all symrefs in this container. + + :return: Dictionary mapping source ref to target ref + """ + ret = {} + for src in self.allkeys(): + try: + dst = parse_symref_value(self.read_ref(src)) + except ValueError: + pass + else: + ret[src] = dst + return ret + class DictRefsContainer(RefsContainer): """RefsContainer backed by a simple dict. @@ -315,11 +357,12 @@ self._refs[name] = SYMREF + other def set_if_equals(self, name, old_ref, new_ref): - if old_ref is not None and self._refs.get(name, None) != old_ref: + if old_ref is not None and self._refs.get(name, ZERO_SHA) != old_ref: return False - realname, _ = self._follow(name) - self._check_refname(realname) - self._refs[realname] = new_ref + realnames, _ = self.follow(name) + for realname in realnames: + self._check_refname(realname) + self._refs[realname] = new_ref return True def add_if_new(self, name, ref): @@ -329,9 +372,12 @@ return True def remove_if_equals(self, name, old_ref): - if old_ref is not None and self._refs.get(name, None) != old_ref: + if old_ref is not None and self._refs.get(name, ZERO_SHA) != old_ref: return False - del self._refs[name] + try: + del self._refs[name] + except KeyError: + pass return True def get_peeled(self, name): @@ -356,7 +402,7 @@ self._peeled = {} for l in f.readlines(): sha, name = l.rstrip(b'\n').split(b'\t') - if name.endswith(b'^{}'): + if name.endswith(ANNOTATED_TAG_SUFFIX): name = name[:-3] if not check_ref_format(name): raise ValueError("invalid ref name %r" % name) @@ -385,8 +431,9 @@ class DiskRefsContainer(RefsContainer): """Refs container that reads refs from disk.""" - def __init__(self, path): + def __init__(self, path, worktree_path=None): self.path = path + self.worktree_path = worktree_path or path self._packed_refs = None self._peeled_refs = None @@ -418,7 +465,9 @@ for root, dirs, files in os.walk(self.refpath(b'refs')): dir = root[len(path):].strip(os.path.sep).replace(os.path.sep, "/") for filename in files: - refname = ("%s/%s" % (dir, filename)).encode(sys.getfilesystemencoding()) + refname = ( + "%s/%s" % (dir, filename)).encode( + sys.getfilesystemencoding()) if check_ref_format(refname): allkeys.add(refname) allkeys.update(self.get_packed_refs()) @@ -428,11 +477,17 @@ """Return the disk path of a ref. """ - if getattr(self.path, "encode", None) and getattr(name, "decode", None): + if (getattr(self.path, "encode", None) and + getattr(name, "decode", None)): name = name.decode(sys.getfilesystemencoding()) if os.path.sep != "/": name = name.replace("/", os.path.sep) - return os.path.join(self.path, name) + # TODO: as the 'HEAD' reference is working tree specific, it + # should actually not be a part of RefsContainer + if name == 'HEAD': + return os.path.join(self.worktree_path, name) + else: + return os.path.join(self.path, name) def get_packed_refs(self): """Get contents of the packed-refs file. @@ -473,9 +528,9 @@ """Return the cached peeled value of a ref, if available. :param name: Name of the ref to peel - :return: The peeled value of the ref. If the ref is known not point to a - tag, this will be the SHA the ref refers to. If the ref may point to - a tag, but no cached information is available, None is returned. + :return: The peeled value of the ref. If the ref is known not point to + a tag, this will be the SHA the ref refers to. If the ref may point + to a tag, but no cached information is available, None is returned. """ self.get_packed_refs() if self._peeled_refs is None or name not in self._packed_refs: @@ -509,7 +564,7 @@ # Read only the first 40 bytes return header + f.read(40 - len(SYMREF)) except IOError as e: - if e.errno == errno.ENOENT: + if e.errno in (errno.ENOENT, errno.EISDIR): return None raise @@ -567,8 +622,9 @@ """ self._check_refname(name) try: - realname, _ = self._follow(name) - except KeyError: + realnames, _ = self.follow(name) + realname = realnames[-1] + except (KeyError, IndexError): realname = name filename = self.refpath(realname) ensure_dir_exists(os.path.dirname(filename)) @@ -578,7 +634,8 @@ # read again while holding the lock orig_ref = self.read_loose_ref(realname) if orig_ref is None: - orig_ref = self.get_packed_refs().get(realname, None) + orig_ref = self.get_packed_refs().get( + realname, ZERO_SHA) if orig_ref != old_ref: f.abort() return False @@ -603,10 +660,11 @@ :return: True if the add was successful, False otherwise. """ try: - realname, contents = self._follow(name) + realnames, contents = self.follow(name) if contents is not None: return False - except KeyError: + realname = realnames[-1] + except (KeyError, IndexError): realname = name self._check_refname(realname) filename = self.refpath(realname) @@ -629,8 +687,8 @@ perform an atomic compare-and-delete operation. :param name: The refname to delete. - :param old_ref: The old sha the refname must refer to, or None to delete - unconditionally. + :param old_ref: The old sha the refname must refer to, or None to + delete unconditionally. :return: True if the delete was successful, False otherwise. """ self._check_refname(name) @@ -641,7 +699,7 @@ if old_ref is not None: orig_ref = self.read_loose_ref(name) if orig_ref is None: - orig_ref = self.get_packed_refs().get(name, None) + orig_ref = self.get_packed_refs().get(name, ZERO_SHA) if orig_ref != old_ref: return False # may only be packed @@ -659,7 +717,7 @@ def _split_ref_line(line): """Split a single ref line into a tuple of SHA1 and name.""" - fields = line.rstrip(b'\n').split(b' ') + fields = line.rstrip(b'\n\r').split(b' ') if len(fields) != 2: raise PackedRefsException("invalid ref line %r" % line) sha, name = fields @@ -737,7 +795,7 @@ def read_info_refs(f): ret = {} for l in f.readlines(): - (sha, name) = l.rstrip("\r\n").split("\t", 1) + (sha, name) = l.rstrip(b"\r\n").split(b"\t", 1) ret[name] = sha return ret @@ -756,7 +814,8 @@ peeled = store.peel_sha(sha) yield o.id + b'\t' + name + b'\n' if o.id != peeled.id: - yield peeled.id + b'\t' + name + b'^{}\n' + yield peeled.id + b'\t' + name + ANNOTATED_TAG_SUFFIX + b'\n' -is_local_branch = lambda x: x.startswith(b'refs/heads/') +def is_local_branch(x): + return x.startswith(b'refs/heads/') diff -Nru dulwich-0.12.0/dulwich/repo.py dulwich-0.18.5/dulwich/repo.py --- dulwich-0.12.0/dulwich/repo.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/repo.py 2017-10-18 22:39:29.000000000 +0000 @@ -2,21 +2,22 @@ # Copyright (C) 2007 James Westby # Copyright (C) 2008-2013 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Repository access. @@ -31,6 +32,7 @@ import errno import os import sys +import stat from dulwich.errors import ( NoIndexPresent, @@ -66,7 +68,7 @@ CommitMsgShellHook, ) -from dulwich.refs import ( +from dulwich.refs import ( # noqa: F401 check_ref_format, RefsContainer, DictRefsContainer, @@ -88,6 +90,9 @@ REFSDIR_TAGS = 'tags' REFSDIR_HEADS = 'heads' INDEX_FILENAME = "index" +COMMONDIR = 'commondir' +GITDIR = 'gitdir' +WORKTREES = 'worktrees' BASE_DIRECTORIES = [ ["branches"], @@ -98,6 +103,8 @@ ["info"] ] +DEFAULT_REF = b'refs/heads/master' + def parse_graftpoints(graftpoints): """Convert a list of graftpoints into a dict @@ -174,6 +181,13 @@ self._graftpoints = {} self.hooks = {} + def _determine_file_mode(self): + """Probe the file-system to determine whether permissions can be trusted. + + :return: True if permissions can be trusted, False otherwise. + """ + raise NotImplementedError(self._determine_file_mode) + def _init_files(self, bare): """Initialize a default set of named files.""" from dulwich.config import ConfigFile @@ -181,7 +195,11 @@ f = BytesIO() cf = ConfigFile() cf.set(b"core", b"repositoryformatversion", b"0") - cf.set(b"core", b"filemode", b"true") + if self._determine_file_mode(): + cf.set(b"core", b"filemode", True) + else: + cf.set(b"core", b"filemode", False) + cf.set(b"core", b"bare", bare) cf.set(b"core", b"logallrefupdates", True) cf.write_to_file(f) @@ -243,8 +261,8 @@ that a revision is present. :param progress: Simple progress function that will be called with updated progress strings. - :param get_tagged: Function that returns a dict of pointed-to sha -> tag - sha for including tags. + :param get_tagged: Function that returns a dict of pointed-to sha -> + tag sha for including tags. :return: iterator over objects, with __len__ implemented """ wants = determine_wants(self.get_refs()) @@ -272,8 +290,9 @@ # Deal with shallow requests separately because the haves do # not reflect what objects are missing if shallows or unshallows: - haves = [] # TODO: filter the haves commits from iter_shas. - # the specific commits aren't missing. + # TODO: filter the haves commits from iter_shas. the specific + # commits aren't missing. + haves = [] def get_parents(commit): if commit.id in shallows: @@ -394,8 +413,8 @@ :param ref: The refname to peel. :return: The fully-peeled SHA1 of a tag object, after peeling all - intermediate tags; if the original ref does not point to a tag, this - will equal the original SHA1. + intermediate tags; if the original ref does not point to a tag, + this will equal the original SHA1. """ cached = self.refs.get_peeled(ref) if cached is not None: @@ -409,8 +428,8 @@ ancestors. Defaults to [HEAD] :param exclude: Iterable of SHAs of commits to exclude along with their ancestors, overriding includes. - :param order: ORDER_* constant specifying the order of results. Anything - other than ORDER_DATE may result in O(n) memory usage. + :param order: ORDER_* constant specifying the order of results. + Anything other than ORDER_DATE may result in O(n) memory usage. :param reverse: If True, reverse the order of output, requiring O(n) memory. :param max_entries: The maximum number of entries to yield, or None for @@ -433,7 +452,8 @@ if isinstance(include, str): include = [include] - kwargs['get_parents'] = lambda commit: self.get_parents(commit.id, commit) + kwargs['get_parents'] = lambda commit: self.get_parents( + commit.id, commit) return Walker(self.object_store, include, *args, **kwargs) @@ -446,7 +466,7 @@ """ if not isinstance(name, bytes): raise TypeError("'name' must be bytestring, not %.80s" % - type(name).__name__) + type(name).__name__) if len(name) in (20, 40): try: return self.object_store[name] @@ -533,7 +553,8 @@ :param author: Author fullname (defaults to committer) :param commit_timestamp: Commit timestamp (defaults to now) :param commit_timezone: Commit timestamp timezone (defaults to GMT) - :param author_timestamp: Author timestamp (defaults to commit timestamp) + :param author_timestamp: Author timestamp (defaults to commit + timestamp) :param author_timezone: Author timestamp timezone (defaults to commit timestamp timezone) :param tree: SHA1 of the tree root to use (if not specified the @@ -618,8 +639,8 @@ self.object_store.add_object(c) ok = self.refs.add_if_new(ref, c.id) if not ok: - # Fail if the atomic compare-and-swap failed, leaving the commit and - # all its objects as garbage. + # Fail if the atomic compare-and-swap failed, leaving the + # commit and all its objects as garbage. raise CommitError("%s changed during commit" % (ref,)) try: @@ -632,7 +653,6 @@ return c.id - def read_gitfile(f): """Read a ``.git`` file. @@ -675,18 +695,29 @@ raise NotGitRepository( "No git repository was found at %(path)s" % dict(path=root) ) + commondir = self.get_named_file(COMMONDIR) + if commondir is not None: + with commondir: + self._commondir = os.path.join( + self.controldir(), + commondir.read().rstrip(b"\r\n").decode( + sys.getfilesystemencoding())) + else: + self._commondir = self._controldir self.path = root - object_store = DiskObjectStore(os.path.join(self.controldir(), - OBJECTDIR)) - refs = DiskRefsContainer(self.controldir()) + object_store = DiskObjectStore( + os.path.join(self.commondir(), OBJECTDIR)) + refs = DiskRefsContainer(self.commondir(), self._controldir) BaseRepo.__init__(self, object_store, refs) self._graftpoints = {} - graft_file = self.get_named_file(os.path.join("info", "grafts")) + graft_file = self.get_named_file(os.path.join("info", "grafts"), + basedir=self.commondir()) if graft_file: with graft_file: self._graftpoints.update(parse_graftpoints(graft_file)) - graft_file = self.get_named_file("shallow") + graft_file = self.get_named_file("shallow", + basedir=self.commondir()) if graft_file: with graft_file: self._graftpoints.update(parse_graftpoints(graft_file)) @@ -719,6 +750,36 @@ """Return the path of the control directory.""" return self._controldir + def commondir(self): + """Return the path of the common directory. + + For a main working tree, it is identical to controldir(). + + For a linked working tree, it is the control directory of the + main working tree.""" + + return self._commondir + + def _determine_file_mode(self): + """Probe the file-system to determine whether permissions can be trusted. + + :return: True if permissions can be trusted, False otherwise. + """ + fname = os.path.join(self.path, '.probe-permissions') + with open(fname, 'w') as f: + f.write('') + + st1 = os.lstat(fname) + os.chmod(fname, st1.st_mode ^ stat.S_IXUSR) + st2 = os.lstat(fname) + + os.unlink(fname) + + mode_differs = st1.st_mode != st2.st_mode + st2_has_exec = (st2.st_mode & stat.S_IXUSR) != 0 + + return mode_differs and st2_has_exec + def _put_named_file(self, path, contents): """Write a file to the control dir with the given name and contents. @@ -729,7 +790,7 @@ with GitFile(os.path.join(self.controldir(), path), 'wb') as f: f.write(contents) - def get_named_file(self, path): + def get_named_file(self, path, basedir=None): """Get a file from the control dir with a specific name. Although the filename should be interpreted as a filename relative to @@ -737,13 +798,17 @@ pointing to a file in that location. :param path: The path to the file, relative to the control dir. + :param basedir: Optional argument that specifies an alternative to the + control dir. :return: An open file object, or None if the file does not exist. """ # TODO(dborowitz): sanitize filenames, since this is used directly by # the dumb web serving code. + if basedir is None: + basedir = self.controldir() path = path.lstrip(os.path.sep) try: - return open(os.path.join(self.controldir(), path), 'rb') + return open(os.path.join(basedir, path), 'rb') except (IOError, OSError) as e: if e.errno == errno.ENOENT: return None @@ -789,6 +854,10 @@ for fs_path in fs_paths: if not isinstance(fs_path, bytes): fs_path = fs_path.encode(sys.getfilesystemencoding()) + if os.path.isabs(fs_path): + raise ValueError( + "path %r should be relative to " + "repository root, not absolute" % fs_path) tree_path = _fs_to_tree_path(fs_path) full_path = os.path.join(root_path_bytes, fs_path) try: @@ -800,13 +869,19 @@ except KeyError: pass # already removed else: - blob = blob_from_path_and_stat(full_path, st) - self.object_store.add_object(blob) - index[tree_path] = index_entry_from_stat(st, blob.id, 0) + if not stat.S_ISDIR(st.st_mode): + blob = blob_from_path_and_stat(full_path, st) + self.object_store.add_object(blob) + index[tree_path] = index_entry_from_stat(st, blob.id, 0) + else: + try: + del index[tree_path] + except KeyError: + pass index.write() def clone(self, target_path, mkdir=True, bare=False, - origin=b"origin"): + origin=b"origin"): """Clone this repository. :param target_path: Target path @@ -819,23 +894,29 @@ if not bare: target = self.init(target_path, mkdir=mkdir) else: - target = self.init_bare(target_path) + target = self.init_bare(target_path, mkdir=mkdir) self.fetch(target) target.refs.import_refs( b'refs/remotes/' + origin, self.refs.as_dict(b'refs/heads')) target.refs.import_refs( b'refs/tags', self.refs.as_dict(b'refs/tags')) try: - target.refs.add_if_new( - b'refs/heads/master', - self.refs[b'refs/heads/master']) + target.refs.add_if_new(DEFAULT_REF, self.refs[DEFAULT_REF]) except KeyError: pass + target_config = target.get_config() + encoded_path = self.path + if not isinstance(encoded_path, bytes): + encoded_path = encoded_path.encode(sys.getfilesystemencoding()) + target_config.set((b'remote', b'origin'), b'url', encoded_path) + target_config.set((b'remote', b'origin'), b'fetch', + b'+refs/heads/*:refs/remotes/origin/*') + target_config.write_to_path() # Update target head - head, head_sha = self.refs._follow(b'HEAD') - if head is not None and head_sha is not None: - target.refs.set_symbolic_ref(b'HEAD', head) + head_chain, head_sha = self.refs.follow(b'HEAD') + if head_chain and head_sha is not None: + target.refs.set_symbolic_ref(b'HEAD', head_chain[-1]) target[b'HEAD'] = head_sha if not bare: @@ -857,14 +938,16 @@ if tree is None: tree = self[b'HEAD'].tree config = self.get_config() - honor_filemode = config.get_boolean('core', 'filemode', os.name != "nt") + honor_filemode = config.get_boolean( + 'core', 'filemode', os.name != "nt") if config.get_boolean('core', 'core.protectNTFS', os.name == "nt"): validate_path_element = validate_path_element_ntfs else: validate_path_element = validate_path_element_default - return build_index_from_tree(self.path, self.index_path(), - self.object_store, tree, honor_filemode=honor_filemode, - validate_path_element=validate_path_element) + return build_index_from_tree( + self.path, self.index_path(), self.object_store, tree, + honor_filemode=honor_filemode, + validate_path_element=validate_path_element) def get_config(self): """Retrieve the config object. @@ -913,7 +996,7 @@ os.mkdir(os.path.join(path, *d)) DiskObjectStore.init(os.path.join(path, OBJECTDIR)) ret = cls(path) - ret.refs.set_symbolic_ref(b'HEAD', b"refs/heads/master") + ret.refs.set_symbolic_ref(b'HEAD', DEFAULT_REF) ret._init_files(bare) return ret @@ -933,14 +1016,58 @@ return cls(path) @classmethod - def init_bare(cls, path): + def _init_new_working_directory(cls, path, main_repo, identifier=None, + mkdir=False): + """Create a new working directory linked to a repository. + + :param path: Path in which to create the working tree. + :param main_repo: Main repository to reference + :param identifier: Worktree identifier + :param mkdir: Whether to create the directory + :return: `Repo` instance + """ + if mkdir: + os.mkdir(path) + if identifier is None: + identifier = os.path.basename(path) + main_worktreesdir = os.path.join(main_repo.controldir(), WORKTREES) + worktree_controldir = os.path.join(main_worktreesdir, identifier) + gitdirfile = os.path.join(path, CONTROLDIR) + with open(gitdirfile, 'wb') as f: + f.write(b'gitdir: ' + + worktree_controldir.encode(sys.getfilesystemencoding()) + + b'\n') + try: + os.mkdir(main_worktreesdir) + except OSError as e: + if e.errno != errno.EEXIST: + raise + try: + os.mkdir(worktree_controldir) + except OSError as e: + if e.errno != errno.EEXIST: + raise + with open(os.path.join(worktree_controldir, GITDIR), 'wb') as f: + f.write(gitdirfile.encode(sys.getfilesystemencoding()) + b'\n') + with open(os.path.join(worktree_controldir, COMMONDIR), 'wb') as f: + f.write(b'../..\n') + with open(os.path.join(worktree_controldir, 'HEAD'), 'wb') as f: + f.write(main_repo.head() + b'\n') + r = cls(path) + r.reset_index() + return r + + @classmethod + def init_bare(cls, path, mkdir=False): """Create a new bare repository. - ``path`` should already exist and be an emty directory. + ``path`` should already exist and be an empty directory. :param path: Path to create bare repository in :return: a `Repo` instance """ + if mkdir: + os.mkdir(path) return cls._init_maybe_bare(path, True) create = init_bare @@ -949,6 +1076,12 @@ """Close any files opened by this repository.""" self.object_store.close() + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + class MemoryRepo(BaseRepo): """Repo that stores refs, objects, and named files in memory. @@ -963,6 +1096,20 @@ self._named_files = {} self.bare = True self._config = ConfigFile() + self._description = None + + def set_description(self, description): + self._description = description + + def get_description(self): + return self._description + + def _determine_file_mode(self): + """Probe the file-system to determine whether permissions can be trusted. + + :return: True if permissions can be trusted, False otherwise. + """ + return sys.platform != 'win32' def _put_named_file(self, path, contents): """Write a file to the control dir with the given name and contents. @@ -1001,13 +1148,6 @@ """ return self._config - def get_description(self): - """Retrieve the repository description. - - This defaults to None, for no description. - """ - return None - @classmethod def init_bare(cls, objects, refs): """Create a new bare repository in memory. diff -Nru dulwich-0.12.0/dulwich/server.py dulwich-0.18.5/dulwich/server.py --- dulwich-0.12.0/dulwich/server.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/server.py 2017-10-07 15:45:18.000000000 +0000 @@ -2,20 +2,22 @@ # Copyright (C) 2008 John Carr # Coprygith (C) 2011-2012 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# or (at your option) any later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Git smart network protocol server implementation. @@ -37,6 +39,7 @@ * report-status * delete-refs * shallow + * symref """ import collections @@ -66,9 +69,10 @@ from dulwich.pack import ( write_pack_objects, ) -from dulwich.protocol import ( +from dulwich.protocol import ( # noqa: F401 BufferedPktLineWriter, capability_agent, + CAPABILITIES_REF, CAPABILITY_DELETE_REFS, CAPABILITY_INCLUDE_TAG, CAPABILITY_MULTI_ACK_DETAILED, @@ -101,8 +105,10 @@ ack_type, extract_capabilities, extract_want_line_capabilities, + symref_capabilities, ) from dulwich.refs import ( + ANNOTATED_TAG_SUFFIX, write_info_refs, ) from dulwich.repo import ( @@ -161,8 +167,8 @@ Yield the objects required for a list of commits. :param progress: is a callback to send progress messages to the client - :param get_tagged: Function that returns a dict of pointed-to sha -> tag - sha for including tags. + :param get_tagged: Function that returns a dict of pointed-to sha -> + tag sha for including tags. """ raise NotImplementedError @@ -184,11 +190,12 @@ class FileSystemBackend(Backend): - """Simple backend that looks up Git repositories in the local file system.""" + """Simple backend looking up Git repositories in the local file system.""" def __init__(self, root=os.sep): super(FileSystemBackend, self).__init__() - self.root = (os.path.abspath(root) + os.sep).replace(os.sep * 2, os.sep) + self.root = (os.path.abspath(root) + os.sep).replace( + os.sep * 2, os.sep) def open_repository(self, path): logger.debug('opening repository at %s', path) @@ -196,7 +203,9 @@ normcase_abspath = os.path.normcase(abspath) normcase_root = os.path.normcase(self.root) if not normcase_abspath.startswith(normcase_root): - raise NotGitRepository("Path %r not inside root %r" % (path, self.root)) + raise NotGitRepository( + "Path %r not inside root %r" % + (path, self.root)) return Repo(abspath) @@ -222,8 +231,9 @@ self._done_received = False @classmethod - def capability_line(cls): - return b"".join([b" " + c for c in cls.capabilities()]) + def capability_line(cls, capabilities): + logger.info('Sending capabilities: %s', capabilities) + return b"".join([b" " + c for c in capabilities]) @classmethod def capabilities(cls): @@ -231,9 +241,9 @@ @classmethod def innocuous_capabilities(cls): - return (CAPABILITY_INCLUDE_TAG, CAPABILITY_THIN_PACK, + return [CAPABILITY_INCLUDE_TAG, CAPABILITY_THIN_PACK, CAPABILITY_NO_PROGRESS, CAPABILITY_OFS_DELTA, - capability_agent()) + capability_agent()] @classmethod def required_capabilities(cls): @@ -264,14 +274,13 @@ self._done_received = True - class UploadPackHandler(PackHandler): - """Protocol handler for uploading a pack to the server.""" + """Protocol handler for uploading a pack to the client.""" def __init__(self, backend, args, proto, http_req=None, advertise_refs=False): - super(UploadPackHandler, self).__init__(backend, proto, - http_req=http_req) + super(UploadPackHandler, self).__init__( + backend, proto, http_req=http_req) self.repo = backend.open_repository(args[0]) self._graph_walker = None self.advertise_refs = advertise_refs @@ -282,27 +291,29 @@ @classmethod def capabilities(cls): - return (CAPABILITY_MULTI_ACK_DETAILED, CAPABILITY_MULTI_ACK, + return [CAPABILITY_MULTI_ACK_DETAILED, CAPABILITY_MULTI_ACK, CAPABILITY_SIDE_BAND_64K, CAPABILITY_THIN_PACK, CAPABILITY_OFS_DELTA, CAPABILITY_NO_PROGRESS, - CAPABILITY_INCLUDE_TAG, CAPABILITY_SHALLOW, CAPABILITY_NO_DONE) + CAPABILITY_INCLUDE_TAG, CAPABILITY_SHALLOW, CAPABILITY_NO_DONE] @classmethod def required_capabilities(cls): - return (CAPABILITY_SIDE_BAND_64K, CAPABILITY_THIN_PACK, CAPABILITY_OFS_DELTA) + return (CAPABILITY_SIDE_BAND_64K, CAPABILITY_THIN_PACK, + CAPABILITY_OFS_DELTA) def progress(self, message): - if self.has_capability(CAPABILITY_NO_PROGRESS) or self._processing_have_lines: + if (self.has_capability(CAPABILITY_NO_PROGRESS) or + self._processing_have_lines): return self.proto.write_sideband(SIDE_BAND_CHANNEL_PROGRESS, message) def get_tagged(self, refs=None, repo=None): """Get a dict of peeled values of tags to their original tag shas. - :param refs: dict of refname -> sha of possible tags; defaults to all of - the backend's refs. - :param repo: optional Repo instance for getting peeled refs; defaults to - the backend's repo, if available + :param refs: dict of refname -> sha of possible tags; defaults to all + of the backend's refs. + :param repo: optional Repo instance for getting peeled refs; defaults + to the backend's repo, if available :return: dict of peeled_sha -> tag_sha, where tag_sha is the sha of a tag whose peeled value is peeled_sha. """ @@ -326,10 +337,12 @@ return tagged def handle(self): - write = lambda x: self.proto.write_sideband(SIDE_BAND_CHANNEL_DATA, x) + def write(x): + return self.proto.write_sideband(SIDE_BAND_CHANNEL_DATA, x) - graph_walker = ProtocolGraphWalker(self, self.repo.object_store, - self.repo.get_peeled) + graph_walker = _ProtocolGraphWalker( + self, self.repo.object_store, self.repo.get_peeled, + self.repo.refs.get_symrefs) objects_iter = self.repo.fetch_objects( graph_walker.determine_wants, graph_walker, self.progress, get_tagged=self.get_tagged) @@ -353,11 +366,14 @@ self._processing_have_lines = False if not graph_walker.handle_done( - not self.has_capability(CAPABILITY_NO_DONE), self._done_received): + not self.has_capability(CAPABILITY_NO_DONE), + self._done_received): return self.progress(b"dul-daemon says what\n") - self.progress(("counting objects: %d, done.\n" % len(objects_iter)).encode('ascii')) + self.progress( + ("counting objects: %d, done.\n" % len(objects_iter)).encode( + 'ascii')) write_pack_objects(ProtocolFile(None, write), objects_iter) self.progress(b"how was that, then?\n") # we are done @@ -413,6 +429,7 @@ these sets may overlap if a commit is reachable along multiple paths. """ parents = {} + def get_parents(sha): result = parents.get(sha, None) if not result: @@ -443,6 +460,7 @@ def _want_satisfied(store, haves, want, earliest): o = store[want] pending = collections.deque([o]) + known = set([want]) while pending: commit = pending.popleft() if commit.id in haves: @@ -451,6 +469,9 @@ # non-commit wants are assumed to be satisfied continue for parent in commit.parents: + if parent in known: + continue + known.add(parent) parent_obj = store[parent] # TODO: handle parents with later commit times than children if parent_obj.commit_time >= earliest: @@ -472,7 +493,6 @@ earliest = min([store[h].commit_time for h in haves]) else: earliest = 0 - unsatisfied_wants = set() for want in wants: if not _want_satisfied(store, haves, want, earliest): return False @@ -480,7 +500,7 @@ return True -class ProtocolGraphWalker(object): +class _ProtocolGraphWalker(object): """A graph walker that knows the git protocol. As a graph walker, this class implements ack(), next(), and reset(). It @@ -490,13 +510,14 @@ The work of determining which acks to send is passed on to the implementation instance stored in _impl. The reason for this is that we do not know at object creation time what ack level the protocol requires. A - call to set_ack_level() is required to set up the implementation, before any - calls to next() or ack() are made. + call to set_ack_type() is required to set up the implementation, before + any calls to next() or ack() are made. """ - def __init__(self, handler, object_store, get_peeled): + def __init__(self, handler, object_store, get_peeled, get_symrefs): self.handler = handler self.store = object_store self.get_peeled = get_peeled + self.get_symrefs = get_symrefs self.proto = handler.proto self.http_req = handler.http_req self.advertise_refs = handler.advertise_refs @@ -526,16 +547,21 @@ :param heads: a dict of refname->SHA1 to advertise :return: a list of SHA1s requested by the client """ + symrefs = self.get_symrefs() values = set(heads.values()) if self.advertise_refs or not self.http_req: for i, (ref, sha) in enumerate(sorted(heads.items())): line = sha + b' ' + ref if not i: - line += b'\x00' + self.handler.capability_line() + line += (b'\x00' + + self.handler.capability_line( + self.handler.capabilities() + + symref_capabilities(symrefs.items()))) self.proto.write_pkt_line(line + b'\n') peeled_sha = self.get_peeled(ref) if peeled_sha != sha: - self.proto.write_pkt_line(peeled_sha + b' ' + ref + b'^{}\n') + self.proto.write_pkt_line( + peeled_sha + b' ' + ref + ANNOTATED_TAG_SUFFIX + b'\n') # i'm done.. self.proto.write_pkt_line(None) @@ -568,8 +594,9 @@ if self.http_req and self.proto.eof(): # The client may close the socket at this point, expecting a - # flush-pkt from the server. We might be ready to send a packfile at - # this point, so we need to explicitly short-circuit in this case. + # flush-pkt from the server. We might be ready to send a packfile + # at this point, so we need to explicitly short-circuit in this + # case. return [] return want_revs @@ -611,7 +638,8 @@ def _handle_shallow_request(self, wants): while True: - command, val = self.read_proto_line((COMMAND_DEEPEN, COMMAND_SHALLOW)) + command, val = self.read_proto_line( + (COMMAND_DEEPEN, COMMAND_SHALLOW)) if command == COMMAND_DEEPEN: depth = val break @@ -846,15 +874,16 @@ def __init__(self, backend, args, proto, http_req=None, advertise_refs=False): - super(ReceivePackHandler, self).__init__(backend, proto, - http_req=http_req) + super(ReceivePackHandler, self).__init__( + backend, proto, http_req=http_req) self.repo = backend.open_repository(args[0]) self.advertise_refs = advertise_refs @classmethod def capabilities(cls): - return (CAPABILITY_REPORT_STATUS, CAPABILITY_DELETE_REFS, CAPABILITY_QUIET, - CAPABILITY_OFS_DELTA, CAPABILITY_SIDE_BAND_64K, CAPABILITY_NO_DONE) + return [CAPABILITY_REPORT_STATUS, CAPABILITY_DELETE_REFS, + CAPABILITY_QUIET, CAPABILITY_OFS_DELTA, + CAPABILITY_SIDE_BAND_64K, CAPABILITY_NO_DONE] def _apply_pack(self, refs): all_exceptions = (IOError, OSError, ChecksumMismatch, ApplyDeltaError, @@ -868,35 +897,36 @@ will_send_pack = True if will_send_pack: - # TODO: more informative error messages than just the exception string + # TODO: more informative error messages than just the exception + # string try: recv = getattr(self.proto, "recv", None) self.repo.object_store.add_thin_pack(self.proto.read, recv) status.append((b'unpack', b'ok')) except all_exceptions as e: status.append((b'unpack', str(e).replace('\n', ''))) - # The pack may still have been moved in, but it may contain broken - # objects. We trust a later GC to clean it up. + # The pack may still have been moved in, but it may contain + # broken objects. We trust a later GC to clean it up. else: - # The git protocol want to find a status entry related to unpack process - # even if no pack data has been sent. + # The git protocol want to find a status entry related to unpack + # process even if no pack data has been sent. status.append((b'unpack', b'ok')) for oldsha, sha, ref in refs: ref_status = b'ok' try: if sha == ZERO_SHA: - if not CAPABILITY_DELETE_REFS in self.capabilities(): + if CAPABILITY_DELETE_REFS not in self.capabilities(): raise GitProtocolError( 'Attempted to delete refs without delete-refs ' 'capability.') try: - del self.repo.refs[ref] + self.repo.refs.remove_if_equals(ref, oldsha) except all_exceptions: ref_status = b'failed to delete' else: try: - self.repo.refs[ref] = sha + self.repo.refs.set_if_equals(ref, oldsha, sha) except all_exceptions: ref_status = b'failed to write' except KeyError as e: @@ -916,7 +946,9 @@ self.proto.write_pkt_line(None) else: write = self.proto.write_pkt_line - flush = lambda: None + + def flush(): + pass for name, msg in status: if name == b'unpack': @@ -931,17 +963,17 @@ def handle(self): if self.advertise_refs or not self.http_req: refs = sorted(self.repo.get_refs().items()) + symrefs = sorted(self.repo.refs.get_symrefs().items()) - if refs: - self.proto.write_pkt_line( - refs[0][1] + b' ' + refs[0][0] + b'\0' + - self.capability_line() + b'\n') - for i in range(1, len(refs)): - ref = refs[i] - self.proto.write_pkt_line(ref[1] + b' ' + ref[0] + b'\n') - else: - self.proto.write_pkt_line(ZERO_SHA + b" capabilities^{}\0" + - self.capability_line()) + if not refs: + refs = [(CAPABILITIES_REF, ZERO_SHA)] + self.proto.write_pkt_line( + refs[0][1] + b' ' + refs[0][0] + b'\0' + + self.capability_line( + self.capabilities() + symref_capabilities(symrefs)) + b'\n') + for i in range(1, len(refs)): + ref = refs[i] + self.proto.write_pkt_line(ref[1] + b' ' + ref[0] + b'\n') self.proto.write_pkt_line(None) if self.advertise_refs: @@ -985,8 +1017,8 @@ DEFAULT_HANDLERS = { b'git-upload-pack': UploadPackHandler, b'git-receive-pack': ReceivePackHandler, -# b'git-upload-archive': UploadArchiveHandler, - } + # b'git-upload-archive': UploadArchiveHandler, +} class TCPGitRequestHandler(SocketServer.StreamRequestHandler): @@ -1020,7 +1052,8 @@ if handlers is not None: self.handlers.update(handlers) self.backend = backend - logger.info('Listening for TCP connections on %s:%d', listen_addr, port) + logger.info('Listening for TCP connections on %s:%d', + listen_addr, port) SocketServer.TCPServer.__init__(self, (listen_addr, port), self._make_handler) @@ -1050,16 +1083,18 @@ gitdir = args[1] else: gitdir = '.' - from dulwich import porcelain - porcelain.daemon(gitdir, address=options.listen_address, - port=options.port) + # TODO(jelmer): Support git-daemon-export-ok and --export-all. + backend = FileSystemBackend(gitdir) + server = TCPGitServer(backend, options.listen_address, options.port) + server.serve_forever() def serve_command(handler_cls, argv=sys.argv, backend=None, inf=sys.stdin, outf=sys.stdout): """Serve a single command. - This is mostly useful for the implementation of commands used by e.g. git+ssh. + This is mostly useful for the implementation of commands used by e.g. + git+ssh. :param handler_cls: `Handler` class to use for the request :param argv: execv-style command-line arguments. Defaults to sys.argv. @@ -1070,6 +1105,7 @@ """ if backend is None: backend = FileSystemBackend() + def send_fn(data): outf.write(data) outf.flush() @@ -1089,7 +1125,9 @@ def generate_objects_info_packs(repo): """Generate an index for for packs.""" for pack in repo.object_store.packs: - yield b'P ' + pack.data.filename.encode(sys.getfilesystemencoding()) + b'\n' + yield ( + b'P ' + pack.data.filename.encode(sys.getfilesystemencoding()) + + b'\n') def update_server_info(repo): @@ -1098,10 +1136,12 @@ This generates info/refs and objects/info/packs, similar to "git update-server-info". """ - repo._put_named_file(os.path.join('info', 'refs'), + repo._put_named_file( + os.path.join('info', 'refs'), b"".join(generate_info_refs(repo))) - repo._put_named_file(os.path.join('objects', 'info', 'packs'), + repo._put_named_file( + os.path.join('objects', 'info', 'packs'), b"".join(generate_objects_info_packs(repo))) diff -Nru dulwich-0.12.0/dulwich/tests/compat/__init__.py dulwich-0.18.5/dulwich/tests/compat/__init__.py --- dulwich-0.12.0/dulwich/tests/compat/__init__.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/compat/__init__.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,26 +1,28 @@ # __init__.py -- Compatibility tests for dulwich # Copyright (C) 2010 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Compatibility tests for Dulwich.""" import unittest + def test_suite(): names = [ 'client', diff -Nru dulwich-0.12.0/dulwich/tests/compat/server_utils.py dulwich-0.18.5/dulwich/tests/compat/server_utils.py --- dulwich-0.12.0/dulwich/tests/compat/server_utils.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/compat/server_utils.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,21 +1,22 @@ # server_utils.py -- Git server compatibility utilities # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Utilities for testing git server compatibility.""" @@ -27,6 +28,9 @@ from dulwich.repo import Repo from dulwich.objects import hex_to_sha +from dulwich.protocol import ( + CAPABILITY_SIDE_BAND_64K, + ) from dulwich.server import ( ReceivePackHandler, ) @@ -168,8 +172,9 @@ port = self._start_server(self._source_repo) # Fetch at depth 1 - run_git_or_fail(['clone', '--mirror', '--depth=1', '--no-single-branch', - self.url(port), self._stub_repo.path]) + run_git_or_fail( + ['clone', '--mirror', '--depth=1', '--no-single-branch', + self.url(port), self._stub_repo.path]) clone = self._stub_repo = Repo(self._stub_repo.path) expected_shallow = [b'35e0b59e187dd72a0af294aedffc213eaa4d03ff', b'514dc6d3fbfe77361bcaef320c4d21b72bc10be9'] @@ -185,13 +190,14 @@ self.addCleanup(tear_down_repo, self._stub_repo_dw) # shallow clone using stock git, then using dulwich - run_git_or_fail(['clone', '--mirror', '--depth=1', '--no-single-branch', - 'file://' + self._source_repo.path, - self._stub_repo_git.path]) + run_git_or_fail( + ['clone', '--mirror', '--depth=1', '--no-single-branch', + 'file://' + self._source_repo.path, self._stub_repo_git.path]) port = self._start_server(self._source_repo) - run_git_or_fail(['clone', '--mirror', '--depth=1', '--no-single-branch', - self.url(port), self._stub_repo_dw.path]) + run_git_or_fail( + ['clone', '--mirror', '--depth=1', '--no-single-branch', + self.url(port), self._stub_repo_dw.path]) # compare the two clones; they should be equal self.assertReposEqual(Repo(self._stub_repo_git.path), @@ -205,8 +211,9 @@ port = self._start_server(self._source_repo) # Fetch at depth 2 - run_git_or_fail(['clone', '--mirror', '--depth=2', '--no-single-branch', - self.url(port), self._stub_repo.path]) + run_git_or_fail( + ['clone', '--mirror', '--depth=2', '--no-single-branch', + self.url(port), self._stub_repo.path]) clone = self._stub_repo = Repo(self._stub_repo.path) # Fetching at the same depth is a no-op. @@ -226,8 +233,9 @@ port = self._start_server(self._source_repo) # Fetch at depth 2 - run_git_or_fail(['clone', '--mirror', '--depth=2', '--no-single-branch', - self.url(port), self._stub_repo.path]) + run_git_or_fail( + ['clone', '--mirror', '--depth=2', '--no-single-branch', + self.url(port), self._stub_repo.path]) clone = self._stub_repo = Repo(self._stub_repo.path) # Fetching at the same depth is a no-op. @@ -245,11 +253,13 @@ def test_fetch_from_dulwich_issue_88_standard(self): # Basically an integration test to see that the ACK/NAK # generation works on repos with common head. - self._source_repo = self.import_repo('issue88_expect_ack_nak_server.export') - self._client_repo = self.import_repo('issue88_expect_ack_nak_client.export') + self._source_repo = self.import_repo( + 'issue88_expect_ack_nak_server.export') + self._client_repo = self.import_repo( + 'issue88_expect_ack_nak_client.export') port = self._start_server(self._source_repo) - run_git_or_fail(['fetch', self.url(port), 'master',], + run_git_or_fail(['fetch', self.url(port), 'master'], cwd=self._client_repo.path) self.assertObjectStoreEqual( self._source_repo.object_store, @@ -257,13 +267,16 @@ def test_fetch_from_dulwich_issue_88_alternative(self): # likewise, but the case where the two repos have no common parent - self._source_repo = self.import_repo('issue88_expect_ack_nak_other.export') - self._client_repo = self.import_repo('issue88_expect_ack_nak_client.export') + self._source_repo = self.import_repo( + 'issue88_expect_ack_nak_other.export') + self._client_repo = self.import_repo( + 'issue88_expect_ack_nak_client.export') port = self._start_server(self._source_repo) - self.assertRaises(KeyError, self._client_repo.get_object, + self.assertRaises( + KeyError, self._client_repo.get_object, b'02a14da1fc1fc13389bbf32f0af7d8899f2b2323') - run_git_or_fail(['fetch', self.url(port), 'master',], + run_git_or_fail(['fetch', self.url(port), 'master'], cwd=self._client_repo.path) self.assertEqual(b'commit', self._client_repo.get_object( b'02a14da1fc1fc13389bbf32f0af7d8899f2b2323').type_name) @@ -271,11 +284,13 @@ def test_push_to_dulwich_issue_88_standard(self): # Same thing, but we reverse the role of the server/client # and do a push instead. - self._source_repo = self.import_repo('issue88_expect_ack_nak_client.export') - self._client_repo = self.import_repo('issue88_expect_ack_nak_server.export') + self._source_repo = self.import_repo( + 'issue88_expect_ack_nak_client.export') + self._client_repo = self.import_repo( + 'issue88_expect_ack_nak_server.export') port = self._start_server(self._source_repo) - run_git_or_fail(['push', self.url(port), 'master',], + run_git_or_fail(['push', self.url(port), 'master'], cwd=self._client_repo.path) self.assertReposEqual(self._source_repo, self._client_repo) @@ -288,8 +303,8 @@ @classmethod def capabilities(cls): - return tuple(c for c in ReceivePackHandler.capabilities() - if c != b'side-band-64k') + return [c for c in ReceivePackHandler.capabilities() + if c != CAPABILITY_SIDE_BAND_64K] def ignore_error(error): @@ -297,4 +312,3 @@ (e_type, e_value, e_tb) = error return (issubclass(e_type, socket.error) and e_value[0] in (errno.ECONNRESET, errno.EPIPE)) - diff -Nru dulwich-0.12.0/dulwich/tests/compat/test_client.py dulwich-0.18.5/dulwich/tests/compat/test_client.py --- dulwich-0.12.0/dulwich/tests/compat/test_client.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/compat/test_client.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,25 +1,25 @@ # test_client.py -- Compatibilty tests for git client. # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Compatibilty tests between the Dulwich client and the cgit server.""" -from contextlib import closing import copy from io import BytesIO import os @@ -45,9 +45,6 @@ BaseHTTPServer = http.server SimpleHTTPServer = http.server -if sys.platform == 'win32': - import ctypes - from dulwich import ( client, errors, @@ -58,28 +55,29 @@ repo, ) from dulwich.tests import ( - get_safe_env, SkipTest, expectedFailure, ) -from dulwich.tests.utils import ( - skipIfPY3, - ) from dulwich.tests.compat.utils import ( CompatTestCase, check_for_daemon, import_repo_to_dir, + rmtree_ro, run_git_or_fail, _DEFAULT_GIT, - rmtree_ro, ) +if sys.platform == 'win32': + import ctypes + + class DulwichClientTestBase(object): """Tests for client/server compatibility.""" def setUp(self): - self.gitroot = os.path.dirname(import_repo_to_dir('server_new.export').rstrip(os.sep)) + self.gitroot = os.path.dirname( + import_repo_to_dir('server_new.export').rstrip(os.sep)) self.dest = os.path.join(self.gitroot, 'dest') file.ensure_dir_exists(self.dest) run_git_or_fail(['init', '--quiet', '--bare'], cwd=self.dest) @@ -90,8 +88,8 @@ def assertDestEqualsSrc(self): repo_dir = os.path.join(self.gitroot, 'server_new.export') dest_repo_dir = os.path.join(self.gitroot, 'dest') - with closing(repo.Repo(repo_dir)) as src: - with closing(repo.Repo(dest_repo_dir)) as dest: + with repo.Repo(repo_dir) as src: + with repo.Repo(dest_repo_dir) as dest: self.assertReposEqual(src, dest) def _client(self): @@ -103,10 +101,10 @@ def _do_send_pack(self): c = self._client() srcpath = os.path.join(self.gitroot, 'server_new.export') - with closing(repo.Repo(srcpath)) as src: + with repo.Repo(srcpath) as src: sendrefs = dict(src.get_refs()) del sendrefs[b'HEAD'] - c.send_pack(self._build_path(b'/dest'), lambda _: sendrefs, + c.send_pack(self._build_path('/dest'), lambda _: sendrefs, src.object_store.generate_pack_contents) def test_send_pack(self): @@ -123,10 +121,10 @@ c = self._client() c._send_capabilities.remove(b'report-status') srcpath = os.path.join(self.gitroot, 'server_new.export') - with closing(repo.Repo(srcpath)) as src: + with repo.Repo(srcpath) as src: sendrefs = dict(src.get_refs()) del sendrefs[b'HEAD'] - c.send_pack(self._build_path(b'/dest'), lambda _: sendrefs, + c.send_pack(self._build_path('/dest'), lambda _: sendrefs, src.object_store.generate_pack_contents) self.assertDestEqualsSrc() @@ -160,11 +158,12 @@ dest, dummy_commit = self.disable_ff_and_make_dummy_commit() dest.refs[b'refs/heads/master'] = dummy_commit repo_dir = os.path.join(self.gitroot, 'server_new.export') - with closing(repo.Repo(repo_dir)) as src: + with repo.Repo(repo_dir) as src: sendrefs, gen_pack = self.compute_send(src) c = self._client() try: - c.send_pack(self._build_path(b'/dest'), lambda _: sendrefs, gen_pack) + c.send_pack(self._build_path('/dest'), + lambda _: sendrefs, gen_pack) except errors.UpdateRefsError as e: self.assertEqual('refs/heads/master failed to update', e.args[0]) @@ -178,17 +177,19 @@ branch, master = b'refs/heads/branch', b'refs/heads/master' dest.refs[branch] = dest.refs[master] = dummy repo_dir = os.path.join(self.gitroot, 'server_new.export') - with closing(repo.Repo(repo_dir)) as src: + with repo.Repo(repo_dir) as src: sendrefs, gen_pack = self.compute_send(src) c = self._client() try: - c.send_pack(self._build_path(b'/dest'), lambda _: sendrefs, gen_pack) + c.send_pack(self._build_path('/dest'), lambda _: sendrefs, + gen_pack) except errors.UpdateRefsError as e: - self.assertIn(str(e), - ['{0}, {1} failed to update'.format( - branch.decode('ascii'), master.decode('ascii')), - '{1}, {0} failed to update'.format( - branch.decode('ascii'), master.decode('ascii'))]) + self.assertIn( + str(e), + ['{0}, {1} failed to update'.format( + branch.decode('ascii'), master.decode('ascii')), + '{1}, {0} failed to update'.format( + branch.decode('ascii'), master.decode('ascii'))]) self.assertEqual({branch: b'non-fast-forward', master: b'non-fast-forward'}, e.ref_status) @@ -196,16 +197,16 @@ def test_archive(self): c = self._client() f = BytesIO() - c.archive(self._build_path(b'/server_new.export'), b'HEAD', f.write) + c.archive(self._build_path('/server_new.export'), b'HEAD', f.write) f.seek(0) tf = tarfile.open(fileobj=f) self.assertEqual(['baz', 'foo'], tf.getnames()) def test_fetch_pack(self): c = self._client() - with closing(repo.Repo(os.path.join(self.gitroot, 'dest'))) as dest: - refs = c.fetch(self._build_path(b'/server_new.export'), dest) - for r in refs.items(): + with repo.Repo(os.path.join(self.gitroot, 'dest')) as dest: + result = c.fetch(self._build_path('/server_new.export'), dest) + for r in result.refs.items(): dest.refs.set_if_equals(r[0], None, r[1]) self.assertDestEqualsSrc() @@ -215,18 +216,18 @@ dest.refs[b'refs/heads/master'] = dummy c = self._client() repo_dir = os.path.join(self.gitroot, 'server_new.export') - with closing(repo.Repo(repo_dir)) as dest: - refs = c.fetch(self._build_path(b'/dest'), dest) - for r in refs.items(): + with repo.Repo(repo_dir) as dest: + result = c.fetch(self._build_path('/dest'), dest) + for r in result.refs.items(): dest.refs.set_if_equals(r[0], None, r[1]) self.assertDestEqualsSrc() def test_fetch_pack_no_side_band_64k(self): c = self._client() c._fetch_capabilities.remove(b'side-band-64k') - with closing(repo.Repo(os.path.join(self.gitroot, 'dest'))) as dest: - refs = c.fetch(self._build_path(b'/server_new.export'), dest) - for r in refs.items(): + with repo.Repo(os.path.join(self.gitroot, 'dest')) as dest: + result = c.fetch(self._build_path('/server_new.export'), dest) + for r in result.refs.items(): dest.refs.set_if_equals(r[0], None, r[1]) self.assertDestEqualsSrc() @@ -234,32 +235,36 @@ # zero sha1s are already present on the client, and should # be ignored c = self._client() - with closing(repo.Repo(os.path.join(self.gitroot, 'dest'))) as dest: - refs = c.fetch(self._build_path(b'/server_new.export'), dest, + with repo.Repo(os.path.join(self.gitroot, 'dest')) as dest: + result = c.fetch( + self._build_path('/server_new.export'), dest, lambda refs: [protocol.ZERO_SHA]) - for r in refs.items(): + for r in result.refs.items(): dest.refs.set_if_equals(r[0], None, r[1]) def test_send_remove_branch(self): - with closing(repo.Repo(os.path.join(self.gitroot, 'dest'))) as dest: + with repo.Repo(os.path.join(self.gitroot, 'dest')) as dest: dummy_commit = self.make_dummy_commit(dest) dest.refs[b'refs/heads/master'] = dummy_commit dest.refs[b'refs/heads/abranch'] = dummy_commit sendrefs = dict(dest.refs) sendrefs[b'refs/heads/abranch'] = b"00" * 20 del sendrefs[b'HEAD'] - gen_pack = lambda have, want: [] + + def gen_pack(have, want): + return [] c = self._client() self.assertEqual(dest.refs[b"refs/heads/abranch"], dummy_commit) - c.send_pack(self._build_path(b'/dest'), lambda _: sendrefs, gen_pack) + c.send_pack( + self._build_path('/dest'), lambda _: sendrefs, gen_pack) self.assertFalse(b"refs/heads/abranch" in dest.refs) def test_get_refs(self): c = self._client() - refs = c.get_refs(self._build_path(b'/server_new.export')) + refs = c.get_refs(self._build_path('/server_new.export')) repo_dir = os.path.join(self.gitroot, 'server_new.export') - with closing(repo.Repo(repo_dir)) as dest: + with repo.Repo(repo_dir) as dest: self.assertDictEqual(dest.refs.as_dict(), refs) @@ -270,8 +275,7 @@ DulwichClientTestBase.setUp(self) if check_for_daemon(limit=1): raise SkipTest('git-daemon was already running on port %s' % - protocol.TCP_GIT_PORT) - env = get_safe_env() + protocol.TCP_GIT_PORT) fd, self.pidfile = tempfile.mkstemp(prefix='dulwich-test-git-client', suffix=".pid") os.fdopen(fd).close() @@ -282,7 +286,7 @@ '--listen=localhost', '--reuseaddr', self.gitroot] self.process = subprocess.Popen( - args, env=env, cwd=self.gitroot, + args, cwd=self.gitroot, stdout=subprocess.PIPE, stderr=subprocess.PIPE) if not check_for_daemon(): raise SkipTest('git-daemon failed to start') @@ -324,10 +328,10 @@ @staticmethod def run_command(host, command, username=None, port=None): - cmd, path = command.split(b' ') - cmd = cmd.split(b'-', 1) - path = path.replace(b"'", b"") - p = subprocess.Popen(cmd + [path], bufsize=0, env=get_safe_env(), stdin=subprocess.PIPE, + cmd, path = command.split(' ') + cmd = cmd.split('-', 1) + path = path.replace("'", "") + p = subprocess.Popen(cmd + [path], bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) return client.SubprocessWrapper(p) @@ -349,7 +353,7 @@ return client.SSHGitClient('localhost') def _build_path(self, path): - return self.gitroot.encode(sys.getfilesystemencoding()) + path + return self.gitroot + path class DulwichSubprocessClientTest(CompatTestCase, DulwichClientTestBase): @@ -366,7 +370,7 @@ return client.SubprocessGitClient(stderr=subprocess.PIPE) def _build_path(self, path): - return self.gitroot.encode(sys.getfilesystemencoding()) + path + return self.gitroot + path class GitHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): @@ -392,7 +396,8 @@ def run_backend(self): """Call out to git http-backend.""" # Based on CGIHTTPServer.CGIHTTPRequestHandler.run_cgi: - # Copyright (c) 2001-2010 Python Software Foundation; All Rights Reserved + # Copyright (c) 2001-2010 Python Software Foundation; + # All Rights Reserved # Licensed under the Python Software Foundation License. rest = self.path # find an explicit query string, if present. @@ -424,7 +429,8 @@ if authorization: authorization = authorization.split() if len(authorization) == 2: - import base64, binascii + import base64 + import binascii env['AUTH_TYPE'] = authorization[0] if authorization[0].lower() == "basic": try: @@ -436,10 +442,9 @@ if len(authorization) == 2: env['REMOTE_USER'] = authorization[0] # XXX REMOTE_IDENT - if self.headers.typeheader is None: - env['CONTENT_TYPE'] = self.headers.type - else: - env['CONTENT_TYPE'] = self.headers.typeheader + content_type = self.headers.get('content-type') + if content_type: + env['CONTENT_TYPE'] = content_type length = self.headers.get('content-length') if length: env['CONTENT_LENGTH'] = length @@ -456,9 +461,9 @@ ua = self.headers.get('user-agent') if ua: env['HTTP_USER_AGENT'] = ua - co = filter(None, self.headers.getheaders('cookie')) + co = self.headers.get('cookie') if co: - env['HTTP_COOKIE'] = ', '.join(co) + env['HTTP_COOKIE'] = co # XXX Other HTTP_* headers # Since we're setting the env in the parent, provide empty # values to override previously set values @@ -466,7 +471,11 @@ 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'): env.setdefault(k, "") - self.send_response(200, "Script output follows") + self.wfile.write(b"HTTP/1.1 200 Script output follows\r\n") + self.wfile.write( + ("Server: %s\r\n" % self.server.server_name).encode('ascii')) + self.wfile.write( + ("Date: %s\r\n" % self.date_time_string()).encode('ascii')) decoded_query = query.replace('+', ' ') @@ -485,7 +494,8 @@ args = ['http-backend'] if '=' not in decoded_query: args.append(decoded_query) - stdout = run_git_or_fail(args, input=data, env=env, stderr=subprocess.PIPE) + stdout = run_git_or_fail( + args, input=data, env=env, stderr=subprocess.PIPE) self.wfile.write(stdout) @@ -494,7 +504,8 @@ allow_reuse_address = True def __init__(self, server_address, root_path): - BaseHTTPServer.HTTPServer.__init__(self, server_address, GitHTTPRequestHandler) + BaseHTTPServer.HTTPServer.__init__( + self, server_address, GitHTTPRequestHandler) self.root_path = root_path self.server_name = "localhost" @@ -502,7 +513,6 @@ return 'http://%s:%s/' % (self.server_name, self.server_port) -@skipIfPY3 class DulwichHttpClientTest(CompatTestCase, DulwichClientTestBase): min_git_version = (1, 7, 0, 2) diff -Nru dulwich-0.12.0/dulwich/tests/compat/test_pack.py dulwich-0.18.5/dulwich/tests/compat/test_pack.py --- dulwich-0.12.0/dulwich/tests/compat/test_pack.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/compat/test_pack.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,21 +1,22 @@ # test_pack.py -- Compatibility tests for git packs. # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Compatibility tests for git packs.""" @@ -47,6 +48,7 @@ _NON_DELTA_RE = re.compile(b'non delta: (?P\d+) objects') + def _git_verify_pack_object_list(output): pack_shas = set() for line in output.splitlines(): @@ -134,7 +136,6 @@ # two copy operations in git's binary delta format. raise SkipTest('skipping slow, large test') with self.get_pack(pack1_sha) as orig_pack: - orig_blob = orig_pack[a_sha] new_blob = Blob() new_blob.data = 'big blob' + ('x' * 2 ** 25) new_blob_2 = Blob() diff -Nru dulwich-0.12.0/dulwich/tests/compat/test_repository.py dulwich-0.18.5/dulwich/tests/compat/test_repository.py --- dulwich-0.12.0/dulwich/tests/compat/test_repository.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/compat/test_repository.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,21 +1,22 @@ # test_repo.py -- Git repo compatibility tests # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Compatibility tests for dulwich repositories.""" @@ -23,15 +24,18 @@ from io import BytesIO from itertools import chain import os +import tempfile from dulwich.objects import ( hex_to_sha, ) from dulwich.repo import ( check_ref_format, + Repo, ) - from dulwich.tests.compat.utils import ( + require_git_version, + rmtree_ro, run_git_or_fail, CompatTestCase, ) @@ -88,7 +92,8 @@ # TODO(dborowitz): peeled ref tests def _get_loose_shas(self): - output = self._run_git(['rev-list', '--all', '--objects', '--unpacked']) + output = self._run_git( + ['rev-list', '--all', '--objects', '--unpacked']) return self._parse_objects(output) def _get_all_shas(self): @@ -104,8 +109,8 @@ self.assertEqual(expected_shas, actual_shas) def test_loose_objects(self): - # TODO(dborowitz): This is currently not very useful since fast-imported - # repos only contained packed objects. + # TODO(dborowitz): This is currently not very useful since + # fast-imported repos only contained packed objects. expected_shas = self._get_loose_shas() self.assertShasMatch(expected_shas, self._repo.object_store._iter_loose_objects()) @@ -118,3 +123,95 @@ def test_all_objects(self): expected_shas = self._get_all_shas() self.assertShasMatch(expected_shas, iter(self._repo.object_store)) + + +class WorkingTreeTestCase(ObjectStoreTestCase): + """Test for compatibility with git-worktree.""" + + min_git_version = (2, 5, 0) + + def create_new_worktree(self, repo_dir, branch): + """Create a new worktree using git-worktree. + + :param repo_dir: The directory of the main working tree. + :param branch: The branch or commit to checkout in the new worktree. + + :returns: The path to the new working tree. + """ + temp_dir = tempfile.mkdtemp() + run_git_or_fail(['worktree', 'add', temp_dir, branch], + cwd=repo_dir) + self.addCleanup(rmtree_ro, temp_dir) + return temp_dir + + def setUp(self): + super(WorkingTreeTestCase, self).setUp() + self._worktree_path = self.create_new_worktree( + self._repo.path, 'branch') + self._worktree_repo = Repo(self._worktree_path) + self.addCleanup(self._worktree_repo.close) + self._mainworktree_repo = self._repo + self._number_of_working_tree = 2 + self._repo = self._worktree_repo + + def test_refs(self): + super(WorkingTreeTestCase, self).test_refs() + self.assertEqual(self._mainworktree_repo.refs.allkeys(), + self._repo.refs.allkeys()) + + def test_head_equality(self): + self.assertNotEqual(self._repo.refs[b'HEAD'], + self._mainworktree_repo.refs[b'HEAD']) + + def test_bare(self): + self.assertFalse(self._repo.bare) + self.assertTrue(os.path.isfile(os.path.join(self._repo.path, '.git'))) + + def _parse_worktree_list(self, output): + worktrees = [] + for line in BytesIO(output): + fields = line.rstrip(b'\n').split() + worktrees.append(tuple(f.decode() for f in fields)) + return worktrees + + def test_git_worktree_list(self): + # 'git worktree list' was introduced in 2.7.0 + require_git_version((2, 7, 0)) + output = run_git_or_fail(['worktree', 'list'], cwd=self._repo.path) + worktrees = self._parse_worktree_list(output) + self.assertEqual(len(worktrees), self._number_of_working_tree) + self.assertEqual(worktrees[0][1], '(bare)') + self.assertEqual(os.path.normcase(worktrees[0][0]), + os.path.normcase(self._mainworktree_repo.path)) + + output = run_git_or_fail( + ['worktree', 'list'], cwd=self._mainworktree_repo.path) + worktrees = self._parse_worktree_list(output) + self.assertEqual(len(worktrees), self._number_of_working_tree) + self.assertEqual(worktrees[0][1], '(bare)') + self.assertEqual(os.path.normcase(worktrees[0][0]), + os.path.normcase(self._mainworktree_repo.path)) + + +class InitNewWorkingDirectoryTestCase(WorkingTreeTestCase): + """Test compatibility of Repo.init_new_working_directory.""" + + min_git_version = (2, 5, 0) + + def setUp(self): + super(InitNewWorkingDirectoryTestCase, self).setUp() + self._other_worktree = self._repo + worktree_repo_path = tempfile.mkdtemp() + self.addCleanup(rmtree_ro, worktree_repo_path) + self._repo = Repo._init_new_working_directory( + worktree_repo_path, self._mainworktree_repo) + self.addCleanup(self._repo.close) + self._number_of_working_tree = 3 + + def test_head_equality(self): + self.assertEqual(self._repo.refs[b'HEAD'], + self._mainworktree_repo.refs[b'HEAD']) + + def test_bare(self): + self.assertFalse(self._repo.bare) + self.assertTrue(os.path.isfile(os.path.join(self._repo.path, '.git'))) diff -Nru dulwich-0.12.0/dulwich/tests/compat/test_server.py dulwich-0.18.5/dulwich/tests/compat/test_server.py --- dulwich-0.12.0/dulwich/tests/compat/test_server.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/compat/test_server.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,21 +1,22 @@ # test_server.py -- Compatibility tests for git server. # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Compatibility tests between Dulwich and the cgit server. @@ -42,7 +43,9 @@ require_git_version, ) -@skipIf(sys.platform == 'win32', 'Broken on windows, with very long fail time.') + +@skipIf(sys.platform == 'win32', + 'Broken on windows, with very long fail time.') class GitServerTestCase(ServerTests, CompatTestCase): """Tests for client/server compatibility. @@ -72,7 +75,8 @@ return port -@skipIf(sys.platform == 'win32', 'Broken on windows, with very long fail time.') +@skipIf(sys.platform == 'win32', + 'Broken on windows, with very long fail time.') class GitServerSideBand64kTestCase(GitServerTestCase): """Tests for client/server compatibility with side-band-64k support.""" @@ -87,7 +91,6 @@ if os.name == 'nt': require_git_version((1, 9, 3)) - def _handlers(self): return None # default handlers include side-band-64k diff -Nru dulwich-0.12.0/dulwich/tests/compat/test_utils.py dulwich-0.18.5/dulwich/tests/compat/test_utils.py --- dulwich-0.12.0/dulwich/tests/compat/test_utils.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/compat/test_utils.py 2017-07-29 00:14:28.000000000 +0000 @@ -1,21 +1,22 @@ # test_utils.py -- Tests for git compatibility utilities # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License - -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, -# Boston, MA 02110-1301, USA. """Tests for git compatibility utilities.""" diff -Nru dulwich-0.12.0/dulwich/tests/compat/test_web.py dulwich-0.18.5/dulwich/tests/compat/test_web.py --- dulwich-0.12.0/dulwich/tests/compat/test_web.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/compat/test_web.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,21 +1,22 @@ # test_web.py -- Compatibility tests for the git web server. # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Compatibility tests between Dulwich and the cgit HTTP server. @@ -37,9 +38,6 @@ SkipTest, skipIf, ) -from dulwich.tests.utils import ( - skipIfPY3, - ) from dulwich.web import ( make_wsgi_chain, HTTPGitApplication, @@ -56,7 +54,8 @@ ) -@skipIf(sys.platform == 'win32', 'Broken on windows, with very long fail time.') +@skipIf(sys.platform == 'win32', + 'Broken on windows, with very long fail time.') class WebTests(ServerTests): """Base tests for web server tests. @@ -80,8 +79,8 @@ return port -@skipIf(sys.platform == 'win32', 'Broken on windows, with very long fail time.') -@skipIfPY3 +@skipIf(sys.platform == 'win32', + 'Broken on windows, with very long fail time.') class SmartWebTestCase(WebTests, CompatTestCase): """Test cases for smart HTTP server. @@ -91,12 +90,12 @@ min_git_version = (1, 6, 6) def _handlers(self): - return {'git-receive-pack': NoSideBand64kReceivePackHandler} + return {b'git-receive-pack': NoSideBand64kReceivePackHandler} def _check_app(self, app): receive_pack_handler_cls = app.handlers[b'git-receive-pack'] caps = receive_pack_handler_cls.capabilities() - self.assertFalse(b'side-band-64k' in caps) + self.assertNotIn(b'side-band-64k', caps) def _make_app(self, backend): app = make_wsgi_chain(backend, handlers=self._handlers()) @@ -112,16 +111,17 @@ # Patch a handler's capabilities by specifying a list of them to be # removed, and return the original classmethod for restoration. original_capabilities = handler.capabilities - filtered_capabilities = tuple( - i for i in original_capabilities() if i not in caps_removed) + filtered_capabilities = [ + i for i in original_capabilities() if i not in caps_removed] + def capabilities(cls): return filtered_capabilities handler.capabilities = classmethod(capabilities) return original_capabilities -@skipIf(sys.platform == 'win32', 'Broken on windows, with very long fail time.') -@skipIfPY3 +@skipIf(sys.platform == 'win32', + 'Broken on windows, with very long fail time.') class SmartWebSideBand64kTestCase(SmartWebTestCase): """Test cases for smart HTTP server with side-band-64k support.""" @@ -129,8 +129,8 @@ min_git_version = (1, 7, 0, 2) def setUp(self): - self.o_uph_cap = patch_capabilities(UploadPackHandler, ("no-done",)) - self.o_rph_cap = patch_capabilities(ReceivePackHandler, ("no-done",)) + self.o_uph_cap = patch_capabilities(UploadPackHandler, (b"no-done",)) + self.o_rph_cap = patch_capabilities(ReceivePackHandler, (b"no-done",)) super(SmartWebSideBand64kTestCase, self).setUp() def tearDown(self): @@ -144,8 +144,8 @@ def _check_app(self, app): receive_pack_handler_cls = app.handlers[b'git-receive-pack'] caps = receive_pack_handler_cls.capabilities() - self.assertTrue(b'side-band-64k' in caps) - self.assertFalse(b'no-done' in caps) + self.assertIn(b'side-band-64k', caps) + self.assertNotIn(b'no-done', caps) class SmartWebSideBand64kNoDoneTestCase(SmartWebTestCase): @@ -162,12 +162,12 @@ def _check_app(self, app): receive_pack_handler_cls = app.handlers[b'git-receive-pack'] caps = receive_pack_handler_cls.capabilities() - self.assertTrue(b'side-band-64k' in caps) - self.assertTrue(b'no-done' in caps) + self.assertIn(b'side-band-64k', caps) + self.assertIn(b'no-done', caps) -@skipIf(sys.platform == 'win32', 'Broken on windows, with very long fail time.') -@skipIfPY3 +@skipIf(sys.platform == 'win32', + 'Broken on windows, with very long fail time.') class DumbWebTestCase(WebTests, CompatTestCase): """Test cases for dumb HTTP server.""" @@ -204,4 +204,3 @@ def test_push_to_dulwich_issue_88_standard(self): raise SkipTest('Dumb web pushing not supported.') - diff -Nru dulwich-0.12.0/dulwich/tests/compat/utils.py dulwich-0.18.5/dulwich/tests/compat/utils.py --- dulwich-0.12.0/dulwich/tests/compat/utils.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/compat/utils.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,21 +1,22 @@ # utils.py -- Git compatibility utilities # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Utilities for interacting with cgit.""" @@ -34,7 +35,6 @@ from dulwich.protocol import TCP_GIT_PORT from dulwich.tests import ( - get_safe_env, SkipTest, TestCase, ) @@ -123,7 +123,7 @@ :raise OSError: if the git executable was not found. """ - env = get_safe_env(popen_kwargs.pop('env', None)) + env = popen_kwargs.pop('env', {}) env['LC_ALL'] = env['LANG'] = 'C' args = [git_path] + args @@ -233,10 +233,12 @@ :param name: The name of the repository export file, relative to dulwich/tests/data/repos. - :returns: An initialized Repo object that lives in a temporary directory. + :returns: An initialized Repo object that lives in a temporary + directory. """ path = import_repo_to_dir(name) repo = Repo(path) + def cleanup(): repo.close() rmtree_ro(os.path.dirname(path.rstrip(os.sep))) diff -Nru dulwich-0.12.0/dulwich/tests/__init__.py dulwich-0.18.5/dulwich/tests/__init__.py --- dulwich-0.12.0/dulwich/tests/__init__.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/__init__.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,21 +1,22 @@ # __init__.py -- The tests for dulwich # Copyright (C) 2007 James Westby # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for Dulwich.""" @@ -29,55 +30,36 @@ # If Python itself provides an exception, use that import unittest -if sys.version_info < (2, 7): - from unittest2 import SkipTest, TestCase as _TestCase, skipIf, expectedFailure -else: - from unittest import SkipTest, TestCase as _TestCase, skipIf, expectedFailure - - -def get_safe_env(env=None): - """Returns the environment "env" (or a copy of "os.environ" by default) - modified to avoid side-effects caused by user's ~/.gitconfig""" - - if env is None: - env = os.environ.copy() - # On Windows it's not enough to set "HOME" to a non-existing - # directory. Git.cmd takes the first existing directory out of - # "%HOME%", "%HOMEDRIVE%%HOMEPATH%" and "%USERPROFILE%". - for e in 'HOME', 'HOMEPATH', 'USERPROFILE': - env[e] = '/nosuchdir' - return env +from unittest import ( # noqa: F401 + SkipTest, + TestCase as _TestCase, + skipIf, + expectedFailure, + ) class TestCase(_TestCase): - def makeSafeEnv(self): - """Create environment with homedirectory-related variables stripped. - - Modifies os.environ for the duration of a test case to avoid - side-effects caused by the user's ~/.gitconfig and other - files in their home directory. - """ - old_env = os.environ - def restore(): - os.environ = old_env - self.addCleanup(restore) - new_env = dict(os.environ) - for e in ['HOME', 'HOMEPATH', 'USERPROFILE']: - new_env[e] = '/nosuchdir' - os.environ = new_env - def setUp(self): super(TestCase, self).setUp() - self.makeSafeEnv() + self._old_home = os.environ.get("HOME") + os.environ["HOME"] = "/nonexistant" + + def tearDown(self): + super(TestCase, self).tearDown() + if self._old_home: + os.environ["HOME"] = self._old_home + else: + del os.environ["HOME"] class BlackboxTestCase(TestCase): """Blackbox testing.""" # TODO(jelmer): Include more possible binary paths. - bin_directories = [os.path.abspath(os.path.join(os.path.dirname(__file__), - "..", "..", "bin")), '/usr/bin', '/usr/local/bin'] + bin_directories = [os.path.abspath(os.path.join( + os.path.dirname(__file__), "..", "..", "bin")), '/usr/bin', + '/usr/local/bin'] def bin_path(self, name): """Determine the full path of a binary. @@ -107,10 +89,11 @@ # # Save us from all that headache and call python with the bin script. argv = [sys.executable, self.bin_path(name)] + args - return subprocess.Popen(argv, - stdout=subprocess.PIPE, - stdin=subprocess.PIPE, stderr=subprocess.PIPE, - env=env) + return subprocess.Popen( + argv, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, stderr=subprocess.PIPE, + env=env) def self_test_suite(): @@ -125,6 +108,7 @@ 'grafts', 'greenthreads', 'hooks', + 'ignore', 'index', 'lru_cache', 'objects', @@ -139,6 +123,7 @@ 'refs', 'repository', 'server', + 'utils', 'walk', 'web', ] @@ -157,15 +142,17 @@ 'conclusion', ] tutorial_files = ["../../docs/tutorial/%s.txt" % name for name in tutorial] + def setup(test): test.__old_cwd = os.getcwd() test.__dulwich_tempdir = tempfile.mkdtemp() os.chdir(test.__dulwich_tempdir) + def teardown(test): os.chdir(test.__old_cwd) shutil.rmtree(test.__dulwich_tempdir) - return doctest.DocFileSuite(setUp=setup, tearDown=teardown, - *tutorial_files) + return doctest.DocFileSuite( + setUp=setup, tearDown=teardown, *tutorial_files) def nocompat_test_suite(): diff -Nru dulwich-0.12.0/dulwich/tests/test_archive.py dulwich-0.18.5/dulwich/tests/test_archive.py --- dulwich-0.12.0/dulwich/tests/test_archive.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_archive.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,25 +1,26 @@ # test_archive.py -- tests for archive # Copyright (C) 2015 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) a later version. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for archive support.""" from io import BytesIO -import sys import tarfile from dulwich.archive import tar_stream @@ -41,8 +42,6 @@ class ArchiveTests(TestCase): def test_empty(self): - if sys.version_info[:2] <= (2, 6): - self.skipTest("archive creation known failing on Python2.6") store = MemoryObjectStore() c1, c2, c3 = build_commit_graph(store, [[1], [2, 1], [3, 1, 2]]) tree = store[c3.tree] @@ -53,7 +52,6 @@ self.assertEqual([], tf.getnames()) def test_simple(self): - self.skipTest("known to fail on python2.6 and 3.4; needs debugging") store = MemoryObjectStore() b1 = Blob.from_string(b"somedata") store.add_object(b1) diff -Nru dulwich-0.12.0/dulwich/tests/test_blackbox.py dulwich-0.18.5/dulwich/tests/test_blackbox.py --- dulwich-0.12.0/dulwich/tests/test_blackbox.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_blackbox.py 2017-07-29 00:14:28.000000000 +0000 @@ -1,20 +1,22 @@ # test_blackbox.py -- blackbox tests # Copyright (C) 2010 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) a later version. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Blackbox tests for Dulwich commands.""" diff -Nru dulwich-0.12.0/dulwich/tests/test_client.py dulwich-0.18.5/dulwich/tests/test_client.py --- dulwich-0.12.0/dulwich/tests/test_client.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_client.py 2017-10-29 14:57:45.000000000 +0000 @@ -1,27 +1,42 @@ # test_client.py -- Tests for the git protocol, client side # Copyright (C) 2009 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# or (at your option) any later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. -from contextlib import closing from io import BytesIO import sys import shutil import tempfile +try: + import urllib2 +except ImportError: + import urllib.request as urllib2 + +try: + from urllib import quote as urlquote +except ImportError: + from urllib.parse import quote as urlquote + +try: + import urlparse +except ImportError: + import urllib.parse as urlparse import dulwich from dulwich import ( @@ -35,10 +50,16 @@ HttpGitClient, ReportStatusParser, SendPackError, + StrangeHostname, + SubprocessSSHVendor, UpdateRefsError, + default_urllib2_opener, get_transport_and_path, get_transport_and_path_from_url, ) +from dulwich.config import ( + ConfigDict, + ) from dulwich.tests import ( TestCase, ) @@ -87,14 +108,15 @@ self.rout.write) def test_caps(self): - agent_cap = ('agent=dulwich/%d.%d.%d' % dulwich.__version__).encode('ascii') + agent_cap = ( + 'agent=dulwich/%d.%d.%d' % dulwich.__version__).encode('ascii') self.assertEqual(set([b'multi_ack', b'side-band-64k', b'ofs-delta', - b'thin-pack', b'multi_ack_detailed', - agent_cap]), - set(self.client._fetch_capabilities)) + b'thin-pack', b'multi_ack_detailed', + agent_cap]), + set(self.client._fetch_capabilities)) self.assertEqual(set([b'ofs-delta', b'report-status', b'side-band-64k', agent_cap]), - set(self.client._send_capabilities)) + set(self.client._send_capabilities)) def test_archive_ack(self): self.rin.write( @@ -107,16 +129,44 @@ def test_fetch_empty(self): self.rin.write(b'0000') self.rin.seek(0) - self.client.fetch_pack(b'/', lambda heads: [], None, None) + + def check_heads(heads): + self.assertIs(heads, None) + return [] + ret = self.client.fetch_pack(b'/', check_heads, None, None) + self.assertIs(None, ret.refs) + self.assertEqual({}, ret.symrefs) + + def test_fetch_pack_ignores_magic_ref(self): + self.rin.write( + b'00000000000000000000000000000000000000000000 capabilities^{}' + b'\x00 multi_ack ' + b'thin-pack side-band side-band-64k ofs-delta shallow no-progress ' + b'include-tag\n' + b'0000') + self.rin.seek(0) + + def check_heads(heads): + self.assertEquals({}, heads) + return [] + ret = self.client.fetch_pack(b'bla', check_heads, None, None, None) + self.assertIs(None, ret.refs) + self.assertEqual({}, ret.symrefs) + self.assertEqual(self.rout.getvalue(), b'0000') def test_fetch_pack_none(self): self.rin.write( - b'008855dcc6bf963f922e1ed5c4bbaaefcfacef57b1d7 HEAD.multi_ack ' + b'008855dcc6bf963f922e1ed5c4bbaaefcfacef57b1d7 HEAD\x00multi_ack ' b'thin-pack side-band side-band-64k ofs-delta shallow no-progress ' b'include-tag\n' b'0000') self.rin.seek(0) - self.client.fetch_pack(b'bla', lambda heads: [], None, None, None) + ret = self.client.fetch_pack( + b'bla', lambda heads: [], None, None, None) + self.assertEqual( + {b'HEAD': b'55dcc6bf963f922e1ed5c4bbaaefcfacef57b1d7'}, + ret.refs) + self.assertEqual({}, ret.symrefs) self.assertEqual(self.rout.getvalue(), b'0000') def test_send_pack_no_sideband64k_with_update_ref_error(self): @@ -129,7 +179,7 @@ b"ng refs/foo/bar pre-receive hook declined", b''] for pkt in pkts: - if pkt == b'': + if pkt == b'': self.rin.write(b"0000") else: self.rin.write(("%04x" % (len(pkt)+4)).encode('ascii') + pkt) @@ -165,7 +215,8 @@ def determine_wants(refs): return { - b'refs/heads/master': b'310ca9477129b8586fa2afc779c1f57cf64bba6c' + b'refs/heads/master': + b'310ca9477129b8586fa2afc779c1f57cf64bba6c' } def generate_pack_contents(have, want): @@ -174,6 +225,32 @@ self.client.send_pack(b'/', determine_wants, generate_pack_contents) self.assertEqual(self.rout.getvalue(), b'0000') + def test_send_pack_keep_and_delete(self): + self.rin.write( + b'0063310ca9477129b8586fa2afc779c1f57cf64bba6c ' + b'refs/heads/master\x00report-status delete-refs ofs-delta\n' + b'003f310ca9477129b8586fa2afc779c1f57cf64bba6c refs/heads/keepme\n' + b'0000000eunpack ok\n' + b'0019ok refs/heads/master\n' + b'0000') + self.rin.seek(0) + + def determine_wants(refs): + return {b'refs/heads/master': b'0' * 40} + + def generate_pack_contents(have, want): + return {} + + self.client.send_pack(b'/', determine_wants, generate_pack_contents) + self.assertIn( + self.rout.getvalue(), + [b'007f310ca9477129b8586fa2afc779c1f57cf64bba6c ' + b'0000000000000000000000000000000000000000 ' + b'refs/heads/master\x00report-status ofs-delta0000', + b'007f310ca9477129b8586fa2afc779c1f57cf64bba6c ' + b'0000000000000000000000000000000000000000 ' + b'refs/heads/master\x00ofs-delta report-status0000']) + def test_send_pack_delete_only(self): self.rin.write( b'0063310ca9477129b8586fa2afc779c1f57cf64bba6c ' @@ -212,7 +289,8 @@ return { b'refs/heads/blah12': b'310ca9477129b8586fa2afc779c1f57cf64bba6c', - b'refs/heads/master': b'310ca9477129b8586fa2afc779c1f57cf64bba6c' + b'refs/heads/master': + b'310ca9477129b8586fa2afc779c1f57cf64bba6c' } def generate_pack_contents(have, want): @@ -254,7 +332,8 @@ def determine_wants(refs): return { b'refs/heads/blah12': commit.id, - b'refs/heads/master': b'310ca9477129b8586fa2afc779c1f57cf64bba6c' + b'refs/heads/master': + b'310ca9477129b8586fa2afc779c1f57cf64bba6c' } def generate_pack_contents(have, want): @@ -266,9 +345,11 @@ self.assertIn( self.rout.getvalue(), [b'007f0000000000000000000000000000000000000000 ' + commit.id + - b' refs/heads/blah12\x00report-status ofs-delta0000' + f.getvalue(), + b' refs/heads/blah12\x00report-status ofs-delta0000' + + f.getvalue(), b'007f0000000000000000000000000000000000000000 ' + commit.id + - b' refs/heads/blah12\x00ofs-delta report-status0000' + f.getvalue()]) + b' refs/heads/blah12\x00ofs-delta report-status0000' + + f.getvalue()]) def test_send_pack_no_deleteref_delete_only(self): pkts = [b'310ca9477129b8586fa2afc779c1f57cf64bba6c refs/heads/master' @@ -310,13 +391,21 @@ self.assertEqual(1234, c._port) self.assertEqual('/bar/baz', path) - def test_ssh_explicit(self): + def test_git_ssh_explicit(self): c, path = get_transport_and_path('git+ssh://foo.com/bar/baz') self.assertTrue(isinstance(c, SSHGitClient)) self.assertEqual('foo.com', c.host) self.assertEqual(None, c.port) self.assertEqual(None, c.username) - self.assertEqual('bar/baz', path) + self.assertEqual('/bar/baz', path) + + def test_ssh_explicit(self): + c, path = get_transport_and_path('ssh://foo.com/bar/baz') + self.assertTrue(isinstance(c, SSHGitClient)) + self.assertEqual('foo.com', c.host) + self.assertEqual(None, c.port) + self.assertEqual(None, c.username) + self.assertEqual('/bar/baz', path) def test_ssh_port_explicit(self): c, path = get_transport_and_path( @@ -324,19 +413,35 @@ self.assertTrue(isinstance(c, SSHGitClient)) self.assertEqual('foo.com', c.host) self.assertEqual(1234, c.port) - self.assertEqual('bar/baz', path) + self.assertEqual('/bar/baz', path) - def test_ssh_abspath_explicit(self): + def test_username_and_port_explicit_unknown_scheme(self): + c, path = get_transport_and_path( + 'unknown://git@server:7999/dply/stuff.git') + self.assertTrue(isinstance(c, SSHGitClient)) + self.assertEqual('unknown', c.host) + self.assertEqual('//git@server:7999/dply/stuff.git', path) + + def test_username_and_port_explicit(self): + c, path = get_transport_and_path( + 'ssh://git@server:7999/dply/stuff.git') + self.assertTrue(isinstance(c, SSHGitClient)) + self.assertEqual('git', c.username) + self.assertEqual('server', c.host) + self.assertEqual(7999, c.port) + self.assertEqual('/dply/stuff.git', path) + + def test_ssh_abspath_doubleslash(self): c, path = get_transport_and_path('git+ssh://foo.com//bar/baz') self.assertTrue(isinstance(c, SSHGitClient)) self.assertEqual('foo.com', c.host) self.assertEqual(None, c.port) self.assertEqual(None, c.username) - self.assertEqual('/bar/baz', path) + self.assertEqual('//bar/baz', path) - def test_ssh_port_abspath_explicit(self): + def test_ssh_port(self): c, path = get_transport_and_path( - 'git+ssh://foo.com:1234//bar/baz') + 'git+ssh://foo.com:1234/bar/baz') self.assertTrue(isinstance(c, SSHGitClient)) self.assertEqual('foo.com', c.host) self.assertEqual(1234, c.port) @@ -413,6 +518,26 @@ self.assertTrue(isinstance(c, HttpGitClient)) self.assertEqual('/jelmer/dulwich', path) + def test_http_auth(self): + url = 'https://user:passwd@github.com/jelmer/dulwich' + + c, path = get_transport_and_path(url) + + self.assertTrue(isinstance(c, HttpGitClient)) + self.assertEqual('/jelmer/dulwich', path) + self.assertEqual('user', c._username) + self.assertEqual('passwd', c._password) + + def test_http_no_auth(self): + url = 'https://github.com/jelmer/dulwich' + + c, path = get_transport_and_path(url) + + self.assertTrue(isinstance(c, HttpGitClient)) + self.assertEqual('/jelmer/dulwich', path) + self.assertIs(None, c._username) + self.assertIs(None, c._password) + class TestGetTransportAndPathFromUrl(TestCase): @@ -436,7 +561,7 @@ self.assertEqual('foo.com', c.host) self.assertEqual(None, c.port) self.assertEqual(None, c.username) - self.assertEqual('bar/baz', path) + self.assertEqual('/bar/baz', path) def test_ssh_port_explicit(self): c, path = get_transport_and_path_from_url( @@ -444,40 +569,45 @@ self.assertTrue(isinstance(c, SSHGitClient)) self.assertEqual('foo.com', c.host) self.assertEqual(1234, c.port) - self.assertEqual('bar/baz', path) + self.assertEqual('/bar/baz', path) - def test_ssh_abspath_explicit(self): - c, path = get_transport_and_path_from_url('git+ssh://foo.com//bar/baz') + def test_ssh_homepath(self): + c, path = get_transport_and_path_from_url( + 'git+ssh://foo.com/~/bar/baz') self.assertTrue(isinstance(c, SSHGitClient)) self.assertEqual('foo.com', c.host) self.assertEqual(None, c.port) self.assertEqual(None, c.username) - self.assertEqual('/bar/baz', path) + self.assertEqual('/~/bar/baz', path) - def test_ssh_port_abspath_explicit(self): + def test_ssh_port_homepath(self): c, path = get_transport_and_path_from_url( - 'git+ssh://foo.com:1234//bar/baz') + 'git+ssh://foo.com:1234/~/bar/baz') self.assertTrue(isinstance(c, SSHGitClient)) self.assertEqual('foo.com', c.host) self.assertEqual(1234, c.port) - self.assertEqual('/bar/baz', path) + self.assertEqual('/~/bar/baz', path) def test_ssh_host_relpath(self): - self.assertRaises(ValueError, get_transport_and_path_from_url, + self.assertRaises( + ValueError, get_transport_and_path_from_url, 'foo.com:bar/baz') def test_ssh_user_host_relpath(self): - self.assertRaises(ValueError, get_transport_and_path_from_url, + self.assertRaises( + ValueError, get_transport_and_path_from_url, 'user@foo.com:bar/baz') def test_local_path(self): - self.assertRaises(ValueError, get_transport_and_path_from_url, + self.assertRaises( + ValueError, get_transport_and_path_from_url, 'foo.bar/baz') def test_error(self): # Need to use a known urlparse.uses_netloc URL scheme to get the # expected parsing of the URL on Python versions less than 2.6.5 - self.assertRaises(ValueError, get_transport_and_path_from_url, + self.assertRaises( + ValueError, get_transport_and_path_from_url, 'prospero://bar/baz') def test_http(self): @@ -501,15 +631,13 @@ self.port = None def run_command(self, host, command, username=None, port=None): - if not isinstance(command, bytes): - raise TypeError(command) - self.host = host self.command = command self.username = username self.port = port - class Subprocess: pass + class Subprocess: + pass setattr(Subprocess, 'read', lambda: None) setattr(Subprocess, 'write', lambda: None) setattr(Subprocess, 'close', lambda: None) @@ -532,21 +660,37 @@ super(SSHGitClientTests, self).tearDown() client.get_ssh_vendor = self.real_vendor + def test_get_url(self): + path = '/tmp/repo.git' + c = SSHGitClient('git.samba.org') + + url = c.get_url(path) + self.assertEqual('ssh://git.samba.org/tmp/repo.git', url) + + def test_get_url_with_username_and_port(self): + path = '/tmp/repo.git' + c = SSHGitClient('git.samba.org', port=2222, username='user') + + url = c.get_url(path) + self.assertEqual('ssh://user@git.samba.org:2222/tmp/repo.git', url) + def test_default_command(self): - self.assertEqual(b'git-upload-pack', - self.client._get_cmd_path(b'upload-pack')) + self.assertEqual( + b'git-upload-pack', + self.client._get_cmd_path(b'upload-pack')) def test_alternative_command_path(self): self.client.alternative_paths[b'upload-pack'] = ( b'/usr/lib/git/git-upload-pack') - self.assertEqual(b'/usr/lib/git/git-upload-pack', + self.assertEqual( + b'/usr/lib/git/git-upload-pack', self.client._get_cmd_path(b'upload-pack')) def test_alternative_command_path_spaces(self): self.client.alternative_paths[b'upload-pack'] = ( b'/usr/lib/git/git-upload-pack -ibla') self.assertEqual(b"/usr/lib/git/git-upload-pack -ibla", - self.client._get_cmd_path(b'upload-pack')) + self.client._get_cmd_path(b'upload-pack')) def test_connect(self): server = self.server @@ -558,11 +702,11 @@ client._connect(b"command", b"/path/to/repo") self.assertEqual(b"username", server.username) self.assertEqual(1337, server.port) - self.assertEqual(b"git-command '/path/to/repo'", server.command) + self.assertEqual("git-command '/path/to/repo'", server.command) client._connect(b"relative-command", b"/~/path/to/repo") - self.assertEqual(b"git-relative-command '~/path/to/repo'", - server.command) + self.assertEqual("git-relative-command '~/path/to/repo'", + server.command) class ReportStatusParserTests(TestCase): @@ -591,6 +735,13 @@ class LocalGitClientTests(TestCase): + def test_get_url(self): + path = "/tmp/repo.git" + c = LocalGitClient() + + url = c.get_url(path) + self.assertEqual('file:///tmp/repo.git', url) + def test_fetch_into_empty(self): c = LocalGitClient() t = MemoryRepo() @@ -604,10 +755,22 @@ self.addCleanup(tear_down_repo, s) out = BytesIO() walker = {} - c.fetch_pack(s.path, lambda heads: [], graph_walker=walker, - pack_data=out.write) - self.assertEqual(b"PACK\x00\x00\x00\x02\x00\x00\x00\x00\x02\x9d\x08" - b"\x82;\xd8\xa8\xea\xb5\x10\xadj\xc7\\\x82<\xfd>\xd3\x1e", out.getvalue()) + ret = c.fetch_pack( + s.path, lambda heads: [], graph_walker=walker, pack_data=out.write) + self.assertEqual({ + b'HEAD': b'a90fa2d900a17e99b433217e988c4eb4a2e9a097', + b'refs/heads/master': b'a90fa2d900a17e99b433217e988c4eb4a2e9a097', + b'refs/tags/mytag': b'28237f4dc30d0d462658d6b937b08a0f0b6ef55a', + b'refs/tags/mytag-packed': + b'b0931cadc54336e78a1d980420e3268903b57a50' + }, ret.refs) + self.assertEqual( + {b'HEAD': b'refs/heads/master'}, + ret.symrefs) + self.assertEqual( + b"PACK\x00\x00\x00\x02\x00\x00\x00\x00\x02\x9d\x08" + b"\x82;\xd8\xa8\xea\xb5\x10\xadj\xc7\\\x82<\xfd>\xd3\x1e", + out.getvalue()) def test_fetch_pack_none(self): c = LocalGitClient() @@ -615,11 +778,21 @@ self.addCleanup(tear_down_repo, s) out = BytesIO() walker = MemoryRepo().get_graph_walker() - c.fetch_pack(s.path, + ret = c.fetch_pack( + s.path, lambda heads: [b"a90fa2d900a17e99b433217e988c4eb4a2e9a097"], graph_walker=walker, pack_data=out.write) + self.assertEqual({b'HEAD': b'refs/heads/master'}, ret.symrefs) + self.assertEqual({ + b'HEAD': b'a90fa2d900a17e99b433217e988c4eb4a2e9a097', + b'refs/heads/master': b'a90fa2d900a17e99b433217e988c4eb4a2e9a097', + b'refs/tags/mytag': b'28237f4dc30d0d462658d6b937b08a0f0b6ef55a', + b'refs/tags/mytag-packed': + b'b0931cadc54336e78a1d980420e3268903b57a50' + }, ret.refs) # Hardcoding is not ideal, but we'll fix that some other day.. - self.assertTrue(out.getvalue().startswith(b'PACK\x00\x00\x00\x02\x00\x00\x00\x07')) + self.assertTrue(out.getvalue().startswith( + b'PACK\x00\x00\x00\x02\x00\x00\x00\x07')) def test_send_pack_without_changes(self): local = open_repo('a.git') @@ -636,7 +809,7 @@ target_path = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, target_path) - with closing(Repo.init_bare(target_path)) as target: + with Repo.init_bare(target_path) as target: self.send_and_verify(b"master", local, target) def test_get_refs(self): @@ -648,17 +821,134 @@ self.assertDictEqual(local.refs.as_dict(), refs) def send_and_verify(self, branch, local, target): + """Send branch from local to remote repository and verify it worked.""" client = LocalGitClient() ref_name = b"refs/heads/" + branch new_refs = client.send_pack(target.path, - lambda _: { ref_name: local.refs[ref_name] }, + lambda _: {ref_name: local.refs[ref_name]}, local.object_store.generate_pack_contents) self.assertEqual(local.refs[ref_name], new_refs[ref_name]) - for name, sha in new_refs.items(): - self.assertEqual(new_refs[name], target.refs[name]) - obj_local = local.get_object(new_refs[ref_name]) obj_target = target.get_object(new_refs[ref_name]) self.assertEqual(obj_local, obj_target) + + +class HttpGitClientTests(TestCase): + + def test_get_url(self): + base_url = 'https://github.com/jelmer/dulwich' + path = '/jelmer/dulwich' + c = HttpGitClient(base_url) + + url = c.get_url(path) + self.assertEqual('https://github.com/jelmer/dulwich', url) + + def test_get_url_bytes_path(self): + base_url = 'https://github.com/jelmer/dulwich' + path_bytes = b'/jelmer/dulwich' + c = HttpGitClient(base_url) + + url = c.get_url(path_bytes) + self.assertEqual('https://github.com/jelmer/dulwich', url) + + def test_get_url_with_username_and_passwd(self): + base_url = 'https://github.com/jelmer/dulwich' + path = '/jelmer/dulwich' + c = HttpGitClient(base_url, username='USERNAME', password='PASSWD') + + url = c.get_url(path) + self.assertEqual('https://github.com/jelmer/dulwich', url) + + def test_init_username_passwd_set(self): + url = 'https://github.com/jelmer/dulwich' + + c = HttpGitClient(url, config=None, username='user', password='passwd') + self.assertEqual('user', c._username) + self.assertEqual('passwd', c._password) + [pw_handler] = [ + h for h in c.opener.handlers + if getattr(h, 'passwd', None) is not None] + self.assertEqual( + ('user', 'passwd'), + pw_handler.passwd.find_user_password( + None, 'https://github.com/jelmer/dulwich')) + + def test_init_no_username_passwd(self): + url = 'https://github.com/jelmer/dulwich' + + c = HttpGitClient(url, config=None) + self.assertIs(None, c._username) + self.assertIs(None, c._password) + pw_handler = [ + h for h in c.opener.handlers + if getattr(h, 'passwd', None) is not None] + self.assertEqual(0, len(pw_handler)) + + def test_from_parsedurl_on_url_with_quoted_credentials(self): + original_username = 'john|the|first' + quoted_username = urlquote(original_username) + + original_password = 'Ya#1$2%3' + quoted_password = urlquote(original_password) + + url = 'https://{username}:{password}@github.com/jelmer/dulwich'.format( + username=quoted_username, + password=quoted_password + ) + + c = HttpGitClient.from_parsedurl(urlparse.urlparse(url)) + self.assertEqual(original_username, c._username) + self.assertEqual(original_password, c._password) + [pw_handler] = [ + h for h in c.opener.handlers + if getattr(h, 'passwd', None) is not None] + self.assertEqual( + (original_username, original_password), + pw_handler.passwd.find_user_password( + None, 'https://github.com/jelmer/dulwich')) + + +class TCPGitClientTests(TestCase): + + def test_get_url(self): + host = 'github.com' + path = '/jelmer/dulwich' + c = TCPGitClient(host) + + url = c.get_url(path) + self.assertEqual('git://github.com/jelmer/dulwich', url) + + def test_get_url_with_port(self): + host = 'github.com' + path = '/jelmer/dulwich' + port = 9090 + c = TCPGitClient(host, port=port) + + url = c.get_url(path) + self.assertEqual('git://github.com:9090/jelmer/dulwich', url) + + +class DefaultUrllib2OpenerTest(TestCase): + + def test_no_config(self): + default_urllib2_opener(config=None) + + def test_config_no_proxy(self): + default_urllib2_opener(config=ConfigDict()) + + def test_config_proxy(self): + config = ConfigDict() + config.set(b'http', b'proxy', b'http://localhost:3128/') + opener = default_urllib2_opener(config=config) + self.assertIn(urllib2.ProxyHandler, + list(map(lambda x: x.__class__, opener.handlers))) + + +class SubprocessSSHVendorTests(TestCase): + + def test_run_command_dashes(self): + vendor = SubprocessSSHVendor() + self.assertRaises(StrangeHostname, vendor.run_command, '--weird-host', + 'git-clone-url') diff -Nru dulwich-0.12.0/dulwich/tests/test_config.py dulwich-0.18.5/dulwich/tests/test_config.py --- dulwich-0.12.0/dulwich/tests/test_config.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_config.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,20 +1,22 @@ # test_config.py -- Tests for reading and writing configuration files # Copyright (C) 2011 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for reading and writing configuration files.""" @@ -28,7 +30,7 @@ _format_string, _escape_value, _parse_string, - _unescape_value, + parse_submodules, ) from dulwich.tests import ( TestCase, @@ -48,10 +50,10 @@ def test_default_config(self): cf = self.from_file(b"""[core] - repositoryformatversion = 0 - filemode = true - bare = false - logallrefupdates = true +\trepositoryformatversion = 0 +\tfilemode = true +\tbare = false +\tlogallrefupdates = true """) self.assertEqual(ConfigFile({(b"core", ): { b"repositoryformatversion": b"0", @@ -94,8 +96,7 @@ self.assertEqual(b"barla", cf.get((b"core", ), b"foo")) def test_from_file_with_open_quoted(self): - self.assertRaises(ValueError, - self.from_file, b"[core]\nfoo = \"bar\n") + self.assertRaises(ValueError, self.from_file, b"[core]\nfoo = \"bar\n") def test_from_file_with_quotes(self): cf = self.from_file( @@ -121,8 +122,8 @@ self.assertEqual(b"bar", cf.get((b"branch", b"foo"), b"foo")) def test_from_file_subsection_invalid(self): - self.assertRaises(ValueError, - self.from_file, b"[branch \"foo]\nfoo = bar\n") + self.assertRaises( + ValueError, self.from_file, b"[branch \"foo]\nfoo = bar\n") def test_from_file_subsection_not_quoted(self): cf = self.from_file(b"[branch.foo]\nfoo = bar\n") @@ -152,15 +153,33 @@ cf = self.from_file(b"[branch.foo] foo = bar\n") self.assertEqual(b"bar", cf.get((b"branch", b"foo"), b"foo")) - #@expectedFailure def test_quoted(self): cf = self.from_file(b"""[gui] - fontdiff = -family \\\"Ubuntu Mono\\\" -size 11 -weight normal -slant roman -underline 0 -overstrike 0 +\tfontdiff = -family \\\"Ubuntu Mono\\\" -size 11 -overstrike 0 """) self.assertEqual(ConfigFile({(b'gui', ): { - b'fontdiff': b'-family "Ubuntu Mono" -size 11 -weight normal -slant roman -underline 0 -overstrike 0', + b'fontdiff': b'-family "Ubuntu Mono" -size 11 -overstrike 0', }}), cf) + def test_quoted_multiline(self): + cf = self.from_file(b"""[alias] +who = \"!who() {\\ + git log --no-merges --pretty=format:'%an - %ae' $@ | uniq -c | sort -rn;\\ +};\\ +who\" +""") + self.assertEqual(ConfigFile({(b'alias', ): { + b'who': (b"!who() {git log --no-merges --pretty=format:'%an - " + b"%ae' $@ | uniq -c | sort -rn;};who") + }}), cf) + + def test_set_hash_gets_quoted(self): + c = ConfigFile() + c.set(b"xandikos", b"color", b"#665544") + f = BytesIO() + c.write_to_file(f) + self.assertEqual(b"[xandikos]\n\tcolor = \"#665544\"\n", f.getvalue()) + class ConfigDictTests(TestCase): @@ -205,15 +224,13 @@ cd = ConfigDict() cd.set((b"core2", ), b"foo", b"bloe") - self.assertEqual([], - list(cd.iteritems((b"core", )))) + self.assertEqual([], list(cd.iteritems((b"core", )))) def test_itersections(self): cd = ConfigDict() cd.set((b"core2", ), b"foo", b"bloe") - self.assertEqual([(b"core2", )], - list(cd.itersections())) + self.assertEqual([(b"core2", )], list(cd.itersections())) class StackedConfigTests(TestCase): @@ -292,3 +309,17 @@ self.assertTrue(_check_section_name(b"foo")) self.assertTrue(_check_section_name(b"foo-bar")) self.assertTrue(_check_section_name(b"bar.bar")) + + +class SubmodulesTests(TestCase): + + def testSubmodules(self): + cf = ConfigFile.from_file(BytesIO(b"""\ +[submodule "core/lib"] +\tpath = core/lib +\turl = https://github.com/phhusson/QuasselC.git +""")) + got = list(parse_submodules(cf)) + self.assertEqual([ + (b'core/lib', b'https://github.com/phhusson/QuasselC.git', + b'core/lib')], got) diff -Nru dulwich-0.12.0/dulwich/tests/test_diff_tree.py dulwich-0.18.5/dulwich/tests/test_diff_tree.py --- dulwich-0.12.0/dulwich/tests/test_diff_tree.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_diff_tree.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,20 +1,22 @@ # test_diff_tree.py -- Tests for file and tree diff utilities. # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for file and tree diff utilities.""" @@ -169,7 +171,8 @@ self.assertChangesEqual([], self.empty_tree, self.empty_tree) self.assertChangesEqual([], tree, tree) self.assertChangesEqual( - [TreeChange(CHANGE_UNCHANGED, (b'a', F, blob.id), (b'a', F, blob.id)), + [TreeChange(CHANGE_UNCHANGED, (b'a', F, blob.id), + (b'a', F, blob.id)), TreeChange(CHANGE_UNCHANGED, (b'b/c', F, blob.id), (b'b/c', F, blob.id))], tree, tree, want_unchanged=True) @@ -267,7 +270,8 @@ tree1 = self.commit_tree([(b'a', blob), (b'a.', blob), (b'a..', blob)]) # Tree order is the reverse of this, so if we used tree order, 'a..' # would not be merged. - tree2 = self.commit_tree([(b'a/x', blob), (b'a./x', blob), (b'a..', blob)]) + tree2 = self.commit_tree( + [(b'a/x', blob), (b'a./x', blob), (b'a..', blob)]) self.assertChangesEqual( [TreeChange.delete((b'a', F, blob.id)), @@ -350,7 +354,8 @@ merge = self.commit_tree([(b'a', blob2)]) self.assertChangesForMergeEqual( [[TreeChange.add((b'a', F, blob2.id)), - TreeChange(CHANGE_MODIFY, (b'a', F, blob1.id), (b'a', F, blob2.id))]], + TreeChange(CHANGE_MODIFY, (b'a', F, blob1.id), + (b'a', F, blob2.id))]], [parent1, parent2], merge) def test_tree_changes_for_merge_modify_modify_conflict(self): @@ -361,8 +366,10 @@ parent2 = self.commit_tree([(b'a', blob2)]) merge = self.commit_tree([(b'a', blob3)]) self.assertChangesForMergeEqual( - [[TreeChange(CHANGE_MODIFY, (b'a', F, blob1.id), (b'a', F, blob3.id)), - TreeChange(CHANGE_MODIFY, (b'a', F, blob2.id), (b'a', F, blob3.id))]], + [[TreeChange(CHANGE_MODIFY, (b'a', F, blob1.id), + (b'a', F, blob3.id)), + TreeChange(CHANGE_MODIFY, (b'a', F, blob2.id), + (b'a', F, blob3.id))]], [parent1, parent2], merge) def test_tree_changes_for_merge_modify_no_conflict(self): @@ -432,7 +439,8 @@ parent2 = self.commit_tree([]) merge = self.commit_tree([(b'b', blob)]) add = TreeChange.add((b'b', F, blob.id)) - self.assertChangesForMergeEqual([[add, add]], [parent1, parent2], merge) + self.assertChangesForMergeEqual( + [[add, add]], [parent1, parent2], merge) def test_tree_changes_for_merge_add_exact_rename_conflict(self): blob = make_object(Blob, data=b'a\nb\nc\nd\n') @@ -440,7 +448,8 @@ parent2 = self.commit_tree([]) merge = self.commit_tree([(b'b', blob)]) self.assertChangesForMergeEqual( - [[TreeChange(CHANGE_RENAME, (b'a', F, blob.id), (b'b', F, blob.id)), + [[TreeChange(CHANGE_RENAME, (b'a', F, blob.id), + (b'b', F, blob.id)), TreeChange.add((b'b', F, blob.id))]], [parent1, parent2], merge, rename_detector=self.detector) @@ -451,7 +460,8 @@ parent2 = self.commit_tree([]) merge = self.commit_tree([(b'b', blob2)]) self.assertChangesForMergeEqual( - [[TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'b', F, blob2.id)), + [[TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), + (b'b', F, blob2.id)), TreeChange.add((b'b', F, blob2.id))]], [parent1, parent2], merge, rename_detector=self.detector) @@ -462,8 +472,10 @@ parent2 = self.commit_tree([(b'b', blob1)]) merge = self.commit_tree([(b'b', blob2)]) self.assertChangesForMergeEqual( - [[TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'b', F, blob2.id)), - TreeChange(CHANGE_MODIFY, (b'b', F, blob1.id), (b'b', F, blob2.id))]], + [[TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), + (b'b', F, blob2.id)), + TreeChange(CHANGE_MODIFY, (b'b', F, blob1.id), + (b'b', F, blob2.id))]], [parent1, parent2], merge, rename_detector=self.detector) @@ -471,7 +483,8 @@ def _do_test_count_blocks(self, count_blocks): blob = make_object(Blob, data=b'a\nb\na\n') - self.assertEqual({hash(b'a\n'): 4, hash(b'b\n'): 2}, count_blocks(blob)) + self.assertEqual({hash(b'a\n'): 4, hash(b'b\n'): 2}, + count_blocks(blob)) test_count_blocks = functest_builder(_do_test_count_blocks, _count_blocks_py) @@ -489,7 +502,8 @@ def _do_test_count_blocks_chunks(self, count_blocks): blob = ShaFile.from_raw_chunks(Blob.type_num, [b'a\nb', b'\na\n']) - self.assertEqual({hash(b'a\n'): 4, hash(b'b\n'): 2}, _count_blocks(blob)) + self.assertEqual({hash(b'a\n'): 4, hash(b'b\n'): 2}, + _count_blocks(blob)) test_count_blocks_chunks = functest_builder(_do_test_count_blocks_chunks, _count_blocks_py) @@ -500,8 +514,8 @@ a = b'a' * 64 data = a + b'xxx\ny\n' + a + b'zzz\n' blob = make_object(Blob, data=data) - self.assertEqual({hash(b'a' * 64): 128, hash(b'xxx\n'): 4, hash(b'y\n'): 2, - hash(b'zzz\n'): 4}, + self.assertEqual({hash(b'a' * 64): 128, hash(b'xxx\n'): 4, + hash(b'y\n'): 2, hash(b'zzz\n'): 4}, _count_blocks(blob)) test_count_blocks_long_lines = functest_builder( @@ -575,7 +589,8 @@ tree1 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) tree2 = self.commit_tree([(b'a', blob1), (b'b', blob3)]) self.assertEqual( - [TreeChange(CHANGE_MODIFY, (b'b', F, blob2.id), (b'b', F, blob3.id))], + [TreeChange(CHANGE_MODIFY, (b'b', F, blob2.id), + (b'b', F, blob3.id))], self.detect_renames(tree1, tree2)) def test_exact_rename_one_to_one(self): @@ -584,9 +599,11 @@ tree1 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) tree2 = self.commit_tree([(b'c', blob1), (b'd', blob2)]) self.assertEqual( - [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'c', F, blob1.id)), - TreeChange(CHANGE_RENAME, (b'b', F, blob2.id), (b'd', F, blob2.id))], - self.detect_renames(tree1, tree2)) + [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), + (b'c', F, blob1.id)), + TreeChange(CHANGE_RENAME, (b'b', F, blob2.id), + (b'd', F, blob2.id))], + self.detect_renames(tree1, tree2)) def test_exact_rename_split_different_type(self): blob = make_object(Blob, data=b'/foo') @@ -603,9 +620,10 @@ tree1 = self.commit_tree([(b'a', blob1)]) tree2 = self.commit_tree([(b'a', blob2, 0o120000), (b'b', blob1)]) self.assertEqual( - [TreeChange.add((b'a', 0o120000, blob2.id)), - TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'b', F, blob1.id))], - self.detect_renames(tree1, tree2)) + [TreeChange.add((b'a', 0o120000, blob2.id)), + TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), + (b'b', F, blob1.id))], + self.detect_renames(tree1, tree2)) def test_exact_rename_one_to_many(self): blob = make_object(Blob, data=b'1') @@ -630,10 +648,13 @@ tree1 = self.commit_tree([(b'a', blob), (b'b', blob)]) tree2 = self.commit_tree([(b'c', blob), (b'd', blob), (b'e', blob)]) self.assertEqual( - [TreeChange(CHANGE_RENAME, (b'a', F, blob.id), (b'c', F, blob.id)), - TreeChange(CHANGE_COPY, (b'a', F, blob.id), (b'e', F, blob.id)), - TreeChange(CHANGE_RENAME, (b'b', F, blob.id), (b'd', F, blob.id))], - self.detect_renames(tree1, tree2)) + [TreeChange(CHANGE_RENAME, (b'a', F, blob.id), + (b'c', F, blob.id)), + TreeChange(CHANGE_COPY, (b'a', F, blob.id), + (b'e', F, blob.id)), + TreeChange(CHANGE_RENAME, (b'b', F, blob.id), + (b'd', F, blob.id))], + self.detect_renames(tree1, tree2)) def test_exact_copy_modify(self): blob1 = make_object(Blob, data=b'a\nb\nc\nd\n') @@ -641,8 +662,10 @@ tree1 = self.commit_tree([(b'a', blob1)]) tree2 = self.commit_tree([(b'a', blob2), (b'b', blob1)]) self.assertEqual( - [TreeChange(CHANGE_MODIFY, (b'a', F, blob1.id), (b'a', F, blob2.id)), - TreeChange(CHANGE_COPY, (b'a', F, blob1.id), (b'b', F, blob1.id))], + [TreeChange(CHANGE_MODIFY, (b'a', F, blob1.id), + (b'a', F, blob2.id)), + TreeChange(CHANGE_COPY, (b'a', F, blob1.id), + (b'b', F, blob1.id))], self.detect_renames(tree1, tree2)) def test_exact_copy_change_mode(self): @@ -661,7 +684,8 @@ tree1 = self.commit_tree([(b'a', blob1)]) tree2 = self.commit_tree([(b'b', blob2)]) self.assertEqual( - [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'b', F, blob2.id))], + [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), + (b'b', F, blob2.id))], self.detect_renames(tree1, tree2, rename_threshold=50)) self.assertEqual( [TreeChange.delete((b'a', F, blob1.id)), @@ -676,8 +700,10 @@ tree1 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) tree2 = self.commit_tree([(b'c', blob3), (b'd', blob4)]) self.assertEqual( - [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'd', F, blob4.id)), - TreeChange(CHANGE_RENAME, (b'b', F, blob2.id), (b'c', F, blob3.id))], + [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), + (b'd', F, blob4.id)), + TreeChange(CHANGE_RENAME, (b'b', F, blob2.id), + (b'c', F, blob3.id))], self.detect_renames(tree1, tree2)) self.assertEqual( [TreeChange.delete((b'a', F, blob1.id)), @@ -707,13 +733,15 @@ tree2 = self.commit_tree([(b'c', blob3)]) self.assertEqual( [TreeChange.delete((b'a', F, blob1.id)), - TreeChange(CHANGE_RENAME, (b'b', F, blob2.id), (b'c', F, blob3.id))], + TreeChange(CHANGE_RENAME, (b'b', F, blob2.id), + (b'c', F, blob3.id))], self.detect_renames(tree1, tree2)) tree3 = self.commit_tree([(b'a', blob2), (b'b', blob1)]) tree4 = self.commit_tree([(b'c', blob3)]) self.assertEqual( - [TreeChange(CHANGE_RENAME, (b'a', F, blob2.id), (b'c', F, blob3.id)), + [TreeChange(CHANGE_RENAME, (b'a', F, blob2.id), + (b'c', F, blob3.id)), TreeChange.delete((b'b', F, blob1.id))], self.detect_renames(tree3, tree4)) @@ -725,7 +753,8 @@ tree2 = self.commit_tree([(b'b', blob2), (b'c', blob3)]) self.assertEqual( [TreeChange(CHANGE_COPY, (b'a', F, blob1.id), (b'b', F, blob2.id)), - TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'c', F, blob3.id))], + TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), + (b'c', F, blob3.id))], self.detect_renames(tree1, tree2)) def test_content_rename_many_to_one(self): @@ -735,7 +764,8 @@ tree1 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) tree2 = self.commit_tree([(b'c', blob3)]) self.assertEqual( - [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'c', F, blob3.id)), + [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), + (b'c', F, blob3.id)), TreeChange.delete((b'b', F, blob2.id))], self.detect_renames(tree1, tree2)) @@ -749,7 +779,8 @@ # TODO(dborowitz): Distribute renames rather than greedily choosing # copies. self.assertEqual( - [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'c', F, blob3.id)), + [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), + (b'c', F, blob3.id)), TreeChange(CHANGE_COPY, (b'a', F, blob1.id), (b'd', F, blob4.id)), TreeChange.delete((b'b', F, blob2.id))], self.detect_renames(tree1, tree2)) @@ -787,12 +818,16 @@ tree1 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) tree2 = self.commit_tree([(b'a', blob2), (b'b', blob1)]) self.assertEqual( - [TreeChange(CHANGE_MODIFY, (b'a', F, blob1.id), (b'a', F, blob2.id)), - TreeChange(CHANGE_MODIFY, (b'b', F, blob2.id), (b'b', F, blob1.id))], + [TreeChange(CHANGE_MODIFY, (b'a', F, blob1.id), + (b'a', F, blob2.id)), + TreeChange(CHANGE_MODIFY, (b'b', F, blob2.id), + (b'b', F, blob1.id))], self.detect_renames(tree1, tree2)) self.assertEqual( - [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'b', F, blob1.id)), - TreeChange(CHANGE_RENAME, (b'b', F, blob2.id), (b'a', F, blob2.id))], + [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), + (b'b', F, blob1.id)), + TreeChange(CHANGE_RENAME, (b'b', F, blob2.id), + (b'a', F, blob2.id))], self.detect_renames(tree1, tree2, rewrite_threshold=50)) def test_content_rename_swap(self): @@ -803,8 +838,10 @@ tree1 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) tree2 = self.commit_tree([(b'a', blob4), (b'b', blob3)]) self.assertEqual( - [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'b', F, blob3.id)), - TreeChange(CHANGE_RENAME, (b'b', F, blob2.id), (b'a', F, blob4.id))], + [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), + (b'b', F, blob3.id)), + TreeChange(CHANGE_RENAME, (b'b', F, blob2.id), + (b'a', F, blob4.id))], self.detect_renames(tree1, tree2, rewrite_threshold=60)) def test_rewrite_threshold(self): @@ -816,15 +853,18 @@ tree2 = self.commit_tree([(b'a', blob3), (b'b', blob2)]) no_renames = [ - TreeChange(CHANGE_MODIFY, (b'a', F, blob1.id), (b'a', F, blob3.id)), + TreeChange(CHANGE_MODIFY, (b'a', F, blob1.id), + (b'a', F, blob3.id)), TreeChange(CHANGE_COPY, (b'a', F, blob1.id), (b'b', F, blob2.id))] self.assertEqual( no_renames, self.detect_renames(tree1, tree2)) self.assertEqual( - no_renames, self.detect_renames(tree1, tree2, rewrite_threshold=40)) + no_renames, self.detect_renames( + tree1, tree2, rewrite_threshold=40)) self.assertEqual( [TreeChange.add((b'a', F, blob3.id)), - TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'b', F, blob2.id))], + TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), + (b'b', F, blob2.id))], self.detect_renames(tree1, tree2, rewrite_threshold=80)) def test_find_copies_harder_exact(self): @@ -845,7 +885,8 @@ self.assertEqual([TreeChange.add((b'b', F, blob2.id))], self.detect_renames(tree1, tree2)) self.assertEqual( - [TreeChange(CHANGE_COPY, (b'a', F, blob1.id), (b'b', F, blob2.id))], + [TreeChange(CHANGE_COPY, (b'a', F, blob1.id), + (b'b', F, blob2.id))], self.detect_renames(tree1, tree2, find_copies_harder=True)) def test_find_copies_harder_with_rewrites(self): @@ -857,7 +898,8 @@ self.assertEqual( [TreeChange(CHANGE_MODIFY, (b'a', F, blob_a1.id), (b'a', F, blob_a2.id)), - TreeChange(CHANGE_COPY, (b'a', F, blob_a1.id), (b'b', F, blob_b2.id))], + TreeChange(CHANGE_COPY, (b'a', F, blob_a1.id), + (b'b', F, blob_b2.id))], self.detect_renames(tree1, tree2, find_copies_harder=True)) self.assertEqual( [TreeChange.add((b'a', F, blob_a2.id)), diff -Nru dulwich-0.12.0/dulwich/tests/test_fastexport.py dulwich-0.18.5/dulwich/tests/test_fastexport.py --- dulwich-0.12.0/dulwich/tests/test_fastexport.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_fastexport.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,21 +1,22 @@ # test_fastexport.py -- Fast export/import functionality # Copyright (C) 2010 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. from io import BytesIO import stat @@ -28,6 +29,7 @@ Blob, Commit, Tree, + ZERO_SHA, ) from dulwich.repo import ( MemoryRepo, @@ -56,25 +58,25 @@ def test_emit_blob(self): b = Blob() - b.data = "fooBAR" + b.data = b"fooBAR" self.fastexporter.emit_blob(b) - self.assertEqual('blob\nmark :1\ndata 6\nfooBAR\n', - self.stream.getvalue()) + self.assertEqual(b'blob\nmark :1\ndata 6\nfooBAR\n', + self.stream.getvalue()) def test_emit_commit(self): b = Blob() - b.data = "FOO" + b.data = b"FOO" t = Tree() - t.add("foo", stat.S_IFREG | 0o644, b.id) + t.add(b"foo", stat.S_IFREG | 0o644, b.id) c = Commit() - c.committer = c.author = "Jelmer " + c.committer = c.author = b"Jelmer " c.author_time = c.commit_time = 1271345553 c.author_timezone = c.commit_timezone = 0 - c.message = "msg" + c.message = b"msg" c.tree = t.id self.store.add_objects([(b, None), (t, None), (c, None)]) - self.fastexporter.emit_commit(c, "refs/heads/master") - self.assertEqual("""blob + self.fastexporter.emit_commit(c, b"refs/heads/master") + self.assertEqual(b"""blob mark :1 data 3 FOO @@ -84,7 +86,7 @@ committer Jelmer 1271345553 +0000 data 3 msg -M 644 1 foo +M 644 :1 foo """, self.stream.getvalue()) @@ -103,30 +105,47 @@ def test_reset_handler(self): from fastimport import commands [c1] = build_commit_graph(self.repo.object_store, [[1]]) - cmd = commands.ResetCommand("refs/heads/foo", c1.id) + cmd = commands.ResetCommand(b"refs/heads/foo", c1.id) + self.processor.reset_handler(cmd) + self.assertEqual(c1.id, self.repo.get_refs()[b"refs/heads/foo"]) + self.assertEqual(c1.id, self.processor.last_commit) + + def test_reset_handler_marker(self): + from fastimport import commands + [c1, c2] = build_commit_graph(self.repo.object_store, [[1], [2]]) + self.processor.markers[b'10'] = c1.id + cmd = commands.ResetCommand(b"refs/heads/foo", b':10') + self.processor.reset_handler(cmd) + self.assertEqual(c1.id, self.repo.get_refs()[b"refs/heads/foo"]) + + def test_reset_handler_default(self): + from fastimport import commands + [c1, c2] = build_commit_graph(self.repo.object_store, [[1], [2]]) + cmd = commands.ResetCommand(b"refs/heads/foo", None) self.processor.reset_handler(cmd) - self.assertEqual(c1.id, self.repo.get_refs()["refs/heads/foo"]) + self.assertEqual(ZERO_SHA, self.repo.get_refs()[b"refs/heads/foo"]) def test_commit_handler(self): from fastimport import commands - cmd = commands.CommitCommand("refs/heads/foo", "mrkr", - ("Jelmer", "jelmer@samba.org", 432432432.0, 3600), - ("Jelmer", "jelmer@samba.org", 432432432.0, 3600), - "FOO", None, [], []) + cmd = commands.CommitCommand( + b"refs/heads/foo", b"mrkr", + (b"Jelmer", b"jelmer@samba.org", 432432432.0, 3600), + (b"Jelmer", b"jelmer@samba.org", 432432432.0, 3600), + b"FOO", None, [], []) self.processor.commit_handler(cmd) commit = self.repo[self.processor.last_commit] - self.assertEqual("Jelmer ", commit.author) - self.assertEqual("Jelmer ", commit.committer) - self.assertEqual("FOO", commit.message) + self.assertEqual(b"Jelmer ", commit.author) + self.assertEqual(b"Jelmer ", commit.committer) + self.assertEqual(b"FOO", commit.message) self.assertEqual([], commit.parents) self.assertEqual(432432432.0, commit.commit_time) self.assertEqual(432432432.0, commit.author_time) self.assertEqual(3600, commit.commit_timezone) self.assertEqual(3600, commit.author_timezone) - self.assertEqual(commit, self.repo["refs/heads/foo"]) + self.assertEqual(commit, self.repo[b"refs/heads/foo"]) def test_import_stream(self): - markers = self.processor.import_stream(BytesIO("""blob + markers = self.processor.import_stream(BytesIO(b"""blob mark :1 data 11 text for a @@ -140,31 +159,35 @@ """)) self.assertEqual(2, len(markers)) - self.assertTrue(isinstance(self.repo[markers["1"]], Blob)) - self.assertTrue(isinstance(self.repo[markers["2"]], Commit)) + self.assertTrue(isinstance(self.repo[markers[b"1"]], Blob)) + self.assertTrue(isinstance(self.repo[markers[b"2"]], Commit)) def test_file_add(self): from fastimport import commands - cmd = commands.BlobCommand("23", "data") + cmd = commands.BlobCommand(b"23", b"data") self.processor.blob_handler(cmd) - cmd = commands.CommitCommand("refs/heads/foo", "mrkr", - ("Jelmer", "jelmer@samba.org", 432432432.0, 3600), - ("Jelmer", "jelmer@samba.org", 432432432.0, 3600), - "FOO", None, [], [commands.FileModifyCommand("path", 0o100644, ":23", None)]) + cmd = commands.CommitCommand( + b"refs/heads/foo", b"mrkr", + (b"Jelmer", b"jelmer@samba.org", 432432432.0, 3600), + (b"Jelmer", b"jelmer@samba.org", 432432432.0, 3600), + b"FOO", None, [], + [commands.FileModifyCommand(b"path", 0o100644, b":23", None)]) self.processor.commit_handler(cmd) commit = self.repo[self.processor.last_commit] self.assertEqual([ - ('path', 0o100644, '6320cd248dd8aeaab759d5871f8781b5c0505172')], + (b'path', 0o100644, b'6320cd248dd8aeaab759d5871f8781b5c0505172')], self.repo[commit.tree].items()) def simple_commit(self): from fastimport import commands - cmd = commands.BlobCommand("23", "data") + cmd = commands.BlobCommand(b"23", b"data") self.processor.blob_handler(cmd) - cmd = commands.CommitCommand("refs/heads/foo", "mrkr", - ("Jelmer", "jelmer@samba.org", 432432432.0, 3600), - ("Jelmer", "jelmer@samba.org", 432432432.0, 3600), - "FOO", None, [], [commands.FileModifyCommand("path", 0o100644, ":23", None)]) + cmd = commands.CommitCommand( + b"refs/heads/foo", b"mrkr", + (b"Jelmer", b"jelmer@samba.org", 432432432.0, 3600), + (b"Jelmer", b"jelmer@samba.org", 432432432.0, 3600), + b"FOO", None, [], + [commands.FileModifyCommand(b"path", 0o100644, b":23", None)]) self.processor.commit_handler(cmd) commit = self.repo[self.processor.last_commit] return commit @@ -176,34 +199,40 @@ :return: The created commit object """ from fastimport import commands - cmd = commands.CommitCommand("refs/heads/foo", "mrkr", - ("Jelmer", "jelmer@samba.org", 432432432.0, 3600), - ("Jelmer", "jelmer@samba.org", 432432432.0, 3600), - "FOO", None, [], file_cmds) + cmd = commands.CommitCommand( + b"refs/heads/foo", b"mrkr", + (b"Jelmer", b"jelmer@samba.org", 432432432.0, 3600), + (b"Jelmer", b"jelmer@samba.org", 432432432.0, 3600), + b"FOO", None, [], file_cmds) self.processor.commit_handler(cmd) return self.repo[self.processor.last_commit] def test_file_copy(self): from fastimport import commands self.simple_commit() - commit = self.make_file_commit([commands.FileCopyCommand("path", "new_path")]) + commit = self.make_file_commit( + [commands.FileCopyCommand(b"path", b"new_path")]) self.assertEqual([ - ('new_path', 0o100644, '6320cd248dd8aeaab759d5871f8781b5c0505172'), - ('path', 0o100644, '6320cd248dd8aeaab759d5871f8781b5c0505172'), - ], self.repo[commit.tree].items()) + (b'new_path', 0o100644, + b'6320cd248dd8aeaab759d5871f8781b5c0505172'), + (b'path', 0o100644, + b'6320cd248dd8aeaab759d5871f8781b5c0505172'), + ], self.repo[commit.tree].items()) def test_file_move(self): from fastimport import commands self.simple_commit() - commit = self.make_file_commit([commands.FileRenameCommand("path", "new_path")]) + commit = self.make_file_commit( + [commands.FileRenameCommand(b"path", b"new_path")]) self.assertEqual([ - ('new_path', 0o100644, '6320cd248dd8aeaab759d5871f8781b5c0505172'), - ], self.repo[commit.tree].items()) + (b'new_path', 0o100644, + b'6320cd248dd8aeaab759d5871f8781b5c0505172'), + ], self.repo[commit.tree].items()) def test_file_delete(self): from fastimport import commands self.simple_commit() - commit = self.make_file_commit([commands.FileDeleteCommand("path")]) + commit = self.make_file_commit([commands.FileDeleteCommand(b"path")]) self.assertEqual([], self.repo[commit.tree].items()) def test_file_deleteall(self): diff -Nru dulwich-0.12.0/dulwich/tests/test_file.py dulwich-0.18.5/dulwich/tests/test_file.py --- dulwich-0.12.0/dulwich/tests/test_file.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_file.py 2017-10-18 23:23:32.000000000 +0000 @@ -1,29 +1,30 @@ # test_file.py -- Test for git files # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. -import errno import io import os import shutil import sys import tempfile -from dulwich.file import GitFile, _fancy_rename +from dulwich.file import FileLocked, GitFile, _fancy_rename from dulwich.tests import ( SkipTest, TestCase, @@ -156,8 +157,8 @@ try: f2 = GitFile(foo, 'wb') self.fail() - except OSError as e: - self.assertEqual(errno.EEXIST, e.errno) + except FileLocked: + pass else: f2.close() f1.write(b' contents') diff -Nru dulwich-0.12.0/dulwich/tests/test_grafts.py dulwich-0.18.5/dulwich/tests/test_grafts.py --- dulwich-0.12.0/dulwich/tests/test_grafts.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_grafts.py 2017-07-29 00:14:28.000000000 +0000 @@ -1,19 +1,21 @@ # test_grafts.py -- Tests for graftpoints # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for graftpoints.""" diff -Nru dulwich-0.12.0/dulwich/tests/test_greenthreads.py dulwich-0.18.5/dulwich/tests/test_greenthreads.py --- dulwich-0.12.0/dulwich/tests/test_greenthreads.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_greenthreads.py 2017-10-07 15:45:18.000000000 +0000 @@ -3,21 +3,22 @@ # # Author: Fabien Boucher # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. import time @@ -37,7 +38,7 @@ ) try: - import gevent + import gevent # noqa: F401 gevent_support = True except ImportError: gevent_support = False @@ -50,15 +51,16 @@ skipmsg = "Gevent library is not installed" + def create_commit(marker=None): - blob = Blob.from_string('The blob content %s' % marker) + blob = Blob.from_string(b'The blob content ' + marker) tree = Tree() - tree.add("thefile %s" % marker, 0o100644, blob.id) + tree.add(b"thefile " + marker, 0o100644, blob.id) cmt = Commit() cmt.tree = tree.id - cmt.author = cmt.committer = "John Doe " - cmt.message = "%s" % marker - tz = parse_timezone('-0200')[0] + cmt.author = cmt.committer = b"John Doe " + cmt.message = marker + tz = parse_timezone(b'-0200')[0] cmt.commit_time = cmt.author_time = int(time.time()) cmt.commit_timezone = cmt.author_timezone = tz return cmt, tree, blob @@ -67,7 +69,7 @@ def init_store(store, count=1): ret = [] for i in range(0, count): - objs = create_commit(marker=i) + objs = create_commit(marker=("%d" % i).encode('ascii')) for obj in objs: ret.append(obj) store.add_object(obj) @@ -86,24 +88,21 @@ def test_len(self): wants = [sha.id for sha in self.objs if isinstance(sha, Commit)] finder = MissingObjectFinder(self.store, (), wants) - iterator = GreenThreadsObjectStoreIterator(self.store, - iter(finder.next, None), - finder) + iterator = GreenThreadsObjectStoreIterator( + self.store, iter(finder.next, None), finder) # One commit refers one tree and one blob self.assertEqual(len(iterator), self.cmt_amount * 3) haves = wants[0:self.cmt_amount-1] finder = MissingObjectFinder(self.store, haves, wants) - iterator = GreenThreadsObjectStoreIterator(self.store, - iter(finder.next, None), - finder) + iterator = GreenThreadsObjectStoreIterator( + self.store, iter(finder.next, None), finder) self.assertEqual(len(iterator), 3) def test_iter(self): wants = [sha.id for sha in self.objs if isinstance(sha, Commit)] finder = MissingObjectFinder(self.store, (), wants) - iterator = GreenThreadsObjectStoreIterator(self.store, - iter(finder.next, None), - finder) + iterator = GreenThreadsObjectStoreIterator( + self.store, iter(finder.next, None), finder) objs = [] for sha, path in iterator: self.assertIn(sha, self.objs) @@ -126,9 +125,8 @@ self.assertEqual(len(finder.sha_done), 0) self.assertEqual(len(finder.objects_to_send), self.cmt_amount) - finder = GreenThreadsMissingObjectFinder(self.store, - wants[0:self.cmt_amount/2], - wants) + finder = GreenThreadsMissingObjectFinder( + self.store, wants[0:int(self.cmt_amount/2)], wants) # sha_done will contains commit id and sha of blob refered in tree self.assertEqual(len(finder.sha_done), (self.cmt_amount/2)*2) self.assertEqual(len(finder.objects_to_send), self.cmt_amount/2) diff -Nru dulwich-0.12.0/dulwich/tests/test_hooks.py dulwich-0.18.5/dulwich/tests/test_hooks.py --- dulwich-0.12.0/dulwich/tests/test_hooks.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_hooks.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,26 +1,27 @@ # test_hooks.py -- Tests for executing hooks # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# or (at your option) a later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for executing hooks.""" import os import stat import shutil -import sys import tempfile from dulwich import errors @@ -42,6 +43,10 @@ self.skipTest('shell hook tests requires POSIX shell') def test_hook_pre_commit(self): + repo_dir = os.path.join(tempfile.mkdtemp()) + os.mkdir(os.path.join(repo_dir, 'hooks')) + self.addCleanup(shutil.rmtree, repo_dir) + pre_commit_fail = """#!/bin/sh exit 1 """ @@ -49,10 +54,8 @@ pre_commit_success = """#!/bin/sh exit 0 """ - - repo_dir = os.path.join(tempfile.mkdtemp()) - os.mkdir(os.path.join(repo_dir, 'hooks')) - self.addCleanup(shutil.rmtree, repo_dir) + pre_commit_cwd = """#!/bin/sh +if [ "$(pwd)" = '""" + repo_dir + "' ]; then exit 0; else exit 1; fi\n" pre_commit = os.path.join(repo_dir, 'hooks', 'pre-commit') hook = PreCommitShellHook(repo_dir) @@ -64,6 +67,12 @@ self.assertRaises(errors.HookError, hook.execute) with open(pre_commit, 'w') as f: + f.write(pre_commit_cwd) + os.chmod(pre_commit, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) + + hook.execute() + + with open(pre_commit, 'w') as f: f.write(pre_commit_success) os.chmod(pre_commit, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) @@ -71,6 +80,10 @@ def test_hook_commit_msg(self): + repo_dir = os.path.join(tempfile.mkdtemp()) + os.mkdir(os.path.join(repo_dir, 'hooks')) + self.addCleanup(shutil.rmtree, repo_dir) + commit_msg_fail = """#!/bin/sh exit 1 """ @@ -79,9 +92,8 @@ exit 0 """ - repo_dir = os.path.join(tempfile.mkdtemp()) - os.mkdir(os.path.join(repo_dir, 'hooks')) - self.addCleanup(shutil.rmtree, repo_dir) + commit_msg_cwd = """#!/bin/sh +if [ "$(pwd)" = '""" + repo_dir + "' ]; then exit 0; else exit 1; fi\n" commit_msg = os.path.join(repo_dir, 'hooks', 'commit-msg') hook = CommitMsgShellHook(repo_dir) @@ -93,6 +105,12 @@ self.assertRaises(errors.HookError, hook.execute, b'failed commit') with open(commit_msg, 'w') as f: + f.write(commit_msg_cwd) + os.chmod(commit_msg, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) + + hook.execute(b'cwd test commit') + + with open(commit_msg, 'w') as f: f.write(commit_msg_success) os.chmod(commit_msg, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) @@ -103,28 +121,37 @@ (fd, path) = tempfile.mkstemp() os.close(fd) - post_commit_msg = """#!/bin/sh + repo_dir = os.path.join(tempfile.mkdtemp()) + os.mkdir(os.path.join(repo_dir, 'hooks')) + self.addCleanup(shutil.rmtree, repo_dir) + + post_commit_success = """#!/bin/sh rm """ + path + "\n" - post_commit_msg_fail = """#!/bin/sh + post_commit_fail = """#!/bin/sh exit 1 """ - repo_dir = os.path.join(tempfile.mkdtemp()) - os.mkdir(os.path.join(repo_dir, 'hooks')) - self.addCleanup(shutil.rmtree, repo_dir) + post_commit_cwd = """#!/bin/sh +if [ "$(pwd)" = '""" + repo_dir + "' ]; then exit 0; else exit 1; fi\n" post_commit = os.path.join(repo_dir, 'hooks', 'post-commit') hook = PostCommitShellHook(repo_dir) with open(post_commit, 'w') as f: - f.write(post_commit_msg_fail) + f.write(post_commit_fail) os.chmod(post_commit, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) self.assertRaises(errors.HookError, hook.execute) with open(post_commit, 'w') as f: - f.write(post_commit_msg) + f.write(post_commit_cwd) + os.chmod(post_commit, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) + + hook.execute() + + with open(post_commit, 'w') as f: + f.write(post_commit_success) os.chmod(post_commit, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) hook.execute() diff -Nru dulwich-0.12.0/dulwich/tests/test_ignore.py dulwich-0.18.5/dulwich/tests/test_ignore.py --- dulwich-0.12.0/dulwich/tests/test_ignore.py 1970-01-01 00:00:00.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_ignore.py 2017-10-07 15:45:18.000000000 +0000 @@ -0,0 +1,260 @@ +# test_ignore.py -- Tests for ignore files. +# Copyright (C) 2017 Jelmer Vernooij +# +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. +# + +"""Tests for ignore files.""" + +from io import BytesIO +import os +import re +import shutil +import tempfile +from dulwich.tests import TestCase + +from dulwich.ignore import ( + IgnoreFilter, + IgnoreFilterManager, + IgnoreFilterStack, + Pattern, + match_pattern, + read_ignore_patterns, + translate, + ) +from dulwich.repo import Repo + + +POSITIVE_MATCH_TESTS = [ + (b"foo.c", b"*.c"), + (b".c", b"*.c"), + (b"foo/foo.c", b"*.c"), + (b"foo/foo.c", b"foo.c"), + (b"foo.c", b"/*.c"), + (b"foo.c", b"/foo.c"), + (b"foo.c", b"foo.c"), + (b"foo.c", b"foo.[ch]"), + (b"foo/bar/bla.c", b"foo/**"), + (b"foo/bar/bla/blie.c", b"foo/**/blie.c"), + (b"foo/bar/bla.c", b"**/bla.c"), + (b"bla.c", b"**/bla.c"), + (b"foo/bar", b"foo/**/bar"), + (b"foo/bla/bar", b"foo/**/bar"), + (b"foo/bar/", b"bar/"), + (b"foo/bar/", b"bar"), + (b"foo/bar/something", b"foo/bar/*"), +] + +NEGATIVE_MATCH_TESTS = [ + (b"foo.c", b"foo.[dh]"), + (b"foo/foo.c", b"/foo.c"), + (b"foo/foo.c", b"/*.c"), + (b"foo/bar/", b"/bar/"), + (b"foo/bar/", b"foo/bar/*"), +] + + +TRANSLATE_TESTS = [ + (b"*.c", b'(?ms)(.*/)?[^/]*\\.c/?\\Z'), + (b"foo.c", b'(?ms)(.*/)?foo\\.c/?\\Z'), + (b"/*.c", b'(?ms)[^/]*\\.c/?\\Z'), + (b"/foo.c", b'(?ms)foo\\.c/?\\Z'), + (b"foo.c", b'(?ms)(.*/)?foo\\.c/?\\Z'), + (b"foo.[ch]", b'(?ms)(.*/)?foo\\.[ch]/?\\Z'), + (b"bar/", b'(?ms)(.*/)?bar\\/\\Z'), + (b"foo/**", b'(?ms)foo(/.*)?/?\\Z'), + (b"foo/**/blie.c", b'(?ms)foo(/.*)?\\/blie\\.c/?\\Z'), + (b"**/bla.c", b'(?ms)(.*/)?bla\\.c/?\\Z'), + (b"foo/**/bar", b'(?ms)foo(/.*)?\\/bar/?\\Z'), + (b"foo/bar/*", b'(?ms)foo\\/bar\\/[^/]+/?\\Z'), +] + + +class TranslateTests(TestCase): + + def test_translate(self): + for (pattern, regex) in TRANSLATE_TESTS: + if re.escape(b'/') == b'/': + # Slash is no longer escaped in Python3.7, so undo the escaping + # in the expected return value.. + regex = regex.replace(b'\\/', b'/') + self.assertEqual( + regex, translate(pattern), + "orig pattern: %r, regex: %r, expected: %r" % + (pattern, translate(pattern), regex)) + + +class ReadIgnorePatterns(TestCase): + + def test_read_file(self): + f = BytesIO(b""" +# a comment + +# and an empty line: + +\#not a comment +!negative +with trailing whitespace +with escaped trailing whitespace\ +""") # noqa: W291 + self.assertEqual(list(read_ignore_patterns(f)), [ + b'\\#not a comment', + b'!negative', + b'with trailing whitespace', + b'with escaped trailing whitespace ' + ]) + + +class MatchPatternTests(TestCase): + + def test_matches(self): + for (path, pattern) in POSITIVE_MATCH_TESTS: + self.assertTrue( + match_pattern(path, pattern), + "path: %r, pattern: %r" % (path, pattern)) + + def test_no_matches(self): + for (path, pattern) in NEGATIVE_MATCH_TESTS: + self.assertFalse( + match_pattern(path, pattern), + "path: %r, pattern: %r" % (path, pattern)) + + +class IgnoreFilterTests(TestCase): + + def test_included(self): + filter = IgnoreFilter([b'a.c', b'b.c']) + self.assertTrue(filter.is_ignored(b'a.c')) + self.assertIs(None, filter.is_ignored(b'c.c')) + self.assertEqual( + [Pattern(b'a.c')], + list(filter.find_matching(b'a.c'))) + self.assertEqual( + [], + list(filter.find_matching(b'c.c'))) + + def test_included_ignorecase(self): + filter = IgnoreFilter([b'a.c', b'b.c'], ignorecase=False) + self.assertTrue(filter.is_ignored(b'a.c')) + self.assertFalse(filter.is_ignored(b'A.c')) + filter = IgnoreFilter([b'a.c', b'b.c'], ignorecase=True) + self.assertTrue(filter.is_ignored(b'a.c')) + self.assertTrue(filter.is_ignored(b'A.c')) + self.assertTrue(filter.is_ignored(b'A.C')) + + def test_excluded(self): + filter = IgnoreFilter([b'a.c', b'b.c', b'!c.c']) + self.assertFalse(filter.is_ignored(b'c.c')) + self.assertIs(None, filter.is_ignored(b'd.c')) + self.assertEqual( + [Pattern(b'!c.c')], + list(filter.find_matching(b'c.c'))) + self.assertEqual([], list(filter.find_matching(b'd.c'))) + + def test_include_exclude_include(self): + filter = IgnoreFilter([b'a.c', b'!a.c', b'a.c']) + self.assertTrue(filter.is_ignored(b'a.c')) + self.assertEqual( + [Pattern(b'a.c'), Pattern(b'!a.c'), Pattern(b'a.c')], + list(filter.find_matching(b'a.c'))) + + def test_manpage(self): + # A specific example from the gitignore manpage + filter = IgnoreFilter([ + b'/*', + b'!/foo', + b'/foo/*', + b'!/foo/bar']) + self.assertTrue(filter.is_ignored(b'a.c')) + self.assertTrue(filter.is_ignored(b'foo/blie')) + self.assertFalse(filter.is_ignored(b'foo')) + self.assertFalse(filter.is_ignored(b'foo/bar')) + self.assertFalse(filter.is_ignored(b'foo/bar/')) + self.assertFalse(filter.is_ignored(b'foo/bar/bloe')) + + +class IgnoreFilterStackTests(TestCase): + + def test_stack_first(self): + filter1 = IgnoreFilter([b'[a].c', b'[b].c', b'![d].c']) + filter2 = IgnoreFilter([b'[a].c', b'![b],c', b'[c].c', b'[d].c']) + stack = IgnoreFilterStack([filter1, filter2]) + self.assertIs(True, stack.is_ignored(b'a.c')) + self.assertIs(True, stack.is_ignored(b'b.c')) + self.assertIs(True, stack.is_ignored(b'c.c')) + self.assertIs(False, stack.is_ignored(b'd.c')) + self.assertIs(None, stack.is_ignored(b'e.c')) + + +class IgnoreFilterManagerTests(TestCase): + + def test_load_ignore(self): + tmp_dir = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, tmp_dir) + repo = Repo.init(tmp_dir) + with open(os.path.join(repo.path, '.gitignore'), 'wb') as f: + f.write(b'/foo/bar\n') + f.write(b'/dir2\n') + f.write(b'/dir3/\n') + os.mkdir(os.path.join(repo.path, 'dir')) + with open(os.path.join(repo.path, 'dir', '.gitignore'), 'wb') as f: + f.write(b'/blie\n') + with open(os.path.join(repo.path, 'dir', 'blie'), 'wb') as f: + f.write(b'IGNORED') + p = os.path.join(repo.controldir(), 'info', 'exclude') + with open(p, 'wb') as f: + f.write(b'/excluded\n') + m = IgnoreFilterManager.from_repo(repo) + self.assertTrue(m.is_ignored('dir/blie')) + self.assertIs(None, + m.is_ignored(os.path.join('dir', 'bloe'))) + self.assertIs(None, m.is_ignored('dir')) + self.assertTrue(m.is_ignored(os.path.join('foo', 'bar'))) + self.assertTrue(m.is_ignored(os.path.join('excluded'))) + self.assertTrue(m.is_ignored(os.path.join( + 'dir2', 'fileinignoreddir'))) + self.assertFalse(m.is_ignored('dir3')) + self.assertTrue(m.is_ignored('dir3/')) + self.assertTrue(m.is_ignored('dir3/bla')) + + def test_load_ignore_ignorecase(self): + tmp_dir = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, tmp_dir) + repo = Repo.init(tmp_dir) + config = repo.get_config() + config.set(b'core', b'ignorecase', True) + config.write_to_path() + with open(os.path.join(repo.path, '.gitignore'), 'wb') as f: + f.write(b'/foo/bar\n') + f.write(b'/dir\n') + m = IgnoreFilterManager.from_repo(repo) + self.assertTrue(m.is_ignored(os.path.join('dir', 'blie'))) + self.assertTrue(m.is_ignored(os.path.join('DIR', 'blie'))) + + def test_ignored_contents(self): + tmp_dir = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, tmp_dir) + repo = Repo.init(tmp_dir) + with open(os.path.join(repo.path, '.gitignore'), 'wb') as f: + f.write(b'a/*\n') + f.write(b'!a/*.txt\n') + m = IgnoreFilterManager.from_repo(repo) + os.mkdir(os.path.join(repo.path, 'a')) + self.assertIs(None, m.is_ignored('a')) + self.assertIs(None, m.is_ignored('a/')) + self.assertFalse(m.is_ignored('a/b.txt')) + self.assertTrue(m.is_ignored('a/c.dat')) diff -Nru dulwich-0.12.0/dulwich/tests/test_index.py dulwich-0.18.5/dulwich/tests/test_index.py --- dulwich-0.12.0/dulwich/tests/test_index.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_index.py 2017-10-07 15:45:18.000000000 +0000 @@ -3,25 +3,26 @@ # encoding: utf-8 # Copyright (C) 2008-2009 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# or (at your option) any later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for the index.""" -from contextlib import closing from io import BytesIO import os import shutil @@ -52,7 +53,9 @@ ) from dulwich.objects import ( Blob, + Commit, Tree, + S_IFGITLINK, ) from dulwich.repo import Repo from dulwich.tests import ( @@ -60,6 +63,7 @@ skipIf, ) + class IndexTestCase(TestCase): datadir = os.path.join(os.path.dirname(__file__), 'data/indexes') @@ -77,10 +81,11 @@ self.assertEqual([b'bla'], list(self.get_simple_index("index"))) def test_getitem(self): - self.assertEqual(((1230680220, 0), (1230680220, 0), 2050, 3761020, - 33188, 1000, 1000, 0, - b'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391', 0), - self.get_simple_index("index")[b"bla"]) + self.assertEqual( + ((1230680220, 0), (1230680220, 0), 2050, 3761020, + 33188, 1000, 1000, 0, + b'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391', 0), + self.get_simple_index("index")[b"bla"]) def test_empty(self): i = self.get_simple_index("notanindex") @@ -95,6 +100,7 @@ self.assertEqual(b'bla', newname) self.assertEqual(b'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391', newsha) + class SimpleIndexWriterTestCase(IndexTestCase): def setUp(self): @@ -128,9 +134,11 @@ shutil.rmtree(self.tempdir) def test_simple_write(self): - entries = {b'barbla': ((1230680220, 0), (1230680220, 0), 2050, 3761020, - 33188, 1000, 1000, 0, - b'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391', 0)} + entries = { + b'barbla': + ((1230680220, 0), (1230680220, 0), 2050, 3761020, 33188, + 1000, 1000, 0, + b'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391', 0)} filename = os.path.join(self.tempdir, 'test-simple-write-index') with open(filename, 'wb+') as x: write_index_dict(x, entries) @@ -167,7 +175,7 @@ self.assertEqual((stat.S_IFDIR, dirid), self.store[rootid][b"bla"]) self.assertEqual((stat.S_IFREG, blob.id), self.store[dirid][b"bar"]) self.assertEqual(set([rootid, dirid, blob.id]), - set(self.store._data.keys())) + set(self.store._data.keys())) class CleanupModeTests(TestCase): @@ -213,9 +221,9 @@ class IndexEntryFromStatTests(TestCase): def test_simple(self): - st = os.stat_result((16877, 131078, 64769, - 154, 1000, 1000, 12288, - 1323629595, 1324180496, 1324180496)) + st = os.stat_result( + (16877, 131078, 64769, 154, 1000, 1000, 12288, + 1323629595, 1324180496, 1324180496)) entry = index_entry_from_stat(st, "22" * 20, 0) self.assertEqual(entry, ( 1324180496, @@ -230,11 +238,12 @@ 0)) def test_override_mode(self): - st = os.stat_result((stat.S_IFREG + 0o644, 131078, 64769, - 154, 1000, 1000, 12288, - 1323629595, 1324180496, 1324180496)) - entry = index_entry_from_stat(st, "22" * 20, 0, - mode=stat.S_IFREG + 0o755) + st = os.stat_result( + (stat.S_IFREG + 0o644, 131078, 64769, + 154, 1000, 1000, 12288, + 1323629595, 1324180496, 1324180496)) + entry = index_entry_from_stat( + st, "22" * 20, 0, mode=stat.S_IFREG + 0o755) self.assertEqual(entry, ( 1324180496, 1324180496, @@ -265,11 +274,12 @@ def test_empty(self): repo_dir = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, repo_dir) - with closing(Repo.init(repo_dir)) as repo: + with Repo.init(repo_dir) as repo: tree = Tree() repo.object_store.add_object(tree) - build_index_from_tree(repo.path, repo.index_path(), + build_index_from_tree( + repo.path, repo.index_path(), repo.object_store, tree.id) # Verify index entries @@ -282,7 +292,7 @@ def test_git_dir(self): repo_dir = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, repo_dir) - with closing(Repo.init(repo_dir)) as repo: + with Repo.init(repo_dir) as repo: # Populate repo filea = Blob.from_string(b'file a') @@ -292,11 +302,11 @@ tree[b'.git/a'] = (stat.S_IFREG | 0o644, filea.id) tree[b'c/e'] = (stat.S_IFREG | 0o644, filee.id) - repo.object_store.add_objects([(o, None) - for o in [filea, filee, tree]]) + repo.object_store.add_objects( + [(o, None) for o in [filea, filee, tree]]) - build_index_from_tree(repo.path, repo.index_path(), - repo.object_store, tree.id) + build_index_from_tree( + repo.path, repo.index_path(), repo.object_store, tree.id) # Verify index entries index = repo.open_index() @@ -309,14 +319,14 @@ # filee epath = os.path.join(repo.path, 'c', 'e') self.assertTrue(os.path.exists(epath)) - self.assertReasonableIndexEntry(index[b'c/e'], - stat.S_IFREG | 0o644, 1, filee.id) + self.assertReasonableIndexEntry( + index[b'c/e'], stat.S_IFREG | 0o644, 1, filee.id) self.assertFileContents(epath, b'd') def test_nonempty(self): repo_dir = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, repo_dir) - with closing(Repo.init(repo_dir)) as repo: + with Repo.init(repo_dir) as repo: # Populate repo filea = Blob.from_string(b'file a') @@ -328,11 +338,11 @@ tree[b'b'] = (stat.S_IFREG | 0o644, fileb.id) tree[b'c/d'] = (stat.S_IFREG | 0o644, filed.id) - repo.object_store.add_objects([(o, None) - for o in [filea, fileb, filed, tree]]) + repo.object_store.add_objects( + [(o, None) for o in [filea, fileb, filed, tree]]) - build_index_from_tree(repo.path, repo.index_path(), - repo.object_store, tree.id) + build_index_from_tree( + repo.path, repo.index_path(), repo.object_store, tree.id) # Verify index entries index = repo.open_index() @@ -341,35 +351,74 @@ # filea apath = os.path.join(repo.path, 'a') self.assertTrue(os.path.exists(apath)) - self.assertReasonableIndexEntry(index[b'a'], - stat.S_IFREG | 0o644, 6, filea.id) + self.assertReasonableIndexEntry( + index[b'a'], stat.S_IFREG | 0o644, 6, filea.id) self.assertFileContents(apath, b'file a') # fileb bpath = os.path.join(repo.path, 'b') self.assertTrue(os.path.exists(bpath)) - self.assertReasonableIndexEntry(index[b'b'], - stat.S_IFREG | 0o644, 6, fileb.id) + self.assertReasonableIndexEntry( + index[b'b'], stat.S_IFREG | 0o644, 6, fileb.id) self.assertFileContents(bpath, b'file b') # filed dpath = os.path.join(repo.path, 'c', 'd') self.assertTrue(os.path.exists(dpath)) - self.assertReasonableIndexEntry(index[b'c/d'], - stat.S_IFREG | 0o644, 6, filed.id) + self.assertReasonableIndexEntry( + index[b'c/d'], stat.S_IFREG | 0o644, 6, filed.id) self.assertFileContents(dpath, b'file d') # Verify no extra files - self.assertEqual(['.git', 'a', 'b', 'c'], - sorted(os.listdir(repo.path))) - self.assertEqual(['d'], - sorted(os.listdir(os.path.join(repo.path, 'c')))) + self.assertEqual( + ['.git', 'a', 'b', 'c'], sorted(os.listdir(repo.path))) + self.assertEqual( + ['d'], sorted(os.listdir(os.path.join(repo.path, 'c')))) + + @skipIf(not getattr(os, 'sync', None), 'Requires sync support') + def test_norewrite(self): + repo_dir = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, repo_dir) + with Repo.init(repo_dir) as repo: + # Populate repo + filea = Blob.from_string(b'file a') + filea_path = os.path.join(repo_dir, 'a') + tree = Tree() + tree[b'a'] = (stat.S_IFREG | 0o644, filea.id) + + repo.object_store.add_objects([(o, None) for o in [filea, tree]]) + + # First Write + build_index_from_tree(repo.path, repo.index_path(), + repo.object_store, tree.id) + # Use sync as metadata can be cached on some FS + os.sync() + mtime = os.stat(filea_path).st_mtime + + # Test Rewrite + build_index_from_tree(repo.path, repo.index_path(), + repo.object_store, tree.id) + os.sync() + self.assertEqual(mtime, os.stat(filea_path).st_mtime) + + # Modify content + with open(filea_path, 'wb') as fh: + fh.write(b'test a') + os.sync() + mtime = os.stat(filea_path).st_mtime + + # Test rewrite + build_index_from_tree(repo.path, repo.index_path(), + repo.object_store, tree.id) + os.sync() + with open(filea_path, 'rb') as fh: + self.assertEqual(b'file a', fh.read()) @skipIf(not getattr(os, 'symlink', None), 'Requires symlink support') def test_symlink(self): repo_dir = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, repo_dir) - with closing(Repo.init(repo_dir)) as repo: + with Repo.init(repo_dir) as repo: # Populate repo filed = Blob.from_string(b'file d') @@ -379,11 +428,11 @@ tree[b'c/d'] = (stat.S_IFREG | 0o644, filed.id) tree[b'c/e'] = (stat.S_IFLNK, filee.id) # symlink - repo.object_store.add_objects([(o, None) - for o in [filed, filee, tree]]) + repo.object_store.add_objects( + [(o, None) for o in [filed, filee, tree]]) - build_index_from_tree(repo.path, repo.index_path(), - repo.object_store, tree.id) + build_index_from_tree( + repo.path, repo.index_path(), repo.object_store, tree.id) # Verify index entries index = repo.open_index() @@ -401,33 +450,123 @@ repo_dir = tempfile.mkdtemp() repo_dir_bytes = repo_dir.encode(sys.getfilesystemencoding()) self.addCleanup(shutil.rmtree, repo_dir) - with closing(Repo.init(repo_dir)) as repo: + with Repo.init(repo_dir) as repo: # Populate repo file = Blob.from_string(b'foo') tree = Tree() latin1_name = u'À'.encode('latin1') + latin1_path = os.path.join(repo_dir_bytes, latin1_name) utf8_name = u'À'.encode('utf8') + utf8_path = os.path.join(repo_dir_bytes, utf8_name) tree[latin1_name] = (stat.S_IFREG | 0o644, file.id) tree[utf8_name] = (stat.S_IFREG | 0o644, file.id) repo.object_store.add_objects( [(o, None) for o in [file, tree]]) + try: + os.path.exists(latin1_path) + except UnicodeDecodeError: + # This happens e.g. with python3.6 on Windows. + # It implicitly decodes using utf8, which doesn't work. + self.skipTest('can not implicitly convert as utf8') + build_index_from_tree( repo.path, repo.index_path(), repo.object_store, tree.id) # Verify index entries index = repo.open_index() + self.assertIn(latin1_name, index) + self.assertIn(utf8_name, index) - latin1_path = os.path.join(repo_dir_bytes, latin1_name) self.assertTrue(os.path.exists(latin1_path)) - utf8_path = os.path.join(repo_dir_bytes, utf8_name) self.assertTrue(os.path.exists(utf8_path)) + def test_git_submodule(self): + repo_dir = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, repo_dir) + with Repo.init(repo_dir) as repo: + filea = Blob.from_string(b'file alalala') + + subtree = Tree() + subtree[b'a'] = (stat.S_IFREG | 0o644, filea.id) + + c = Commit() + c.tree = subtree.id + c.committer = c.author = b'Somebody ' + c.commit_time = c.author_time = 42342 + c.commit_timezone = c.author_timezone = 0 + c.parents = [] + c.message = b'Subcommit' + + tree = Tree() + tree[b'c'] = (S_IFGITLINK, c.id) + + repo.object_store.add_objects( + [(o, None) for o in [tree]]) + + build_index_from_tree( + repo.path, repo.index_path(), repo.object_store, tree.id) + + # Verify index entries + index = repo.open_index() + self.assertEqual(len(index), 1) + + # filea + apath = os.path.join(repo.path, 'c/a') + self.assertFalse(os.path.exists(apath)) + + # dir c + cpath = os.path.join(repo.path, 'c') + self.assertTrue(os.path.isdir(cpath)) + self.assertEqual(index[b'c'][4], S_IFGITLINK) # mode + self.assertEqual(index[b'c'][8], c.id) # sha + + def test_git_submodule_exists(self): + repo_dir = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, repo_dir) + with Repo.init(repo_dir) as repo: + filea = Blob.from_string(b'file alalala') + + subtree = Tree() + subtree[b'a'] = (stat.S_IFREG | 0o644, filea.id) + + c = Commit() + c.tree = subtree.id + c.committer = c.author = b'Somebody ' + c.commit_time = c.author_time = 42342 + c.commit_timezone = c.author_timezone = 0 + c.parents = [] + c.message = b'Subcommit' + + tree = Tree() + tree[b'c'] = (S_IFGITLINK, c.id) + + os.mkdir(os.path.join(repo_dir, 'c')) + repo.object_store.add_objects( + [(o, None) for o in [tree]]) + + build_index_from_tree( + repo.path, repo.index_path(), repo.object_store, tree.id) + + # Verify index entries + index = repo.open_index() + self.assertEqual(len(index), 1) + + # filea + apath = os.path.join(repo.path, 'c/a') + self.assertFalse(os.path.exists(apath)) + + # dir c + cpath = os.path.join(repo.path, 'c') + self.assertTrue(os.path.isdir(cpath)) + self.assertEqual(index[b'c'][4], S_IFGITLINK) # mode + self.assertEqual(index[b'c'][8], c.id) # sha + class GetUnstagedChangesTests(TestCase): @@ -436,7 +575,7 @@ repo_dir = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, repo_dir) - with closing(Repo.init(repo_dir)) as repo: + with Repo.init(repo_dir) as repo: # Commit a dummy file then modify it foo1_fullpath = os.path.join(repo_dir, 'foo1') @@ -458,6 +597,27 @@ changes = get_unstaged_changes(repo.open_index(), repo_dir) + self.assertEqual(list(changes), [b'foo1']) + + def test_get_unstaged_deleted_changes(self): + """Unit test for get_unstaged_changes.""" + + repo_dir = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, repo_dir) + with Repo.init(repo_dir) as repo: + + # Commit a dummy file then remove it + foo1_fullpath = os.path.join(repo_dir, 'foo1') + with open(foo1_fullpath, 'wb') as f: + f.write(b'origstuff') + + repo.stage(['foo1']) + repo.do_commit(b'test status', author=b'', committer=b'') + + os.unlink(foo1_fullpath) + + changes = get_unstaged_changes(repo.open_index(), repo_dir) + self.assertEqual(list(changes), [b'foo1']) diff -Nru dulwich-0.12.0/dulwich/tests/test_lru_cache.py dulwich-0.18.5/dulwich/tests/test_lru_cache.py --- dulwich-0.12.0/dulwich/tests/test_lru_cache.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_lru_cache.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,18 +1,21 @@ # Copyright (C) 2006, 2008 Canonical Ltd # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """Tests for the lru_cache module.""" @@ -23,6 +26,7 @@ TestCase, ) + class TestLRUCache(TestCase): """Test that LRU cache properly keeps track of entries.""" @@ -98,6 +102,7 @@ def test_cleanup(self): """Test that we can use a cleanup function.""" cleanup_called = [] + def cleanup_func(key, val): cleanup_called.append((key, val)) @@ -118,6 +123,7 @@ def test_cleanup_on_replace(self): """Replacing an object should cleanup the old value.""" cleanup_called = [] + def cleanup_func(key, val): cleanup_called.append((key, val)) @@ -150,7 +156,7 @@ self.assertEqual(8, len(cache)) - cache[1] = 15 # replacement + cache[1] = 15 # replacement self.assertEqual(8, len(cache)) @@ -282,7 +288,7 @@ cache[9] = 10 cache[10] = 11 self.assertEqual([3, 4, 5, 6, 7, 8, 9, 10], sorted(cache.keys())) - cache[11] = 12 # triggers cleanup back to new after_cleanup_count + cache[11] = 12 # triggers cleanup back to new after_cleanup_count self.assertEqual([6, 7, 8, 9, 10, 11], sorted(cache.keys())) @@ -323,20 +329,21 @@ self.assertEqual({'test': 'key'}, cache.items()) cache.add('test2', 'key that is too big') self.assertEqual(3, cache._value_size) - self.assertEqual({'test':'key'}, cache.items()) + self.assertEqual({'test': 'key'}, cache.items()) # If we would add a key, only to cleanup and remove all cached entries, # then obviously that value should not be stored cache.add('test3', 'bigkey') self.assertEqual(3, cache._value_size) - self.assertEqual({'test':'key'}, cache.items()) + self.assertEqual({'test': 'key'}, cache.items()) cache.add('test4', 'bikey') self.assertEqual(3, cache._value_size) - self.assertEqual({'test':'key'}, cache.items()) + self.assertEqual({'test': 'key'}, cache.items()) def test_no_add_over_size_cleanup(self): """If a large value is not cached, we will call cleanup right away.""" cleanup_calls = [] + def cleanup(key, value): cleanup_calls.append((key, value)) @@ -353,28 +360,28 @@ def test_adding_clears_cache_based_on_size(self): """The cache is cleared in LRU order until small enough""" cache = lru_cache.LRUSizeCache(max_size=20) - cache.add('key1', 'value') # 5 chars - cache.add('key2', 'value2') # 6 chars - cache.add('key3', 'value23') # 7 chars + cache.add('key1', 'value') # 5 chars + cache.add('key2', 'value2') # 6 chars + cache.add('key3', 'value23') # 7 chars self.assertEqual(5+6+7, cache._value_size) - cache['key2'] # reference key2 so it gets a newer reference time - cache.add('key4', 'value234') # 8 chars, over limit + cache['key2'] # reference key2 so it gets a newer reference time + cache.add('key4', 'value234') # 8 chars, over limit # We have to remove 2 keys to get back under limit self.assertEqual(6+8, cache._value_size) - self.assertEqual({'key2':'value2', 'key4':'value234'}, + self.assertEqual({'key2': 'value2', 'key4': 'value234'}, cache.items()) def test_adding_clears_to_after_cleanup_size(self): cache = lru_cache.LRUSizeCache(max_size=20, after_cleanup_size=10) - cache.add('key1', 'value') # 5 chars - cache.add('key2', 'value2') # 6 chars - cache.add('key3', 'value23') # 7 chars + cache.add('key1', 'value') # 5 chars + cache.add('key2', 'value2') # 6 chars + cache.add('key3', 'value23') # 7 chars self.assertEqual(5+6+7, cache._value_size) - cache['key2'] # reference key2 so it gets a newer reference time - cache.add('key4', 'value234') # 8 chars, over limit + cache['key2'] # reference key2 so it gets a newer reference time + cache.add('key4', 'value234') # 8 chars, over limit # We have to remove 3 keys to get back under limit self.assertEqual(8, cache._value_size) - self.assertEqual({'key4':'value234'}, cache.items()) + self.assertEqual({'key4': 'value234'}, cache.items()) def test_custom_sizes(self): def size_of_list(lst): @@ -382,23 +389,23 @@ cache = lru_cache.LRUSizeCache(max_size=20, after_cleanup_size=10, compute_size=size_of_list) - cache.add('key1', ['val', 'ue']) # 5 chars - cache.add('key2', ['val', 'ue2']) # 6 chars - cache.add('key3', ['val', 'ue23']) # 7 chars + cache.add('key1', ['val', 'ue']) # 5 chars + cache.add('key2', ['val', 'ue2']) # 6 chars + cache.add('key3', ['val', 'ue23']) # 7 chars self.assertEqual(5+6+7, cache._value_size) - cache['key2'] # reference key2 so it gets a newer reference time - cache.add('key4', ['value', '234']) # 8 chars, over limit + cache['key2'] # reference key2 so it gets a newer reference time + cache.add('key4', ['value', '234']) # 8 chars, over limit # We have to remove 3 keys to get back under limit self.assertEqual(8, cache._value_size) - self.assertEqual({'key4':['value', '234']}, cache.items()) + self.assertEqual({'key4': ['value', '234']}, cache.items()) def test_cleanup(self): cache = lru_cache.LRUSizeCache(max_size=20, after_cleanup_size=10) # Add these in order - cache.add('key1', 'value') # 5 chars - cache.add('key2', 'value2') # 6 chars - cache.add('key3', 'value23') # 7 chars + cache.add('key1', 'value') # 5 chars + cache.add('key2', 'value2') # 6 chars + cache.add('key3', 'value23') # 7 chars self.assertEqual(5+6+7, cache._value_size) cache.cleanup() @@ -445,4 +452,3 @@ self.assertEqual([2, 3, 4, 5, 6], sorted(cache.keys())) cache[7] = 'stu' self.assertEqual([4, 5, 6, 7], sorted(cache.keys())) - diff -Nru dulwich-0.12.0/dulwich/tests/test_missing_obj_finder.py dulwich-0.18.5/dulwich/tests/test_missing_obj_finder.py --- dulwich-0.12.0/dulwich/tests/test_missing_obj_finder.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_missing_obj_finder.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,20 +1,22 @@ # test_missing_obj_finder.py -- tests for MissingObjectFinder # Copyright (C) 2012 syntevo GmbH # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# or (at your option) any later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. from dulwich.object_store import ( MemoryObjectStore, @@ -42,28 +44,33 @@ def assertMissingMatch(self, haves, wants, expected): for sha, path in self.store.find_missing_objects(haves, wants): - self.assertTrue(sha in expected, - "(%s,%s) erroneously reported as missing" % (sha, path)) + self.assertTrue( + sha in expected, + "(%s,%s) erroneously reported as missing" % (sha, path)) expected.remove(sha) - self.assertEqual(len(expected), 0, - "some objects are not reported as missing: %s" % (expected, )) + self.assertEqual( + len(expected), 0, + "some objects are not reported as missing: %s" % (expected, )) class MOFLinearRepoTest(MissingObjectFinderTest): def setUp(self): super(MOFLinearRepoTest, self).setUp() - f1_1 = make_object(Blob, data=b'f1') # present in 1, removed in 3 - f2_1 = make_object(Blob, data=b'f2') # present in all revisions, changed in 2 and 3 + # present in 1, removed in 3 + f1_1 = make_object(Blob, data=b'f1') + # present in all revisions, changed in 2 and 3 + f2_1 = make_object(Blob, data=b'f2') f2_2 = make_object(Blob, data=b'f2-changed') f2_3 = make_object(Blob, data=b'f2-changed-again') - f3_2 = make_object(Blob, data=b'f3') # added in 2, left unmodified in 3 + # added in 2, left unmodified in 3 + f3_2 = make_object(Blob, data=b'f3') commit_spec = [[1], [2, 1], [3, 2]] trees = {1: [(b'f1', f1_1), (b'f2', f2_1)], 2: [(b'f1', f1_1), (b'f2', f2_2), (b'f3', f3_2)], - 3: [(b'f2', f2_3), (b'f3', f3_2)] } + 3: [(b'f2', f2_3), (b'f3', f3_2)]} # commit 1: f1 and f2 # commit 2: f3 added, f2 changed. Missing shall report commit id and a # tree referenced by commit @@ -78,16 +85,19 @@ f2_2.id, f3_2.id, f2_3.id] def test_1_to_2(self): - self.assertMissingMatch([self.cmt(1).id], [self.cmt(2).id], - self.missing_1_2) + self.assertMissingMatch( + [self.cmt(1).id], [self.cmt(2).id], + self.missing_1_2) def test_2_to_3(self): - self.assertMissingMatch([self.cmt(2).id], [self.cmt(3).id], - self.missing_2_3) + self.assertMissingMatch( + [self.cmt(2).id], [self.cmt(3).id], + self.missing_2_3) def test_1_to_3(self): - self.assertMissingMatch([self.cmt(1).id], [self.cmt(3).id], - self.missing_1_3) + self.assertMissingMatch( + [self.cmt(1).id], [self.cmt(3).id], + self.missing_1_3) def test_bogus_haves(self): """Ensure non-existent SHA in haves are tolerated""" @@ -101,8 +111,8 @@ bogus_sha = self.cmt(2).id[::-1] haves = [self.cmt(1).id] wants = [self.cmt(3).id, bogus_sha] - self.assertRaises(KeyError, self.store.find_missing_objects, - haves, wants) + self.assertRaises( + KeyError, self.store.find_missing_objects, haves, wants) def test_no_changes(self): self.assertMissingMatch([self.cmt(3).id], [self.cmt(3).id], []) @@ -120,21 +130,22 @@ f1_1 = make_object(Blob, data=b'f1') f1_2 = make_object(Blob, data=b'f1-2') f1_4 = make_object(Blob, data=b'f1-4') - f1_7 = make_object(Blob, data=b'f1-2') # same data as in rev 2 + f1_7 = make_object(Blob, data=b'f1-2') # same data as in rev 2 f2_1 = make_object(Blob, data=b'f2') f2_3 = make_object(Blob, data=b'f2-3') f3_3 = make_object(Blob, data=b'f3') f3_5 = make_object(Blob, data=b'f3-5') commit_spec = [[1], [2, 1], [3, 2], [4, 2], [5, 3], [6, 3, 4], [7, 6]] trees = {1: [(b'f1', f1_1), (b'f2', f2_1)], - 2: [(b'f1', f1_2), (b'f2', f2_1)], # f1 changed - # f3 added, f2 changed - 3: [(b'f1', f1_2), (b'f2', f2_3), (b'f3', f3_3)], - 4: [(b'f1', f1_4), (b'f2', f2_1)], # f1 changed - 5: [(b'f1', f1_2), (b'f3', f3_5)], # f2 removed, f3 changed - 6: [(b'f1', f1_4), (b'f2', f2_3), (b'f3', f3_3)], # merged 3 and 4 - # f1 changed to match rev2. f3 removed - 7: [(b'f1', f1_7), (b'f2', f2_3)]} + 2: [(b'f1', f1_2), (b'f2', f2_1)], # f1 changed + # f3 added, f2 changed + 3: [(b'f1', f1_2), (b'f2', f2_3), (b'f3', f3_3)], + 4: [(b'f1', f1_4), (b'f2', f2_1)], # f1 changed + 5: [(b'f1', f1_2), (b'f3', f3_5)], # f2 removed, f3 changed + # merged 3 and 4 + 6: [(b'f1', f1_4), (b'f2', f2_3), (b'f3', f3_3)], + # f1 changed to match rev2. f3 removed + 7: [(b'f1', f1_7), (b'f2', f2_3)]} self.commits = build_commit_graph(self.store, commit_spec, trees) self.f1_2_id = f1_2.id @@ -152,8 +163,9 @@ # which is an overkill (i.e. in sha_done it records f1_4 as known, and # doesn't record f1_2 was known prior to that, hence can't detect f1_7 # is in fact f1_2 and shall not be reported) - self.assertMissingMatch([self.cmt(6).id], [self.cmt(7).id], - [self.cmt(7).id, self.cmt(7).tree, self.f1_7_id]) + self.assertMissingMatch( + [self.cmt(6).id], [self.cmt(7).id], + [self.cmt(7).id, self.cmt(7).tree, self.f1_7_id]) def test_have4_want7(self): # have 4, want 7. Shall not include rev5 as it is not in the tree diff -Nru dulwich-0.12.0/dulwich/tests/test_objectspec.py dulwich-0.18.5/dulwich/tests/test_objectspec.py --- dulwich-0.12.0/dulwich/tests/test_objectspec.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_objectspec.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,21 +1,22 @@ # test_objectspec.py -- tests for objectspec.py # Copyright (C) 2014 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for revision spec parsing.""" @@ -32,6 +33,7 @@ parse_refs, parse_reftuple, parse_reftuples, + parse_tree, ) from dulwich.repo import MemoryRepo from dulwich.tests import ( @@ -65,8 +67,8 @@ def test_commit_by_sha(self): r = MemoryRepo() - c1, c2, c3 = build_commit_graph(r.object_store, [[1], [2, 1], - [3, 1, 2]]) + c1, c2, c3 = build_commit_graph( + r.object_store, [[1], [2, 1], [3, 1, 2]]) self.assertEqual([c1], list(parse_commit_range(r, c1.id))) @@ -151,16 +153,26 @@ def test_head(self): r = {b"refs/heads/foo": "bla"} self.assertEqual((b"refs/heads/foo", b"refs/heads/foo", False), - parse_reftuple(r, r, b"foo")) + parse_reftuple(r, r, b"foo")) self.assertEqual((b"refs/heads/foo", b"refs/heads/foo", True), - parse_reftuple(r, r, b"+foo")) + parse_reftuple(r, r, b"+foo")) self.assertEqual((b"refs/heads/foo", b"refs/heads/foo", True), - parse_reftuple(r, {}, b"+foo")) + parse_reftuple(r, {}, b"+foo")) def test_full(self): r = {b"refs/heads/foo": "bla"} self.assertEqual((b"refs/heads/foo", b"refs/heads/foo", False), - parse_reftuple(r, r, b"refs/heads/foo")) + parse_reftuple(r, r, b"refs/heads/foo")) + + def test_no_left_ref(self): + r = {b"refs/heads/foo": "bla"} + self.assertEqual((None, b"refs/heads/foo", False), + parse_reftuple(r, r, b":refs/heads/foo")) + + def test_no_right_ref(self): + r = {b"refs/heads/foo": "bla"} + self.assertEqual((b"refs/heads/foo", None, False), + parse_reftuple(r, r, b"refs/heads/foo:")) class ParseReftuplesTests(TestCase): @@ -168,14 +180,29 @@ def test_nonexistent(self): r = {} self.assertRaises(KeyError, parse_reftuples, r, r, - [b"thisdoesnotexist"]) + [b"thisdoesnotexist"]) def test_head(self): r = {b"refs/heads/foo": "bla"} self.assertEqual([(b"refs/heads/foo", b"refs/heads/foo", False)], - parse_reftuples(r, r, [b"foo"])) + parse_reftuples(r, r, [b"foo"])) def test_full(self): r = {b"refs/heads/foo": "bla"} self.assertEqual([(b"refs/heads/foo", b"refs/heads/foo", False)], - parse_reftuples(r, r, b"refs/heads/foo")) + parse_reftuples(r, r, b"refs/heads/foo")) + + +class ParseTreeTests(TestCase): + """Test parse_tree.""" + + def test_nonexistent(self): + r = MemoryRepo() + self.assertRaises(KeyError, parse_tree, r, "thisdoesnotexist") + + def test_from_commit(self): + r = MemoryRepo() + c1, c2, c3 = build_commit_graph( + r.object_store, [[1], [2, 1], [3, 1, 2]]) + self.assertEqual(r[c1.tree], parse_tree(r, c1.id)) + self.assertEqual(r[c1.tree], parse_tree(r, c1.tree)) diff -Nru dulwich-0.12.0/dulwich/tests/test_objects.py dulwich-0.18.5/dulwich/tests/test_objects.py --- dulwich-0.12.0/dulwich/tests/test_objects.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_objects.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,21 +1,22 @@ # test_objects.py -- tests for objects.py # Copyright (C) 2007 James Westby # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for git base objects.""" @@ -48,8 +49,9 @@ hex_to_filename, check_hexsha, check_identity, - parse_timezone, object_class, + parse_timezone, + pretty_format_tree_entry, parse_tree, _parse_tree_py, sorted_tree_items, @@ -134,6 +136,22 @@ b = Blob.from_string(string) self.assertEqual([string], b.chunked) + def test_splitlines(self): + for case in [ + [], + [b'foo\nbar\n'], + [b'bl\na', b'blie'], + [b'bl\na', b'blie', b'bloe\n'], + [b'', b'bl\na', b'blie', b'bloe\n'], + [b'', b'', b'', b'bla\n'], + [b'', b'', b'', b'bla\n', b''], + [b'bl', b'', b'a\naaa'], + [b'a\naaa', b'a'], + ]: + b = Blob() + b.chunked = case + self.assertEqual(b.data.splitlines(True), b.splitlines()) + def test_set_chunks(self): b = Blob() b.chunked = [b'te', b'st', b' 5\n'] @@ -159,10 +177,12 @@ def test_read_tree_from_file_parse_count(self): old_deserialize = Tree._deserialize + def reset_deserialize(): Tree._deserialize = old_deserialize self.addCleanup(reset_deserialize) self.deserialize_count = 0 + def counting_deserialize(*args, **kwargs): self.deserialize_count += 1 return old_deserialize(*args, **kwargs) @@ -179,7 +199,17 @@ self.assertEqual(t.name, b'signed') self.assertEqual(t.tagger, b'Ali Sabil ') self.assertEqual(t.tag_time, 1231203091) - self.assertEqual(t.message, b'This is a signed tag\n-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG v1.4.9 (GNU/Linux)\n\niEYEABECAAYFAkliqx8ACgkQqSMmLy9u/kcx5ACfakZ9NnPl02tOyYP6pkBoEkU1\n5EcAn0UFgokaSvS371Ym/4W9iJj6vh3h\n=ql7y\n-----END PGP SIGNATURE-----\n') + self.assertEqual( + t.message, + b'This is a signed tag\n' + b'-----BEGIN PGP SIGNATURE-----\n' + b'Version: GnuPG v1.4.9 (GNU/Linux)\n' + b'\n' + b'iEYEABECAAYFAkliqx8ACgkQqSMmLy9u/' + b'kcx5ACfakZ9NnPl02tOyYP6pkBoEkU1\n' + b'5EcAn0UFgokaSvS371Ym/4W9iJj6vh3h\n' + b'=ql7y\n' + b'-----END PGP SIGNATURE-----\n') def test_read_commit_from_file(self): sha = b'60dacdc733de308bb77bb76ce0fb0f9b44c9769e' @@ -231,13 +261,14 @@ c = make_commit(id=sha, message=b'foo') self.assertTrue(isinstance(c, Commit)) self.assertEqual(sha, c.id) - self.assertNotEqual(sha, c._make_sha()) + self.assertNotEqual(sha, c.sha()) class ShaFileCheckTests(TestCase): def assertCheckFails(self, cls, data): obj = cls() + def do_check(): obj.set_raw_string(data) obj.check() @@ -300,6 +331,16 @@ c1.set_raw_string(c.as_raw_string()) self.assertEqual(30, c1.commit_time) + def test_full_tree(self): + c = self.make_commit(commit_time=30) + t = Tree() + t.add(b'data-x', 0o644, Blob().id) + c.tree = t + c1 = Commit() + c1.set_raw_string(c.as_raw_string()) + self.assertEqual(t.id, c1.tree) + self.assertEqual(c.as_raw_string(), c1.as_raw_string()) + def test_raw_length(self): c = self.make_commit() self.assertEqual(len(c.as_raw_string()), c.raw_length()) @@ -376,7 +417,7 @@ -----END PGP SIGNATURE----- Merge ../b -""", commit.as_raw_string()) +""", commit.as_raw_string()) # noqa: W291,W293 def test_serialize_mergetag(self): tag = make_object( @@ -409,7 +450,7 @@ -----END PGP SIGNATURE----- Merge ../b -""", commit.as_raw_string()) +""", commit.as_raw_string()) # noqa: W291,W293 def test_serialize_mergetags(self): tag = make_object( @@ -455,7 +496,7 @@ -----END PGP SIGNATURE----- Merge ../b -""", commit.as_raw_string()) +""", commit.as_raw_string()) # noqa: W291,W293 def test_deserialize_mergetag(self): tag = make_object( @@ -488,14 +529,17 @@ self.assertEqual(commit, d) -default_committer = b'James Westby 1174773719 +0000' +default_committer = ( + b'James Westby 1174773719 +0000') + class CommitParseTests(ShaFileCheckTests): def make_commit_lines(self, tree=b'd80c186a03f423a81b39df39dc87fd269736ca86', - parents=[b'ab64bbdcc51b170d21588e5c5d391ee5c0c96dfd', - b'4cffe90e0a41ad3f5190079d7c8f036bde29cbe6'], + parents=[ + b'ab64bbdcc51b170d21588e5c5d391ee5c0c96dfd', + b'4cffe90e0a41ad3f5190079d7c8f036bde29cbe6'], author=default_committer, committer=default_committer, encoding=None, @@ -535,10 +579,10 @@ c.parents) expected_time = datetime.datetime(2007, 3, 24, 22, 1, 59) self.assertEqual(expected_time, - datetime.datetime.utcfromtimestamp(c.commit_time)) + datetime.datetime.utcfromtimestamp(c.commit_time)) self.assertEqual(0, c.commit_timezone) self.assertEqual(expected_time, - datetime.datetime.utcfromtimestamp(c.author_time)) + datetime.datetime.utcfromtimestamp(c.author_time)) self.assertEqual(0, c.author_timezone) self.assertEqual(None, c.encoding) @@ -618,7 +662,7 @@ -----END PGP SIGNATURE----- foo -""") +""") # noqa: W291,W293 self.assertEqual(b'foo\n', c.message) self.assertEqual([], c.extra) self.assertEqual(b"""-----BEGIN PGP SIGNATURE----- @@ -639,6 +683,39 @@ =X6RT -----END PGP SIGNATURE-----""", c.gpgsig) + def test_parse_header_trailing_newline(self): + c = Commit.from_string(b'''\ +tree a7d6277f78d3ecd0230a1a5df6db00b1d9c521ac +parent c09b6dec7a73760fbdb478383a3c926b18db8bbe +author Neil Matatall 1461964057 -1000 +committer Neil Matatall 1461964057 -1000 +gpgsig -----BEGIN PGP SIGNATURE----- + + wsBcBAABCAAQBQJXI80ZCRA6pcNDcVZ70gAAarcIABs72xRX3FWeox349nh6ucJK + CtwmBTusez2Zwmq895fQEbZK7jpaGO5TRO4OvjFxlRo0E08UFx3pxZHSpj6bsFeL + hHsDXnCaotphLkbgKKRdGZo7tDqM84wuEDlh4MwNe7qlFC7bYLDyysc81ZX5lpMm + 2MFF1TvjLAzSvkT7H1LPkuR3hSvfCYhikbPOUNnKOo0sYjeJeAJ/JdAVQ4mdJIM0 + gl3REp9+A+qBEpNQI7z94Pg5Bc5xenwuDh3SJgHvJV6zBWupWcdB3fAkVd4TPnEZ + nHxksHfeNln9RKseIDcy4b2ATjhDNIJZARHNfr6oy4u3XPW4svRqtBsLoMiIeuI= + =ms6q + -----END PGP SIGNATURE----- + + +3.3.0 version bump and docs +''') # noqa: W291,W293 + self.assertEqual([], c.extra) + self.assertEqual(b'''\ +-----BEGIN PGP SIGNATURE----- + +wsBcBAABCAAQBQJXI80ZCRA6pcNDcVZ70gAAarcIABs72xRX3FWeox349nh6ucJK +CtwmBTusez2Zwmq895fQEbZK7jpaGO5TRO4OvjFxlRo0E08UFx3pxZHSpj6bsFeL +hHsDXnCaotphLkbgKKRdGZo7tDqM84wuEDlh4MwNe7qlFC7bYLDyysc81ZX5lpMm +2MFF1TvjLAzSvkT7H1LPkuR3hSvfCYhikbPOUNnKOo0sYjeJeAJ/JdAVQ4mdJIM0 +gl3REp9+A+qBEpNQI7z94Pg5Bc5xenwuDh3SJgHvJV6zBWupWcdB3fAkVd4TPnEZ +nHxksHfeNln9RKseIDcy4b2ATjhDNIJZARHNfr6oy4u3XPW4svRqtBsLoMiIeuI= +=ms6q +-----END PGP SIGNATURE-----\n''', c.gpgsig) + _TREE_ITEMS = { b'a.c': (0o100755, b'd80c186a03f423a81b39df39dc87fd269736ca86'), @@ -649,7 +726,8 @@ _SORTED_TREE_ITEMS = [ TreeEntry(b'a.c', 0o100755, b'd80c186a03f423a81b39df39dc87fd269736ca86'), TreeEntry(b'a', stat.S_IFDIR, b'd80c186a03f423a81b39df39dc87fd269736ca86'), - TreeEntry(b'a/c', stat.S_IFDIR, b'd80c186a03f423a81b39df39dc87fd269736ca86'), + TreeEntry(b'a/c', stat.S_IFDIR, + b'd80c186a03f423a81b39df39dc87fd269736ca86'), ] @@ -660,7 +738,8 @@ x = Tree() x.add(b'myname', 0o100755, myhexsha) self.assertEqual(x[b'myname'], (0o100755, myhexsha)) - self.assertEqual(b'100755 myname\0' + hex_to_sha(myhexsha), + self.assertEqual( + b'100755 myname\0' + hex_to_sha(myhexsha), x.as_raw_string()) def test_add_old_order(self): @@ -732,7 +811,8 @@ # C/Python implementations may differ in specific error types, but # should all error on invalid inputs. # For example, the C implementation has stricter type checks, so may - # raise TypeError where the Python implementation raises AttributeError. + # raise TypeError where the Python implementation raises + # AttributeError. errors = (TypeError, ValueError, AttributeError) self.assertRaises(errors, do_sort, b'foo') self.assertRaises(errors, do_sort, {b'foo': (1, 2, 3)}) @@ -785,12 +865,15 @@ # shas self.assertCheckFails(t, b'100644 a\0' + (b'x' * 5)) self.assertCheckFails(t, b'100644 a\0' + (b'x' * 18) + b'\0') - self.assertCheckFails(t, b'100644 a\0' + (b'x' * 21) + b'\n100644 b\0' + sha) + self.assertCheckFails( + t, b'100644 a\0' + (b'x' * 21) + b'\n100644 b\0' + sha) # ordering sha2 = hex_to_sha(b_sha) - self.assertCheckSucceeds(t, b'100644 a\0' + sha + b'\n100644 b\0' + sha) - self.assertCheckSucceeds(t, b'100644 a\0' + sha + b'\n100644 b\0' + sha2) + self.assertCheckSucceeds( + t, b'100644 a\0' + sha + b'\n100644 b\0' + sha) + self.assertCheckSucceeds( + t, b'100644 a\0' + sha + b'\n100644 b\0' + sha2) self.assertCheckFails(t, b'100644 a\0' + sha + b'\n100755 a\0' + sha2) self.assertCheckFails(t, b'100644 b\0' + sha2 + b'\n100644 a\0' + sha) @@ -803,13 +886,14 @@ class TagSerializeTests(TestCase): def test_serialize_simple(self): - x = make_object(Tag, - tagger=b'Jelmer Vernooij ', - name=b'0.1', - message=b'Tag 0.1', - object=(Blob, b'd80c186a03f423a81b39df39dc87fd269736ca86'), - tag_time=423423423, - tag_timezone=0) + x = make_object( + Tag, + tagger=b'Jelmer Vernooij ', + name=b'0.1', + message=b'Tag 0.1', + object=(Blob, b'd80c186a03f423a81b39df39dc87fd269736ca86'), + tag_time=423423423, + tag_timezone=0) self.assertEqual((b'object d80c186a03f423a81b39df39dc87fd269736ca86\n' b'type blob\n' b'tag 0.1\n' @@ -818,6 +902,21 @@ b'\n' b'Tag 0.1'), x.as_raw_string()) + def test_serialize_none_message(self): + x = make_object( + Tag, + tagger=b'Jelmer Vernooij ', + name=b'0.1', + message=None, + object=(Blob, b'd80c186a03f423a81b39df39dc87fd269736ca86'), + tag_time=423423423, + tag_timezone=0) + self.assertEqual((b'object d80c186a03f423a81b39df39dc87fd269736ca86\n' + b'type blob\n' + b'tag 0.1\n' + b'tagger Jelmer Vernooij ' + b'423423423 +0000\n'), x.as_raw_string()) + default_tagger = (b'Linus Torvalds ' b'1183319674 -0700') @@ -849,8 +948,8 @@ lines.append(b'tag ' + name) if tagger is not None: lines.append(b'tagger ' + tagger) - lines.append(b'') if message is not None: + lines.append(b'') lines.append(message) return lines @@ -868,7 +967,7 @@ object_sha) self.assertEqual(Commit, object_type) self.assertEqual(datetime.datetime.utcfromtimestamp(x.tag_time), - datetime.datetime(2007, 7, 1, 19, 54, 34)) + datetime.datetime(2007, 7, 1, 19, 54, 34)) self.assertEqual(-25200, x.tag_timezone) def test_parse_no_tagger(self): @@ -876,6 +975,18 @@ x.set_raw_string(self.make_tag_text(tagger=None)) self.assertEqual(None, x.tagger) self.assertEqual(b'v2.6.22-rc7', x.name) + self.assertEqual(None, x.tag_time) + + def test_parse_no_message(self): + x = Tag() + x.set_raw_string(self.make_tag_text(message=None)) + self.assertEqual(None, x.message) + self.assertEqual( + b'Linus Torvalds ', x.tagger) + self.assertEqual(datetime.datetime.utcfromtimestamp(x.tag_time), + datetime.datetime(2007, 7, 1, 19, 54, 34)) + self.assertEqual(-25200, x.tag_timezone) + self.assertEqual(b'v2.6.22-rc7', x.name) def test_check(self): self.assertCheckSucceeds(Tag, self.make_tag_text()) @@ -912,6 +1023,20 @@ else: self.assertCheckFails(Tag, text) + def test_tree_copy_after_update(self): + """Check Tree.id is correctly updated when the tree is copied after updated. + """ + shas = [] + tree = Tree() + shas.append(tree.id) + tree.add(b'data', 0o644, Blob().id) + copied = tree.copy() + shas.append(tree.id) + shas.append(copied.id) + + self.assertNotIn(shas[0], shas[1:]) + self.assertEqual(shas[1], shas[2]) + class CheckTests(TestCase): @@ -1033,12 +1158,12 @@ class ShaFileSerializeTests(TestCase): - """ - Test that `ShaFile` objects only gets serialized once if they haven't changed. + """`ShaFile` objects only gets serialized once if they haven't changed. """ @contextmanager - def assert_serialization_on_change(self, obj, needs_serialization_after_change=True): + def assert_serialization_on_change( + self, obj, needs_serialization_after_change=True): old_id = obj.id self.assertFalse(obj._needs_serialization) @@ -1071,7 +1196,8 @@ def test_blob_serialize(self): blob = make_object(Blob, data=b'i am a blob') - with self.assert_serialization_on_change(blob, needs_serialization_after_change=False): + with self.assert_serialization_on_change( + blob, needs_serialization_after_change=False): blob.data = b'i am another blob' def test_tree_serialize(self): @@ -1091,3 +1217,13 @@ with self.assert_serialization_on_change(tag): tag.message = b'new message' + + +class PrettyFormatTreeEntryTests(TestCase): + + def test_format(self): + self.assertEqual( + '40000 tree 40820c38cfb182ce6c8b261555410d8382a5918b\tfoo\n', + pretty_format_tree_entry( + b"foo", 0o40000, + b"40820c38cfb182ce6c8b261555410d8382a5918b")) diff -Nru dulwich-0.12.0/dulwich/tests/test_object_store.py dulwich-0.18.5/dulwich/tests/test_object_store.py --- dulwich-0.12.0/dulwich/tests/test_object_store.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_object_store.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,20 +1,22 @@ # test_object_store.py -- tests for object_store.py # Copyright (C) 2008 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# or (at your option) any later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for the object store interface.""" @@ -23,6 +25,7 @@ from io import BytesIO import os import shutil +import stat import tempfile from dulwich.index import ( @@ -41,6 +44,7 @@ DiskObjectStore, MemoryObjectStore, ObjectStoreGraphWalker, + commit_tree_changes, tree_lookup_path, ) from dulwich.pack import ( @@ -63,12 +67,13 @@ class ObjectStoreTests(object): def test_determine_wants_all(self): - self.assertEqual([b"1" * 40], + self.assertEqual( + [b"1" * 40], self.store.determine_wants_all({b"refs/heads/foo": b"1" * 40})) def test_determine_wants_all_zero(self): - self.assertEqual([], - self.store.determine_wants_all({b"refs/heads/foo": b"0" * 40})) + self.assertEqual( + [], self.store.determine_wants_all({b"refs/heads/foo": b"0" * 40})) def test_iter(self): self.assertEqual([], list(self.store)) @@ -87,6 +92,20 @@ # access to a serialized form. self.store.add_objects([]) + def test_store_resilience(self): + """Test if updating an existing stored object doesn't erase the + object from the store. + """ + test_object = make_object(Blob, data=b'data') + + self.store.add_object(test_object) + test_object_id = test_object.id + test_object.data = test_object.data + b'update' + stored_test_object = self.store[test_object_id] + + self.assertNotEqual(test_object.id, stored_test_object.id) + self.assertEqual(stored_test_object.id, test_object_id) + def test_add_object(self): self.store.add_object(testobject) self.assertEqual(set([testobject.id]), set(self.store)) @@ -113,13 +132,15 @@ tree1_id = commit_tree(self.store, blobs_1) blobs_2 = [(b'a', blob_a2.id, 0o100644), (b'b', blob_b.id, 0o100644)] tree2_id = commit_tree(self.store, blobs_2) - change_a = ((b'a', b'a'), (0o100644, 0o100644), (blob_a1.id, blob_a2.id)) + change_a = ((b'a', b'a'), (0o100644, 0o100644), + (blob_a1.id, blob_a2.id)) self.assertEqual([change_a], - list(self.store.tree_changes(tree1_id, tree2_id))) + list(self.store.tree_changes(tree1_id, tree2_id))) self.assertEqual( - [change_a, ((b'b', b'b'), (0o100644, 0o100644), (blob_b.id, blob_b.id))], + [change_a, ((b'b', b'b'), (0o100644, 0o100644), + (blob_b.id, blob_b.id))], list(self.store.tree_changes(tree1_id, tree2_id, - want_unchanged=True))) + want_unchanged=True))) def test_iter_tree_contents(self): blob_a = make_object(Blob, data=b'a') @@ -137,7 +158,7 @@ ] tree_id = commit_tree(self.store, blobs) self.assertEqual([TreeEntry(p, m, h) for (p, h, m) in blobs], - list(self.store.iter_tree_contents(tree_id))) + list(self.store.iter_tree_contents(tree_id))) def test_iter_tree_contents_include_trees(self): blob_a = make_object(Blob, data=b'a') @@ -228,7 +249,6 @@ self.assertEqual((Blob.type_num, b'more yummy data'), o.get_raw(packed_blob_sha)) - def test_add_thin_pack_empty(self): o = MemoryObjectStore() @@ -252,11 +272,52 @@ self.store.add_object(b1) b2 = make_object(Blob, data=b"more yummy data") self.store.add_object(b2) - self.assertEqual([], list(self.store.packs)) + b3 = make_object(Blob, data=b"even more yummy data") + b4 = make_object(Blob, data=b"and more yummy data") + self.store.add_objects([(b3, None), (b4, None)]) + self.assertEqual({b1.id, b2.id, b3.id, b4.id}, set(self.store)) + self.assertEqual(1, len(self.store.packs)) self.assertEqual(2, self.store.pack_loose_objects()) self.assertNotEqual([], list(self.store.packs)) self.assertEqual(0, self.store.pack_loose_objects()) + def test_repack(self): + b1 = make_object(Blob, data=b"yummy data") + self.store.add_object(b1) + b2 = make_object(Blob, data=b"more yummy data") + self.store.add_object(b2) + b3 = make_object(Blob, data=b"even more yummy data") + b4 = make_object(Blob, data=b"and more yummy data") + self.store.add_objects([(b3, None), (b4, None)]) + b5 = make_object(Blob, data=b"and more data") + b6 = make_object(Blob, data=b"and some more data") + self.store.add_objects([(b5, None), (b6, None)]) + self.assertEqual({b1.id, b2.id, b3.id, b4.id, b5.id, b6.id}, + set(self.store)) + self.assertEqual(2, len(self.store.packs)) + self.assertEqual(6, self.store.repack()) + self.assertEqual(1, len(self.store.packs)) + self.assertEqual(0, self.store.pack_loose_objects()) + + def test_repack_existing(self): + b1 = make_object(Blob, data=b"yummy data") + self.store.add_object(b1) + b2 = make_object(Blob, data=b"more yummy data") + self.store.add_object(b2) + self.store.add_objects([(b1, None), (b2, None)]) + self.store.add_objects([(b2, None)]) + self.assertEqual({b1.id, b2.id}, set(self.store)) + self.assertEqual(2, len(self.store.packs)) + self.assertEqual(2, self.store.repack()) + self.assertEqual(1, len(self.store.packs)) + self.assertEqual(0, self.store.pack_loose_objects()) + + self.assertEqual({b1.id, b2.id}, set(self.store)) + self.assertEqual(1, len(self.store.packs)) + self.assertEqual(2, self.store.repack()) + self.assertEqual(1, len(self.store.packs)) + self.assertEqual(0, self.store.pack_loose_objects()) + class DiskObjectStoreTests(PackBasedObjectStoreTests, TestCase): @@ -300,7 +361,8 @@ alternate_store.add_object(b2) store = DiskObjectStore(self.store_dir) self.assertRaises(KeyError, store.__getitem__, b2.id) - store.add_alternate_path(os.path.relpath(alternate_dir, self.store_dir)) + store.add_alternate_path( + os.path.relpath(alternate_dir, self.store_dir)) self.assertEqual(list(alternate_store), list(store.alternates[0])) self.assertIn(b2.id, store) self.assertEqual(b2, store[b2.id]) @@ -335,7 +397,8 @@ with o.add_thin_pack(f.read, None) as pack: packed_blob_sha = sha_to_hex(entries[0][3]) pack.check_length_and_checksum() - self.assertEqual(sorted([blob.id, packed_blob_sha]), list(pack)) + self.assertEqual( + sorted([blob.id, packed_blob_sha]), list(pack)) self.assertTrue(o.contains_packed(packed_blob_sha)) self.assertTrue(o.contains_packed(blob.id)) self.assertEqual((Blob.type_num, b'more yummy data'), @@ -387,19 +450,23 @@ self.assertTrue(isinstance(self.store[o_id], Tree)) def test_lookup_nonexistent(self): - self.assertRaises(KeyError, tree_lookup_path, self.get_object, self.tree_id, b'j') + self.assertRaises( + KeyError, tree_lookup_path, self.get_object, self.tree_id, b'j') def test_lookup_not_tree(self): - self.assertRaises(NotTreeError, tree_lookup_path, self.get_object, self.tree_id, b'ad/b/j') + self.assertRaises( + NotTreeError, tree_lookup_path, self.get_object, self.tree_id, + b'ad/b/j') class ObjectStoreGraphWalkerTests(TestCase): def get_walker(self, heads, parent_map): - new_parent_map = dict([ - (k * 40, [(p * 40) for p in ps]) for (k, ps) in parent_map.items()]) + new_parent_map = dict( + [(k * 40, [(p * 40) for p in ps]) + for (k, ps) in parent_map.items()]) return ObjectStoreGraphWalker([x * 40 for x in heads], - new_parent_map.__getitem__) + new_parent_map.__getitem__) def test_ack_invalid_value(self): gw = self.get_walker([], {}) @@ -454,17 +521,90 @@ # A branch (a, c) or (b, d) may be done after 2 steps or 3 depending on # the order walked: 3-step walks include (a, b, c) and (b, a, d), etc. if walk == [b"a" * 40, b"c" * 40] or walk == [b"b" * 40, b"d" * 40]: - gw.ack(walk[0]) - acked = True + gw.ack(walk[0]) + acked = True walk.append(next(gw)) if not acked and walk[2] == b"c" * 40: - gw.ack(b"a" * 40) + gw.ack(b"a" * 40) elif not acked and walk[2] == b"d" * 40: - gw.ack(b"b" * 40) + gw.ack(b"b" * 40) walk.append(next(gw)) self.assertIs(None, next(gw)) - self.assertEqual([b"a" * 40, b"b" * 40, b"c" * 40, b"d" * 40], sorted(walk)) + self.assertEqual([b"a" * 40, b"b" * 40, b"c" * 40, b"d" * 40], + sorted(walk)) self.assertLess(walk.index(b"a" * 40), walk.index(b"c" * 40)) self.assertLess(walk.index(b"b" * 40), walk.index(b"d" * 40)) + + +class CommitTreeChangesTests(TestCase): + + def setUp(self): + super(CommitTreeChangesTests, self).setUp() + self.store = MemoryObjectStore() + self.blob_a = make_object(Blob, data=b'a') + self.blob_b = make_object(Blob, data=b'b') + self.blob_c = make_object(Blob, data=b'c') + for blob in [self.blob_a, self.blob_b, self.blob_c]: + self.store.add_object(blob) + + blobs = [ + (b'a', self.blob_a.id, 0o100644), + (b'ad/b', self.blob_b.id, 0o100644), + (b'ad/bd/c', self.blob_c.id, 0o100755), + (b'ad/c', self.blob_c.id, 0o100644), + (b'c', self.blob_c.id, 0o100644), + ] + self.tree_id = commit_tree(self.store, blobs) + + def test_no_changes(self): + self.assertEqual( + self.store[self.tree_id], + commit_tree_changes(self.store, self.store[self.tree_id], [])) + + def test_add_blob(self): + blob_d = make_object(Blob, data=b'd') + new_tree = commit_tree_changes( + self.store, self.store[self.tree_id], [ + (b'd', 0o100644, blob_d.id)]) + self.assertEqual( + new_tree[b'd'], + (33188, b'c59d9b6344f1af00e504ba698129f07a34bbed8d')) + + def test_add_blob_in_dir(self): + blob_d = make_object(Blob, data=b'd') + new_tree = commit_tree_changes( + self.store, self.store[self.tree_id], [ + (b'e/f/d', 0o100644, blob_d.id)]) + self.assertEqual( + new_tree.items(), [ + TreeEntry(path=b'a', mode=stat.S_IFREG | 0o100644, + sha=self.blob_a.id), + TreeEntry(path=b'ad', mode=stat.S_IFDIR, + sha=b'0e2ce2cd7725ff4817791be31ccd6e627e801f4a'), + TreeEntry(path=b'c', mode=stat.S_IFREG | 0o100644, + sha=self.blob_c.id), + TreeEntry(path=b'e', mode=stat.S_IFDIR, + sha=b'6ab344e288724ac2fb38704728b8896e367ed108') + ]) + e_tree = self.store[new_tree[b'e'][1]] + self.assertEqual( + e_tree.items(), [ + TreeEntry(path=b'f', mode=stat.S_IFDIR, + sha=b'24d2c94d8af232b15a0978c006bf61ef4479a0a5') + ]) + f_tree = self.store[e_tree[b'f'][1]] + self.assertEqual( + f_tree.items(), [ + TreeEntry(path=b'd', mode=stat.S_IFREG | 0o100644, + sha=blob_d.id) + ]) + + def test_delete_blob(self): + new_tree = commit_tree_changes( + self.store, self.store[self.tree_id], [ + (b'ad/bd/c', None, None)]) + self.assertEqual(set(new_tree), {b'a', b'ad', b'c'}) + ad_tree = self.store[new_tree[b'ad'][1]] + self.assertEqual(set(ad_tree), {b'b', b'c'}) diff -Nru dulwich-0.12.0/dulwich/tests/test_pack.py dulwich-0.18.5/dulwich/tests/test_pack.py --- dulwich-0.12.0/dulwich/tests/test_pack.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_pack.py 2017-10-07 15:45:18.000000000 +0000 @@ -2,20 +2,22 @@ # Copyright (C) 2007 James Westby # Copyright (C) 2008 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License, or (at your option) any later version of the license. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for Dulwich packs.""" @@ -91,19 +93,24 @@ self.tempdir = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, self.tempdir) - datadir = os.path.abspath(os.path.join(os.path.dirname(__file__), - 'data/packs')) + datadir = os.path.abspath( + os.path.join(os.path.dirname(__file__), 'data/packs')) def get_pack_index(self, sha): """Returns a PackIndex from the datadir with the given sha""" - return load_pack_index(os.path.join(self.datadir, 'pack-%s.idx' % sha.decode('ascii'))) + return load_pack_index( + os.path.join(self.datadir, + 'pack-%s.idx' % sha.decode('ascii'))) def get_pack_data(self, sha): """Returns a PackData object from the datadir with the given sha""" - return PackData(os.path.join(self.datadir, 'pack-%s.pack' % sha.decode('ascii'))) + return PackData( + os.path.join( + self.datadir, 'pack-%s.pack' % sha.decode('ascii'))) def get_pack(self, sha): - return Pack(os.path.join(self.datadir, 'pack-%s' % sha.decode('ascii'))) + return Pack( + os.path.join(self.datadir, 'pack-%s' % sha.decode('ascii'))) def assertSucceeds(self, func, *args, **kwargs): try: @@ -163,8 +170,9 @@ test_string_huge = b'Z' * 100000 def _test_roundtrip(self, base, target): - self.assertEqual(target, - b''.join(apply_delta(base, create_delta(base, target)))) + self.assertEqual( + target, + b''.join(apply_delta(base, create_delta(base, target)))) def test_nochange(self): self._test_roundtrip(self.test_string1, self.test_string1) @@ -189,13 +197,50 @@ self.test_string_huge + self.test_string2) def test_dest_overflow(self): - self.assertRaises( - ApplyDeltaError, - apply_delta, b'a'*0x10000, b'\x80\x80\x04\x80\x80\x04\x80' + b'a'*0x10000) + self.assertRaises(ApplyDeltaError, apply_delta, + b'a'*0x10000, b'\x80\x80\x04\x80\x80\x04\x80' + + b'a'*0x10000) self.assertRaises( ApplyDeltaError, apply_delta, b'', b'\x00\x80\x02\xb0\x11\x11') + def test_pypy_issue(self): + # Test for https://github.com/jelmer/dulwich/issues/509 / + # https://bitbucket.org/pypy/pypy/issues/2499/cpyext-pystring_asstring-doesnt-work + chunks = [ + b'tree 03207ccf58880a748188836155ceed72f03d65d6\n' + b'parent 408fbab530fd4abe49249a636a10f10f44d07a21\n' + b'author Victor Stinner ' + b'1421355207 +0100\n' + b'committer Victor Stinner ' + b'1421355207 +0100\n' + b'\n' + b'Backout changeset 3a06020af8cf\n' + b'\nStreamWriter: close() now clears the reference to the ' + b'transport\n' + b'\nStreamWriter now raises an exception if it is closed: ' + b'write(), writelines(),\n' + b'write_eof(), can_write_eof(), get_extra_info(), drain().\n'] + delta = [ + b'\xcd\x03\xad\x03]tree ff3c181a393d5a7270cddc01ea863818a8621ca8\n' + b'parent 20a103cc90135494162e819f98d0edfc1f1fba6b\x91]7\x0510738' + b'\x91\x99@\x0b10738 +0100\x93\x04\x01\xc9'] + res = apply_delta(chunks, delta) + expected = [ + b'tree ff3c181a393d5a7270cddc01ea863818a8621ca8\n' + b'parent 20a103cc90135494162e819f98d0edfc1f1fba6b', + b'\nauthor Victor Stinner 14213', + b'10738', + b' +0100\ncommitter Victor Stinner ' + b'14213', + b'10738 +0100', + b'\n\nStreamWriter: close() now clears the reference to the ' + b'transport\n\n' + b'StreamWriter now raises an exception if it is closed: ' + b'write(), writelines(),\n' + b'write_eof(), can_write_eof(), get_extra_info(), drain().\n'] + self.assertEqual(b''.join(expected), b''.join(res)) + class TestPackData(PackTests): """Tests getting the data from the packfile.""" @@ -204,7 +249,8 @@ self.get_pack_data(pack1_sha).close() def test_from_file(self): - path = os.path.join(self.datadir, 'pack-%s.pack' % pack1_sha.decode('ascii')) + path = os.path.join(self.datadir, + 'pack-%s.pack' % pack1_sha.decode('ascii')) with open(path, 'rb') as f: PackData.from_file(f, os.path.getsize(path)) @@ -218,13 +264,14 @@ def test_iterobjects(self): with self.get_pack_data(pack1_sha) as p: - commit_data = (b'tree b2a2766a2879c209ab1176e7e778b81ae422eeaa\n' - b'author James Westby ' - b'1174945067 +0100\n' - b'committer James Westby ' - b'1174945067 +0100\n' - b'\n' - b'Test commit\n') + commit_data = ( + b'tree b2a2766a2879c209ab1176e7e778b81ae422eeaa\n' + b'author James Westby ' + b'1174945067 +0100\n' + b'committer James Westby ' + b'1174945067 +0100\n' + b'\n' + b'Test commit\n') blob_sha = b'6f670c0fb53f9463760b7295fbb814e965fb20c8' tree_data = b'100644 a\0' + hex_to_sha(blob_sha) actual = [] @@ -280,7 +327,7 @@ self.assertRaises(AssertionError, compute_file_sha, f, end_ofs=-20) self.assertRaises(AssertionError, compute_file_sha, f, end_ofs=20) self.assertRaises(AssertionError, compute_file_sha, f, start_ofs=10, - end_ofs=-12) + end_ofs=-12) class TestPack(PackTests): @@ -309,7 +356,8 @@ def test_pack_tuples(self): with self.get_pack(pack1_sha) as p: tuples = p.pack_tuples() - expected = set([(p[s], None) for s in [commit_sha, tree_sha, a_sha]]) + expected = set( + [(p[s], None) for s in [commit_sha, tree_sha, a_sha]]) self.assertEqual(expected, set(list(tuples))) self.assertEqual(expected, set(list(tuples))) self.assertEqual(3, len(tuples)) @@ -338,7 +386,7 @@ self.assertSucceeds(newpack.index.check) self.assertEqual(origpack.name(), newpack.name()) self.assertEqual(origpack.index.get_pack_checksum(), - newpack.index.get_pack_checksum()) + newpack.index.get_pack_checksum()) wrong_version = origpack.index.version != newpack.index.version orig_checksum = origpack.index.get_stored_checksum() @@ -545,10 +593,10 @@ entry1_sha = hex_to_sha('4e6388232ec39792661e2e75db8fb117fc869ce6') entry2_sha = hex_to_sha('e98f071751bd77f59967bfa671cd2caebdccc9a2') entries = [(entry1_sha, 0xf2972d0830529b87, 24), - (entry2_sha, (~0xf2972d0830529b87)&(2**64-1), 92)] + (entry2_sha, (~0xf2972d0830529b87) & (2 ** 64 - 1), 92)] if not self._supports_large: self.assertRaises(TypeError, self.index, 'single.idx', - entries, pack_checksum) + entries, pack_checksum) return idx = self.index('single.idx', entries, pack_checksum) self.assertEqual(idx.get_pack_checksum(), pack_checksum) @@ -665,7 +713,8 @@ def setUp(self): super(ReadZlibTests, self).setUp() self.read = BytesIO(self.comp + self.extra).read - self.unpacked = UnpackedObject(Tree.type_num, None, len(self.decomp), 0) + self.unpacked = UnpackedObject( + Tree.type_num, None, len(self.decomp), 0) def test_decompress_size(self): good_decomp_len = len(self.decomp) @@ -931,7 +980,8 @@ n = 100 objects_spec = [(Blob.type_num, b'blob')] for i in range(n): - objects_spec.append((OFS_DELTA, (i, b'blob' + str(i).encode('ascii')))) + objects_spec.append( + (OFS_DELTA, (i, b'blob' + str(i).encode('ascii')))) f = BytesIO() entries = build_pack(f, objects_spec) self.assertEntriesMatch(range(n + 1), entries, self.make_pack_iter(f)) @@ -940,7 +990,8 @@ n = 100 objects_spec = [(Blob.type_num, b'blob')] for i in range(n): - objects_spec.append((OFS_DELTA, (0, b'blob' + str(i).encode('ascii')))) + objects_spec.append( + (OFS_DELTA, (0, b'blob' + str(i).encode('ascii')))) f = BytesIO() entries = build_pack(f, objects_spec) self.assertEntriesMatch(range(n + 1), entries, self.make_pack_iter(f)) @@ -1007,8 +1058,7 @@ def test_bad_ext_ref_non_thin_pack(self): blob, = self.store_blobs([b'blob']) f = BytesIO() - entries = build_pack(f, [(REF_DELTA, (blob.id, b'blob1'))], - store=self.store) + build_pack(f, [(REF_DELTA, (blob.id, b'blob1'))], store=self.store) pack_iter = self.make_pack_iter(f, thin=False) try: list(pack_iter._walk_all_chains()) @@ -1050,5 +1100,7 @@ def test_basic(self): self.assertEqual(b'\x80', _encode_copy_operation(0, 0)) self.assertEqual(b'\x91\x01\x0a', _encode_copy_operation(1, 10)) - self.assertEqual(b'\xb1\x64\xe8\x03', _encode_copy_operation(100, 1000)) - self.assertEqual(b'\x93\xe8\x03\x01', _encode_copy_operation(1000, 1)) + self.assertEqual(b'\xb1\x64\xe8\x03', + _encode_copy_operation(100, 1000)) + self.assertEqual(b'\x93\xe8\x03\x01', + _encode_copy_operation(1000, 1)) diff -Nru dulwich-0.12.0/dulwich/tests/test_patch.py dulwich-0.18.5/dulwich/tests/test_patch.py --- dulwich-0.12.0/dulwich/tests/test_patch.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_patch.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,25 +1,26 @@ # test_patch.py -- tests for patch.py # Copyright (C) 2010 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) a later version. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for patch.py.""" from io import BytesIO, StringIO -import sys from dulwich.objects import ( Blob, @@ -56,7 +57,8 @@ write_commit_patch(f, c, b"CONTENTS", (1, 1), version="custom") f.seek(0) lines = f.readlines() - self.assertTrue(lines[0].startswith(b"From 0b0d34d1b5b596c928adc9a727a4b9e03d025298")) + self.assertTrue(lines[0].startswith( + b"From 0b0d34d1b5b596c928adc9a727a4b9e03d025298")) self.assertEqual(lines[1], b"From: Jelmer \n") self.assertTrue(lines[2].startswith(b"Date: ")) self.assertEqual([ @@ -76,13 +78,11 @@ class ReadGitAmPatch(TestCase): def test_extract_string(self): - if sys.version_info[:2] <= (2, 6): - raise SkipTest("email.parser.Parser.parsestr() inserts extra lines") - - text = b"""From ff643aae102d8870cac88e8f007e70f58f3a7363 Mon Sep 17 00:00:00 2001 + text = b"""\ +From ff643aae102d8870cac88e8f007e70f58f3a7363 Mon Sep 17 00:00:00 2001 From: Jelmer Vernooij Date: Thu, 15 Apr 2010 15:40:28 +0200 -Subject: [PATCH 1/2] Remove executable bit from prey.ico (triggers a lintian warning). +Subject: [PATCH 1/2] Remove executable bit from prey.ico (triggers a warning). --- pixmaps/prey.ico | Bin 9662 -> 9662 bytes @@ -91,12 +91,13 @@ -- 1.7.0.4 -""" - c, diff, version = git_am_patch_split(StringIO(text.decode("utf-8")), "utf-8") +""" # noqa: W291 + c, diff, version = git_am_patch_split( + StringIO(text.decode("utf-8")), "utf-8") self.assertEqual(b"Jelmer Vernooij ", c.committer) self.assertEqual(b"Jelmer Vernooij ", c.author) self.assertEqual(b"Remove executable bit from prey.ico " - b"(triggers a lintian warning).\n", c.message) + b"(triggers a warning).\n", c.message) self.assertEqual(b""" pixmaps/prey.ico | Bin 9662 -> 9662 bytes 1 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 pixmaps/prey.ico @@ -105,10 +106,11 @@ self.assertEqual(b"1.7.0.4", version) def test_extract_bytes(self): - text = b"""From ff643aae102d8870cac88e8f007e70f58f3a7363 Mon Sep 17 00:00:00 2001 + text = b"""\ +From ff643aae102d8870cac88e8f007e70f58f3a7363 Mon Sep 17 00:00:00 2001 From: Jelmer Vernooij Date: Thu, 15 Apr 2010 15:40:28 +0200 -Subject: [PATCH 1/2] Remove executable bit from prey.ico (triggers a lintian warning). +Subject: [PATCH 1/2] Remove executable bit from prey.ico (triggers a warning). --- pixmaps/prey.ico | Bin 9662 -> 9662 bytes @@ -117,12 +119,12 @@ -- 1.7.0.4 -""" +""" # noqa: W291 c, diff, version = git_am_patch_split(BytesIO(text)) self.assertEqual(b"Jelmer Vernooij ", c.committer) self.assertEqual(b"Jelmer Vernooij ", c.author) self.assertEqual(b"Remove executable bit from prey.ico " - b"(triggers a lintian warning).\n", c.message) + b"(triggers a warning).\n", c.message) self.assertEqual(b""" pixmaps/prey.ico | Bin 9662 -> 9662 bytes 1 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 pixmaps/prey.ico @@ -147,9 +149,15 @@ -- 1.7.0.4 -""" +""" # noqa: W291 c, diff, version = git_am_patch_split(BytesIO(text), "utf-8") - self.assertEqual(b'Added unit tests for dulwich.object_store.tree_lookup_path.\n\n* dulwich/tests/test_object_store.py\n (TreeLookupPathTests): This test case contains a few tests that ensure the\n tree_lookup_path function works as expected.\n', c.message) + self.assertEqual(b'''\ +Added unit tests for dulwich.object_store.tree_lookup_path. + +* dulwich/tests/test_object_store.py + (TreeLookupPathTests): This test case contains a few tests that ensure the + tree_lookup_path function works as expected. +''', c.message) def test_extract_pseudo_from_header(self): text = b"""From ff643aae102d8870cac88e8f007e70f58f3a7363 Mon Sep 17 00:00:00 2001 @@ -170,13 +178,20 @@ -- 1.7.0.4 -""" +""" # noqa: W291 c, diff, version = git_am_patch_split(BytesIO(text), "utf-8") self.assertEqual(b"Jelmer Vernooy ", c.author) - self.assertEqual(b'Added unit tests for dulwich.object_store.tree_lookup_path.\n\n* dulwich/tests/test_object_store.py\n (TreeLookupPathTests): This test case contains a few tests that ensure the\n tree_lookup_path function works as expected.\n', c.message) + self.assertEqual(b'''\ +Added unit tests for dulwich.object_store.tree_lookup_path. + +* dulwich/tests/test_object_store.py + (TreeLookupPathTests): This test case contains a few tests that ensure the + tree_lookup_path function works as expected. +''', c.message) def test_extract_no_version_tail(self): - text = b"""From ff643aae102d8870cac88e8f007e70f58f3a7363 Mon Sep 17 00:00:00 2001 + text = b"""\ +From ff643aae102d8870cac88e8f007e70f58f3a7363 Mon Sep 17 00:00:00 2001 From: Jelmer Vernooij Date: Thu, 15 Apr 2010 15:40:28 +0200 Subject: [Dulwich-users] [PATCH] Added unit tests for @@ -194,8 +209,11 @@ self.assertEqual(None, version) def test_extract_mercurial(self): - raise SkipTest("git_am_patch_split doesn't handle Mercurial patches properly yet") - expected_diff = """diff --git a/dulwich/tests/test_patch.py b/dulwich/tests/test_patch.py + raise SkipTest( + "git_am_patch_split doesn't handle Mercurial patches " + "properly yet") + expected_diff = """\ +diff --git a/dulwich/tests/test_patch.py b/dulwich/tests/test_patch.py --- a/dulwich/tests/test_patch.py +++ b/dulwich/tests/test_patch.py @@ -158,7 +158,7 @@ @@ -207,8 +225,10 @@ class DiffTests(TestCase): -""" - text = """From dulwich-users-bounces+jelmer=samba.org@lists.launchpad.net Mon Nov 29 00:58:18 2010 +""" # noqa: W291,W293 + text = """\ +From dulwich-users-bounces+jelmer=samba.org@lists.launchpad.net \ +Mon Nov 29 00:58:18 2010 Date: Sun, 28 Nov 2010 17:57:27 -0600 From: Augie Fackler To: dulwich-users @@ -225,7 +245,7 @@ Unsubscribe : https://launchpad.net/~dulwich-users More help : https://help.launchpad.net/ListHelp -""" % expected_diff +""" % expected_diff # noqa: W291 c, diff, version = git_am_patch_split(BytesIO(text)) self.assertEqual(expected_diff, diff) self.assertEqual(None, version) @@ -236,8 +256,9 @@ def test_blob_diff(self): f = BytesIO() - write_blob_diff(f, (b"foo.txt", 0o644, Blob.from_string(b"old\nsame\n")), - (b"bar.txt", 0o644, Blob.from_string(b"new\nsame\n"))) + write_blob_diff( + f, (b"foo.txt", 0o644, Blob.from_string(b"old\nsame\n")), + (b"bar.txt", 0o644, Blob.from_string(b"new\nsame\n"))) self.assertEqual([ b"diff --git a/foo.txt b/bar.txt", b"index 3b0f961..a116b51 644", @@ -251,30 +272,32 @@ def test_blob_add(self): f = BytesIO() - write_blob_diff(f, (None, None, None), - (b"bar.txt", 0o644, Blob.from_string(b"new\nsame\n"))) + write_blob_diff( + f, (None, None, None), + (b"bar.txt", 0o644, Blob.from_string(b"new\nsame\n"))) self.assertEqual([ b'diff --git /dev/null b/bar.txt', b'new mode 644', b'index 0000000..a116b51 644', b'--- /dev/null', b'+++ b/bar.txt', - b'@@ -1,0 +1,2 @@', + b'@@ -0,0 +1,2 @@', b'+new', b'+same' ], f.getvalue().splitlines()) def test_blob_remove(self): f = BytesIO() - write_blob_diff(f, (b"bar.txt", 0o644, Blob.from_string(b"new\nsame\n")), - (None, None, None)) + write_blob_diff( + f, (b"bar.txt", 0o644, Blob.from_string(b"new\nsame\n")), + (None, None, None)) self.assertEqual([ b'diff --git a/bar.txt /dev/null', b'deleted mode 644', b'index a116b51..0000000', b'--- a/bar.txt', b'+++ /dev/null', - b'@@ -1,2 +1,0 @@', + b'@@ -1,2 +0,0 @@', b'-new', b'-same' ], f.getvalue().splitlines()) @@ -304,7 +327,7 @@ b'index 0000000..76d4bb8 644', b'--- /dev/null', b'+++ b/added.txt', - b'@@ -1,0 +1,1 @@', + b'@@ -0,0 +1 @@', b'+add', b'diff --git a/changed.txt b/changed.txt', b'index bf84e48..1be2436 644', @@ -319,7 +342,7 @@ b'index 2c3f0b3..0000000', b'--- a/removed.txt', b'+++ /dev/null', - b'@@ -1,1 +1,0 @@', + b'@@ -1 +0,0 @@', b'-removed', ], f.getvalue().splitlines()) @@ -328,10 +351,10 @@ store = MemoryObjectStore() tree1 = Tree() tree1.add(b"asubmodule", S_IFGITLINK, - b"06d0bdd9e2e20377b3180e4986b14c8549b393e4") + b"06d0bdd9e2e20377b3180e4986b14c8549b393e4") tree2 = Tree() tree2.add(b"asubmodule", S_IFGITLINK, - b"cc975646af69f279396d4d5e1379ac6af80ee637") + b"cc975646af69f279396d4d5e1379ac6af80ee637") store.add_objects([(o, None) for o in [tree1, tree2]]) write_tree_diff(f, store, tree1.id, tree2.id) self.assertEqual([ @@ -339,7 +362,7 @@ b'index 06d0bdd..cc97564 160000', b'--- a/asubmodule', b'+++ b/asubmodule', - b'@@ -1,1 +1,1 @@', + b'@@ -1 +1 @@', b'-Submodule commit 06d0bdd9e2e20377b3180e4986b14c8549b393e4', b'+Submodule commit cc975646af69f279396d4d5e1379ac6af80ee637', ], f.getvalue().splitlines()) @@ -376,7 +399,7 @@ b'index 0000000..a116b51 644', b'--- /dev/null', b'+++ b/bar.txt', - b'@@ -1,0 +1,2 @@', + b'@@ -0,0 +1,2 @@', b'+new', b'+same' ], f.getvalue().splitlines()) @@ -394,7 +417,7 @@ b'index a116b51..0000000', b'--- a/bar.txt', b'+++ /dev/null', - b'@@ -1,2 +1,0 @@', + b'@@ -1,2 +0,0 @@', b'-new', b'-same' ], f.getvalue().splitlines()) @@ -403,15 +426,20 @@ f = BytesIO() # Prepare two slightly different PNG headers b1 = Blob.from_string( - b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" - b"\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x04\x00\x00\x00\x05\x04\x8b") + b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a" + b"\x00\x00\x00\x0d\x49\x48\x44\x52" + b"\x00\x00\x01\xd5\x00\x00\x00\x9f" + b"\x08\x04\x00\x00\x00\x05\x04\x8b") b2 = Blob.from_string( - b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" - b"\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x03\x00\x00\x00\x98\xd3\xb3") + b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a" + b"\x00\x00\x00\x0d\x49\x48\x44\x52" + b"\x00\x00\x01\xd5\x00\x00\x00\x9f" + b"\x08\x03\x00\x00\x00\x98\xd3\xb3") store = MemoryObjectStore() store.add_objects([(b1, None), (b2, None)]) - write_object_diff(f, store, (b'foo.png', 0o644, b1.id), - (b'bar.png', 0o644, b2.id), diff_binary=True) + write_object_diff( + f, store, (b'foo.png', 0o644, b1.id), + (b'bar.png', 0o644, b2.id), diff_binary=True) self.assertEqual([ b'diff --git a/foo.png b/bar.png', b'index f73e47d..06364b7 644', @@ -421,9 +449,11 @@ b' \x89PNG', b' \x1a', b' \x00\x00\x00', - b'-IHDR\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x04\x00\x00\x00\x05\x04\x8b', + b'-IHDR\x00\x00\x01\xd5\x00\x00\x00' + b'\x9f\x08\x04\x00\x00\x00\x05\x04\x8b', b'\\ No newline at end of file', - b'+IHDR\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x03\x00\x00\x00\x98\xd3\xb3', + b'+IHDR\x00\x00\x01\xd5\x00\x00\x00\x9f' + b'\x08\x03\x00\x00\x00\x98\xd3\xb3', b'\\ No newline at end of file' ], f.getvalue().splitlines()) @@ -431,11 +461,15 @@ f = BytesIO() # Prepare two slightly different PNG headers b1 = Blob.from_string( - b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" - b"\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x04\x00\x00\x00\x05\x04\x8b") + b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a" + b"\x00\x00\x00\x0d\x49\x48\x44\x52" + b"\x00\x00\x01\xd5\x00\x00\x00\x9f" + b"\x08\x04\x00\x00\x00\x05\x04\x8b") b2 = Blob.from_string( - b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" - b"\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x03\x00\x00\x00\x98\xd3\xb3") + b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a" + b"\x00\x00\x00\x0d\x49\x48\x44\x52" + b"\x00\x00\x01\xd5\x00\x00\x00\x9f" + b"\x08\x03\x00\x00\x00\x98\xd3\xb3") store = MemoryObjectStore() store.add_objects([(b1, None), (b2, None)]) write_object_diff(f, store, (b'foo.png', 0o644, b1.id), @@ -449,8 +483,10 @@ def test_object_diff_add_bin_blob(self): f = BytesIO() b2 = Blob.from_string( - b'\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52' - b'\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x03\x00\x00\x00\x98\xd3\xb3') + b'\x89\x50\x4e\x47\x0d\x0a\x1a\x0a' + b'\x00\x00\x00\x0d\x49\x48\x44\x52' + b'\x00\x00\x01\xd5\x00\x00\x00\x9f' + b'\x08\x03\x00\x00\x00\x98\xd3\xb3') store = MemoryObjectStore() store.add_object(b2) write_object_diff(f, store, (None, None, None), @@ -465,8 +501,10 @@ def test_object_diff_remove_bin_blob(self): f = BytesIO() b1 = Blob.from_string( - b'\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52' - b'\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x04\x00\x00\x00\x05\x04\x8b') + b'\x89\x50\x4e\x47\x0d\x0a\x1a\x0a' + b'\x00\x00\x00\x0d\x49\x48\x44\x52' + b'\x00\x00\x01\xd5\x00\x00\x00\x9f' + b'\x08\x04\x00\x00\x00\x05\x04\x8b') store = MemoryObjectStore() store.add_object(b1) write_object_diff(f, store, (b'foo.png', 0o644, b1.id), @@ -483,8 +521,10 @@ b1 = Blob.from_string(b"new\nsame\n") store = MemoryObjectStore() store.add_object(b1) - write_object_diff(f, store, (b"bar.txt", 0o644, b1.id), - (b"bar.txt", 0o160000, b"06d0bdd9e2e20377b3180e4986b14c8549b393e4")) + write_object_diff( + f, store, (b"bar.txt", 0o644, b1.id), + (b"bar.txt", 0o160000, + b"06d0bdd9e2e20377b3180e4986b14c8549b393e4")) self.assertEqual([ b'diff --git a/bar.txt b/bar.txt', b'old mode 644', @@ -492,7 +532,7 @@ b'index a116b51..06d0bdd 160000', b'--- a/bar.txt', b'+++ b/bar.txt', - b'@@ -1,2 +1,1 @@', + b'@@ -1,2 +1 @@', b'-new', b'-same', b'+Submodule commit 06d0bdd9e2e20377b3180e4986b14c8549b393e4', diff -Nru dulwich-0.12.0/dulwich/tests/test_porcelain.py dulwich-0.18.5/dulwich/tests/test_porcelain.py --- dulwich-0.12.0/dulwich/tests/test_porcelain.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_porcelain.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,24 +1,25 @@ # test_porcelain.py -- porcelain tests # Copyright (C) 2013 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) a later version. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for dulwich.porcelain.""" -from contextlib import closing from io import BytesIO try: from StringIO import StringIO @@ -28,6 +29,7 @@ import shutil import tarfile import tempfile +import time from dulwich import porcelain from dulwich.diff_tree import tree_changes @@ -35,6 +37,7 @@ Blob, Tag, Tree, + ZERO_SHA, ) from dulwich.repo import Repo from dulwich.tests import ( @@ -42,6 +45,7 @@ ) from dulwich.tests.utils import ( build_commit_graph, + make_commit, make_object, ) @@ -63,12 +67,13 @@ """Tests for the archive command.""" def test_simple(self): - c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], [3, 1, 2]]) + c1, c2, c3 = build_commit_graph( + self.repo.object_store, [[1], [2, 1], [3, 1, 2]]) self.repo.refs[b"refs/heads/master"] = c3.id out = BytesIO() err = BytesIO() porcelain.archive(self.repo.path, b"refs/heads/master", outstream=out, - errstream=err) + errstream=err) self.assertEqual(b"", err.getvalue()) tf = tarfile.TarFile(fileobj=out) self.addCleanup(tf.close) @@ -78,22 +83,24 @@ class UpdateServerInfoTests(PorcelainTestCase): def test_simple(self): - c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], - [3, 1, 2]]) + c1, c2, c3 = build_commit_graph( + self.repo.object_store, [[1], [2, 1], [3, 1, 2]]) self.repo.refs[b"refs/heads/foo"] = c3.id porcelain.update_server_info(self.repo.path) - self.assertTrue(os.path.exists(os.path.join(self.repo.controldir(), - 'info', 'refs'))) + self.assertTrue(os.path.exists( + os.path.join(self.repo.controldir(), 'info', 'refs'))) class CommitTests(PorcelainTestCase): def test_custom_author(self): - c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], - [3, 1, 2]]) + c1, c2, c3 = build_commit_graph( + self.repo.object_store, [[1], [2, 1], [3, 1, 2]]) self.repo.refs[b"refs/heads/foo"] = c3.id - sha = porcelain.commit(self.repo.path, message=b"Some message", - author=b"Joe ", committer=b"Bob ") + sha = porcelain.commit( + self.repo.path, message=b"Some message", + author=b"Joe ", + committer=b"Bob ") self.assertTrue(isinstance(sha, bytes)) self.assertEqual(len(sha), 40) @@ -110,15 +117,26 @@ c1, c2, c3 = build_commit_graph(self.repo.object_store, commit_spec, trees) self.repo.refs[b"refs/heads/master"] = c3.id + self.repo.refs[b"refs/tags/foo"] = c3.id target_path = tempfile.mkdtemp() errstream = BytesIO() self.addCleanup(shutil.rmtree, target_path) r = porcelain.clone(self.repo.path, target_path, checkout=False, errstream=errstream) self.assertEqual(r.path, target_path) - self.assertEqual(Repo(target_path).head(), c3.id) + target_repo = Repo(target_path) + self.assertEqual(target_repo.head(), c3.id) + self.assertEqual(c3.id, target_repo.refs[b'refs/tags/foo']) self.assertTrue(b'f1' not in os.listdir(target_path)) self.assertTrue(b'f2' not in os.listdir(target_path)) + c = r.get_config() + encoded_path = self.repo.path + if not isinstance(encoded_path, bytes): + encoded_path = encoded_path.encode('utf-8') + self.assertEqual(encoded_path, c.get((b'remote', b'origin'), b'url')) + self.assertEqual( + b'+refs/heads/*:refs/remotes/origin/*', + c.get((b'remote', b'origin'), b'fetch')) def test_simple_local_with_checkout(self): f1_1 = make_object(Blob, data=b'f1') @@ -133,11 +151,11 @@ target_path = tempfile.mkdtemp() errstream = BytesIO() self.addCleanup(shutil.rmtree, target_path) - with closing(porcelain.clone(self.repo.path, target_path, - checkout=True, - errstream=errstream)) as r: + with porcelain.clone(self.repo.path, target_path, + checkout=True, + errstream=errstream) as r: self.assertEqual(r.path, target_path) - with closing(Repo(target_path)) as r: + with Repo(target_path) as r: self.assertEqual(r.head(), c3.id) self.assertTrue('f1' in os.listdir(target_path)) self.assertTrue('f2' in os.listdir(target_path)) @@ -155,10 +173,12 @@ target_path = tempfile.mkdtemp() errstream = BytesIO() self.addCleanup(shutil.rmtree, target_path) - r = porcelain.clone(self.repo.path, target_path, - bare=True, errstream=errstream) - self.assertEqual(r.path, target_path) - self.assertEqual(Repo(target_path).head(), c3.id) + with porcelain.clone( + self.repo.path, target_path, bare=True, + errstream=errstream) as r: + self.assertEqual(r.path, target_path) + with Repo(target_path) as r: + self.assertRaises(KeyError, r.head) self.assertFalse(b'f1' in os.listdir(target_path)) self.assertFalse(b'f2' in os.listdir(target_path)) @@ -169,12 +189,28 @@ (c1, ) = build_commit_graph(self.repo.object_store, commit_spec, trees) self.repo.refs[b"refs/heads/master"] = c1.id + self.repo.refs[b"HEAD"] = c1.id target_path = tempfile.mkdtemp() errstream = BytesIO() self.addCleanup(shutil.rmtree, target_path) - self.assertRaises(ValueError, porcelain.clone, self.repo.path, + self.assertRaises( + ValueError, porcelain.clone, self.repo.path, target_path, checkout=True, bare=True, errstream=errstream) + def test_no_head_no_checkout(self): + f1_1 = make_object(Blob, data=b'f1') + commit_spec = [[1]] + trees = {1: [(b'f1', f1_1), (b'f2', f1_1)]} + + (c1, ) = build_commit_graph(self.repo.object_store, commit_spec, trees) + self.repo.refs[b"refs/heads/master"] = c1.id + target_path = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, target_path) + errstream = BytesIO() + r = porcelain.clone( + self.repo.path, target_path, checkout=True, errstream=errstream) + r.close() + class InitTests(TestCase): @@ -192,13 +228,13 @@ class AddTests(PorcelainTestCase): def test_add_default_paths(self): - # create a file for initial commit - with open(os.path.join(self.repo.path, 'blah'), 'w') as f: + fullpath = os.path.join(self.repo.path, 'blah') + with open(fullpath, 'w') as f: f.write("\n") - porcelain.add(repo=self.repo.path, paths=['blah']) + porcelain.add(repo=self.repo.path, paths=[fullpath]) porcelain.commit(repo=self.repo.path, message=b'test', - author=b'test', committer=b'test') + author=b'test', committer=b'test') # Add a second test file and a file in a directory with open(os.path.join(self.repo.path, 'foo'), 'w') as f: @@ -206,40 +242,110 @@ os.mkdir(os.path.join(self.repo.path, 'adir')) with open(os.path.join(self.repo.path, 'adir', 'afile'), 'w') as f: f.write("\n") - porcelain.add(self.repo.path) + cwd = os.getcwd() + try: + os.chdir(self.repo.path) + porcelain.add(self.repo.path) + finally: + os.chdir(cwd) # Check that foo was added and nothing in .git was modified index = self.repo.open_index() self.assertEqual(sorted(index), [b'adir/afile', b'blah', b'foo']) + def test_add_default_paths_subdir(self): + os.mkdir(os.path.join(self.repo.path, 'foo')) + with open(os.path.join(self.repo.path, 'blah'), 'w') as f: + f.write("\n") + with open(os.path.join(self.repo.path, 'foo', 'blie'), 'w') as f: + f.write("\n") + + cwd = os.getcwd() + try: + os.chdir(os.path.join(self.repo.path, 'foo')) + porcelain.add(repo=self.repo.path) + porcelain.commit(repo=self.repo.path, message=b'test', + author=b'test', committer=b'test') + finally: + os.chdir(cwd) + + index = self.repo.open_index() + self.assertEqual(sorted(index), [b'foo/blie']) + def test_add_file(self): + fullpath = os.path.join(self.repo.path, 'foo') + with open(fullpath, 'w') as f: + f.write("BAR") + porcelain.add(self.repo.path, paths=[fullpath]) + self.assertIn(b"foo", self.repo.open_index()) + + def test_add_ignored(self): + with open(os.path.join(self.repo.path, '.gitignore'), 'w') as f: + f.write("foo") with open(os.path.join(self.repo.path, 'foo'), 'w') as f: f.write("BAR") - porcelain.add(self.repo.path, paths=["foo"]) + with open(os.path.join(self.repo.path, 'bar'), 'w') as f: + f.write("BAR") + (added, ignored) = porcelain.add(self.repo.path, paths=[ + os.path.join(self.repo.path, "foo"), + os.path.join(self.repo.path, "bar")]) + self.assertIn(b"bar", self.repo.open_index()) + self.assertEqual(set(['bar']), set(added)) + self.assertEqual(set(['foo']), ignored) + + def test_add_file_absolute_path(self): + # Absolute paths are (not yet) supported + with open(os.path.join(self.repo.path, 'foo'), 'w') as f: + f.write("BAR") + porcelain.add(self.repo, paths=[os.path.join(self.repo.path, "foo")]) + self.assertIn(b"foo", self.repo.open_index()) class RemoveTests(PorcelainTestCase): def test_remove_file(self): - with open(os.path.join(self.repo.path, 'foo'), 'w') as f: + fullpath = os.path.join(self.repo.path, 'foo') + with open(fullpath, 'w') as f: f.write("BAR") - porcelain.add(self.repo.path, paths=["foo"]) - porcelain.rm(self.repo.path, paths=["foo"]) + porcelain.add(self.repo.path, paths=[fullpath]) + porcelain.commit(repo=self.repo, message=b'test', author=b'test', + committer=b'test') + self.assertTrue(os.path.exists(os.path.join(self.repo.path, 'foo'))) + cwd = os.getcwd() + try: + os.chdir(self.repo.path) + porcelain.remove(self.repo.path, paths=["foo"]) + finally: + os.chdir(cwd) + self.assertFalse(os.path.exists(os.path.join(self.repo.path, 'foo'))) + + def test_remove_file_staged(self): + fullpath = os.path.join(self.repo.path, 'foo') + with open(fullpath, 'w') as f: + f.write("BAR") + cwd = os.getcwd() + try: + os.chdir(self.repo.path) + porcelain.add(self.repo.path, paths=[fullpath]) + self.assertRaises(Exception, porcelain.rm, self.repo.path, + paths=["foo"]) + finally: + os.chdir(cwd) class LogTests(PorcelainTestCase): def test_simple(self): - c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], - [3, 1, 2]]) + c1, c2, c3 = build_commit_graph( + self.repo.object_store, [[1], [2, 1], [3, 1, 2]]) self.repo.refs[b"HEAD"] = c3.id outstream = StringIO() porcelain.log(self.repo.path, outstream=outstream) self.assertEqual(3, outstream.getvalue().count("-" * 50)) def test_max_entries(self): - c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], - [3, 1, 2]]) + c1, c2, c3 = build_commit_graph( + self.repo.object_store, [[1], [2, 1], [3, 1, 2]]) self.repo.refs[b"HEAD"] = c3.id outstream = StringIO() porcelain.log(self.repo.path, outstream=outstream, max_entries=1) @@ -249,16 +355,16 @@ class ShowTests(PorcelainTestCase): def test_nolist(self): - c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], - [3, 1, 2]]) + c1, c2, c3 = build_commit_graph( + self.repo.object_store, [[1], [2, 1], [3, 1, 2]]) self.repo.refs[b"HEAD"] = c3.id outstream = StringIO() porcelain.show(self.repo.path, objects=c3.id, outstream=outstream) self.assertTrue(outstream.getvalue().startswith("-" * 50)) def test_simple(self): - c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], - [3, 1, 2]]) + c1, c2, c3 = build_commit_graph( + self.repo.object_store, [[1], [2, 1], [3, 1, 2]]) self.repo.refs[b"HEAD"] = c3.id outstream = StringIO() porcelain.show(self.repo.path, objects=[c3.id], outstream=outstream) @@ -271,44 +377,104 @@ porcelain.show(self.repo.path, objects=[b.id], outstream=outstream) self.assertEqual(outstream.getvalue(), "The Foo\n") + def test_commit_no_parent(self): + a = Blob.from_string(b"The Foo\n") + ta = Tree() + ta.add(b"somename", 0o100644, a.id) + ca = make_commit(tree=ta.id) + self.repo.object_store.add_objects([(a, None), (ta, None), (ca, None)]) + outstream = StringIO() + porcelain.show(self.repo.path, objects=[ca.id], outstream=outstream) + self.assertMultiLineEqual(outstream.getvalue(), """\ +-------------------------------------------------- +commit: 344da06c1bb85901270b3e8875c988a027ec087d +Author: Test Author +Committer: Test Committer +Date: Fri Jan 01 2010 00:00:00 +0000 + +Test message. + +diff --git /dev/null b/somename +new mode 100644 +index 0000000..ea5c7bf 100644 +--- /dev/null ++++ b/somename +@@ -0,0 +1 @@ ++The Foo +""") + + def test_commit_with_change(self): + a = Blob.from_string(b"The Foo\n") + ta = Tree() + ta.add(b"somename", 0o100644, a.id) + ca = make_commit(tree=ta.id) + b = Blob.from_string(b"The Bar\n") + tb = Tree() + tb.add(b"somename", 0o100644, b.id) + cb = make_commit(tree=tb.id, parents=[ca.id]) + self.repo.object_store.add_objects( + [(a, None), (b, None), (ta, None), (tb, None), + (ca, None), (cb, None)]) + outstream = StringIO() + porcelain.show(self.repo.path, objects=[cb.id], outstream=outstream) + self.assertMultiLineEqual(outstream.getvalue(), """\ +-------------------------------------------------- +commit: 2c6b6c9cb72c130956657e1fdae58e5b103744fa +Author: Test Author +Committer: Test Committer +Date: Fri Jan 01 2010 00:00:00 +0000 + +Test message. + +diff --git a/somename b/somename +index ea5c7bf..fd38bcb 100644 +--- a/somename ++++ b/somename +@@ -1 +1 @@ +-The Foo ++The Bar +""") + class SymbolicRefTests(PorcelainTestCase): def test_set_wrong_symbolic_ref(self): - c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], - [3, 1, 2]]) + c1, c2, c3 = build_commit_graph( + self.repo.object_store, [[1], [2, 1], [3, 1, 2]]) self.repo.refs[b"HEAD"] = c3.id - self.assertRaises(ValueError, porcelain.symbolic_ref, self.repo.path, b'foobar') + self.assertRaises(ValueError, porcelain.symbolic_ref, self.repo.path, + b'foobar') def test_set_force_wrong_symbolic_ref(self): - c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], - [3, 1, 2]]) + c1, c2, c3 = build_commit_graph( + self.repo.object_store, [[1], [2, 1], [3, 1, 2]]) self.repo.refs[b"HEAD"] = c3.id porcelain.symbolic_ref(self.repo.path, b'force_foobar', force=True) - #test if we actually changed the file + # test if we actually changed the file with self.repo.get_named_file('HEAD') as f: new_ref = f.read() self.assertEqual(new_ref, b'ref: refs/heads/force_foobar\n') def test_set_symbolic_ref(self): - c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], - [3, 1, 2]]) + c1, c2, c3 = build_commit_graph( + self.repo.object_store, [[1], [2, 1], [3, 1, 2]]) self.repo.refs[b"HEAD"] = c3.id porcelain.symbolic_ref(self.repo.path, b'master') def test_set_symbolic_ref_other_than_master(self): - c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], - [3, 1, 2]], attrs=dict(refs='develop')) + c1, c2, c3 = build_commit_graph( + self.repo.object_store, [[1], [2, 1], [3, 1, 2]], + attrs=dict(refs='develop')) self.repo.refs[b"HEAD"] = c3.id self.repo.refs[b"refs/heads/develop"] = c3.id porcelain.symbolic_ref(self.repo.path, b'develop') - #test if we actually changed the file + # test if we actually changed the file with self.repo.get_named_file('HEAD') as f: new_ref = f.read() self.assertEqual(new_ref, b'ref: refs/heads/develop\n') @@ -317,19 +483,20 @@ class DiffTreeTests(PorcelainTestCase): def test_empty(self): - c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], - [3, 1, 2]]) + c1, c2, c3 = build_commit_graph( + self.repo.object_store, [[1], [2, 1], [3, 1, 2]]) self.repo.refs[b"HEAD"] = c3.id outstream = BytesIO() - porcelain.diff_tree(self.repo.path, c2.tree, c3.tree, outstream=outstream) + porcelain.diff_tree(self.repo.path, c2.tree, c3.tree, + outstream=outstream) self.assertEqual(outstream.getvalue(), b"") class CommitTreeTests(PorcelainTestCase): def test_simple(self): - c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], - [3, 1, 2]]) + c1, c2, c3 = build_commit_graph( + self.repo.object_store, [[1], [2, 1], [3, 1, 2]]) b = Blob() b.data = b"foo the bar" t = Tree() @@ -347,8 +514,8 @@ class RevListTests(PorcelainTestCase): def test_simple(self): - c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], - [3, 1, 2]]) + c1, c2, c3 = build_commit_graph( + self.repo.object_store, [[1], [2, 1], [3, 1, 2]]) outstream = BytesIO() porcelain.rev_list( self.repo.path, [c3.id], outstream=outstream) @@ -362,12 +529,12 @@ class TagCreateTests(PorcelainTestCase): def test_annotated(self): - c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], - [3, 1, 2]]) + c1, c2, c3 = build_commit_graph( + self.repo.object_store, [[1], [2, 1], [3, 1, 2]]) self.repo.refs[b"HEAD"] = c3.id porcelain.tag_create(self.repo.path, b"tryme", b'foo ', - b'bar', annotated=True) + b'bar', annotated=True) tags = self.repo.refs.as_dict(b"refs/tags") self.assertEqual(list(tags.keys()), [b"tryme"]) @@ -375,10 +542,11 @@ self.assertTrue(isinstance(tag, Tag)) self.assertEqual(b"foo ", tag.tagger) self.assertEqual(b"bar", tag.message) + self.assertLess(time.time() - tag.tag_time, 5) def test_unannotated(self): - c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], - [3, 1, 2]]) + c1, c2, c3 = build_commit_graph( + self.repo.object_store, [[1], [2, 1], [3, 1, 2]]) self.repo.refs[b"HEAD"] = c3.id porcelain.tag_create(self.repo.path, b"tryme", annotated=False) @@ -417,12 +585,13 @@ class ResetTests(PorcelainTestCase): def test_hard_head(self): - with open(os.path.join(self.repo.path, 'foo'), 'w') as f: + fullpath = os.path.join(self.repo.path, 'foo') + with open(fullpath, 'w') as f: f.write("BAR") - porcelain.add(self.repo.path, paths=["foo"]) + porcelain.add(self.repo.path, paths=[fullpath]) porcelain.commit(self.repo.path, message=b"Some message", - committer=b"Jane ", - author=b"John ") + committer=b"Jane ", + author=b"John ") with open(os.path.join(self.repo.path, 'foo'), 'wb') as f: f.write(b"OOH") @@ -436,6 +605,31 @@ self.assertEqual([], changes) + def test_hard_commit(self): + fullpath = os.path.join(self.repo.path, 'foo') + with open(fullpath, 'w') as f: + f.write("BAR") + porcelain.add(self.repo.path, paths=[fullpath]) + sha = porcelain.commit(self.repo.path, message=b"Some message", + committer=b"Jane ", + author=b"John ") + + with open(fullpath, 'wb') as f: + f.write(b"BAZ") + porcelain.add(self.repo.path, paths=[fullpath]) + porcelain.commit(self.repo.path, message=b"Some other message", + committer=b"Jane ", + author=b"John ") + + porcelain.reset(self.repo, "hard", sha) + + index = self.repo.open_index() + changes = list(tree_changes(self.repo, + index.commit(self.repo.object_store), + self.repo[sha].tree)) + + self.assertEqual([], changes) + class PushTests(PorcelainTestCase): @@ -449,32 +643,42 @@ errstream = BytesIO() porcelain.commit(repo=self.repo.path, message=b'init', - author=b'', committer=b'') + author=b'', committer=b'') # Setup target repo cloned from temp test repo clone_path = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, clone_path) target_repo = porcelain.clone(self.repo.path, target=clone_path, - errstream=errstream) - target_repo.close() + errstream=errstream) + try: + self.assertEqual(target_repo[b'HEAD'], self.repo[b'HEAD']) + finally: + target_repo.close() # create a second file to be pushed back to origin handle, fullpath = tempfile.mkstemp(dir=clone_path) os.close(handle) - porcelain.add(repo=clone_path, paths=[os.path.basename(fullpath)]) + porcelain.add(repo=clone_path, paths=[fullpath]) porcelain.commit(repo=clone_path, message=b'push', - author=b'', committer=b'') + author=b'', committer=b'') # Setup a non-checked out branch in the remote refs_path = b"refs/heads/foo" - self.repo.refs[refs_path] = self.repo[b'HEAD'].id + new_id = self.repo[b'HEAD'].id + self.assertNotEqual(new_id, ZERO_SHA) + self.repo.refs[refs_path] = new_id # Push to the remote - porcelain.push(clone_path, self.repo.path, b"HEAD:" + refs_path, outstream=outstream, - errstream=errstream) + porcelain.push(clone_path, self.repo.path, b"HEAD:" + refs_path, + outstream=outstream, errstream=errstream) # Check that the target and source - with closing(Repo(clone_path)) as r_clone: + with Repo(clone_path) as r_clone: + self.assertEqual({ + b'HEAD': new_id, + b'refs/heads/foo': r_clone[b'HEAD'].id, + b'refs/heads/master': new_id, + }, self.repo.get_refs()) self.assertEqual(r_clone[b'HEAD'].id, self.repo[refs_path].id) # Get the change in the target repo corresponding to the add @@ -482,52 +686,102 @@ change = list(tree_changes(self.repo, self.repo[b'HEAD'].tree, self.repo[b'refs/heads/foo'].tree))[0] self.assertEqual(os.path.basename(fullpath), - change.new.path.decode('ascii')) - + change.new.path.decode('ascii')) -class PullTests(PorcelainTestCase): - - def test_simple(self): + def test_delete(self): + """Basic test of porcelain push, removing a branch. + """ outstream = BytesIO() errstream = BytesIO() + porcelain.commit(repo=self.repo.path, message=b'init', + author=b'', committer=b'') + + # Setup target repo cloned from temp test repo + clone_path = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, clone_path) + target_repo = porcelain.clone(self.repo.path, target=clone_path, + errstream=errstream) + target_repo.close() + + # Setup a non-checked out branch in the remote + refs_path = b"refs/heads/foo" + new_id = self.repo[b'HEAD'].id + self.assertNotEqual(new_id, ZERO_SHA) + self.repo.refs[refs_path] = new_id + + # Push to the remote + porcelain.push(clone_path, self.repo.path, b":" + refs_path, + outstream=outstream, errstream=errstream) + + self.assertEqual({ + b'HEAD': new_id, + b'refs/heads/master': new_id, + }, self.repo.get_refs()) + + +class PullTests(PorcelainTestCase): + + def setUp(self): + super(PullTests, self).setUp() # create a file for initial commit handle, fullpath = tempfile.mkstemp(dir=self.repo.path) os.close(handle) - filename = os.path.basename(fullpath) - porcelain.add(repo=self.repo.path, paths=filename) + porcelain.add(repo=self.repo.path, paths=fullpath) porcelain.commit(repo=self.repo.path, message=b'test', author=b'test', committer=b'test') # Setup target repo - target_path = tempfile.mkdtemp() - self.addCleanup(shutil.rmtree, target_path) - target_repo = porcelain.clone(self.repo.path, target=target_path, - errstream=errstream) + self.target_path = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, self.target_path) + target_repo = porcelain.clone(self.repo.path, target=self.target_path, + errstream=BytesIO()) target_repo.close() # create a second file to be pushed handle, fullpath = tempfile.mkstemp(dir=self.repo.path) os.close(handle) - filename = os.path.basename(fullpath) - porcelain.add(repo=self.repo.path, paths=filename) + porcelain.add(repo=self.repo.path, paths=fullpath) porcelain.commit(repo=self.repo.path, message=b'test2', - author=b'test2', committer=b'test2') + author=b'test2', committer=b'test2') self.assertTrue(b'refs/heads/master' in self.repo.refs) self.assertTrue(b'refs/heads/master' in target_repo.refs) + def test_simple(self): + outstream = BytesIO() + errstream = BytesIO() + + # Pull changes into the cloned repo + porcelain.pull(self.target_path, self.repo.path, b'refs/heads/master', + outstream=outstream, errstream=errstream) + + # Check the target repo for pushed changes + with Repo(self.target_path) as r: + self.assertEqual(r[b'HEAD'].id, self.repo[b'HEAD'].id) + + def test_no_refspec(self): + outstream = BytesIO() + errstream = BytesIO() + # Pull changes into the cloned repo - porcelain.pull(target_path, self.repo.path, b'refs/heads/master', - outstream=outstream, errstream=errstream) + porcelain.pull(self.target_path, self.repo.path, outstream=outstream, + errstream=errstream) # Check the target repo for pushed changes - with closing(Repo(target_path)) as r: + with Repo(self.target_path) as r: self.assertEqual(r[b'HEAD'].id, self.repo[b'HEAD'].id) class StatusTests(PorcelainTestCase): + def test_empty(self): + results = porcelain.status(self.repo) + self.assertEqual( + {'add': [], 'delete': [], 'modify': []}, + results.staged) + self.assertEqual([], results.unstaged) + def test_status(self): """Integration test for `status` functionality.""" @@ -536,9 +790,9 @@ with open(fullpath, 'w') as f: f.write('origstuff') - porcelain.add(repo=self.repo.path, paths=['foo']) + porcelain.add(repo=self.repo.path, paths=[fullpath]) porcelain.commit(repo=self.repo.path, message=b'test status', - author=b'', committer=b'') + author=b'', committer=b'') # modify access and modify time of path os.utime(fullpath, (0, 0)) @@ -551,11 +805,12 @@ fullpath = os.path.join(self.repo.path, filename_add) with open(fullpath, 'w') as f: f.write('stuff') - porcelain.add(repo=self.repo.path, paths=filename_add) + porcelain.add(repo=self.repo.path, paths=fullpath) results = porcelain.status(self.repo) - self.assertEqual(results.staged['add'][0], filename_add.encode('ascii')) + self.assertEqual(results.staged['add'][0], + filename_add.encode('ascii')) self.assertEqual(results.unstaged, [b'foo']) def test_get_tree_changes_add(self): @@ -563,16 +818,18 @@ # Make a dummy file, stage filename = 'bar' - with open(os.path.join(self.repo.path, filename), 'w') as f: + fullpath = os.path.join(self.repo.path, filename) + with open(fullpath, 'w') as f: f.write('stuff') - porcelain.add(repo=self.repo.path, paths=filename) + porcelain.add(repo=self.repo.path, paths=fullpath) porcelain.commit(repo=self.repo.path, message=b'test status', - author=b'', committer=b'') + author=b'', committer=b'') filename = 'foo' - with open(os.path.join(self.repo.path, filename), 'w') as f: + fullpath = os.path.join(self.repo.path, filename) + with open(fullpath, 'w') as f: f.write('stuff') - porcelain.add(repo=self.repo.path, paths=filename) + porcelain.add(repo=self.repo.path, paths=fullpath) changes = porcelain.get_tree_changes(self.repo.path) self.assertEqual(changes['add'][0], filename.encode('ascii')) @@ -588,12 +845,12 @@ fullpath = os.path.join(self.repo.path, filename) with open(fullpath, 'w') as f: f.write('stuff') - porcelain.add(repo=self.repo.path, paths=filename) + porcelain.add(repo=self.repo.path, paths=fullpath) porcelain.commit(repo=self.repo.path, message=b'test status', - author=b'', committer=b'') + author=b'', committer=b'') with open(fullpath, 'w') as f: f.write('otherstuff') - porcelain.add(repo=self.repo.path, paths=filename) + porcelain.add(repo=self.repo.path, paths=fullpath) changes = porcelain.get_tree_changes(self.repo.path) self.assertEqual(changes['modify'][0], filename.encode('ascii')) @@ -606,12 +863,18 @@ # Make a dummy file, stage, commit, remove filename = 'foo' - with open(os.path.join(self.repo.path, filename), 'w') as f: + fullpath = os.path.join(self.repo.path, filename) + with open(fullpath, 'w') as f: f.write('stuff') - porcelain.add(repo=self.repo.path, paths=filename) + porcelain.add(repo=self.repo.path, paths=fullpath) porcelain.commit(repo=self.repo.path, message=b'test status', - author=b'', committer=b'') - porcelain.rm(repo=self.repo.path, paths=[filename]) + author=b'', committer=b'') + cwd = os.getcwd() + try: + os.chdir(self.repo.path) + porcelain.remove(repo=self.repo.path, paths=[filename]) + finally: + os.chdir(cwd) changes = porcelain.get_tree_changes(self.repo.path) self.assertEqual(changes['delete'][0], filename.encode('ascii')) @@ -619,6 +882,39 @@ self.assertEqual(len(changes['modify']), 0) self.assertEqual(len(changes['delete']), 1) + def test_get_untracked_paths(self): + with open(os.path.join(self.repo.path, '.gitignore'), 'w') as f: + f.write('ignored\n') + with open(os.path.join(self.repo.path, 'ignored'), 'w') as f: + f.write('blah\n') + with open(os.path.join(self.repo.path, 'notignored'), 'w') as f: + f.write('blah\n') + self.assertEqual( + set(['ignored', 'notignored', '.gitignore']), + set(porcelain.get_untracked_paths(self.repo.path, self.repo.path, + self.repo.open_index()))) + self.assertEqual(set(['.gitignore', 'notignored']), + set(porcelain.status(self.repo).untracked)) + self.assertEqual(set(['.gitignore', 'notignored', 'ignored']), + set(porcelain.status(self.repo, ignored=True) + .untracked)) + + def test_get_untracked_paths_nested(self): + with open(os.path.join(self.repo.path, 'notignored'), 'w') as f: + f.write('blah\n') + subrepo = Repo.init(os.path.join(self.repo.path, 'nested'), mkdir=True) + with open(os.path.join(subrepo.path, 'another'), 'w') as f: + f.write('foo\n') + + self.assertEqual( + set(['notignored']), + set(porcelain.get_untracked_paths(self.repo.path, self.repo.path, + self.repo.open_index()))) + self.assertEqual( + set(['another']), + set(porcelain.get_untracked_paths(subrepo.path, subrepo.path, + subrepo.open_index()))) + # TODO(jelmer): Add test for dulwich.porcelain.daemon @@ -628,7 +924,8 @@ def test_upload_pack(self): outf = BytesIO() - exitcode = porcelain.upload_pack(self.repo.path, BytesIO(b"0000"), outf) + exitcode = porcelain.upload_pack( + self.repo.path, BytesIO(b"0000"), outf) outlines = outf.getvalue().splitlines() self.assertEqual([b"0000"], outlines) self.assertEqual(0, exitcode) @@ -639,18 +936,23 @@ def test_receive_pack(self): filename = 'foo' - with open(os.path.join(self.repo.path, filename), 'w') as f: + fullpath = os.path.join(self.repo.path, filename) + with open(fullpath, 'w') as f: f.write('stuff') - porcelain.add(repo=self.repo.path, paths=filename) + porcelain.add(repo=self.repo.path, paths=fullpath) self.repo.do_commit(message=b'test status', - author=b'', committer=b'', author_timestamp=1402354300, - commit_timestamp=1402354300, author_timezone=0, commit_timezone=0) + author=b'', committer=b'', + author_timestamp=1402354300, + commit_timestamp=1402354300, author_timezone=0, + commit_timezone=0) outf = BytesIO() - exitcode = porcelain.receive_pack(self.repo.path, BytesIO(b"0000"), outf) + exitcode = porcelain.receive_pack( + self.repo.path, BytesIO(b"0000"), outf) outlines = outf.getvalue().splitlines() self.assertEqual([ - b'00739e65bdcf4a22cdd4f3700604a275cd2aaf146b23 HEAD\x00 report-status ' - b'delete-refs quiet ofs-delta side-band-64k no-done', + b'00919e65bdcf4a22cdd4f3700604a275cd2aaf146b23 HEAD\x00 report-status ' # noqa: E501 + b'delete-refs quiet ofs-delta side-band-64k ' + b'no-done symref=HEAD:refs/heads/master', b'003f9e65bdcf4a22cdd4f3700604a275cd2aaf146b23 refs/heads/master', b'0000'], outlines) self.assertEqual(0, exitcode) @@ -708,8 +1010,7 @@ # create a file for initial commit handle, fullpath = tempfile.mkstemp(dir=self.repo.path) os.close(handle) - filename = os.path.basename(fullpath) - porcelain.add(repo=self.repo.path, paths=filename) + porcelain.add(repo=self.repo.path, paths=fullpath) porcelain.commit(repo=self.repo.path, message=b'test', author=b'test', committer=b'test') @@ -717,25 +1018,24 @@ target_path = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, target_path) target_repo = porcelain.clone(self.repo.path, target=target_path, - errstream=errstream) + errstream=errstream) # create a second file to be pushed handle, fullpath = tempfile.mkstemp(dir=self.repo.path) os.close(handle) - filename = os.path.basename(fullpath) - porcelain.add(repo=self.repo.path, paths=filename) + porcelain.add(repo=self.repo.path, paths=fullpath) porcelain.commit(repo=self.repo.path, message=b'test2', - author=b'test2', committer=b'test2') + author=b'test2', committer=b'test2') self.assertFalse(self.repo[b'HEAD'].id in target_repo) target_repo.close() # Fetch changes into the cloned repo porcelain.fetch(target_path, self.repo.path, outstream=outstream, - errstream=errstream) + errstream=errstream) # Check the target repo for pushed changes - with closing(Repo(target_path)) as r: + with Repo(target_path) as r: self.assertTrue(self.repo[b'HEAD'].id in r) @@ -747,6 +1047,124 @@ def test_simple(self): handle, fullpath = tempfile.mkstemp(dir=self.repo.path) os.close(handle) - filename = os.path.basename(fullpath) - porcelain.add(repo=self.repo.path, paths=filename) + porcelain.add(repo=self.repo.path, paths=fullpath) porcelain.repack(self.repo) + + +class LsTreeTests(PorcelainTestCase): + + def test_empty(self): + porcelain.commit(repo=self.repo.path, message=b'test status', + author=b'', committer=b'') + + f = StringIO() + porcelain.ls_tree(self.repo, b"HEAD", outstream=f) + self.assertEqual(f.getvalue(), "") + + def test_simple(self): + # Commit a dummy file then modify it + fullpath = os.path.join(self.repo.path, 'foo') + with open(fullpath, 'w') as f: + f.write('origstuff') + + porcelain.add(repo=self.repo.path, paths=[fullpath]) + porcelain.commit(repo=self.repo.path, message=b'test status', + author=b'', committer=b'') + + f = StringIO() + porcelain.ls_tree(self.repo, b"HEAD", outstream=f) + self.assertEqual( + f.getvalue(), + '100644 blob 8b82634d7eae019850bb883f06abf428c58bc9aa\tfoo\n') + + +class LsRemoteTests(PorcelainTestCase): + + def test_empty(self): + self.assertEqual({}, porcelain.ls_remote(self.repo.path)) + + def test_some(self): + cid = porcelain.commit(repo=self.repo.path, message=b'test status', + author=b'', committer=b'') + + self.assertEqual({ + b'refs/heads/master': cid, + b'HEAD': cid}, + porcelain.ls_remote(self.repo.path)) + + +class RemoteAddTests(PorcelainTestCase): + + def test_new(self): + porcelain.remote_add( + self.repo, 'jelmer', 'git://jelmer.uk/code/dulwich') + c = self.repo.get_config() + self.assertEqual( + c.get((b'remote', b'jelmer'), b'url'), + b'git://jelmer.uk/code/dulwich') + + def test_exists(self): + porcelain.remote_add( + self.repo, 'jelmer', 'git://jelmer.uk/code/dulwich') + self.assertRaises(porcelain.RemoteExists, porcelain.remote_add, + self.repo, 'jelmer', 'git://jelmer.uk/code/dulwich') + + +class CheckIgnoreTests(PorcelainTestCase): + + def test_check_ignored(self): + with open(os.path.join(self.repo.path, '.gitignore'), 'w') as f: + f.write("foo") + with open(os.path.join(self.repo.path, 'foo'), 'w') as f: + f.write("BAR") + with open(os.path.join(self.repo.path, 'bar'), 'w') as f: + f.write("BAR") + self.assertEqual( + ['foo'], + list(porcelain.check_ignore(self.repo, ['foo']))) + self.assertEqual([], list(porcelain.check_ignore(self.repo, ['bar']))) + + def test_check_added(self): + with open(os.path.join(self.repo.path, 'foo'), 'w') as f: + f.write("BAR") + self.repo.stage(['foo']) + with open(os.path.join(self.repo.path, '.gitignore'), 'w') as f: + f.write("foo\n") + self.assertEqual( + [], list(porcelain.check_ignore(self.repo, ['foo']))) + self.assertEqual( + ['foo'], + list(porcelain.check_ignore(self.repo, ['foo'], no_index=True))) + + +class UpdateHeadTests(PorcelainTestCase): + + def test_set_to_branch(self): + [c1] = build_commit_graph(self.repo.object_store, [[1]]) + self.repo.refs[b"refs/heads/blah"] = c1.id + porcelain.update_head(self.repo, "blah") + self.assertEqual(c1.id, self.repo.head()) + self.assertEqual(b'ref: refs/heads/blah', + self.repo.refs.read_ref('HEAD')) + + def test_set_to_branch_detached(self): + [c1] = build_commit_graph(self.repo.object_store, [[1]]) + self.repo.refs[b"refs/heads/blah"] = c1.id + porcelain.update_head(self.repo, "blah", detached=True) + self.assertEqual(c1.id, self.repo.head()) + self.assertEqual(c1.id, self.repo.refs.read_ref(b'HEAD')) + + def test_set_to_commit_detached(self): + [c1] = build_commit_graph(self.repo.object_store, [[1]]) + self.repo.refs[b"refs/heads/blah"] = c1.id + porcelain.update_head(self.repo, c1.id, detached=True) + self.assertEqual(c1.id, self.repo.head()) + self.assertEqual(c1.id, self.repo.refs.read_ref(b'HEAD')) + + def test_set_new_branch(self): + [c1] = build_commit_graph(self.repo.object_store, [[1]]) + self.repo.refs[b"refs/heads/blah"] = c1.id + porcelain.update_head(self.repo, "blah", new_branch="bar") + self.assertEqual(c1.id, self.repo.head()) + self.assertEqual(b'ref: refs/heads/bar', + self.repo.refs.read_ref(b'HEAD')) diff -Nru dulwich-0.12.0/dulwich/tests/test_protocol.py dulwich-0.18.5/dulwich/tests/test_protocol.py --- dulwich-0.12.0/dulwich/tests/test_protocol.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_protocol.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,20 +1,22 @@ # test_protocol.py -- Tests for the git protocol # Copyright (C) 2009 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# or (at your option) any later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for the smart protocol utility functions.""" @@ -126,7 +128,8 @@ def recv(self, size): # fail fast if no bytes are available; in a real socket, this would # block forever - if self.tell() == len(self.getvalue()) and not self.allow_read_past_eof: + if (self.tell() == len(self.getvalue()) + and not self.allow_read_past_eof): raise GitProtocolError('Blocking read past end of socket') if size == 1: return self.read(1) @@ -213,28 +216,30 @@ def test_caps(self): self.assertEqual((b'bla', [b'la']), extract_capabilities(b'bla\0la')) self.assertEqual((b'bla', [b'la']), extract_capabilities(b'bla\0la\n')) - self.assertEqual((b'bla', [b'la', b'la']), extract_capabilities(b'bla\0la la')) + self.assertEqual((b'bla', [b'la', b'la']), + extract_capabilities(b'bla\0la la')) def test_plain_want_line(self): - self.assertEqual((b'want bla', []), extract_want_line_capabilities(b'want bla')) + self.assertEqual((b'want bla', []), + extract_want_line_capabilities(b'want bla')) def test_caps_want_line(self): self.assertEqual((b'want bla', [b'la']), - extract_want_line_capabilities(b'want bla la')) + extract_want_line_capabilities(b'want bla la')) self.assertEqual((b'want bla', [b'la']), - extract_want_line_capabilities(b'want bla la\n')) + extract_want_line_capabilities(b'want bla la\n')) self.assertEqual((b'want bla', [b'la', b'la']), - extract_want_line_capabilities(b'want bla la la')) + extract_want_line_capabilities(b'want bla la la')) def test_ack_type(self): self.assertEqual(SINGLE_ACK, ack_type([b'foo', b'bar'])) self.assertEqual(MULTI_ACK, ack_type([b'foo', b'bar', b'multi_ack'])) self.assertEqual(MULTI_ACK_DETAILED, - ack_type([b'foo', b'bar', b'multi_ack_detailed'])) + ack_type([b'foo', b'bar', b'multi_ack_detailed'])) # choose detailed when both present self.assertEqual(MULTI_ACK_DETAILED, - ack_type([b'foo', b'bar', b'multi_ack', - b'multi_ack_detailed'])) + ack_type([b'foo', b'bar', b'multi_ack', + b'multi_ack_detailed'])) class BufferedPktLineWriterTests(TestCase): diff -Nru dulwich-0.12.0/dulwich/tests/test_reflog.py dulwich-0.18.5/dulwich/tests/test_reflog.py --- dulwich-0.12.0/dulwich/tests/test_reflog.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_reflog.py 2017-10-07 15:45:18.000000000 +0000 @@ -2,21 +2,22 @@ # encoding: utf-8 # Copyright (C) 2015 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for dulwich.reflog.""" @@ -57,13 +58,15 @@ 1446552482, 0, b'clone: from git://jelmer.uk/samba')) def test_parse(self): + reflog_line = ( + b'0000000000000000000000000000000000000000 ' + b'49030649db3dfec5a9bc03e5dde4255a14499f16 Jelmer Vernooij ' + b' 1446552482 +0000 ' + b'clone: from git://jelmer.uk/samba' + ) self.assertEqual( (b'0000000000000000000000000000000000000000', b'49030649db3dfec5a9bc03e5dde4255a14499f16', b'Jelmer Vernooij ', 1446552482, 0, b'clone: from git://jelmer.uk/samba'), - parse_reflog_line( - b'0000000000000000000000000000000000000000 ' - b'49030649db3dfec5a9bc03e5dde4255a14499f16 Jelmer Vernooij ' - b' 1446552482 +0000 ' - b'clone: from git://jelmer.uk/samba')) + parse_reflog_line(reflog_line)) diff -Nru dulwich-0.12.0/dulwich/tests/test_refs.py dulwich-0.18.5/dulwich/tests/test_refs.py --- dulwich-0.12.0/dulwich/tests/test_refs.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_refs.py 2017-10-07 15:45:18.000000000 +0000 @@ -2,21 +2,22 @@ # encoding: utf-8 # Copyright (C) 2013 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for dulwich.refs.""" @@ -29,11 +30,13 @@ from dulwich.file import ( GitFile, ) +from dulwich.objects import ZERO_SHA from dulwich.refs import ( DictRefsContainer, InfoRefsContainer, check_ref_format, _split_ref_line, + parse_symref_value, read_packed_refs_with_peeled, read_packed_refs, write_packed_refs, @@ -82,6 +85,7 @@ THREES = b'3' * 40 FOURS = b'4' * 40 + class PackedRefsFileTests(TestCase): def test_split_ref_line_errors(self): @@ -104,7 +108,8 @@ f = BytesIO(b'\n'.join([ ONES + b' ref/1', b'^' + TWOS])) - self.assertRaises(errors.PackedRefsException, list, read_packed_refs(f)) + self.assertRaises(errors.PackedRefsException, list, + read_packed_refs(f)) def test_read_with_peeled(self): f = BytesIO(b'\n'.join([ @@ -122,13 +127,15 @@ f = BytesIO(b'\n'.join([ b'^' + TWOS, ONES + b' ref/1'])) - self.assertRaises(errors.PackedRefsException, list, read_packed_refs(f)) + self.assertRaises(errors.PackedRefsException, list, + read_packed_refs(f)) f = BytesIO(b'\n'.join([ - ONES + b' ref/1', - b'^' + TWOS, - b'^' + THREES])) - self.assertRaises(errors.PackedRefsException, list, read_packed_refs(f)) + ONES + b' ref/1', + b'^' + TWOS, + b'^' + THREES])) + self.assertRaises(errors.PackedRefsException, list, + read_packed_refs(f)) def test_write_with_peeled(self): f = BytesIO() @@ -152,11 +159,13 @@ # Dict of refs that we expect all RefsContainerTests subclasses to define. _TEST_REFS = { b'HEAD': b'42d06bd4b77fed026b154d16493e5deab78f02ec', - b'refs/heads/40-char-ref-aaaaaaaaaaaaaaaaaa': b'42d06bd4b77fed026b154d16493e5deab78f02ec', + b'refs/heads/40-char-ref-aaaaaaaaaaaaaaaaaa': + b'42d06bd4b77fed026b154d16493e5deab78f02ec', b'refs/heads/master': b'42d06bd4b77fed026b154d16493e5deab78f02ec', b'refs/heads/packed': b'42d06bd4b77fed026b154d16493e5deab78f02ec', b'refs/tags/refs-0.1': b'df6800012397fb85c56e7418dd4eb9405dee075c', b'refs/tags/refs-0.2': b'3ec9c43c84ff242e3ef4a9fc5bc111fd780a76a8', + b'refs/heads/loop': b'ref: refs/heads/loop', } @@ -165,8 +174,6 @@ def test_keys(self): actual_keys = set(self._refs.keys()) self.assertEqual(set(self._refs.allkeys()), actual_keys) - # ignore the symref loop if it exists - actual_keys.discard(b'refs/heads/loop') self.assertEqual(set(_TEST_REFS.keys()), actual_keys) actual_keys = self._refs.keys(b'refs/heads') @@ -179,10 +186,22 @@ def test_as_dict(self): # refs/heads/loop does not show up even if it exists - self.assertEqual(_TEST_REFS, self._refs.as_dict()) + expected_refs = dict(_TEST_REFS) + del expected_refs[b'refs/heads/loop'] + self.assertEqual(expected_refs, self._refs.as_dict()) + + def test_get_symrefs(self): + self._refs.set_symbolic_ref(b'refs/heads/src', b'refs/heads/dst') + symrefs = self._refs.get_symrefs() + if b'HEAD' in symrefs: + symrefs.pop(b'HEAD') + self.assertEqual({b'refs/heads/src': b'refs/heads/dst', + b'refs/heads/loop': b'refs/heads/loop'}, + symrefs) def test_setitem(self): - self._refs[b'refs/some/ref'] = b'42d06bd4b77fed026b154d16493e5deab78f02ec' + self._refs[b'refs/some/ref'] = ( + b'42d06bd4b77fed026b154d16493e5deab78f02ec') self.assertEqual(b'42d06bd4b77fed026b154d16493e5deab78f02ec', self._refs[b'refs/some/ref']) self.assertRaises( @@ -203,6 +222,10 @@ nines)) self.assertEqual(nines, self._refs[b'refs/heads/master']) + self.assertTrue(self._refs.set_if_equals( + b'refs/heads/nonexistant', ZERO_SHA, nines)) + self.assertEqual(nines, self._refs[b'refs/heads/nonexistant']) + def test_add_if_new(self): nines = b'9' * 40 self.assertFalse(self._refs.add_if_new(b'refs/heads/master', nines)) @@ -258,11 +281,13 @@ self.assertEqual(b'42d06bd4b77fed026b154d16493e5deab78f02ec', self._refs[b'HEAD']) self.assertTrue(self._refs.remove_if_equals( - b'refs/tags/refs-0.2', b'3ec9c43c84ff242e3ef4a9fc5bc111fd780a76a8')) + b'refs/tags/refs-0.2', + b'3ec9c43c84ff242e3ef4a9fc5bc111fd780a76a8')) + self.assertTrue(self._refs.remove_if_equals( + b'refs/tags/refs-0.2', ZERO_SHA)) self.assertFalse(b'refs/tags/refs-0.2' in self._refs) - class DictRefsContainerTests(RefsContainerTests, TestCase): def setUp(self): @@ -274,6 +299,7 @@ # some way of injecting invalid refs. self._refs._refs[b'refs/stash'] = b'00' * 20 expected_refs = dict(_TEST_REFS) + del expected_refs[b'refs/heads/loop'] expected_refs[b'refs/stash'] = b'00' * 20 self.assertEqual(expected_refs, self._refs.as_dict()) @@ -320,11 +346,13 @@ # ensure HEAD was not modified f = open(os.path.join(self._refs.path, 'HEAD'), 'rb') - self.assertEqual(b'ref: refs/heads/master', next(iter(f)).rstrip(b'\n')) + v = next(iter(f)).rstrip(b'\n\r') f.close() + self.assertEqual(b'ref: refs/heads/master', v) # ensure the symbolic link was written through - f = open(os.path.join(self._refs.path, 'refs', 'heads', 'master'), 'rb') + f = open(os.path.join(self._refs.path, 'refs', 'heads', 'master'), + 'rb') self.assertEqual(ones, f.read()[:40]) f.close() @@ -367,13 +395,13 @@ self.assertEqual(nines, refs[b'refs/heads/master']) def test_follow(self): - self.assertEqual((b'refs/heads/master', + self.assertEqual(([b'HEAD', b'refs/heads/master'], b'42d06bd4b77fed026b154d16493e5deab78f02ec'), - self._refs._follow(b'HEAD')) - self.assertEqual((b'refs/heads/master', + self._refs.follow(b'HEAD')) + self.assertEqual(([b'refs/heads/master'], b'42d06bd4b77fed026b154d16493e5deab78f02ec'), - self._refs._follow(b'refs/heads/master')) - self.assertRaises(KeyError, self._refs._follow, b'refs/heads/loop') + self._refs.follow(b'refs/heads/master')) + self.assertRaises(KeyError, self._refs.follow, b'refs/heads/loop') def test_delitem(self): RefsContainerTests.test_delitem(self) @@ -433,28 +461,33 @@ self.assertRaises(KeyError, lambda: self._refs[b'refs/tags/refs-0.1']) def test_read_ref(self): - self.assertEqual(b'ref: refs/heads/master', self._refs.read_ref(b'HEAD')) + self.assertEqual(b'ref: refs/heads/master', + self._refs.read_ref(b'HEAD')) self.assertEqual(b'42d06bd4b77fed026b154d16493e5deab78f02ec', self._refs.read_ref(b'refs/heads/packed')) self.assertEqual(None, self._refs.read_ref(b'nonexistant')) def test_non_ascii(self): try: - encoded_ref = u'refs/tags/schön'.encode(sys.getfilesystemencoding()) - except UnicodeDecodeError: - raise SkipTest("filesystem encoding doesn't support special character") - p = os.path.join(self._repo.path, 'refs', 'tags', 'schön') + encoded_ref = u'refs/tags/schön'.encode( + sys.getfilesystemencoding()) + except UnicodeEncodeError: + raise SkipTest( + "filesystem encoding doesn't support special character") + p = os.path.join(self._repo.path, 'refs', 'tags', u'schön') with open(p, 'w') as f: f.write('00' * 20) expected_refs = dict(_TEST_REFS) expected_refs[encoded_ref] = b'00' * 20 + del expected_refs[b'refs/heads/loop'] self.assertEqual(expected_refs, self._repo.get_refs()) _TEST_REFS_SERIALIZED = ( - b'42d06bd4b77fed026b154d16493e5deab78f02ec\trefs/heads/40-char-ref-aaaaaaaaaaaaaaaaaa\n' + b'42d06bd4b77fed026b154d16493e5deab78f02ec\t' + b'refs/heads/40-char-ref-aaaaaaaaaaaaaaaaaa\n' b'42d06bd4b77fed026b154d16493e5deab78f02ec\trefs/heads/master\n' b'42d06bd4b77fed026b154d16493e5deab78f02ec\trefs/heads/packed\n' b'df6800012397fb85c56e7418dd4eb9405dee075c\trefs/tags/refs-0.1\n' @@ -469,16 +502,16 @@ expected_refs = dict(_TEST_REFS) del expected_refs[b'HEAD'] expected_refs[b'refs/stash'] = b'00' * 20 + del expected_refs[b'refs/heads/loop'] self.assertEqual(expected_refs, refs.as_dict()) def test_keys(self): refs = InfoRefsContainer(BytesIO(_TEST_REFS_SERIALIZED)) actual_keys = set(refs.keys()) self.assertEqual(set(refs.allkeys()), actual_keys) - # ignore the symref loop if it exists - actual_keys.discard(b'refs/heads/loop') expected_refs = dict(_TEST_REFS) del expected_refs[b'HEAD'] + del expected_refs[b'refs/heads/loop'] self.assertEqual(set(expected_refs.keys()), actual_keys) actual_keys = refs.keys(b'refs/heads') @@ -494,6 +527,7 @@ # refs/heads/loop does not show up even if it exists expected_refs = dict(_TEST_REFS) del expected_refs[b'HEAD'] + del expected_refs[b'refs/heads/loop'] self.assertEqual(expected_refs, refs.as_dict()) def test_contains(self): @@ -507,3 +541,14 @@ self.assertEqual( _TEST_REFS[b'refs/heads/master'], refs.get_peeled(b'refs/heads/master')) + + +class ParseSymrefValueTests(TestCase): + + def test_valid(self): + self.assertEqual( + b'refs/heads/foo', + parse_symref_value(b'ref: refs/heads/foo')) + + def test_invalid(self): + self.assertRaises(ValueError, parse_symref_value, b'foobar') diff -Nru dulwich-0.12.0/dulwich/tests/test_repository.py dulwich-0.18.5/dulwich/tests/test_repository.py --- dulwich-0.12.0/dulwich/tests/test_repository.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_repository.py 2017-10-18 23:48:44.000000000 +0000 @@ -2,25 +2,25 @@ # test_repository.py -- tests for repository.py # Copyright (C) 2007 James Westby # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for the repository.""" -from contextlib import closing import locale import os import stat @@ -65,13 +65,20 @@ def _check_repo_contents(self, repo, expect_bare): self.assertEqual(expect_bare, repo.bare) - self.assertFileContentsEqual(b'Unnamed repository', repo, 'description') - self.assertFileContentsEqual(b'', repo, os.path.join('info', 'exclude')) + self.assertFileContentsEqual( + b'Unnamed repository', repo, 'description') + self.assertFileContentsEqual( + b'', repo, os.path.join('info', 'exclude')) self.assertFileContentsEqual(None, repo, 'nonexistent file') barestr = b'bare = ' + str(expect_bare).lower().encode('ascii') with repo.get_named_file('config') as f: config_text = f.read() self.assertTrue(barestr in config_text, "%r" % config_text) + expect_filemode = sys.platform != 'win32' + barestr = b'filemode = ' + str(expect_filemode).lower().encode('ascii') + with repo.get_named_file('config') as f: + config_text = f.read() + self.assertTrue(barestr in config_text, "%r" % config_text) def test_create_memory(self): repo = MemoryRepo.init_bare([], {}) @@ -91,6 +98,31 @@ self.assertEqual(os.path.join(tmp_dir, '.git'), repo._controldir) self._check_repo_contents(repo, False) + def test_create_disk_non_bare_mkdir(self): + tmp_dir = tempfile.mkdtemp() + target_dir = os.path.join(tmp_dir, "target") + self.addCleanup(shutil.rmtree, tmp_dir) + repo = Repo.init(target_dir, mkdir=True) + self.assertEqual(os.path.join(target_dir, '.git'), repo._controldir) + self._check_repo_contents(repo, False) + + def test_create_disk_bare_mkdir(self): + tmp_dir = tempfile.mkdtemp() + target_dir = os.path.join(tmp_dir, "target") + self.addCleanup(shutil.rmtree, tmp_dir) + repo = Repo.init_bare(target_dir, mkdir=True) + self.assertEqual(target_dir, repo._controldir) + self._check_repo_contents(repo, True) + + +class MemoryRepoTests(TestCase): + + def test_set_description(self): + r = MemoryRepo.init_bare([], {}) + description = b"Some description" + r.set_description(description) + self.assertEqual(description, r.get_description()) + class RepositoryRootTests(TestCase): @@ -111,7 +143,7 @@ r = self.open_repo('a.git') r[b"refs/tags/foo"] = b'a90fa2d900a17e99b433217e988c4eb4a2e9a097' self.assertEqual(b'a90fa2d900a17e99b433217e988c4eb4a2e9a097', - r[b"refs/tags/foo"].id) + r[b"refs/tags/foo"].id) def test_getitem_unicode(self): r = self.open_repo('a.git') @@ -153,7 +185,8 @@ b'HEAD': b'a90fa2d900a17e99b433217e988c4eb4a2e9a097', b'refs/heads/master': b'a90fa2d900a17e99b433217e988c4eb4a2e9a097', b'refs/tags/mytag': b'28237f4dc30d0d462658d6b937b08a0f0b6ef55a', - b'refs/tags/mytag-packed': b'b0931cadc54336e78a1d980420e3268903b57a50', + b'refs/tags/mytag-packed': + b'b0931cadc54336e78a1d980420e3268903b57a50', }, r.get_refs()) def test_head(self): @@ -219,32 +252,46 @@ def test_get_walker(self): r = self.open_repo('a.git') # include defaults to [r.head()] - self.assertEqual([e.commit.id for e in r.get_walker()], - [r.head(), b'2a72d929692c41d8554c07f6301757ba18a65d91']) self.assertEqual( - [e.commit.id for e in r.get_walker([b'2a72d929692c41d8554c07f6301757ba18a65d91'])], + [e.commit.id for e in r.get_walker()], + [r.head(), b'2a72d929692c41d8554c07f6301757ba18a65d91']) + self.assertEqual( + [e.commit.id for e in + r.get_walker([b'2a72d929692c41d8554c07f6301757ba18a65d91'])], [b'2a72d929692c41d8554c07f6301757ba18a65d91']) self.assertEqual( - [e.commit.id for e in r.get_walker(b'2a72d929692c41d8554c07f6301757ba18a65d91')], + [e.commit.id for e in + r.get_walker(b'2a72d929692c41d8554c07f6301757ba18a65d91')], [b'2a72d929692c41d8554c07f6301757ba18a65d91']) def test_clone(self): r = self.open_repo('a.git') tmp_dir = self.mkdtemp() self.addCleanup(shutil.rmtree, tmp_dir) - with closing(r.clone(tmp_dir, mkdir=False)) as t: + with r.clone(tmp_dir, mkdir=False) as t: self.assertEqual({ b'HEAD': b'a90fa2d900a17e99b433217e988c4eb4a2e9a097', b'refs/remotes/origin/master': b'a90fa2d900a17e99b433217e988c4eb4a2e9a097', - b'refs/heads/master': b'a90fa2d900a17e99b433217e988c4eb4a2e9a097', - b'refs/tags/mytag': b'28237f4dc30d0d462658d6b937b08a0f0b6ef55a', + b'refs/heads/master': + b'a90fa2d900a17e99b433217e988c4eb4a2e9a097', + b'refs/tags/mytag': + b'28237f4dc30d0d462658d6b937b08a0f0b6ef55a', b'refs/tags/mytag-packed': b'b0931cadc54336e78a1d980420e3268903b57a50', }, t.refs.as_dict()) shas = [e.commit.id for e in r.get_walker()] self.assertEqual(shas, [t.head(), b'2a72d929692c41d8554c07f6301757ba18a65d91']) + c = t.get_config() + encoded_path = r.path + if not isinstance(encoded_path, bytes): + encoded_path = encoded_path.encode(sys.getfilesystemencoding()) + self.assertEqual(encoded_path, + c.get((b'remote', b'origin'), b'url')) + self.assertEqual( + b'+refs/heads/*:refs/remotes/origin/*', + c.get((b'remote', b'origin'), b'fetch')) def test_clone_no_head(self): temp_dir = self.mkdtemp() @@ -315,13 +362,14 @@ os.path.join(temp_dir, 'a.git'), symlinks=True) rel = os.path.relpath(os.path.join(repo_dir, 'submodule'), temp_dir) os.symlink(os.path.join(rel, 'dotgit'), os.path.join(temp_dir, '.git')) - with closing(Repo(temp_dir)) as r: - self.assertEqual(r.head(), b'a90fa2d900a17e99b433217e988c4eb4a2e9a097') + with Repo(temp_dir) as r: + self.assertEqual(r.head(), + b'a90fa2d900a17e99b433217e988c4eb4a2e9a097') def test_common_revisions(self): """ - This test demonstrates that ``find_common_revisions()`` actually returns - common heads, not revisions; dulwich already uses + This test demonstrates that ``find_common_revisions()`` actually + returns common heads, not revisions; dulwich already uses ``find_common_revisions()`` in such a manner (see ``Repo.fetch_objects()``). """ @@ -334,17 +382,17 @@ # Re-create each-side of the merge in simple_merge.git. # # Since the trees and blobs are missing, the repository created is - # corrupted, but we're only checking for commits for the purpose of this - # test, so it's immaterial. + # corrupted, but we're only checking for commits for the purpose of + # this test, so it's immaterial. r1_dir = self.mkdtemp() self.addCleanup(shutil.rmtree, r1_dir) - r1_commits = [b'ab64bbdcc51b170d21588e5c5d391ee5c0c96dfd', # HEAD + r1_commits = [b'ab64bbdcc51b170d21588e5c5d391ee5c0c96dfd', # HEAD b'60dacdc733de308bb77bb76ce0fb0f9b44c9769e', b'0d89f20333fbb1d2f3a94da77f4981373d8f4310'] r2_dir = self.mkdtemp() self.addCleanup(shutil.rmtree, r2_dir) - r2_commits = [b'4cffe90e0a41ad3f5190079d7c8f036bde29cbe6', # HEAD + r2_commits = [b'4cffe90e0a41ad3f5190079d7c8f036bde29cbe6', # HEAD b'60dacdc733de308bb77bb76ce0fb0f9b44c9769e', b'0d89f20333fbb1d2f3a94da77f4981373d8f4310'] @@ -378,8 +426,9 @@ """ repo_dir = os.path.join(self.mkdtemp()) - r = Repo.init(repo_dir) self.addCleanup(shutil.rmtree, repo_dir) + r = Repo.init(repo_dir) + self.addCleanup(r.close) pre_commit = os.path.join(r.controldir(), 'hooks', 'pre-commit') @@ -418,8 +467,9 @@ """ repo_dir = self.mkdtemp() - r = Repo.init(repo_dir) self.addCleanup(shutil.rmtree, repo_dir) + r = Repo.init(repo_dir) + self.addCleanup(r.close) commit_msg = os.path.join(r.controldir(), 'hooks', 'commit-msg') @@ -450,9 +500,10 @@ self.skipTest('shell hook tests requires POSIX shell') repo_dir = self.mkdtemp() + self.addCleanup(shutil.rmtree, repo_dir) r = Repo.init(repo_dir) - self.addCleanup(shutil.rmtree, repo_dir) + self.addCleanup(r.close) (fd, path) = tempfile.mkstemp(dir=repo_dir) os.close(fd) @@ -502,24 +553,66 @@ author=b'Test Author ', commit_timestamp=12345, commit_timezone=0, author_timestamp=12345, author_timezone=0) - self.assertEqual(len(warnings_list), 1, warnings_list) - self.assertIsInstance(warnings_list[-1], UserWarning) - self.assertTrue("post-commit hook failed: " in str(warnings_list[-1])) + expected_warning = UserWarning( + 'post-commit hook failed: Hook post-commit exited with ' + 'non-zero status',) + for w in warnings_list: + if (type(w) == type(expected_warning) and + w.args == expected_warning.args): + break + else: + raise AssertionError( + 'Expected warning %r not in %r' % + (expected_warning, warnings_list)) self.assertEqual([commit_sha], r[commit_sha2].parents) def test_as_dict(self): def check(repo): - self.assertEqual(repo.refs.subkeys(b'refs/tags'), repo.refs.subkeys(b'refs/tags/')) - self.assertEqual(repo.refs.as_dict(b'refs/tags'), repo.refs.as_dict(b'refs/tags/')) - self.assertEqual(repo.refs.as_dict(b'refs/heads'), repo.refs.as_dict(b'refs/heads/')) + self.assertEqual( + repo.refs.subkeys(b'refs/tags'), + repo.refs.subkeys(b'refs/tags/')) + self.assertEqual( + repo.refs.as_dict(b'refs/tags'), + repo.refs.as_dict(b'refs/tags/')) + self.assertEqual( + repo.refs.as_dict(b'refs/heads'), + repo.refs.as_dict(b'refs/heads/')) bare = self.open_repo('a.git') tmp_dir = self.mkdtemp() self.addCleanup(shutil.rmtree, tmp_dir) - with closing(bare.clone(tmp_dir, mkdir=False)) as nonbare: + with bare.clone(tmp_dir, mkdir=False) as nonbare: check(nonbare) check(bare) + def test_working_tree(self): + temp_dir = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, temp_dir) + worktree_temp_dir = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, worktree_temp_dir) + r = Repo.init(temp_dir) + self.addCleanup(r.close) + root_sha = r.do_commit( + b'empty commit', + committer=b'Test Committer ', + author=b'Test Author ', + commit_timestamp=12345, commit_timezone=0, + author_timestamp=12345, author_timezone=0) + r.refs[b'refs/heads/master'] = root_sha + w = Repo._init_new_working_directory(worktree_temp_dir, r) + self.addCleanup(w.close) + new_sha = w.do_commit( + b'new commit', + committer=b'Test Committer ', + author=b'Test Author ', + commit_timestamp=12345, commit_timezone=0, + author_timestamp=12345, author_timezone=0) + w.refs[b'HEAD'] = new_sha + self.assertEqual(os.path.abspath(r.controldir()), + os.path.abspath(w.commondir())) + self.assertEqual(r.refs.keys(), w.refs.keys()) + self.assertNotEqual(r.head(), w.head()) + class BuildRepoRootTests(TestCase): """Tests that build on-disk repos from scratch. @@ -544,11 +637,12 @@ with open(os.path.join(r.path, 'a'), 'wb') as f: f.write(b'file contents') r.stage(['a']) - commit_sha = r.do_commit(b'msg', - committer=b'Test Committer ', - author=b'Test Author ', - commit_timestamp=12345, commit_timezone=0, - author_timestamp=12345, author_timezone=0) + commit_sha = r.do_commit( + b'msg', + committer=b'Test Committer ', + author=b'Test Author ', + commit_timestamp=12345, commit_timezone=0, + author_timestamp=12345, author_timezone=0) self.assertEqual([], r[commit_sha].parents) self._root_commit = commit_sha @@ -566,11 +660,12 @@ with open(os.path.join(r.path, 'a'), 'wb') as f: f.write(b'new contents') r.stage(['a']) - commit_sha = r.do_commit(b'modified a', - committer=b'Test Committer ', - author=b'Test Author ', - commit_timestamp=12395, commit_timezone=0, - author_timestamp=12395, author_timezone=0) + commit_sha = r.do_commit( + b'modified a', + committer=b'Test Committer ', + author=b'Test Author ', + commit_timestamp=12395, commit_timezone=0, + author_timestamp=12395, author_timezone=0) self.assertEqual([self._root_commit], r[commit_sha].parents) a_mode, a_id = tree_lookup_path(r.get_object, r[commit_sha].tree, b'a') self.assertEqual(stat.S_IFREG | 0o644, a_mode) @@ -581,11 +676,12 @@ r = self._repo os.symlink('a', os.path.join(r.path, 'b')) r.stage(['a', 'b']) - commit_sha = r.do_commit(b'Symlink b', - committer=b'Test Committer ', - author=b'Test Author ', - commit_timestamp=12395, commit_timezone=0, - author_timestamp=12395, author_timezone=0) + commit_sha = r.do_commit( + b'Symlink b', + committer=b'Test Committer ', + author=b'Test Author ', + commit_timestamp=12395, commit_timezone=0, + author_timestamp=12395, author_timezone=0) self.assertEqual([self._root_commit], r[commit_sha].parents) b_mode, b_id = tree_lookup_path(r.get_object, r[commit_sha].tree, b'b') self.assertTrue(stat.S_ISLNK(b_mode)) @@ -595,11 +691,12 @@ r = self._repo os.remove(os.path.join(r.path, 'a')) r.stage(['a']) - commit_sha = r.do_commit(b'deleted a', - committer=b'Test Committer ', - author=b'Test Author ', - commit_timestamp=12395, commit_timezone=0, - author_timestamp=12395, author_timezone=0) + commit_sha = r.do_commit( + b'deleted a', + committer=b'Test Committer ', + author=b'Test Author ', + commit_timestamp=12395, commit_timezone=0, + author_timestamp=12395, author_timezone=0) self.assertEqual([self._root_commit], r[commit_sha].parents) self.assertEqual([], list(r.open_index())) tree = r[r[commit_sha].tree] @@ -608,22 +705,24 @@ def test_commit_follows(self): r = self._repo r.refs.set_symbolic_ref(b'HEAD', b'refs/heads/bla') - commit_sha = r.do_commit(b'commit with strange character', - committer=b'Test Committer ', - author=b'Test Author ', - commit_timestamp=12395, commit_timezone=0, - author_timestamp=12395, author_timezone=0, - ref=b'HEAD') + commit_sha = r.do_commit( + b'commit with strange character', + committer=b'Test Committer ', + author=b'Test Author ', + commit_timestamp=12395, commit_timezone=0, + author_timestamp=12395, author_timezone=0, + ref=b'HEAD') self.assertEqual(commit_sha, r[b'refs/heads/bla'].id) def test_commit_encoding(self): r = self._repo - commit_sha = r.do_commit(b'commit with strange character \xee', - committer=b'Test Committer ', - author=b'Test Author ', - commit_timestamp=12395, commit_timezone=0, - author_timestamp=12395, author_timezone=0, - encoding=b"iso8859-1") + commit_sha = r.do_commit( + b'commit with strange character \xee', + committer=b'Test Committer ', + author=b'Test Author ', + commit_timestamp=12395, commit_timezone=0, + author_timestamp=12395, author_timezone=0, + encoding=b"iso8859-1") self.assertEqual(b"iso8859-1", r[commit_sha].encoding) def test_commit_config_identity(self): @@ -683,12 +782,13 @@ def test_commit_branch(self): r = self._repo - commit_sha = r.do_commit(b'commit to branch', - committer=b'Test Committer ', - author=b'Test Author ', - commit_timestamp=12395, commit_timezone=0, - author_timestamp=12395, author_timezone=0, - ref=b"refs/heads/new_branch") + commit_sha = r.do_commit( + b'commit to branch', + committer=b'Test Committer ', + author=b'Test Author ', + commit_timestamp=12395, commit_timezone=0, + author_timestamp=12395, author_timezone=0, + ref=b"refs/heads/new_branch") self.assertEqual(self._root_commit, r[b"HEAD"].id) self.assertEqual(commit_sha, r[b"refs/heads/new_branch"].id) self.assertEqual([], r[commit_sha].parents) @@ -696,30 +796,33 @@ new_branch_head = commit_sha - commit_sha = r.do_commit(b'commit to branch 2', - committer=b'Test Committer ', - author=b'Test Author ', - commit_timestamp=12395, commit_timezone=0, - author_timestamp=12395, author_timezone=0, - ref=b"refs/heads/new_branch") + commit_sha = r.do_commit( + b'commit to branch 2', + committer=b'Test Committer ', + author=b'Test Author ', + commit_timestamp=12395, commit_timezone=0, + author_timestamp=12395, author_timezone=0, + ref=b"refs/heads/new_branch") self.assertEqual(self._root_commit, r[b"HEAD"].id) self.assertEqual(commit_sha, r[b"refs/heads/new_branch"].id) self.assertEqual([new_branch_head], r[commit_sha].parents) def test_commit_merge_heads(self): r = self._repo - merge_1 = r.do_commit(b'commit to branch 2', - committer=b'Test Committer ', - author=b'Test Author ', - commit_timestamp=12395, commit_timezone=0, - author_timestamp=12395, author_timezone=0, - ref=b"refs/heads/new_branch") - commit_sha = r.do_commit(b'commit with merge', - committer=b'Test Committer ', - author=b'Test Author ', - commit_timestamp=12395, commit_timezone=0, - author_timestamp=12395, author_timezone=0, - merge_heads=[merge_1]) + merge_1 = r.do_commit( + b'commit to branch 2', + committer=b'Test Committer ', + author=b'Test Author ', + commit_timestamp=12395, commit_timezone=0, + author_timestamp=12395, author_timezone=0, + ref=b"refs/heads/new_branch") + commit_sha = r.do_commit( + b'commit with merge', + committer=b'Test Committer ', + author=b'Test Author ', + commit_timestamp=12395, commit_timezone=0, + author_timestamp=12395, author_timezone=0, + merge_heads=[merge_1]) self.assertEqual( [self._root_commit, merge_1], r[commit_sha].parents) @@ -729,12 +832,13 @@ old_shas = set(r.object_store) old_refs = r.get_refs() - commit_sha = r.do_commit(b'commit with no ref', - committer=b'Test Committer ', - author=b'Test Author ', - commit_timestamp=12395, commit_timezone=0, - author_timestamp=12395, author_timezone=0, - ref=None) + commit_sha = r.do_commit( + b'commit with no ref', + committer=b'Test Committer ', + author=b'Test Author ', + commit_timestamp=12395, commit_timezone=0, + author_timestamp=12395, author_timezone=0, + ref=None) new_shas = set(r.object_store) - old_shas # New sha is added, but no new refs @@ -749,12 +853,13 @@ old_shas = set(r.object_store) old_refs = r.get_refs() - commit_sha = r.do_commit(b'commit with no ref', - committer=b'Test Committer ', - author=b'Test Author ', - commit_timestamp=12395, commit_timezone=0, - author_timestamp=12395, author_timezone=0, - ref=None, merge_heads=[self._root_commit]) + commit_sha = r.do_commit( + b'commit with no ref', + committer=b'Test Committer ', + author=b'Test Author ', + commit_timestamp=12395, commit_timezone=0, + author_timestamp=12395, author_timezone=0, + ref=None, merge_heads=[self._root_commit]) new_shas = set(r.object_store) - old_shas # New sha is added, but no new refs @@ -764,12 +869,25 @@ self.assertEqual([self._root_commit], r[commit_sha].parents) self.assertEqual(old_refs, r.get_refs()) + def test_stage_absolute(self): + r = self._repo + os.remove(os.path.join(r.path, 'a')) + self.assertRaises(ValueError, r.stage, [os.path.join(r.path, 'a')]) + def test_stage_deleted(self): r = self._repo os.remove(os.path.join(r.path, 'a')) r.stage(['a']) r.stage(['a']) # double-stage a deleted path + def test_stage_directory(self): + r = self._repo + os.mkdir(os.path.join(r.path, 'c')) + r.stage(['c']) + self.assertEqual([b'a'], list(r.open_index())) + + @skipIf(sys.platform == 'win32' and sys.version_info[:2] >= (3, 6), + 'tries to implicitly decode as utf8') def test_commit_no_encode_decode(self): r = self._repo repo_path_bytes = r.path.encode(sys.getfilesystemencoding()) @@ -784,12 +902,13 @@ self.addCleanup(os.remove, full_path) r.stage(names) - commit_sha = r.do_commit(b'Files with different encodings', - committer=b'Test Committer ', - author=b'Test Author ', - commit_timestamp=12395, commit_timezone=0, - author_timestamp=12395, author_timezone=0, - ref=None, merge_heads=[self._root_commit]) + commit_sha = r.do_commit( + b'Files with different encodings', + committer=b'Test Committer ', + author=b'Test Author ', + commit_timestamp=12395, commit_timezone=0, + author_timestamp=12395, author_timezone=0, + ref=None, merge_heads=[self._root_commit]) for name, encoding in zip(names, encodings): mode, id = tree_lookup_path(r.get_object, r[commit_sha].tree, name) diff -Nru dulwich-0.12.0/dulwich/tests/test_server.py dulwich-0.18.5/dulwich/tests/test_server.py --- dulwich-0.12.0/dulwich/tests/test_server.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_server.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,20 +1,22 @@ # test_server.py -- Tests for the git server # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# or (at your option) any later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for the smart protocol server.""" @@ -23,6 +25,8 @@ import shutil import tempfile +import sys + from dulwich.errors import ( GitProtocolError, NotGitRepository, @@ -46,7 +50,7 @@ _split_proto_line, serve_command, _find_shallow, - ProtocolGraphWalker, + _ProtocolGraphWalker, ReceivePackHandler, SingleAckGraphWalkerImpl, UploadPackHandler, @@ -107,11 +111,11 @@ @classmethod def capabilities(cls): - return (b'cap1', b'cap2', b'cap3') + return [b'cap1', b'cap2', b'cap3'] @classmethod def required_capabilities(cls): - return (b'cap2',) + return [b'cap2'] class HandlerTestCase(TestCase): @@ -127,7 +131,9 @@ self.fail(e) def test_capability_line(self): - self.assertEqual(b' cap1 cap2 cap3', self._handler.capability_line()) + self.assertEqual( + b' cap1 cap2 cap3', + self._handler.capability_line([b'cap1', b'cap2', b'cap3'])) def test_set_client_capabilities(self): set_caps = self._handler.set_client_capabilities @@ -148,7 +154,8 @@ self.assertSucceeds(set_caps, [b'cap2', b'ignoreme']) def test_has_capability(self): - self.assertRaises(GitProtocolError, self._handler.has_capability, b'cap') + self.assertRaises(GitProtocolError, self._handler.has_capability, + b'cap') caps = self._handler.capabilities() self._handler.set_client_capabilities(caps) for cap in caps: @@ -201,7 +208,7 @@ caps = list(self._handler.required_capabilities()) + [b'include-tag'] self._handler.set_client_capabilities(caps) self.assertEqual({b'1234' * 10: ONE, b'5678' * 10: TWO}, - self._handler.get_tagged(refs, repo=self._repo)) + self._handler.get_tagged(refs, repo=self._repo)) # non-include-tag case caps = self._handler.required_capabilities() @@ -283,9 +290,11 @@ class TestUploadPackHandler(UploadPackHandler): + @classmethod def required_capabilities(self): - return () + return [] + class ReceivePackHandlerTestCase(TestCase): @@ -302,6 +311,7 @@ b'refs/heads/fake-branch': ONE} self._repo.refs._update(refs) update_refs = [[ONE, ZERO_SHA, b'refs/heads/fake-branch'], ] + self._handler.set_client_capabilities([b'delete-refs']) status = self._handler._apply_pack(update_refs) self.assertEqual(status[0][0], b'unpack') self.assertEqual(status[0][1], b'ok') @@ -314,9 +324,11 @@ super(ProtocolGraphWalkerEmptyTestCase, self).setUp() self._repo = MemoryRepo.init_bare([], {}) backend = DictBackend({b'/': self._repo}) - self._walker = ProtocolGraphWalker( - TestUploadPackHandler(backend, [b'/', b'host=lolcats'], TestProto()), - self._repo.object_store, self._repo.get_peeled) + self._walker = _ProtocolGraphWalker( + TestUploadPackHandler(backend, [b'/', b'host=lolcats'], + TestProto()), + self._repo.object_store, self._repo.get_peeled, + self._repo.refs.get_symrefs) def test_empty_repository(self): # The server should wait for a flush packet. @@ -329,7 +341,6 @@ self.assertEqual(None, self._walker.proto.get_received_line()) - class ProtocolGraphWalkerTestCase(TestCase): def setUp(self): @@ -347,9 +358,11 @@ ] self._repo = MemoryRepo.init_bare(commits, {}) backend = DictBackend({b'/': self._repo}) - self._walker = ProtocolGraphWalker( - TestUploadPackHandler(backend, [b'/', b'host=lolcats'], TestProto()), - self._repo.object_store, self._repo.get_peeled) + self._walker = _ProtocolGraphWalker( + TestUploadPackHandler(backend, [b'/', b'host=lolcats'], + TestProto()), + self._repo.object_store, self._repo.get_peeled, + self._repo.refs.get_symrefs) def test_all_wants_satisfied_no_haves(self): self._walker.set_wants([ONE]) @@ -387,9 +400,9 @@ def test_split_proto_line(self): allowed = (b'want', b'done', None) self.assertEqual((b'want', ONE), - _split_proto_line(b'want ' + ONE + b'\n', allowed)) + _split_proto_line(b'want ' + ONE + b'\n', allowed)) self.assertEqual((b'want', TWO), - _split_proto_line(b'want ' + TWO + b'\n', allowed)) + _split_proto_line(b'want ' + TWO + b'\n', allowed)) self.assertRaises(GitProtocolError, _split_proto_line, b'want xxxx\n', allowed) self.assertRaises(UnexpectedCommandError, _split_proto_line, @@ -397,7 +410,8 @@ self.assertRaises(GitProtocolError, _split_proto_line, b'foo ' + FOUR + b'\n', allowed) self.assertRaises(GitProtocolError, _split_proto_line, b'bar', allowed) - self.assertEqual((b'done', None), _split_proto_line(b'done\n', allowed)) + self.assertEqual((b'done', None), + _split_proto_line(b'done\n', allowed)) self.assertEqual((None, None), _split_proto_line(b'', allowed)) def test_determine_wants(self): @@ -423,16 +437,20 @@ self._walker.advertise_refs = False self._walker.proto.set_output([b'want ' + FOUR + b' multi_ack', None]) - self.assertRaises(GitProtocolError, self._walker.determine_wants, heads) + self.assertRaises(GitProtocolError, self._walker.determine_wants, + heads) self._walker.proto.set_output([None]) self.assertEqual([], self._walker.determine_wants(heads)) - self._walker.proto.set_output([b'want ' + ONE + b' multi_ack', b'foo', None]) - self.assertRaises(GitProtocolError, self._walker.determine_wants, heads) + self._walker.proto.set_output( + [b'want ' + ONE + b' multi_ack', b'foo', None]) + self.assertRaises(GitProtocolError, self._walker.determine_wants, + heads) self._walker.proto.set_output([b'want ' + FOUR + b' multi_ack', None]) - self.assertRaises(GitProtocolError, self._walker.determine_wants, heads) + self.assertRaises(GitProtocolError, self._walker.determine_wants, + heads) def test_determine_wants_advertisement(self): self._walker.proto.set_output([None]) @@ -550,8 +568,8 @@ return # Whether or not PACK is sent after is determined by this, so # record this value. - self.pack_sent = self._impl.handle_done(self.done_required, - self.done_received) + self.pack_sent = self._impl.handle_done( + self.done_required, self.done_received) return self.pack_sent def notify_done(self): @@ -991,11 +1009,14 @@ self.path = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, self.path) self.repo = Repo.init(self.path) - self.backend = FileSystemBackend() + if sys.platform == 'win32': + self.backend = FileSystemBackend(self.path[0] + ':' + os.sep) + else: + self.backend = FileSystemBackend() def test_nonexistant(self): - self.assertRaises(NotGitRepository, - self.backend.open_repository, "/does/not/exist/unless/foo") + self.assertRaises(NotGitRepository, self.backend.open_repository, + "/does/not/exist/unless/foo") def test_absolute(self): repo = self.backend.open_repository(self.path) @@ -1004,8 +1025,9 @@ os.path.normcase(os.path.abspath(self.repo.path))) def test_child(self): - self.assertRaises(NotGitRepository, - self.backend.open_repository, os.path.join(self.path, "foo")) + self.assertRaises( + NotGitRepository, + self.backend.open_repository, os.path.join(self.path, "foo")) def test_bad_repo_path(self): backend = FileSystemBackend() @@ -1020,8 +1042,9 @@ def test_nonexistant(self): repo = MemoryRepo.init_bare([], {}) backend = DictBackend({b'/': repo}) - self.assertRaises(NotGitRepository, - backend.open_repository, "/does/not/exist/unless/foo") + self.assertRaises( + NotGitRepository, backend.open_repository, + "/does/not/exist/unless/foo") def test_bad_repo_path(self): repo = MemoryRepo.init_bare([], {}) @@ -1039,19 +1062,22 @@ self.backend = DictBackend({}) def serve_command(self, handler_cls, args, inf, outf): - return serve_command(handler_cls, [b"test"] + args, backend=self.backend, - inf=inf, outf=outf) + return serve_command( + handler_cls, [b"test"] + args, backend=self.backend, inf=inf, + outf=outf) def test_receive_pack(self): commit = make_commit(id=ONE, parents=[], commit_time=111) self.backend.repos[b"/"] = MemoryRepo.init_bare( [commit], {b"refs/heads/master": commit.id}) outf = BytesIO() - exitcode = self.serve_command(ReceivePackHandler, [b"/"], BytesIO(b"0000"), outf) + exitcode = self.serve_command(ReceivePackHandler, [b"/"], + BytesIO(b"0000"), outf) outlines = outf.getvalue().splitlines() self.assertEqual(2, len(outlines)) - self.assertEqual(b"1111111111111111111111111111111111111111 refs/heads/master", - outlines[0][4:].split(b"\x00")[0]) + self.assertEqual( + b"1111111111111111111111111111111111111111 refs/heads/master", + outlines[0][4:].split(b"\x00")[0]) self.assertEqual(b"0000", outlines[-1]) self.assertEqual(0, exitcode) @@ -1069,7 +1095,8 @@ update_server_info(self.repo) with open(os.path.join(self.path, ".git", "info", "refs"), 'rb') as f: self.assertEqual(b'', f.read()) - with open(os.path.join(self.path, ".git", "objects", "info", "packs"), 'rb') as f: + p = os.path.join(self.path, ".git", "objects", "info", "packs") + with open(p, 'rb') as f: self.assertEqual(b'', f.read()) def test_simple(self): @@ -1080,5 +1107,6 @@ update_server_info(self.repo) with open(os.path.join(self.path, ".git", "info", "refs"), 'rb') as f: self.assertEqual(f.read(), commit_id + b'\trefs/heads/foo\n') - with open(os.path.join(self.path, ".git", "objects", "info", "packs"), 'rb') as f: + p = os.path.join(self.path, ".git", "objects", "info", "packs") + with open(p, 'rb') as f: self.assertEqual(f.read(), b'') diff -Nru dulwich-0.12.0/dulwich/tests/test_utils.py dulwich-0.18.5/dulwich/tests/test_utils.py --- dulwich-0.12.0/dulwich/tests/test_utils.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_utils.py 2017-07-29 00:14:28.000000000 +0000 @@ -1,21 +1,22 @@ # test_utils.py -- Tests for git test utilities. # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License - -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, -# Boston, MA 02110-1301, USA. """Tests for git test utilities.""" diff -Nru dulwich-0.12.0/dulwich/tests/test_walk.py dulwich-0.18.5/dulwich/tests/test_walk.py --- dulwich-0.12.0/dulwich/tests/test_walk.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_walk.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,20 +1,22 @@ # test_walk.py -- Tests for commit walking functionality. # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# or (at your option) any later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for commit walking functionality.""" @@ -48,6 +50,7 @@ from dulwich.tests.utils import ( F, make_object, + make_tag, build_commit_graph, ) @@ -102,6 +105,12 @@ actual = list(walker) self.assertEqual(expected, actual) + def test_tag(self): + c1, c2, c3 = self.make_linear_commits(3) + t2 = make_tag(target=c2) + self.store.add_object(t2) + self.assertWalkYields([c2, c1], [t2.id]) + def test_linear(self): c1, c2, c3 = self.make_linear_commits(3) self.assertWalkYields([c1], [c1.id]) @@ -167,9 +176,11 @@ 2, trees={1: [(b'a', blob_a1)], 2: [(b'a', blob_a2), (b'b', blob_b2)]}) e1 = TestWalkEntry(c1, [TreeChange.add((b'a', F, blob_a1.id))]) - e2 = TestWalkEntry(c2, [TreeChange(CHANGE_MODIFY, (b'a', F, blob_a1.id), + e2 = TestWalkEntry( + c2, + [TreeChange(CHANGE_MODIFY, (b'a', F, blob_a1.id), (b'a', F, blob_a2.id)), - TreeChange.add((b'b', F, blob_b2.id))]) + TreeChange.add((b'b', F, blob_b2.id))]) self.assertWalkYields([e2, e1], [c2.id]) def test_changes_multiple_parents(self): @@ -182,8 +193,9 @@ 3: [(b'a', blob_a3), (b'b', blob_b2)]}) # a is a modify/add conflict and b is not conflicted. changes = [[ - TreeChange(CHANGE_MODIFY, (b'a', F, blob_a1.id), (b'a', F, blob_a3.id)), - TreeChange.add((b'a', F, blob_a3.id)), + TreeChange(CHANGE_MODIFY, + (b'a', F, blob_a1.id), (b'a', F, blob_a3.id)), + TreeChange.add((b'a', F, blob_a3.id)), ]] self.assertWalkYields([TestWalkEntry(c3, changes)], [c3.id], exclude=[c1.id, c2.id]) @@ -283,7 +295,8 @@ c1, c2, c3, c4, c5, c6 = self.make_linear_commits(6, trees=trees) self.assertWalkYields([c5], [c6.id], paths=[b'c']) - e = lambda n: (n, F, blob.id) + def e(n): + return (n, F, blob.id) self.assertWalkYields( [TestWalkEntry(c5, [TreeChange(CHANGE_RENAME, e(b'b'), e(b'c'))]), TestWalkEntry(c3, [TreeChange(CHANGE_RENAME, e(b'a'), e(b'b'))]), @@ -300,7 +313,8 @@ 5: [(b'a', blob)], 6: [(b'c', blob)]}) - e = lambda n: (n, F, blob.id) + def e(n): + return (n, F, blob.id) # Once the path changes to b, we aren't interested in a or c anymore. self.assertWalkYields( [TestWalkEntry(c6, [TreeChange(CHANGE_RENAME, e(b'a'), e(b'c'))]), @@ -346,8 +360,8 @@ 11, times=[9, 0, 1, 2, 3, 4, 5, 8, 6, 7, 9]) c8, _, c10, c11 = commits[-4:] del self.store[commits[0].id] - # c9 is older than we want to walk, but is out of order with its parent, - # so we need to walk past it to get to c8. + # c9 is older than we want to walk, but is out of order with its + # parent, so we need to walk past it to get to c8. # c1 would also match, but we've deleted it, and it should get pruned # even with over-scanning. self.assertWalkYields([c11, c10, c8], [c11.id], since=7) @@ -413,3 +427,128 @@ def test_empty_walk(self): c1, c2, c3 = self.make_linear_commits(3) self.assertWalkYields([], [c3.id], exclude=[c3.id]) + + +class WalkEntryTest(TestCase): + + def setUp(self): + super(WalkEntryTest, self).setUp() + self.store = MemoryObjectStore() + + def make_commits(self, commit_spec, **kwargs): + times = kwargs.pop('times', []) + attrs = kwargs.pop('attrs', {}) + for i, t in enumerate(times): + attrs.setdefault(i + 1, {})['commit_time'] = t + return build_commit_graph(self.store, commit_spec, attrs=attrs, + **kwargs) + + def make_linear_commits(self, num_commits, **kwargs): + commit_spec = [] + for i in range(1, num_commits + 1): + c = [i] + if i > 1: + c.append(i - 1) + commit_spec.append(c) + return self.make_commits(commit_spec, **kwargs) + + def test_all_changes(self): + # Construct a commit with 2 files in different subdirectories. + blob_a = make_object(Blob, data=b'a') + blob_b = make_object(Blob, data=b'b') + c1 = self.make_linear_commits( + 1, + trees={1: [(b'x/a', blob_a), (b'y/b', blob_b)]}, + )[0] + + # Get the WalkEntry for the commit. + walker = Walker(self.store, c1.id) + walker_entry = list(walker)[0] + changes = walker_entry.changes() + + # Compare the changes with the expected values. + entry_a = (b'x/a', F, blob_a.id) + entry_b = (b'y/b', F, blob_b.id) + self.assertEqual( + [TreeChange.add(entry_a), + TreeChange.add(entry_b)], + changes, + ) + + def test_all_with_merge(self): + blob_a = make_object(Blob, data=b'a') + blob_a2 = make_object(Blob, data=b'a2') + blob_b = make_object(Blob, data=b'b') + blob_b2 = make_object(Blob, data=b'b2') + x1, y2, m3 = self.make_commits( + [[1], [2], [3, 1, 2]], + trees={1: [(b'x/a', blob_a)], + 2: [(b'y/b', blob_b)], + 3: [(b'x/a', blob_a2), (b'y/b', blob_b2)]}) + + # Get the WalkEntry for the merge commit. + walker = Walker(self.store, m3.id) + entries = list(walker) + walker_entry = entries[0] + self.assertEqual(walker_entry.commit.id, m3.id) + changes = walker_entry.changes() + self.assertEqual(2, len(changes)) + + entry_a = (b'x/a', F, blob_a.id) + entry_a2 = (b'x/a', F, blob_a2.id) + entry_b = (b'y/b', F, blob_b.id) + entry_b2 = (b'y/b', F, blob_b2.id) + self.assertEqual( + [[TreeChange(CHANGE_MODIFY, entry_a, entry_a2), + TreeChange.add(entry_a2)], + [TreeChange.add(entry_b2), + TreeChange(CHANGE_MODIFY, entry_b, entry_b2)]], + changes, + ) + + def test_filter_changes(self): + # Construct a commit with 2 files in different subdirectories. + blob_a = make_object(Blob, data=b'a') + blob_b = make_object(Blob, data=b'b') + c1 = self.make_linear_commits( + 1, + trees={1: [(b'x/a', blob_a), (b'y/b', blob_b)]}, + )[0] + + # Get the WalkEntry for the commit. + walker = Walker(self.store, c1.id) + walker_entry = list(walker)[0] + changes = walker_entry.changes(path_prefix=b'x') + + # Compare the changes with the expected values. + entry_a = (b'a', F, blob_a.id) + self.assertEqual( + [TreeChange.add(entry_a)], + changes, + ) + + def test_filter_with_merge(self): + blob_a = make_object(Blob, data=b'a') + blob_a2 = make_object(Blob, data=b'a2') + blob_b = make_object(Blob, data=b'b') + blob_b2 = make_object(Blob, data=b'b2') + x1, y2, m3 = self.make_commits( + [[1], [2], [3, 1, 2]], + trees={1: [(b'x/a', blob_a)], + 2: [(b'y/b', blob_b)], + 3: [(b'x/a', blob_a2), (b'y/b', blob_b2)]}) + + # Get the WalkEntry for the merge commit. + walker = Walker(self.store, m3.id) + entries = list(walker) + walker_entry = entries[0] + self.assertEqual(walker_entry.commit.id, m3.id) + changes = walker_entry.changes(b'x') + self.assertEqual(1, len(changes)) + + entry_a = (b'a', F, blob_a.id) + entry_a2 = (b'a', F, blob_a2.id) + self.assertEqual( + [[TreeChange(CHANGE_MODIFY, entry_a, entry_a2)]], + changes, + ) diff -Nru dulwich-0.12.0/dulwich/tests/test_web.py dulwich-0.18.5/dulwich/tests/test_web.py --- dulwich-0.12.0/dulwich/tests/test_web.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/test_web.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,20 +1,22 @@ # test_web.py -- Tests for the git HTTP server # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# or (at your option) any later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Tests for the Git HTTP server.""" @@ -64,6 +66,30 @@ ) +class MinimalistWSGIInputStream(object): + """WSGI input stream with no 'seek()' and 'tell()' methods.""" + def __init__(self, data): + self.data = data + self.pos = 0 + + def read(self, howmuch): + start = self.pos + end = self.pos + howmuch + if start >= len(self.data): + return '' + self.pos = end + return self.data[start:end] + + +class MinimalistWSGIInputStream2(MinimalistWSGIInputStream): + """WSGI input stream with no *working* 'seek()' and 'tell()' methods.""" + def seek(self, pos): + raise NotImplementedError + + def tell(self): + raise NotImplementedError + + class TestHTTPGitRequest(HTTPGitRequest): """HTTPGitRequest with overridden methods to help test caching.""" @@ -134,7 +160,7 @@ xs = b'x' * bufsize f = BytesIO(2 * xs) self.assertEqual([xs, xs], - list(send_file(self._req, f, 'some/thing'))) + list(send_file(self._req, f, 'some/thing'))) self.assertEqual(HTTP_OK, self._status) self.assertContentTypeEquals('some/thing') self.assertTrue(f.closed) @@ -193,15 +219,18 @@ backend = _test_backend([blob]) mat = re.search('^(..)(.{38})$', blob.id.decode('ascii')) - def as_legacy_object_error(): + def as_legacy_object_error(self): raise IOError - blob.as_legacy_object = as_legacy_object_error + self.addCleanup( + setattr, Blob, 'as_legacy_object', Blob.as_legacy_object) + Blob.as_legacy_object = as_legacy_object_error list(get_loose_object(self._req, backend, mat)) self.assertEqual(HTTP_ERROR, self._status) def test_get_pack_file(self): - pack_name = os.path.join('objects', 'pack', 'pack-%s.pack' % ('1' * 40)) + pack_name = os.path.join( + 'objects', 'pack', 'pack-%s.pack' % ('1' * 40)) backend = _test_backend([], named_files={pack_name: b'pack contents'}) mat = re.search('.*', pack_name) output = b''.join(get_pack_file(self._req, backend, mat)) @@ -240,10 +269,10 @@ mat = re.search('.*', '//info/refs') self.assertEqual([blob1.id + b'\trefs/heads/master\n', - blob3.id + b'\trefs/tags/blob-tag\n', - tag1.id + b'\trefs/tags/tag-tag\n', - blob2.id + b'\trefs/tags/tag-tag^{}\n'], - list(get_info_refs(self._req, backend, mat))) + blob3.id + b'\trefs/tags/blob-tag\n', + tag1.id + b'\trefs/tags/tag-tag\n', + blob2.id + b'\trefs/tags/tag-tag^{}\n'], + list(get_info_refs(self._req, backend, mat))) self.assertEqual(HTTP_OK, self._status) self.assertContentTypeEquals('text/plain') self.assertFalse(self._req.cached) @@ -272,7 +301,8 @@ mat = re.search('.*', '//info/packs') output = b''.join(get_info_packs(self._req, backend, mat)) expected = b''.join( - [(b'P pack-' + s + b'.pack\n') for s in [b'1' * 40, b'2' * 40, b'3' * 40]]) + [(b'P pack-' + s + b'.pack\n') + for s in [b'1' * 40, b'2' * 40, b'3' * 40]]) self.assertEqual(expected, output) self.assertEqual(HTTP_OK, self._status) self.assertContentTypeEquals('text/plain') @@ -297,13 +327,13 @@ return self._handler def _handlers(self): - return {'git-upload-pack': self._make_handler} + return {b'git-upload-pack': self._make_handler} def test_handle_service_request_unknown(self): mat = re.search('.*', '/git-evil-handler') content = list(handle_service_request(self._req, 'backend', mat)) self.assertEqual(HTTP_FORBIDDEN, self._status) - self.assertFalse('git-evil-handler' in "".join(content)) + self.assertFalse(b'git-evil-handler' in b"".join(content)) self.assertFalse(self._req.cached) def _run_handle_service_request(self, content_length=None): @@ -311,11 +341,11 @@ if content_length is not None: self._environ['CONTENT_LENGTH'] = content_length mat = re.search('.*', '/git-upload-pack') - handler_output = ''.join( + handler_output = b''.join( handle_service_request(self._req, 'backend', mat)) write_output = self._output.getvalue() # Ensure all output was written via the write callback. - self.assertEqual('', handler_output) + self.assertEqual(b'', handler_output) self.assertEqual(b'handled input: foo', write_output) self.assertContentTypeEquals('application/x-git-upload-pack-result') self.assertFalse(self._handler.advertise_refs) @@ -334,7 +364,7 @@ def test_get_info_refs_unknown(self): self._environ['QUERY_STRING'] = 'service=git-evil-handler' content = list(get_info_refs(self._req, b'backend', None)) - self.assertFalse('git-evil-handler' in "".join(content)) + self.assertFalse(b'git-evil-handler' in b"".join(content)) self.assertEqual(HTTP_FORBIDDEN, self._status) self.assertFalse(self._req.cached) @@ -346,9 +376,9 @@ handler_output = b''.join(get_info_refs(self._req, b'backend', mat)) write_output = self._output.getvalue() self.assertEqual((b'001e# service=git-upload-pack\n' - b'0000' - # input is ignored by the handler - b'handled input: '), write_output) + b'0000' + # input is ignored by the handler + b'handled input: '), write_output) # Ensure all output was written via the write callback. self.assertEqual(b'', handler_output) self.assertTrue(self._handler.advertise_refs) @@ -381,18 +411,18 @@ def test_not_found(self): self._req.cache_forever() # cache headers should be discarded message = 'Something not found' - self.assertEqual(message, self._req.not_found(message)) + self.assertEqual(message.encode('ascii'), self._req.not_found(message)) self.assertEqual(HTTP_NOT_FOUND, self._status) self.assertEqual(set([('Content-Type', 'text/plain')]), - set(self._headers)) + set(self._headers)) def test_forbidden(self): self._req.cache_forever() # cache headers should be discarded message = 'Something not found' - self.assertEqual(message, self._req.forbidden(message)) + self.assertEqual(message.encode('ascii'), self._req.forbidden(message)) self.assertEqual(HTTP_FORBIDDEN, self._status) self.assertEqual(set([('Content-Type', 'text/plain')]), - set(self._headers)) + set(self._headers)) def test_respond_ok(self): self._req.respond() @@ -497,19 +527,17 @@ 'wsgi.input' except for '.read()'. (In particular, it shouldn't require '.seek()'. See https://github.com/jelmer/dulwich/issues/140.) """ - class MinimalistWSGIInputStream(object): - def __init__(self, data): - self.data = data - self.pos = 0 - - def read(self, howmuch): - start = self.pos - end = self.pos + howmuch - if start >= len(self.data): - return '' - self.pos = end - return self.data[start:end] - zstream, zlength = self._get_zstream(self.example_text) - self._test_call(self.example_text, + self._test_call( + self.example_text, MinimalistWSGIInputStream(zstream.read()), zlength) + + def test_call_no_working_seek(self): + """ + Similar to 'test_call_no_seek', but this time the methods are available + (but defunct). See https://github.com/jonashaag/klaus/issues/154. + """ + zstream, zlength = self._get_zstream(self.example_text) + self._test_call( + self.example_text, + MinimalistWSGIInputStream2(zstream.read()), zlength) diff -Nru dulwich-0.12.0/dulwich/tests/utils.py dulwich-0.18.5/dulwich/tests/utils.py --- dulwich-0.12.0/dulwich/tests/utils.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/tests/utils.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,21 +1,22 @@ # utils.py -- Test utilities for Dulwich. # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# of the License or (at your option) any later version of -# the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """Utility functions common to Dulwich tests.""" @@ -23,7 +24,6 @@ import datetime import os import shutil -import sys import tempfile import time import types @@ -50,9 +50,9 @@ create_delta, ) from dulwich.repo import Repo -from dulwich.tests import ( - SkipTest, +from dulwich.tests import ( # noqa: F401 skipIf, + SkipTest, ) @@ -92,7 +92,8 @@ """Make an object for testing and assign some members. This method creates a new subclass to allow arbitrary attribute - reassignment, which is not otherwise possible with objects having __slots__. + reassignment, which is not otherwise possible with objects having + __slots__. :param attrs: dict of attributes to set on the new object. :return: A newly initialized object of type cls. @@ -101,9 +102,9 @@ class TestObject(cls): """Class that inherits from the given class, but without __slots__. - Note that classes with __slots__ can't have arbitrary attributes monkey- - patched in, so this is a class that is exactly the same only with a - __dict__ instead of __slots__. + Note that classes with __slots__ can't have arbitrary attributes + monkey-patched in, so this is a class that is exactly the same only + with a __dict__ instead of __slots__. """ pass TestObject.__name__ = 'TestObject_' + cls.__name__ @@ -125,7 +126,7 @@ :param attrs: dict of attributes to overwrite from the default values. :return: A newly initialized Commit object. """ - default_time = int(time.mktime(datetime.datetime(2010, 1, 1).timetuple())) + default_time = 1262304000 # 2010-01-01 00:00:00 all_attrs = {'author': b'Test Author ', 'author_time': default_time, 'author_timezone': 0, @@ -286,15 +287,15 @@ :param object_store: An ObjectStore to commit objects to. :param commit_spec: An iterable of iterables of ints defining the commit - graph. Each entry defines one commit, and entries must be in topological - order. The first element of each entry is a commit number, and the - remaining elements are its parents. The commit numbers are only + graph. Each entry defines one commit, and entries must be in + topological order. The first element of each entry is a commit number, + and the remaining elements are its parents. The commit numbers are only meaningful for the call to make_commits; since real commit objects are created, they will get created with real, opaque SHAs. :param trees: An optional dict of commit number -> tree spec for building - trees for commits. The tree spec is an iterable of (path, blob, mode) or - (path, blob) entries; if mode is omitted, it defaults to the normal file - mode (0100644). + trees for commits. The tree spec is an iterable of (path, blob, mode) + or (path, blob) entries; if mode is omitted, it defaults to the normal + file mode (0100644). :param attrs: A dict of commit number -> (dict of attribute -> value) for assigning additional values to the commits. :return: The list of commit objects created. @@ -360,5 +361,3 @@ warnings.showwarning = original_showwarning return caught_warnings, restore_showwarning - -skipIfPY3 = skipIf(sys.version_info[0] == 3, "Feature not yet ported to python3.") diff -Nru dulwich-0.12.0/dulwich/walk.py dulwich-0.18.5/dulwich/walk.py --- dulwich-0.12.0/dulwich/walk.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/walk.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,20 +1,22 @@ # walk.py -- General implementation of walking commits and their contents. # Copyright (C) 2010 Google, Inc. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# or (at your option) any later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """General implementation of walking commits and their contents.""" @@ -34,6 +36,9 @@ from dulwich.errors import ( MissingCommitError, ) +from dulwich.objects import ( + Tag, + ) ORDER_DATE = 'date' ORDER_TOPO = 'topo' @@ -51,18 +56,22 @@ self.commit = commit self._store = walker.store self._get_parents = walker.get_parents - self._changes = None + self._changes = {} self._rename_detector = walker.rename_detector - def changes(self): + def changes(self, path_prefix=None): """Get the tree changes for this entry. + :param path_prefix: Portion of the path in the repository to + use to filter changes. Must be a directory name. Must be + a full, valid, path reference (no partial names or wildcards). :return: For commits with up to one parent, a list of TreeChange - objects; if the commit has no parents, these will be relative to the - empty tree. For merge commits, a list of lists of TreeChange + objects; if the commit has no parents, these will be relative to + the empty tree. For merge commits, a list of lists of TreeChange objects; see dulwich.diff.tree_changes_for_merge. """ - if self._changes is None: + cached = self._changes.get(path_prefix) + if cached is None: commit = self.commit if not self._get_parents(commit): changes_func = tree_changes @@ -70,13 +79,41 @@ elif len(self._get_parents(commit)) == 1: changes_func = tree_changes parent = self._store[self._get_parents(commit)[0]].tree + if path_prefix: + mode, subtree_sha = parent.lookup_path( + self._store.__getitem__, + path_prefix, + ) + parent = self._store[subtree_sha] else: changes_func = tree_changes_for_merge - parent = [self._store[p].tree for p in self._get_parents(commit)] - self._changes = list(changes_func( - self._store, parent, commit.tree, + parent = [ + self._store[p].tree for p in self._get_parents(commit)] + if path_prefix: + parent_trees = [self._store[p] for p in parent] + parent = [] + for p in parent_trees: + try: + mode, st = p.lookup_path( + self._store.__getitem__, + path_prefix, + ) + except KeyError: + pass + else: + parent.append(st) + commit_tree_sha = commit.tree + if path_prefix: + commit_tree = self._store[commit_tree_sha] + mode, commit_tree_sha = commit_tree.lookup_path( + self._store.__getitem__, + path_prefix, + ) + cached = list(changes_func( + self._store, parent, commit_tree_sha, rename_detector=self._rename_detector)) - return self._changes + self._changes[path_prefix] = cached + return self._changes[path_prefix] def __repr__(self): return '' % ( @@ -103,15 +140,20 @@ for commit_id in chain(walker.include, walker.excluded): self._push(commit_id) - def _push(self, commit_id): + def _push(self, object_id): try: - commit = self._store[commit_id] + obj = self._store[object_id] except KeyError: - raise MissingCommitError(commit_id) - if commit_id not in self._pq_set and commit_id not in self._done: + raise MissingCommitError(object_id) + if isinstance(obj, Tag): + self._push(obj.object[1]) + return + # TODO(jelmer): What to do about non-Commit and non-Tag objects? + commit = obj + if commit.id not in self._pq_set and commit.id not in self._done: heapq.heappush(self._pq, (-commit.commit_time, commit)) - self._pq_set.add(commit_id) - self._seen.add(commit_id) + self._pq_set.add(commit.id) + self._seen.add(commit.id) def _exclude_parents(self, commit): excluded = self._excluded @@ -150,20 +192,20 @@ for _, c in self._pq): _, n = self._pq[0] if self._last and n.commit_time >= self._last.commit_time: - # If the next commit is newer than the last one, we need - # to keep walking in case its parents (which we may not - # have seen yet) are excluded. This gives the excluded - # set a chance to "catch up" while the commit is still - # in the Walker's output queue. + # If the next commit is newer than the last one, we + # need to keep walking in case its parents (which we + # may not have seen yet) are excluded. This gives the + # excluded set a chance to "catch up" while the commit + # is still in the Walker's output queue. reset_extra_commits = True else: reset_extra_commits = False if (self._min_time is not None and - commit.commit_time < self._min_time): + commit.commit_time < self._min_time): # We want to stop walking at min_time, but commits at the - # boundary may be out of order with respect to their parents. So - # we walk _MAX_EXTRA_COMMITS more commits once we hit this + # boundary may be out of order with respect to their parents. + # So we walk _MAX_EXTRA_COMMITS more commits once we hit this # boundary. reset_extra_commits = False @@ -203,8 +245,8 @@ ancestors. :param exclude: Iterable of SHAs of commits to exclude along with their ancestors, overriding includes. - :param order: ORDER_* constant specifying the order of results. Anything - other than ORDER_DATE may result in O(n) memory usage. + :param order: ORDER_* constant specifying the order of results. + Anything other than ORDER_DATE may result in O(n) memory usage. :param reverse: If True, reverse the order of output, requiring O(n) memory. :param max_entries: The maximum number of entries to yield, or None for @@ -226,7 +268,9 @@ if order not in ALL_ORDERS: raise ValueError('Unknown walk order %s' % order) self.store = store - if not isinstance(include, list): + if isinstance(include, bytes): + # TODO(jelmer): Really, this should require a single type. + # Print deprecation warning here? include = [include] self.include = include self.excluded = set(exclude or []) @@ -276,8 +320,8 @@ """Determine if a walk entry should be returned.. :param entry: The WalkEntry to consider. - :return: True if the WalkEntry should be returned by this walk, or False - otherwise (e.g. if it doesn't match any requested paths). + :return: True if the WalkEntry should be returned by this walk, or + False otherwise (e.g. if it doesn't match any requested paths). """ commit = entry.commit if self.since is not None and commit.commit_time < self.since: @@ -324,8 +368,8 @@ :param results: An iterator of WalkEntry objects, in the order returned from the queue_cls. - :return: An iterator or list of WalkEntry objects, in the order required - by the Walker. + :return: An iterator or list of WalkEntry objects, in the order + required by the Walker. """ if self.order == ORDER_TOPO: results = _topo_reorder(results, self.get_parents) diff -Nru dulwich-0.12.0/dulwich/web.py dulwich-0.18.5/dulwich/web.py --- dulwich-0.12.0/dulwich/web.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich/web.py 2017-10-07 15:45:18.000000000 +0000 @@ -2,20 +2,22 @@ # Copyright (C) 2010 Google, Inc. # Copyright (C) 2012 Jelmer Vernooij # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# or (at your option) any later version of the License. +# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU +# General Public License as public by the Free Software Foundation; version 2.0 +# or (at your option) any later version. You can redistribute it and/or +# modify it under the terms of either of these two licenses. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the licenses; if not, see +# for a copy of the GNU General Public License +# and for a copy of the Apache +# License, Version 2.0. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. """HTTP server for dulwich that implements the git smart HTTP protocol.""" @@ -88,8 +90,8 @@ :param mat: A regex match object. :returns: The URL prefix, defined as the text before the match in the - original string. Normalized to start with one leading slash and end with - zero. + original string. Normalized to start with one leading slash and end + with zero. """ return '/' + mat.string[:mat.start()].strip('/') @@ -175,16 +177,18 @@ params = parse_qs(req.environ['QUERY_STRING']) service = params.get('service', [None])[0] if service and not req.dumb: - handler_cls = req.handlers.get(service, None) + handler_cls = req.handlers.get(service.encode('ascii'), None) if handler_cls is None: yield req.forbidden('Unsupported service') return req.nocache() - write = req.respond(HTTP_OK, 'application/x-%s-advertisement' % service) + write = req.respond( + HTTP_OK, 'application/x-%s-advertisement' % service) proto = ReceivableProtocol(BytesIO().read, write) handler = handler_cls(backend, [url_prefix(mat)], proto, http_req=req, advertise_refs=True) - handler.proto.write_pkt_line(b'# service=' + service.encode('ascii') + b'\n') + handler.proto.write_pkt_line( + b'# service=' + service.encode('ascii') + b'\n') handler.proto.write_pkt_line(None) handler.handle() else: @@ -231,7 +235,7 @@ def handle_service_request(req, backend, mat): service = mat.group().lstrip('/') logger.info('Handling service request for %s', service) - handler_cls = req.handlers.get(service, None) + handler_cls = req.handlers.get(service.encode('ascii'), None) if handler_cls is None: yield req.forbidden('Unsupported service') return @@ -275,21 +279,21 @@ self._cache_headers = [] logger.info('Not found: %s', message) self.respond(HTTP_NOT_FOUND, 'text/plain') - return message + return message.encode('ascii') def forbidden(self, message): """Begin a HTTP 403 response and return the text of a message.""" self._cache_headers = [] logger.info('Forbidden: %s', message) self.respond(HTTP_FORBIDDEN, 'text/plain') - return message + return message.encode('ascii') def error(self, message): """Begin a HTTP 500 response and return the text of a message.""" self._cache_headers = [] logger.error('Error: %s', message) self.respond(HTTP_ERROR, 'text/plain') - return message + return message.encode('ascii') def nocache(self): """Set the response to never be cached by the client.""" @@ -321,9 +325,12 @@ ('GET', re.compile('/objects/info/alternates$')): get_text_file, ('GET', re.compile('/objects/info/http-alternates$')): get_text_file, ('GET', re.compile('/objects/info/packs$')): get_info_packs, - ('GET', re.compile('/objects/([0-9a-f]{2})/([0-9a-f]{38})$')): get_loose_object, - ('GET', re.compile('/objects/pack/pack-([0-9a-f]{40})\\.pack$')): get_pack_file, - ('GET', re.compile('/objects/pack/pack-([0-9a-f]{40})\\.idx$')): get_idx_file, + ('GET', re.compile('/objects/([0-9a-f]{2})/([0-9a-f]{38})$')): + get_loose_object, + ('GET', re.compile('/objects/pack/pack-([0-9a-f]{40})\\.pack$')): + get_pack_file, + ('GET', re.compile('/objects/pack/pack-([0-9a-f]{40})\\.idx$')): + get_idx_file, ('POST', re.compile('/git-upload-pack$')): handle_service_request, ('POST', re.compile('/git-receive-pack$')): handle_service_request, @@ -356,7 +363,7 @@ if self.fallback_app is not None: return self.fallback_app(environ, start_response) else: - return req.not_found('Sorry, that method is not supported') + return [req.not_found('Sorry, that method is not supported')] return handler(req, self.backend, mat) @@ -371,18 +378,20 @@ def __call__(self, environ, start_response): if environ.get('HTTP_CONTENT_ENCODING', '') == 'gzip': - if hasattr(environ['wsgi.input'], 'seek'): + try: + environ['wsgi.input'].tell() wsgi_input = environ['wsgi.input'] - else: + except (AttributeError, IOError, NotImplementedError): # The gzip implementation in the standard library of Python 2.x - # requires the '.seek()' and '.tell()' methods to be available - # on the input stream. Read the data into a temporary file to - # work around this limitation. + # requires working '.seek()' and '.tell()' methods on the input + # stream. Read the data into a temporary file to work around + # this limitation. wsgi_input = tempfile.SpooledTemporaryFile(16 * 1024 * 1024) shutil.copyfileobj(environ['wsgi.input'], wsgi_input) wsgi_input.seek(0) - environ['wsgi.input'] = gzip.GzipFile(filename=None, fileobj=wsgi_input, mode='r') + environ['wsgi.input'] = gzip.GzipFile( + filename=None, fileobj=wsgi_input, mode='r') del environ['HTTP_CONTENT_ENCODING'] if 'CONTENT_LENGTH' in environ: del environ['CONTENT_LENGTH'] @@ -415,7 +424,7 @@ correctly wrapped with needed middleware. """ app = HTTPGitApplication(*args, **kwargs) - wrapped_app = GunzipFilter(LimitedInputFilter(app)) + wrapped_app = LimitedInputFilter(GunzipFilter(app)) return wrapped_app @@ -423,7 +432,7 @@ """ServerHandler that uses dulwich's logger for logging exceptions.""" def log_exception(self, exc_info): - if sys.version < (2, 7): + if sys.version_info < (2, 7): logger.exception('Exception happened during processing of request') else: logger.exception('Exception happened during processing of request', @@ -453,7 +462,7 @@ """Handle a single HTTP request""" self.raw_requestline = self.rfile.readline() - if not self.parse_request(): # An error code has been sent, just exit + if not self.parse_request(): # An error code has been sent, just exit return handler = ServerHandlerLogger( @@ -467,7 +476,9 @@ def handle_error(self, request, client_address): """Handle an error. """ - logger.exception('Exception happened during processing of request from %s' % str(client_address)) + logger.exception( + 'Exception happened during processing of request from %s' % + str(client_address)) def main(argv=sys.argv): diff -Nru dulwich-0.12.0/dulwich.egg-info/PKG-INFO dulwich-0.18.5/dulwich.egg-info/PKG-INFO --- dulwich-0.12.0/dulwich.egg-info/PKG-INFO 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich.egg-info/PKG-INFO 2017-10-29 16:36:37.000000000 +0000 @@ -1,11 +1,11 @@ Metadata-Version: 1.1 Name: dulwich -Version: 0.12.0 +Version: 0.18.5 Summary: Python Git Library Home-page: https://www.dulwich.io/ -Author: Jelmer Vernooij +Author: UNKNOWN Author-email: jelmer@jelmer.uk -License: GPLv2 or later +License: Apachev2 or later or GPLv2 Description: Python implementation of the Git file formats and protocols, without the need to have git installed. @@ -13,17 +13,20 @@ All functionality is available in pure Python. Optional C extensions can be built for improved performance. - The project is named after the part of London that Mr. and Mrs. Git live in - in the particular Monty Python sketch. + The project is named after the part of London that Mr. and Mrs. Git live + in in the particular Monty Python sketch. Keywords: git Platform: UNKNOWN Classifier: Development Status :: 4 - Beta -Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+) -Classifier: Programming Language :: Python :: 2.6 +Classifier: License :: OSI Approved :: Apache Software License Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Operating System :: POSIX +Classifier: Operating System :: Microsoft :: Windows Classifier: Topic :: Software Development :: Version Control diff -Nru dulwich-0.12.0/dulwich.egg-info/SOURCES.txt dulwich-0.18.5/dulwich.egg-info/SOURCES.txt --- dulwich-0.12.0/dulwich.egg-info/SOURCES.txt 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/dulwich.egg-info/SOURCES.txt 2017-10-29 16:36:37.000000000 +0000 @@ -1,11 +1,13 @@ .testr.conf +.travis.yml AUTHORS -CONTRIBUTING +CONTRIBUTING.md COPYING MANIFEST.in Makefile NEWS README.md +README.swift.md TODO appveyor.yml dulwich.cfg @@ -23,6 +25,7 @@ docs/protocol.txt docs/tutorial/Makefile docs/tutorial/conclusion.txt +docs/tutorial/encoding.txt docs/tutorial/file-format.txt docs/tutorial/index.txt docs/tutorial/introduction.txt @@ -32,7 +35,6 @@ docs/tutorial/repo.txt docs/tutorial/tag.txt dulwich/__init__.py -dulwich/_compat.py dulwich/_diff_tree.c dulwich/_objects.c dulwich/_pack.c @@ -45,6 +47,7 @@ dulwich/file.py dulwich/greenthreads.py dulwich/hooks.py +dulwich/ignore.py dulwich/index.py dulwich/log_utils.py dulwich/lru_cache.py @@ -68,7 +71,9 @@ dulwich.egg-info/top_level.txt dulwich/contrib/__init__.py dulwich/contrib/paramiko_vendor.py +dulwich/contrib/release_robot.py dulwich/contrib/swift.py +dulwich/contrib/test_release_robot.py dulwich/contrib/test_swift.py dulwich/contrib/test_swift_smoke.py dulwich/tests/__init__.py @@ -82,6 +87,7 @@ dulwich/tests/test_grafts.py dulwich/tests/test_greenthreads.py dulwich/tests/test_hooks.py +dulwich/tests/test_ignore.py dulwich/tests/test_index.py dulwich/tests/test_lru_cache.py dulwich/tests/test_missing_obj_finder.py diff -Nru dulwich-0.12.0/examples/diff.py dulwich-0.18.5/examples/diff.py --- dulwich-0.12.0/examples/diff.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/examples/diff.py 2017-07-29 00:14:28.000000000 +0000 @@ -10,10 +10,11 @@ import sys repo_path = "." -commit_id = "a6602654997420bcfd0bee2a0563d9416afe34b4" +commit_id = b"a6602654997420bcfd0bee2a0563d9416afe34b4" r = Repo(repo_path) commit = r[commit_id] parent_commit = r[commit.parents[0]] -write_tree_diff(sys.stdout, r.object_store, parent_commit.tree, commit.tree) +outstream = getattr(sys.stdout, 'buffer', sys.stdout) +write_tree_diff(outstream, r.object_store, parent_commit.tree, commit.tree) diff -Nru dulwich-0.12.0/examples/latest_change.py dulwich-0.18.5/examples/latest_change.py --- dulwich-0.12.0/examples/latest_change.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/examples/latest_change.py 2017-07-29 00:14:28.000000000 +0000 @@ -11,7 +11,9 @@ r = Repo(".") -w = r.get_walker(paths=[sys.argv[1]], max_entries=1) +path = sys.argv[1].encode('utf-8') + +w = r.get_walker(paths=[path], max_entries=1) try: c = next(iter(w)).commit except StopIteration: diff -Nru dulwich-0.12.0/Makefile dulwich-0.18.5/Makefile --- dulwich-0.12.0/Makefile 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/Makefile 2017-10-07 15:45:17.000000000 +0000 @@ -1,14 +1,12 @@ PYTHON = python PYFLAKES = pyflakes PEP8 = pep8 +FLAKE8 ?= flake8 SETUP = $(PYTHON) setup.py PYDOCTOR ?= pydoctor -ifeq ($(shell $(PYTHON) -c "import sys; print(sys.version_info >= (2, 7))"),True) TESTRUNNER ?= unittest -else -TESTRUNNER ?= unittest2.__main__ -endif RUNTEST = PYTHONHASHSEED=random PYTHONPATH=.:$(PYTHONPATH) $(PYTHON) -m $(TESTRUNNER) $(TEST_OPTIONS) +COVERAGE = python3-coverage DESTDIR=/ @@ -60,5 +58,14 @@ pep8: $(PEP8) dulwich +style: + $(FLAKE8) --exclude=build,.git,build-pypy,.tox + before-push: check git diff origin/master | $(PEP8) --diff + +coverage: + $(COVERAGE) run --source=dulwich -m unittest dulwich.tests.test_suite dulwich.contrib.test_suite + +coverage-html: coverage + $(COVERAGE) html diff -Nru dulwich-0.12.0/MANIFEST.in dulwich-0.18.5/MANIFEST.in --- dulwich-0.12.0/MANIFEST.in 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/MANIFEST.in 2017-10-07 15:45:17.000000000 +0000 @@ -1,10 +1,10 @@ include NEWS include AUTHORS include README.md +include README.swift.md include Makefile include COPYING -include HACKING -include CONTRIBUTING +include CONTRIBUTING.md include TODO include setup.cfg include dulwich/stdint.h @@ -15,3 +15,4 @@ include dulwich.cfg include appveyor.yml include .testr.conf +include .travis.yml diff -Nru dulwich-0.12.0/NEWS dulwich-0.18.5/NEWS --- dulwich-0.12.0/NEWS 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/NEWS 2017-10-29 16:28:21.000000000 +0000 @@ -1,3 +1,390 @@ +0.18.5 2017-10-29 + + BUG FIXES + + * Fix cwd for hooks. (Fabian Grünbichler) + + * Fix setting of origin in config when non-standard origin is passed into + ``Repo.clone``. (Kenneth Lareau, #565) + + * Prevent setting SSH arguments from SSH URLs when using SSH through a + subprocess. Note that Dulwich doesn't support cloning submodules. + (CVE 2017-1000117) (Jelmer Vernooij) + + IMPROVEMENTS + + * Silently ignored directories in ``Repo.stage``. + (Jelmer Vernooij, #564) + + API CHANGES + + * GitFile now raises ``FileLocked`` when encountering a lock + rather than OSError(EEXIST). (Jelmer Vernooij) + +0.18.4 2017-10-01 + + BUG FIXES + + * Make default User-Agent start with "git/" because GitHub won't response to + HTTP smart server requests otherwise (and reply with a 404). + (Jelmer vernooij, #562) + +0.18.3 2017-09-03 + + BUG FIXES + + * Read config during porcelain operations that involve remotes. + (Jelmer Vernooij, #545) + + * Fix headers of empty chunks in unified diffs. (Taras Postument, #543) + + * Properly follow redirects over HTTP. (Jelmer Vernooij, #117) + + IMPROVEMENTS + + * Add ``dulwich.porcelain.update_head``. (Jelmer Vernooij, #439) + + * ``GitClient.fetch_pack`` now returns symrefs. + (Jelmer Vernooij, #485) + + * The server now supports providing symrefs. + (Jelmer Vernooij, #485) + + * Add ``dulwich.object_store.commit_tree_changes`` to incrementally + commit changes to a tree structure. (Jelmer Vernooij) + + * Add basic ``PackBasedObjectStore.repack`` method. + (Jelmer Vernooij, Earl Chew, #296, #549, #552) + +0.18.2 2017-08-01 + + TEST FIXES + + * Use constant timestamp so tests pass in all timezones, not just BST. + (Jelmer Vernooij) + +0.18.1 2017-07-31 + + BUG FIXES + + * Fix syntax error in dulwich.contrib.test_swift_smoke. + (Jelmer Vernooij) + +0.18.0 2017-07-31 + + BUG FIXES + + * Fix remaining tests on Windows. (Jelmer Vernooij, #493) + + * Fix build of C extensions with Python 3 on Windows. + (Jelmer Vernooij) + + * Pass 'mkdir' argument onto Repo.init_bare in Repo.clone. + (Jelmer Vernooij, #504) + + * In ``dulwich.porcelain.add``, if no files are specified, + add from current working directory rather than repository root. + (Jelmer Vernooij, #521) + + * Properly deal with submodules in 'porcelain.status'. + (Jelmer Vernooij, #517) + + * ``dulwich.porcelain.remove`` now actually removes files from + disk, not just from the index. (Jelmer Vernooij, #488) + + * Fix handling of "reset" command with markers and without + "from". (Antoine Pietri) + + * Fix handling of "merge" command with markers. (Antoine Pietri) + + * Support treeish argument to porcelain.reset(), rather than + requiring a ref/commit id. (Jelmer Vernooij) + + * Handle race condition when mtime doesn't change between writes/reads. + (Jelmer Vernooij, #541) + + * Fix ``dulwich.porcelain.show`` on commits with Python 3. + (Jelmer Vernooij, #532) + + IMPROVEMENTS + + * Add basic support for reading ignore files in ``dulwich.ignore``. + ``dulwich.porcelain.add`` and ``dulwich.porcelain.status`` now honor + ignores. (Jelmer Vernooij, Segev Finer, #524, #526) + + * New ``dulwich.porcelain.check_ignore`` command. + (Jelmer Vernooij) + + * ``dulwich.porcelain.status`` now supports a ``ignored`` argument. + (Jelmer Vernooij) + + DOCUMENTATION + + * Clarified docstrings for Client.{send_pack,fetch_pack} implementations. + (Jelmer Vernooij, #523) + +0.17.3 2017-03-20 + + PLATFORM SUPPORT + + * List Python 3.3 as supported. (Jelmer Vernooij, #513) + + BUG FIXES + + * Fix compatibility with pypy 3. (Jelmer Vernooij) + +0.17.2 2017-03-19 + + BUG FIXES + + * Add workaround for + https://bitbucket.org/pypy/pypy/issues/2499/cpyext-pystring_asstring-doesnt-work, + fixing Dulwich when used with C extensions on pypy < 5.6. (Victor Stinner) + + * Properly quote config values with a '#' character in them. + (Jelmer Vernooij, #511) + +0.17.1 2017-03-01 + + IMPROVEMENTS + + * Add basic 'dulwich pull' command. (Jelmer Vernooij) + + BUG FIXES + + * Cope with existing submodules during pull. + (Jelmer Vernooij, #505) + +0.17.0 2017-03-01 + + TEST FIXES + + * Skip test that requires sync to synchronize filesystems if os.sync is + not available. (Koen Martens) + + IMPROVEMENTS + + * Implement MemoryRepo.{set_description,get_description}. + (Jelmer Vernooij) + + * Raise exception in Repo.stage() when absolute paths are + passed in. Allow passing in relative paths to + porcelain.add().(Jelmer Vernooij) + + BUG FIXES + + * Handle multi-line quoted values in config files. + (Jelmer Vernooij, #495) + + * Allow porcelain.clone of repository without HEAD. + (Jelmer Vernooij, #501) + + * Support passing tag ids to Walker()'s include argument. + (Jelmer Vernooij) + + * Don't strip trailing newlines from extra headers. + (Nicolas Dandrimont) + + * Set bufsize=0 for subprocess interaction with SSH client. + Fixes hangs on Python 3. (René Stern, #434) + + * Don't drop first slash for SSH paths, except for those + starting with "~". (Jelmer Vernooij, René Stern, #463) + + * Properly log off after retrieving just refs. + (Jelmer Vernooij) + +0.16.3 2016-01-14 + + TEST FIXES + + * Remove racy check that relies on clock time changing between writes. + (Jelmer Vernooij) + + IMPROVEMENTS + + * Add porcelain.remote_add. (Jelmer Vernooij) + +0.16.2 2016-01-14 + + IMPROVEMENTS + + * Fixed failing test-cases on windows. + (Koen Martens) + + API CHANGES + + * Repo is now a context manager, so that it can be easily + closed using a ``with`` statement. (Søren Løvborg) + + TEST FIXES + + * Only run worktree list compat tests against git 2.7.0, + when 'git worktree list' was introduced. (Jelmer Vernooij) + + BUG FIXES + + * Ignore filemode when building index when core.filemode + is false. + (Koen Martens) + + * Initialize core.filemode configuration setting by + probing the filesystem for trustable permissions. + (Koen Martens) + + * Fix ``porcelain.reset`` to respect the comittish argument. + (Koen Martens) + + * Fix dulwich.porcelain.ls_remote() on Python 3. + (#471, Jelmer Vernooij) + + * Allow both unicode and byte strings for host paths + in dulwich.client. (#435, Jelmer Vernooij) + + * Add remote from porcelain.clone. (#466, Jelmer Vernooij) + + * Fix unquoting of credentials before passing to urllib2. + (#475, Volodymyr Holovko) + + * Cope with submodules in `build_index_from_tree`. + (#477, Jelmer Vernooij) + + * Handle deleted files in `get_unstaged_changes`. + (#483, Doug Hellmann) + + * Don't overwrite files when they haven't changed in + `build_file_from_blob`. + (#479, Benoît HERVIER) + + * Check for existence of index file before opening pack. + Fixes a race when new packs are being added. + (#482, wme) + +0.16.1 2016-12-25 + + BUG FIXES + + * Fix python3 compatibility for dulwich.contrib.release_robot. + (Jelmer Vernooij) + +0.16.0 2016-12-24 + + IMPROVEMENTS + + * Add support for worktrees. See `git-worktree(1)` and + `gitrepository-layout(5)`. (Laurent Rineau) + + * Add support for `commondir` file in Git control + directories. (Laurent Rineau) + + * Add support for passwords in HTTP URLs. + (Jon Bain, Mika Mäenpää) + + * Add `release_robot` script to contrib, + allowing easy finding of current version based on Git tags. + (Mark Mikofski) + + * Add ``Blob.splitlines`` method. + (Jelmer Vernooij) + + BUG FIXES + + * Fix handling of ``Commit.tree`` being set to an actual + tree object rather than a tree id. (Jelmer Vernooij) + + * Return remote refs from LocalGitClient.fetch_pack(), + consistent with the documentation for that method. + (#461, Jelmer Vernooij) + + * Fix handling of unknown URL schemes in get_transport_and_path. + (#465, Jelmer Vernooij) + +0.15.0 2016-10-09 + + BUG FIXES + + * Allow missing trailing LF when reading service name from + HTTP servers. (Jelmer Vernooij, Andrew Shadura, #442) + + * Fix dulwich.porcelain.pull() on Python3. (Jelmer Vernooij, #451) + + * Properly pull in tags during dulwich.porcelain.clone. + (Jelmer Vernooij, #408) + + CHANGES + + * Changed license from "GNU General Public License, version 2.0 or later" + to "Apache License, version 2.0 or later or GNU General Public License, + version 2.0 or later". (#153) + + IMPROVEMENTS + + * Add ``dulwich.porcelain.ls_tree`` implementation. (Jelmer Vernooij) + +0.14.1 2016-07-05 + + BUG FIXES + + * Fix regression removing untouched refs when pushing over SSH. + (Jelmer Vernooij #441) + + * Skip Python3 tests for SWIFT contrib module, as it has not yet + been ported. + +0.14.0 2016-07-03 + + BUG FIXES + + * Fix ShaFile.id after modification of a copied ShaFile. + (Félix Mattrat, Jelmer Vernooij) + + * Support removing refs from porcelain.push. + (Jelmer Vernooij, #437) + + * Stop magic protocol ref `capabilities^{}` from leaking out + to clients. (Jelmer Vernooij, #254) + + IMPROVEMENTS + + * Add `dulwich.config.parse_submodules` function. + + * Add `RefsContainer.follow` method. (#438) + +0.13.0 2016-04-24 + + IMPROVEMENTS + + * Support `ssh://` URLs in get_transport_and_path_from_url(). + (Jelmer Vernooij, #402) + + * Support missing empty line after headers in Git commits and tags. + (Nicolas Dandrimont, #413) + + * Fix `dulwich.porcelain.status` when used in empty trees. + (Jelmer Vernooij, #415) + + * Return copies of objects in MemoryObjectStore rather than + references, making the behaviour more consistent with that of + DiskObjectStore. (Félix Mattrat, Jelmer Vernooij) + + * Fix ``dulwich.web`` on Python3. (#295, Jonas Haag) + + CHANGES + + * Drop support for Python 2.6. + + * Fix python3 client web support. (Jelmer Vernooij) + + BUG FIXES + + * Fix hang on Gzip decompression. (Jonas Haag) + + * Don't rely on working tell() and seek() methods + on wsgi.input. (Jonas Haag) + + * Support fastexport/fastimport functionality on python3 with newer + versions of fastimport (>= 0.9.5). (Jelmer Vernooij, Félix Mattrat) + 0.12.0 2015-12-13 IMPROVEMENTS @@ -6,7 +393,7 @@ Based on code from Jonas Haag in klaus. * Add a `dulwich.reflog` module for reading and writing reflogs. - (Jelmer Vernooij) + (Jelmer Vernooij) * Fix handling of ambiguous refs in `parse_ref` to make it match the behaviour described in https://git-scm.com/docs/gitrevisions. @@ -32,15 +419,15 @@ IMPROVEMENTS - * Add support for agent= capability. (Jelmer Vernooij, #298) + * Add support for agent= capability. (Jelmer Vernooij, #298) - * Add support for quiet capability. (Jelmer Vernooij) + * Add support for quiet capability. (Jelmer Vernooij) CHANGES * The ParamikoSSHVendor class has been moved to * dulwich.contrib.paramiko_vendor, as it's currently untested. - (Jelmer Vernooij, #364) + (Jelmer Vernooij, #364) 0.11.1 2015-09-13 @@ -51,21 +438,21 @@ IMPROVEMENTS * Extended Python3 support to most of the codebase. - (Gary van der Merwe, Jelmer Vernooij) + (Gary van der Merwe, Jelmer Vernooij) * The `Repo` object has a new `close` method that can be called to close any open resources. (Gary van der Merwe) * Support 'git.bat' in SubprocessGitClient on Windows. (Stefan Zimmermann) * Advertise 'ofs-delta' capability in receive-pack server side - capabilities. (Jelmer Vernooij) + capabilities. (Jelmer Vernooij) * Switched `default_local_git_client_cls` to `LocalGitClient`. (Gary van der Merwe) * Add `porcelain.ls_remote` and `GitClient.get_refs`. (Michael Edgar) * Add `Repo.discover` method. (B. M. Corser) - * Add `dulwich.objectspec.parse_refspec`. (Jelmer Vernooij) + * Add `dulwich.objectspec.parse_refspec`. (Jelmer Vernooij) * Add `porcelain.pack_objects` and `porcelain.repack`. - (Jelmer Vernooij) + (Jelmer Vernooij) BUG FIXES @@ -75,12 +462,12 @@ * Avoid recursion limit issues resolving deltas. (William Grant, #81) * Allow arguments in local client binary path overrides. - (Jelmer Vernooij) + (Jelmer Vernooij) * Fix handling of commands with arguments in paramiko SSH - client. (Andreas Klöckner, Jelmer Vernooij, #363) + client. (Andreas Klöckner, Jelmer Vernooij, #363) - * Fix parsing of quoted strings in configs. (Jelmer Vernooij, #305) + * Fix parsing of quoted strings in configs. (Jelmer Vernooij, #305) 0.10.1 2015-03-25 @@ -88,7 +475,7 @@ * Return `ApplyDeltaError` when encountering delta errors in both C extensions and native delta application code. - (Jelmer Vernooij, #259) + (Jelmer Vernooij, #259) 0.10.0 2015-03-22 @@ -98,13 +485,13 @@ refuse to create entries that start with .git/. * Fix running of testsuite when installed. - (Jelmer Vernooij, #223) + (Jelmer Vernooij, #223) * Use a block cache in _find_content_rename_candidates(), improving performance. (Mike Williams) * Add support for ``core.protectNTFS`` setting. - (Jelmer Vernooij) + (Jelmer Vernooij) * Fix TypeError when fetching empty updates. (Hwee Miin Koh) @@ -120,11 +507,11 @@ IMPROVEMENTS - * New public method `Repo.reset_index`. (Jelmer Vernooij) + * New public method `Repo.reset_index`. (Jelmer Vernooij) * Prevent duplicate parsing of loose files in objects directory when reading. Thanks to David Keijser for the - report. (Jelmer Vernooij, #231) + report. (Jelmer Vernooij, #231) 0.9.9 2015-03-20 @@ -135,7 +522,7 @@ Thanks to Ivan Fratric of the Google Security Team for reporting this issue. - (Jelmer Vernooij) + (Jelmer Vernooij) 0.9.8 2014-11-30 @@ -150,38 +537,38 @@ (Michael Edgar) * Remove assignment to PyList_SIZE() that was causing segfaults on - pypy. (Jelmer Vernooij, #196) + pypy. (Jelmer Vernooij, #196) IMPROVEMENTS - * Add porcelain 'receive-pack' and 'upload-pack'. (Jelmer Vernooij) + * Add porcelain 'receive-pack' and 'upload-pack'. (Jelmer Vernooij) - * Handle SIGINT signals in bin/dulwich. (Jelmer Vernooij) + * Handle SIGINT signals in bin/dulwich. (Jelmer Vernooij) - * Add 'status' support to bin/dulwich. (Jelmer Vernooij) + * Add 'status' support to bin/dulwich. (Jelmer Vernooij) * Add 'branch_create', 'branch_list', 'branch_delete' porcelain. - (Jelmer Vernooij) + (Jelmer Vernooij) - * Add 'fetch' porcelain. (Jelmer Vernooij) + * Add 'fetch' porcelain. (Jelmer Vernooij) - * Add 'tag_delete' porcelain. (Jelmer Vernooij) + * Add 'tag_delete' porcelain. (Jelmer Vernooij) * Add support for serializing/deserializing 'gpgsig' attributes in Commit. - (Jelmer Vernooij) + (Jelmer Vernooij) CHANGES * dul-web is now available as 'dulwich web-daemon'. - (Jelmer Vernooij) + (Jelmer Vernooij) * dulwich.porcelain.tag has been renamed to tag_create. dulwich.porcelain.list_tags has been renamed to tag_list. - (Jelmer Vernooij) + (Jelmer Vernooij) API CHANGES - * Restore support for Python 2.6. (Jelmer Vernooij, Gary van der Merwe) + * Restore support for Python 2.6. (Jelmer Vernooij, Gary van der Merwe) 0.9.7 2014-06-08 @@ -211,7 +598,7 @@ * Add porcelain 'status'. (Ryan Faulkner) - * Add porcelain 'daemon'. (Jelmer Vernooij) + * Add porcelain 'daemon'. (Jelmer Vernooij) * Add `dulwich.greenthreads` module which provides support for concurrency of some object store operations. @@ -232,128 +619,128 @@ be easily closed using a `with` statement. (Gary van der Merwe) * Remove deprecated `num_objects` argument to `write_pack` methods. - (Jelmer Vernooij) + (Jelmer Vernooij) OTHER CHANGES * The 'dul-daemon' script has been removed. The same functionality - is now available as 'dulwich daemon'. (Jelmer Vernooij) + is now available as 'dulwich daemon'. (Jelmer Vernooij) 0.9.6 2014-04-23 IMPROVEMENTS - * Add support for recursive add in 'git add'. - (Ryan Faulkner, Jelmer Vernooij) + * Add support for recursive add in 'git add'. + (Ryan Faulkner, Jelmer Vernooij) - * Add porcelain 'list_tags'. (Ryan Faulkner) + * Add porcelain 'list_tags'. (Ryan Faulkner) - * Add porcelain 'push'. (Ryan Faulkner) + * Add porcelain 'push'. (Ryan Faulkner) - * Add porcelain 'pull'. (Ryan Faulkner) + * Add porcelain 'pull'. (Ryan Faulkner) - * Support 'http.proxy' in HttpGitClient. - (Jelmer Vernooij, #1096030) + * Support 'http.proxy' in HttpGitClient. + (Jelmer Vernooij, #1096030) - * Support 'http.useragent' in HttpGitClient. - (Jelmer Vernooij) + * Support 'http.useragent' in HttpGitClient. + (Jelmer Vernooij) - * In server, wait for clients to send empty list of - wants when talking to empty repository. - (Damien Tournoud) + * In server, wait for clients to send empty list of + wants when talking to empty repository. + (Damien Tournoud) - * Various changes to improve compatibility with - Python 3. (Gary van der Merwe) + * Various changes to improve compatibility with + Python 3. (Gary van der Merwe) BUG FIXES - * Support unseekable 'wsgi.input' streams. - (Jonas Haag) + * Support unseekable 'wsgi.input' streams. + (Jonas Haag) - * Raise TypeError when passing unicode() object - to Repo.__getitem__. - (Jonas Haag) + * Raise TypeError when passing unicode() object + to Repo.__getitem__. + (Jonas Haag) - * Fix handling of `reset` command in dulwich.fastexport. - (Jelmer Vernooij, #1249029) + * Fix handling of `reset` command in dulwich.fastexport. + (Jelmer Vernooij, #1249029) - * In client, don't wait for server to close connection - first. Fixes hang when used against GitHub - server implementation. (Siddharth Agarwal) + * In client, don't wait for server to close connection + first. Fixes hang when used against GitHub + server implementation. (Siddharth Agarwal) - * DeltaChainIterator: fix a corner case where an object is inflated as an - object already in the repository. - (Damien Tournoud, #135) + * DeltaChainIterator: fix a corner case where an object is inflated as an + object already in the repository. + (Damien Tournoud, #135) - * Stop leaking file handles during pack reload. (Damien Tournoud) + * Stop leaking file handles during pack reload. (Damien Tournoud) - * Avoid reopening packs during pack cache reload. (Jelmer Vernooij) + * Avoid reopening packs during pack cache reload. (Jelmer Vernooij) API CHANGES - * Drop support for Python 2.6. (Jelmer Vernooij) + * Drop support for Python 2.6. (Jelmer Vernooij) 0.9.5 2014-02-23 IMPROVEMENTS - * Add porcelain 'tag'. (Ryan Faulkner) + * Add porcelain 'tag'. (Ryan Faulkner) - * New module `dulwich.objectspec` for parsing strings referencing - objects and commit ranges. (Jelmer Vernooij) + * New module `dulwich.objectspec` for parsing strings referencing + objects and commit ranges. (Jelmer Vernooij) - * Add shallow branch support. (milki) + * Add shallow branch support. (milki) - * Allow passing urllib2 `opener` into HttpGitClient. - (Dov Feldstern, #909037) + * Allow passing urllib2 `opener` into HttpGitClient. + (Dov Feldstern, #909037) CHANGES - * Drop support for Python 2.4 and 2.5. (Jelmer Vernooij) + * Drop support for Python 2.4 and 2.5. (Jelmer Vernooij) API CHANGES - * Remove long deprecated ``Repo.commit``, ``Repo.get_blob``, - ``Repo.tree`` and ``Repo.tag``. (Jelmer Vernooij) + * Remove long deprecated ``Repo.commit``, ``Repo.get_blob``, + ``Repo.tree`` and ``Repo.tag``. (Jelmer Vernooij) - * Remove long deprecated ``Repo.revision_history`` and ``Repo.ref``. - (Jelmer Vernooij) + * Remove long deprecated ``Repo.revision_history`` and ``Repo.ref``. + (Jelmer Vernooij) - * Remove long deprecated ``Tree.entries``. (Jelmer Vernooij) + * Remove long deprecated ``Tree.entries``. (Jelmer Vernooij) BUG FIXES - * Raise KeyError rather than TypeError when passing in - unicode object of length 20 or 40 to Repo.__getitem__. - (Jelmer Vernooij) + * Raise KeyError rather than TypeError when passing in + unicode object of length 20 or 40 to Repo.__getitem__. + (Jelmer Vernooij) - * Use 'rm' rather than 'unlink' in tests, since the latter - does not exist on OpenBSD and other platforms. - (Dmitrij D. Czarkoff) + * Use 'rm' rather than 'unlink' in tests, since the latter + does not exist on OpenBSD and other platforms. + (Dmitrij D. Czarkoff) 0.9.4 2013-11-30 IMPROVEMENTS - * Add ssh_kwargs attribute to ParamikoSSHVendor. (milki) + * Add ssh_kwargs attribute to ParamikoSSHVendor. (milki) - * Add Repo.set_description(). (Víðir Valberg Guðmundsson) + * Add Repo.set_description(). (Víðir Valberg Guðmundsson) - * Add a basic `dulwich.porcelain` module. (Jelmer Vernooij, Marcin Kuzminski) + * Add a basic `dulwich.porcelain` module. (Jelmer Vernooij, Marcin Kuzminski) - * Various performance improvements for object access. - (Jelmer Vernooij) + * Various performance improvements for object access. + (Jelmer Vernooij) - * New function `get_transport_and_path_from_url`, - similar to `get_transport_and_path` but only - supports URLs. - (Jelmer Vernooij) + * New function `get_transport_and_path_from_url`, + similar to `get_transport_and_path` but only + supports URLs. + (Jelmer Vernooij) - * Add support for file:// URLs in `get_transport_and_path_from_url`. - (Jelmer Vernooij) + * Add support for file:// URLs in `get_transport_and_path_from_url`. + (Jelmer Vernooij) - * Add LocalGitClient implementation. - (Jelmer Vernooij) + * Add LocalGitClient implementation. + (Jelmer Vernooij) BUG FIXES @@ -363,15 +750,15 @@ CHANGES * Ref handling has been moved to dulwich.refs. - (Jelmer Vernooij) + (Jelmer Vernooij) API CHANGES * Remove long deprecated RefsContainer.set_ref(). - (Jelmer Vernooij) + (Jelmer Vernooij) * Repo.ref() is now deprecated in favour of Repo.refs[]. - (Jelmer Vernooij) + (Jelmer Vernooij) FEATURES @@ -381,7 +768,7 @@ BUG FIXES - * Fix path for stdint.h in MANIFEST.in. (Jelmer Vernooij) + * Fix path for stdint.h in MANIFEST.in. (Jelmer Vernooij) 0.9.2 2013-09-26 @@ -393,9 +780,9 @@ BUG FIXES - * Support lookups of 40-character refs in BaseRepo.__getitem__. (Chow Loong Jin, Jelmer Vernooij) + * Support lookups of 40-character refs in BaseRepo.__getitem__. (Chow Loong Jin, Jelmer Vernooij) - * Fix fetching packs with side-band-64k capability disabled. (David Keijser, Jelmer Vernooij) + * Fix fetching packs with side-band-64k capability disabled. (David Keijser, Jelmer Vernooij) * Several fixes in send-pack protocol behaviour - handling of empty pack files and deletes. (milki, #1063087) @@ -403,16 +790,16 @@ * Fix capability negotiation when fetching packs over HTTP. (#1072461, William Grant) - * Enforce determine_wants returning an empty list rather than None. (Fabien Boucher, Jelmer Vernooij) + * Enforce determine_wants returning an empty list rather than None. (Fabien Boucher, Jelmer Vernooij) - * In the server, support pushes just removing refs. (Fabien Boucher, Jelmer Vernooij) + * In the server, support pushes just removing refs. (Fabien Boucher, Jelmer Vernooij) IMPROVEMENTS * Support passing a single revision to BaseRepo.get_walker() rather than a list of revisions. (Alberto Ruiz) - * Add `Repo.get_description` method. (Jelmer Vernooij) + * Add `Repo.get_description` method. (Jelmer Vernooij) * Support thin packs in Pack.iterobjects() and Pack.get_raw(). (William Grant) @@ -423,21 +810,21 @@ * Add paramiko-based SSH vendor. (Aaron O'Mullan) * Support running 'dulwich.server' and 'dulwich.web' using 'python -m'. - (Jelmer Vernooij) + (Jelmer Vernooij) - * Add ObjectStore.close(). (Jelmer Vernooij) + * Add ObjectStore.close(). (Jelmer Vernooij) * Raise appropriate NotImplementedError when encountering dumb HTTP servers. - (Jelmer Vernooij) + (Jelmer Vernooij) API CHANGES * SSHVendor.connect_ssh has been renamed to SSHVendor.run_command. - (Jelmer Vernooij) + (Jelmer Vernooij) * ObjectStore.add_pack() now returns a 3-tuple. The last element will be an abort() method that can be used to cancel the pack operation. - (Jelmer Vernooij) + (Jelmer Vernooij) 0.9.0 2013-05-31 @@ -446,7 +833,7 @@ * Push efficiency - report missing objects only. (#562676, Artem Tikhomirov) * Use indentation consistent with C Git in config files. - (#1031356, Curt Moore, Jelmer Vernooij) + (#1031356, Curt Moore, Jelmer Vernooij) * Recognize and skip binary files in diff function. (Takeshi Kanemoto) @@ -475,7 +862,7 @@ * Add optional honor_filemode flag to build_index_from_tree. (Mark Mikofski) - * Support core/filemode setting when building trees. (Jelmer Vernooij) + * Support core/filemode setting when building trees. (Jelmer Vernooij) * Add chapter on tags in tutorial. (Ryan Faulkner) @@ -499,7 +886,7 @@ API CHANGES * dulwich.__init__ no longer imports client, protocol, repo and - server modules. (Jelmer Vernooij) + server modules. (Jelmer Vernooij) FEATURES @@ -515,11 +902,11 @@ * Make index entry tests a little bit less strict, to cope with slightly different behaviour on various platforms. - (Jelmer Vernooij) + (Jelmer Vernooij) * ``setup.py test`` (available when setuptools is installed) now runs all tests, not just the basic unit tests. - (Jelmer Vernooij) + (Jelmer Vernooij) BUG FIXES @@ -529,28 +916,28 @@ * Handle None elements in lists of TreeChange objects. (Alex Holmes) * Support cloning repositories without HEAD set. - (D-Key, Jelmer Vernooij, issue #69) + (D-Key, Jelmer Vernooij, issue #69) - * Support ``MemoryRepo.get_config``. (Jelmer Vernooij) + * Support ``MemoryRepo.get_config``. (Jelmer Vernooij) * In ``get_transport_and_path``, pass extra keyword arguments on to - HttpGitClient. (Jelmer Vernooij) + HttpGitClient. (Jelmer Vernooij) 0.8.5 2012-03-29 BUG FIXES - * Avoid use of 'with' in dulwich.index. (Jelmer Vernooij) + * Avoid use of 'with' in dulwich.index. (Jelmer Vernooij) * Be a little bit strict about OS behaviour in index tests. - Should fix the tests on Debian GNU/kFreeBSD. (Jelmer Vernooij) + Should fix the tests on Debian GNU/kFreeBSD. (Jelmer Vernooij) 0.8.4 2012-03-28 BUG FIXES * Options on the same line as sections in config files are now supported. - (Jelmer Vernooij, #920553) + (Jelmer Vernooij, #920553) * Only negotiate capabilities that are also supported by the server. (Rod Cloutier, Risto Kankkunen) @@ -565,7 +952,7 @@ * $HOME is now explicitly specified for tests that use it to read ``~/.gitconfig``, to prevent test isolation issues. - (Jelmer Vernooij, #920330) + (Jelmer Vernooij, #920330) FEATURES @@ -574,7 +961,7 @@ * The WSGI server now transparently handles when a git client submits data using Content-Encoding: gzip. - (David Blewett, Jelmer Vernooij) + (David Blewett, Jelmer Vernooij) * Add dulwich.index.build_index_from_tree(). (milki) @@ -584,23 +971,23 @@ * The config parser now supports the git-config file format as described in git-config(1) and can write git config files. - (Jelmer Vernooij, #531092, #768687) + (Jelmer Vernooij, #531092, #768687) * ``Repo.do_commit`` will now use the user identity from .git/config or ~/.gitconfig if none was explicitly specified. - (Jelmer Vernooij) + (Jelmer Vernooij) BUG FIXES * Allow ``determine_wants`` methods to include the zero sha in their - return value. (Jelmer Vernooij) + return value. (Jelmer Vernooij) 0.8.2 2011-12-18 BUG FIXES * Cope with different zlib buffer sizes in sha1 file parser. - (Jelmer Vernooij) + (Jelmer Vernooij) * Fix get_transport_and_path for HTTP/HTTPS URLs. (Bruno Renié) @@ -610,18 +997,18 @@ * Fix use --bare argument to 'dulwich init'. (Chris Eberle) * Properly abort connections when the determine_wants function - raises an exception. (Jelmer Vernooij, #856769) + raises an exception. (Jelmer Vernooij, #856769) * Tweak xcodebuild hack to deal with more error output. - (Jelmer Vernooij, #903840) + (Jelmer Vernooij, #903840) FEATURES * Add support for retrieving tarballs from remote servers. - (Jelmer Vernooij, #379087) + (Jelmer Vernooij, #379087) * New method ``update_server_info`` which generates data - for dumb server access. (Jelmer Vernooij, #731235) + for dumb server access. (Jelmer Vernooij, #731235) 0.8.1 2011-10-31 @@ -629,44 +1016,44 @@ * Repo.do_commit has a new argument 'ref'. - * Repo.do_commit has a new argument 'merge_heads'. (Jelmer Vernooij) + * Repo.do_commit has a new argument 'merge_heads'. (Jelmer Vernooij) - * New ``Repo.get_walker`` method. (Jelmer Vernooij) + * New ``Repo.get_walker`` method. (Jelmer Vernooij) - * New ``Repo.clone`` method. (Jelmer Vernooij, #725369) + * New ``Repo.clone`` method. (Jelmer Vernooij, #725369) * ``GitClient.send_pack`` now supports the 'side-band-64k' capability. - (Jelmer Vernooij) + (Jelmer Vernooij) * ``HttpGitClient`` which supports the smart server protocol over - HTTP. "dumb" access is not yet supported. (Jelmer Vernooij, #373688) + HTTP. "dumb" access is not yet supported. (Jelmer Vernooij, #373688) - * Add basic support for alternates. (Jelmer Vernooij, #810429) + * Add basic support for alternates. (Jelmer Vernooij, #810429) CHANGES * unittest2 or python >= 2.7 is now required for the testsuite. - testtools is no longer supported. (Jelmer Vernooij, #830713) + testtools is no longer supported. (Jelmer Vernooij, #830713) BUG FIXES * Fix compilation with older versions of MSVC. (Martin gz) - * Special case 'refs/stash' as a valid ref. (Jelmer Vernooij, #695577) + * Special case 'refs/stash' as a valid ref. (Jelmer Vernooij, #695577) * Smart protocol clients can now change refs even if they are - not uploading new data. (Jelmer Vernooij, #855993) + not uploading new data. (Jelmer Vernooij, #855993) - * Don't compile C extensions when running in pypy. - (Ronny Pfannschmidt, #881546) + * Don't compile C extensions when running in pypy. + (Ronny Pfannschmidt, #881546) - * Use different name for strnlen replacement function to avoid clashing - with system strnlen. (Jelmer Vernooij, #880362) + * Use different name for strnlen replacement function to avoid clashing + with system strnlen. (Jelmer Vernooij, #880362) API CHANGES * ``Repo.revision_history`` is now deprecated in favor of ``Repo.get_walker``. - (Jelmer Vernooij) + (Jelmer Vernooij) 0.8.0 2011-08-07 @@ -687,9 +1074,9 @@ BUG FIXES * Avoid storing all objects in memory when writing pack. - (Jelmer Vernooij, #813268) + (Jelmer Vernooij, #813268) - * Support IPv6 for git:// connections. (Jelmer Vernooij, #801543) + * Support IPv6 for git:// connections. (Jelmer Vernooij, #801543) * Improve performance of Repo.revision_history(). (Timo Schmid, #535118) @@ -704,14 +1091,14 @@ * Fix a bug in reading the pack checksum when there are fewer than 20 bytes left in the buffer. (Dave Borowitz) - * Support ~ in git:// URL paths. (Jelmer Vernooij, #813555) + * Support ~ in git:// URL paths. (Jelmer Vernooij, #813555) * Make ShaFile.__eq__ work when other is not a ShaFile. (Dave Borowitz) * ObjectStore.get_graph_walker() now no longer yields the same revision more than once. This has a significant improvement for performance when wide revision graphs are involved. - (Jelmer Vernooij, #818168) + (Jelmer Vernooij, #818168) * Teach ReceivePackHandler how to read empty packs. (Dave Borowitz) @@ -730,10 +1117,10 @@ * write_pack no longer takes the num_objects argument and requires an object to be passed in that is iterable (rather than an iterator) and that - provides __len__. (Jelmer Vernooij) + provides __len__. (Jelmer Vernooij) * write_pack_data has been renamed to write_pack_objects and no longer takes a - num_objects argument. (Jelmer Vernooij) + num_objects argument. (Jelmer Vernooij) * take_msb_bytes, read_zlib_chunks, unpack_objects, and PackStreamReader.read_objects now take an additional argument indicating a @@ -778,7 +1165,7 @@ TEST CHANGES * If setuptools is installed, "python setup.py test" will now run the testsuite. - (Jelmer Vernooij) + (Jelmer Vernooij) * Add a new build_pack test utility for building packs from a simple spec. (Dave Borowitz) @@ -798,10 +1185,10 @@ (Max Bowsher, #707438) * BaseObjectStore.determine_wants_all no longer breaks on zero SHAs. - (Jelmer Vernooij) + (Jelmer Vernooij) * write_tree_diff() now supports submodules. - (Jelmer Vernooij) + (Jelmer Vernooij) * Fix compilation for XCode 4 and older versions of distutils.sysconfig. (Daniele Sluijters) @@ -816,10 +1203,10 @@ * The order of the parameters to Tree.add(name, mode, sha) has changed, and is now consistent with the rest of Dulwich. Existing code will still - work but print a DeprecationWarning. (Jelmer Vernooij, #663550) + work but print a DeprecationWarning. (Jelmer Vernooij, #663550) * Tree.entries() is now deprecated in favour of Tree.items() and - Tree.iteritems(). (Jelmer Vernooij) + Tree.iteritems(). (Jelmer Vernooij) 0.7.0 2011-01-21 @@ -828,14 +1215,14 @@ * New `dulwich.diff_tree` module for simple content-based rename detection. (Dave Borowitz) - * Add Tree.items(). (Jelmer Vernooij) + * Add Tree.items(). (Jelmer Vernooij) * Add eof() and unread_pkt_line() methods to Protocol. (Dave Borowitz) - * Add write_tree_diff(). (Jelmer Vernooij) + * Add write_tree_diff(). (Jelmer Vernooij) * Add `serve_command` function for git server commands as executables. - (Jelmer Vernooij) + (Jelmer Vernooij) * dulwich.client.get_transport_and_path now supports rsync-style repository URLs. (Dave Borowitz, #568493) @@ -846,9 +1233,9 @@ (Dave Borowitz) * Support parsing git mbox patches without a version tail, as generated by - Mercurial. (Jelmer Vernooij) + Mercurial. (Jelmer Vernooij) - * Fix dul-receive-pack and dul-upload-pack. (Jelmer Vernooij) + * Fix dul-receive-pack and dul-upload-pack. (Jelmer Vernooij) * Zero-padded file modes in Tree objects no longer trigger an exception but the check code warns about them. (Augie Fackler, #581064) @@ -862,9 +1249,9 @@ DOCUMENTATION - * Run the tutorial inside the test suite. (Jelmer Vernooij) + * Run the tutorial inside the test suite. (Jelmer Vernooij) - * Reorganized and updated the tutorial. (Jelmer Vernooij, Dave Borowitz, #610550, + * Reorganized and updated the tutorial. (Jelmer Vernooij, Dave Borowitz, #610550, #610540) @@ -885,7 +1272,7 @@ FEATURES - * Use slots for core objects to save up on memory. (Jelmer Vernooij) + * Use slots for core objects to save up on memory. (Jelmer Vernooij) * Web server supports streaming progress/pack output. (Dave Borowitz) @@ -895,9 +1282,9 @@ (Dave Borowitz) * Initial work on support for fastimport using python-fastimport. - (Jelmer Vernooij) + (Jelmer Vernooij) - * New dulwich.pack.MemoryPackIndex class. (Jelmer Vernooij) + * New dulwich.pack.MemoryPackIndex class. (Jelmer Vernooij) * Delegate SHA peeling to the object store. (Dave Borowitz) @@ -910,9 +1297,9 @@ * Standardize quote delimiters in test_protocol. (Dave Borowitz) - * Fix use when testtools is installed. (Jelmer Vernooij) + * Fix use when testtools is installed. (Jelmer Vernooij) - * Add trivial test for write_pack_header. (Jelmer Vernooij) + * Add trivial test for write_pack_header. (Jelmer Vernooij) * Refactor some of dulwich.tests.compat.server_utils. (Dave Borowitz) @@ -938,11 +1325,11 @@ * Tweak server handler injection. (Dave Borowitz) * PackIndex1 and PackIndex2 now subclass FilePackIndex, which is - itself a subclass of PackIndex. (Jelmer Vernooij) + itself a subclass of PackIndex. (Jelmer Vernooij) DOCUMENTATION - * Add docstrings for various functions in dulwich.objects. (Jelmer Vernooij) + * Add docstrings for various functions in dulwich.objects. (Jelmer Vernooij) * Clean up docstrings in dulwich.protocol. (Dave Borowitz) @@ -961,7 +1348,7 @@ * Use correct path separators for named repo files. (Dave Borowitz) * python > 2.7 and testtools-based test runners will now also pick up skipped - tests correctly. (Jelmer Vernooij) + tests correctly. (Jelmer Vernooij) FEATURES @@ -973,9 +1360,9 @@ (Augie Fackler) * Allow overriding paths to executables in GitSSHClient. - (Ross Light, Jelmer Vernooij, #585204) + (Ross Light, Jelmer Vernooij, #585204) - * Add PackBasedObjectStore.pack_loose_objects(). (Jelmer Vernooij) + * Add PackBasedObjectStore.pack_loose_objects(). (Jelmer Vernooij) TESTS @@ -1000,7 +1387,7 @@ API CHANGES * dulwich.pack.write_pack_index_v{1,2} now take a file-like object - rather than a filename. (Jelmer Vernooij) + rather than a filename. (Jelmer Vernooij) * Make dul-daemon/dul-web trivial wrappers around server functionality. (Dave Borowitz) @@ -1025,12 +1412,12 @@ can not be disabled in the server. (Dave Borowitz) * Fix trailing newlines in generated patch files. - (Jelmer Vernooij) + (Jelmer Vernooij) - * Implement RefsContainer.__contains__. (Jelmer Vernooij) + * Implement RefsContainer.__contains__. (Jelmer Vernooij) * Cope with \r in ref files on Windows. ( - http://github.com/jelmer/dulwich/issues/#issue/13, Jelmer Vernooij) + http://github.com/jelmer/dulwich/issues/#issue/13, Jelmer Vernooij) * Fix GitFile breakage on Windows. (Anatoly Techtonik, #557585) @@ -1050,7 +1437,7 @@ empty. (Dave Borowitz) * Always update ShaFile.id when the contents of the object get changed. - (Jelmer Vernooij) + (Jelmer Vernooij) * Various Python2.4-compatibility fixes. (Dave Borowitz) @@ -1061,9 +1448,9 @@ * Add include-tag capability to server. (Dave Borowitz) * New dulwich.fastexport module that can generate fastexport - streams. (Jelmer Vernooij) + streams. (Jelmer Vernooij) - * Implemented BaseRepo.__contains__. (Jelmer Vernooij) + * Implemented BaseRepo.__contains__. (Jelmer Vernooij) * Add __setitem__ to DictRefsContainer. (Dave Borowitz) @@ -1078,7 +1465,7 @@ * Add various tests for the use of non-bare repositories. (Dave Borowitz) * Cope with diffstat not being available on all platforms. - (Tay Ray Chuan, Jelmer Vernooij) + (Tay Ray Chuan, Jelmer Vernooij) * Add make_object and make_commit convenience functions to test utils. (Dave Borowitz) @@ -1086,25 +1473,25 @@ API BREAKAGES * The 'committer' and 'message' arguments to Repo.do_commit() have - been swapped. 'committer' is now optional. (Jelmer Vernooij) + been swapped. 'committer' is now optional. (Jelmer Vernooij) * Repo.get_blob, Repo.commit, Repo.tag and Repo.tree are now deprecated. - (Jelmer Vernooij) + (Jelmer Vernooij) * RefsContainer.set_ref() was renamed to RefsContainer.set_symbolic_ref(), - for clarity. (Jelmer Vernooij) + for clarity. (Jelmer Vernooij) API CHANGES * The primary serialization APIs in dulwich.objects now work with chunks of strings rather than with full-text strings. - (Jelmer Vernooij) + (Jelmer Vernooij) 0.5.02010-03-03 BUG FIXES - * Support custom fields in commits (readonly). (Jelmer Vernooij) + * Support custom fields in commits (readonly). (Jelmer Vernooij) * Improved ref handling. (Dave Borowitz) @@ -1115,14 +1502,14 @@ cgit. (Dave Borowitz) * Cope with forward slashes correctly in the index on Windows. - (Jelmer Vernooij, #526793) + (Jelmer Vernooij, #526793) FEATURES * --pure option to setup.py to allow building/installing without the C - extensions. (Hal Wine, Anatoly Techtonik, Jelmer Vernooij, #434326) + extensions. (Hal Wine, Anatoly Techtonik, Jelmer Vernooij, #434326) - * Implement Repo.get_config(). (Jelmer Vernooij, Augie Fackler) + * Implement Repo.get_config(). (Jelmer Vernooij, Augie Fackler) * HTTP dumb and smart server. (Dave Borowitz) @@ -1133,14 +1520,14 @@ FEATURES - * Add ObjectStore.iter_tree_contents(). (Jelmer Vernooij) + * Add ObjectStore.iter_tree_contents(). (Jelmer Vernooij) - * Add Index.changes_from_tree(). (Jelmer Vernooij) + * Add Index.changes_from_tree(). (Jelmer Vernooij) - * Add ObjectStore.tree_changes(). (Jelmer Vernooij) + * Add ObjectStore.tree_changes(). (Jelmer Vernooij) * Add functionality for writing patches in dulwich.patch. - (Jelmer Vernooij) + (Jelmer Vernooij) 0.4.0 2009-10-07 @@ -1259,4 +1646,4 @@ 0.1.0 2009-01-24 - * Initial release. + * Initial release. diff -Nru dulwich-0.12.0/PKG-INFO dulwich-0.18.5/PKG-INFO --- dulwich-0.12.0/PKG-INFO 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/PKG-INFO 2017-10-29 16:36:37.000000000 +0000 @@ -1,11 +1,11 @@ Metadata-Version: 1.1 Name: dulwich -Version: 0.12.0 +Version: 0.18.5 Summary: Python Git Library Home-page: https://www.dulwich.io/ -Author: Jelmer Vernooij +Author: UNKNOWN Author-email: jelmer@jelmer.uk -License: GPLv2 or later +License: Apachev2 or later or GPLv2 Description: Python implementation of the Git file formats and protocols, without the need to have git installed. @@ -13,17 +13,20 @@ All functionality is available in pure Python. Optional C extensions can be built for improved performance. - The project is named after the part of London that Mr. and Mrs. Git live in - in the particular Monty Python sketch. + The project is named after the part of London that Mr. and Mrs. Git live + in in the particular Monty Python sketch. Keywords: git Platform: UNKNOWN Classifier: Development Status :: 4 - Beta -Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+) -Classifier: Programming Language :: Python :: 2.6 +Classifier: License :: OSI Approved :: Apache Software License Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Operating System :: POSIX +Classifier: Operating System :: Microsoft :: Windows Classifier: Topic :: Software Development :: Version Control diff -Nru dulwich-0.12.0/README.md dulwich-0.18.5/README.md --- dulwich-0.12.0/README.md 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/README.md 2017-10-07 15:45:17.000000000 +0000 @@ -1,12 +1,14 @@ [![Build Status](https://travis-ci.org/jelmer/dulwich.png?branch=master)](https://travis-ci.org/jelmer/dulwich) +[![Windows Build status](https://ci.appveyor.com/api/projects/status/mob7g4vnrfvvoweb?svg=true)](https://ci.appveyor.com/project/jelmer/dulwich/branch/master) This is the Dulwich project. It aims to provide an interface to git repos (both local and remote) that doesn't call out to git directly but instead uses pure Python. -Homepage: https://www.dulwich.io/ -Author: Jelmer Vernooij +**Main website**: [www.dulwich.io](https://www.dulwich.io/) + +**License**: Apache License, version 2 or GNU General Public License, version 2 or later. The project is named after the part of London that Mr. and Mrs. Git live in in the particular Monty Python sketch. @@ -22,29 +24,65 @@ $ python setup.py --pure install -or if you are installing from pip: +or if you are installing from pip:: $ pip install dulwich --global-option="--pure" -Further documentation ---------------------- +Getting started +--------------- + +Dulwich comes with both a lower-level API and higher-level plumbing ("porcelain"). + +For example, to use the lower level API to access the commit message of the +last commit: + + >>> from dulwich.repo import Repo + >>> r = Repo('.') + >>> r.head() + '57fbe010446356833a6ad1600059d80b1e731e15' + >>> c = r[r.head()] + >>> c + + >>> c.message + 'Add note about encoding.\n' + +And to print it using porcelain: + + >>> from dulwich import porcelain + >>> porcelain.log('.', max_entries=1) + -------------------------------------------------- + commit: 57fbe010446356833a6ad1600059d80b1e731e15 + Author: Jelmer Vernooij + Date: Sat Apr 29 2017 23:57:34 +0000 -The dulwich documentation can be found in doc/ and on the web: + Add note about encoding. -https://www.dulwich.io/docs/ +Further documentation +--------------------- -The API reference can be generated using pydoctor, by running "make pydoctor", or on the web: +The dulwich documentation can be found in docs/ and +[on the web](https://www.dulwich.io/docs/). -https://www.dulwich.io/apidocs +The API reference can be generated using pydoctor, by running "make pydoctor", +or [on the web](https://www.dulwich.io/apidocs). Help ---- -There is a #dulwich IRC channel on Freenode, and a dulwich mailing list at -https://launchpad.net/~dulwich-users. +There is a *#dulwich* IRC channel on the [Freenode](https://www.freenode.net/), and +[dulwich-announce](https://groups.google.com/forum/#!forum/dulwich-announce) +and [dulwich-discuss](https://groups.google.com/forum/#!forum/dulwich-discuss) +mailing lists. + +Contributing +------------ + +For a full list of contributors, see the git logs or [AUTHORS](AUTHORS). + +If you'd like to contribute to Dulwich, see the [CONTRIBUTING](CONTRIBUTING.md) +file and [list of open issues](https://github.com/jelmer/dulwich/issues). Supported versions of Python ---------------------------- -At the moment, Dulwich supports (and is tested on) CPython 2.6, 2.7, 3.4, 3.5 and Pypy. -The ``dulwich.web`` module is currently broken on Python 3 (issue #295). +At the moment, Dulwich supports (and is tested on) CPython 2.7, 3.3, 3.4, 3.5, 3.6 and Pypy. diff -Nru dulwich-0.12.0/README.swift.md dulwich-0.18.5/README.swift.md --- dulwich-0.12.0/README.swift.md 1970-01-01 00:00:00.000000000 +0000 +++ dulwich-0.18.5/README.swift.md 2017-10-07 15:45:17.000000000 +0000 @@ -0,0 +1,133 @@ +Openstack Swift as backend for Dulwich +====================================== +Fabien Boucher + +The module dulwich/contrib/swift.py implements dulwich.repo.BaseRepo +in order to being compatible with Openstack Swift. +We can then use Dulwich as server (Git server) and instead of using +a regular POSIX file system to store repository objects we use the +object storage Swift via its own API. + +c Git client <---> Dulwich server <---> Openstack Swift API + +This implementation is still a work in progress and we can say that +is a Beta version so you need to be prepared to find bugs. + +Configuration file +------------------ + +We need to provide some configuration values in order to let Dulwich +talk and authenticate against Swift. The following config file must +be used as template: + + [swift] + # Authentication URL (Keystone or Swift) + auth_url = http://127.0.0.1:5000/v2.0 + # Authentication version to use + auth_ver = 2 + # The tenant and username separated by a semicolon + username = admin;admin + # The user password + password = pass + # The Object storage region to use (auth v2) (Default RegionOne) + region_name = RegionOne + # The Object storage endpoint URL to use (auth v2) (Default internalURL) + endpoint_type = internalURL + # Concurrency to use for parallel tasks (Default 10) + concurrency = 10 + # Size of the HTTP pool (Default 10) + http_pool_length = 10 + # Timeout delay for HTTP connections (Default 20) + http_timeout = 20 + # Chunk size to read from pack (Bytes) (Default 12228) + chunk_length = 12228 + # Cache size (MBytes) (Default 20) + cache_length = 20 + + +Note that for now we use the same tenant to perform the requests +against Swift. Therefor there is only one Swift account used +for storing repositories. Each repository will be contained in +a Swift container. + +How to start unittest +--------------------- + +There is no need to have a Swift cluster running to run the unitests. +Just run the following command in the Dulwich source directory: + + $ PYTHONPATH=. python -m dulwich.contrib.test_swift + +How to start functional tests +----------------------------- + +We provide some basic tests to perform smoke tests against a real Swift +cluster. To run those functional tests you need a properly configured +configuration file. The tests can be run as follow: + + $ DULWICH_SWIFT_CFG=/etc/swift-dul.conf PYTHONPATH=. python -m dulwich.contrib.test_swift_smoke + +How to install +-------------- + +Install the Dulwich library via the setup.py. The dependencies will be +automatically retrieved from pypi: + + $ python ./setup.py install + +How to run the server +--------------------- + +Start the server using the following command: + + $ python -m dulwich.contrib.swift daemon -c /etc/swift-dul.conf -l 127.0.0.1 + +Note that a lot of request will be performed against the Swift +cluster so it is better to start the Dulwich server as close +as possible of the Swift proxy. The best solution is to run +the server on the Swift proxy node to reduce the latency. + +How to use +---------- + +Once you have validated that the functional tests is working as expected and +the server is running we can init a bare repository. Run this +command with the name of the repository to create: + + $ python -m dulwich.contrib.swift init -c /etc/swift-dul.conf edeploy + +The repository name will be the container that will contain all the Git +objects for the repository. Then standard c Git client can be used to +perform operations against this repository. + +As an example we can clone the previously empty bare repository: + + $ git clone git://localhost/edeploy + +Then push an existing project in it: + + $ git clone https://github.com/enovance/edeploy.git edeployclone + $ cd edeployclone + $ git remote add alt git://localhost/edeploy + $ git push alt master + $ git ls-remote alt + 9dc50a9a9bff1e232a74e365707f22a62492183e HEAD + 9dc50a9a9bff1e232a74e365707f22a62492183e refs/heads/master + +The other Git commands can be used the way you do usually against +a regular repository. + +Note the daemon subcommands starts a Git server listening for the +Git protocol. Therefor there is no authentication or encryption +at all between the cGIT client and the GIT server (Dulwich). + +Note on the .info file for pack object +-------------------------------------- + +The Swift interface of Dulwich relies only on the pack format +to store Git objects. Instead of using only an index (pack-sha.idx) +along with the pack, we add a second file (pack-sha.info). This file +is automatically created when a client pushes some references on the +repository. The purpose of this file is to speed up pack creation +server side when a client fetches some references. Currently this +.info format is not optimized and may change in future. diff -Nru dulwich-0.12.0/setup.cfg dulwich-0.18.5/setup.cfg --- dulwich-0.12.0/setup.cfg 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/setup.cfg 2017-10-29 16:36:37.000000000 +0000 @@ -3,5 +3,4 @@ [egg_info] tag_build = tag_date = 0 -tag_svn_revision = 0 diff -Nru dulwich-0.12.0/setup.py dulwich-0.18.5/setup.py --- dulwich-0.12.0/setup.py 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/setup.py 2017-10-07 15:45:18.000000000 +0000 @@ -1,22 +1,25 @@ #!/usr/bin/python +# encoding: utf-8 # Setup file for dulwich -# Copyright (C) 2008-2011 Jelmer Vernooij +# Copyright (C) 2008-2016 Jelmer Vernooij try: from setuptools import setup, Extension except ImportError: from distutils.core import setup, Extension from distutils.core import Distribution +import os +import sys -dulwich_version_string = '0.12.0' +dulwich_version_string = '0.18.5' include_dirs = [] # Windows MSVC support -import os -import sys -if sys.platform == 'win32': +if sys.platform == 'win32' and sys.version_info[:2] < (3, 6): + # Include dulwich/ for fallback stdint.h include_dirs.append('dulwich') + class DulwichDistribution(Distribution): def is_pure(self): @@ -32,6 +35,7 @@ pure = False + if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'): # XCode 4.0 dropped support for ppc architecture, which is hardcoded in # distutils.sysconfig @@ -46,39 +50,35 @@ if l.startswith('Xcode') and int(l.split()[1].split('.')[0]) >= 4: os.environ['ARCHFLAGS'] = '' -if sys.version_info[0] == 2: - tests_require = ['fastimport'] - if not '__pypy__' in sys.modules and not sys.platform == 'win32': - tests_require.extend([ - 'gevent', 'geventhttpclient', 'mock', 'setuptools>=17.1']) - if sys.version_info < (2, 7): - tests_require.append('unittest2') -else: - # fastimport, gevent, geventhttpclient are not available for PY3 - # mock only used for test_swift, which requires gevent/geventhttpclient - tests_require = [] - -if sys.version_info[0] > 2 and sys.platform == 'win32': - # C Modules don't build for python3 windows, and prevent tests from running - ext_modules = [] -else: - ext_modules = [ - Extension('dulwich._objects', ['dulwich/_objects.c'], - include_dirs=include_dirs), - Extension('dulwich._pack', ['dulwich/_pack.c'], - include_dirs=include_dirs), - Extension('dulwich._diff_tree', ['dulwich/_diff_tree.c'], - include_dirs=include_dirs), - ] +tests_require = ['fastimport'] + + +if '__pypy__' not in sys.modules and not sys.platform == 'win32': + tests_require.extend([ + 'gevent', 'geventhttpclient', 'mock', 'setuptools>=17.1']) + +ext_modules = [ + Extension('dulwich._objects', ['dulwich/_objects.c'], + include_dirs=include_dirs), + Extension('dulwich._pack', ['dulwich/_pack.c'], + include_dirs=include_dirs), + Extension('dulwich._diff_tree', ['dulwich/_diff_tree.c'], + include_dirs=include_dirs), +] +if sys.platform == 'win32': + # Win32 setup breaks with non-ascii characters. + author = "Jelmer Vernooij" +else: + author = "Jelmer Vernooij" + setup(name='dulwich', description='Python Git Library', keywords='git', version=dulwich_version_string, url='https://www.dulwich.io/', - license='GPLv2 or later', - author='Jelmer Vernooij', + license='Apachev2 or later or GPLv2', author_email='jelmer@jelmer.uk', long_description=""" Python implementation of the Git file formats and protocols, @@ -87,21 +87,25 @@ All functionality is available in pure Python. Optional C extensions can be built for improved performance. - The project is named after the part of London that Mr. and Mrs. Git live in - in the particular Monty Python sketch. + The project is named after the part of London that Mr. and Mrs. Git live + in in the particular Monty Python sketch. """, - packages=['dulwich', 'dulwich.tests', 'dulwich.tests.compat', 'dulwich.contrib'], + packages=['dulwich', 'dulwich.tests', 'dulwich.tests.compat', + 'dulwich.contrib'], package_data={'': ['../docs/tutorial/*.txt']}, scripts=['bin/dulwich', 'bin/dul-receive-pack', 'bin/dul-upload-pack'], classifiers=[ 'Development Status :: 4 - Beta', - 'License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)', - 'Programming Language :: Python :: 2.6', + 'License :: OSI Approved :: Apache Software License', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Operating System :: POSIX', + 'Operating System :: Microsoft :: Windows', 'Topic :: Software Development :: Version Control', ], ext_modules=ext_modules, diff -Nru dulwich-0.12.0/tox.ini dulwich-0.18.5/tox.ini --- dulwich-0.12.0/tox.ini 2015-12-13 15:34:37.000000000 +0000 +++ dulwich-0.18.5/tox.ini 2017-10-07 15:45:18.000000000 +0000 @@ -1,10 +1,8 @@ [tox] downloadcache = {toxworkdir}/cache/ -envlist = py26, py27, pypy, py27-noext, pypy-noext, py34, py34-noext +envlist = py27, pypy, py27-noext, pypy-noext, py33, py33-noext, py34, py34-noext, py35, py35-noext, py36, py36-noext [testenv] -deps = - unittest2 commands = make check recreate = True @@ -18,3 +16,9 @@ [testenv:py34-noext] commands = make check-noextensions + +[testenv:py35-noext] +commands = make check-noextensions + +[testenv:py36-noext] +commands = make check-noextensions diff -Nru dulwich-0.12.0/.travis.yml dulwich-0.18.5/.travis.yml --- dulwich-0.12.0/.travis.yml 1970-01-01 00:00:00.000000000 +0000 +++ dulwich-0.18.5/.travis.yml 2017-10-07 15:45:17.000000000 +0000 @@ -0,0 +1,41 @@ +language: python +sudo: false +cache: pip + +python: + - 2.7 + - 3.3 + - 3.4 + - 3.5 + - 3.5-dev + - 3.6 + - 3.6-dev + - 3.7-dev + - pypy3.3-5.2-alpha1 + +env: + - PYTHONHASHSEED=random + TEST_REQUIRE="gevent greenlet geventhttpclient fastimport" + +matrix: + include: + - python: pypy + env: TEST_REQUIRE=fastimport + +install: + - travis_retry pip install -U pip coverage codecov flake8 $TEST_REQUIRE + +script: + # Test without c extensions + - python -m coverage run -p --source=dulwich -m unittest dulwich.tests.test_suite + + # Test with c extensions + - python setup.py build_ext -i + - python -m coverage run -p --source=dulwich -m unittest dulwich.tests.test_suite + + # Style + - make style + +after_success: + - python -m coverage combine + - codecov