From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Ken Olum Newsgroups: gmane.emacs.bugs Subject: bug#4258: [patch] HTML mail in rmail Date: Fri, 27 Dec 2013 15:10:59 -0500 Message-ID: References: <200908251936.10632.marcooliva@sapo.pt> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: ger.gmane.org 1389121995 22072 80.91.229.3 (7 Jan 2014 19:13:15 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Tue, 7 Jan 2014 19:13:15 +0000 (UTC) To: 4258@debbugs.gnu.org Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Tue Jan 07 20:13:22 2014 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1W0c5Y-0000yP-0s for geb-bug-gnu-emacs@m.gmane.org; Tue, 07 Jan 2014 20:13:20 +0100 Original-Received: from localhost ([::1]:42363 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1W0c5X-0005yr-FR for geb-bug-gnu-emacs@m.gmane.org; Tue, 07 Jan 2014 14:13:19 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:49017) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1W0c5N-0005u5-4i for bug-gnu-emacs@gnu.org; Tue, 07 Jan 2014 14:13:14 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1W0c5H-0003V7-36 for bug-gnu-emacs@gnu.org; Tue, 07 Jan 2014 14:13:09 -0500 Original-Received: from debbugs.gnu.org ([140.186.70.43]:55663) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1W0c5G-0003Uu-Vd for bug-gnu-emacs@gnu.org; Tue, 07 Jan 2014 14:13:03 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.80) (envelope-from ) id 1W0c5G-00083m-Ej for bug-gnu-emacs@gnu.org; Tue, 07 Jan 2014 14:13:02 -0500 X-Loop: help-debbugs@gnu.org Resent-From: Ken Olum Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Tue, 07 Jan 2014 19:13:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 4258 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: Original-Received: via spool by 4258-submit@debbugs.gnu.org id=B4258.138912192230872 (code B ref 4258); Tue, 07 Jan 2014 19:13:02 +0000 Original-Received: (at 4258) by debbugs.gnu.org; 7 Jan 2014 19:12:02 +0000 Original-Received: from localhost ([127.0.0.1]:41449 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1W0c4D-00081S-VB for submit@debbugs.gnu.org; Tue, 07 Jan 2014 14:12:01 -0500 Original-Received: from fencepost.gnu.org ([208.118.235.10]:38587) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1W0c47-00081C-UC for 4258@debbugs.gnu.org; Tue, 07 Jan 2014 14:11:55 -0500 Original-Received: from rgm by fencepost.gnu.org with local (Exim 4.71) (envelope-from ) id 1W0c47-0006lE-LS for 4258@debbugs.gnu.org; Tue, 07 Jan 2014 14:11:51 -0500 In-Reply-To: <87hacl9ypc.fsf@gnu.org> (message from Kenichi Handa on Sun, 13 Oct 2013 20:50:39 +0900) Original-Lines: 315 X-From-Line: emacs-devel-bounces+rgm=gnu.org@gnu.org Fri Dec 27 15:11:37 2013 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:39143) by fencepost.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1Vwdkv-0007us-2N for rgm@gnu.org; Fri, 27 Dec 2013 15:11:37 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Vwdkp-0006oA-EO for rgm@gnu.org; Fri, 27 Dec 2013 15:11:36 -0500 Original-Received: from lists.gnu.org ([2001:4830:134:3::11]:46174) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Vwdkp-0006o6-BR for rgm@gnu.org; Fri, 27 Dec 2013 15:11:31 -0500 Original-Received: from localhost ([::1]:50181 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Vwdkp-0005wV-9S for rgm@gnu.org; Fri, 27 Dec 2013 15:11:31 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:43059) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VwdkU-0005vz-Tu for emacs-devel@gnu.org; Fri, 27 Dec 2013 15:11:15 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1VwdkO-0006hh-Pl for emacs-devel@gnu.org; Fri, 27 Dec 2013 15:11:10 -0500 Original-Received: from cosmos.phy.tufts.edu ([130.64.83.16]:38828) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VwdkO-0006hD-MA for emacs-devel@gnu.org; Fri, 27 Dec 2013 15:11:04 -0500 Original-Received: from kdo by cosmos.phy.tufts.edu ([local]:local) with local id 1VwdkJ-0007gq-9T - Using Exim-4.80.1 (MandrivaLinux) MTA (return-path ); Fri, 27 Dec 2013 15:10:59 -0500 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). User-Agent: Gnus (www.gnus.org), GNU Emacs (www.gnu.org/software/emacs/) X-BeenThere: debbugs-submit@debbugs.gnu.org X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 140.186.70.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.bugs:83134 Archived-At: --=-=-= Here is some code for handling HTML parts. To show the part it funcalls rmail-mime-render-html-function, which defaults to using shr if libxml is available, or lynx if you have that. Other methods could be written. In the long run it would probably be better to integrate rmail mime handling with gnus mime handling, but that seems like a big job. Even if you use shr, if it does not render images called for by the html part, because asynchronous retrieving of images gets confused by rmail's buffer-swapping. (Personally, I'm using lynx, because I don't want my email to look like a web page. I just want to figure out what the sender had to say.) If the message is only HTML, it just renders it. But if there is an alternative text/plain part, it prefers that and you have to use the "Show" button to get the HTML rendered. It does not render HTML on searching, so searching looks through the actual HTML. Otherwise it would be very slow if you have a large mail file. Here is a bazaar bundle. Ken --=-=-= Content-Disposition: attachment; filename=html-patch.text # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: kdo@cosmos.phy.tufts.edu-20131227194350-\ # g3o5h6bnyg8yntf2 # target_branch: bzr://bzr.savannah.gnu.org/emacs/trunk/ # testament_sha1: dc810978fb73db2f3fdc77450d771642e5e60cfb # timestamp: 2013-12-27 14:44:04 -0500 # source_branch: rmail # base_revision_id: eggert@cs.ucla.edu-20131227191410-z9ibc5kpdv0wj3ub # # Begin patch === modified file 'lisp/ChangeLog' --- lisp/ChangeLog 2013-12-27 15:43:54 +0000 +++ lisp/ChangeLog 2013-12-27 19:28:08 +0000 @@ -1,3 +1,19 @@ +2013-12-27 Ken Olum + + * mail/rmail.el (rmail-mime-process): Handle text/html + separately from other text/ types. Suppress tagline for + multipart body. + (rmail-mime-parse): Don't change visibility of tagline here. + (rmail-mime-set-bulk-data, rmail-mime-insert-bulk): + Handle text/html specially. + (rmail-mime-render-html-function): New variable. + (rmail-mime-insert-html, rmail-mime-render-html-shr) + (rmail-mime-render-html-lynx): New functions. + (rmail-mime-fix-inserted-faces): New function. + (rmail-mime-searching): New variable. + (rmail-search-mime-message): Bind rmail-mime-searching to + suppress rendering while searching. + 2013-12-27 Lars Ingebrigtsen * net/shr.el (shr-insert): Don't infloop if the width is zero. === modified file 'lisp/mail/rmail.el' --- lisp/mail/rmail.el 2013-10-30 16:29:36 +0000 +++ lisp/mail/rmail.el 2013-12-27 19:28:08 +0000 @@ -4668,7 +4668,7 @@ ;;;*** -;;;### (autoloads nil "rmailmm" "rmailmm.el" "8c14f4cf6e7dacb0c94fd300d814caf7") +;;;### (autoloads nil "rmailmm" "rmailmm.el" "9ac7864956507a43015757329f12895a") ;;; Generated autoloads from rmailmm.el (autoload 'rmail-mime "rmailmm" "\ === modified file 'lisp/mail/rmailmm.el' --- lisp/mail/rmailmm.el 2013-09-18 03:47:11 +0000 +++ lisp/mail/rmailmm.el 2013-12-27 19:28:08 +0000 @@ -131,6 +131,17 @@ :version "23.2" :group 'rmail-mime) +(defcustom rmail-mime-render-html-function + (cond ((fboundp 'libxml-parse-html-region) 'rmail-mime-render-html-shr) + ((executable-find "lynx") 'rmail-mime-render-html-lynx) + (t nil)) + "Function to convert HTML to text. Called with buffer containing HTML +extracted from message in a temporary buffer. Converts to text in current +buffer. If NIL, display HTML source." + :group 'rmail + :version "24.4" + :type '(choice function (const nil))) + ;;; End of user options. ;;; Global variables that always have let-binding when referred. @@ -150,6 +161,10 @@ The value is usually nil, and bound to non-nil while inserting MIME entities.") +(defvar rmail-mime-searching nil + "Bound to T inside `rmail-search-mime-message' to suppress expensive +operations such as HTML decoding") + ;;; MIME-entity object (defun rmail-mime-entity (type disposition transfer-encoding @@ -636,6 +651,57 @@ (insert-image (create-image data (cdr bulk-data) t)) (insert "\n"))) +(defun rmail-mime-insert-html (entity) + "Decode, render, and insert html from MIME-entity ENTITY." + (let ((body (rmail-mime-entity-body entity)) + (transfer-encoding (rmail-mime-entity-transfer-encoding entity)) + (buffer (current-buffer))) + (with-temp-buffer + (set-buffer-multibyte nil) + (setq buffer-undo-list t) + (insert-buffer-substring rmail-mime-mbox-buffer + (aref body 0) (aref body 1)) + (cond ((string= transfer-encoding "base64") + (ignore-errors (base64-decode-region (point-min) (point-max)))) + ((string= transfer-encoding "quoted-printable") + (quoted-printable-decode-region (point-min) (point-max)))) + ;; Convert html in temporary buffer to text and insert in original buffer + (let ((source-buffer (current-buffer))) + (with-current-buffer buffer + (let ((start (point))) + (if rmail-mime-render-html-function + (funcall rmail-mime-render-html-function source-buffer) + (insert-buffer-substring source-buffer)) + (rmail-mime-fix-inserted-faces start))))))) + +(defun rmail-mime-render-html-shr (source-buffer) + (let ((dom (with-current-buffer source-buffer + (libxml-parse-html-region (point-min) (point-max)))) + ;; Image retrieval happens asynchronously, but meanwhile + ;; `rmail-swap-buffers' may have been run, leaving + ;; `shr-image-fetched' trying to insert the image in the wrong buffer. + (shr-inhibit-images t)) + (shr-insert-document dom))) + +(defun rmail-mime-render-html-lynx (source-buffer) + (let ((destination-buffer (current-buffer))) + (with-current-buffer source-buffer + (call-process-region (point-min) (point-max) + "lynx" nil destination-buffer nil + "-stdin" "-dump" "-force_html" + "-dont_wrap_pre" "-width=70")))) + +;; Put font-lock-face properties matching face properties on text +;; inserted, e.g., by shr, in text from START to point. +(defun rmail-mime-fix-inserted-faces (start) + (while (< start (point)) + (let ((face (get-text-property start 'face)) + (next (next-single-property-change + start 'face (current-buffer) (point)))) + (if face ; anything to do? + (put-text-property start next 'font-lock-face face)) + (setq start next)))) + (defun rmail-mime-toggle-button (button) "Hide or show the body of the MIME-entity associated with BUTTON." (save-excursion @@ -680,6 +746,8 @@ (setq size (/ (* size 7) 3))))))) (cond + ((string-match "text/html" content-type) + (setq type 'html)) ((string-match "text/" content-type) (setq type 'text)) ((string-match "image/\\(.*\\)" content-type) @@ -789,6 +857,12 @@ (if (rmail-mime-display-body new) (cond ((eq (cdr bulk-data) 'text) (rmail-mime-insert-decoded-text entity)) + ((eq (cdr bulk-data) 'html) + ;; Render HTML if display single message, but if searching + ;; don't render but just search HTML itself. + (if rmail-mime-searching + (rmail-mime-insert-decoded-text entity) + (rmail-mime-insert-html entity))) ((cdr bulk-data) (rmail-mime-insert-image entity)) (t @@ -1119,6 +1193,8 @@ (cond ((string-match "multipart/.*" (car content-type)) (save-restriction (narrow-to-region (1- end) (point-max)) + (if (zerop (length parse-tag)) ; top level of message + (aset new 1 (aset tagline 2 nil))) ; don't show tagline (setq children (rmail-mime-process-multipart content-type content-disposition @@ -1139,6 +1215,12 @@ (aset (rmail-mime-entity-tagline msg) 2 nil) (setq children (list msg) handler 'rmail-mime-insert-multipart)))) + ((and is-inline (string-match "text/html" (car content-type))) + ;; Display tagline, so part can be detached + (aset new 1 (aset tagline 2 t)) + (aset new 2 (aset body 2 t)) ; display body also. + (setq handler 'rmail-mime-insert-bulk)) + ;; Inline non-HTML text ((and is-inline (string-match "text/" (car content-type))) ;; Don't need a tagline. (aset new 1 (aset tagline 2 nil)) @@ -1191,10 +1273,6 @@ (new (aref (rmail-mime-entity-display entity) 1))) ;; Show header. (aset new 0 (aset (rmail-mime-entity-header entity) 2 t)) - ;; Show tagline if and only if body is not shown. - (if (aref new 2) - (aset new 1 (aset (rmail-mime-entity-tagline entity) 2 nil)) - (aset new 1 (aset (rmail-mime-entity-tagline entity) 2 t))) entity))) (error (format "%s" err))))) @@ -1395,7 +1473,8 @@ "Function to set in `rmail-search-mime-message-function' (which see)." (save-restriction (narrow-to-region (rmail-msgbeg msg) (rmail-msgend msg)) - (let* ((rmail-mime-mbox-buffer (current-buffer)) + (let* ((rmail-mime-searching t) ; mark inside search + (rmail-mime-mbox-buffer (current-buffer)) (rmail-mime-view-buffer rmail-view-buffer) (header-end (save-excursion (re-search-forward "^$" nil 'move) (point))) # Begin bundle IyBCYXphYXIgcmV2aXNpb24gYnVuZGxlIHY0CiMKQlpoOTFBWSZTWQ0LF3sAC83fgHwwWPf///9v 3CD////wYBO82u7u+87mm3hb3pXt6+noouwGvoroc+q++D7vi7ZzgxIPbC+YAM47lOj5K9db7YSR EaJgjQCZqNNAMp5SNNNN6kAANDQEkgTE000ITEk8JPVHkQAyeoGgAGgBKASDSap6Gpqej1Gj1RtJ p6nqeiMgwgyMBAEmlImkpsnqm2oGifqjah6E8oMPUaEaBkNGg0EUlNMm0mqeyk/VPakfqnpNHqae o9QaAAAAAaBEoQCaAITRiAQ2qYyI9NRoAB6QaOGa2MDRklQs+/ghViEFRYIMgihMmsKh7up7Mvy0 7ePx5L2hNXDuP2y2MOL9G+FOAGYGXlfObne08f1pQ+hutfY6fVIbT/NI02yewPKRcPCrx1Gc+R8T /SM2Mc4y0k3pM3F2nlPhjueTptTxZxk9/V57vNyYfPm0j72zusEltiTr7wds/e/nmgkwRYZhE46Y QO3cukLBcs98SRFWSKnZDM8GS6WEvng0eDKEnd2NISacoxQYgpGKSlirJ044fG8oYjmGbOw63Nvf SROwrsWp/6NkCIU8Bw6UOxuuZ8T/WnptvW4bOhWuHo1V0UB/pw4rT1trrf1/PE4J83ykFkSEHXb+ SB+ZxuC/nCMiUFS3diyjLHBmO9zxEKd9a4QO60uC9yTGEqtDkLj/TOBk7tC6KiyY1k6VFosSKTOl 2Il2NGao3JsXUmfSL0f6GZ8mGzbo7ptbS76TF7CvsnDBKesaaR6YRJZmD8t467GUy5IoNp6zh7WH RszHDTq3tH52xf9Z9Oetc99viVvAi7FKcUhUUmef7yFlHN8kbnEiBCRdhxZWJu5Mmw2e65E44HJj 047fKWNvNblQgTl3YETJwdxylXrnnVHboVLb1eSSKQa+eekCMLctiJUtJztScyWfLPzy+TdtnDbz LcoMd3j8Zafbd1WY12OYipE0zO62aqLatHQ1BOZulWYx2W64GvecKOuaYsG5eR5NDBcjr1gLHUp0 ichinOvY+amuV6vj4kOYBVBTLPXhbrqyKgCIKCCCeMNx8xs7pnIck8FiJOl3vq6bFAufpwfS1J0k zKhnTLS68Fhi2mukmSYzxiqpqGajCXPLIUZgq4HfJzm3r4O8zoOeq52YRGTVnePS6Gfm3a9u7VQ0 kNHyLGWQ6Pm1CngqIjvsyJDVPRg4+ppQy54HaI0vR8MUqj05YTUk+W5z3BoOHyPeLGcqATkUyZLI mScIAn0BPEPFfKjmtgd31dOvgbaUu1kKMkWwyj9a7mkoniaq/QPlJTOqcLLkHmFVNKIy1E8aEndZ pHg81/TeZPoR7CoYBZHkRQqGVR96t1xuKUvKbJWSCQi9Bo235FS7LG8whfwp7L69bZ0N3FNZOzGh k77Xyrbfw48Yw5rzVhAMmAenqSEgQNncVbCryEWDw8E+FA6XSSYlq2W6ePUuydamz4/lz4YChqZB YG0mlyDCdlLgUNPRQSUeIN+qwvd5KkxICY11+bLNKpaRUnXPPVbbEXelTiiIu9Lw4uSZOa37jOWz d3Z0E1i7ms1UlJtsiGwBMTOQoFJZOxWhEn3QoPiMNY06ODRbTQa143mbUdd0aZSVqJyVvJScCHMy cNym6SFOyp6Cqv3/snOG7+niC6/dFDGWEk/WxhOST9SA7RiosxaI+EJspJADHQ3nG3hrB/bB2Xqv xLZaE3kBAs1isr0GmxArbR2u4sF8HDl8Bbi3LDBFRSloHJK8ojs3liQIjgMQHjonKE/owK0Op/3W p5eGm6SwASGBqGzAuaNV1buVg7U75T9tV7BFC9VBYyybQ0xEwdHGTDvBOCuJjRpg2OpX74iBTgXq 3m8w7M6mLccyAgGHwSsrpZiO3Jx5SNqzOnfxHPSmcsoESy0W62m5LWAIlIo22cQSNGGwhRdbfFYy t3mIHtFxpTBQpluV1pOVx3vXzoXsuM1lOu/EeEnpS3lDSW9GG6FKdZU5IykFApzxxtKNFdBLtK7b OIqJspidGHtWhsCjiuFZjBBuEYZLwa3gubg1K+WBZ6kdUrQqNaxOHpEIvnY38OUegeYO8ZmQxN7s Xqe9C53QvgHfJRXvXKl5mbnVYgzT+a7+swZ1KE3nRAY5uQ3jCvZjIAkuIRSplE1DQL5azOpg7IVD Zl7sklPyY2mO148aYbMXHiatN8XMtO9k5bSeUfy0g0wU0jZ8OjVfl7tfRK7iAx1ry0HkbXMdXEjJ C+hLMHmuQz2u7ER1EoPGZnoteApY5LJV4a6dk5VM0o67XaSpSTtq7EjMHho9JydxjnTa0jIkjIw2 m5HZhK1Q6+qmu8LG06QX3BabTM8XIm0njFfjXTGnVJcewZpklzNik6bTjHJfBmeT3iNOna+jBCon EaDQX2XNmYvcoLS5TG51ZnIWwyVLHu4yvvZKbchNrsmT6UKSrYoHcT7pIoDkh25d3eZfxBxkTbYZ J8aW44vm+iU0LzS+CFI9jTijmNGfVzWzg8dkujA4+U/DwnEM53SBIsCBmQpNqjYapQK7VnspyZLh 3O1J6pTzlRgoHRY0rtPsLNAOFLPY7TJvcWwQJ7U14MklXbauO+c3eJQSGEVd7ZoJqamNrUEigqhx pFIrG7jI1Midyo7b/yFI1nllfPlrpq6c6HMBgqQK3E7mKlOGniG5uZ01tMR7Bh76Ok54Z6hBJSWI VySTOit5RcRghba5GIZ2e9Gd4lSahrg53p4DFsENAsdJxLHIFc97yg+Xnm/DqSkcdV2v+ICeQ4rw zHqDrsFhhyGCWHqHtpiMUcBilw0QIOA6GQ2bXqZiGWYwcy9/AD4Vq+RKm+4PrtGmUZiYxU35SGUn ePETtzjY9cYeshGLUhCHt8e8/FIFB9yi5D9j3HvDvz+T18UONSMEEBQ823iXmpezTXNVySdC9n9D 8sHZcZQzH5lwEmn/Crc4IYYBXWC5wrGr+2fpCc7DIdaSFPTjlQppLiDf2qDlGXHQFQkX8Dit/N+M fSh/YuL05T1HKubtchVOfSonzW5l5FWRIhr+F5lx2aDgVnRX5+BZgcQZMMK0QPAWdLbBIAh2fA1L TtP5fcdFJFAp89gpw9nib0ohMTWr9CMxRlx4UoVQ4JQOWKAZQtBxgqLPqKQHJe9MLOMp2FeP2yMh cThYmGDpT4bsCUcTEfMdnHxnCT7/FD+EVvqxxJJLbbXmcMztc6PqaTrqdB9HlOIVivpzwqY6GxLw 4Yj7pu6zcOqXF0kC+UjIDA+pwOZg9ElqFh40RAcX5nPU5MQ9eeObvj0rnk5NUz6dCEWYr5c9270t nq/PbWcErhwweSDq5fSbwPUaLGwsu+59flnQmMwtKHaOhmKwoZspJhm3FNsHzEXrRg4ueIGd+5xT np5gmCQiQTjlbZt4ilumw05qQryHXslTHbVnE5EqqqJZAUY7epKjr3QDhxjF6i74wAa2wUPFdToa HJqnlWcav62XiHp2PmazzDxR7ZSpU2nsU8MFuTAJ4iLllVpbvzNi6gAZXx0aq3rVCzvWv4bjOnqy LXFycknY2zSFYA7dA4RUSQYEo1HO75UK3BbYLKJXgMKHdLCgjUMgDvK2txuhvO7uOZxenqVY4tQ7 ZdsOukeXZTlqdm+0ro1tmH3HcMusvLiEPWjWiKIJhm2jMDDq/VpQ4gtMRtjSWoTl1PXdjw+OgWeu /JXAgKa71AAWJLqK/D8PHQyo0pKQrtOx8HiLIKY3k2MtZHoKKMqXcLKu9hKVm63q36RO8+Xe0wst 8JSl/IpAt2o2B/aUTcHTQhdXan7mEqBkT+XBmh5BCyBr1N+SPgOkDvkgd0EpAgkAnt1G+74Bnffr ial0gPIYGQFY+zxJdK1He1OKBobhyAhDGuZUMDuyMKqnoD5By6cywwMwzCRztFT79Y5ac3BrN8W5 7qlkG89HnLf4UwLXkeA5zPVBCfyKLElNPdtOev9tm8hBnV1UiyljPS5deTMu14GKm5WIPgEE2HvM eOXapZQPoR+aeCckyDd5+lgtF7RgGQi0HkHS4uhCvZiA987ZQ9KFH7MQ3hUvlwUaQkUkEL6HrGum Y8YkC4sOB97aDtu+L7Ee19I+pIBM18ApWwpzo/KFAsGdX1rW2mgXLaGc/EzQuXMebWTJc0GRQj2x F4lHQOldqNgYunsqHr4EsJZhRVgsYBQnFmoAkbcrMw3BIh7Qj4uw7IsAHc8UkEoPsgOK5mr3iBqD mjYDWwei4z/X8RDmh5Nx7zN4RqbiB7Cla0JDqOHGcE5lFpGPYDeW+ofSO0eRnOYQkaDIgRy3gPDd 8oEp2a9qYdg5Xxc+xiV54Pij5LmlixhusKQc1phdaWpa0u86awcdwcdMORLQpQZQS3uSbg5SBzzn nVl5EHmnLjBRx1qURByWNAoWRABMJRjA77LAPt8qpeNQHkPm3BivLIDqnnPBrConu7TZUfXhALa0 +ni59UCSdnbnd9YQ9oygyoO9LjQ2Xr9DgU3DDFf+rsx6y/PUhjsgxDyEglUfsfrR9/E2n3Ee/QIo nKCHFpRcylBoFCFwgWSprCxPKNyDUUNR9yh6yJwB4DxEXPlsHRSh8F0/jxcgNt6zGGQVLVkZalAg SxAD6Xx2rVS7gkL8B+oyCoFry5KVghYWrUqhYb075KqVIoXDC+ajehTAJ1TC2tDYriULkDII1rCj BaFAKEiUoR0CoHRYIWiBgNBC5vL09AU2+qJzieih1OD4hxVuUWxtIU9gNb3RDIfXJcvIFcq9CuLK wdgewV6CtucRzuYQpj3CkSYiRzEnD0JtaTVebpElAQYx6/agrghTzUiG0RbBFuKul4eGvIC0xaXO QHBXKIsqjEgYs2bRkkIGxljFKm94grwOTBG0HPiIv0al2qWWE7i8OXM3BSXjJ0b1575SMgkkkfGF nckjOjTmk5ppOygbmj8ibVGGQwFJIEAuYBsvEO4aB6amF3QCtUdV51uscU5kEnghUFvanQfSNFMU MQsXlOvUu3rQyQ4Dq6GJ93XOoltUWMW21lFPgpv7QkNowuIFSGqPguwz9gGpuN++9Tj7upC2kOQE 71oFIRhCEBax4PA2hr92BgIWNKI4G6GAztBnnnHh4vNCYQlaFIhLvFgHzg6mxUwHMNhue9kSX/bx 2oF4HdhAqHpa3QkkIB2JlVneYGgcsm6+1lS2CbE5wkJCQJCczmIuWpDSQhlIu8A0XTG1hub4SWeR uIPUkEiQ0G2ebpckLg+BpcdoJnEGc8cesYXaBh73uUgcXrNmaBA0TNRk02fAqngOqEiUHfBLm2dW o3jeNUqJKkLXa4jbIW8uJTLwyHr0DkYaaGh0GjWEIHnIJwG4LfMweEQrfMTp2A6AsGopDM0bAeYC ZKqTkYUghUQ5BysdOVi8eLheO3ziLOVkCDSbhFkUEDEX1TI6h0EFhGs05O5c68jafFrTaO18pioE EWBvHqXs9RRfJncGJE/NlszNEJl9xDPcukAMSDODmaH/ZA84XlqsWxC7l5DjvPkr3ZF28Q7o1L7K m8IHAhIcjRwO/DVEg8jjgriu6lowVtJsapQxIvcnCbRYRF8pL2QyRHIBA5R0N997jGws7CRBMswq UyEELhl9fUIth6gTscPtkkmD0R5BgetxPiGibGttSa6p99HJv4RywH/i7kinChIBoWLvYA== --=-=-=--