On Wed, 2008-04-02 at 20:31 +0200, Jelmer Vernooij wrote:
> On Mi, 2008-04-02 at 17:32 +0200, Daniel Schierbeck wrote:
> > This patch makes the revision view use Seahorse to verify signatures.
> bb:resubmit
> 
> There is a copyright header missing on top of crypt.py. 
> 
> Please skip the signature tab if dbus was not found or the seahorse
> interfaces weren't accessible.

If tested this with and without Seahorse, and missing DBus should also
cause the Signature tab to be hidden.

I've also added the copyright information.


Cheers,
Daniel
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: [EMAIL PROTECTED]
#   v94g99u4v3z9jvg2
# target_branch: file:///home/daniel/Projects/bzr-gtk/trunk/
# testament_sha1: 228b4023025c81c7f045444d7d4d518f1506df55
# timestamp: 2008-04-02 23:02:27 +0200
# base_revision_id: [EMAIL PROTECTED]
# 
# Begin patch
=== modified file 'NEWS'
--- NEWS	2008-04-01 10:20:12 +0000
+++ NEWS	2008-04-02 15:32:02 +0000
@@ -6,6 +6,8 @@
 
   * Use new D-Bus revision signal. (James Henstridge, #206443)
 
+  * Switched to using Seahorse to verify signatures. (Daniel Schierbeck)
+
  UI
 
   * Made the tags associated with a revision render next to the revision

=== added file 'crypt.py'
--- crypt.py	1970-01-01 00:00:00 +0000
+++ crypt.py	2008-04-02 21:02:12 +0000
@@ -0,0 +1,125 @@
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+__copyright__ = 'Copyright © 2008 Daniel Schierbeck'
+__author__ = 'Daniel Schierbeck <[EMAIL PROTECTED]>'
+
+import dbus
+
+BUS_NAME = 'org.gnome.seahorse'
+
+CRYPTO_INTERFACE = 'org.gnome.seahorse.CryptoService'
+CRYPTO_PATH = '/org/gnome/seahorse/crypto'
+
+OPENPGP_INTERFACE = 'org.gnome.seahorse.Keys'
+OPENPGP_PATH = '/org/gnome/seahorse/keys/openpgp'
+
+KEY_TYPE_OPENPGP = 'openpgp'
+KEY_TYPE_SSH = 'ssh'
+
+bus = dbus.SessionBus()
+
+if not bus.name_has_owner(BUS_NAME):
+    raise ImportError
+
+crypto = dbus.Interface(bus.get_object(BUS_NAME, CRYPTO_PATH), 
+                        CRYPTO_INTERFACE)
+openpgp = dbus.Interface(bus.get_object(BUS_NAME, OPENPGP_PATH),
+                         OPENPGP_INTERFACE)
+
+FLAG_VALID = 0x0001
+FLAG_CAN_ENCRYPT = 0x0002
+FLAG_CAN_SIGN = 0x0004
+FLAG_EXPIRED = 0x0100
+FLAG_REVOKED = 0x0200
+FLAG_DISABLED = 0x0400
+FLAG_TRUSTED = 0x1000
+
+TRUST_NEVER = -1
+TRUST_UNKNOWN = 0
+TRUST_MARGINAL = 1
+TRUST_FULL = 5
+TRUST_ULTIMATE = 10
+
+LOCATION_MISSING = 10
+LOCATION_SEARCHING = 20
+LOCATION_REMOTE = 50
+LOCATION_LOCAL = 100
+
+def discover(*key_ids):
+    return openpgp.DiscoverKeys(key_ids, 0)
+
+def verify(crypttext):
+    (cleartext, key) = crypto.VerifyText(KEY_TYPE_OPENPGP, 1, crypttext)
+
+    return Key(key)
+
+class Key:
+
+    def __init__(self, key):
+        self.key = key
+        self.fingerprint = None
+        self.trust = None
+        self.flags = None
+        self.display_name = None
+        self.location = None
+
+        discover(key)
+
+    def get_field(self, field, default=None):
+        (valid, value) = openpgp.GetKeyField(self.key, field)
+
+        if valid:
+            return value
+        else:
+            return default
+    
+    def get_flags(self):
+        if self.flags is None:
+            self.flags = self.get_field('flags', 0)
+
+        return self.flags
+
+    def get_display_name(self):
+        if self.display_name is None:
+            self.display_name = self.get_field('display-name')
+
+        return self.display_name
+
+    def get_id(self):
+        return self.key.split(':')[1][8:]
+
+    def get_fingerprint(self):
+        if self.fingerprint is None:
+            self.fingerprint = self.get_field('fingerprint')
+
+        return self.fingerprint
+
+    def get_trust(self):
+        if self.trust is None:
+            self.trust = self.get_field('trust', TRUST_UNKNOWN)
+
+        return self.trust
+
+    def get_location(self):
+        if self.location is None:
+            self.location = self.get_field('location', LOCATION_MISSING)
+
+        return self.location
+
+    def is_available(self):
+        return self.key != ""
+
+    def is_trusted(self):
+        return self.get_flags() & FLAG_TRUSTED

=== removed file 'gpg.py'
--- gpg.py	2007-11-21 20:04:21 +0000
+++ gpg.py	1970-01-01 00:00:00 +0000
@@ -1,173 +0,0 @@
-# Code for running GnuPG in batch mode and dealing with the results
-
-__rcsid__ = '$Id: GPG.py,v 1.1.1.1 2000/04/17 02:17:24 amk Exp $'
-
-import os, string
-import cStringIO, popen2
-
-class Signature:
-    "Class used to hold information about a signature result"
-
-    def __init__(self):
-        self.valid = 0
-        self.fingerprint = self.creation_date = self.timestamp = None
-        self.signature_id = self.key_id = None
-        self.username = None
-        
-    def is_valid(self):
-        return self.valid
-    
-class GPGSubprocess:
-
-    # Default path used for searching for the GPG binary, when the
-    # PATH environment variable isn't set.
-    DEFAULT_PATH = ['/bin', '/usr/bin', '/usr/local/bin']
-    
-    def __init__(self, gpg_binary = None):
-        """Initialize an object instance.  Options are:
-
-        gpg_binary -- full pathname for GPG binary.  If not supplied,
-        the current value of PATH will be searched, falling back to the
-        DEFAULT_PATH class variable if PATH isn't available.
-        """
-
-        # If needed, look for the gpg binary along the path
-        if gpg_binary is None:
-            import os
-            if os.environ.has_key('PATH'):
-                path = os.environ['PATH']
-                path = string.split(path, os.pathsep)
-            else:
-                path = self.DEFAULT_PATH
-
-            for dir in path:
-                fullname = os.path.join(dir, 'gpg')
-                if os.path.exists( fullname ):
-                    gpg_binary = fullname
-                    break
-            else:
-                raise ValueError, ("Couldn't find 'gpg' binary on path"
-                                   + repr(path) )
-            
-        self.gpg_binary = gpg_binary
-
-    def verify(self, data):
-        "Verify the signature on the contents of the string 'data'"
-        file = cStringIO.StringIO( data )
-        return self.verify_file( file )
-    
-    def verify_file(self, file):
-        "Verify the signature on the contents of the file-like object 'file'"
-        child_stdout, child_stdin, child_stderr = self._open_subprocess()
-
-        # Copy the file to the GPG subprocess
-        while 1:
-            data = file.read(1024)
-            if data == "": break
-            child_stdin.write(data)
-
-        child_stdin.close()
-        
-        # Get the response information
-        resp = self._read_response(child_stderr)
-
-        # Create an object to return, and fill it with data
-        sig = Signature()
-        if resp.has_key('BADSIG'):
-            sig.valid = 0
-            sig.key_id, sig.username = string.split(resp['BADSIG'], None, 1)
-        elif resp.has_key('GOODSIG'):
-            sig.valid = 1
-            sig.key_id, sig.username = string.split(resp['GOODSIG'], None, 1)
-
-        if resp.has_key('VALIDSIG'):
-            L = string.split(resp['VALIDSIG'], None)
-            sig.fingerprint, sig.creation_date, sig.timestamp = L[0], L[1], L[2]
-
-        if resp.has_key('SIG_ID'):
-            L = string.split(resp['SIG_ID'], None)
-            sig.signature_id, sig.creation_date, sig.timestamp = L
-
-        # Read the contents of the file from GPG's stdout
-        sig.data = ""
-        while 1:
-            data = child_stdout.read(1024)
-            if data == "": break
-            sig.data = sig.data + data
-            
-        return sig
-    
-    def _open_subprocess(self, *args):
-        # Internal method: open a pipe to a GPG subprocess and return
-        # the file objects for communicating with it.
-
-        cmd = self.gpg_binary + ' --status-fd 2 ' + string.join(args)
-        
-        child_stdout, child_stdin, child_stderr = popen2.popen3(cmd)
-        return child_stdout, child_stdin, child_stderr
-
-    def _read_response(self, child_stdout):
-        # Internal method: reads all the output from GPG, taking notice
-        # only of lines that begin with the magic [GNUPG:] prefix.
-        # (See doc/DETAILS in the GPG distribution for info on GPG's
-        # output when --status-fd is specified.)
-        #
-        # Returns a dictionary, mapping GPG's keywords to the arguments
-        # for that keyword.
-        
-        resp = {}
-        while 1:
-            line = child_stdout.readline()
-            if line == "": break
-            line = string.rstrip( line )
-            if line[0:9] == '[GNUPG:] ':
-                # Chop off the prefix
-                line = line[9:]
-                L = string.split(line, None, 1)
-                keyword = L[0]
-                if len(L) > 1:
-                    resp[ keyword ] = L[1]
-                else:
-                    resp[ keyword ] = ""
-        return resp
-    
-
-    # Not yet implemented, because I don't need these methods
-    # The methods certainly don't have all the parameters they'd need.
-    
-    def sign(self, data):
-        "Sign the contents of the string 'data'"
-        pass
-
-    def sign_file(self, file):
-        "Sign the contents of the file-like object 'file'"
-        pass
-
-    def encrypt_file(self, file):
-        "Encrypt the message read from the file-like object 'file'"
-        pass
-
-    def encrypt(self, data):
-        "Encrypt the message contained in the string 'data'"
-        pass
-
-    def decrypt_file(self, file):
-        "Decrypt the message read from the file-like object 'file'"
-        pass
-
-    def decrypt(self, data):
-        "Decrypt the message contained in the string 'data'"
-        pass
-
-    
-if __name__ == '__main__':
-    import sys
-    if len(sys.argv) == 1:
-        print 'Usage: GPG.py <signed file>'
-        sys.exit()
-
-    obj = GPGSubprocess()
-    file = open(sys.argv[1], 'rb')
-    sig = obj.verify_file( file )
-    print sig.__dict__
-

=== modified file 'revisionview.py'
--- revisionview.py	2008-03-31 21:59:11 +0000
+++ revisionview.py	2008-04-02 20:59:49 +0000
@@ -21,17 +21,26 @@
 import pango
 import gobject
 import subprocess
+import dbus
 
 from bzrlib.plugins.gtk import icon_path
 from bzrlib.osutils import format_date
 from bzrlib.util.bencode import bdecode
 
+try:
+    from bzrlib.plugins.gtk import crypt
+except ImportError:
+    has_crypt = False
+else:
+    has_crypt = True
+
 def _open_link(widget, uri):
     subprocess.Popen(['sensible-browser', uri], close_fds=True)
 
 gtk.link_button_set_uri_hook(_open_link)
 
 class BugsTab(gtk.Table):
+
     def __init__(self):
         super(BugsTab, self).__init__(rows=5, columns=2)
         self.set_row_spacings(6)
@@ -56,58 +65,141 @@
 
 
 class SignatureTab(gtk.VBox):
-    def __init__(self):
-        from gpg import GPGSubprocess
-        self.gpg = GPGSubprocess()
+
+    def __init__(self, repository):
+        self.key = None
+        self.revision = None
+        self.repository = repository
+
         super(SignatureTab, self).__init__(False, 6)
-        signature_box = gtk.Table(rows=1, columns=2)
-        signature_box.set_col_spacing(0, 12)
+        signature_box = gtk.Table(rows=3, columns=3)
+        signature_box.set_col_spacing(0, 16)
+        signature_box.set_col_spacing(1, 12)
+        signature_box.set_row_spacings(6)
 
         self.signature_image = gtk.Image()
         signature_box.attach(self.signature_image, 0, 1, 0, 1, gtk.FILL)
 
+        align = gtk.Alignment(0.0, 0.1)
         self.signature_label = gtk.Label()
-        signature_box.attach(self.signature_label, 1, 2, 0, 1, gtk.FILL)
-
-        signature_info = gtk.Table(rows=1, columns=2)
-        signature_info.set_row_spacings(6)
-        signature_info.set_col_spacings(6)
-
-        align = gtk.Alignment(1.0, 0.5)
-        label = gtk.Label()
-        label.set_markup("<b>Key Id:</b>")
-        align.add(label)
-        signature_info.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
+        align.add(self.signature_label)
+        signature_box.attach(align, 1, 3, 0, 1, gtk.FILL)
+
+        align = gtk.Alignment(0.0, 0.5)
+        self.signature_key_id_label = gtk.Label()
+        self.signature_key_id_label.set_markup("<b>Key Id:</b>")
+        align.add(self.signature_key_id_label)
+        signature_box.attach(align, 1, 2, 1, 2, gtk.FILL, gtk.FILL)
 
         align = gtk.Alignment(0.0, 0.5)
         self.signature_key_id = gtk.Label()
         self.signature_key_id.set_selectable(True)
         align.add(self.signature_key_id)
-        signature_info.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
+        signature_box.attach(align, 2, 3, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
+
+        align = gtk.Alignment(0.0, 0.5)
+        self.signature_fingerprint_label = gtk.Label()
+        self.signature_fingerprint_label.set_markup("<b>Fingerprint:</b>")
+        align.add(self.signature_fingerprint_label)
+        signature_box.attach(align, 1, 2, 2, 3, gtk.FILL, gtk.FILL)
+
+        align = gtk.Alignment(0.0, 0.5)
+        self.signature_fingerprint = gtk.Label()
+        self.signature_fingerprint.set_selectable(True)
+        align.add(self.signature_fingerprint)
+        signature_box.attach(align, 2, 3, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
+
+        align = gtk.Alignment(0.0, 0.5)
+        self.signature_trust_label = gtk.Label()
+        self.signature_trust_label.set_markup("<b>Trust:</b>")
+        align.add(self.signature_trust_label)
+        signature_box.attach(align, 1, 2, 3, 4, gtk.FILL, gtk.FILL)
+
+        align = gtk.Alignment(0.0, 0.5)
+        self.signature_trust = gtk.Label()
+        self.signature_trust.set_selectable(True)
+        align.add(self.signature_trust)
+        signature_box.attach(align, 2, 3, 3, 4, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
         self.set_border_width(6)
         self.pack_start(signature_box, expand=False)
-        self.pack_start(signature_info, expand=False)
         self.show_all()
 
+    def set_revision(self, revision):
+        self.revision = revision
+        revid = revision.revision_id
+
+        if self.repository.has_signature_for_revision_id(revid):
+            signature_text = self.repository.get_signature_text(revid)
+            self.show_signature(signature_text)
+        else:
+            self.show_no_signature()
+
     def show_no_signature(self):
+        self.signature_key_id_label.hide()
         self.signature_key_id.set_text("")
+
+        self.signature_fingerprint_label.hide()
+        self.signature_fingerprint.set_text("")
+
+        self.signature_trust_label.hide()
+        self.signature_trust.set_text("")
+
         self.signature_image.set_from_file(icon_path("sign-unknown.png"))
-        self.signature_label.set_text("This revision has not been signed.")
-
-    def show_signature(self, text):
-        signature = self.gpg.verify(text)
-
-        if signature.key_id is not None:
-            self.signature_key_id.set_text(signature.key_id)
-
-        if signature.is_valid():
-            self.signature_image.set_from_file(icon_path("sign-ok.png"))
-            self.signature_label.set_text("This revision has been signed.")
+        self.signature_label.set_markup("<b>Authenticity unknown</b>\n" +
+                                        "This revision has not been signed.")
+
+    def show_signature(self, crypttext):
+        key = crypt.verify(crypttext)
+
+        if key.is_available():
+            if key.is_trusted():
+                if key.get_display_name() == self.revision.committer:
+                    self.signature_image.set_from_file(icon_path("sign-ok.png"))
+                    self.signature_label.set_markup("<b>Authenticity confirmed</b>\n" +
+                                                    "This revision has been signed with " +
+                                                    "a trusted key.")
+                else:
+                    self.signature_image.set_from_file(icon_path("sign-bad.png"))
+                    self.signature_label.set_markup("<b>Authenticity cannot be confirmed</b>\n" +
+                                                    "Revision committer is not the same as signer.")
+            else:
+                self.signature_image.set_from_file(icon_path("sign-bad.png"))
+                self.signature_label.set_markup("<b>Authenticity cannot be confirmed</b>\n" +
+                                                "This revision has been signed, but the " +
+                                                "key is not trusted.")
         else:
+            self.show_no_signature()
             self.signature_image.set_from_file(icon_path("sign-bad.png"))
-            self.signature_label.set_text("This revision has been signed, " + 
-                    "but the authenticity of the signature cannot be verified.")
+            self.signature_label.set_markup("<b>Authenticity cannot be confirmed</b>\n" +
+                                            "Signature key not available.")
+            return
+
+        trust = key.get_trust()
+
+        if trust <= crypt.TRUST_NEVER:
+            trust_text = 'never trusted'
+        elif trust == crypt.TRUST_UNKNOWN:
+            trust_text = 'not trusted'
+        elif trust == crypt.TRUST_MARGINAL:
+            trust_text = 'marginally trusted'
+        elif trust == crypt.TRUST_FULL:
+            trust_text = 'fully trusted'
+        elif trust == crypt.TRUST_ULTIMATE:
+            trust_text = 'ultimately trusted'
+
+        self.signature_key_id_label.show()
+        self.signature_key_id.set_text(key.get_id())
+
+        fingerprint = key.get_fingerprint()
+        if fingerprint == "":
+            fingerprint = '<span foreground="dim grey">N/A</span>'
+
+        self.signature_fingerprint_label.show()
+        self.signature_fingerprint.set_markup(fingerprint)
+
+        self.signature_trust_label.show()
+        self.signature_trust.set_text('This key is ' + trust_text)
 
 
 class RevisionView(gtk.Notebook):
@@ -151,9 +243,13 @@
     def __init__(self, branch=None):
         gtk.Notebook.__init__(self)
 
+        self._revision = None
+        self._branch = branch
+
         self._create_general()
         self._create_relations()
-        self._create_signature()
+        if has_crypt:
+            self._create_signature()
         self._create_file_info_view()
         self._create_bugs()
 
@@ -280,13 +376,7 @@
         self._add_tags()
 
     def _update_signature(self, widget, param):
-        revid = self._revision.revision_id
-
-        if self._branch.repository.has_signature_for_revision_id(revid):
-            signature_text = self._branch.repository.get_signature_text(revid)
-            self.signature_table.show_signature(signature_text)
-        else:
-            self.signature_table.show_no_signature()
+        self.signature_table.set_revision(self._revision)
 
     def set_children(self, children):
         self._add_parents_or_children(children,
@@ -374,11 +464,7 @@
         vbox.show()
 
     def _create_signature(self):
-        try:
-            self.signature_table = SignatureTab()
-        except ValueError: # No GPG found
-            self.signature_table = None
-            return
+        self.signature_table = SignatureTab(self._branch.repository)
         self.append_page(self.signature_table, tab_label=gtk.Label('Signature'))
         self.connect_after('notify::revision', self._update_signature)
 

# Begin bundle
IyBCYXphYXIgcmV2aXNpb24gYnVuZGxlIHY0CiMKQlpoOTFBWSZTWaqwBWUAP71/lGxUVgB9////
f////r////QAIBAAYD/ebw+vp99329x93Cue9tO709K4289xPenmPt4OXl7O7PdyfRVfW3efN9fN
JH2276e9JL1uvLqk9Lu4Ldxz6NQOzfb64CddcD03vY4e2XWoOx4HwrjTQADQKoJieHe7oG1HTOWE
sPMOdwbt3vnPvhV8+mIPUzxS557AdtRQKdBRoK0oChopdsSnzYFHptmmtaUJKSaaNMjQyGgTA0gy
ZMAmRpk0TTKMkajQDTZQCUQAARBJ6Q1Gp6SeU/VPKZplPKbU00eUAAAAAAxBGhJAieI0ZNQ9qgT0
E9IxkyEMgDTJgmmCZAk0kk0QjIKn+kwaVP1PCNU9qbRpRvSmm9UbUeoNAaGgaG0ECKRAQEaYExAm
CYTTEDINRptJpk01PUoeoZqMRgkiBNGQmhACek0p4ap7UYozRT8oniI9T1NAAAAyXQNJXIVzgImm
QoJNytMP/vFOo/tT/VFETiZnn/iDlj6/6f6+iKG5CW2fmaHLxHSC81nl5+jpqOmqDHTTDpEpNk6k
4LXF9b6fm0CIylm6q2+DMYLFzGwyzSjWjrkbaOb3yE8R3YlqSGZgL+FPkyhWefK7Us7tPkwvGQ8O
GD13wGHNl/XrJhvSVEfIhcv6er9E8DL3he8V802z742xv3xviG375eU13zLcMf95dxeeXrjqP7I+
kXmlNpsV/VL0HQ/IV/fW5QXh3tnxiPMqoVdxEGgNB4eeYJd8OGPaLqxk58zXtpq9PCaFVNU9FNtU
0nTdD0SY5nnDPMj18PA9+TOJ0AyQU6YuUAhvrU1571UFWDIoLHiZznG6drYw6uFNuO0wFODSt41r
ARBrsMyRRRPh823rBQ33smMM/ByPPz9DnN2ykQPMftMqFd+xL/3oTGp82u446ytPnUQS/v+/TnW/
drp9e/x8luSj9P849V7++P/vTqv3bv+eeNv0AicCqwCJpBYRfsBNdPf8gIMgg36zjDJlw7vQ5ITc
/XNr0ryouoTsQMUEgd2WtZvzPg+/+H0H1U+Fd4qpWAIn7FaYhlTpt9nVVTtBf9/8e6y6IrXDlgQt
hS2AlBKFcFMhxk0yFQOhJ15etAkmnuoL7fDx4nJkhzJdHPQ3J5x0ptQd1wQ7yy3PaaZwhalbIw4B
gWghkgQ8q24AnEXs11IVhYRbM5zN1Au7tZrM0iHnGRbFXvaqTRkEnEvcAjCB/79Iz3d2ACVkCHYX
z6PnD3w+bR3fCWUG+XdGvP8u/j86NTDz7YgjZhc31xpuexUR9kIKjrVRYUSITg8K7HRAb7fZN/D6
FaewJO9t2kGDIougDn6wk/N3tfzQh8tN8AA+N90APBAUCMhFkAUFURFFFgoLEYjFiokBGCrFFiMY
yKKLBYTac8NkC/Ev928fSKDF+Xk088ueKPIC8ODCLihGBZQLF8S8cnY9WUfPZnbva6M5uxhNuHCw
hTEAt1TNF2US1jwallGFNMBlGldYRF4Y0kiZCF88OIGRApAAWKxOkZVxTUjSEU0ICZs+VnMrjFCJ
Js4u5siE8PD/1uWlqsMNe0Y5jFDC5WLsycwugm1G0nhHB7oKkgUXNGSGsI7oBIu9ZUrEQlTPO7VS
E42Ie902xYU+lnRV5Uai7zdnw0FLtzB06NKNvCcUqPZK7JLNVZeoQdO0IRapSLABBtnLM7jWmR3M
+zZQMxWzQlTMLKQVcOocRbbEClOmOcXvAshLMgFhcnQvEF+AQH2eQw/6/s+H6bQ/l3+NW7+z1+Ts
mij7E+Z4ew+v9u/I5EN/2in2HKBVJ5Y4Swi5OTofbbwMPhy+HiH6/X9NEN+C8AS2jpb/3FtT6WqV
zWC5C+WQNg1dtQLugLluaHYBd8EQ6SULN8/f9eZEv/i3t2JSp/1zS7Tz3NWaPG2PyCvfBYNjEpVx
bQSH6tB7WMUvqKr+K3aLAeh2CSwhIYFq2ApuwGWMSU0qCJEcg50ymVnT49VD/KD6WV2ZZARBBW72
MsCGDgl5fYCUwSvVIypthqIuC/ewzM4FpgsMMf1vKBDWGZLta0SGgJYdEX6sw0M3EwJHvHUHXorB
vA0lAWKpSKvctGMZJqvdgSYQEnMcBgZ8xofpc4TslW/U9xVDn0ww4vt4BNta6ZAOlFFFFFFFFFFF
FFFFFFFFFFFFFFFBNcnjirtCwtUt3eoJPF04bnF36dqE35zlnNH0WYocAiSlcRAHDSvN0c1aiqx7
nTztZgIsJSbcO8qIhr/N2IcybJyVHhSWKqWcI+4EmH0h7jO9qzApuN56GGWneV157WQ4RCJSQXu/
sHh8fjf3HnwMvTtfx0Hd6F3imvQa4873o7fUOzb0L9pelOPRf4PA7+lc1ZKrlN7SC7ILjByKm+q9
kOemmGIti9GzGWPIv5V0sV8L4WSpBWK1cLyt1mt1ssVVfcsetL+Onjy8W8806+PF7wvOvnl18reY
6WG143qXK9ezl2x04zWEmugvexSUoy12JjpYyajuPZdPn0b07Sc7tTp1yu+rR/bu1mCHeyoslmql
bXTZZVq/9EbvgJHiEg0xxIc6ADTWLtmDTLLpv556dOmejXrLabmu/t2X9eCkF3ukFxguiKMaC/K/
8uJ8Ni1KSEgoO1kkLA/nkNwOkEmBL5e3o82rMHmJbtcOwgSd2ev6AeNPzzoswZMKgqgqwWmiSlEi
gdECw5wR418r5M5vp1qy2lyPDZonZ59AIm2U5oSJwGQA7D/HyjoFhwSQLUJnRRfLPYBn4mgQkviE
cScARIZIJAqtgduP4duy679c1pf+AZBzfTi/t+WPlwX02/BoSzgfNmYIko5G99Iek9frbXb3+bWA
2ObZX/AEumRN3OZtzGBrN0zGzumYsEzShvlREGXvgsc5lmCEeDGCD9g9FINbd0MfJEWCeOCnk+4O
TshF9mj8vYdffR12PxMot/4/aS/9SsmaZ+nT9vxgkZZ6upjCmyo33dwhqoB+038hPrJByhMsUWQC
Pmed9qTb3z2DOcDfHD5QZ079fSlkEyijFVegkV3JcZ+QFVwgid4InUAXLlxSuUlLnUjCOwKmybyd
4jEIzFIAAkkKCBvGYVIRhG8j482hMKPQjeRmC4RGaNYBQjmAMyMpIyFJBMAwawXpBbQwKhWj1gEl
YALwFiEyQiW3O2PUaO4yZizffvA0Zi+6BB7c2ZMhIEoM0Jga7AlPc02lGU3n16X1h/y5ub19L040
w+2c/jlJfK7cikSRtyEIWlnMZFxJE7xrS+vWByshAl30E6WCWlvKUvbhhnCMNbQNfdIdR0dsRj8Z
uqIiqoRipJBkhv5mF66qIMsKRgZITFqlOQTYC1k0qricEgln+MAAHBdk2L8AlXN16D8/3EqNQe+K
XdgxSEarxtDVpbS2y2y2y2y2y2ltLbLaW0tpbS2ltLaW0tpbS2ltLaW0tpXdO7/u5A9T00zzM7T6
280cO+H5ct8Y7W4Aly8Zv7lEEJKAX0bTLdr9fZj3IOHp0ZJdNPm4GbQFmO5FtEqbGdFVzhyyE4XY
pCcuNITcJJFIcysD6GczIczKzkmIckkUUIcBA35uXLRxeCckqAF0YQSANkAKeszxJtJp7HfABOCP
ESF5ttwSRmhRiAwidMHLmbUYBJwSvGEgsM4kJuAyVozYUWBQgwwCiMYLhoALpVOLbpdoFCRAohak
AI3a7lCNxLkCNZSh938mZCwBiZH0kM2MaNdAs4orYQqGsQIIF+04OkiteBMSRsb3EkaE5oTmupzz
vnjgWHjLrdKFkEQYZxRBKGw553gxjFjdS6CLJBb9BJCRE1wKPE14amus7IVULeQrsaIWMSxcskqI
U/LbSyFcSQ7CQQAJFSJcuQKIW8o4BseAxmN8QD+xE2VG3ITFo01LEMjIeTHEguXGnfMns5fQ2KAm
CoiIbFNRGva5ycFqNvLDlawI/vQssEBlgOkJAnfm5qEAsgi2i5sSrQCA0VBLFzk6SDfsRE6HA0wR
AN+wRwQnAHfGABAsaOWLtSeZJy9jehPJJJuk5Qzx+r3N6/mbf/t/HZq83qFi0MIymRWBsqEsrjt1
C1Ikaj9bFVscSQ4SM2KHfgcuU6Ee00AIwPbapyXaXJDyA+I0fH0KXvGBO0T3TFaMGHQcTioUyJvO
hQ0pZMIWypQPt33umDWhNe8Aej9ZCrT4kLsu/LnQ2kaSSVfgFnLfB38os6qYQC7Cq08ya89fHFK8
sBEkieKCSYCBJZEGVVoiJvIcgv09y15xEPFYvkA9WUqadZ8PsbEpH66sQcAg1DYw+ASa69++5CPc
f6u1aEIz04SBdgDVLDugdBJCkkLhpwA9PcNbmjYxmABx5Cq68yNhnsZMZ5ZyaNxEpFSADMUlDg4T
CCBEcjUaxHSA0+0iKxccV0AiBIksJBcQSLwGFGxJwopsAuWm8U0o0IyNjxmwawZyTUdHqccwhFzp
NXlFdiMBl97261uZ1rVjb75hWN24ndznNtmDp2XL4Yuyb6gIjChIubHLYddIDKIdfBybXzhBYmg6
khb09ZlmIA5u3mJoZfCNWERABzjkdQXHyrdARGjyDNjmOkbQToQMHJA0ZlNUFHaGVLIJQanQUGgm
MzOUJRi8TEcmTsaoW42LLblqQrxySFcYihQNixHeMZoTKA6FEyjQmPpA40OPAjl8ELAvsaQKq2Yy
QthTHaLWmCJsiw5nH57sFKemDhoyT0EgnBZqCNJeXl1cVO2CDTxcYOwY6jxxMH9Hdx1GxFyIIXHG
p0sgEEE1ykaxOxVHLIoXCxmTASosSSXH/Xnux2Oel7+cFS7Ugnz/9P/AhhdKowaat39yE4ff0DkT
YoR4t2OYkOzz1PMeEihSJ3nX5zkpESVZEjk5NaGFyo8/ET9xAi8Ylx9Q5JpEuOMVgXGDtEmk4nkB
QJsAPD7/U9O96oB8aPeT9aexC0eecA/cyo1YsuPPt6tuh73faSFiD20UzN2C3RIriqyuhEdEgZqg
oLS051iQ2bdOGGul3fgziI1v2IHKhIWhEqRhIBswRglbU1hXBVXhBaBHsAGm0AkooNKUI1I2g0zR
xFBRslCoTAGBKbjIXIZC9EJmFiQyylFgGignuNt9/yK+SUpUdRARb/CzUkSGIz3DMF+B5WpIyrHu
tJCwKzSTj3gA6yGPTPA/M85DbY6A5mZx3XB66uhTNRiV0KKE7humZU9kjpIGZWrNcAngPERCfJ02
wcSaqXdi5LoXfwo85d8uq80VBNjZkjD169QYyraAG+Q08YdjgtG4kMQitGBwPJtrPI4kTEca6SKD
JIyKEjfJxJFdxCBCA2BWmxAoWo2I5qWKlhCwMMTEsMkkORtR2xqYzVUVBIzJHHGTbyJEWZcYKVOB
zwERLPlyxR4Df34eHoyjQGWyvSJVatZJVgs1ZVkbHS34gDGieXLWYSLHs9fsJ2KC0p7f43VIbNlx
tD2kVIkTgYfyoXTtgcR9iBsr4mQxI3mZAsXKDoyKH4oUKLTIbBvkisukEzbvl0w9SPsRy+Hrj07C
OtHMjmAflKKFjbrOO4fWC5lzX7EFJdTEAlwaRCFXRCajPc0mkY01QCL5mJEyYYlqZIxiYxEPjmvM
w+GePCNiUgxBgkAUmlMICF/KiSe3wRQ7svZZjHYSMQUhtKoVtMRLDlyhXK+LXYkdBCxoaFas+hS5
EqTOhrO0uxoJH0GDZTOEEcGSTyETImWMCJiORmZCRrZ8UksmEo1IEDXEjke3BxFzSrGXsXUxXB+o
3Myt4MQSl+hN5YgSa0xIbG8WYa0ckLEIIUHkhXCKFE/bTPpMzcbc0DQyNFW4ya7OmWYlISREjwGb
XUhwIjGS2OE0EvaQr2oJXil7AG59efLZU7GNksjIOcmsq4bQwgiWMEh6zsiJp8h5MtMe6uzQ+2z1
fzdNk/i3B5+FmPpUhJ1SeUx8bGDlIN6CyW7csdHis5XLA4Og0xbHsaKOBo6A4k48xShF0RVOx62X
FI4Z0VXl2NVeC47kcVHmC5g9eRySKYJjCYQL2YMJhA0hU0LhRPe/SANZUQmvrb4gVKMuhWoypYMd
306XJm8I8ukLae0RZuUQydXczZuadyXDc0ABog9gEsIVHQo1JuCBBzsIQaliZWoApF6jKIiIn04H
fVobhPSmTJ8k48FxzzWx4o2ww9hHXWR9mqOLjCIKF2KRPaPFLDqDjrNudFTZ6IiS+lmywUJ2lkoM
scikjuNMFDiIacTPr4OGh1oOO34tGrS3zsAYglQDX1AsCE2oqCPVBJnR1xR4tCMTrXIw4I1Xr1HD
z1tKHQsTOy818jG4uxA6Z0QJMMaxQr4G6R7kLJ+I5vGNC5T7tgw3kHXBS1qA7jVWgJd8D2ylw80C
9XS2IOCSyAabcrza5GRD8qiQlBobSSXnV19+oEm6Xltdl0ntVjn1D976Hv98DNxjBjzodjwe40R8
zzIngGjsq4kYw+qHoLgxkcPHDitS6CQ2XRAaqNwN+AiIWJU4+GBoOeYqkTiNjNhwo10CYpgU+NBJ
okVBo68AmOcDQoQKFiJAxkKR9kAPt5fBCxSWiFA36fP1IPSAyJeiX31QuaDOxpjDlw6T1Ns6w4xg
VZo67j5Tfm0cBmt7cN7v0XOFUi8XL5iSVguxkLHrhjf2AUQkOAQQsK45EiQTtdiYkjEvyliRM0L1
xGwa5oTWQSmndoaoV0KgVEkRllWcYtJJGiF3FzUkY55pKRlgR3mEjKp3FODCo9bbWytQ3BBGF8jC
OJFSBoAVQBYjOMRGa5AiYmRoZExl1w+SroPQM3CCE1nVwJMsMIji6jxZoJ06bKIJWhQ55eXG6Ihg
fnBwOFIk0UV3P2OHwME5IBIkgrkE6nBXSEznqQOowsWUlJeTkoKQqXJoJpBHDImRsslRgyCtqWbN
BHFkEgPtkmGCBdckxRr6GMFxVGH2kjNGTAJGUE2UQSpmw8GkYZBoOcM1ZHIJgeW3oXKIlwtQcQcM
QwRO/U0X0AlSnNzimTWzKhRdtD9PiHC/oonohWCIrkNkcJwSueqkTgTOo4meJQiMWGOXKJmQg8jn
fAYSRgMOWMyx2udKFcuw2CcwGIkhipkVgu2D21TgqWUKJiBWLBWS2UMlYECJZpIyXAMgFPBxoy9S
PNuUoyB4edHmAMd8vOyX5nExO/GannG3zSRngY9AR4QaEUzNb0AlxpFmKLJou14yrknkX2dMwy6L
qU9Nr5MuQboc+tr2WRxBdwSO93qwQqmBeg2vX2FCJ0/hzkjAmbqnES9TCSJm4lzdXnfrySLjVuTT
kpg7LVsVXhp27cUMHYoXTghQyxo4lgS1DiZIdk20eZz24KpXQwlSuEE4QYNOCKCOLmSh0rBZqxVV
Dk2QJDzPNBw8egmEEwYHGigrxw4jZGSar1q0jKY4fKWKFS8zBcmXLDCNVFZWBkmSGzLc1F92Rpiu
xqk5mjGg3IoaAAp4/T8f7v77/lXByKowsmDvsuMdse88ePB4BSx16yRhSgti7SbzB3GFYPGTHlCn
g1qeXTsSHLlDXQcbQY8c7+hCwOs3jjGhvGmaSCRAzIGhgSJxx5IWIJfSj4sG9nZhmb5pLiRrR+CO
H5+XOjQjYjAfL14ADiSaNCPyr3cX63SR0I06Jkm1ADtgxKAhP2yeA7xhLBnevPHSy6mine9TaiSJ
ipftIgsCYthgjgZD2HecXWWQnFmMERlUJoT6TmPQwxyzMKjSzhieYfgV4o75mGxmcF/bWhq5o05v
uBEiNeLsGFzAiIULjAqbgeXzoJjKcVFWhzM4OeSxswIiC72WES8WWLDR5fABuAoynR8x61WsaMJP
ERCh+dELCoaOvvp8neRk2S1gyplOHETgjQccvkcdHDD4VZcljDOopk+ABofcRcGFJxBTp07DGNJn
0nLTRyUCBKWj6sZ2cynEcp1LkhvfqcRKSSEpc723fzb6Znn+uf0gKJSLxaISWWKY5F3tlW084E4Y
DRo88hqgCkJi6HVKkQheBG4wcxo83A+tBJOJWjaf5WuHlqF+CQ9xUUqVRRqCe3203YdsOwhE3G0p
uXehAufZ6NQD59MfSJH1/NC82pD1IcKPehvQHP4VmJGekxaoozxyREpR6Z5M7KiVUqxofus0Yazx
d30M1w2tizxDoKZx54vWKVAAYIA14lcif5+0HeiEkCoN3oVi5DIwMoFYGcdESJoVa1pd8VGJmYK8
u87+/EpY9ue6xsamd0JkLbHwMB8DQYhTG9boUh0w12N2A7zJDmBZiQBUadDJ5dNj3wiOeMMGMIIz
QoOQSNpioJgGRQSg+BUuWHFCZ9wBqxu9WrhGHS57x5kgdCPf4vToNXudsZkq8HKOKkjX4NOwA+aT
cLXfeI8q0UcNOLHUESE8ndg8XoKYgDZliww2TJ44fDAEnUE6vv1aOIJQ0Z+IEt+iyN3L0MqALBUL
p0GbUOskYHR3dd72MZFD1AHYWOqhAySXAAloNDzH8caERCxwSYKel2maWKFBdFjuSJDhp6WiDxTr
QaT+72PLHRIHBTA4Ychjv35PJLIIuSFwQq1AIuDC+sEmS8bAlnjSDPvjxe056a9tLHPmLjFSOWbs
Kh5mZtXbtNLo+ROIINEFiDkEmbJ6+HktWYZHAtmYh9iSxJSGsAamxbEIUovc4nOEz1epsCpMaWNn
BkvjcZqAeqsWTHILV/Y8EKcjtLZjkKllZWwaozsQd0KuIBceEzBU8i8j7QDyrS1CY4s4UyOJ8852
iJXAo2S1K/KBwVHqTQs1p246OAH1cdeqPQSZAdAe4hKknUwSccFDRU/CJs7lOpMHFiqoQnQ0MLwy
cjJFCReQ8fddI0U0xzqkHq0xO8RkSDyRXZk8TvSZLUS5eg4LCshjMifoMJmj3AWQBK/9bqnnblZW
u1nZGTCkQj5ARDVakNzbBGUYiZGwmjhBON7kGIwWIK5fWZ0EyMuk1GMiRaxBCo52RKDFajhA1YMl
sYoVvKZ5/O5xLFyphINDDCxqSOA+8ziVKmUIqhTyQtNUL0gfUhc29C0/UIz+CPcj6eYA0AbABuR9
wJVzmpBzyHbieXskk4IIjiP27u2ikuaBDIC+cFkN0FgPuGYRlAJfog4piYkBYWQC7sIQqu+qLPiV
XfAVpvez27F0BeIL9WzJe0F6QF9dIjEgX9+3ULE1BD8gsILm7T9CvV9Pd4yx+zm29JXcmRTt3LPj
eo/4/faFSD8QSAGIPIO57AWJQxIpAWCAos8wEiBQkSLJ2BJaRCRBkOwJ5WLFiMRiMRiMWLEYsWLF
ixYsWLFixYsWLERTBYJCZJDxHlBORGTiwDyxH5qBEAvQC1AswWsFkAtFwF6w9F0EmwGiCoKQ4IUU
hQk8kkIEoJHv9rMD57Alex+LdKj9Pc0acNj9aftno9sfG3PBLYkU/xU+1Li/487slOHyrzLGahFT
u6Pxco98c/yjZTX00HusEcWKafA9gh1xEL7QwJqJ4CXTM958AIuPDXCJjDzEMTrGMOpnuwLsAXBI
zW4unouAjAkKLEEQXJOOFB4IWkJEtiGMYMLu3/kBLFw/Ww123ZnyCT35TNt7845MzvXje++Yy7Gm
9/r4Yfx/kMRL5V0ROHlL3swTHMFj+GVNPv+erRyClxZ8/n4dyt1M7zuDi/DNsPfv2en+zu6/EPc6
x9rQA8EkVWOcCXOQEm3c7t3DWyU9u6R32BKrPy8N1/9rFzHBeg3aNWaV1yjyGJpZgSzAkwEtefnh
0+2q96fjXumSne2Kb16M3N/rmcvbdLzM/blbxe9ndJscLlV6VtBLlOsPgJftBXguz9vqCWcDa59j
tpI5U5QJMJzDQnsvdWD4oBog8yO/z+btvDN0Qo8z9egJVEl5+t19EoYwZeak8LJ4N2b2OLGl0Xxq
+nTgw8ZT9/P1jQJM0byDbh5OglibQJXjFvmoQiiuYJUASR96Xu+kXJiwJeXDfR6/TJidBLyxoSkZ
2bTcw0+dEEmwS5bNMuj5R15be7b50OeOfy2AlMjv9vdpo+Pfx9b8/y193Pwf7Ns7FXiVWSx4YUIR
DOoSJKcRCEnI2cENo09LdblzPmlPiyskzbrOiUebGa1qs9eXHmv4jUwqMO5meW0gyw3mvLRd424a
drvit5vTsl+lP6utOQX8ODY4Iis+HmMy0efNXDPsj1d2NdEslyG5jm0ttgyxlkAqzdspuckRZ92k
tzs3rsnLVJselJJm2oWWHU7LLIsX3o6HQ4glMwOyX7X510anlm8uTjsGysRk2y8DiEpZssWp/OGH
VvvZUadrVnPWe65YOdI9VcBJqr8zPEf9Oy3xIp101BfAXn0ou4zBeKk2Nmx42v1PzuAd30+dViRf
SS0RUm7DLiGU8W6mgqMFWw4teXJxfnQS7Htep7hdOVDza4aLSJmFa/4/lqv9o7vHRw603fDU37ST
eUea9p45MiSEQd9eD3VWwFOkB2hDvBJ6YCEMSpuyemLuhzvonvRfbcVUeU/Rkm2Pb13tpnzo8O1/
bFgy23Jru5t0EtN6VTo/L9Xjtgl1+df4pyeffl31s2izJFj8afY5488lkEscudAAlXzjaGytidz3
43Z02w1xnt529gloR73d2fn0gqVMnYSdI37dKtNBKZ4t4LeCtM1Ak2wESRXR7vV27SLZfLwu1+mK
YdWBVuQncfM+FmkpZs7XKKrLTPXNM9vThvxxdfbdZcTiGgruciUiV4z2uMAW1zssiIiIxytAXyei
3BYGFSkrOa3taPtM3Adf6e3q3qv4JkklP7wpJO9bwwEslfFo8ct+fSq4ZZaMbFW1awEqD2F7xlCX
qFPtsS9Out06vZy3T+t0N7P1Vo1tIbus1UyRx+j0mPeHflWcEsVif9N4t1mg9cw9rO1+NcOWSMr/
OTrd5/VqR7sv+l4etUu+bk7N12oF61zp28UFm/75JT2Dt50bLb3Rx61sm+r1qzNDWjfZ1feaHHF1
dWHt4SqvTLq8I57+R2DwI/ijl7vt1uaX3vXr1PemfXduavH+of29BH7uB8A23zxzGxmYomQdISN1
opIpqgok9wVJJ5fPBzHIXuF94vwKmiVDMDeQpkMAwxCEfv5icolJkhkIQQJZGBuaKAAP0IstpSDU
stIGifMayPHckDloejXr1OIqjFERYxVioKCMWIqrGe7i8jhoEPezpZpEKRBBAQDQhgfFPV9lO07T
k820dRynqKCOgwWF0rRq+hORkVWmkK+rilSopSTUwa/Hs/SgmCxaRsux5+8MLDxk8vODYwicaFqX
eVJDgpYcRLnJgaYP1o9DNEfgoW+4omDeyRgqjssJuGteXPuwOKj9swPG3BxM4s2+4jB5EmMHlxgo
PKE8CoJmBY2d8FCJQiQDZYl+np1Y6GT2mPjGyfHUlkQ4NjCEqDRMqUEJUTGMnGJQcfidJHI6JEje
l+QmBKowMYjpyIam43t7Uj8No1F81WN7YNoi0wAciPeui4mc2S+aIiCvzIlj4vRDzrXkqNRylRjj
QcF3FR5CQvg8ioUF0pMhXabJaYZmnFYdCHIAuUBXu0KYcIiVKiDxLt1+EM0/wJ/AEL+fGvr2vpYW
uwpSxCIFQC7+vhZVKtoHUe9V4FRBrGncq1XuzTmgF7CqL6qJniFZUsxwA0kJPoBdJXRokAtOxKmx
DYGcFBsCPIBkFV1I8ZgcvopnJgw7W9i2EMAeHGC7Tysnljl/b71UVaScpkpRy+WU4u7yX5iMYyAO
/oMMyZ0DlMCGw/eSIeJ8UKpJkhapwXIOHlhxpSRQnccNDRw8gV9owsOyw1kRmYkRsipiQJEypQzK
GTseKRYZFnKkNGD2fQ0iXwNFISNHQG6ImBRYDBpM5FwQN7JpokNmPERBrCZImNLWmMY/BrUADREY
QowcYOCZOezMtmyeqGBdDjY4cPOf5A/sETBFXY/rnyJEBEQVSEjIikRjGhwyoqNlA3glKSSiVoeD
ud3EyAthgfAEm0U9+aDiQwkQJFT5akxTbxp0PNzVTcz2ezA4eZT7RENolbqUgK92eDs7AqDbzY3O
uc2EhIW4qNmXBb0CMSBhYuNqrkzeM/4sRIn1e1C3AHyAM/R7EaifaEbQPr+vb1C6Fc/0vUboLFqO
pHwsAMdzn3Tu4ZJe9JAZD2JJeWC5i8LRVP8QqDY+Td0V2a/X2V8GCRzNU2HPSeHhE7PHsPTP1WMd
t0L7TjM59CpmdCxxFOCkBEQuXJqQFJEyoBIiDhuDEBpEgRKEC8xhQgWFKWIHywHmJ2QSEipU3uhg
3LJvc2YLDzkcQijCQwkQwixqs3kjRYZwXGlThjSL7pc9qjzJX3+n66fd8On4aJCmjLTJ37tIDjoO
bculL2LuYO4JnE7Dr6hi+8SQ/YMdhc1K6lDFCnwHaRNj5IUxyJ2XKGY5XmbDYoDXQpEokcxLimT1
1Dv3wZJEcofxdC5bBNhthfgiOMbI+v8Zi0Dnki4PDWq9qog1I7aQPnAKkdlHtRyItNALJBQaOXe6
GHqYPBUQYIhFrQgSd5fR8cyO2jtVVFZIgDrgN9TvUhSTRgoJhgI2F8O+7pR5DF4GyxuSkzc1yyB1
QKgg1k0JIbcH5/VfCm0N4b2NGpThJcRyFiRpbYVOUMm3L0m34NT0SnRzleDD08CyPxjz5B81PJWt
ob3Vt52LFxS5WY+Tx/zDpEjkuTJnzII/BksUJcmj3uLkMIJAvJKFdJUtVHayKTJxHuqMFImip/Bk
yhW5CHAiIQHFomRp+iJoxI2mCo46gAYDZEqNINGbFeKSEnoazMUJjeMUwGNxjoXAS2IfqQq2iZWN
xmS3mXOhL924BxJIzYQn+f6OptX59t8cEWGQpMxfK8uQ8UbCsP9AXyPBxBLrCGcwlMImYFwGZkKZ
BmZCmQZmQpkGZkKZBmZCmQZmBcBmYFwGZkKZBmYFwGZgXAZmBcBmYFwGZgXAZmBcBmYFwGZgXAZm
BcBmYFwGZgXAZmBcBmYFwGZgXAZmBcjMyDkAZQgn+/7uYyNDTuC3aSgbpkjsulqbyR2HcdsjjmL8
9kRdGi8zFT2kjw84JGvvAh+Cn2KikPWkpds73e4dsemf0Qn25o3O8drrOuB+U9SKoooipGg01Sc9
dpbM26vdATFPvl3FVz5feVt97D1SPxINRbb/pW+N9alL74u8SPqXUO1CqFAA7PdCmv5mIaYq1Hx9
SbvLzV7glleQGULl3BoNrGiE9ouSALRLBTNRAtvcHCgrqfGAKZs38DPmd+ya4kqJAkIkNuHTOnbf
7RIThiUN4fxwMw41VUUUUUVVFVVVVVVVVVVFup+b6RvIsZ4oXMe4iR9Y5qAQ3w9T0c3Qib8Cjwzw
PLHH3nqzmPdavn0b9fga29MeyjaMhitJ0vjNcUzeU63MZlbHzmBTgf/Sxx3PyHNx51PzeCGjwnmp
LB5D33OR5NBMU8EcGChMZOfd/bx5MG+p3SDtK3set3LT1Q6+p7r+bYVZKNpjZOhMk1juHvL4h53r
LG/UKeoigmQTZQ4jkyoxU+j4dakdDWr7L9lCxoQIFy51lTUuYDLhNQbIYRNFsQGFChD4MGPFPZXA
yZMsSM0LQtmVmaGJEdYlyh9qF7UK2ZeIB1noYYIYg3M4gTBY3lV4QF4t0S5ALuAsDARCBELCB9L3
SXvtIq+fLhOb5/2mC7HMoEkCkAkmCT5OaSC1ZukJcrlxhbwDnUTu4Lo+UOQQiGvE1kh0w1F+eyYS
h4L58to3S4FBvA5UfL5eQ5asxhMp5qb7fMJjrFFH5plMPL5tPlHDMjBBhrog4zC64QJxpCnvkd5S
iRAKOZQ04HeXOXAgdhvHOcgOd5Q84H48g8iZIqW6rADL0Rt2ajeB3RxLcuWBUrvO4A5kKIKCF3Aj
RCTLmpY2IDmZgiosMZmL+gRvfLau5UcIpy+rEG8qwbsKyVEGASAW8Cr3hwqR7fdIP9v98XZs8eyD
glk/l6AcicgocaNQtYImhVE5N1GQ1+bCbtZZOXkhgAkQ0dM0W8pTS7Yoo0PjppDtaMaPSqGUpKQW
MYAvQWTxN0BcJgAWo5+0EmBJ0eCTmlgDAYMkYwUIhunIBy9OHF/NKxAgoYEQJm9fR1MICHCnoEhv
aErIUBAEQ6gk8X0zJ2TwT8nuzNtu34ze1ZBSIDCRIlhYLLnDhw9v4c1s7ex/ohGEoQeHMJeIkMCQ
nlJ5fQaHk57z3GniOaGqgawhlrFUlM3nLE5UK1GTEzM8cXJFMDQ8PfkU0sfAq3I4bYvE4QwPaXGt
mRJFW7GkzSXFhIuNKEy0ZVIDRg8eSsMIWNDxSUix+cETkYKccTMCm6EzTQmWMimSB96CQYUGOKE/
PipOhi1j7OoOY41c3mA0ImMTiZtDmMIEdw9vL9O32+XhxdWSJmjodIdjRJOTZ0HROCpowQJCjCEi
icj1JF+i6OqDWPNxySKDsHkcbW5T09H+ita2Y/ghGbO8xqpJLebzOsq3EjhYiMTNDvSSX1+Iv0GE
PkoEkOZQJqB8fUR66YlREP7NA/VgGfnA2agqClo36gnAok9mV3vpQ9CPv8rnR0VMYCVDs/PSuB88
vbdUEka6yQ0MA++lCzgZ/Ls7EKqCUgBcAy4+/N9+P1Ka+0210zX/u97aMf6Hs+hbkCSPL2AQ+r6v
qYKIqKqMFZ7oT8UJ7A8Zx5ChHeRoygEYP/yog/IAtEDD7ZIImBOVs9oO1J2ke17RHGRdEoiCl/bZ
AJ/5Bd++WghXrU64S8Kkzf3d+XlDKShJJ8pITFCfRZsAFxHIjahKA3P18bfOjfBftz342NHq77JX
/66fvkviKGjPAPwI+GrTPTIUyAfChtuR9CpXFMAtaRRR9zTAPLiR/H05UeS/ZoES8iygDw5r6MwC
jrD3/PtR3xfJ6lFDlV5UbZW8FwAhZH3eioJYw9PVavs5/jb32pQ0cda+xc8GUYoXvBL9iD5Td8g8
VRZhbH7QksJJPhPXTXEkJCalPuRCQhUKKN0BbUQLni/eH99UqIW6H0F+IAHdkNED0QGRD8orIAjJ
CEoEYE9/IKoOwILjlZjQ2UF3MDhPTJwI7esBWmjDw7F4kwYQAzwM5xgLhZdFDbEZGmeoF+SfQFwg
tQL2q49kiGIFYIBhYxVhIpCRkIwhILJDifw9QH5g3E3oCFZJ2k4YIh2h6426Q9WczAHkVFC/WJa+
2oOI3fPXJeWOlH98g9qrgRv8W8jNG33I4fIWAFqgbFSN1HUj9LqOtHxx3+7x3Of5+TLV1lntR1AF
wAzJ7/tARAZBTy7e/mR1I/pvI+uhH7AHUjuAQAXd/7/XzXhXeIFd2QITD0YkedGv+1aNCPJaqIOO
3eufPd1G/+s7J8AB/XQk9n0+73n42h6hD4RRTKFEl0fYaED9d2n3eKPH9rwB9UeyFceXy3wFeIOP
ffUAdiNYpzI+xXw/v60bQ0sKtsK+naVEGQqI4/x2w4ELJW3S0Z0JiASfxgEoA7s7s7+vpm73VpuB
IoBci7FilhTuAUZRtzSFO4ptUCQwhCURJQMcKBa1GO5IRWQVpu1+kA+G8XW3Wl+6EhHIJ11FIUXA
DDyT+IYQFlFxTUjd8zfj85+hU86pc6cqOGd5C/f/ALLOAsYwd5GsyFaOk4xTj5710rDSC1mC4UcE
NAogx8PL5pAFASPqbM6b/B2788PSSZJ33vXXQQVUDiSECkkSVdBaI2lM6O99poWgaaSIImABUBAL
uCa1MIL8wW6gtWFVwgH51hnwKvCejnO5GNgFp8VV4sCfqb6MT2L18A6q4tpiJyDYEgwSSIpz2Sfc
ctaYHDAltNtTUL+4gyWWASISAWAghYFY8Y6vN41cfRfRtRcEIKX/N1zPzn7v6Pp04KctiKt/yuuN
EElV9eYVXe15AxqsvtIC4ArSEroKVi9iOaShgenk9lYFxdkFl0ITS7CocyMPwqBaBRRmebD2Iw/t
hEqHLmJgSOoYTZh9LtA9eLAecoS3IShIkSCEGESE8MpJRCEHtxqqJf6gWaItipPbjw78obMIcGiR
v8l1WtF4b+zSXPlyhmMezjw9NjefVEc/rws2R7B2IiS9VKoM7AN3Q9VLWKm//HbT2BVqALzdgmQq
IMIYQJABwKjXAGSFEAmhCL+QDOCzAwUAKb439UnrS0lECaAJVkJp8vMsbnJZInWSlwflRAJe4vT7
1MCWsxRN+wRNYwCw2iKQOb56UCrr78PnubPWUz+QUZlnKG2AlceORzi11+wPWrewTAvcaOyhOA7U
Zo01YaEdALR8/Du8bODt92ntv92LvmK24vZh+PWPLBxAvyBfkqLmpHeQ2kfzQqujDj6EZoIHkB3U
cSNv88KOyBdIDoOlDdRgJIbfJUGEgiofL34GlHqvKhldALoAWAWnLfC7Ag5PQSBpzI21aq8SoMPO
kqPdn96yFkisoViYhQ63HprfpJV8al6/CW+IkS0Ltxoff5Ee/sR18ex+Zin9cKNt0A1SzJongt9g
N5HahjulI4SAyeSaROhJgkwWBkV9vqRoPjj/jCj4baOdG/ZEKhy/HV6eLd3K/N7r2PZ2/theZGAF
fxccbgVe2QjbQKKOCs0mplEoVDUkpACfV0Cdw7PhCTjvJIQ294pxn5/z91a1YDC3/IqqtiXrBYRm
xuAtEpAsKBtgs6JoiUov3VMhkryOvZUJbPw5UcFHKYJL0GGNSPAqG2XeaFfcYBRO0Dcu6gGjBcce
tTYmALvBmvd14znCE6fPV5ughCkkQgOogSVCSjAL6m+gwQoF9H10PN1I/yAWitEAB1o2SBbudRes
hPAR0I+hHLnNbpCInMjuYTHvyCaah+RlQmrwwRV2DQyvz+RzuMd+1sJ/F3xAgM+TcUL6gshU+hC/
GxnkM4JXAWAXw+PhbKz33qPh4/zPE5gvxJDIAv91OWfSD9ckIEsndO/4vGayq+OysAPHpRXaBcN5
+ghEPfP4vb1fjAOj6DRJs/C3uXKW0tsCmUuWoitlxkWWf6/uzC0NMcDWqOqMKISCApicv3i8oxAB
1Zr4LSqvqBbyq4jbALRW1IiYB3ITWlKgW8LUjOBA6L2shOHeGEyIkGPc5u+kkBSSISKq4SFEDqG1
QPCWtUXx/UA6/5v7lyVwI+GugDEHo+Wk4AF/mDIAkXoAWSJi1gK3DzEHxAkcIjy82ed/7nfy+v/O
Z5L0PIRLu3kZ0kkoZgI5OPkBDjqAhAk3+GqWaZ6YAl1QYDBtfQFXLyhwBOfNBKYFQcVaSqLad/7N
snNy4Zb4KWkyIhkXfxdAMyOPy76v0stuFILUC4gFjvz5TCu9k/Te+teTQWIBhwkQREQREgHOoEQo
EkkDEBn3XhNJFPIBVJBbTXGsLgC5s19GKqwMkI0CNFwiFWTfRbwxAsTOWJwVtAoGcBeEAwLahQqX
RZylL8YrwtoC21gLYhpiIUYsRwihG7OypiEkTqgSTY0EiCBJxCYXb1r7e43AX5TUJQp0JJqIiFmM
HTNBcMKi4QM98iEXAnhwAFIqtIDm3n4o2CWRcBbkiQgXtpH4tTEZiUoiIII17GBGYekC+jyAG9t7
WG3a5KYJSQOeXcoKfO4hUsTQkR1mDHQB4hZuZFLSWC0AXb9/tSaC7Zv2GutjDA9Ku6jieKq+KIYQ
9wQhY2orKmA0IXwiNCPxB/SSPCAawk9MJ9vZ1Sg9uDRiPtJ5aZoJOABQOqEaQtPUzEA5MuSR7+v+
l2H5w0nfAsJZFVoqNFWXTdYwYxhX3E5ysESJDZ5oiCc7NsDrRuXWptVLoqSVFDxonCP17UaqPKKb
+0jygHFy/ZHIj2Mp3rc75mIirZ3IQDMXo+z/HqCd/1Qn0gHm/z/r8a+HHydMr/4/n6+H9t2z1T0D
qePtR06SCIgjzAtAdMAbKNgSMaPYjuelKN4qPbWAV2+UA36ikF7pArEpBWAYDNtnej9N1jHBZw1b
9nupVnidyxkCFg48PKpp0ekA86OBAE5UbwBvTx8O9OQw0RUkQMSCS2H8D14ElH63bo5bWa2IBmhU
2RSYEAqpnCROEoiIGAXWjoyI79/2gFSO6AZHZTwJdMX5hcSyacb3nBUXKaAVRQAlqQmbLSFvAKeO
oAoT5dSOlHi/PEj2m1+urYVQ4G0DEkRAcZ6eDNuWOqB3UelD89kg9hso7COkNxHErqAajm0NevD9
pyEr6FTDKkBeBMvPz0LNitdQJZJyPXCPMXUd0H0VyR4X/mP+hdyRThQkKqwBWUA=

Attachment: signature.asc
Description: This is a digitally signed message part

-- 
bzr-gtk mailing list
[email protected]
Modify settings or unsubscribe at: 
https://lists.canonical.com/mailman/listinfo/bzr-gtk

Reply via email to