Bug 964465: Check certificate whitelist strings before sending remote lookup (r=gcp)
authorMonica Chew <mmc@mozilla.com>
Wed, 19 Feb 2014 15:14:02 -0800
changeset 169971 892751e80036a9c04fd361ca3fb8c348d809693a
parent 169970 c4c0017c70208522562ad77ec8ef3c790a8ba8e4
child 169972 7f6f5e970345d4ec0bfd45d5f9145bceee007ee6
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersgcp
bugs964465
milestone30.0a1
Bug 964465: Check certificate whitelist strings before sending remote lookup (r=gcp)
toolkit/components/downloads/ApplicationReputation.cpp
toolkit/components/downloads/ApplicationReputation.h
toolkit/components/downloads/test/unit/data/digest.chunk
toolkit/components/downloads/test/unit/data/signed_win.exe
toolkit/components/downloads/test/unit/test_app_rep.js
toolkit/components/downloads/test/unit/test_app_rep_windows.js
toolkit/components/downloads/test/unit/xpcshell.ini
--- a/toolkit/components/downloads/ApplicationReputation.cpp
+++ b/toolkit/components/downloads/ApplicationReputation.cpp
@@ -1,49 +1,57 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
+// See
+// https://wiki.mozilla.org/Security/Features/Application_Reputation_Design_Doc
+// for a description of Chrome's implementation of this feature.
 #include "ApplicationReputation.h"
 #include "csd.pb.h"
 
 #include "nsIArray.h"
 #include "nsIApplicationReputation.h"
 #include "nsIChannel.h"
 #include "nsIHttpChannel.h"
 #include "nsIIOService.h"
 #include "nsIPrefService.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIStreamListener.h"
 #include "nsIStringStream.h"
 #include "nsIUploadChannel2.h"
 #include "nsIURI.h"
 #include "nsIUrlClassifierDBService.h"
 #include "nsIX509Cert.h"
+#include "nsIX509CertDB.h"
 #include "nsIX509CertList.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
+#include "mozilla/TimeStamp.h"
 
+#include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsNetCID.h"
 #include "nsReadableUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
+#include "nsTArray.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOMStrings.h"
 
 using mozilla::Preferences;
 using mozilla::Telemetry::Accumulate;
+using safe_browsing::ClientDownloadRequest;
 using safe_browsing::ClientDownloadRequest_SignatureInfo;
+using safe_browsing::ClientDownloadRequest_CertificateChain;
 
 // Preferences that we need to initialize the query. We may need another
 // preference than browser.safebrowsing.malware.enabled, or simply use
 // browser.safebrowsing.appRepURL. See bug 887041.
 #define PREF_SB_APP_REP_URL "browser.safebrowsing.appRepURL"
 #define PREF_SB_MALWARE_ENABLED "browser.safebrowsing.malware.enabled"
 #define PREF_GENERAL_LOCALE "general.useragent.locale"
 #define PREF_DOWNLOAD_BLOCK_TABLE "urlclassifier.download_block_table"
@@ -53,166 +61,503 @@ using safe_browsing::ClientDownloadReque
 #if defined(PR_LOGGING)
 PRLogModuleInfo *ApplicationReputationService::prlog = nullptr;
 #define LOG(args) PR_LOG(ApplicationReputationService::prlog, PR_LOG_DEBUG, args)
 #define LOG_ENABLED() PR_LOG_TEST(ApplicationReputationService::prlog, 4)
 #else
 #define LOG(args)
 #define LOG_ENABLED() (false)
 #endif
-/**
- * Keep track of pending lookups. Once the ApplicationReputationService creates
- * this, it is guaranteed to call mCallback. This class is private to
- * ApplicationReputationService.
- */
-class PendingLookup MOZ_FINAL :
-  public nsIStreamListener,
-  public nsIUrlClassifierCallback {
+
+class PendingDBLookup;
+
+// A single use class private to ApplicationReputationService encapsulating an
+// nsIApplicationReputationQuery and an nsIApplicationReputationCallback. Once
+// created by ApplicationReputationService, it is guaranteed to call mCallback.
+// This class is private to ApplicationReputationService.
+class PendingLookup MOZ_FINAL : public nsIStreamListener
+{
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
-  NS_DECL_NSIURLCLASSIFIERCALLBACK
+
+  // Constructor and destructor.
   PendingLookup(nsIApplicationReputationQuery* aQuery,
                 nsIApplicationReputationCallback* aCallback);
   ~PendingLookup();
 
+  // Start the lookup. The lookup may have 2 parts: local and remote. In the
+  // local lookup, PendingDBLookups are created to query the local allow and
+  // blocklists for various URIs associated with this downloaded file. In the
+  // event that no results are found, a remote lookup is sent to the Application
+  // Reputation server.
+  nsresult StartLookup();
+
 private:
-  /**
-   * Telemetry states.
-   */
-  /**
-   * The download appeared on the allowlist, blocklist, or no list (and thus
-   * could trigger a remote query).
-   */
-  enum LIST_TYPES {
-    ALLOW_LIST = 0,
-    BLOCK_LIST = 1,
-    NO_LIST = 2,
-  };
-  /**
-   * Status of the remote response (valid or not).
-   */
+  friend class PendingDBLookup;
+
+  // Telemetry states.
+  // Status of the remote response (valid or not).
   enum SERVER_RESPONSE_TYPES {
     SERVER_RESPONSE_VALID = 0,
     SERVER_RESPONSE_FAILED = 1,
     SERVER_RESPONSE_INVALID = 2,
   };
 
+  // The query containing metadata about the downloaded file.
   nsCOMPtr<nsIApplicationReputationQuery> mQuery;
+
+  // The callback with which to report the verdict.
   nsCOMPtr<nsIApplicationReputationCallback> mCallback;
-  /**
-   * The response from the application reputation query. This is read in chunks
-   * as part of our nsIStreamListener implementation and may contain embedded
-   * NULLs.
-   */
+
+  // An array of strings created from certificate information used to whitelist
+  // the downloaded file.
+  nsTArray<nsCString> mAllowlistSpecs;
+
+  // When we started this query
+  TimeStamp mStartTime;
+
+  // The protocol buffer used to store signature information extracted using
+  // the Windows Authenticode API, if the binary is signed.
+  ClientDownloadRequest_SignatureInfo mSignatureInfo;
+
+  // The response from the application reputation query. This is read in chunks
+  // as part of our nsIStreamListener implementation and may contain embedded
+  // NULLs.
   nsCString mResponse;
-  /**
-   * Clean up and call the callback. PendingLookup must not be used after this
-   * function is called.
-   */
+
+  // Clean up and call the callback. PendingLookup must not be used after this
+  // function is called.
   nsresult OnComplete(bool shouldBlock, nsresult rv);
-  /**
-   * Wrapper function for nsIStreamListener.onStopRequest to make it easy to
-   * guarantee calling the callback
-   */
+
+  // Wrapper function for nsIStreamListener.onStopRequest to make it easy to
+  // guarantee calling the callback
   nsresult OnStopRequestInternal(nsIRequest *aRequest,
                                  nsISupports *aContext,
                                  nsresult aResult,
                                  bool* aShouldBlock);
-  /**
-   * Parse the XPCOM certificate lists and stick them into the protocol buffer
-   * version.
-   */
+
+  // Escape '/' and '%' in certificate attribute values.
+  nsCString EscapeCertificateAttribute(const nsACString& aAttribute);
+
+  // Escape ':' in fingerprint values.
+  nsCString EscapeFingerprint(const nsACString& aAttribute);
+
+  // Generate whitelist strings for the given certificate pair from the same
+  // certificate chain.
+  nsresult GenerateWhitelistStringsForPair(
+    nsIX509Cert* certificate, nsIX509Cert* issuer);
+
+  // Generate whitelist strings for the given certificate chain, which starts
+  // with the signer and may go all the way to the root cert.
+  nsresult GenerateWhitelistStringsForChain(
+    const ClientDownloadRequest_CertificateChain& aChain);
+
+  // For signed binaries, generate strings of the form:
+  // http://sb-ssl.google.com/safebrowsing/csd/certificate/
+  //   <issuer_cert_sha1_fingerprint>[/CN=<cn>][/O=<org>][/OU=<unit>]
+  // for each (cert, issuer) pair in each chain of certificates that is
+  // associated with the binary.
+  nsresult GenerateWhitelistStrings(
+    const ClientDownloadRequest_SignatureInfo& aSignatureInfo);
+
+  // Parse the XPCOM certificate lists and stick them into the protocol buffer
+  // version.
   nsresult ParseCertificates(nsIArray* aSigArray,
                              ClientDownloadRequest_SignatureInfo* aSigInfo);
-  /**
-   * Sends a query to the remote application reputation service. Returns NS_OK
-   * on success.
-   */
+
+  // Helper function to ensure that we call PendingLookup::LookupNext or
+  // PendingLookup::OnComplete.
+  nsresult DoLookupInternal();
+
+  // Looks up all the URIs that may be responsible for allowlisting or
+  // blocklisting the downloaded file. These URIs may include whitelist strings
+  // generated by certificates verifying the binary as well as the target URI
+  // from which the file was downloaded.
+  nsresult LookupNext();
+
+  // Sends a query to the remote application reputation service. Returns NS_OK
+  // on success.
   nsresult SendRemoteQuery();
+
+  // Helper function to ensure that we always call the callback.
+  nsresult SendRemoteQueryInternal();
+};
+
+// A single-use class for looking up a single URI in the safebrowsing DB. This
+// class is private to PendingLookup.
+class PendingDBLookup MOZ_FINAL : public nsIUrlClassifierCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIURLCLASSIFIERCALLBACK
+
+  // Constructor and destructor
+  PendingDBLookup(PendingLookup* aPendingLookup);
+  ~PendingDBLookup();
+
+  // Look up the given URI in the safebrowsing DBs, optionally on both the allow
+  // list and the blocklist. If there is a match, call
+  // PendingLookup::OnComplete. Otherwise, call PendingLookup::LookupNext.
+  nsresult LookupSpec(const nsACString& aSpec, bool aAllowListOnly);
+private:
+  // The download appeared on the allowlist, blocklist, or no list (and thus
+  // could trigger a remote query.
+  enum LIST_TYPES {
+    ALLOW_LIST = 0,
+    BLOCK_LIST = 1,
+    NO_LIST = 2,
+  };
+
+  nsCString mSpec;
+  bool mAllowListOnly;
+  nsRefPtr<PendingLookup> mPendingLookup;
+  nsresult LookupSpecInternal(const nsACString& aSpec);
 };
 
-NS_IMPL_ISUPPORTS3(PendingLookup,
+NS_IMPL_ISUPPORTS1(PendingDBLookup,
+                   nsIUrlClassifierCallback)
+
+PendingDBLookup::PendingDBLookup(PendingLookup* aPendingLookup) :
+  mAllowListOnly(false),
+  mPendingLookup(aPendingLookup)
+{
+  LOG(("Created pending DB lookup [this = %p]", this));
+}
+
+PendingDBLookup::~PendingDBLookup()
+{
+  LOG(("Destroying pending DB lookup [this = %p]", this));
+  mPendingLookup = nullptr;
+}
+
+nsresult
+PendingDBLookup::LookupSpec(const nsACString& aSpec,
+                            bool aAllowListOnly)
+{
+  LOG(("Checking principal %s", aSpec.Data()));
+  mSpec = aSpec;
+  mAllowListOnly = aAllowListOnly;
+  nsresult rv = LookupSpecInternal(aSpec);
+  if (NS_FAILED(rv)) {
+    LOG(("Error in LookupSpecInternal"));
+    return mPendingLookup->OnComplete(false, NS_OK);
+  }
+  // LookupSpecInternal has called nsIUrlClassifierCallback.lookup, which is
+  // guaranteed to call HandleEvent.
+  return rv;
+}
+
+nsresult
+PendingDBLookup::LookupSpecInternal(const nsACString& aSpec)
+{
+  nsresult rv;
+
+  nsCOMPtr<nsIURI> uri = nullptr;
+  nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
+  rv = ios->NewURI(aSpec, nullptr, nullptr, getter_AddRefs(uri));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIPrincipal> principal = nullptr;
+  nsCOMPtr<nsIScriptSecurityManager> secMan =
+    do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = secMan->GetNoAppCodebasePrincipal(uri, getter_AddRefs(principal));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Check local lists to see if the URI has already been whitelisted or
+  // blacklisted.
+  LOG(("Checking DB service for principal %s [this = %p]", mSpec.get(), this));
+  nsCOMPtr<nsIUrlClassifierDBService> dbService =
+    do_GetService(NS_URLCLASSIFIERDBSERVICE_CONTRACTID, &rv);
+  return dbService->Lookup(principal, this);
+}
+
+NS_IMETHODIMP
+PendingDBLookup::HandleEvent(const nsACString& tables)
+{
+  // HandleEvent is guaranteed to call either:
+  // 1) PendingLookup::OnComplete if the URL can be classified locally, or
+  // 2) PendingLookup::LookupNext if the URL can be cannot classified locally.
+  // Allow listing trumps block listing.
+  nsAutoCString allowList;
+  Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE, &allowList);
+  if (FindInReadable(tables, allowList)) {
+    Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, ALLOW_LIST);
+    LOG(("Found principal %s on allowlist [this = %p]", mSpec.get(), this));
+    return mPendingLookup->OnComplete(false, NS_OK);
+  }
+
+  nsAutoCString blockList;
+  Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE, &blockList);
+  if (!mAllowListOnly && FindInReadable(tables, blockList)) {
+    Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, BLOCK_LIST);
+    LOG(("Found principal %s on blocklist [this = %p]", mSpec.get(), this));
+    return mPendingLookup->OnComplete(true, NS_OK);
+  }
+
+  LOG(("Didn't find principal %s on any list [this = %p]", mSpec.get(), this));
+  Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, NO_LIST);
+  return mPendingLookup->LookupNext();
+}
+
+NS_IMPL_ISUPPORTS2(PendingLookup,
                    nsIStreamListener,
-                   nsIRequestObserver,
-                   nsIUrlClassifierCallback)
+                   nsIRequestObserver)
 
 PendingLookup::PendingLookup(nsIApplicationReputationQuery* aQuery,
                              nsIApplicationReputationCallback* aCallback) :
   mQuery(aQuery),
   mCallback(aCallback)
 {
   LOG(("Created pending lookup [this = %p]", this));
 }
 
 PendingLookup::~PendingLookup()
 {
   LOG(("Destroying pending lookup [this = %p]", this));
 }
 
 nsresult
-PendingLookup::OnComplete(bool shouldBlock, nsresult rv)
-{
-  Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SHOULD_BLOCK,
-    shouldBlock);
-  if (shouldBlock) {
-    LOG(("Application Reputation check failed, blocking bad binary "
-         "[this = %p]", this));
-  } else {
-    LOG(("Application Reputation check passed [this = %p]", this));
-  }
-  nsresult res = mCallback->OnComplete(shouldBlock, rv);
-  return res;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//// nsIUrlClassifierCallback
-NS_IMETHODIMP
-PendingLookup::HandleEvent(const nsACString& tables)
+PendingLookup::LookupNext()
 {
-  // HandleEvent is guaranteed to call the callback if either the URL can be
-  // classified locally, or if there is an error sending the remote lookup.
-  // Allow listing trumps block listing.
-  nsCString allow_list;
-  Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE, &allow_list);
-  if (FindInReadable(tables, allow_list)) {
-    Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, ALLOW_LIST);
-    LOG(("Found principal on allowlist [this = %p]", this));
-    return OnComplete(false, NS_OK);
+  // We must call LookupNext or SendRemoteQuery upon return.
+  // Look up all of the URLs that could whitelist this download.
+  int index = mAllowlistSpecs.Length() - 1;
+  if (index >= 0) {
+    nsCString spec = mAllowlistSpecs[index];
+    mAllowlistSpecs.RemoveElementAt(index);
+    nsRefPtr<PendingDBLookup> lookup(new PendingDBLookup(this));
+    bool allowListOnly = true;
+    if (index == 0) {
+      // The last URI is the target URI, which may be used for blacklisting as
+      // well as whitelisting.
+      allowListOnly = false;
+    }
+    return lookup->LookupSpec(spec, allowListOnly);
   }
-
-  nsCString block_list;
-  Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE, &block_list);
-  if (FindInReadable(tables, block_list)) {
-    Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, BLOCK_LIST);
-    LOG(("Found principal on blocklist [this = %p]", this));
-    return OnComplete(true, NS_OK);
-  }
-
-  Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, NO_LIST);
+  // There are no more URIs to check against local list, so send the remote
+  // query if we can.
   // Revert to just ifdef XP_WIN when remote lookups are enabled (bug 933432)
 #if 0
   nsresult rv = SendRemoteQuery();
   if (NS_FAILED(rv)) {
     return OnComplete(false, rv);
   }
   return NS_OK;
 #else
   return OnComplete(false, NS_OK);
 #endif
 }
 
+nsCString
+PendingLookup::EscapeCertificateAttribute(const nsACString& aAttribute)
+{
+  // Escape '/' because it's a field separator, and '%' because Chrome does
+  nsCString escaped;
+  escaped.SetCapacity(aAttribute.Length());
+  for (unsigned int i = 0; i < aAttribute.Length(); ++i) {
+    if (aAttribute.Data()[i] == '%') {
+      escaped.Append("%25");
+    } else if (aAttribute.Data()[i] == '/') {
+      escaped.Append("%2F");
+    } else if (aAttribute.Data()[i] == ' ') {
+      escaped.Append("%20");
+    } else {
+      escaped.Append(aAttribute.Data()[i]);
+    }
+  }
+  return escaped;
+}
+
+nsCString
+PendingLookup::EscapeFingerprint(const nsACString& aFingerprint)
+{
+  // Google's fingerprint doesn't have colons
+  nsCString escaped;
+  escaped.SetCapacity(aFingerprint.Length());
+  for (unsigned int i = 0; i < aFingerprint.Length(); ++i) {
+    if (aFingerprint.Data()[i] != ':') {
+      escaped.Append(aFingerprint.Data()[i]);
+    }
+  }
+  return escaped;
+}
+
+nsresult
+PendingLookup::GenerateWhitelistStringsForPair(
+  nsIX509Cert* certificate,
+  nsIX509Cert* issuer)
+{
+  // The whitelist paths have format:
+  // http://sb-ssl.google.com/safebrowsing/csd/certificate/<issuer_cert_fingerprint>[/CN=<cn>][/O=<org>][/OU=<unit>]
+  // Any of CN, O, or OU may be omitted from the whitelist entry. Unfortunately
+  // this is not publicly documented, but the Chrome implementation can be found
+  // here:
+  // https://code.google.com/p/chromium/codesearch#search/&q=GetCertificateWhitelistStrings
+  nsCString whitelistString(
+    "http://sb-ssl.google.com/safebrowsing/csd/certificate/");
+
+  nsString fingerprint;
+  nsresult rv = issuer->GetSha1Fingerprint(fingerprint);
+  NS_ENSURE_SUCCESS(rv, rv);
+  whitelistString.Append(
+    EscapeFingerprint(NS_ConvertUTF16toUTF8(fingerprint)));
+
+  nsString commonName;
+  rv = certificate->GetCommonName(commonName);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!commonName.IsEmpty()) {
+    whitelistString.Append("/CN=");
+    whitelistString.Append(
+      EscapeCertificateAttribute(NS_ConvertUTF16toUTF8(commonName)));
+  }
+
+  nsString organization;
+  rv = certificate->GetOrganization(organization);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!organization.IsEmpty()) {
+    whitelistString.Append("/O=");
+    whitelistString.Append(
+      EscapeCertificateAttribute(NS_ConvertUTF16toUTF8(organization)));
+  }
+
+  nsString organizationalUnit;
+  rv = certificate->GetOrganizationalUnit(organizationalUnit);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!organizationalUnit.IsEmpty()) {
+    whitelistString.Append("/OU=");
+    whitelistString.Append(
+      EscapeCertificateAttribute(NS_ConvertUTF16toUTF8(organizationalUnit)));
+  }
+  LOG(("Whitelisting %s", whitelistString.get()));
+
+  mAllowlistSpecs.AppendElement(whitelistString);
+  return NS_OK;
+}
+
+nsresult
+PendingLookup::GenerateWhitelistStringsForChain(
+  const safe_browsing::ClientDownloadRequest_CertificateChain& aChain)
+{
+  // We need a signing certificate and an issuer to construct a whitelist
+  // entry.
+  if (aChain.element_size() < 2) {
+    return NS_OK;
+  }
+
+  // Get the signer.
+  nsresult rv;
+  nsCOMPtr<nsIX509CertDB> certDB = do_GetService(NS_X509CERTDB_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIX509Cert> signer = nullptr;
+  rv = certDB->ConstructX509(
+    const_cast<char *>(aChain.element(0).certificate().data()),
+    aChain.element(0).certificate().size(), getter_AddRefs(signer));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  for (int i = 1; i < aChain.element_size(); ++i) {
+    // Get the issuer.
+    nsCOMPtr<nsIX509Cert> issuer = nullptr;
+    rv = certDB->ConstructX509(
+      const_cast<char *>(aChain.element(i).certificate().data()),
+      aChain.element(i).certificate().size(), getter_AddRefs(issuer));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsresult rv = GenerateWhitelistStringsForPair(signer, issuer);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  return NS_OK;
+}
+
+nsresult
+PendingLookup::GenerateWhitelistStrings(
+  const safe_browsing::ClientDownloadRequest_SignatureInfo& aSignatureInfo)
+{
+  for (int i = 0; i < aSignatureInfo.certificate_chain_size(); ++i) {
+    nsresult rv = GenerateWhitelistStringsForChain(
+      aSignatureInfo.certificate_chain(i));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  return NS_OK;
+}
+
+nsresult
+PendingLookup::StartLookup()
+{
+  mStartTime = TimeStamp::Now();
+  nsresult rv = DoLookupInternal();
+  if (NS_FAILED(rv)) {
+    return OnComplete(false, NS_OK);
+  };
+  return rv;
+}
+
+nsresult
+PendingLookup::DoLookupInternal()
+{
+  // We want to check the target URI against the local lists.
+  nsCOMPtr<nsIURI> uri = nullptr;
+  nsresult rv = mQuery->GetSourceURI(getter_AddRefs(uri));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCString spec;
+  rv = uri->GetSpec(spec);
+  NS_ENSURE_SUCCESS(rv, rv);
+  mAllowlistSpecs.AppendElement(spec);
+
+  // Extract the signature and parse certificates so we can use it to check
+  // whitelists.
+  nsCOMPtr<nsIArray> sigArray = nullptr;
+  rv = mQuery->GetSignatureInfo(getter_AddRefs(sigArray));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (sigArray) {
+    rv = ParseCertificates(sigArray, &mSignatureInfo);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  rv = GenerateWhitelistStrings(mSignatureInfo);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Start the call chain.
+  return LookupNext();
+}
+
+nsresult
+PendingLookup::OnComplete(bool shouldBlock, nsresult rv)
+{
+  Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SHOULD_BLOCK,
+    shouldBlock);
+#if defined(PR_LOGGING)
+  double t = (TimeStamp::Now() - mStartTime).ToMilliseconds();
+#endif
+  if (shouldBlock) {
+    LOG(("Application Reputation check failed, blocking bad binary in %f ms "
+         "[this = %p]", t, this));
+  } else {
+    LOG(("Application Reputation check passed in %f ms [this = %p]", t, this));
+  }
+  nsresult res = mCallback->OnComplete(shouldBlock, rv);
+  return res;
+}
+
 nsresult
 PendingLookup::ParseCertificates(
   nsIArray* aSigArray,
   ClientDownloadRequest_SignatureInfo* aSignatureInfo)
 {
+  // If we haven't been set for any reason, bail.
+  NS_ENSURE_ARG_POINTER(aSigArray);
+
   // Binaries may be signed by multiple chains of certificates. If there are no
-  // chains, the binary is unsiged (or we were unable to extract signature
+  // chains, the binary is unsigned (or we were unable to extract signature
   // information on a non-Windows platform)
   nsCOMPtr<nsISimpleEnumerator> chains = nullptr;
   nsresult rv = aSigArray->Enumerate(getter_AddRefs(chains));
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool hasMoreChains = false;
   rv = chains->HasMoreElements(&hasMoreChains);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -255,21 +600,33 @@ PendingLookup::ParseCertificates(
     aSignatureInfo->set_trusted(true);
   }
   return NS_OK;
 }
 
 nsresult
 PendingLookup::SendRemoteQuery()
 {
+  nsresult rv = SendRemoteQueryInternal();
+  if (NS_FAILED(rv)) {
+    return OnComplete(false, NS_OK);
+  }
+  // SendRemoteQueryInternal has fired off the query and we call OnComplete in
+  // the nsIStreamListener.onStopRequest.
+  return rv;
+}
+
+nsresult
+PendingLookup::SendRemoteQueryInternal()
+{
   LOG(("Sending remote query for application reputation [this = %p]", this));
   // We did not find a local result, so fire off the query to the application
   // reputation service.
   safe_browsing::ClientDownloadRequest req;
-  nsCOMPtr<nsIURI> uri;
+  nsCOMPtr<nsIURI> uri = nullptr;
   nsresult rv;
   rv = mQuery->GetSourceURI(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
   nsCString spec;
   rv = uri->GetSpec(spec);
   NS_ENSURE_SUCCESS(rv, rv);
   req.set_url(spec.get());
 
@@ -287,31 +644,24 @@ PendingLookup::SendRemoteQuery()
   nsCString sha256Hash;
   rv = mQuery->GetSha256Hash(sha256Hash);
   NS_ENSURE_SUCCESS(rv, rv);
   req.mutable_digests()->set_sha256(sha256Hash.Data());
   nsString fileName;
   rv = mQuery->GetSuggestedFileName(fileName);
   NS_ENSURE_SUCCESS(rv, rv);
   req.set_file_basename(NS_ConvertUTF16toUTF8(fileName).get());
-
-  // Extract the signature and parse certificates so we can use it to check
-  // whitelists.
-  nsCOMPtr<nsIArray> sigArray = nullptr;
-  rv = mQuery->GetSignatureInfo(getter_AddRefs(sigArray));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // This actually needs to be further up, but it can wait until bug 964465
-  rv = ParseCertificates(sigArray, req.mutable_signature());
-  NS_ENSURE_SUCCESS(rv, rv);
+  req.mutable_signature()->CopyFrom(mSignatureInfo);
 
   if (req.signature().trusted()) {
-    LOG(("Got signed binary for application reputation [this = %p]", this));
+    LOG(("Got signed binary for remote application reputation check "
+         "[this = %p]", this));
   } else {
-    LOG(("Got unsigned binary for application reputation [this = %p]", this));
+    LOG(("Got unsigned binary for remote application reputation check "
+         "[this = %p]", this));
   }
 
   // Serialize the protocol buffer to a string. This can only fail if we are
   // out of memory, or if the protocol buffer req is missing required fields
   // (only the URL for now).
   std::string serialized;
   if (!req.SerializeToString(&serialized)) {
     return NS_ERROR_UNEXPECTED;
@@ -320,24 +670,22 @@ PendingLookup::SendRemoteQuery()
   // Set the input stream to the serialized protocol buffer
   nsCOMPtr<nsIStringInputStream> sstream =
     do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = sstream->SetData(serialized.c_str(), serialized.length());
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // Set up the channel to transmit the request to the service.
-  nsCOMPtr<nsIChannel> channel;
+  nsCOMPtr<nsIChannel> channel = nullptr;
   nsCString serviceUrl;
   NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_SB_APP_REP_URL, &serviceUrl),
                     NS_ERROR_NOT_AVAILABLE);
+  nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
   rv = ios->NewChannel(serviceUrl, nullptr, nullptr, getter_AddRefs(channel));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Upload the protobuf to the application reputation service.
   nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(channel, &rv);
@@ -470,87 +818,66 @@ ApplicationReputationService::GetSinglet
   gApplicationReputationService = new ApplicationReputationService();
   if (gApplicationReputationService) {
     NS_ADDREF(gApplicationReputationService);
   }
 
   return gApplicationReputationService;
 }
 
-ApplicationReputationService::ApplicationReputationService() :
-  mDBService(nullptr),
-  mSecurityManager(nullptr) {
+ApplicationReputationService::ApplicationReputationService()
+{
 #if defined(PR_LOGGING)
   if (!prlog) {
     prlog = PR_NewLogModule("ApplicationReputation");
   }
 #endif
   LOG(("Application reputation service started up"));
 }
 
 ApplicationReputationService::~ApplicationReputationService() {
+  LOG(("Application reputation service shutting down"));
 }
 
 NS_IMETHODIMP
 ApplicationReputationService::QueryReputation(
     nsIApplicationReputationQuery* aQuery,
     nsIApplicationReputationCallback* aCallback) {
   NS_ENSURE_ARG_POINTER(aQuery);
   NS_ENSURE_ARG_POINTER(aCallback);
 
-  LOG(("Sending application reputation query"));
   Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_COUNT, true);
   nsresult rv = QueryReputationInternal(aQuery, aCallback);
   if (NS_FAILED(rv)) {
     aCallback->OnComplete(false, rv);
   }
   return NS_OK;
 }
 
 nsresult ApplicationReputationService::QueryReputationInternal(
   nsIApplicationReputationQuery* aQuery,
   nsIApplicationReputationCallback* aCallback) {
-  // Lazily instantiate mDBService and mSecurityManager
   nsresult rv;
-  if (!mDBService) {
-    mDBService = do_GetService(NS_URLCLASSIFIERDBSERVICE_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  if (!mSecurityManager) {
-    mSecurityManager = do_GetService("@mozilla.org/scriptsecuritymanager;1",
-                                     &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
   // If malware checks aren't enabled, don't query application reputation.
   if (!Preferences::GetBool(PREF_SB_MALWARE_ENABLED, false)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // If there is no service URL for querying application reputation, abort.
   nsCString serviceUrl;
   NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_SB_APP_REP_URL, &serviceUrl),
                     NS_ERROR_NOT_AVAILABLE);
   if (serviceUrl.EqualsLiteral("")) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  // Create a new pending lookup.
+  nsCOMPtr<nsIURI> uri = nullptr;
+  rv = aQuery->GetSourceURI(getter_AddRefs(uri));
+  NS_ENSURE_SUCCESS(rv, rv);
+  // Bail if the URI hasn't been set.
+  NS_ENSURE_STATE(uri);
+
+  // Create a new pending lookup and start the call chain.
   nsRefPtr<PendingLookup> lookup(new PendingLookup(aQuery, aCallback));
   NS_ENSURE_STATE(lookup);
 
-  nsCOMPtr<nsIURI> uri;
-  rv = aQuery->GetSourceURI(getter_AddRefs(uri));
-  NS_ENSURE_SUCCESS(rv, rv);
-  // If the URI hasn't been set, bail
-  NS_ENSURE_STATE(uri);
-  nsCOMPtr<nsIPrincipal> principal;
-  // In nsIUrlClassifierDBService.lookup, the only use of the principal is to
-  // wrap the URI. nsISecurityManager.getNoAppCodebasePrincipal is the easiest
-  // way to wrap a URI inside a principal, since principals can't be
-  // constructed.
-  rv = mSecurityManager->GetNoAppCodebasePrincipal(uri,
-                                                   getter_AddRefs(principal));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Check local lists to see if the URI has already been whitelisted or
-  // blacklisted.
-  return mDBService->Lookup(principal, lookup);
+  return lookup->StartLookup();
 }
--- a/toolkit/components/downloads/ApplicationReputation.h
+++ b/toolkit/components/downloads/ApplicationReputation.h
@@ -11,46 +11,41 @@
 #include "nsIRequestObserver.h"
 #include "nsIStreamListener.h"
 #include "nsISupports.h"
 
 #include "nsCOMPtr.h"
 #include "nsString.h"
 
 class nsIRequest;
-class nsIUrlClassifierDBService;
-class nsIScriptSecurityManager;
+class PendingDBLookup;
 class PendingLookup;
 class PRLogModuleInfo;
 
 class ApplicationReputationService MOZ_FINAL :
   public nsIApplicationReputationService {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIAPPLICATIONREPUTATIONSERVICE
 
 public:
   static ApplicationReputationService* GetSingleton();
 
 private:
   friend class PendingLookup;
+  friend class PendingDBLookup;
   /**
    * Global singleton object for holding this factory service.
    */
   static ApplicationReputationService* gApplicationReputationService;
   /**
    * NSPR_LOG_MODULES=ApplicationReputation:5
    */
   static PRLogModuleInfo* prlog;
   /**
-   * Keeps track of services used to query the local database of URLs.
-   */
-  nsCOMPtr<nsIUrlClassifierDBService> mDBService;
-  nsCOMPtr<nsIScriptSecurityManager> mSecurityManager;
-  /**
    * This is a singleton, so disallow construction.
    */
   ApplicationReputationService();
   ~ApplicationReputationService();
   /**
    * Wrapper function for QueryReputation that makes it easier to ensure the
    * callback is called.
    */
--- a/toolkit/components/downloads/test/unit/data/digest.chunk
+++ b/toolkit/components/downloads/test/unit/data/digest.chunk
@@ -1,2 +1,3 @@
-a:5:32:32
-_H^a7]=#nmnoQ
\ No newline at end of file
+a:5:32:64
+_H^a7]=#nmnoQ
+@.R0D7Y4ퟆS$8
\ No newline at end of file
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..de3bb40e848c8bf8bb8cc341096f463c628a5961
GIT binary patch
literal 61064
zc%1CL30PCt^Dum~L4ZJ_f}*0PimfezC{jhB1to|JHW~?{fT9pXL_oMfw4$MbYPhD_
z*0y$+(%Ma1`)gh53St4biYr!a#ie>_P*JHyi`;kS+yv3K|NsB_zVH2>=Y3wUp5&}^
z=FH5QGjq<lA%6O5N<>kV7y`#p)HaHLqNxA=se|CvZ@U-ugWKV!wh1O4erl>dGhdmT
zmoqmnZN73&T6T60t(={q%rj;yGqaU3FKLwXbJ8<{JU!k09WahPvsCkTtb*zs`a<X$
z)t!0=zPn8CO1`IL&e3DpzddRdMNJe)sFWKIMmqBvDY?KyAaSRrKw><fXU9?mlj|5I
z#)bU&DeB=jr4W!rSdjpWDXPfP&iU;GvzVfW!S~Ju6jg|sl@ukFbVBR|MXl)dKOpA6
zJ%Hy1!M_&=(HVs_e0Ntn5Sr3?5d5R0s98aI>1lKtMSbGV!=z*ow2#({g8xB$HR^*A
z@L|FZAk6+-y~?1x{Jc4oW2}`BTp+CeJG_|-GqV?G=FTJnRS)3>2yg$b-spcH;gjco
z=flMK%b1D&Ql>tx^cd~UO!XIQ1CCmA1!aO2;x5sXjCWI^intm+q*{jdS1><MD&0@}
zV4HxtGQ}uwsZkcioSIa6%y=`TGPT+un3h^ipw`7f#b>Q?f--2Es9~1)%gRod{ly$*
zs;e3mI;MI?Rg>`c*USO)1<~!VW;(kOGvZRp6h(<7%1u$!UNM!xG#58V!5pxbR<?7T
znlnl{pC59gD6^e2N}#{})l7l{u=g}5l1i*thZb7g)tt{Js1#DUznY`{#Smni&o;;*
z9cgedvP5mjS<a^tGwo(hwA9PAWCWLNbX6vg;0%HpTFz%Dl*QdW-MjMc>C&^bq$UQ+
zXQs}mHc$W%@Yo0CSR7T20Hs<Un%OQ~+EY|xDg<C%%HrBh_mXKh)4fr&*ffiyQu(^3
z7bx1t(ZCuL%#~3QLuf@rGA+}8V2Prjqz+0_t6}`+W`$MN0wY&&Mi|8jY@xENNuEzc
zM~V-GK8GNakcN!i=Ys`NRJB2&R{~%{iBQv4i21CAe1lLQ1qpUCOv0ojAh>=V)M9_;
zIIcRR64H$4mCaF9ISU`mQLXu6Kb`r0p8}Dg&vfQy@s&nbiqaLg&vGqr)fFF@<!Tdp
zoGQLR*(qu_t|q$hp9{nury%zzQ)j*aZSM*4#Rh>*3bB3wrC$=$0#JazI&-@+Ut->`
zv?&0fNq_+Nn*`AGXWc-meTqkwnhQPmE#TG!JT~u>0U>u+{t!j!m0JkC&f%aja+;Tp
z3ao4#pf&ih6%SByLVY*ksMo-#p*f#qe+OVha+P}ZMl5c0v~I-K680J?uO?zB1vJYs
zU!zSdt)#tx{PkFSAm&l)u)Qgxonk_<ixsN28r6eQR#lst&BF0Ta-2~s-<l%T7{qLs
zP8gj6Yz+N*?(jLC_(Mb!@aP2~%$$VoLIC!50HnXSi5Qhs7Y?Dq7XZ+SI&+bf!b(OT
zgOL5Q?a_X0zVfXqsoEf5S3O#yFgQAAmx5v_t(2FJ1`%K@Akj1uVoWH~*IH@^dJsyf
zxl?>f?GWM;38qXVgJ@Bl&2BD|QM9X`DvP3Q620BhsewVM{u`NJ1$xX(4XK>5f3K9%
zhTI7QF$D>xMTVi%)K0-wbS8}1E-(t!sZ32Ov~Wr>^CC(yKWPSOU_QWfQ1hY@a@MLk
zt-%NSCo{$8GGqfELkzA^q?ZD!YPQQ?M7T;TjecdS)7JRYqpE4Ih#=ZMLP@)nMGK;B
z*GKj=y68MP&NjGA5D}awjtDCdMcZzNRF-uab)4=Y-*U_<HXUs={}Ej1ZocofOd{GF
zZMy^`6I11fU>b3r_z69@vEBob)^yMEHk@7){lx~UPQHcGSp=K}v!+3t#1JklG7HEg
zFWmv$C~t!(<4J8oB`F5cFRr2}0i_MEs=$&wZCIoj$||tJ?)}h!?mw?GJ>*#8-OxfH
zqkL<nS}z0}LF*)D2@o?eu_L)+0kX<x<${NDxSGVVFD_IHpX`4CY*^$8=q@SA*tc^L
zKSep8m|P_0Wv~y*F*~)Q1Rz-=v0_N&OkM>h!lIziCUT*Tq)8Ms39q4DK{nf=oymU$
zH4}M@WYHR=A(cljq#nJ;)Q23svsXa79c4q6=55z(!lUdY<JD9klolwpdmRirk*x!X
zwYZpT#AA!w*MW{$(mWH^C0Mn}F~uAm2Mfl4MC)V$=4s_yhRV0tL+)0wQfByOgb-2_
zJ>JNs&aA-dk0^K4)!{V-UCX4X*5P9aiu<)Fn-C?@v0h2RXf#F6`UbRuHgPxVCpw;q
zbW9+SbUhZSxduL^X8*XyaqF;JQp6DEXIdPQY?S=?)D~y;$!dNza8%cF4W>w}P0?Pa
z7ls%`H7?1qu$~xi!Hk)ix*x?Kl!7V4gQ`)vjjB<(CzpXbl3G<b7n;f1kQ82rt6EBD
zj5SOj9HEvV3<7c7w5=b+l4-w=I4n^-5?aTsvdw&CL?q8KawZaSriVMQ1`s<XcOjNc
zKlOOl5ZcQD<^xM7!ayetrd{_SHp=*-lUDtJwJ>*vC`+-xk!uKK_uTzg9WMa#ns#Bu
zWb0=9GDh%VlZ_!|rS}ocX8bGxoenFoJFLVmWwC--+jU%}6&QsgDHMq#hS;u`x$rTg
zAl8Z08Te%E;dkZcw_s|J3a$V}6l=R}dKy+GzMi4GnjL)Cp<n0>=g_=5$N`oEJ;@34
zox3>DBs$WO;jHEJP>$3FC0l#vU&rlc=Qxy&ra3?}A{#UIS9cgxnvk<$ef-eYNR*fU
z3}|Zz;|)SByZnv=NqOmJoa%j0qtKIx>Ipdp>V(%NnmwprIy6dRXN{uKB>p!Vg<riL
z3%AywU?2QRM#-Cb&Y|#WgNUpF>PJ?_7IVAM=w%S_s(*DDs{W8lbGwUtO{Gob9IqaG
zVZ(5>GxTTQ#Xfjz(9r=;5PxD}p2M!$13)b^QKD3mG02f?nD6Y{gjN&&oBt03++S^O
z_Or(LQ<?;AA`_VlSs~X9f`o(wkY%7}$;Uvk9iXqN-9q>P_2X^67qrlgR-5lB=}6mv
z@Pi9I=rK%H;5w*i^I)n1YUtsC?Wo85#nI2j#nDd#cm;GvFF<WXqJ<E(4d+5vAR(l{
zyd>Mx;XfN)@5K1`Gts@l=2e7C5LY@7YA=D>j$~g%4S@JL&?}t>V;@#IGc{#~d7odk
z9?3!|Z}4DKTZy1`T7g*?V*J|b(Wr*SoR)@@6^VX^5Zy3<eXjLig)<8{!5(SnXPtBX
zJ~+Z*g_0FqspU;6Sj4TWM!lkp7!d%17$63J_TcSDtsAJ-(Hkls=lqARQup&vHDFxS
zN*XsrArB}m-)%j1%bZSfP_PED4*xC5I58}=1tgyK;X+%#MXkyH36R1_f*u4bW)?Bo
z_~mOYE?*MN0as)o8;q#2%K;Kwq6ALLoGW8J!Rs_E?u4RAmQX~7sZO1d!i!=n(i{iK
zmfS<Zdz!s`i%bmJV-o|@2I}t<!!gI}U=h`z5aRqo5|)m<j{q8&6qzX;c+Z6Liwb7z
zm1PbK4O1E<a}l^3-hA31fPPu1+~f+#5f_8FO)Vm$G~6gH0B<Il7EQ9<M$KpJUN(tC
zcgJ&79ql%%(&%Ek7X-Qznw9khZD=j^BYLdC^jGqVzC?-o@%qh=U)_A{#5U|_><3K@
zLG150AC`6u-=MIio9)ViXhsEwqJb)t-ub2X{sDF6OMn~$aaU(Dv_|<H=v&r_K&^;(
zyaJq4p+x`y7LG&{(cr}{`-ff>1_!o(Co)(u9okd|L@_8hpDf7Nl~yi!8qDunRhtB|
z0b(n%O+Ofr(1XO8I3J2PJNr3!XXwp}Zay}(hJoK&X2q6#`;z^%hv^<#pmBmI+f5=K
zQF?zeFoG)?_K&y+#MA?I1{@7PY8>(-?%@om3a_I59cVag7y{YcBobA{)liG0YN*Vb
zC_%2l(V0GZ6B=(@ACN_WcYpWuHgyC}pTsbuf;MQiTK%*#MA)Br(1r-BYM)lkw%;Jl
zaKgrwb=U-k>4}`rAc)MXyl;rBQJpotn{@Y{MYRtaL6yN!lE|Fqe1dTn{YDSa5Fs0m
z7Di9>dYvgW5TU+r?xP&Q{W=f?t(gs$mpl!Lz(!n|O1^>5IC<$fQmlxhy&w^R+@hLF
zDb)BaI9?8-YW*8d(Vn#c`6DmshVEYTw>-0$&<}FS41wO)ktvKNB>5?sMJ*5M8>Kcx
zaXw#=`aT;7w=1x=yrdF4wPMxDPQnOCpXtzN(<e}Bj+ElUL-#0`Z6ZCx0;e_vlS+Ej
z_+%#l5^7umL)YPA)P*cZ0`3$O-x?6#CaQWjVDC}88az74X$gq8cgn%0pDrNV1GW*)
z&IX$!Wh64-Ar)2-D$rd3ZYUNSg4H|*;4SaptDq(m`{6PS3syQyvcE!JvIX$pJq;{L
zEslQ;5sUD@<Rx!Fel785f>%UA4BelrkY|ZRub@_f&JAi~RuGJBM_c+}j-?N_;OWS)
z2|Ohk+=vO)CTiHIYeddq675qG?@+1XLN9N?7Ri8Rf+fb^n`c!5&m!|c><z3GpEwjN
zZ~d$?^#8AeBPXqe6%sKhgKu2?l6afH^iCo@9BsfMOeBRS3)c`(1CHlXA5e5d=)Pd)
z^#bb<qTL`WhhnA$4e<t)P23E#*@>;;3_h!K9qxookJ>?tPA&Tj=QIg@h`p(0mk}E`
ziR^EsR^#>rNV%3VO111B7<Q6jC|lpm>q?@+(WCFKC3!p`FD*L_D&pKtin6)OcdMl4
zMnx4XPEFxQroWG?AJ{CsF6ymi-{bql?x}z3p5Oj(xcqt((k=w*C1J(qoY{q#?dQzy
z=E!C(4uHb_)y}MgfNPR-0|DjjoO90#Hyewar-~>vFwl&Wmu!?`;yHQAL}|y={!D^X
zyW~5Z;V`S;#VR}fbD^0^Udlj<+3Bwr5q<}w!BZ<QHDYck3bq~%*F^4A$XSLB>!Zi@
z+|8hZs?)Hl%|f!|B{5PoesO)4myCpXIa<92QOL2RyEnrmXbsEFO2{|N2Utw#aBN&n
zi*flhND^u#XhWh{ai=tz1l~YwYH<Z5N(iEW{*MCk^$zatusV5335iJMB~reikqnQX
zXlAE^Ee=gX&T9BoqZfPl52W1>{xpE%myNJK-kT8Dl~7RwhH)31%+3lUvG+VEaTtvg
ziGGon&P0SZ5L$(twU+p&qkaswWcmBStX9G_@@MBjPcTIWx#6Tu_8*Ly0L51giQzDx
zHlZyLew%)J4|;y!I{K)2<>j7eFeLCMGa8{<H6!IFdt;Quq)iuHVA^lBqZKGGISC0+
zH$V7i5rm0Otb#l}OQv-tFRNlhfK6x^%>LX2K9}cU^w!U+NA{%sCL0E^KQ`ei4&=qP
zw__Q%DIiP#Fe@oZ<H&F<?460&GzoE909JHU3_++y;)i^E6t7(%e#{RJs%gM}6_7m@
zq9K{c5~5*uwW3#*U=V;}02@V+0KOI1pux1$XNVxKlD-8h8gx*&3kgVcLxRDetw8sT
zUyu?K_$e~757sbtact)ry0A4Y4B6HrfgSf3KZAW8W%|`+#J?hgnh9!IKMVEvd@auq
zEgN<p>7Hclfyh0IQ`4_7oLx9e5*!8L)fVCg0`C>{QotEqz63I!bUo#Whg23v($F<U
zNvR5)Lq!7_pAORmtrFT>qelzZpaQs40WPT)UTq8@i19tqQkX(MIGqd<P?6qz{Na4E
z^s(~`1o_0BE=!-&xHa4xENXr}qx#1=t>x!*f|j4td=H2+k3LE=Xzjq2gbxlA>||#*
zH4K(Ex|_oV_Ru^;=bx)Hfk@j9N}q)0-ty8KR05%hqQQstxr#Cx;~&+@n2+%v`Ow8Y
z$<JdZ5IJY;rh(FkM=a2jPLavcqT5DK=B0uTA=@x)pR2sO^)L4aFe80w1)w^J7<CtK
z^Q$JZ8|B#6-Jwq8?9wX_jZg;$^k9_iE@Y)5`oq@S;K?Rj>Ff`m?11$TQX~>}%n~M&
z7!P1Gpv^&dA@&Wjn%Flm4x&)B4#OspH*7R8S-6wnB@YUi=pXDfV>JJerRB|<us%c?
z1tJo(Z&)8xa<n3>59%XY8rCNYzKt?NK5zzbj6ybYp*YL*Hyu+D@*BwV0MX8pNNjEk
zSqxa44Yzv|Eld1;oku>tOWh11?5N+tnh-LzupF~N5N9Hh!476ObTB%@F$FP&0>w|)
z6~tU_U?O99twrhrVeUwlLa~_kE+0<VX>>J6+4{@;PM}6FMm6>mVsFs$doR2+@Q$C+
zlb4B6cH9*R(wCqgf;qq_qEC%oh`QlxA=IR#ZDcbK>3|Ue7O^GLWFcG!nM%P5xX9de
z2;xbHeQ4;(h5(pdxghQ81g?P#_21-F-+bI`cVFg~z}qf1H^W>9`~-wTVm42W6XI|(
z9$WxuS2C+?Qp0oXu}ee*9o6}Aqg11>C7UJ=;gvg=K!@<cR~-K}(P02*c8Ft2SRa%}
zI)S$j3>i*se~On|kOa@pmT&{ezq1CE^?jtwCU;mOc(S2u!UH5UDvS4_Q3IwkSW}ZV
zTK+^uZ%2ka3pA~f41!N}jii1v`C84VR&OG1hrSi-y^b^Pl)eSyM>msrGs!1AAbJ+#
zLkX<lt)v-y6;=nfABbYeSx~Zl%k>sA7(e|iUZ3EG1^Wk>0FdcAtg`7Y9x-%sCJllC
zM^nL>bg_`VkPep;GjJb7FNF-JxnDuy>A*I!Wr$Ind3BrlnTN*!K#mWAb8I%TtH|Dp
zt5(Z?4C2DNfpOtrWH+*&X!N)f&wg`(Xe_?EF{zy92cU*|`N+--<{JaS^EQaposSgE
zcCoR$8uImou59oHr;+Swc!srHKx4G=BFAkKpf`y-PUws7@dgwVVO*!+qoR(GdS(SV
z%BU{Kp+Uts-X0MFnak@oKa687XB_chLhJdO?zpYO9~PFM`?gaSCIIkcuy(;X_f9mp
zu`P`pXK-asvsWQvPWM;fisoD9Qc_8KL<Bl_mv4X{5=cuUhR90~<9U80PBKj_Ogh@%
zmtnbvaJERKvEli*FV~>O9QmHZ;Z^dLAECG7#nhDSrxm!f;9<IOeub25FEsPDahuc+
zoPkE?ftd6u3C=5<P6LG+Eqmp>vt+74kfN~!Vky@kY9Uy<MktNA|4kAh5&ex4Few$W
z+TEyA9J{{eB`_|!`#Q{mTAmU89UJerOixq1ANJV!zxLq;0NnrO=swocW`_^KPQB8x
zKd$A^|5Nu@15J0>(c0IryZnJfElH(!XmC(F9rxlHD@TtuL}?S9G81L&%Vb9wsDwm^
ze6dDPjk6fUPn2krF|mUZ!zFLyj-NZfuNP8jQ}8jM8+4XLHlb5y@S}(kk_|b|CyTf`
zkp|EPGwFempTalYH}VVo>Jj+^d=<DxZow}%^FZRD+ww#1;Qq=^t=d9dc6RM=9B0a?
zr%V|~AsmBng4hwN)3vIdUT7j2EA=r92)xC(&tj~z7!TE`j+<&!C%e|DP6=yN4MN93
zf@&uZ(y<Fufm)1wFrkBS*%L0q?GjKf6p2)<ASwua(F)wdw3hC~?J>M1g==>%FX)5$
z?1v}AJ09T4a56a=HYur&V!U9X7k~z_I9zuc7YUz2%pM8YTU2eF&k}wIB?k5L2qx5o
zh5ih$6@B5m0+)O)G=|rKV$7M?DW^hB27}uOhG?A8OymZ#HPf&|0CH7<i>e0m$$Azw
zqXL&G4M~!5X(|S7;&*x)^kR}8*^x#|R#P$9ncjgiMV3=d1=)p0Sw)&?*x@kXBrq8r
z*A)1D=V?!YjDV#kFO3Gk?B#o)2PZxt=UX051OFMDj!9AAhV3AOMBi41z_QLcb(m<3
z?XqJZkqbq<43uqj$l2neHr#8p5PL^%ZU&4@MQ!3d_gYDm!-m#{^(jM@+0W6DhAlex
z*e+z#*8<0400Y7M8$wf)5Vu*QEuMa4i4fpo5Ku*q0V;4Wh^=skIp#SHCUp2gdOmOb
zGiR8+#h37g+CX#+?w;*^)_m!Kxmt|v22|hKtDw6wKF9~tC|D6phm`6mHaGktn1!63
ztZ|<CyUI(tql;`8%1b=ayA7R2Y#G$WcO*qnTS&toy`7G?QNgYUFDHqtD2CC%{1)7$
ziNt-O@Y_ru+!(T`?DPn{2f$Yv&eCUzLb0>w&N%m&-ttG%LLOmhGQSt3<&LvAfM;Cj
za3W&3LrCO+&%o$$o-2;rkq>hRKg=EcFxU5Ct}}YPT*>>=UDS>eWk*R2UxGWLCD6Cy
z0)>L`T5eW=UUXRZ?(8}kidxe&0P@iMK>NceP1FKO$Oc<!B}_F~p4(yQoUx7HY{P9u
zeko;6TT<-2CIs0Om)?P@$DyiL^VsRVgxv|*j7ni$dJhksJxW1Em#I8Pii}+iQecOM
z{q`59@VdP;#&(`%&Z2>286)NSC#Btx^zRGtNG9HH_QWH2E}%tJrB$<GxER%ONdXef
zO$t-V$O%1kt8yYvHfJ&N=|hUhC~e}N+dlk?&TmY%kTJ-iRn4n6q2#xkS8u`BMt*m4
zva55)^0WzU-pBSSlX`;)4#Xs7+D%#;-Aubldq8FT85D_n6K**o2ucf@aS-0Jcx+oZ
zQLC;%X0r5YLJ}=6$1}-(+qPzJ=xKXjn1Q)?NE>n|yhdK~BJN~+qS2P938>@GrwjrM
zxluBcc1=uVryyIn(A>WrO<@jT=SQ$RM0B_BUu4F2;vgA4)VlM_qqQp9NYon~A99t$
z(U#AFMG0xaZfro0hb-)PK7~b=P;96sjs#CeODNWbIT%XT1|9%+%KCxC$A<Ds_IesX
z|C*MF1e7&dB%pouLg7OrQ=_gRbH2gmY95F&RMvMSsAv?Vx|_x^{}2l1XdE*S(=+gb
z1gMi)0O|Yw;Kzte)$M5Zgd7`nfOa2s&~e6cYovk}L`_jonF7u&vWy*RCrg$d5;OR#
zict{~v@DQYlZn;m5LUdu6pFWFEQEp<azBQU4McA2FL;D_9r6nX%C|}`Z^n5eFFA;N
z|8}Xo<a>0d`mimh&@deff>BOx*GO>lAiQSjaJAzOPSCFi$8iT|#J}IcnRU@w&)9_>
zavItC@F0@$`~&)j8O2X13n>b(;wRO!lhyn=ZeMoxslT1$dOHqRKW)In)oOM#Oieb*
zm!g^`;Ki}^kR(^t+|J^9CNBwv+;XHrS$x|HDJQ`w$6sI~*MeYSpp1uEFQ{&-DZfta
z>4om*fk;6xtf~{WyRk}m%|ap5!bC<9BQKO#=DYwmm?LpEP)r>^9q4i9dmJ4T{xdzf
zbpMhFUAZrr66mz(qSWkjr*Zp`+{D^%?C}^VCk%ujWNrcsPlJ%1d%@Y{Sx-ceA#Kc&
zxbRWDq|?tZRs3z*zScqk%k-eAU>Ero7d87fL}On{GTL|uOx3tl$F!|3ZXS##LxMKZ
z(2EJhk<HlC2qhZl-KbL`*TCis4GSbL)6<U+WhSuTVO^-{VG!Ab1~Iz@m{5LkGpY!t
z&R;rXuynw|JFw<jJQ3w6U2)_cD6FF@SV$Il8iuipPN3M;pX4}@O)MmLLO}D5KSQ$Y
zh<fB&G7X)?a?~s{;SC{hMM^#d5spZfm%K$z*inRdvnMQ&h+z=ha0X?92JhnClf>HT
z$`%kYq21WzUr7#5Vx+NcVOSs{OUJNRyW?d!V|Z8~9;RuHVuJ)m>B9bWoZnCLePlmP
z0ip-Moi_^>^uU`1?Bq7Qx7QgdwhfR83%s=%9Zw>6;Xf6)KOEg12@+6BduR*_XCCj(
zBh&a}(ZO1)>V)CB$NbLcXji~)7<fs#;-cm{p1{6{ESqxzsi|rvcURTywbLYdoe5n_
zTmvVdQ;^xxDVgdxhV{0pnhjka>#i$w=gL<9MnJVaY(>;RUW7(9h4%rlx<Q-@vs2qK
zN?5wX9P@{M_Hp^*PLR3$NNo>;6o~J}YM?(Yde>z0f<1QB=@YuL1HqEDy-NzoZUnp8
zSx8SKxnzff{kWq^*Dwij@>ALMV5T(9;2fZN$9-W(>oC4GIxTFPGgn>`K<@33G002%
z;+3a<?3YK8+!PSKNrq}^S3BM_b&zooqmqKICZ3J9R&^S8Y-&~KUV0HUp|P=6bs1l!
zu&7vzu^F7B1!yU@fQJPxRjb8luT`~8WXSpV0(3N;H|grhm8f_bUcIOaIUB;oS&EwF
zC4Gc=u;WF)Y-S7TtiVO+?7fH=dT3=F-4i&5w=L9@iFqj|_kNq)anTdn&}xFO?);Is
zbOGMsv^QNN=d=nwQ^s#cV|3*LhueXh(M`2bYY*BiO}o%=2-Wk)4R}_8l}<riB*rn4
z6ZTWOuoRg^r7*hJs$^uJJw;(H4d#%9o6q2RooWs5e=Eoa8uYk^y?h=QeQ$o=SybKz
zFI?MY#L6+Q;Z7Mm(cV@#(+vg}{POW8W*S7K9VLAIjstFAGSYYjgSU9w<lj~yRo}gP
zPpz@4&f%`CA2|*`p*9HgQyB*|Ki&Zy$)l}tc%W*>D4?O5QMCoAP8(dR>_Uj!`R!xU
zR*4Iu>N3PFs&i0ef%xCi`+o=+XyuLM+Wed2dW3_LO5}A>vrXw>j^i+e=Z;|*Ii?^9
zrWUU|@Vke_%`y?$O9(k@=x<f6)u`F!KLZ<#GA3RM&>2u10v#8lS{rSJkK(=fa#yr2
z{J8O|F3h;rC}&iD7GFlCwD>lCg?sZxr9m*0aj_IB!5IQND|KPX@oSCloJy%P7bz75
zHj)_*E#lkqMV5HGK`~vvbuUy?tu=b^9Uy$0E-V26?6s&G3q$~mNwHh{9*15Nl=<EW
zL%wxl{Mvwfg}ngu(W|yAI%cA3Eg~u3dNZKiQnYW8t3lWlMvm2Zu%aUXSZ9%1Qj|K&
zhXp`xx^GCOeCq)nGgb1QAVC?WV-lcfjbtH|MU69F1<xkrn2vFYwWyS_Ak#|PA10Vm
z$E;M*P{mq8zT{hfiH24wt*|5j_>i-sb>RuJyjpX7D_(?wT$uXcn50OdTWo-1DIJbC
zi&b?N)d}GD-Lv7UM)|54LOfJettDb}PRCF>rcOdAQo0|6NM7=c5bvMZtuj$qe4G3=
zZvm<Z)#WHl{2_~KC!=b$?j(R6m|0ZyOoE+JHFBy}v#MSwNH`=q6Hslg73;$HT8aRr
z(_jSEGpOYQs$r1R%2JeN2ah+(x7LNn?_ByUV?6gG_8$;`SyY8QMDi<+Hpa&47%yFT
zRe?C5%61hR83bhqQ;%PfZ>@>47*ANmQAZoqPCGl~SomIf3El^ZwZ^&V1fgZ<<Qo0V
zmBO%uR*RPfWJqC&ud~SM;S6AGlv?7im&KpfS%S+{_7$pY&(#53Q?<fsY(CC1FSV^m
zX@(xG5z(LNK4Xe18Py>s<#d>;O}@$tsav#<iEk^bdafFp0(E3S3}ix>ip_JWQ8mx8
zy~wCoByPA$k+0B2Qi9&c?w&F4m9I!>w)K{8O;E_URtD^45`2#~mU#;ymr3x}F=C-v
z#R_#6SY1_8px43a$1m!bgSz9a1w{g-I{rkyKVz&D?Uio@ekrZ8an^`Q&LY4b@Xb4q
z_#wfqaon`yjZlrL8xSuA4%4!sDyi=1d?m~$+aC}IegZa92Q$I+;z=x>G~ibFP1>D{
zZ;gu!NU_^~*MYVv1|>rzrBYt{3oMwNs?DrACxot0v|ihQyFnZtl1&R!H5!GEOHaT6
zTE=ZgnPKt0za)LhN-$-758+kWX|}WIp5T2FEtA`3W?j_0V_ABKsWDzl<zHu+Ot!Jf
z+alol!-}k7Ql#6^#10@<xh}jqU&x%6Z@sNWcbfQa?7{sUCm>M)aX3KNbh6$htojJY
zJ)0sG7~2S7<Axc|wbrk^1h>_oD_!{thyg?Fy7HBLtd&tIw2aXX+)|B1-xl&%M&<2H
zL-H~Z%T8E743<A!F9T}Lm9AB+h&gQ?UZXdmFY@8f#7M<BbHrRLJ>KZ1YMWrZhz1cT
zA9g?$=tq!2oqUU5Gzg;3{FkEOW%!&UFF6TwwCp88adiu@<M{kGs8bct0_!{|5PSY8
zXb!zr2UJ<&oB4jx7;tv%->G9LS2gopbMY<4-=HOQUM=6sj|FgeNeDPJp|*k!JGw$-
zpgUs-VB5<0Aa`K?m%Ste>#>zGt&j=jrFZeV8Xm#0qwoe_Mk|%nbO~b6RxU5;A;fLH
z{~p_d!~Yew2Lo;c-Qy8gKxcre4KWmr6<Soy4jy<rdEosIJm`7&zvRJX<bmv8craP;
zKjlG-b5!<@j@KRPG^!S}s>L}f`~RE=|HH0A|I4lpG6}6t3i9L&sMiG~3IlEf_~MJ)
zGF&PXKt~){D29Go_aH-=+JGt`ZF7N$Q8hoSY6gDEx2Cj~JH((^)q-jQ+Kh&o)k2lY
zq$^~;#&f!`qE<^ni_t5Q9n{841e4H`1a+Dz<C{&jZRFy=tuJt{Rab%24KzyyPDR4Y
z3Y>}$FH)MDW&G^aFnvn+so4hZIzLZgg0`72xF!bN#JOuRwjH-YH*Jdiq4KRoZH6Ax
z0%{4F+B(SOb?|Xf@HoxIw;3-K*C2x}DPTsoRoN8(4Vc;k{{jZa#2mbP6c#k1Ae+9%
zZEdEGhYoPb8Vp^ak}iP7Aqq4N_9b=nCI5S0GybhF5MMjv$#cWL!Vfg12`8J%ZuC&I
z8}B3O?Nm~gO<G)Ihh9O+L&wI~k;)dHmW29{ZhM^S#m#EGGnr_J+b#t?n#An2?%IT}
zi;TSeb<#FB%8jz_s>i$DGEBQtu9UTCQ?1JG3d6nqHPkL@{2Ctq4bV2Vz7mXKy$L@A
z<lJl%xxkFKpV-Pj(fahm#ikhdndI9fl!Qv+`@0PwT544-_)<4OYE|o_R^p}Ql~Lpk
z7J^b=HM<00Ff9PZ+%8z^32OuU+CA_Mm>Szq&e%e>$XhX2f<)P;fMp2p5!$>lFCA+_
zIXeR*NCF21zChL0S@g8JNC{a7+VVreE-=VI<2c#HT8CHh?AlQGPwZli7i=Ps);)g#
zC(5ctcg>CcfEyAdN0m*2A|L@<E&q8`3kI1bzI9qal@qc<5T<m391m7lYX|J}4<SZ5
zpoyWSL#8~ONVax>a{WgnTa6dWs*7vd07Y}79DtcC#Z@*jDEo7L1@h$*F}@O8#P*dh
zzmHcTLAt<3D98mdu2r>8BWGhRxQ0UaB1hlu&c2&F`ethXsqbc(J7iEoMBxEZEN<pT
zS27(%uny=fCGpenMX7a$ZZkp0*z=XB5t&-6cRcnT0M3}Z&ipB<M2#LDXS`umfya!m
z1EcI*R^!=0qO}yu6V+_oZM-mplE=|LW=>#q<BXSC?Ol|c+9p4AL14j;KZnB<!2;13
z!og^DjErA~VA@*Z>(P&X)f)u`Ok<dOZTW-vL9Vaq7$j&ba}A6VM&farOg1p1^1zCT
zuLpIGYtLo0LB-J))oD=qaRMC^B@DO;sw`50sv_SThF*kNk3}U{)ZFwg8tbKBQiL6C
zLWj_}fgj`)nuiN(pPEiW19n`SgWfGDG{rUTgx4a%YZ2J$@~sV`17Iq)R@sE%2jr`|
zkg;{R4)T&d#8IdN9TKf$>R~CWx~PDNbtl1)*?Y9<_SfC^>p%&pS_G<Q@FSc4Ktv<E
zP)0Q`89ch~;IaK3-Rz$_p=&p*?48jCaR4kq9C!h-O+t9C4tQSw2(Ryd4R4vb(xb}e
zT3mCF2pm6rAO=zZhu>B3$nfdD@~!owLn@gAj`8xN6(ls{fQy>Rln5_smA}%Ab~DJ6
z3uq?fc@nQ-%a^P~*=fYLf>0zMQ;eMP950`|epa&;Ee^$2dWUue<z}2>8(VH7Fizk;
z18)5gl?ZFWIfq*0j4ass)5r^Zb2_!}jqO3mPqPbJiORdo>RUUs<O$B$2xXXizSW^6
zjomRhhOd2^9o0${<iq#a$f@1qDpJr{MM^uVNPCBhv{e6-2CzF+B-q2nHArx#8pX0v
zaFi_SLW{tmQ{0>or5$V@@sALtsL#wb?I7SfODLiVQmu;=QapK$u53405Nv)rvNyO2
zramnEFaSu{?T`=|gq;Se4Y>nQc=vz+vxFiPz!8=?$SNZoV_{VcuSqic>&h_$=6YL!
z7zq+)w9}_}vqjCyZxd3&Qd6D_88RVLXTB+re+vQ#5-jD<ay<38CRc68cR>d1)mAl0
z8F49j6}XD=#AX#(VZdIIBn!uVbHJ_4N=b=S8dZVmD%Llog|s2B52uDyRbU(bu6?Zp
zn-*$^cdaFjEpt@^Y9&~{8ZycK7*F|@N;R907w2Pb*Brn4ATviclMUHuSd0y_$F_TA
zw5!33JppTrIS_ZPJ8E~srJMC4Z}BHNej>xM{R=SheQT`8rg3CL4*=r}Q0&beWM3+T
zv(k~R>~SEh<J{smh>lG%bFwABn<Md@fC9a#;%N7f{bin4afjZGKSio)@<mHbVy@$C
zLVjsyl7X@TOulb=cqsnf2<@h2eQ_gnpV-_^(ZSX+N;P{IGCV5j?#vivWbm*tTBD2^
z;}02KHC(;TlNsZu*0KXhBdLL6#(1+Ea5v>RGhNIqmNI!Vd|$w97ntiQX0`$#bF;mx
z4;?y$KW1Wi&N5wWSuC~W$t<%KjM(2Y+PikN5__o~O@4$Zy&OL+qkZw`MBHje<GTx-
zEgWA9_t;On<4>7bM*EL5N|`8#_}QMqyXz1cNB7VsT4{ej>*7J?doBf%AMqz}Zhtm2
zHJ}oE=by>sRS!@HkyZd?o#-!CPu8;CB{&$|$nFkE%G5Z3DQb2mpPiai&3LZeim${{
zlR2McU^Zl}CQo_cCjMsv)RT$Z4<vDv@DC9;b9-ar!r^$ly&Ly%@g;l6TIiu=14$Dx
zat9`1H&~11T2|^tG9lv^^43accsMh()?9h{xeXw4S6aeb7ItNRu4)obX1hq7%@mA}
z1KlZ{&oo501IFY?yhI4&mgadEH(@*wKS~88KfgJkj@ef#U@kB~Ua`9R5P3f{F#&A{
z{TEuYk<ID>zZ6gm%<n1q2EX`jv=~Xf4aY{yk#;~tg+;gxckiol*B#G(4DI?&03e+s
za+huyh+j~mWTr`(Tp8XR3ks+G{pms0;RTHBdkf%biIbU^d@rVqUCH{u#C$Jh@hUqv
z^?RwUI(-%SVJF}a{sK%$Wjc5R@Dwi+V@E)R$uee&ENrsWC?U92YbNhl2Wy1Q-4Ov*
zYndRmj1gPr$&zO56;p?ByarCc39ng=oEha1MzsT5^S%}sjU|JvRkac9RZ>PuKwDIo
zZ9SQUW=6<RA^X8jrYhxYs+g*3CW8$D^RNwt7#!1d3qg@59(1_6yly34g$G$rG`jBE
zhS>EfN9O63O1#{q<{8MarPt&o0Z4<$iR7?Ik3S=&#~*7l;OS^f=^c3qzTAK%3Q{7V
zwEPRsN02BF5VmnpIevkNxYtTBhM{~XpY=?wWTzv`*1J|kB8(?yS>nZ(K#VX6FoCVF
zVz1VCU+Y_I$<VzmMui1PnIH?P+=F+ILynp43i*e7$D6C90~*FhSti>pi`pV_7k$a4
zA1QKr%(SD8q>vz<;Vt=te#G1Jn>edhYMCUqbYJl*p5rqKisBj^PVrt+?^fP<=BJsd
zDb-}hh5WRP8mPe!j+PU`B@m(|JFc3fJTiM6^U=|oiN%+05I|VyX}sm?#z5De7%grm
z5S5Hp%(%cjvIrS3h+8I$Ngs|5Qmbnv$UkJRV~X)FnIo;t%M_fvWF3;@nsuu}i7%PA
zyhs(lfCDv-WyUJ>NC{Cj;g<su&_IE;E<{*-XwWR<E#!+Ci@Hf1UQ-~lEJXF{t%@VB
zQ+O8Ju(kQRwDfGzr%VI0Ok8@64l*zH<&1-fWLn27&0(C;BMNxIbD)KM8QrF^H^|TS
zh{FlKG=U59vI>7@hTM@veFO7*8`2^?PEjBUkMqv+V5mq!oKZI0ohrF8ggIJ#k&^?D
z?&Gmm-T;{VQlJ;;QKo7j1r}I9oka+`23@^-Vrz^*3Ujr(lRhKA_z`j}AWl(rPsY%7
zzSfvJU&vIHbrIdN1lI^CMp_#sM#(C^N2u4U9H|mzwks<zVstks*jM<$JgP2NPi6=|
zHL&i<@U2LI%0w$*y2(rMbyH@M)cU0_@P@e?aMOI<x2j!Kbwws>34f3u#JGl6FX<gN
zQ(Vwp0}I|Qfn$v@&OzKPGsR$N@zNRoAnzEHDP<NZtP6a@|AeM4q-iQ?n#gYLXc|qL
zri#HMDy}6>|AeM)<%nZ=9b!k1Ag8RP9zmVy5pdkn!?H-q-@ffs&>af8TNXG9i1IM+
z=fdI@v@4^+-nuaJ9dm<!ZOwKG@)=N!cD%V9@PJX6Z}?VmqN=MhQG3Xp@S{dpmlQ=F
zj9O~0l*Wc1rCXR9gSe_u#>;3TG*fausDSY0J-i?TZ1zR<9<smm4s&4XFzZ;cRV}Wi
zkWChKTa5{I5?3q0bZs=@p@c|mm2{HvB(}DeBK<9aJPGo>Kw81puabt><+b8Zq710A
zsAMZ<f_jlM?g0la?#x(mK(%#ZKdEJ`*dn(sN5SGrz<zU|Tv-TZ;uI^Aq}HiDr4<O)
z(q%jLA8@?*5*m{*iy5^q5KrxEEq>S<K0_*JBt0rWD8^(?FY)(=jv0CQUf_~9bJO}U
zDyY0sR&kdVUBavL70d`I7u^cjYZ(EE*<{v4Y3*3@%YT+0R`D{c*aU83w6v;G9I%(n
zE>V?fBt;9E7OS{>I2vZY`9?H7jS;4$0?HaIz&8=W7>|m^diTN)<cBd?{@&q#8r>`}
ziJ2DWu&rmnFX3n8uk5Ax->qO;Y_0=wgrXMf;O^Gth+#OJ_m!;=bipGGn7RcVu($#Q
z+al{rQj6R+&zd7;{tUmFztk#oHmb{CKu{U*Z>aRep7SPl>Jip%@4Q|7dll9H*q@ta
zh=U?Pj}_gm-X?2oCwlPoe@tJOFWE%2qBkm{$y_}#aA+5PJqI<S9WyL=+L+*AVoFQV
zRB2;UWUw&bjUq;gyz~{aM%iJl!Cgg_-7=<)$(J!NOVQ#;v&st0OIC>~TBS{}&Qowx
zSBX<n0RmXNA%zi8OT*qHKfNM`Qmuv!B0m-*g=Pi;6SGRneg_$hc$H<04BASe0TeOA
zSJ}$RFL&Df2&Oh41a9Co>~#W6nnJRjMpOd%#lL5nXg@U*?W<<@J1T*Jzzv+5ix$t5
z=l_mJy_o-YhwqUP20`$J@aiIpx&`4fgwqh}Ap8fydI)bX;@iY7q^NxmwnK<01RD_Y
zr$d+oVKjv2A^1a3KoCN(L!FBdjzQQ5;RgtxKv)A|1%zA(S_p9vegl|;;oA>_7yhs+
zgj<leyMUq&KsX1X6@m=%e6cQskr38Gm<6E_!YdG7gYXFi7Q)X9dAu9*DXJx(kLMXF
zY8}8?4Z#E<4?;SGsSw6Pcmcv-2)(c`2zQ`93*j_`Itbe!tcS1~!cquA2s#MUAw)q4
zg3upAR|xeqnCTEUKv)Z5C4|Kg(jjOe#6TDhVGx8~5WFB<hwvNykY8x3?M#fK>LCou
zf0!?XI<2|TzEj?R0AFeW&^7O2ULo)y0_sI{%8LblOR;UIyxAxhxld%~|8$v2qcr-A
ztSnVw#vCJ^5luZs#b#!w^U1W?S&*ZNQ%#&0GAt-PD~oCtXfo(A#=QKTJnAh$d`5nL
z+T4uMIfc<wx^Q8BZeC_Kt&6563lnnF)98#ufRqFDnIagMU67WQiFs2p=0MN4sj(S!
zOxE0SbjExbH$cr!cO)G^qp4fimm_m(Mj;(d^$|cTby{9VHchRjVlwk{)95++js}!U
zI5jUVJ3kA$ab!}|w*qxW#=M7h1PEh7#v%<3wWz*A=z~X7odet(jHQ}I`2-I-FLMs$
zMPeRbhai83VEYi_50na!&rHk8nM?iIX<R(c48<1#k;Y}GXXXI}7(v?nXllM-LS|Oh
z)Xe!Ac~p1AgO36S#^}@XCXv<`kp|Io=*$8qG4EhbY))Rr+`JrPcDf_?sfXj>yQRvg
zn2dZnFK3aX#(w}y<foG--w9yA^utjE3N`wih0anbj(K8QzN2q-POcH=dY)inPFgz8
z{%GnG!Amg697J}sQ3q_Nb_=Ft%*_Pi<&DWo%g=|LEl3-68i)nJL>vhPFC&%4WMu*$
zN%~m_)yL#yfn2#!qX9~`qt$?iBTLN5OQ&KUa)YEzFixF=gLye~pw9%2YKlW#sij1k
z$7JQ?XHc(@cx+}~J{_Bhf=wMIco_3gpkfGXa`F~IzD%h3$NX|(7LYV6BOBt+QDgEl
zK!iG%zd_39q#^x#Aq?yXxk{}C%FRj3a;B%|bd+eQn4J6=0Q6Y?O5x0`IoJdBJ`Xh)
zdw30Ub8<7X!Mw(DGOm}9BHElR<NS<q*}9y(`Dt`!4xqRiYAnpl1SwwyF#wfLQRj(#
z<^fBmF3JTGSfL~<A0`NuBLpJiw5MhX#%ATrPRjy}=K(bxspzb%oH<moBNdyM0dq^|
zNF`)vk-9S->A1A)^eiZU&oOGO{5Buw!SE8G$}Y&v%gLS(RMw{DW%Al)@<aaScc$%H
zo(l7GGoz`s6oQ<XIXf>cZxPj<Xa(ebdU_tnGw4ksr;hSy{M~n?P)1%p(1oJ9A!k&1
zc{zFUIq8698{z^{V^#)Ie^T0fl#LHDH+qbkqUKSm!c5Wu0&jm<n;$0B{ffv@&iwh1
zF)=e6va$tn8ELsZmGmUe5Bzt*6dK625XMKP3bmPew9!GI8P1f0LK9R|CaIiimx=``
z&&r@PUYczn`g1&Jfh>qQ(_<XvF=If7IV3YSC!2PvoM@_+a6Y3Tb56#XwA_43>6G6w
z(bQ}hA&q+DB_q_7Ahj?BnZO!#4$Q-lVbRn^gfMy@KS`-v)I&tqWM$6HM%~~@JId9V
z?x;OBW}IUvp{OG!8#D42Y16WdNWJJ7ZM521OdjWN^sik^QJsI<9@EaCR)q{x1!-cZ
z{j&-3<oW;bc?klVe)vN8ZxHb3(WXGiflvTp34~$@`yiZ!fVa}|3}-k5{H?EI2ya99
z0K)qs(4+Vp2M|2K_wEXz5B?nm2m>TwyMUAY4umZby17zRJOmSj?;%t|xCtRtN>TXx
z0JIy}Yq<CCK~Y%{-iDx-LI0j$_ds|L!p9IcLD&qz-wRfAFMdoX6DQ~N{;|uIl;SZ@
z`MI}#S>)%d{yFGV>-)otFKv<f{S<q0N8GD{1GcRjRQc;?{?8}pt-8E;W5mD*lA>#K
z{;IG2%cn@sjb2vyz-p82e`|1J(6QPB3;eIi=DvD<MBA$$1%4)Ks|}4yS^JfCuSf3a
z!^<z$%t$Sqv+S4Z&(FDSnmuh=)zPXUl0%<$-S&+*C$X|e@*gWd96R>Q4>$E4-}Kw)
zJ^Q{@PhS+c?#A`)gRZ~v@w;=T@3|H}bieNAz0kz}tUMq7`G&0n%Co;po_6T{?|Q!d
zOW?XU=A@@KyfVY}x2T}SZ_$(fko-RFR;c1s*(+ZiEIqLJ(yvFq+x}j7L$4XHe64!w
z>XbEEJ5zU6k9_x|<iqQmr`Xr)F3eFj-mFLqdeFUj>apHQW7ll7uYX-TvM|t?Io<Wh
zN3zCWx}1!7_q#XG%`#e-F2BCM``a&Pw&oYa2K-f=a&D=4(C=THUYQX#<SSX@t&JPz
zjbD?|bBDWa%;3STC#H|tAwH{*4=Vlgx&AwDys>dw-Y%ub-fca4968kYkoCFJlxMCU
za8Lav>RjB_K36_S^*B4dX$}|hjN;N8p5uG|^=otM$wehkH@}!w<&jd>b=UmU6Du;0
zKCcvwF8SPZbiji*{`jWLYQKs>=95z*4hO`Pe>v~myi@Pj{8qW$?_zAxX-UevF57(6
z!{ffVV>nqpl=>#|>`lM*8h69IypJm8231xptm<+&cYJ#3^XYTPbeZZmz`bG6hEGNe
z>oqQijoUbV_u`+W=9wX%nx-`GFxTZ;FC=Tc_x`x3;zZfuiJyElXK|h<eRs^uu3N&V
zg?}(_V$&<n{^#uxzxDob=(#_8Cxt99M|JD^+OAZ0Px<%?)qy@gj|*%a8k6U)`L^q<
z6JNh`V$bIvW+~>q*(BTDYt^J@-uZlpDE~A1xA7A#Zo^F7N^ag6;C?0N_-kteGlCkF
zqdvVmz1L6SH@=?Wo93epdYeu?%dX$GX_P&;G(vhkQ95*gv5&{0X9l}8`wsD(y*_?q
z<K~NBPZLb}!#<kcCSLwyX7H$>q<3oyu71%lq3`6_MF;1HWo1M>pAxWO#^7-;=e{$3
z@%XC9%iUA@`;;DS7(JofW5Etlx8)~p*nc=K8a4LWtA1NgtsZvSI(6xaLwkCD>?-@_
z>0h52JU(~pC$qf2_+iP=eOIfGy*Fxf=+)B7wR2CXgLCNDS9fbj4(;-X`0X945q&(D
z9{%hiz5U9mspY5B=c~NDEa9(ycK6Sl)ywCf)kzPAC(SMW^7IPZkcdwg&F(dG#0y#1
z*ZN->|M?}$@S|?)-}Zba*8bVEJ+E!J>~l=hw^rM<V@_e!1mBdE>y{OMnmy}$V0^{+
zQ7bM~39?(x&GLP2OXG@|>3;+^WCsnF9^1Uqdi>LJ{|QwS(q1h1eUSgBvE$C3vF(WT
z{p~H?%HrVxs_{KGduL8q9x`4NSe856I_Yqi&57dz-%u~F*mi1m*AxCf6>Y6J6Z~Ub
z(DoAl14H+E-T2@g+jl#9moGnVf8E`G)hY>_y77#Dir>JOKYXq#sm1-wdyG}Q-n?KT
zO`DfSu5KJ$*qru!`2pF*iI%UouX}xPa=2^L2!kS4d&*Wczt?Qpyn>W_y)Et`ubOYy
z$+r$%y*d2S_}wWVyX^a;?O?8d$d<k~Uw`AxSs{a6evS9<F}n8I32zJ;*K^a(?tx#<
zis@5xJNUEl#|;@H57!#!n{$WS2Jb7_{;5QEeb>Bm<EeXJb=&%YG4HswG-!jza|@;)
zs6X}BD*4ARyui-CT)2#;UR-vkd4J2%3m2aH^@Xm!12iYC@_c#&qdYYA^>_bzd-%Wu
zyL+ex7yIwF`&!<K_C2!chXZQg$PufvznQdR$C;@Eu2(+DHum@WpmO-;XoJtx<mdkQ
zv7ut<#;_ynd+rP#(Q>)Ra#5iwe$us2(@~k%$1f~iToU?mdUHUM^xEvLclw;|_3GT?
z9))4MKS)_QGx>1Makq&_8q;ruE(!Z+XWMz-Gm%Sf%(=Ssg`vN{xBbEF_ka8?`^v0k
zy|<XTO9y7I-!p1ZuRhDXn&e)KU$E!&dg0=G-RrZitr>Or)6@~Q4WInya;vNK&%1kX
zxo^u~zgi&AVGDXnRQFdNX7BXKIJ9tBfnQK*bd-BV<U416-EKNoG<Dkc>Fmh&-x_wU
zSLRm(c3fSr-PdPUg6c$C)8amvJ#Q}zi_b59@Zp0?FUe-UID60B7t_uUJ$TW!yxC_*
zx@pe`nOF8cQ>@*8rK<g-qQ#SLe0<@^%wJwQdgsC2W$C|u{k`C?z}Ppc3Q8CJcHwHT
zj~DA&I9K0;!9N{P-1Gf-llNa^rtevG>c}tsavv-=Hox@BwfBmu`lg-qntx;HF^`qA
zrZMLw2lu|T<bm?uvYOXU&zvR?iRGS+UAJNW={IYSn4Y)Xi#a{N_eViPb><tU1WlX!
z`N-+RXS9uN3%N65WzOifshR&NVzgiV`R1CJ&HL_rv|Hu3|Hk``3ya@1Ml~7h>{m{e
z{mjj+;*NVSEIIkUsq)utd)${AZ-u8VI@6G{XI#sSr}vzn<^NV*!4l5bc0VL<`?jC%
zE>EAsa(&KxwK28u<Cguu>{)$q*(V9&oq=bUKIrYR6*5-u8oy7iDBUx-fB)I@7e$|*
z#BA(7ql<@+$o-X+x+AgD?fxEytM|o!bhC?JFl{c)@j3G0xw`g4m50uS&DBl1RehuP
ze>^Ase*S~MTt}Q+aAe~}?^>TQABpP+E&Eo!S2Fcp^jn&SE?*z7>$mZp%<Aqv-n;$1
z<@wOG2^+1F4?So18}a+h=9_&tUkhkhGrO?h_Og(f+nbE>C3nxb|32QY$CdQ~<ClD{
zjqLW!=CupzwnS|CabHl_Q%7bOBxEkyw#7DR5SQNf2l;13%jKCLyAJL@EbHT;0}B4k
zKHDQGtYWLz5z!05ZKvC(7|!q1FC%s-25?oTXXYxw9M=Vc!JL<d4hx}#fD%!zl$`2E
zy+|#iIEoW+BF>eQbN#p%xn+2v3ZF^wV+8rSLgCq;uZL^!?;b%odmO%B0UuM^*$U&h
zhe`5Rs;K0D>tCq{Z!aTHIVUGC&zMUGDV5;t=VyY0rJR#y%+DC5RL)LISEgs?&r?Ee
z9k@M88n;e#&?p04BPwY6IQ)xIRFG<#3cuA8Mn{jCFo04<3{1yA(KIeQpH2gVT?z62
z1M~Z19sW-_CFkRYQZl-!$#Zz82+HC$N)2=Xh~{PLARRMKqn;R@L}lkF=V#0ZA5RIc
zs}lUT9Ah3hTGUikJn4TbeBqy%8knyPR1SoEeTFih_&yow&?Ji=W;UIc3BZln`NrJb
zoIEIVpQBGhVu05-SecuZk(Li**XMx4tW1M}>tLkd+&CaW2NPVS)P&$*YJ$XtnjjET
z6GUPvFcbV~=vxy<_MK>bY+la%aWNA!^JzXWK8-kQnz+$<;I4wll^09kVH+fZ!>NhO
z$ikggDjqyEEJL3~L3N?JQutDgE9FMHQ0|n3@}R_&45Y@B5>j3uKXND+L7oK4U7@BM
z)bfB*IkfFY^`Lr#Yz?3Ss1Pa~JU|UKhsvkSR0UN{U801Y9j8u@!0}J$_$PAw6FdG%
z9RDbQHiy&~I&eTdJ)!5G)Kk<@Dw;~8im56}ASf0HgaVO3EN~G>1u}s`;44rHf(226
zS&#SY?9bVc3mF%V49A9}!|{0l4P?-hCxyQyif3l{SK>0VbBuHK4q530S1Eb3=OY7j
z{OvX8Ws-VF;2aO0yWad~(mZ*dJWrk{&y(lL^W=H*Jb9ixPo5{wljq6v<azQud7eB^
zo+r<f=gITrdGb7Yo;**UC(o1T$@Ap-KkUJ~0geB1N8-uzZx7zzpzzL*39nH{QTV$R
zkCr1zr$N4uBCiKNRxTjrEB+zhO-xbi{?;F#zv*A{D<M`7AFS|U$+l{W8q|rejr%C7
zf2Wp%c>ED}fFgFH{V$J+$JAuvzwjSs!AE5!_Rf+26vy#j_>Uv+VE!GH%Zg49rn!fY
z{eN4!rLb_JymNZ_^4!j8uEkZ}xjsck<4CtqR3RYSQGX$M3Omxvwr$(CRo*GhFwAoK
zqvd!1oAO860|K4v7jE0OaG_k<k-p0;hseV;w{RKYXX=dq-GvL6{de@4#q*;hony~^
zG`;-Ma((WjY29P(9q@F{hol=R%Ju)JAcxQo!ix}=LEsuG0f%yqSD}>DLpe85|Mr;v
zU&(o4A^#FA$QBRI^%3AGLzF{yD8h3JzEKt^K7IGGG$}`B!Z(otyd}r;o}w1!BF{-L
z3m4{6?E-3DyP%(dTka)j1Lb6LUd-(B?*Z%VgSzdmUIPAxn^LJDR6<Q6pcD!|g?i*)
zRm1-+&zDNa71J>}^K*@KX7=2{%5m9qf|P%MfWblh9nhdTIrE`yAf!TuDFd@Ilz}+~
z8F~Eci0TO<u{RJ_N|{&p56bwm=jACtWfRD#JG8+U73XIJ(S@{Cb1?gCuwti2{fmRe
zm#-*{$F7zy*DmN%ZA_2ba-8}x_}HkYN53A_FR$%x()j;8bs|um5|g)Z?%Y=o`YlQr
zko80H=HQ?6-I@+(e02D!lLvYRrd9cj*MD<5{CVA_k!2OXU;Dw7@%97p{co?Aot*vp
z?-T6p)a}go=Bn<@abqHOBz-nvqzf}{_ebmB?0=$m@`6oX^XhJH?cp+S>d!St8*UGI
zE$Bk(7vpaPwoR|x`1Du%=-eNI7n@JHZ`>xU(`58{=Z)}Dg6Q6t4sL1>Z~yM#+Skwh
zDmaz-mhHV?%#zP9tlL%WaW*e>`VOWxyQt{TX+4jw`|k4l8|s5sR0O!ZnK*b?wdvUV
zTTe_Mw&bUg+t+(dSu<qyxgS1L7xWzGw`*?jucov8{Ucvy^STz^`dQMadtr6y<(ImS
zRc{%&cj&fD*1{_%Uax!gK6hc1km)*MMExk?H(&LA{%rZ|1+5oiw&tE2Is4wt!Z!|I
zWRE}Fw(+Y`TgEA?GHc7{7hXOw|I<zT7Jjl}g!eCbBUViAqK&RN_S#pU1!YRls^_iD
z_3piSsoU=}&fVIu_49T9J%4C<yZ-(+tY1>(537DC(y8h0Z3C9gp&z*3EgrK!S+ljo
zwEKherZ4V=?0UM(^)K%xZ7bh1?)eG6k?(nXe_OiPYkRNvxZqD_Z9Q}Ft3NNeJ*DVf
zrM)rq(1gPa!~1_#e{J)krY}BxZczC9-vx8>Gnzk&gYO7t4XWr?8@S@siO6FKe%G#_
zUs7rvwExaufnNo;SH`O=N`9SkW6Y{+34I1DODEkJwV-u{F@!z!{<P!Yl?!{X@p|P!
zOSelS9!lOPDQ^bUtX$qY`Du#WqsFVa+A-;4a<b{XoUExiIaxs&g&EhDcrKZGa`TOk
zCdRo8^S>}a(Iw-=x~YRaUEY!p?KZ6Xop*zSM+!XspL#cKnAf<}E3$rpr>m$8zt}}Z
zOY2|GFUl={Va9j2?tig<>mTv)?$b0sz1@%(Fkkc8HH+mb;po-uPHy_uLt|f+o%!~B
z(Vp|yDyRR|yt!fe%hxtHq`v&|oA1t9G;3A8`%_Z{7d9yZN8j`P>FTokf_L9+57hYe
zZ2$iK>B9%UxFfUl>r?ae`VBK1rfvRBosy84IHc#PQ8Mf9QDf$RSoOmR-L#oAFRgtc
z@A8U>gj(~jz5jYTa`CUl(%}({s*8K8+b@KF?KLsx-kUdM;lXd)E>;$<84<Bebo`mf
z_g-#Xv}VgQV?(%aZmj9I?e%+_cW=ng@4s~a))_;OruZLzHFW<Zro!j;V$ZuV`Vae0
zc(Z(5qRWfX<&n?#81%k)+gBHC-cP-H;SFVI|K`LG3a|7y*L<}sdeGiq+$R(yb04zH
zMx;!2U$CS(^n-}l6_$%f?CZLP4P11a-v8aW(eKXqqF4IVXI{K6`aa<EhN<Hcwq=i-
zTs!Hr%;0`8Gd>@&xcAqkqg+<1R*D91D?2i%{pLOHR!CL<eY;FP)HAb0v!0nXr152w
zwsJVL_pcdiL{!nUv&F&PHc_)&Pf*WOTSPCY@7<j%<%GLh_J|AhYg&$|?v1I8uGdU%
z4>_=ESSjbX&ULkL`ogzcY_T5Pl9m_U&aCvlz?Et3Tgx{H+o<5)e}3%Mqdk1AlzT1f
z0=G5!g8Vc0WkdB%>-zW(X-lae@VOhevHnu;fHvmn^F4omxp9~9-MI@Mc(rm~MZZ+e
zDNP=kVn4F3s2@9jPRSn0t<Nr%EPEzBe$)l7d-|aPrOW)&Ul_e;MAk1WmWDhP?m6md
zvzGcrVUMj?+I`7V_7~Te+r=p%QS)MF3?1LcKC#zI!S*4e?=E8R5By_!@D^99q2h%e
z1(RZaaof`xuV_zBo*6rHdHq!n)7+!y-(UUhtLKjU<_x^QqxXL%RDR#<cfTKsWWSz|
z{dkB-yY7Lg<$=eUJ*)1gRmQQ?1iOFQwq@N$>EeLkE0@-ND6*&7{%YCpu_;4%zn?rf
zY9ZB@Uw&i#@Sm4ltn2#J3!;eJj}_-7GV1Mjq&FXQHGUhF+)7OnjiNT1s!it4#{Z?S
zTTd<S;jtj(K(|4wgsCDwL4-%4ON+R1a%DB;rgp1yUG{9H+iFTKDiHlP=Iri)-%VTg
znOfiXy;Yxn*kWlGthu;_+A`om_SkH{&+>xoyM&9Aw;tI({e@d@FLiy-GH1=5q7l-=
z`QMk;%u&2|?){_RPHzv;A2xkXy-ckVZV~l$k%^8xy~gVAnk#%xAg8p{cmYH0s2tJn
z_ej$Y>YnMWV3Db};78L@DpKjGq&}{^?qNzb$qMDUy;i>!?9%Jfm_?)W7YZMAuS{y4
zaQx4nKfC=$TRdYjclFuP-EJm)s}&r}_kV4v_G{|svTkE<9DgpV^}@`uqc0wV$OFxs
z*8J&`746@DSr+-4_~p?fo^4B8(*Mpx(SxRui}&^YBxh%6?5fjm#_^%sOP?QEv{9IT
z_4hCL&$&`$pSseg*RR&m-@p7?z4y5##+}U{gwQ+fSC@QOc<(v8e^j4J$uy5auBQds
z%C082+#$2NH@+4&{(OTdTQqUZ1FcCAEC}wt+O1wNg%Yus`md93>UwFU?7gc;d}|kS
zZ`LjdPV+m`xV_IMHu_$_t+Kwg{jU1=8~e_M8zWv@m1D71gzO)=)N|yQuU!gU^WxYa
z)8DK2tNqhv`d`$&b$A_3axW}qw3xwSJYtqCW@ct)X0(_sv{;rbW@ct)W@cvo<b8LO
zO|rT9?|wHu&zv)jQ{7edtLk~Cr&WRIfy0H{E}>%zyTmIH7QRSS*x5CysR{j1qZn<h
zkD#)9nk3#n6ws1fCrSFg;FIVTN!N?(Cv6oR8CZXpgNyB*gZzOzNnYhcg(L6LDV{pg
zWQB?&R94uZh>-x_SgcPstuJgF<##tK+uCc`tt=GGyO)FXF`RosU#&xLhL(MIoQ|wH
zKwybR_{`SvC0BR$bDV!X3B>aV;b-6#9K@*r=UlYzI9_)-SKY6QOW;Po4`amikm|m_
z^ljZ%yc*kMjuKEIlmI4vKxt56yS>BzJTeeU1GevGll#7}Zg&b79Y~z;#R6xRqkg9u
z8I@ol3wT*-dnB|Yuat$CxNyEuf>q}xUFs&zo9DG?JVfcT?;y8)^MZ}Q6Xw18$!?;}
zgSVPIhp#m1$ghi+Cio*+^o?LE1&&}mOj9wQ0b{BLi8tSU5fO+Ur11T`jp9PtAnjO*
zZXl)9={@pwkRuWs&vE$V_jR4Jj%d^Fr9@*FjqAJz?)sR{P*wZtQAtUX#mPxAcir^<
zwTP&)w0DN7<5Tdm$dKw@QMq}kt}fl&f@kaAiC3MIyiUiwwvHy?q};*NUfSL}V^-}#
z9r*b+IJM5nwj`{9qxt;X0vvx9w8t=#6ZPRT(IMTWVo0{?wpSxmEaD(|C=SFypuV3p
zH1l*FWbK-`)namQFv1|N_%z~@3z@%&$1}gBc#iFM)WSU5O@9~jbPZH3iumo2xK}M6
zW2WV;Ft3HoVT2PNuylu&Sw=y--*`J%#&5|G=Z$osgIe*diWz2eK$KJB_jv~_*l#i|
z@}b5fR^S&9Di2^|&-w3_JeAE8$>`%l6X@7!v_?moCN;}i2B;q6>cAR{3mDahdkhQy
zn<vzbjf9&xPzO?il9M^GDR(MQT68kq=-!<RHI{dNubQ&~NwEAiHe=se{C^mA<5ZmV
zzEkD~<(K7wxzbv@G(h=~_`+hx<RyMb;%?3_vqUVW-HEKUKZCEF!hfak%OqHVd@|ez
z#*TY+u6)D;C05d)tQc^PHzvgOlBDa1_S8Z-Dl3EUzP`C!5mwBU`I@(~_tIGnv(}nP
zrID{dkhL{#sW|~YytSDTWYPc?G`Mt)68cDXYz(Q4W}xTKk6(0OSgr0qX~}O{y75-F
z#4O^AU}vXEU}pUQ>A7j+seYL|X~{#F5R`c=B!78+J!#0e#(&BU+Da2^=?tObi{3~F
zAOZ|wX~7TNYi10LW{o0ug6@Hoy=|qZOd@Wyk%nyUhO;2H^Y5gH?xgTC95dCo&5~~L
z@gD!;XoALv>1L3I@OOt!+RcMtv5{s^bFwDazR)H(_>h2q$;y$R7ce|z0HqKe1J!r}
zEuXfNlm4->cg&kt{DZ51fUJbYWH@36MT)<{2Kn7K*iHsi7>XuLXJ+;k$kz$GAtpw~
zaDF7eRjGt)hH9d3vvdm=k>uM26<)>Y(Y0IGi$Q?Rt0VjXW%$dIz*{U`1`EyJk=FAg
zMB_llHUc6U_6i_P^ZK@{WsNe=r3p=65AL@r#qh7MXvZTE=JJusT#XFBK$W-4!JzA}
z&-qpATgBoHGyR76zXf?WZ;ING?~C~a;C|Q{ceQjifR;c;=Skxtwu=+uOKwET!L^Av
ze(_2(6&~0{+X)Qbr@4e1)7ea6iZt#=&2#8t7sFK4zU?J-)AD&`ioWZV2{*^Kure;F
zu8Dx$MqjaY%o$hFzLPiO&zlo=Ei%4tvPZo$ikl;4%8RGWkUG*IzOw(3sPF9EnUMUG
zm?<xf5|V$(C84ygh1Y19ZD1<rTZNjFG!IK9bt~}r3fRG!Sz>EFqvzl>4Ig*(yvJh0
zgf|XbyzTcJt<Lkv%Zk<K&7#YSfHxw?ti&`@3syua7G0<gq1-Hw(Z{l)ijEmu_|%tz
z)R+AFB4rA=gU2o@??_Tf{8Jgx)+{{g-c+__4Wran?WP3hYL2eKZWWcuXTfW`!#Sp`
z;GTml(Q9X3dflO4uX4E6w|cC5i1Lmx-(V}MKC(3+Qg<8dgTU~GtcdJ?yfT)&&P)mD
z73Qhar)_wO)(5*Ftw|ice40zEWshK0x>|9p^yY?uRUQcVi?G-x(P>6|YlH*GbATrt
zttwuVAU;FzVWmVqx37I|b33i~HDvBrjt*kp1IO!{kaxC$4b5wPu%YOAzb4&Okt6to
zi|^a$^Re!J7M%FgeoQ|Js^JD@`3sobYBf)U!h=Bhh#={3`Pj#JTz=-<{hFG(HNcCH
z)gg{hE*}tZ*5O3+dU>9gEGXTw$EBo2iO9bD3X_-l{n(@uDAH%Zoh2C@pywFkbqiA;
zfR}Jnc0I7PyH|QfD;8K^9qXRV@~S{h?#!aKFE6!ZRgPo!=71tFTz!x~m!TLVTX*U(
z{*Ix5TA4n+Z2mpk#@g;KC000z#V|3WV5mwKOoa_n)ipR(l^)V^enf*^+{LnNVARu{
z`ZUizb<gi9mWd*D`EK0r$!M^3A-q&S?1V*+S8?(f&SDK}L{a7Gh57rw;-oVO!a<;1
znNa9xjrIVOR{&0bRQTuxrF$>M@tM@*Gzx1L^v|>@CYmMDq)reH_;GWKHk<mV+kScd
z4Wi1S13rC)PA`qRVv=zx)5+DXbZg$DmJvLJmsH=tp1{gJ+_@bDHayv`aEqi0J(Cpj
zWv^K;ot_rJ>t2x7{0KL-zT<(Fqv`zlg=0QvTV$=-?pbAb#~t^44t+cY<s<p7sVI3r
zl*t+lAA6OH6KWXjbI%qFlpYxU8Kc>|FhoV^mKis~_&FRK$U+90;R3RyN>#9wau2-9
zlM9dINq}wH(KGtsXgVFn7e~zzk?^K6L&|-KT2z6qk!vnBXiSC9RZ3kf;a|J>GRmbj
ze9^=QkGpb(1U4=OOIDbRVrB|Z+^TPz=En<0XW6p`^sqrDQ_y=yTg)ro^Av{RuMn6F
zD=%&z_c1w3DqlttXcPtw;<x6qZ7=dRbbPC_>yBb5YgE~&v8yw!#twI+E}{CqR~m+M
z5@k?zWOStlgqP2x2#oS%)wQVzu0*$D2&T0?>&@R=z%Sj1-fkd|7yVW;e~+J=OP1ug
zK@&rOJxE}>E1jzgD+>80tym~#o}r>N)5B4b-AuJ;T96%_pIF#TvM*{4ZpmJTpusDw
zpImXcP*j#Q^INnX#l%3A9d*3QA)1f7VT{en7uu1#Ari5>T;K<(6>Hj@u&+7%!LIR)
z%C;lq7<tYp|H#m01_=DGxUE!Um4^O(hrR16j#tTEKYVvRvg2!Fhms~1nQ|U722%cQ
z5bLd->Y<WiMD(a=$9RP!LR;#@bL8{NF&?RRX(wzBI!-<jT<Xm1gf)Q(1#K?Zm|bC{
zkp<iyN#3yjO9GKzgU1h_oPN9F*E*VT{p~h}e$7<moi~|4RdRac^`te_i(m4n7x?f8
zYMvCeu&~Ok!qLkTcTYcBWlJM|7RfDmkA}o7I#|0Wp{@%uY{ZY4TDrTT8)GbICGMWg
zzlO4^;8<^UC}6KzMBSIPrC4+fDz(^4|Ck-XUlFQ9%~>$MhL5R+9c<7FL@uDHOc0u#
zb;(FQZOBY%zGHUJYAn!!=BbI{f^8qdUfp7}ID~EwQ*OZqn}na7b6VBw+cRaZZ!p?0
zUUoqB%p8)lWzsufV@eS3Mlg$%R)V!jWVZGiWVpP9)qw4;(RDN@F1RTr*C=y<;iNWG
z9?DBpYgK7-B`NEaDdA#y{W8_V!3kF0{hdclg5Hc}oQE_IwN@kXd)q4vNrH>H&`hGz
z2EfjV`s_5>B(*wYmiOW3E#du{v9WQNd(ost(2h4~wJixAgzOZ0OtJLYgDRJXAlK2n
z05;0~agWY-t`#a-AWKD0Rs;70Wqfgm602+95LlS*HFcO4&st^Pih?A@#xoqOVQo3h
z5w!5om74A7{&MSDjxDEF{)+Lt)ZxvvfoX(pR|gog4;j;)PA%j10Nd+RU!WkN<H%Tr
zUELy*YwGw~hZX^kGKF0H62-ls*`XSiS|U~+$*-v8ZHJ+nAw!yX!^{Zwc0RXSt6fhk
zUxRtsb+>oU5Sa`h4<e+Vbg`<w-wa5($59%U%sKm0N@F=mktv>)k4QbS3-XXPM5ZI^
zY7tgEU`}rBZL$sC21Gc<NJI!G#gD8OsgMe!)@Mp2o>#B*&{NCjs$T15a*eKZz1M^q
z=r?GB{=6J+e0*z)cJ(vPx`rta)$CrqGs!zKn-C*X@w}8dW|)4XpQ;l{#beA8%2a9*
zjk{gTck!lnmA;X5l~Q?XL#|p^&3<4xd_Mq{c&J*p&0ViJe3FM6>y{<zYg%_U3YbJ~
zwS+y03_^BX0pT*2$<x%*hsQ>g$*vq9$~Psj1!q4tCP<h`ZJekx21!{Xcg>rr9<X1Q
zSb2!n;nk&F5@rjKolJ<(g-C{f@yL2jWRHI+n90erVxyp^rnVBLPEw)cOs-Ya;%9}Y
z%as5(UO&UMjR*@inN+OS77Bso64@En4NeG+2R+AR`87%O6IX5RD?!1HzXnch44mo&
z5??kS1-Brc$=PGp>tMhHQZG4V@JzpLZVcK1l54nR?1GHJ;fKsRC2WUM4>IGAgGyQF
zY1x?^^&II`FpO!6#R7R?hvM$CTkWk8)q=)H!BhjMLAqJEgItIvxd0X8otY6+r&i}z
z%4X-vrh##4&4KUG{^X2@tO1M#scd<94zvNxacJ<k8`Fzo8Kdw3L)YG743EL$YDP=F
z6Tj1`zWmFE9@LWI)vtRBeZ|IET4*22xF25^;Yf9Pa`sDdk1X0f7C%j`+Vbd07X`|g
zI&@)}Bn>u4%(Jrx@enCpL#<Skj<=t~b5A3jVmhl+FxKYirKb~$hMb|Z_SW%4({Xh(
z@78gNQLlW=+8w28%<mF&urxS?p}2B$NgWHdY0N|%yFFG6*T6<T`)Rk+M&TpZylI=-
z+!$NVoS4RLj)%#(W=C{z>C&kT{C8t7Wt8{5RJJ&?5AMD1bNh0#wnCYxhiAs%7-udG
z=pV=vIiHxC1$B@REB|JEeW%}Sdlw~_`2)Syg}!=8a35AB9i_5l>5NW3HsIU1y3lI@
zYI(LSWQ1KVliWVs=A#M77+qGEJK5<8;sIt@-dR`pqT4N{6TRX5(um!P4{&|6NjFr9
zS6F_dHs9j6pdM@{`x#ScvT-7mA@-li*rqyJ-SQ$XaneLJ<5YZz=4lqQQ*OEa`II6{
zdWsW!h0=4y(ihu0LY5KBLb%1!^M3B%d_ELeEsS~#%aPXfzpma@5{N1!ht=(UOKY)H
zK7<s4ZIk%Q^<jO`fQEm<|0Kal0K0zFi<<?TT4N1-1D-018?UHn*1d#5hbMxSFFJFz
z14gt+z^2f=LaQh;+;a6f7pgdWmp;<%eM^Qc-||_@nXv!R1QlP+8ZCU7X%CfWSzwgt
zI2>7EwYZ-Yp?yiObGVlfC}2vIS#u>Ck?&Xt?IZHej(Fhr9fj<`Vf~Hx4Ed7HIF;Tq
zgo}56pm69^5hA>#yGWb?CEE@u`hK~a5_y9qYnVQ;*y%>Rqt$PSb%JHeG|wPoOoNFS
zNxVAo2(rW5&-3Ynn_}W%CM)X6mK$mHEcPw-H2PQdx}ogQM!rQhFs=8wvF-3gX6dyi
z+cWKWM<O!%L|+m0hhP!tuXaD8^`Aw?FrDjp1zd^u7hq<YtPVZG8K&4Hem0GP`M`v)
zN8>hI6zLp!7g>W9W1K$nNs9V#<mDY|{|x2<Q)ba6pgpAOM^)GraX)EfS)?jJr^qeS
zo^%JhpROOjA3l<_KMm7X|J1ZK<BG1o1?xip6>O(1G7am(&@1-JF%pr1ck~fz2fe=n
zU}L;G{>Z+A&`*w;W#kopW!|6Bc@D84QTp|X!dUKxp9YKC>ui-ZrrpEFz0-B&-O~;^
zC(UfEUi}u&Wn-~48Dr0;C7pjDFHKHBK_XW)ajk)+&Z@xn(Dt!#Gis^jaO8O{MLO;G
zD11i&Cr|B!$Mpd42rqAbL1|Tp<MI)*%*@JGXKT-heQm0%5eMlH@<ShwUH+WIZjP45
z`WVX>S4Cl5_Q2G1aas8Nx@{?HFAr6Yw=zCcM9PGX@7CqO9{96}Bp+~dZ)3kZS8*BH
zG<#gnwPlX1>BoU(f$sGdJw+97Y!<-d;>3^5;f<48-^Nx+t@YTq>n1n!T6A_HGh5Q7
zYi#bvrt$Mevd5ct;3>zJoSsZJLRJAZRb7=^JrVmT*yd96BT4aVOL~k}herA5H|NMD
zNmD!W*EL$grm5|Iww^Qd-rdOvd}q0G;~ZkFHh71b$jlF5&Rtu;5gW}?=U<!%R?(dX
z`Lp<_>_bTrKyOg4a<56?3d%s`K@>z+2k`3WPY>u(!KB@vQ#*~C5!|kCiTvuu?#<jY
z&kZzTqGJ_wQbx3)26(uU+E>aNMjU@#8{i-$7+-UcEGl;yqOEbTAFv7^x;!mN4Io5t
zum6^+pob?C*U6`JWe^{p8KGWWduDwW{P~J7E}ASeh4QQ%v5d6W8W#ij6iRai!oVc%
zeB^6*?0bHGOU)2VSgFV;3LL?qe!1!bmuaRij=r5ln)bL{CivL{eeCI5n7Tb3^JcIb
z&$GEVR37P-rDHtvdpUDgxa^c>JxQn1Df|&O#li%Yhuse`XYaAC)D6Gh?yS-UJwf)F
zZvAklrm&b@ZPJv_v^OY?37E3T%J^^0Q}fT+_Qq~Wp&Qog!fNUD&rF3T3lQ<gJf_mi
z7*2@WjJ=@Qz$H|@2S!aFyFl&=b3^DzB^quIa5oAoZLJoQquStbO0^=<AJ5odiogF&
zt6ZZ^3QPySOX+q7u{I&OQpP0h<~6glphv+c?H+-)L_!d$7;Kzu(+91_Ng&qm>So9I
zdLzt;${X*J|9GZrB4}XW&&-#Be=`m&4ZPFlgRCpd!+PM<*4;4$^46x;*{?ntNYviw
zGaj+G(7G|-MLg3^_JuS+M8+ujq8Ucpf|<~wJS@n7C$s0U5_8lfwlFLfl^Nzl8#a6B
zF1En)+XTGm2uE&qw{KPo`?m>q)j9Ee)}`T7d->Lz(!G;;<E*Q8yHHOeO7P1&DYgw4
z^tsB5+J|RhT-dep9<=X*WpW|++})RC^5*uCYU3PeTx#WL6N{~odTn#xY)OyI6_$EM
zhP^FGi6(zZDgS39b_aV32m9s`k>a=C&x)KHH#@WCT8HKwVnR>hIp)ix#&+(#J^>o*
z%{gjW(*$>u1<BhyPWK9}xxT)X>r)+}vxmPF7gjLL-ApLfN?jhP<RgXD3luUt3Nh{c
zG`MFC^gWO8+a*0k^q0k>Jc}4~>&o}zEHX+;lCW2b?BTF|-9S9Qzs}m)sjZ4UEWJ=-
z)iKnaK+ADafVQd@ddcoDP{8-R4>;fQ6s!q)nP$1wi3DZpM_$#FY_Sw_&$^L8Ph{A0
zH*f!PM=$oKW1-qW^z1D3!Wc1DYG0}4pX1+nN=XtLFf5KPP{Mmr#)`8QBfg2Ix)D^~
zAF$}KEv&wkj{PofG|?AT!UQdA@k3w%gYtO*z0XR51)kQigo;T!F-80LqLISLmpU!~
zxYsIdV{GpZbW)p5p|<4#JFN+c>4Ey3pJ!v@ZkY;aZuu1GkY(a-h$XGG#begRQ5UF`
zHxDhudt5pM#m$53nIz4@V=+stBizgrZY=X;pVumxfv&;Bp0c<X<s_|nR24kY4}(ap
z13Nwu4F(L)<LKMnIhfY(Uv3$&tL<y0B9O#Xx>3kIyK`Q3>}+w*iWGX;&B%9*?j)<w
zMLu>KOg<jVmVDPXQq|E>Ct7>h)|3<kHk2z0Db{Pv`3>hh26Bp<v14-OrKTnB+Jhmq
zqGmkUSefVN#bt<0%-B>sd7?RqqQk<EVFvOw#BX8qXwcB8OK^UJ*Wp7e@CkFp_n}Zq
zJmn>XN6npBayxIU-!|B12x{mmYpTvQj#Kw?OAA|Tmw|QN<zSWRgzVnrD3CVcMn&C5
zT!a^6H%w3~vs+rQL0v()VN+2RpDfJS)&)HVE4WhC`6@6gDX}msiKXFuQ@>b~TceP!
zxN@W5M@1heWNdbt$BBBtNQt5*_(tZBig|ppxQZ?v<lzkSRlYs;%nE6nCaQjUicq-`
zRTfQeEkd9g_xlfAk)7h4M7amxJB6PO^~0K)+FOa69nSqh4;cH(EydW>zx#_dhAg$r
zZY5Yma@znEFmEcV;&)NmYx+uqBT6m#max>}QF1D3^2axB*&~mtN3?X+<GPL4z&oQs
zH+`U#K|v+&8q^Wtr%z1Z0?H8I&u+WKrrjhh^b;2}Tgt~D+2pu(5OxgaEqYFOKjvyc
zQ6l!qYbOA4J|`POACtqhte#bO<z5K;vdklRCsqhUI(t_fqiMr|ZV?oHQ3!$C-qB-1
z9>O)F2WG2Qj?6ZNWiF@-3mldUiD6FyZ;AdA7&#PuWXdcO7Hv|k+3L%ig%K)!Hxg}&
z3d*W=<5N(>Z8?jI7tVz=68oLw<O%17aT_RPAp^*Hp~UfRgUyYb2*y0viV}ct69FP2
zg0J#4g$2sC_HJ#5VAW`;K?p^^hpAI=a87<ehZouu-|-5))(2A^e+%$2wa(~Kx?tg$
z<xNv?o3;PVTxK1cZytVEyyWxilD1s+Ex;>35)>MK8nRH=CALDJ;aVi6aiX<#R_hB^
zpJex}TAe)uBe>_ZyebM#Pfco{jzv*ff0NiZ*{Xthd2(7;CCMD^pC`rhIU&o<sn1aY
zt;Li@6~#p7j8&;GySfQ#xKKG3M#C@G`+GllKT5nT$cjxLyNNxort54T$IPUDV^mbA
z`;YbhVurW*?x@;$sLC$bfX2eCRW_kx75;)J6G+NmAI*Z0Gxi{^;Jr5Y{5gIKRkMfu
zBZSRG^}~uC=Qq!6?(^_d9Ys|x-*e@SnM5{p8D?!q3_55c-jb|FvUSU(F?8PBcF^U)
zDC0DNWDq|*tiv1e&Ke@<e<?RDcXtTLRt_j!l*A{oPSR(!%0~!a6MT6W&eDIS`>8JQ
zc;sVEI?TK`NA@oDtc@h`oM9MoT_4{l3$tPm;?#`zqrzeVrof;ytNkNBBJRCBvjR(F
zf;cLt;JVl)sL~vYbLb2!fESm|aYB2KBxpS@(1#oPP3cQ>df<9iV7Vk#YGzs>FSygr
za@U1(O2C?)MEbk5om~j4itcVd7EAftr64=?z}&-jt1`wwMGs{)%ljMqq^qM=n*-0d
z!E(u0lkn?yOrI{vTWfal4Za;!;+_jtnCFm)`FWKDA?O=l4`gJFTl&$57Ox4<Gue(w
zN(`SXcGVyO!nPR4MCcZel*tkVPgUEB(F@wp)tBL6;|d9Psi?wrojm+4#S*0pJgs9T
z?>38!jw;dXn3u%357h5eX-P{h5!QKBC{Dv__^SQYl6Z=~A=B$6^{!4%m0t9)w>Mb`
z_`XtA{0kNDwET6Hb-B51ZMna5f3wxZpOBuBlAht^-;1@i{cdaPoz*{9hQPAC7~5@z
zc4Z{XUY8Ljn3Iq?BXf)EPgu4w06%LBu$B-;S(cPK{;44TllS;%eVWu&pjsmIl`<0X
z+{dZ6L-=zuiJ(^cOWy|3c;bTHl`Sk2*HMNN%&kZ{6WSrq%77g!?)eBvt;wBncqy;9
zXvwz{{2Ey;yfX?0Q@o!OI<v?S`QxR$lhR~y#1q&x&k9R@{j(imSuASr5YNTvtwLCm
zU#n{pvG+<6C{%YJ;)@2k1|A|6)=~2~e4@0IToeSmSAgMOTRu2Xe><7YR9V2D^B=12
z(&c-&ncjywhj9hpVQhbda&6MV%LWm&JbHf@VVLe1OQoCc!^6hu&=hhv<%hP)Wy6S#
zLa35Q{T33cU`pcl6|aaH&uin0q!0;D-#9cx6;h*)w<&+_Vme`l!C1h2OP`(cJ#u=w
z_omK>DI6$C#&HYAA;#t1>o_PwV01-K`A-B*T)w9w6))wtJ+B-syVz9_AHnb65B%0o
zHlx~2^(mGLAcx8~b)Q5{n3`&J8^)Ty)M5$HAF`QjUDgvVcZ{E{iLZQLU5~GQGq?G$
z3w0T7y)(XIiH^`I`-0K1J*(1V+Y&mal{3nryl<ss!qKskqmyx96er;uQ)QJ!Wx4fW
zDHn;@*FvDd^rX$)Yl&{-%-V;<O6f6C)QTJtcT#S=cA)(Jcyl^;V#E0n;Z+&TftPQ8
zrtG#8(Hch7<fAdNpfOPEQ<NTFR~fx<sZ6bAofU$XN=au{<jvr5S!{pUG#t?NHGGiX
z_$fwxy}vq2{(_P_ag&HoH}ZNmA{dM(vGThco%xQbK~L;O_9L``(h%O!wy>D?e18t}
z_JLFRo0w9OZLirTMWON=ZUo_88}_YBqzC>B#@)$RgYs0Iko%LbgKo2#-DksT+{JS=
zxay|i8M(c%n=d3baKv1sob4FBP``{q`!2=MN(p0PA>q^a;Ovo4I<a?KpgTkovp`~^
zkW`gAUI5b&<MKMMOrb9f<%3z&28=b?wvCu##5IAr6-hT&dQCwz50oS7bEC>7CJcQu
zf@NhX%PFl0RyhG1J~g<i5nB@qvlyWCoh%hUXKS)3T^+ISGG!=Xch*Z{-MFr#*R`!%
zO)RD^nh&)}`<hVZHzOywpA5+KSd(vreQt3TM?GahC)Q2Wa$9*qcxCwMX+jT~s(g!^
zvecL<snh3gH|r#u+I6COz|EKledCgOUgr~w`LA8WA{%{9<vT={O+(FNCt`Yr5j-mf
z16B=K=&<D#b_Yc4Xj=|zgeD`FB|2MVynCR_md3cdoz<}7o-=d1m_dolF<p-#dVP35
zBGTb^oXa*JkmfG23#_hUn+FmdvLuzg>x##IR4sTDtrUC>R=L(p2)*z)tYH*|CysIP
zf8=PrUnR+k=iny)en&PgRqf7DMSseDUXm*x${lUgCzue!4egC6%ae}nj*dJM;UT1e
zwsNxR&7MSEd#q`8YtKc*qg|x%Qm((G#u{mhg}k<~Q}h+3+R;nXq#K{J@$T(#CPPf8
z=%9^wYJZT4fZ$MOR~>YrxM@FXel0dyOEEKS&P=BmS@{RKh;LaYEiO5%n`&soboMgV
z6y_1V!UiLSCIYQwo9OPL2W-|(kt%4p#V_%pN5LVoXeJX|k_JTDf=9zj*I<up6S3>l
zGqTd0Xa43LSEbakqwIw2v5}qfgBL{5bMRudlVsqc((i{2AL<3!8uQ=0YRTo%mquqF
z9bwgDA27_KTFt4eH#oNMi;=TsP0Be=lZUuB*x;Ya8=Q~5JR#dcB&()}7&>@ulx5rZ
znPyTg7v1j)KdzoMecuWG{ROma?RaYWOtXVJ6d)i!qQ72!R1q{%u(hUhba$Ln_tjEb
zNbF#O<Lo|^qEV9)ORi=96{XQl7^(-;jYEWti<utA_YHZSoe9fi@Dg9(t4sCQunCN1
zuWS38q536OYb)L*>*uG%(v_dA*8DXH^~_iCG1hByHU*R`6S<4MN`}$`UN1ZkV?xYF
zF>i*hEr{c*FXucDtB&J52dj=3mg}!jBL<P`K+WTe$R^x%rxSeSz!C5JEfI@b;CxMc
zq2_P0Hs*+6FA>iMQyEp|>pAD;VkA^(XGjqS4!<cdDJ>Q^DUYM>L#ff}Mk!@x>}*=@
zI?K6m;#Gf9E=4=s?#~-VCeQBz4bP6jx<8erv5`Gox}<b8fVY4$)Aht_>RGj!^J>0%
z$V=zb_MxXZOrh%rU|2R^<pM$BPvm2uMC>FJ;@rVaqIC3i8g}R(p_>(lN<e<dF0A|F
zf=6D3!D6i>-w{dlEJ9Q5+b48967eFwhP|W3u~YnNok~xl*4ANj))Ou*kUS1x&#yh*
zk3=ZvReR}2gTT&p4xeCi65MlkLpg7D2ARv!PM9zAj*hMvZ)q#aE6O-NpfO)FEf}%9
z($PddiE*x}J%^bIR)6$&Unma^+oC{nl<MzvYi`BAm^gCI^(a?HD;-zWWw!%0^mUtg
zvN$|%WFI-*NP)`C&iC&2JnZoHnju(!b$M)Rd4N{EhrQ5FUFWOPZFD$l$jSt;X+E%=
z6azfB3xkSIB53iQ6~iWqRM%{OST`%3UV`&>FD^=*<uwb`EbE-gE!ZTZ>lU-58Ea~m
z+@mda-Ntl@8BAInO_;-~gP;p^Yy}yZWfxCP`wD7WZlah?x5Fe#^Fb?BVAq`KI+?dl
z@Kz@$8ONjyd2V}F|Kx7;ZC#{WQ!`9Tnl+M)u#}3cBa^J2RofkZ#7Bw0Sn@O|<nf_?
z*)qgDfK3sgF~V|IUmegUJF!n*a#~F^p7x1$pY<p?t}1e+pUEGJjyvz<`$clwYoDgd
z1&2DAgh5CqXl3G)Mqe50=7llpYO~@nJ52U#sh1;-j!Psc!iMJ~%nC*FTUBm%si%_K
z>HIIerQ$WHmMy63()%yCLmVofV-h7@4Bw~HHj$GYY(^}4Xz1}yLm>rmeYtYOa(>)t
zPqAe%K(TqBFmR$6G!kCeRD~(7uQHGel`I-`!y}9Q%ux5_6;mBsoL28_!!hL{^|4EY
zG{eWy@2k7rLH<OaW@_-kHSr@2)ANZex7~`;&=q7Eh^W)16S%X+kKr4P9X{Yfq(2F0
z7|0cP`3UbUOYn(eliKeBs2|7_SQUuPAe!+TtRA%<&?Vs};U-=$4WFF?IKj9fS(1X7
zKWTNu4II=rNqE1q)0)SX;Z~YK11ST>69avf2@f@#h-w`fLuyMsVjLh5wqd@gkw+b&
z7Gy(P-&v=zm#?gb<D;G(I~E_)ePk*igze+wTbAdKpzdVz`{tLUp0>yDIGH}pM<rY^
zin2COE^P8w^AEMco~pNZr+RUV^h8M3caS{>A95We_cnJUKWr#5qnv`iPfyPOT%1FR
z3Jj@705gnRMLRza6ZUMFjy;T?oX>Vt?wjY>efJ<>yCnd<HBct`7I~-Jpc2VaWuh`L
z&(IZ`jF_GlRL`yBoChv(-!*q^_LUE1cw-kAHZcl;pz(4{I(H+hHRlAQor6;#k5~&1
zPy(IlvwkKE(~+g;leg3Q&cvrRH<58$o%TS9cX7yvgI9IQaG<o6L(IK}g~l91+{#Xc
zn_cLC)sdlxpKT$&Wv|ij+wM1JZ+y4@GSICT&LTF}N}E{n1W~D6!jUJ&NKDa}o|?2X
zM2|}WWI~j@d^+-Raj0vHGGn7r>b)ZG`l-9Vd6fniKyuX&$z%H`t%Bz)JgCcfC=r!F
z{(ODQY&%=;@!tosgl6mm-w@zg!?h%|iC`iWR67l^T)^wfg{nAZDWcHxrhYM_KPL8*
z`f=w~LG=v)1!e2$>vV);olX!Sc6x=chWb3o25=(ZliT>K`Rwu5D3~5491uKFSfiTo
zUj0rEE~0}Xq=jSXUHOh``-Z+Ry9#rercPccxcnAi?OT((c&=<*LWiOE>8sQ%E>_TR
z?7V2@zX`1KCab@U86R#KADyzN4u~OrVV+VmlW{_0bhWH52TD=Wn(&2i#QkNV5szQj
zb9{IBBsy6RrSgJkA<01>A|7z_HM#R9IrKoLu1OBf%H9`QQA1*OpL;aXN&V!PLY{HT
zvRdri;boC$J^4C?iiSnPIDXk3o&nU4wr}dD^56qkPwK<<Aali5mHKnDCMC~I#u<VW
zzUWN)=3LR}m2!rgSeOC#xed-dlu}doyWF`>qhA=&1;v!t41YxE%i*{4Th>)_+<A$S
zbItmM@TajB4HS6l=^a-}Yd2^kk58j#F12`<En8wTXd?tp@um@HL3|CjlUUO$Dj&hA
zeB$<6$gGdA=nT{iz{ejV!*D}Z@e0RoeWn|X>t-!@i^X|Wx~p1y!k2}36D}8FM$q)e
zf4J=s;K7g9%|TSVg|R+cVLl9<wpY^*DMIAXy(>9)AMQ7e?`CRBP=F(#tmeB#vUu@D
z{ZO`fJ=1E^vX;><re+myz|l}iMU)gSNg58iB2tWKCE+!?AB=vKQ%OrlA*fCmPTFx^
z$`dkG^?t2eedKC#sN?UVNz~4>(`+54@x>)M4vTH_hcZt5LKF-1>y!&lfETKKL~&Wx
zw5c|1UtK%5RUR$BR^}(J`())ANBnd6mg2x*@@)TE7?EV3-x^*q;LEQ8fR_o^M4i_$
z;p;YQKCAkibkYx|Y3zz+T?60s!~=v&YlG}>6Ad{AzU`zH9s(Mr%`m4ED{mHMa}~^1
zfS0?cWAv3ltS7Mi;geoeyEB+cHV4dIOO?CwvsG{lN$w3ML)g+~BP_L9lhX1e$x>!6
zSwi`nqO6*w51Z8ruOF-u9m{Hm1RO;4mmAl8)Bu01M)r!uEL)n}VsGK&$H&^IZnoL5
z-im5E+*7L?z3q#Rv`lt9hA-dI$>>=HqV<34Li9_)%aU%zO^eRXpC!+C<2q{i^JlU<
z&KDpoi8xU%M9h{?R6XHjKp|Ju*ZX^s{~$jmiEaUNvfg~9lPqE8yF`65rpwc?DT8ot
z*Q5q_;Y6v4z4(6grLHevw*DDyDE~lKA%E9_!jp!3<j(w=3soh|hwE@dIY8NPaLDEB
z-luq-)eLg9*J3iQOImJ;yPS)|dRZ^;kR(yt=VcSHgKO0>86VT(1b|>MK9x|*memxO
zvDTTV)L(rO1`Tl>Rh1;7Q_3Dy)M6ED<mv$_!z~jN-@cLC68OTx^x%++Be0~WruQpa
z&91@xskgh+)qQ@=;z)PbhDqg7k&t61Y^{7ESnY{NBAv&V#Qq&dZq8X{&Qy_&W(J`x
z??io|Tr&LvayY$L{e?lR(Q$-zysBy+{{Ra{iVbPxXz-Rf)}($iM=`;*u>1^b&Rw$(
zVcQm-rf)uL2Yv}|EwPYJ(@zaPh5H32dLdv)1E8K}3_@Ut-rxdE;(s=Ez703|`m41Q
zIXqdn8HzHv?6^Emo@mb{`bl|VU{wP#hQ+k1tUQN?*S>X9a?RtNk`Z)Cgr4{8<ieij
zeDSFkV??fnE^E#Q{$l>TV^BL?uI?_|)rwIG_h&YTt#Ax-x-m|#pp~4ZHPlpZNQKan
z+dcTanaSMFeTYu}*Zp;PyWbeNGPy^<%;Ik&SUP#*fhIpvkH`u5?tidRl--zzR5i{t
z$WgfZpHlutqCc^l^4^X0$#YjgmMOM(&@{GY*i8+j;0rO8?XZFSfTWqfwNzSgRj<>X
zS)t55fJ4r|d38{UII@*AEBQ?dO)G05Zl<CR(tn%Q>9*XdBZ1SO9Qll1Xz>hNC|LUx
zYp5Dj>)C{+Afa#IyLLT-`0HGa_XR8c+-AV9z<u_{0|cQuO!VVhDld;?kHdUpy14UC
z8HedkVXR1Az0m{}Yq~geW$XG*mB=Sk8x>9&Paga+={ExY6KNqgUMj@)5%cNeF)sz-
zXJciIM$NhxpO`!aVI$3gFn}}H#H4_}ec;Su#_@vOZLCV^69$!y=8ZE|_<Zb{gye#p
zQXG24P;SF2OGHs+0+B6;k%qTa^>eETwtx0VHS#ULqU9S;L&8ujSN0F@(ViAX)!>3S
zfpAs_V>cHi_A`V;^LQxm+-~vW=-gr$_u*oK(+osL!iMCFhQrY*)&8kZ1-YVS$uk|8
zp|EFGf-g8leCR<1adIH+_TbH{|5hrqOt3D*#a)PrS9JQoW#K-Mu!1oRlfuE5C&qgB
zI>1EHqf*J;HrwmU?p!H^mFZ+z@s%#}eE257km~>wodX#oUW%v8Nvby_ew6nbXEBKH
z$|mE@R(I>kI((YUsFl9k!M75-i7d{fliOI^-cN>e{Q(OftA_6t&X9YovHnh8=7=!k
zD%~*rQc8h+H$PC)M+F(-YZN9LLNlmS27rninq$JGKolW4-YnahaHhHi4(q2brJvl@
zSx}a!|63P7F>yQ-9!6X~RtOzm(LvUDekr%GDgz>J?#;q&6qGMX4mTeyDk2(pKP;v7
zjXO=t_X<Ck7tQ`|3j$G6hIE4-a74I*MusJn9z`h)KPiC;-=EWg500{@Z|ze&?z}v@
zf-Q&~h4+>)rnE^vP!yu8OsAsxw<Njz@niUj@Cr&b?u31q5B;wE^B15*p~_8Q%1ypD
z((xl<S=ZM!3#OS5@?6MMu(Ytefe4s;7;S5PML@-GH-%w6HP=jUp1`2$9@mGN)W&`t
z6-^-v!vruuc3}9cQ{#~*ZYhdy(P(#4^-yBQX8K~$#C2)^4$X`@y@cYUo!TP%AZ{8x
zI@U$tdZaGW;$eOJT0X^)C2QX$t_!1H#kY;w7xLuA8|}fHMi7tQGgA_;Q2s&S9G_m+
zC}FZsu1)F`Qc$0O`zu?G2VbL7-9ahl;lL=Wtjr~G5Tj(k7*`>hs@yoREfRf_Z8^+~
zM*0Q!08<(FJQyQ?=?fk89FxorcnT<L?*wWsx+UFYzG3c~NygI2{CYr-q8WRitjbZ(
z@<%TU6&$w=^8V&UB%YbXk(fS#g3Ug#Ks}lEo)vKEVqI|pMqR60Y|^Be+2aE))$={O
zl;h3-3is{r)y$HruCy??geg<Q%#@7g^y&8<X&4a7-Q>j-5y%@aD7fUw{u|Y-t!|;t
z<g0I-q%^M4bG*X(GEMFTAKsW9_^zAC_<Q(U%k^7&TXW~Zvi9VfKV@n+I0p==rz4|_
zwuF`OD6fAj^~ay5I6~eoN=Ndfk1alPSSzx5U$#G1JkePe<VnLc3>#P#O0Qy_CL0K@
zmsir<Gu=<99~PaQpnT=Q`Py}E7PE+dCsw6rL|*Nzs5q-jaCQb85w}RT9s>NuFl}m?
zP(krKJJKmS7B%ArEvs;VLM1isYdQs7C~{aGa0xG&i+GM{p{pn}?x_wz<m!mp%*$EI
zR2Jd7nVZM^=&7?5W+`k)?pmj_Xs*7l989h2nGvm-YhSPBayw)(zP!b0_#IT08go6-
zKAIu_hnz|DhJ$!>_g1i)-M2^vE_NMqi8}`x2$L01r<lG)Q@_NND$4nRZ8u1jG)xh}
z7=Fv1=-5SgS~$Oe3Tld`90tTicvH@MBikNko-W|~pi^rhs#fXfQy3AC;YyF+meZ!H
zQda1mxm83)OX@f-k2t+Q4<Uuhr#B!u9oSb1LK*H+47D~Uow0|9)>}(>anCT+(JXK(
zgB_hSe%-D@sw`sqI%kYTqQ&-R7?kpOoRMg5=$=_FTp?VpxzNs7hG4f4^XUJ0>XgP|
zK8D#2V%dsH(^2dth`Mu`I{P(cNdDeLi+Ry}WMf~}TEk1|vOx{Ui2ftz?V+D|3vHXw
z<H6Jx1+t}&*Id(27R7P8fkGc?<g3eFM0vSSk$O@uf3>Tc%!9WBfVH8n%H}OR)9#Tz
zG??t2wxsiM)lr&jttQb&>;(k&Ft@%fwrU&kX<a#8S>}oErqcQmO%2CF+6vUl9cLJQ
zUmzp%LUX;*z)PR0z?5ltBL7J3QgrvgCt%q!9~qyl3KO;(Y6aSYAKiqQLiA^ra2S2G
z;_xuAa;ui4L*<<n^zzA5h9kiV$U9Oa!#V$v`F=wd!|em;>(AU!!vR0MJ+}NWfE>&L
zHS!kdT1Lm2HFG`!9I$g`nqYhjoXv((;dDN#iZ<v1b!@v-v+p{o{2}&dV|f?S8`C;@
zn*A!@@`>v~nW03t?}+NLjp+;HY$P6eDk7KL9^X(2RJ$DblbwuLUq#p<RqHl09O*ki
zm=N-wj)1jMm?;Ey`p>*k;}GnRT&jU`FJcL-sGn_*Ze_+-f^Q%7QX3)>h<%d==B$YN
zTb`_VVjFR456UmHp5H6s>RsLzPwH$A)8`XTM@H&i_)l^#3T}aGG9)p&9xquG7#}By
zMn3M?fSv(3G#@5U4I>o^nkxIHPNiDa)%iK~+e6`kXd%@7RAkgLD(S`S+V<@RW5tuj
zyTytnfyD*I?`0Na$5ADHa}}lUeaGv^<i#KD`%+^{W5--kh}%Y>u#zVd1QctU_d(Sa
ziEp*17{)Wkwh7vFhU3EHAP0EkE(h`l&<FGxk|VbC7Ze24eA@3GP40VaD$y0(P1kFQ
z?co_{3Vo{7)l|tUJ~`JjiLOd2s^w)?#a2bF1&j($d&3o~J|)+E!?}qli4{ukwTz}{
zFTT&O7;=)pAgHka{mSM)0T_r7sNVn2MnC_)&iS9~{i}7U{|F!cV?Y1ZD%F3k|F0e^
z{v*`;%XIwnLq@qjd))v5{cTH@KcCp3p7|HczeBM<e?UNh00Gz@5YSY?V!;paz>px>
z0oazGNv0sczzhfgI0&e}azMbrfdHluFw_vhV88)vAi!YR>Ht*$(?6vk0!TrDfwBp6
z@cnRsxc_#@0vP}~0@(st0T}~1eV)934gleQY!(a&p5dVWH+llhgm8~_Lgg>F>;TkO
zKmf>}lN1C=0&AK*GC&<SPuo!!O?x#L0RL~~0)u^0<)6tw0KkI)RR)7WL02($Fjq7;
zwINh;aCUSelreU4wRNy$zyP5ADFBOt@Q(t2t#xOh0Z{#w@%7(pnp+zaDmv*~+nL*#
z{@K>(&(^Ah3bwXR49oz=ze-cf$P!A4^9zVei7TlS@(T(I%P9#9(GXG?QgW~n{>OU&
z@G;;y*Z?dHjDWud4GeeyGk^iW#>m9N!NQ^O|9%Jjv;KL^fx&@5{i8m|Jq*O(A83Y-
z@Y2kShL+ABw+f=V0JZ@cUUgY<(RpWxiK=?`9$J(6<|y_Z8g6n0N26)%;6Ah}qceo`
z#9Ix?(ZX%S9__7P?VhqkO(zNLjn)8J%^!EzK4Fenjec$v6+Do&Iia{#TH@%Oi?~cB
z#fK=}D!5ZZG})pypLOmPns%m^BMszJ=79f`NPvFQrLT;@>p2kj#RnNFns{`!28CMl
zP-qv{E%Ud}O#!3>{<iwXXy8s*g4BwP{zc927ZDygX4ot{mHTKaUySiR!wz?INJqNL
zM2hgcv*My@?J<~PB<Ps&nhJ`fUU$mCxYdtlQvtF3Sft$!{l|30z4$3dzQ?x=*}zie
zsW)UpLqGr+2p|B|G8+^G7z6|f%EMaA)*xQCx%W+Q3l%ftf1?n8$}A%z`+pQ-)_)V?
z?0?V)^Iz!ud(UA1+n#X&IQ}ZYsATM5?MP^ALg@TwKQAXjGbblIM^1YBKVP75`K;{t
zS^3X5DCiyR^#2eFU|=8zkp8I*ih}=t?mZv?nD{@-aR4ylCjbJ&fCK;o19f`nhn!Rw
zfEwaZW9zT`HK5^TN5t~9kjDk0a-0N>Q^rA<i2VkYjXIPqxUA5bqQ@HeaaEV;`GaWu
z=wnCb^0gvSt<jKnKl+{$bIdMUuIDVN<=aX->7jt~{C&_h#6d&~F2fBd;;d9U3DqhN
zPE5c}<wb$?4r;+>%=~~^x4v4D!|WqKOm>Oh){t;1Y&gKPTe6j+&)@7|Kj2%b2xs;a
zAM2P@i;{qbhq32-;O>B^QfU*<sowzhLobuR{}5H;i?0r6NG$5gD4p~5h2=g^kzTt1
zaItK$W+5LjL)I6xC&yp-0TE!-$08t7)-eQfbcvEwZV-V^JU?P+L>d03O1b`x0RK9`
zHwdUtU<U_>{1nemfdK&i5eEPVll)=ICr@k*9qs-jQ~spTJ38q*IXeQNKbeAu@~H$s
z0HHsZgkU^?KYWJO3l;+oeiR1&Y#KlXp!i3_Ai(Ij|0iw(Q2w++3I+#&`9~E{C;;rA
zL_{z!P!NdDe}MwDH?oF$G?>eGB!~(%a3?jbra|3b&tgR2C$Od4Z@fGR0gRYt5pgYy
zDVOn$TxbyFp1dFsK);FN0#RW4e(rr!?E@!0tf)Y?DB9y1Ir@5^K&kyr`6f>>V~~}T
z!x+lZ@i*giMQO(XIQNI}Y0u#@$<(-EIds9xHqOo)Ka54vCklM|v|`VHp#UOK7f=K@
zcPR1{@4eya#<%|r0(K140Ls4<fmqPi&fUS>)XeFRloK*=d}^e$xuJutqpgV(p`fjU
z9UTKc0QaxLX#X+C*5Pj(#WN5A2>wzG%zw}Sg8+p5&Q4~w4(3koe>Y7G3;+Pz-?f?%
zz`)4F@b~Ed4;1}RN?HWc)@8TPbuEKKk(KPG131npiXzp0d{30hZFtYfA>{Rv&q>RP
z(POln5-@T<FLfDGTE0F>55|qj3N)$dv-B|dI*-S_jtCrgnAF=rYm%C3MwT+kNz(yW
zr$*K#asb22o<vhb!BKcB{#{@v5V0lIN?E@&!0VSDt*g{gO05xBsvIr@6bWip(ZLUD
z%zd7phNyaK;Ko^aYziR{1s4e*{Wvon%EC2aejS{rN(nOMZv`IKPBImkqe;*dL_n&s
zdgg2`l3yUXRX#Ldew)BR<xl&os9w}_>7e_&f-T>-m-{8YSC0A47hpSRaSyj%Lgf+z
zY9NA!Y6x9Xf;RrHq=f+gJb-`BAL$1M^G^l%C;K63PTF0VJ7j*9_d%_ej{X?=<?w%>
ze*yo4jD!B+Vd5cdXVgtHCd*kPFv+4T@{N`rV^${Y2ru}L(3m04eWL42)p(k^Y|%l3
zi`RuwE-sBCRweUy5^KJp(c*P*uQi4!o-8Cgi<b8<vY6(buakm1$Qp#Q#|EAi#aM&X
zY^1d9#=pLVlOh`CJ}BWn5e?0w-6)jW2r@#x1z<exnOeci-?v>U4745e0$vFjphNKz
zDY2#J@Idme{5C;rHE*ld2360E#Rn9W>T5wMzI?>aUqZ$BG$!|zu+i*#>=wH2xMTxO
zTJUvEvxaZ-e<@_Kz_D0i*_y=#+bt9Z8`NN8w~@w$H)w##iJX~J;u9IeRnE6t`NA0z
z@0?XIo&AXlL!Z7u)!%&s3)mFdPN4N-<k`h<sfF$P|4+aI;1dTvVS)W0#-4?N;nRWr
zD+=(>XMp`1z=J`cfG8_6pa78nM4(V$RP`OrK5eek=Y5a>2!9eGQ6LqJjjU~LjQ&9x
z%zsDOf7i`Fj`M%%=2rmGU){j|=NzL?yZ>o4{+N9^LH<wFApEar0{;h1p#Fv?Ie}=3
zi?9F$=EM(9obDTXPiwZX(l^tQdv6tQeIU#PBm;ZDR+UbDK%7KNRv6XLQROMwuY9U&
zBRNIuIAm;;x*6a&tzX8*M&#0Q$_o~kB*^&c`bJ`sw#Bcl7mt#7*TE+)^Of1k==H|@
z^aXy2D@h<?Hb^eTrvNlWosDW|TGr}I;4)+z9l0<$i)%w7D?=-#WeH@E>zMpD;5m(K
zg$9`0B;wOr^YNa*zsl%Q#*9*32d1W!6|TV8dCoKQ$L2Md3~1rt<uW0pDS=}1{fuoK
zJ>UUz62y&p;MlJz74xv9LNEZ<=Wwxk<NXOKiFySr@`eVqDY`7X3%=_|3IYt88vso4
zIUXec%D#gB?|>L22<X4hLeYRfJpo{#zxkVZ2w>=7sFYewIQ3&p=PjA)-Vb}jbiMG*
z6acJ$tAGju28Ry=qzL5vIaL(|;{PXy0$2oq_%|O082AGW902<H_piWASZzC%-}g16
zL(J8+JaTCtt53BSyREiK7vz_)1&8`m6L~tVbmnW$E_*n;5wXBV6Ft>L!#u?B3021?
zc97SKJQU9@@yu+eb*Mj@kgm~pqu!zfn(&oaHVMRr!&&_p7niLi(hrir#3(QorG#I&
zoV)?8=XoKKxRH|Xx^?2uI*jI=1WIMj(i=tx0PYndaiN!&UR(kD{cIlLb4#GCQh~N+
z<QkRTm$@7&|3w0s9OZWM7`yx&E1VCd@PJl-$^2hcCLTo?1tSohq9^$^M+|6+JXByE
zAC6+H5#)lehKJ`QA)4*1Q-=(9j7vHj?jFs6QuAPmmq_FeoG2xAKma)6Czw6_1!ge%
zkjR36d%nQ@&x7^n>B0XR(D;LXi~tr!765>Sg_->i`Z0ZiAIs--=T8*i|LgyDM;QM*
zH3I!_8v7sJsN$41jzUcGTWTIyDLyY(x`L6qsYv*4PN#+Vy3K4!K6RaOVcAr7$}90v
zY|(S&ZYbU_gpbPC=ejeS!%3}x^>?*Fa>mIZrNhe~NG&(i?O1!DX|Ta~H<cWbjc>4c
z+>T_Ws?@`jIcTu~Xxpy2-xNS2GP>=W367ZWZ&>1Fl)pbN1L_IaRUzs$1R&PcN3J*W
z+t5Vqu}Hs8kJzSRxo=o#Ub%p~Q@-}7495sT1u2J*3?hn1Dvv}axk`i_7V$$2e83->
zsTw-;n5)(bPB3x;R&kEz1j(q}-xF7eO-Y96Cz*HlU*yGu8@VS*b>~krLG?q5%)Sjg
zZiDVm{C{JGG^3gr9e^QX1CA<Leo#J#bR@7Ag@`gG`^2$8g%4ZwFE7}(u0Bp}(~kBa
zO@qdjAbDk$#)ZJnd;`{UXJN^bfhaFA8yH?7r^>T98#o%+FR)!;-DU~x(j^z==;gx9
z0r$KLvNJ)V`UOS#DW%CJ#rmM^0?IAGoT3MmK+EOeBGFX&uW-npmv=tg<q@g+wS4or
zzss#o?0WkA8}skr+skLJTrk^g4)g0KcNxXKBB!6HJ8&Kso5&P;&e>_pS?SmfpN&{-
zg`UbTXJ6jGxXv}SOnTbXiJ2enT735Cd|%+O{r1+-h6Bt;^tNf-;cDVL?)<pQBX$Q{
z=Ol-myrXx1%zDqgPm!xf-)igI<>w09Gn5~0ej-u3!<})d_K}Fwn>usi|6bNPlD|WF
z6YqbUPlB!`pE%iOO5Dvq(8eesB&#j9KJ0y}@xoW>eqX$sR=?!G!}#ldLVcQh>}j2X
zGu3`K*>>dh@cc`XbNV^CX3=V{c^8xg*7;VePs~@_m+@<v{e0!uC+6;MH*8{62NsLU
zOu!y4urEDS3O<y=8)b-gLE{$#QFxun&uGYDz{bqPzzFQyOTwd{li835d4L$`d<2gh
zJeZ8&ivp7^TqBF25Q~5aE31`@)&H#@RQPwW`Nzc?G&Ufq=P?B4oCN_5ehgs@nG8h?
zCBO<hkpXl|y)%Ogn9OI$1Bw=bN1GKG0)Vo445?st8c?hVNQVP;<}suIdF2enKz;;6
zz(Sb@DQMEqEl$ZpP5bCgN0uf=2Ki&%T{RV=ausuCsIU6qw3m5;;Br^38{LLHlQ%px
zw%L~LDHbF)=Z)l5{w(?VzRS4deVl^Vb<JS5IUt~*z3$m9CHtdc(@nRDO2)6+ByYak
z_vE7;1$|pDIXeou&hOdBE%kcki<R5&s;<+yq-^tUp4#4^S>{p;_sv?ga=rD5@6T2}
zTA{$*v21cx<>B>zzbuXZy8iDZVc{o<yHYrtcYBBPzEW;hd?r}^nXB`%=&yeZFI{}|
zJtu9=o*>B&?n!s`&L;7?zh8etJS$M^cGzA4xu|R3=JkH)tK>IZ`{9vEqd@#$@ms0?
zR#qELW!rJVqO?(re}mN{!$q9)Sp%Y?FZ|^ATi6=5u!+gXpoz%?5qWHeO-z<RAyaT_
z-#q9?GO>)sqNepo0}13b&5f8GVFV^=xD2ZyKd4~<Y!p};85tWJ0lP)$NqWcS4yh)#
z$+s>=Hi|awKlc<%dYa$A>`UIU0(WndO<}vwYbNhKvdlPSQmvgyMYe$GpQnrZ9p~<m
zt9<vI?LtUy$c9QOtHh6$3(~r*!*{rPEUej~{!+4t^U;I<F5)jQUEY*f(U2~==+1;G
z$%<K_I<qSDLgMTKKUZ4Kt@zU~^5Kiw6CRgir{;PXOnc2zZ8`M@C+8-H)d4PR;`vN}
zTiCukWb^#z&yVaCdPfYl#J^L!k{Qpp@7qF=o;2l}12clSl!bU0=l<Nf;&t(t3wstG
zO69$>?`?d^UvbZ%b7zdd-BP(>eNic~a2fmUrKRpcZu0A0ZU`FOV_MsArn7Nbf8wf-
dUo3SqW^5ATeD}^WrDEZ(uGeMWdUmP|3;>^yx9b1^
--- a/toolkit/components/downloads/test/unit/test_app_rep.js
+++ b/toolkit/components/downloads/test/unit/test_app_rep.js
@@ -57,82 +57,24 @@ function run_test() {
   // doesn't have it enabled.
   Services.prefs.setBoolPref("browser.safebrowsing.malware.enabled", true);
   do_register_cleanup(function() {
     Services.prefs.clearUserPref("browser.safebrowsing.malware.enabled");
   });
 
   gHttpServ = new HttpServer();
   gHttpServ.registerDirectory("/", do_get_cwd());
-
-  function createVerdict(aShouldBlock) {
-    // We can't programmatically create a protocol buffer here, so just
-    // hardcode some already serialized ones.
-    blob = String.fromCharCode(parseInt(0x08, 16));
-    if (aShouldBlock) {
-      // A safe_browsing::ClientDownloadRequest with a DANGEROUS verdict
-      blob += String.fromCharCode(parseInt(0x01, 16));
-    } else {
-      // A safe_browsing::ClientDownloadRequest with a SAFE verdict
-      blob += String.fromCharCode(parseInt(0x00, 16));
-    }
-    return blob;
-  }
-
   gHttpServ.registerPathHandler("/download", function(request, response) {
-    response.setHeader("Content-Type", "application/octet-stream", false);
-    let buf = NetUtil.readInputStreamToString(
-      request.bodyInputStream,
-      request.bodyInputStream.available());
-    do_print("Request length: " + buf.length);
-    // A garbage response. By default this produces NS_CANNOT_CONVERT_DATA as
-    // the callback status.
-    let blob = "this is not a serialized protocol buffer";
-    // We can't actually parse the protocol buffer here, so just switch on the
-    // length instead of inspecting the contents.
-    if (buf.length == 35) {
-      // evil.com
-      blob = createVerdict(true);
-    } else if (buf.length == 38) {
-      // mozilla.com
-      blob = createVerdict(false);
-    }
-    response.bodyOutputStream.write(blob, blob.length);
+    do_throw("This test should never make a remote lookup");
   });
-
   gHttpServ.start(4444);
 
   run_next_test();
 }
 
-/*
-// Uncomment when remote lookups are enabled (bug 933432)
-add_test(function test_shouldBlock() {
-  gAppRep.queryReputation({
-    sourceURI: createURI("http://evil.com"),
-    fileSize: 12,
-  }, function onComplete(aShouldBlock, aStatus) {
-    do_check_true(aShouldBlock);
-    do_check_eq(Cr.NS_OK, aStatus);
-    run_next_test();
-  });
-});
-
-add_test(function test_shouldNotBlock() {
-  gAppRep.queryReputation({
-    sourceURI: createURI("http://mozilla.com"),
-    fileSize: 12,
-  }, function onComplete(aShouldBlock, aStatus) {
-    do_check_eq(Cr.NS_OK, aStatus);
-    do_check_false(aShouldBlock);
-    run_next_test();
-  });
-});
-*/
-
 add_test(function test_nullSourceURI() {
   gAppRep.queryReputation({
     // No source URI
     fileSize: 12,
   }, function onComplete(aShouldBlock, aStatus) {
     do_check_eq(Cr.NS_ERROR_UNEXPECTED, aStatus);
     do_check_false(aShouldBlock);
     run_next_test();
@@ -159,33 +101,16 @@ add_test(function test_disabled() {
   }, function onComplete(aShouldBlock, aStatus) {
     // We should be getting NS_ERROR_NOT_AVAILABLE if the service is disabled
     do_check_eq(Cr.NS_ERROR_NOT_AVAILABLE, aStatus);
     do_check_false(aShouldBlock);
     run_next_test();
   });
 });
 
-/*
-// Uncomment when remote lookups are enabled (bug 933432)
-add_test(function test_garbage() {
-  Services.prefs.setCharPref("browser.safebrowsing.appRepURL",
-                             "http://localhost:4444/download");
-  gAppRep.queryReputation({
-    sourceURI: createURI("http://whitelisted.com"),
-    fileSize: 12,
-  }, function onComplete(aShouldBlock, aStatus) {
-    // We should be getting the garbage response.
-    do_check_eq(Cr.NS_ERROR_CANNOT_CONVERT_DATA, aStatus);
-    do_check_false(aShouldBlock);
-    run_next_test();
-  });
-});
-*/
-
 // Set up the local whitelist.
 add_test(function test_local_list() {
   // Construct a response with redirect urls.
   function processUpdateRequest() {
     let response = "n:1000\n";
     for (let table in gTables) {
       response += "i:" + table + "\n";
       for (let i = 0; i < gTables[table].length; ++i) {
new file mode 100644
--- /dev/null
+++ b/toolkit/components/downloads/test/unit/test_app_rep_windows.js
@@ -0,0 +1,331 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * This file tests signature extraction using Windows Authenticode APIs of
+ * downloaded files.
+ */
+
+////////////////////////////////////////////////////////////////////////////////
+//// Globals
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+                                  "resource://gre/modules/FileUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
+                                  "resource://gre/modules/NetUtil.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+                                  "resource://gre/modules/commonjs/sdk/core/promise.js");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+                                  "resource://gre/modules/Task.jsm");
+
+const BackgroundFileSaverOutputStream = Components.Constructor(
+      "@mozilla.org/network/background-file-saver;1?mode=outputstream",
+      "nsIBackgroundFileSaver");
+
+const StringInputStream = Components.Constructor(
+      "@mozilla.org/io/string-input-stream;1",
+      "nsIStringInputStream",
+      "setData");
+
+const TEST_FILE_NAME_1 = "test-backgroundfilesaver-1.txt";
+
+const gAppRep = Cc["@mozilla.org/downloads/application-reputation-service;1"].
+                  getService(Ci.nsIApplicationReputationService);
+let gStillRunning = true;
+let gTables = {};
+let gHttpServer = null;
+
+/**
+ * Returns a reference to a temporary file.  If the file is then created, it
+ * will be removed when tests in this file finish.
+ */
+function getTempFile(aLeafName) {
+  let file = FileUtils.getFile("TmpD", [aLeafName]);
+  do_register_cleanup(function GTF_cleanup() {
+    if (file.exists()) {
+      file.remove(false);
+    }
+  });
+  return file;
+}
+
+function readFileToString(aFilename) {
+  let f = do_get_file(aFilename);
+  let stream = Cc["@mozilla.org/network/file-input-stream;1"]
+                 .createInstance(Ci.nsIFileInputStream);
+  stream.init(f, -1, 0, 0);
+  let buf = NetUtil.readInputStreamToString(stream, stream.available());
+  return buf;
+}
+
+/**
+ * Waits for the given saver object to complete.
+ *
+ * @param aSaver
+ *        The saver, with the output stream or a stream listener implementation.
+ * @param aOnTargetChangeFn
+ *        Optional callback invoked with the target file name when it changes.
+ *
+ * @return {Promise}
+ * @resolves When onSaveComplete is called with a success code.
+ * @rejects With an exception, if onSaveComplete is called with a failure code.
+ */
+function promiseSaverComplete(aSaver, aOnTargetChangeFn) {
+  let deferred = Promise.defer();
+  aSaver.observer = {
+    onTargetChange: function BFSO_onSaveComplete(aSaver, aTarget)
+    {
+      if (aOnTargetChangeFn) {
+        aOnTargetChangeFn(aTarget);
+      }
+    },
+    onSaveComplete: function BFSO_onSaveComplete(aSaver, aStatus)
+    {
+      if (Components.isSuccessCode(aStatus)) {
+        deferred.resolve();
+      } else {
+        deferred.reject(new Components.Exception("Saver failed.", aStatus));
+      }
+    },
+  };
+  return deferred.promise;
+}
+
+/**
+ * Feeds a string to a BackgroundFileSaverOutputStream.
+ *
+ * @param aSourceString
+ *        The source data to copy.
+ * @param aSaverOutputStream
+ *        The BackgroundFileSaverOutputStream to feed.
+ * @param aCloseWhenDone
+ *        If true, the output stream will be closed when the copy finishes.
+ *
+ * @return {Promise}
+ * @resolves When the copy completes with a success code.
+ * @rejects With an exception, if the copy fails.
+ */
+function promiseCopyToSaver(aSourceString, aSaverOutputStream, aCloseWhenDone) {
+  let deferred = Promise.defer();
+  let inputStream = new StringInputStream(aSourceString, aSourceString.length);
+  let copier = Cc["@mozilla.org/network/async-stream-copier;1"]
+               .createInstance(Ci.nsIAsyncStreamCopier);
+  copier.init(inputStream, aSaverOutputStream, null, false, true, 0x8000, true,
+              aCloseWhenDone);
+  copier.asyncCopy({
+    onStartRequest: function () { },
+    onStopRequest: function (aRequest, aContext, aStatusCode)
+    {
+      if (Components.isSuccessCode(aStatusCode)) {
+        deferred.resolve();
+      } else {
+        deferred.reject(new Components.Exception(aResult));
+      }
+    },
+  }, null);
+  return deferred.promise;
+}
+
+// Registers a table for which to serve update chunks.
+function registerTableUpdate(aTable, aFilename) {
+  // If we haven't been given an update for this table yet, add it to the map
+  if (!(aTable in gTables)) {
+    gTables[aTable] = [];
+  }
+
+  // The number of chunks associated with this table.
+  let numChunks = gTables[aTable].length + 1;
+  let redirectPath = "/" + aTable + "-" + numChunks;
+  let redirectUrl = "localhost:4444" + redirectPath;
+
+  // Store redirect url for that table so we can return it later when we
+  // process an update request.
+  gTables[aTable].push(redirectUrl);
+
+  gHttpServer.registerPathHandler(redirectPath, function(request, response) {
+    do_print("Mock safebrowsing server handling request for " + redirectPath);
+    let contents = readFileToString(aFilename);
+    do_print("Length of " + aFilename + ": " + contents.length);
+    response.setHeader("Content-Type",
+                       "application/vnd.google.safebrowsing-update", false);
+    response.setStatusLine(request.httpVersion, 200, "OK");
+    response.bodyOutputStream.write(contents, contents.length);
+  });
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// Tests
+
+function run_test()
+{
+  run_next_test();
+}
+
+add_task(function test_setup()
+{
+  // Wait 10 minutes, that is half of the external xpcshell timeout.
+  do_timeout(10 * 60 * 1000, function() {
+    if (gStillRunning) {
+      do_throw("Test timed out.");
+    }
+  });
+  // Set up a local HTTP server to return bad verdicts.
+  Services.prefs.setCharPref("browser.safebrowsing.appRepURL",
+                             "http://localhost:4444/download");
+  // Ensure safebrowsing is enabled for this test, even if the app
+  // doesn't have it enabled.
+  Services.prefs.setBoolPref("browser.safebrowsing.malware.enabled", true);
+  do_register_cleanup(function() {
+    Services.prefs.clearUserPref("browser.safebrowsing.malware.enabled");
+  });
+
+  gHttpServer = new HttpServer();
+  gHttpServer.registerDirectory("/", do_get_cwd());
+
+  function createVerdict(aShouldBlock) {
+    // We can't programmatically create a protocol buffer here, so just
+    // hardcode some already serialized ones.
+    blob = String.fromCharCode(parseInt(0x08, 16));
+    if (aShouldBlock) {
+      // A safe_browsing::ClientDownloadRequest with a DANGEROUS verdict
+      blob += String.fromCharCode(parseInt(0x01, 16));
+    } else {
+      // A safe_browsing::ClientDownloadRequest with a SAFE verdict
+      blob += String.fromCharCode(parseInt(0x00, 16));
+    }
+    return blob;
+  }
+
+  gHttpServer.registerPathHandler("/throw", function(request, response) {
+    do_throw("We shouldn't be getting here");
+  });
+
+  gHttpServer.registerPathHandler("/download", function(request, response) {
+    response.setHeader("Content-Type", "application/octet-stream", false);
+    let buf = NetUtil.readInputStreamToString(
+      request.bodyInputStream,
+      request.bodyInputStream.available());
+    do_print("Request length: " + buf.length);
+    // A garbage response. By default this produces NS_CANNOT_CONVERT_DATA as
+    // the callback status.
+    let blob = "this is not a serialized protocol buffer";
+    // We can't actually parse the protocol buffer here, so just switch on the
+    // length instead of inspecting the contents.
+    if (buf.length == 35) {
+      // evil.com
+      blob = createVerdict(true);
+    } else if (buf.length == 38) {
+      // mozilla.com
+      blob = createVerdict(false);
+    }
+    response.bodyOutputStream.write(blob, blob.length);
+  });
+
+  gHttpServer.start(4444);
+});
+
+// Construct a response with redirect urls.
+function processUpdateRequest() {
+  let response = "n:1000\n";
+  for (let table in gTables) {
+    response += "i:" + table + "\n";
+    for (let i = 0; i < gTables[table].length; ++i) {
+      response += "u:" + gTables[table][i] + "\n";
+    }
+  }
+  do_print("Returning update response: " + response);
+  return response;
+}
+
+// Set up the local whitelist.
+function waitForUpdates() {
+  let deferred = Promise.defer();
+  gHttpServer.registerPathHandler("/downloads", function(request, response) {
+    let buf = NetUtil.readInputStreamToString(request.bodyInputStream,
+      request.bodyInputStream.available());
+    let blob = processUpdateRequest();
+    response.setHeader("Content-Type",
+                       "application/vnd.google.safebrowsing-update", false);
+    response.setStatusLine(request.httpVersion, 200, "OK");
+    response.bodyOutputStream.write(blob, blob.length);
+  });
+
+  let streamUpdater = Cc["@mozilla.org/url-classifier/streamupdater;1"]
+    .getService(Ci.nsIUrlClassifierStreamUpdater);
+  streamUpdater.updateUrl = "http://localhost:4444/downloads";
+
+  // Load up some update chunks for the safebrowsing server to serve. This
+  // particular chunk contains the hash of whitelisted.com/ and
+  // sb-ssl.google.com/safebrowsing/csd/certificate/.
+  registerTableUpdate("goog-downloadwhite-digest256", "data/digest.chunk");
+
+  // Resolve the promise once processing the updates is complete.
+  function updateSuccess(aEvent) {
+    // Timeout of n:1000 is constructed in processUpdateRequest above and
+    // passed back in the callback in nsIUrlClassifierStreamUpdater on success.
+    do_check_eq("1000", aEvent);
+    do_print("All data processed");
+    deferred.resolve(true);
+  }
+  // Just throw if we ever get an update or download error.
+  function handleError(aEvent) {
+    do_throw("We didn't download or update correctly: " + aEvent);
+    deferred.reject();
+  }
+  streamUpdater.downloadUpdates(
+    "goog-downloadwhite-digest256",
+    "goog-downloadwhite-digest256;\n",
+    updateSuccess, handleError, handleError);
+  return deferred.promise;
+}
+
+function promiseQueryReputation(query, expectedShouldBlock) {
+  let deferred = Promise.defer();
+  function onComplete(aShouldBlock, aStatus) {
+    do_check_eq(Cr.NS_OK, aStatus);
+    do_check_eq(aShouldBlock, expectedShouldBlock);
+    deferred.resolve(true);
+  }
+  gAppRep.queryReputation(query, onComplete);
+  return deferred.promise;
+}
+
+add_task(function test_signature_whitelists()
+{
+  // We should never get to the remote server.
+  Services.prefs.setCharPref("browser.safebrowsing.appRepURL",
+                             "http://localhost:4444/throw");
+  // Wait for Safebrowsing local list updates to complete.
+  yield waitForUpdates();
+
+  // Use BackgroundFileSaver to extract the signature on Windows.
+  let destFile = getTempFile(TEST_FILE_NAME_1);
+
+  let data = readFileToString("data/signed_win.exe");
+  let saver = new BackgroundFileSaverOutputStream();
+  let completionPromise = promiseSaverComplete(saver);
+  saver.enableSignatureInfo();
+  saver.setTarget(destFile, false);
+  yield promiseCopyToSaver(data, saver, true);
+
+  saver.finish(Cr.NS_OK);
+  yield completionPromise;
+
+  // Clean up.
+  destFile.remove(false);
+
+  // evil.com is not on the allowlist, but this binary is signed by an entity
+  // whose certificate information is on the allowlist.
+  yield promiseQueryReputation({sourceURI: createURI("http://evil.com"),
+                                signatureInfo: saver.signatureInfo,
+                                fileSize: 12}, false);
+});
+
+add_task(function test_teardown()
+{
+  gStillRunning = false;
+});
--- a/toolkit/components/downloads/test/unit/xpcshell.ini
+++ b/toolkit/components/downloads/test/unit/xpcshell.ini
@@ -1,18 +1,21 @@
 [DEFAULT]
 head = head_download_manager.js
 tail = tail_download_manager.js
 firefox-appdir = browser
 support-files =
   downloads_manifest.js
   test_downloads.manifest
   data/digest.chunk
+  data/signed_win.exe
 
 [test_app_rep.js]
+[test_app_rep_windows.js]
+run-if = os == "win"
 [test_bug_382825.js]
 [test_bug_384744.js]
 [test_bug_395092.js]
 [test_bug_401430.js]
 [test_bug_406857.js]
 [test_bug_420230.js]
 [test_cancel_download_files_removed.js]
 # Bug 676989: test hangs consistently on Android