summaryrefslogtreecommitdiffstats
path: root/framework
diff options
context:
space:
mode:
Diffstat (limited to 'framework')
-rw-r--r--framework/qml/AttachmentDelegate.qml10
-rw-r--r--framework/qml/Icons.qml1
-rw-r--r--framework/qml/MailViewer.qml2
-rw-r--r--framework/src/domain/mime/attachmentmodel.cpp35
-rw-r--r--framework/src/domain/mime/attachmentmodel.h2
-rw-r--r--framework/src/domain/mime/testdata/openpgp-pkey-attachment.mbox210
6 files changed, 260 insertions, 0 deletions
diff --git a/framework/qml/AttachmentDelegate.qml b/framework/qml/AttachmentDelegate.qml
index 3c308e65..4469cbdd 100644
--- a/framework/qml/AttachmentDelegate.qml
+++ b/framework/qml/AttachmentDelegate.qml
@@ -23,10 +23,12 @@ Item {
23 id: root 23 id: root
24 24
25 property string name 25 property string name
26 property string type
26 property string icon 27 property string icon
27 property alias actionIcon: actionButton.iconName 28 property alias actionIcon: actionButton.iconName
28 signal clicked; 29 signal clicked;
29 signal execute; 30 signal execute;
31 signal publicKeyImport;
30 32
31 width: content.width + Kube.Units.smallSpacing * 1.5 33 width: content.width + Kube.Units.smallSpacing * 1.5
32 height: content.height + Kube.Units.smallSpacing 34 height: content.height + Kube.Units.smallSpacing
@@ -70,6 +72,14 @@ Item {
70 color: Kube.Colors.backgroundColor 72 color: Kube.Colors.backgroundColor
71 } 73 }
72 Kube.IconButton { 74 Kube.IconButton {
75 visible: root.type == "application/pgp-keys"
76 iconName: Kube.Icons.key_import_inverted
77 height: Kube.Units.gridUnit
78 width: height
79 onClicked: root.publicKeyImport()
80 padding: 0
81 }
82 Kube.IconButton {
73 id: actionButton 83 id: actionButton
74 height: Kube.Units.gridUnit 84 height: Kube.Units.gridUnit
75 width: height 85 width: height
diff --git a/framework/qml/Icons.qml b/framework/qml/Icons.qml
index 2afe840e..4dfae3d7 100644
--- a/framework/qml/Icons.qml
+++ b/framework/qml/Icons.qml
@@ -63,6 +63,7 @@ Item {
63 property string secure: "document-encrypt" 63 property string secure: "document-encrypt"
64 property string insecure: "document-decrypt" 64 property string insecure: "document-decrypt"
65 property string signed: "document-sign" 65 property string signed: "document-sign"
66 property string key_import_inverted: "view-certificate-import-inverted"
66 67
67 property string addNew: "list-add" 68 property string addNew: "list-add"
68 property string remove: "kube-list-remove-inverted" 69 property string remove: "kube-list-remove-inverted"
diff --git a/framework/qml/MailViewer.qml b/framework/qml/MailViewer.qml
index 565adedd..e9ffd108 100644
--- a/framework/qml/MailViewer.qml
+++ b/framework/qml/MailViewer.qml
@@ -283,6 +283,7 @@ Rectangle {
283 283
284 delegate: AttachmentDelegate { 284 delegate: AttachmentDelegate {
285 name: model.name 285 name: model.name
286 type: model.type
286 icon: model.iconName 287 icon: model.iconName
287 288
288 clip: true 289 clip: true
@@ -290,6 +291,7 @@ Rectangle {
290 actionIcon: Kube.Icons.save_inverted 291 actionIcon: Kube.Icons.save_inverted
291 onExecute: messageParser.attachments.saveAttachmentToDisk(messageParser.attachments.index(index, 0)) 292 onExecute: messageParser.attachments.saveAttachmentToDisk(messageParser.attachments.index(index, 0))
292 onClicked: messageParser.attachments.openAttachment(messageParser.attachments.index(index, 0)) 293 onClicked: messageParser.attachments.openAttachment(messageParser.attachments.index(index, 0))
294 onPublicKeyImport: messageParser.attachments.importPublicKey(messageParser.attachments.index(index, 0))
293 } 295 }
294 } 296 }
295 } 297 }
diff --git a/framework/src/domain/mime/attachmentmodel.cpp b/framework/src/domain/mime/attachmentmodel.cpp
index 2eb2cc13..8b12679b 100644
--- a/framework/src/domain/mime/attachmentmodel.cpp
+++ b/framework/src/domain/mime/attachmentmodel.cpp
@@ -32,6 +32,11 @@
32#include <QUrl> 32#include <QUrl>
33#include <QMimeDatabase> 33#include <QMimeDatabase>
34 34
35#include <QGpgME/ImportJob>
36#include <QGpgME/Protocol>
37
38#include <memory>
39
35QString sizeHuman(float size) 40QString sizeHuman(float size)
36{ 41{
37 QStringList list; 42 QStringList list;
@@ -210,6 +215,36 @@ bool AttachmentModel::openAttachment(const QModelIndex &index)
210 return false; 215 return false;
211} 216}
212 217
218bool AttachmentModel::importPublicKey(const QModelIndex &index)
219{
220 Q_ASSERT(index.internalPointer());
221 const auto part = static_cast<MimeTreeParser::MessagePart *>(index.internalPointer());
222 Q_ASSERT(part);
223 auto pkey = part->node()->decodedContent();
224
225 const auto *proto = QGpgME::openpgp();
226 std::unique_ptr<QGpgME::ImportJob> job(proto->importJob());
227 auto result = job->exec(pkey);
228
229 bool success = true;
230
231 QString message;
232 if(result.numConsidered() == 0) {
233 message = tr("No keys were found in this attachment");
234 success = false;
235 } else {
236 message = tr("%n Key(s) imported", "", result.numImported());
237 if(result.numUnchanged() != 0) {
238 message += "\n" + tr("%n Key(s) were already imported", "", result.numUnchanged());
239 }
240 }
241
242 Kube::Fabric::Fabric{}.postMessage("notification",
243 {{"message", message}});
244
245 return success;
246}
247
213QModelIndex AttachmentModel::parent(const QModelIndex &) const 248QModelIndex AttachmentModel::parent(const QModelIndex &) const
214{ 249{
215 return QModelIndex(); 250 return QModelIndex();
diff --git a/framework/src/domain/mime/attachmentmodel.h b/framework/src/domain/mime/attachmentmodel.h
index 93ba8d02..d599b1f0 100644
--- a/framework/src/domain/mime/attachmentmodel.h
+++ b/framework/src/domain/mime/attachmentmodel.h
@@ -56,6 +56,8 @@ public:
56 Q_INVOKABLE bool saveAttachmentToDisk(const QModelIndex &parent); 56 Q_INVOKABLE bool saveAttachmentToDisk(const QModelIndex &parent);
57 Q_INVOKABLE bool openAttachment(const QModelIndex &index); 57 Q_INVOKABLE bool openAttachment(const QModelIndex &index);
58 58
59 Q_INVOKABLE bool importPublicKey(const QModelIndex &index);
60
59private: 61private:
60 std::unique_ptr<AttachmentModelPrivate> d; 62 std::unique_ptr<AttachmentModelPrivate> d;
61}; 63};
diff --git a/framework/src/domain/mime/testdata/openpgp-pkey-attachment.mbox b/framework/src/domain/mime/testdata/openpgp-pkey-attachment.mbox
new file mode 100644
index 00000000..aa8bb3e9
--- /dev/null
+++ b/framework/src/domain/mime/testdata/openpgp-pkey-attachment.mbox
@@ -0,0 +1,210 @@
1From test@example.com Wed Feb 28 10:49:49 2018
2Message-ID: <1519811389.16313.1.camel@example.local>
3Subject: Key import
4From: test <test@example.com>
5To: you@you.com
6Content-Type: multipart/mixed; boundary="=-JKkHmrzBpdm/vidMcYH8"
7Date: Wed, 28 Feb 2018 10:49:49 +0100
8Mime-Version: 1.0
9
10
11--=-JKkHmrzBpdm/vidMcYH8
12Content-Type: text/plain
13Content-Transfer-Encoding: 8bit
14
15A very random message.
16
17--=-JKkHmrzBpdm/vidMcYH8
18Content-Type: application/pgp-keys; name="319683D39A1B4DE13DC251FDFEA888C9F5D64F62.asc"
19Content-Disposition: attachment; filename="319683D39A1B4DE13DC251FDFEA888C9F5D64F62.asc"
20Content-Transfer-Encoding: base64
21
22LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tCgptUUVOQkZJTTg3MEJDQUN6RlEy
23dXhXdlIvTDhtaVFldjBaaU5CVFRWSUdpcmVpWXhyTjZQdk1MTGJWd05hUUROClFyZDQ3aldoS1N1
24dkFjRE1BZ3AwSnNUYmptUjdNZDdKZk02bmg5czJwMUd1UHVkTzJmTk43TWlVTTZGWjV4RXUKaFN4
25Y243WjBidHVPMnFLNnJsaURBWmRwZ2wvK1RVWTlxRG1iTHJBSUdLNDZ5eEtzQ0JzQUdKeVRQMzZ1
26eUdlOQp1UjhHRGJ3U3gxNklXVnBnR1RwRDhoUHNVSU1OT2N3cm9sckJmeXRVd2EzZXdRS2hVL01q
27WXFFQmRyR3RWRVFGCk9JeUZWK0dRRWRVSTZmTnVialQ4eUcwbFkwSkZkdlhMejZEelhtVXUzc1d1
28bjByTTJYMEtyclg1VzVrRzVhczIKUjNPUkNDc0JyOGM0a0UvWTBuMDVmRTArVHRtUnFzbm10aUVw
29QUJFQkFBRzBKRzFwYm1scVlXTnJjMjl1SUR4dAphVzVwYW1GamEzTnZia0J5YVhObGRYQXVibVYw
30UG9rQlFRUVRBUUlBS3dJYkl3VUpDV1lCZ0FZTENRZ0hBd0lHCkZRZ0NDUW9MQkJZQ0F3RUNIZ0VD
31RjRBRkFsSVF3Nk1DR1FFQUNna1EvcWlJeWZYV1QySnord2Y5SDNWTGorVGIKQU9YWFdPTEd3RWds
32QTdyZHVaakd6bGE5YUovRW00eTltUVZjVUxIVkZZWGtKRDZ0cmRtNzQ2bzd4dFpsU3JzSQpVNS8y
33cy9mS0lwRjVha3M5bk04a0pkV3JsNnRIcUxPcFphNHFWOFYxRkFkZzhPR1YxQ21abStMMGFzV1RV
34cUxLCmtVMm5BT3dwUnNBRHNySFUvcjVSWjErRXJERnFGU0xHbkFFOWZoY004OE43bkJBUGQrTWtl
35T1lvOTN0NWMzVmEKN2gxSmNuck4wV2luRldwY3l0aEdZL0sxVFFIQ25GZGJrRmlBaVJZRUFLcm9k
36Z3p3MURmZ0V3MXZuRXRNeG0xcQowMGJkaldETDhITXJacEI3cUcvYVMzQkJseXNJQnVheEVqWjNq
37VWtHV3plSEF2c3I3NGdvTEprclRHeWpmUEd0CjhUa2xlMktqVjN0TEs0aGVCQkFSQ0FBR0JRSlZ0
38cC9tQUFvSkVLdFBlK20rclI0ODZ6WUEvaXoxbGdOZC9IcUcKbVN2YUdKaGtpWjJhYjN0WWFwakFk
39KzlGdFpqYnh0Z29BUDBkanlodlF6Rm5DaHZyelRFZ25NemNoUWV0aUFTWApaRThpZlVDZG1nZEx0
40SWtFSEFRUUFRZ0FCZ1VDV0tvZld3QUtDUkNheU1mNThUVXNIbzZvSC8wZlZENktDRmxDClhueC9a
41czA2eG5IOGF1dllzbitpRGl2MENsV1VEcmtPby94cnczODVaUE1pa25EZW5xUjczUmt6QXU5a0F2
42Z2MKYTNIcmhPNzh6OENLdWpvMmpXT20zTDVoYjQ5MDZlR1pyK2MzcDZkUEdMK01OSUhTNHltVW5Y
43M0gyd1phWVN5dgo0YWdiYTVnUFE1ZDdmblhJeitKYnUyTHMwd3d6bXNqSmd5WitxNE96d3Zzdkw3
44UDdGRWNxbzVNNWY4QlV0TFJnCmVxTVFDNU1GUGJ1MVpCa2FsSkRQRFBtZ0RUT25rdlJzVmxtam1S
45NGZwUFN1ZGNkcG1uTGMrQ2orcWFjRVVuSDEKcHFMcFZDbzROV1hpb1NiMUVxbDltUmFMdGVxd3Z5
46endVYnNzNUV4OGptWmg3bmU0dFJPd0lKWjRMaGJIaEtqNwpEMDEvM1Y4UENXVXRkUVk5VC9RT1RY
47NUl3MmhSU1pXYmN5M2tqejVEOFl1WnUyeVdsUHIyYi8yaTJrdjlrRjdGCndCcUNZL01xckZqTmxa
48Y3psWUZMY3JKU0oreWZRU3dMaVc5NGRwYzNwRHY2UVJ0VDhFbHFnMnluMHlKenFrNDgKQURhczBM
49Y3l5U0dEUThROXU0aVJEeTd4aTVGUEdnRDFmdWVEYmVVb09EUTVrczMrdXFnRTNiaVA2SlNHbHpt
50ZwpwS3JqTkdxTzB6eitpakhIb1IxRnhYTG5mZ0FBaUpwcmlTM3N0emY0blA1ekkrak5OOHJmZFJS
51OWMrcUlXaDFiCnJsZS9hZzYvWlNINTNYdTAvR3g5aXliQjJGR1QxMmN2ekJsRjNCbXJhRVdJRUtt
52bktESzVGeTdJM21ISUZPOTAKcjhiaXBiN21iZ0MyWFVTOEJldW9vTS9XZDRCNkdUVVJQUVFJRlhu
53ZWRRL3dNVTBrMFlyekdIMFRyU0xSdkVlegpMdUQwTnFVZmRVeS9kSFNkc3BtZndrOUtrYlhITHkv
54V1NHL29SU2NlZjZjcEFWLzJmM0RlUUF2VWxqSm9ZMTIyCi8xS05MUS96aGJtUlk4Ynd2SksxYjh0
55KzdmY1IwZitDdlFrQ1NNeDM3NXIxUlhJclRVMkd1KzlYM3JIUVRkcXYKb25oQ0t1SWNmY3U1SWp2
56QUZ5NG83TWdDaWt5ck95UVlNNG0zaW1rZHNza3grMjNFcVQvWXpoVzNlK1Z0ZFl2Lwp6VjRna3ZT
57K2F1UWoxN3I5Szdudng5TmhsYVVpZkk2V0lBd3dVU2t2VVcwRm0yQUFyOVAyYnJoU1lMdG51Q2ph
58CmpLOUxyTDFCanlXM3M4V3dXRWdrdktaRmdWRjhoNVFMeUpVSE83NjlQMVRSaTV4M0JnTjE0c2Nr
59Ym9iWk5QOVgKdE5Wcit0WUVvVXB1MVhJS2dDVEpTSmhDUldTZkxsa1crQTljTFN4NS9Zbk5YSlhY
60a0tmNWdUSURlTFpsV0wyQwp0WEVtWjc2UkF4Z25pQTAySVJGOHNweUdEUWphdHFCZWE3TG5mNVJl
61TVFwQTdhVnVhUFFQZXpMTUZvSWtrb0tuCmdkRzNhRXUwM0NLMW0zM0dtclc5bVp1YXVCRTVmaXpH
62OXRVb0ZYRHVMMTJ6V2YvMVAxdVA2SEh3ZlpzbncrYVoKWm5PZEYvVlhob1o0RjFqWG5vdnlxYUIr
63VlF3a09ZTDdNK2pRNjJwNk5uSjVxRlhUV0VTanQzUkV3WjJKaGhVTApreDN0L2lJSFIzMURBQWtU
64VXN6NVJWeDB4dHY4QUxLNWNKYWcxaTc2UWpOYTc1WjNXd0xybnQxd0hKcjBNZEh5CjdkbU96NjVR
65cThQcHRDUnRhVzVwYW1GamEzTnZiaUE4YldsdWFXcGhZMnR6YjI1QWFHOTBiV0ZwYkM1bWNqNkoK
66QVQ0RUV3RUNBQ2dGQWxJTTlFRUNHeU1GQ1FsbUFZQUdDd2tJQndNQ0JoVUlBZ2tLQ3dRV0FnTUJB
67aDRCQWhlQQpBQW9KRVA2b2lNbjExazlpMVVFSC9pc3pBWUlPVjN0RE1oR2pVRUFZWk1Qck9pdHQ2
68NktFVlI5U1l1dEZUcG9lCm5salJ2Snl4Q25DRERlOEtWa1NheTBXUWRhUHhIVTFvWG56b0o0Ujg0
69R0tQSXhBb1NlR1MyM20rZ0ZEOVhKWkEKcDRCWSt4Rmg3NHNRYmM0cGxLUXFCLzJIOFJ1NFZtUmNw
70UTNlQ0lJckJ2R1NKVGs1eVBEeUh6WHBwd3FvNHJocwpsWkphUjhZTlgwSDZ1bmRFUG5wSDA1eVU0
71aG94MktHN21sMGp0dDUwUXlHN1hFbjFEdU9nVkE3WDJGaHJqUFJGCnJWTXFlSWUzbWxxYjNHNVJD
72Q2hLdmtnMmI3QUdJclFWMlVuMDRZOUgwYVBOZERLcFYxVHAvNHAxTG15bFBGMFAKRWJkeXFhai9l
73ZUd2cjBwZDFWWjkxR2hhTW9tK3RjQnpsQ0Z1VjNINjcwMklYZ1FRRVFnQUJnVUNWYmFmNWdBSwpD
74UkNyVDN2cHZxMGVQSWFPQVA5dmhBRHcrMWtuVTRzakZZeW1ZN2IvUWNjT1Fab3pCblNiY1hEMGFK
75eHJJZ0QrCk83VVVlNkxTNnp3Rm5ZV0tVRXdvSTJtK0FRV0M2c081TmExdVhyTDBhOEtKQkJ3RUVB
76RUlBQVlGQWxpcUgyTUEKQ2drUW1zakgrZkUxTEI3SEpCLy9aR2haNnlOY0x2eW11dTk2QW53UHlI
77V1QvUmlYYVE3SC9jZWxlOWVPODZmWQpPaDJjd0xDNzZmaGRyNFJvNDQrRGlJZmRWa1paTU4xYjVi
78amhHcVpmRjQzK21TMXRKVWZwSnV6OTZjMDlySXM0Ckt2dVI2cnJFVFJRbmMzcXdEVUw5UUNoU2hy
79b2FsV2tSMVdSWEZmSG9IeVFQMjlGMlFpYVpRMU52YUVmQjZ0MlYKdmx6TFRPQldCcmNHZ2JOdy9L
80b1lwYTN4MHcwTFZPcTl2bWtLby9YY01QNVBxcnJjWFhGRlpyczU3NkpVU1lBOApqQ2FkZTdMWkZw
81ckErMDZycER2T1liM1hkMzFHYnp6YjBYcHB4UitGNTZmditMSkU0WjhWNjFGSTZGMHhEZFFrCjZ2
82UkFVSUVnUnVvN1l1aXJqbHFIOWxJekpDWEFyT2VBK1FpMTZCTlo2M2pZQzcyeDNNVGZMNGhaeCtT
83L0o4ZUQKWEdmYUZCRDl4enJBQ3hjY1hlOUIxYTJ5T3hxYUtmL3hpSG1vVHE0ZkJyM1B6U1owVm82
84UERTSGlicDBzRER1cAp1NjlxaTVLdHcxcjc3dlhEWlhlQ2VCYTRhNE1BbUVyeFdmdkNKMzBBYlkw
85bUU1dXV4RW1QZVgwWGJYdjdqbGFHCi9nUFl2ZTJKZHkrQVpwOTRZclZjbjgwOVI2YjJoYWo0OXA3
86MDRuMmFBTk5pTExmR0ZQbEN0REFhUkltVzZsUWgKeFJHUm5ZcjhmNldRRCtWWlh1U09jUk0wNnV0
87SUxEUVpYZUcybzFVV2N0YlgxRVFjNmoyU2lyMk9IbGZHMm1iYQo5VzNpYWJuY3lEUFJTMWNGb25a
88dVI0K1BRREU1dStaS0t2TTV0Rk42Y2tQa21zdFJ5clQ1Z3J3SEJSTVJFUTg3CjRxdi9BQjFmenNZ
89R0FCT0RBVjZ6MmYzSGo1YzFxVzZydVp3NVAvRk5ObGw3RmRoYVlxSXF1UzZEa05POEtSSE0KdEc0
90b3VOQkE3U0dNVnp0Ly9HRGg0dlp1SWdXamkwY3NmWi9tRmhVSGQ5cWV3RDZLSnFNTHpJNTZkUi83
91TnZDMwo5MWdvb1c4VjErQkRWWXRqWld3VjFBL2QxeVFBUWd5V2tYdUEwREthOUYwNEpnRHVxQ0NQ
92clVVYWhUVjRKUXlkCkNWZWYzd21icDB5ZmwyZkRNaFNKQjBqdTg0eC9jOUZwWWtEVGhzMDlyb1dM
93SXdiYnB1bUhVNTNXU1B0NHFkVTUKVktSdmF4M0RPRysxOGlVOUdaVmNVSzZQdVRPckErYnE0WXUx
94WGt6NXRvZzFBajZHTUtpY05qdk9MamxEbW9zcQpHZ0NlcmdJVTlTTkI3bnM3MGZ0QnFxQnEwQkYz
95d0dwYlNsTVgzcnJiQUxnMzhSdVpERkZpL1lMTjNBS2lRNW1pCkRrdnp5N0xqUTIweFVFNFBCZGZ5
96Zjl5WTdVLzRmU2FCRjVubUk4U2pkY1hzMGwxeC96M21IWHV0amJYN1YzcFgKZG1uTUdRNkdqNzhF
97NEowNVJ5VmZXT29oKzJCclJnUWZ1RjN6SnREUWlMaUNONmhRczJBV2VEaUZrWmhzaHIxQwphaTl3
98NEVwUXRQcllpSkRlZFQvSHFRaTVUQW14OGl3VGcvM2dGbmhyYmpMSkYvM0wrZEFOK05OSmNqRXBP
99dTZnCnNmVFZZM3JSajdwUEh4cTROR2xiRXpnaUpibEFzeFVaMncvTzZYVG8ybXB5Vk00NFlsUTlH
100SlZnd1lqVUlhWkMKNERXWktnaDE0TlhRYlpBeEhsb1J5eXRQeVU1NTRkZlJtcXVjMnl3Znk3UWxi
101V2x1YVdwaFkydHpiMjRnUEcxcApibWxxWVdOcmMyOXVRRzE1YjNCbGNtRXVZMjl0UG9rQkh3UXdB
102UWdBQ1FVQ1ZhNFJDZ0lkQUFBS0NSRCtxSWpKCjlkWlBZa2ZuQi85NWV1L1JnYVdST2kzZWZueXcz
103N1MrN1BPMCt1eXBKZ2oxZzBpcHVWTjI2UHpleFlEcHVuT2EKcWlMT0dRNy91eS9IRWNkZWw2b2cr
104TDJJd3hMRW9RNVZmMzdCZ2gxVVJlckJDc2xDSis5TVNOQkFpbGRvL3liRApONUhQSHlwSWw3ajVL
105MHo0bjJwY3REeXJETUJxRHFyTUhOQWZ4eWVqWEtOancvVjBBc2NTN2c0SUQxWUhNRVlHCmM0MVBW
106bFZXNzJTM1FEWkxMN0hWMVRXWHZNMkFTcElEckppVDVKTllUaFJiTXVSZlhuZTM2SUtSb0EwVFVT
107NzEKSE5GUlVwU01yQTg0Y3E0RzR6d1Z0eUhmRGF0MVVwZ0RaUDBIOVMyY0ZoM3R1S2pvT2RTZlMz
108dDl4SUdkb2ZFUQpaaFk3clNBNXI3NzBQaFRYakZIekQ3dHllaUhmUnlNTmlRRkJCQk1CQWdBckFo
109c2pCUWtKWmdHQUJnc0pDQWNECkFnWVZDQUlKQ2dzRUZnSURBUUllQVFJWGdBVUNVZ3owUmdJWkFR
110QUtDUkQrcUlqSjlkWlBZbno4Qi85dU0vcGgKYm9oNVNOSHJGRzdZd04vSzRlZnlpNlM5VnNLTWgr
111Zm1sSEVRWWFHaXFra2xYSWYvQ1dqTWNvSlV1bE1IQXhLegpJYXJ0SlpiUW1yeVhjSndwY3NGWDdN
112VTRYNWlnRmMxanFrbUxFRFNxVFVISGJ4U2xNUGI1aHdNajBzY0txUzA4CjVSUDArdnJqb3hnK2Nk
113VjFXUm42cE0yZFpCbUJ1dms3UlVWaUEvV3psNWRkUmlwZmc3RHZoOHRHeC80eTNzVEIKb1VKYXBJ
114VmlBYmpvQTF6RTUvdDlSVVBza1NqdHZvNUZSZEwwK0cxcEhjTzdVWHVYQkg5ajRKK1FqQ0JObFRE
115VwpYNTFKV1MwY1dPdVU0TW1xb1o5Q2FGUGVIN2c0QTcxczQ3Q0VueXZCVFRmUWwwSFVRL3BzeHF3
116Zm9MNzl2Q3YrCnFTSitEemRPZHFmbTRKdTlpUUUrQkJNQkFnQW9CUUpTRFBPOUFoc2pCUWtKWmdH
117QUJnc0pDQWNEQWdZVkNBSUoKQ2dzRUZnSURBUUllQVFJWGdBQUtDUkQrcUlqSjlkWlBZaURKQi85
118UkY3NmlMVWZyN3psZkYrL2REcG1rNWI2cgplZkZCRDZtUjNXSGx3QWtzbFdjNWt4NVh5ZmpZVHpu
119ZzEzUk1LeW5tMndRQkdPL04wVThMMUsvZ3p0Y0tBTkdxCmdWZU5aWXM0V004eVF3a3B3WkxOK2Ra
120N2tTc2VtRDdtNitBbitQWldxMmdiSTVRMHNpOERWbDVZWTVLMm5VSXgKeHRYUyt3RnhVVzA1RkF2
121Z0RtdVJodTFUM2txQ0JkNjBYUDRJbmVqbnV0YjdrUGh6bGlGenZ1VTUxQlJQcmFRZwo3ZXViN1pK
122TW41aWFMdERvNnk3MVVnOXFCYk92TlZ2MHQ1MjF0QWZtQW9ERGZGbnRzeWdxR0gzbFFkN2gvbTFM
123CnlWVmdER0ZMdmxPZXhtSnVxaU5xN0ZVZUhPVC9IWXJtVmZqMEZ1MEtielNKQ0dGRGhEeEFnb0xH
124T1d3WmlRRSsKQkJNQkFnQW9BaHNqQlFrSlpnR0FCZ3NKQ0FjREFnWVZDQUlKQ2dzRUZnSURBUUll
125QVFJWGdBVUNVaEREb3dBSwpDUkQrcUlqSjlkWlBZaFY4Qi80aEErNkJLNkV6K3dISmU3clJ4c2Ez
126WGNlWVdMR2l5bXZud2Q0amRwcjZUZVBQCk5FOXRKZUJjY0lnWVd4RE9HVk53SzF4cDZiWVozNXlm
127WklhaEZGbVhmczUreGZqeE5UamF1Z0FKZU9IS0dkNDIKK1U4WGcydTZITlNDZVdVaXpuUFhua092
128TkgzMmdtdDZOc3JBKzRVZDFLblc5ZS9Nd2tYOEo0ejM2MnVYWWVhQQo5YkdDck5VVDROK0R1cXpn
129VUk3UGMvUFRrRDlBenB1eEN0NnlKZ2sxT2c3S0U1Ukh1Vjh1VnNOREVka2x0dWc1CnZOZUxSM1Js
130RC9PT09aSHhtSnc3a1ZzMEtUbDBzSzFLWVQ0Mnl2bklUTTZ3d3lqZndUaEJKdFZDcldoK0J3WVcK
131a2p6cGUzcGZqRzJTUjBoZWRZWmhXVnZRV3VSSUYxYUxSV0NUd3E5QWlRUWNCQkFCQ0FBR0JRSllx
132aDltQUFvSgpFSnJJeC9ueE5Td2VSN1FmLzA3OWhNZmJ5ODZPWUIvSHUrV3JYVVhKcjRhU3VzTFZu
133aVhNQTRsczdyandyUld0CklVQkRSUk1WOXFRRW41VUNZTWxnNkpHU3kybnhRK3RGZm5KKy9UUWpX
134WG1zS1Q3ZS9SMlF5SU5aZmJhSFdPVVcKZ1JNUWt2NFZYQzFBWTY2R0Z3RklUb3U0dkJuOXNFN21v
135bjZiRkYwOTRxN3IyZFEzSGdGUDk4WnlUV0g2NnRpTwp0bk4yblE1MlE2bnNyT1ZtZWtjZE5Oemxy
136cmFxTmNHOURTNVRpaW5nSFE0bFF1d0FZcUFKMTJuSmx4bSt6L0MxCk5yNm4yL2lVYWd6Z0F6Kzh1
137cXNzalgxeEdBODY5L0RibE9HR0hkSHhsbStjVmlSbElXZWJOUkNjN1RFTytwcTcKUW5pN0Vic3g1
138MktjdmJFbTBBMVQ1a3h4bTB5ZDRQa0poWDFxb2UweVQ1djNSSzNNVWZyNFhFK1JSV1JkWFl3NQpF
139eFJUQmJnTy9UcDBuWW00cGFqRW1DbThFM3dmNkJTR201L2NrYkc3aVFFelUySFFzWlBxYXlZalpK
140eDA1M2IwCmVyRUpKWlFUMlV4REFPN1pUNUJKN2VZemN4ZWRDbTZSOTF5YTJqQ3lldVNtcmJlT1pq
141NGdrNDU3NjFCTUZjZS8KMytuSVB6T1dydUxHZEI2NklFNERGa3hYdlVMVVE2Mk9qOU5FZWRVNDRq
142ZmlSTlNEeU9iaTQ0d1hpb2daYkpOKwpLV1BJRzZhdU54dm9PRXFwc1AvMzBON1BsZnc5ZGM4OERK
143aVNzajJTVW5sdXJQQ1B5bFNWT3E2Qk9oVkxTUmhwCk45OWdnd3VKb25GT2JpeHZ4M0NiOSsyYUhZ
144aVFWdU1sTmJ1Z3Bkb1dVUXJmL2R0dHBDYlBSaXpFZ3loWmc2WEIKdFdXZ0V3WFJFWGxkaWJRWUJO
145RjFWVDdiUWdSenFlR2RWVGpjT2VmTUFnZVlObHBmMnErZ3ZVWjZlS3c1S2pHNgozM1JSL3NucTBl
146eEQ1ZjNJeTVKVnlkZWNBWXAwRHVaazR3dkVYWlc5SGlpaFhIR2lNNUkrQUIzd1FMNitCckQ5CmFk
147NDlmSUcxbTZVSG1JcmlxaUVEWmlhc2o1aUM3ZnZRVENIUXRINVgyVlpLZUt2T0V2WThzMFhUSEZ5
148YjV5Z3QKM0hQOHJKVE5HWHlVV3JLQm9yM1VuMWVGcERPb2hVR1pBeWVSV3lnVC81STUwQ2NCRkVT
149SjI5bk1SOHNzc0wrUAppY0F0QW5URGMyazVaUnp0cmtuV3hXdkJNQkVwdXNVR1N2dE5CdzYwV3A0
150OUx3TjA3MjRNV3M1c29BQjdNOWlIClZEbk9yRy9WMjV5M2tJa1FBYWhCbVRoMkM2MHluTHVGd3B1
151bExIRXBSbDFzbW1yVUlrTXhVZGZYZFJCYzJMREYKV2FVT0hHM1NXWjN5OXVEUjJseDRXQXFsbnZa
152YUErSUFFZnVLaWdRSytiekxGRTJzWWdERFBqK29RL2N6SWJRcgpqUiswSTFRNVUwNjgwUlR3bE80
153NGZnR2pZZjNHS1Q1SXBIUXNWTlIyKzVpOGM4YW4yTFpSeDRDRUJOSlhyREtoCi9zN2ZCR1NhNUFH
154UzVzTWF3K1VLeWFPbGt2Mi9BR0tEUkhib0crQmpKOUczUGpsbmRKcHNWeUJ1TUpIZDh3aEUKMG1H
155emZLY0wybUpFNjg1RlFsQm50SlhIRGxXdTNNOGlaTWM2c0dkT3FPVjNrYkYvd1pIaWpHanc4Sytt
156WGZ2bwpwQXNkZElCLzFYUnlmeEl6enNNdTlTbVAxZTZMazhPMlhIUlY3TFMwSW0xcGJtbHFZV05y
157YzI5dUlEeHRhVzVwCmFtRmphM052YmtCMmJXRnBiQzV0WlQ2SkFUNEVFd0VDQUNnRkFsTU45SXND
158R3lNRkNRbG1BWUFHQ3drSUJ3TUMKQmhVSUFna0tDd1FXQWdNQkFoNEJBaGVBQUFvSkVQNm9pTW4x
159MWs5aTN4QUlBTER1WnJtbkEvU0xTampWdnY3ZgpqZTIza3M3aU9tcnRTTmwrbXJURTF2NGRpRzVl
160a2I0aTdoeXRXNHJCK3ovSkFtaWxuNFQreGFNdDNJakl6bi9jCklOeEQ0cHNsOWlSdjIySUJWNlN4
161MWpHaWttK054RjZjQWJDWDNxOFhBK091N0Q0MDlOQjliTUhTRjgxOUZscXEKSWlhck9tNmVhYUM5
162THViOWdlMEtwREJsQmJpRkVGOFNvMkJubzlCU2c1WDdXcVh2NXVxU1hFcEM2UFd3KzNBTgpvbTkr
163TWM5OVcveEl6c3ZMYk1GQ3BiaHpvbE83THF5OXhCT2tpU3htSUx3NDQ5aytudmJIZlA0cllWUEQy
164Y01QCjFjeCtQV3pxbHBEUXRRZncxMk91eEVTNUZhMGxqVk9tYkY1VkZEangranBrOUphakxaK1d4
165aGZ1aVRMS2VSanQKNnI2SVhnUVFFUWdBQmdVQ1ZiYWY1Z0FLQ1JDclQzdnB2cTBlUEtSK0FQd0xw
166VEtnTFJvc3A5a3dERjVrWTRoUApLUG1ENFVQSG41aFJJNzF0YzhzaDFRRDlHajl4dTcvYXdaN3A3
167Y0dwMXErSVpCTUM0Y0ZXTzlVT2wydS9MUnFZCnJKYUpCQndFRUFFSUFBWUZBbGlxSDJFQUNna1Ft
168c2pIK2ZFMUxCNUhoQi84RC9WUnZsQWxxdFQzVGFPTHJuZVoKS09pbWVEZHJ3cTVBTzBPWWRveUly
169SjZ4ZlBzYmZ1MWlpVk1nemZxNjZaUG8vNkJmRU8rUWdiZWpaRUxYc0NuYgpjSlZLZU9vUkdWMVlz
170bE02R0F2SXlwNk9KRWFlaWlEREJIRzh5L2ZTVytpeFVJaSsrZ1NsWHFLVFZkZEVTeUdPCjMyM0tM
171UHRUQlB1NG5DMlhMWUM0YVV5bUVUNWRVblllalYySFdsYUw2RkNIRndyRmk0akJPSHZIWmllc1dt
172R0UKTUhEd0hKVWRsZUpLcE0zQ0Z5b21iRWRwN2I4MXBqRXNZODg2VDFmSkprTEJLLzh1RlZObDlU
173aFJWSkU1VzFlQgpFVUcyOUtIUHNsRHJCRWhxZ0U1Z3ZXeVFOS2pBdEYycmI4dW9YbERUc29mdERj
174RlFZSU1UQUVuN0YwOGZTQk1aCm4yc0Rwc0FkVVRWRHM3QVdvQlNET2lzZEpKTC9ybG54N2MvQ1dl
175L2NGRHZCMXhUSEtYRzhJS0UzUk5XUnU1eWsKc2hOTVdVWXpBN2pMaG9iRGEzSzlOclVEY2UrYlRP
176a1VRTTc2cHF0aVZuTnpUUDBrenRoWll0UXhvWVc4US90VApqRmJDbFo3bC9vYmw5ZkluMGpTdXNK
177ZGVyc0VNVjBhemdqOE42QjFCQWw0Zk4rTHM1UUtvWVBlVFhxaUFOYjF5CjJjWE9DRjBSNFBKRDFH
178SWZVdjZRN2JWUVp1bTRJUDhKK0Q4VTcyOXhyWlZmMFNqbEx3M3UvODBQanZ2UmU2ZHUKTUxveWMx
179VzNjbEJCZTE5Y0loMFVIOCtRSjg1WUxUOERzYzM2UTVYdnYvVTdha2o3TTU2c2dPWFJuRjRrRG5o
180WQpHZ01GV0F2Y0xCaFVQZmFqSEJmMlROeGt6ZmUwVkt1d2plR2RRK3FHT2VhSHpiQVZEdGRwMnd5
181NnNPM0loVDBtCnZMckRsaWZwNmZIdHpsRVJsU0hpaE0rNm1oVUhXenYya0xObUJlK0FGV0F3cmxo
182c3dLUkg4UHRCQjVYNGRZM3YKWXpuOGY5ZEZtRlc5Q0svQ2M4YWladjg4d2krK1c1Yi9uZTczYWxW
183eVpyWWlCUEFMOFFyeUdzZi82UEdaaWdBSgpiZFhhMGhqSCtlVmsrVlkvLzlPcHhGOTR3czFIbzUx
184ODNEbCtQcTFEY0VxY0lzM3Z2VHlSbXZINTRWcHJXdzF2Ck5sd2YxRDVDSmx0WHIzZXcvaGJPeEFZ
185Y2pHTzhUZnk3OVJ5N0dpcDgrQWMyaVl0Qk03SEI2ZHo2WFp3UEVtUnoKUTNsN1JnOW5lME5RUWUx
186QU5WTThnY081aW9jSmVoZ1padkhDWGNNY0tHeXhHb3dzYmJ3eUZxc2VpR0hUbGRmMAp4OWlTUGNm
187c1pxc25oTmowVUdVQnFyMjVEYi9OMUNoYzlnM05pSWtQRlBocVd6aE1yRE5NcURjWDZreWFXSW1r
188Ci85cDdEdXpPTEJFWlRZSGtLUVJReXRVL0hmclViUHNibkROVDk1QXBqd3NNVkJhZS95c3ZpUUUy
189ZCs0dDZZeFAKKzBLNGxPYktBMGl3cUxqc2hHVmV3U1NBTHNPd1RDUk5kM2J3U2VRWWNnZDI0L1V4
190d2FVZElUV0RnRzkvTEl6Vwp3czVtaGF6Y1RIMmJOOXZ1N3BkOWhDd2szamZkYlBBNHFnZGQ3Y0JG
191TnpmOExJcmR4VVJOZmU0cE05T1ZLTy9jCm4yeXYwL3JUMHNwZU85T2VzNlhpRjdNeGNtbU13N0t3
192bjNkQlJabFVhaHRmbGhPL1RsYysvMEUyT1RSazNsMVcKL3JrQkRRUlNEUE85QVFnQXNQMEZxUDRI
193YmtjeWNRYUx2ekkwV1UzMXdQRFZTRWpRZzZoSUlYQndpbmRpd1kvNgpPTG1hVUNYRjQvT3BncURO
194ZzV5aEFEV29MZ1dGd2FkZjFFdHE1WjB0KzZ1b25FbjNJWTlKYzgwZ1NMYko2eG50CnkvYTFGL3hM
195MkFMQXhsLzkrZVczZUhvQVNJUlc2c05XbXlBWkMzQ1JBU0VZdkVhNnlibURSY1dYVWcyYmljOXgK
196czIwQ3dxMWd6bWtWTCtVNmNvR2dhTWhDRjBOMVY3ajJCbXNhNHJINHpJa21Feml0enFFWkZLV1N4
197UzJENU5KVApWaGRiUHpCdHNZZWQ1dGZFR09sOTlSM21WZ04vUFYyd0hnRk1DQjQ5cW92QXZIS1Yy
198NVU0VW1qQTR0c040SVpYCmpQb3B0RGZMSUNQZndQbFNGcEEyeFJWRTRRdk9vb04vWmRvd3JRQVJB
199UUFCaVFFbEJCZ0JBZ0FQQlFKU0RQTzkKQWhzTUJRa0paZ0dBQUFvSkVQNm9pTW4xMWs5aXgyZ0gv
200MzV0V1o1a3BneXRuZEs5M2s5ZllaUVJQamRabkxwMwpaTHFKV3VGd05CcXUwUWxxaEh5MkRBMExL
201NEFtUmdHc1JOUHdBWGhKTDJ1ZnFMaUl6eHB1S3k1eWZucWluL0F1CjlsWjU5ZmpWZTVKTmh2NmJX
202bFBSalVMeExnQWxyQnZuVU9pSHZ4dHU5cXBXcS9FQWhTWnFMMml2ZU5mNzZMQmEKV1M2MFVheTJx
203Vm1tcGxWQ1VCMHh5R25NR0dHYjUzQlpzaGJmOW1manNwZW52MitUb2FtK3MxYXljaVhVdFU3ZQp5
204ZjU3aEMrWThOM3ZIall4QTRONnVrV01nM0RVb0xSNnh6Z0YyTmlnaE1qbWwrQURrUVlJU1QxeUY2
205TGhueFBkCnQvTU4vSWp6VnVzUUtVaEhvQUNZWGR2OHRiMUd2bDN4U1VyNjE5dWZiRWN5czhFMERI
206WU1NbE09Cj0wTmZxCi0tLS0tRU5EIFBHUCBQVUJMSUMgS0VZIEJMT0NLLS0tLS0K
207
208
209--=-JKkHmrzBpdm/vidMcYH8--
210