From 4b1798f0cdf87361869e7cf2b341acacd056c410 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Wed, 5 Apr 2017 15:04:00 +0200 Subject: Moved cpp code into src directory --- framework/domain/actions/sinkactions.cpp | 56 - framework/domain/actions/tests/CMakeLists.txt | 6 - framework/domain/actions/tests/sinkactiontest.cpp | 58 - framework/domain/attachmentmodel.cpp | 145 -- framework/domain/completer.cpp | 26 - framework/domain/completer.h | 40 - framework/domain/composercontroller.cpp | 307 ----- framework/domain/composercontroller.h | 93 -- framework/domain/contactcontroller.cpp | 68 - framework/domain/contactcontroller.h | 52 - framework/domain/controller.cpp | 59 - framework/domain/controller.h | 90 -- framework/domain/foldercontroller.cpp | 62 - framework/domain/foldercontroller.h | 36 - framework/domain/folderlistmodel.cpp | 145 -- framework/domain/folderlistmodel.h | 71 - framework/domain/identitiesmodel.cpp | 99 -- framework/domain/identitiesmodel.h | 60 - framework/domain/mailcontroller.cpp | 125 -- framework/domain/mailcontroller.h | 44 - framework/domain/maillistmodel.cpp | 265 ---- framework/domain/maillistmodel.h | 94 -- framework/domain/mailtemplates.cpp | 801 ----------- framework/domain/mailtemplates.h | 28 - framework/domain/messageparser.cpp | 114 -- framework/domain/messageparser.h | 157 --- framework/domain/messageparser_new.cpp | 486 ------- framework/domain/messageparser_old.cpp | 151 --- framework/domain/mimetreeparser/CMakeLists.txt | 15 - framework/domain/mimetreeparser/interface.cpp | 1179 ----------------- framework/domain/mimetreeparser/interface.h | 378 ------ framework/domain/mimetreeparser/interface_p.h | 56 - .../domain/mimetreeparser/objecttreesource.cpp | 152 --- framework/domain/mimetreeparser/objecttreesource.h | 57 - .../domain/mimetreeparser/stringhtmlwriter.cpp | 150 --- framework/domain/mimetreeparser/stringhtmlwriter.h | 71 - .../domain/mimetreeparser/tests/CMakeLists.txt | 23 - .../mimetreeparser/tests/data/alternative.mbox | 28 - .../mimetreeparser/tests/data/cid-links.mbox | 1384 -------------------- .../domain/mimetreeparser/tests/data/html.mbox | 15 - ...ed-attachment-and-non-encrypted-attachment.mbox | 115 -- .../data/openpgp-inline-charset-encrypted.mbox | 40 - .../data/openpgp-inline-encrypted+nonenc.mbox | 31 - .../mimetreeparser/tests/data/plaintext.mbox | 13 - .../mimetreeparser/tests/data/smime-encrypted.mbox | 22 - .../mimetreeparser/tests/gnupg_home/CMakeLists.txt | 10 - .../tests/gnupg_home/dirmngr-cache.d/DIR.txt | 3 - ...crl-4E31CEB57DDD4A7B9991AB05507B1ED4293FF952.db | Bin 2130 -> 0 bytes ...crl-7F2A402CBB016A9146D613568C89D3596A4111AA.db | Bin 2048 -> 0 bytes .../mimetreeparser/tests/gnupg_home/dirmngr.conf | 8 - .../tests/gnupg_home/gpg-agent.conf.in | 10 - .../mimetreeparser/tests/gnupg_home/gpg.conf | 244 ---- .../mimetreeparser/tests/gnupg_home/gpgsm.conf.in | 10 - .../tests/gnupg_home/pinentry-fake.sh | 9 - .../1AA8BA52430E51AE249AF0DA97D59F869E4101A8.key | Bin 528 -> 0 bytes .../mimetreeparser/tests/gnupg_home/pubring.gpg | Bin 6757 -> 0 bytes .../mimetreeparser/tests/gnupg_home/pubring.kbx | Bin 2017 -> 0 bytes .../mimetreeparser/tests/gnupg_home/scdaemon.conf | 8 - .../mimetreeparser/tests/gnupg_home/secring.gpg | Bin 5163 -> 0 bytes .../mimetreeparser/tests/gnupg_home/trustdb.gpg | Bin 1440 -> 0 bytes .../mimetreeparser/tests/gnupg_home/trustlist.txt | 11 - .../domain/mimetreeparser/tests/gpgerrortest.cpp | 214 --- .../domain/mimetreeparser/tests/interfacetest.cpp | 310 ----- .../tests/kdepim_add_gpg_crypto_test.cmake | 60 - .../kdepim_generate_crypto_test_wrapper.cmake | 45 - framework/domain/mimetreeparser/thoughts.txt | 148 --- framework/domain/modeltest.cpp | 588 --------- framework/domain/modeltest.h | 83 -- framework/domain/mouseproxy.cpp | 39 - framework/domain/mouseproxy.h | 39 - framework/domain/objecttreesource.cpp | 152 --- framework/domain/objecttreesource.h | 57 - framework/domain/outboxcontroller.cpp | 47 - framework/domain/outboxcontroller.h | 39 - framework/domain/outboxmodel.cpp | 141 -- framework/domain/outboxmodel.h | 77 -- framework/domain/peoplemodel.cpp | 127 -- framework/domain/peoplemodel.h | 69 - framework/domain/recepientautocompletionmodel.cpp | 134 -- framework/domain/recepientautocompletionmodel.h | 56 - framework/domain/retriever.cpp | 55 - framework/domain/retriever.h | 55 - framework/domain/selector.cpp | 31 - framework/domain/selector.h | 56 - framework/domain/settings/CMakeLists.txt | 6 - framework/domain/settings/accountsettings.cpp | 391 ------ framework/domain/settings/accountsettings.h | 123 -- framework/domain/stringhtmlwriter.cpp | 150 --- framework/domain/stringhtmlwriter.h | 71 - 89 files changed, 11143 deletions(-) delete mode 100644 framework/domain/actions/sinkactions.cpp delete mode 100644 framework/domain/actions/tests/CMakeLists.txt delete mode 100644 framework/domain/actions/tests/sinkactiontest.cpp delete mode 100644 framework/domain/attachmentmodel.cpp delete mode 100644 framework/domain/completer.cpp delete mode 100644 framework/domain/completer.h delete mode 100644 framework/domain/composercontroller.cpp delete mode 100644 framework/domain/composercontroller.h delete mode 100644 framework/domain/contactcontroller.cpp delete mode 100644 framework/domain/contactcontroller.h delete mode 100644 framework/domain/controller.cpp delete mode 100644 framework/domain/controller.h delete mode 100644 framework/domain/foldercontroller.cpp delete mode 100644 framework/domain/foldercontroller.h delete mode 100644 framework/domain/folderlistmodel.cpp delete mode 100644 framework/domain/folderlistmodel.h delete mode 100644 framework/domain/identitiesmodel.cpp delete mode 100644 framework/domain/identitiesmodel.h delete mode 100644 framework/domain/mailcontroller.cpp delete mode 100644 framework/domain/mailcontroller.h delete mode 100644 framework/domain/maillistmodel.cpp delete mode 100644 framework/domain/maillistmodel.h delete mode 100644 framework/domain/mailtemplates.cpp delete mode 100644 framework/domain/mailtemplates.h delete mode 100644 framework/domain/messageparser.cpp delete mode 100644 framework/domain/messageparser.h delete mode 100644 framework/domain/messageparser_new.cpp delete mode 100644 framework/domain/messageparser_old.cpp delete mode 100644 framework/domain/mimetreeparser/CMakeLists.txt delete mode 100644 framework/domain/mimetreeparser/interface.cpp delete mode 100644 framework/domain/mimetreeparser/interface.h delete mode 100644 framework/domain/mimetreeparser/interface_p.h delete mode 100644 framework/domain/mimetreeparser/objecttreesource.cpp delete mode 100644 framework/domain/mimetreeparser/objecttreesource.h delete mode 100644 framework/domain/mimetreeparser/stringhtmlwriter.cpp delete mode 100644 framework/domain/mimetreeparser/stringhtmlwriter.h delete mode 100644 framework/domain/mimetreeparser/tests/CMakeLists.txt delete mode 100644 framework/domain/mimetreeparser/tests/data/alternative.mbox delete mode 100644 framework/domain/mimetreeparser/tests/data/cid-links.mbox delete mode 100644 framework/domain/mimetreeparser/tests/data/html.mbox delete mode 100644 framework/domain/mimetreeparser/tests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox delete mode 100644 framework/domain/mimetreeparser/tests/data/openpgp-inline-charset-encrypted.mbox delete mode 100644 framework/domain/mimetreeparser/tests/data/openpgp-inline-encrypted+nonenc.mbox delete mode 100644 framework/domain/mimetreeparser/tests/data/plaintext.mbox delete mode 100644 framework/domain/mimetreeparser/tests/data/smime-encrypted.mbox delete mode 100644 framework/domain/mimetreeparser/tests/gnupg_home/CMakeLists.txt delete mode 100644 framework/domain/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/DIR.txt delete mode 100644 framework/domain/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/crl-4E31CEB57DDD4A7B9991AB05507B1ED4293FF952.db delete mode 100644 framework/domain/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/crl-7F2A402CBB016A9146D613568C89D3596A4111AA.db delete mode 100644 framework/domain/mimetreeparser/tests/gnupg_home/dirmngr.conf delete mode 100644 framework/domain/mimetreeparser/tests/gnupg_home/gpg-agent.conf.in delete mode 100644 framework/domain/mimetreeparser/tests/gnupg_home/gpg.conf delete mode 100644 framework/domain/mimetreeparser/tests/gnupg_home/gpgsm.conf.in delete mode 100755 framework/domain/mimetreeparser/tests/gnupg_home/pinentry-fake.sh delete mode 100644 framework/domain/mimetreeparser/tests/gnupg_home/private-keys-v1.d/1AA8BA52430E51AE249AF0DA97D59F869E4101A8.key delete mode 100644 framework/domain/mimetreeparser/tests/gnupg_home/pubring.gpg delete mode 100644 framework/domain/mimetreeparser/tests/gnupg_home/pubring.kbx delete mode 100644 framework/domain/mimetreeparser/tests/gnupg_home/scdaemon.conf delete mode 100644 framework/domain/mimetreeparser/tests/gnupg_home/secring.gpg delete mode 100644 framework/domain/mimetreeparser/tests/gnupg_home/trustdb.gpg delete mode 100644 framework/domain/mimetreeparser/tests/gnupg_home/trustlist.txt delete mode 100644 framework/domain/mimetreeparser/tests/gpgerrortest.cpp delete mode 100644 framework/domain/mimetreeparser/tests/interfacetest.cpp delete mode 100644 framework/domain/mimetreeparser/tests/kdepim_add_gpg_crypto_test.cmake delete mode 100644 framework/domain/mimetreeparser/tests/kdepim_generate_crypto_test_wrapper.cmake delete mode 100644 framework/domain/mimetreeparser/thoughts.txt delete mode 100644 framework/domain/modeltest.cpp delete mode 100644 framework/domain/modeltest.h delete mode 100644 framework/domain/mouseproxy.cpp delete mode 100644 framework/domain/mouseproxy.h delete mode 100644 framework/domain/objecttreesource.cpp delete mode 100644 framework/domain/objecttreesource.h delete mode 100644 framework/domain/outboxcontroller.cpp delete mode 100644 framework/domain/outboxcontroller.h delete mode 100644 framework/domain/outboxmodel.cpp delete mode 100644 framework/domain/outboxmodel.h delete mode 100644 framework/domain/peoplemodel.cpp delete mode 100644 framework/domain/peoplemodel.h delete mode 100644 framework/domain/recepientautocompletionmodel.cpp delete mode 100644 framework/domain/recepientautocompletionmodel.h delete mode 100644 framework/domain/retriever.cpp delete mode 100644 framework/domain/retriever.h delete mode 100644 framework/domain/selector.cpp delete mode 100644 framework/domain/selector.h delete mode 100644 framework/domain/settings/CMakeLists.txt delete mode 100644 framework/domain/settings/accountsettings.cpp delete mode 100644 framework/domain/settings/accountsettings.h delete mode 100644 framework/domain/stringhtmlwriter.cpp delete mode 100644 framework/domain/stringhtmlwriter.h (limited to 'framework/domain') diff --git a/framework/domain/actions/sinkactions.cpp b/framework/domain/actions/sinkactions.cpp deleted file mode 100644 index 460cb6a1..00000000 --- a/framework/domain/actions/sinkactions.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#include - -#include -#include - -#include -#include - -SINK_DEBUG_AREA("sinkactions") - -using namespace Kube; -using namespace Sink; -using namespace Sink::ApplicationDomain; - -class FolderContext : public Kube::ContextWrapper { - using Kube::ContextWrapper::ContextWrapper; - KUBE_CONTEXTWRAPPER_PROPERTY(Sink::ApplicationDomain::Folder::Ptr, Folder, folder) -}; - -static ActionHandlerHelper synchronizeHandler("org.kde.kube.actions.synchronize", - [](Context *context) -> bool { - return true; - }, - [](Context *context_) { - auto context = FolderContext{*context_}; - if (auto folder = context.getFolder()) { - SinkLog() << "Synchronizing folder " << folder->resourceInstanceIdentifier() << folder->identifier(); - auto scope = SyncScope().resourceFilter(folder->resourceInstanceIdentifier()).filter(QVariant::fromValue(folder->identifier())); - scope.setType(); - Store::synchronize(scope).exec(); - } else { - SinkLog() << "Synchronizing all"; - Store::synchronize(SyncScope()).exec(); - } - } -); - diff --git a/framework/domain/actions/tests/CMakeLists.txt b/framework/domain/actions/tests/CMakeLists.txt deleted file mode 100644 index 2c34d628..00000000 --- a/framework/domain/actions/tests/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -include_directories(${CMAKE_CURRENT_BINARY_DIR}) -cmake_policy(SET CMP0063 NEW) -add_executable(sinkactiontest sinkactiontest.cpp) -add_test(sinkactiontest sinkactiontest) -qt5_use_modules(sinkactiontest Core Test Concurrent) -target_link_libraries(sinkactiontest sink frameworkplugin KF5::Mime) diff --git a/framework/domain/actions/tests/sinkactiontest.cpp b/framework/domain/actions/tests/sinkactiontest.cpp deleted file mode 100644 index 79375503..00000000 --- a/framework/domain/actions/tests/sinkactiontest.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -using namespace Sink; - -class SinkActionTest : public QObject -{ - Q_OBJECT -private slots: - - void initTestCase() - { - Sink::Test::initTest(); - Sink::Log::setDebugOutputLevel(Sink::Log::Trace); - } - - void testSaveAsDraftFail() - { - Kube::Context context; - auto future = Kube::Action("org.kde.kube.actions.save-as-draft", context).executeWithResult(); - - QTRY_VERIFY(future.isDone()); - //because of empty context - QVERIFY(future.error()); - } - - void testSaveAsDraftNew() - { - auto message = KMime::Message::Ptr::create(); - message->subject(true)->fromUnicodeString(QString::fromLatin1("Foobar"), "utf8"); - message->assemble(); - - auto &&account = Test::TestAccount::registerAccount(); - - Kube::Context context; - context.setProperty("message", QVariant::fromValue(message)); - context.setProperty("accountId", QVariant::fromValue(account.identifier)); - auto future = Kube::Action("org.kde.kube.actions.save-as-draft", context).executeWithResult(); - - QTRY_VERIFY(future.isDone()); - QVERIFY(!future.error()); - auto mails = account.entities(); - QCOMPARE(mails.size(), 1); - auto mail = mails.first(); - QVERIFY(mail->getProperty("draft").toBool()); - } -}; - -QTEST_GUILESS_MAIN(SinkActionTest) -#include "sinkactiontest.moc" diff --git a/framework/domain/attachmentmodel.cpp b/framework/domain/attachmentmodel.cpp deleted file mode 100644 index e98c6fc0..00000000 --- a/framework/domain/attachmentmodel.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - Copyright (c) 2016 Sandro Knauß - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#include "messageparser.h" -#include "mimetreeparser/interface.h" - -#include -#include - -QString sizeHuman(const Content::Ptr &content) -{ - float num = content->content().size(); - QStringList list; - list << "KB" << "MB" << "GB" << "TB"; - - QStringListIterator i(list); - QString unit("Bytes"); - - while(num >= 1024.0 && i.hasNext()) - { - unit = i.next(); - num /= 1024.0; - } - - if (unit == "Bytes") { - return QString().setNum(num) + " " + unit; - } else { - return QString().setNum(num,'f',2)+" "+unit; - } -} - -class AttachmentModelPrivate -{ -public: - AttachmentModelPrivate(AttachmentModel *q_ptr, const std::shared_ptr &parser); - - AttachmentModel *q; - std::shared_ptr mParser; - QVector mAttachments; -}; - -AttachmentModelPrivate::AttachmentModelPrivate(AttachmentModel* q_ptr, const std::shared_ptr& parser) - : q(q_ptr) - , mParser(parser) -{ - mAttachments = mParser->collectAttachmentParts(); -} - -AttachmentModel::AttachmentModel(std::shared_ptr parser) - : d(std::unique_ptr(new AttachmentModelPrivate(this, parser))) -{ -} - -AttachmentModel::~AttachmentModel() -{ -} - -QHash AttachmentModel::roleNames() const -{ - QHash roles; - roles[TypeRole] = "type"; - roles[NameRole] = "name"; - roles[SizeRole] = "size"; - roles[IconRole] = "icon"; - roles[IsEncryptedRole] = "encrypted"; - roles[IsSignedRole] = "signed"; - return roles; -} - -QModelIndex AttachmentModel::index(int row, int column, const QModelIndex &parent) const -{ - if (row < 0 || column != 0) { - return QModelIndex(); - } - - if (row < d->mAttachments.size()) { - return createIndex(row, column, d->mAttachments.at(row).get()); - } - return QModelIndex(); -} - -QVariant AttachmentModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) { - switch (role) { - case Qt::DisplayRole: - return QString("root"); - } - return QVariant(); - } - - if (index.internalPointer()) { - const auto entry = static_cast(index.internalPointer()); - const auto content = entry->content().at(0); - switch(role) { - case TypeRole: - return content->mailMime()->mimetype().name(); - case NameRole: - return entry->mailMime()->filename(); - case IconRole: - return QIcon::fromTheme(content->mailMime()->mimetype().iconName()); - case SizeRole: - return sizeHuman(content); - case IsEncryptedRole: - return content->encryptions().size() > 0; - case IsSignedRole: - return content->signatures().size() > 0; - } - } - return QVariant(); -} - -QModelIndex AttachmentModel::parent(const QModelIndex &index) const -{ - return QModelIndex(); -} - -int AttachmentModel::rowCount(const QModelIndex &parent) const -{ - if (!parent.isValid()) { - return d->mAttachments.size(); - } - return 0; -} - -int AttachmentModel::columnCount(const QModelIndex &parent) const -{ - return 1; -} diff --git a/framework/domain/completer.cpp b/framework/domain/completer.cpp deleted file mode 100644 index cacb4faa..00000000 --- a/framework/domain/completer.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekofp - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#include "completer.h" - -#include - -Completer::Completer(QAbstractItemModel *model) : mModel{model} -{ - QQmlEngine::setObjectOwnership(mModel, QQmlEngine::CppOwnership); -} diff --git a/framework/domain/completer.h b/framework/domain/completer.h deleted file mode 100644 index a672b809..00000000 --- a/framework/domain/completer.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekofp - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#pragma once - -#include -#include -#include - -class Completer : public QObject { - Q_OBJECT - Q_PROPERTY (QAbstractItemModel* model READ model CONSTANT) - Q_PROPERTY (QString searchString WRITE setSearchString READ searchString) - -public: - Completer(QAbstractItemModel *model); - QAbstractItemModel *model() { return mModel; } - virtual void setSearchString(const QString &s) { mSearchString = s; } - QString searchString() const { return mSearchString; } - -private: - QAbstractItemModel *mModel = nullptr; - QString mSearchString; -}; - diff --git a/framework/domain/composercontroller.cpp b/framework/domain/composercontroller.cpp deleted file mode 100644 index 3328d9eb..00000000 --- a/framework/domain/composercontroller.cpp +++ /dev/null @@ -1,307 +0,0 @@ -/* - Copyright (c) 2016 Michael Bohlender - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - - -#include "composercontroller.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "identitiesmodel.h" -#include "recepientautocompletionmodel.h" -#include "mailtemplates.h" - -SINK_DEBUG_AREA("composercontroller"); - -class IdentitySelector : public Selector { -public: - IdentitySelector(ComposerController &controller) : Selector(new IdentitiesModel), mController(controller) - { - } - - void setCurrent(const QModelIndex &index) Q_DECL_OVERRIDE - { - if (index.isValid()) { - auto currentAccountId = index.data(IdentitiesModel::AccountId).toByteArray(); - - KMime::Types::Mailbox mb; - mb.setName(index.data(IdentitiesModel::Username).toString()); - mb.setAddress(index.data(IdentitiesModel::Address).toString().toUtf8()); - SinkLog() << "Setting current identity: " << mb.prettyAddress() << "Account: " << currentAccountId; - - mController.setIdentity(mb); - mController.setAccountId(currentAccountId); - } else { - SinkWarning() << "No valid identity for index: " << index; - mController.clearIdentity(); - mController.clearAccountId(); - } - - } -private: - ComposerController &mController; -}; - -class RecipientCompleter : public Completer { -public: - RecipientCompleter() : Completer(new RecipientAutocompletionModel) - { - } - - void setSearchString(const QString &s) { - static_cast(model())->setFilter(s); - Completer::setSearchString(s); - } -}; - - -ComposerController::ComposerController() - : Kube::Controller(), - action_send{new Kube::ControllerAction{this, &ComposerController::send}}, - action_saveAsDraft{new Kube::ControllerAction{this, &ComposerController::saveAsDraft}}, - mRecipientCompleter{new RecipientCompleter}, - mIdentitySelector{new IdentitySelector{*this}} -{ - updateSaveAsDraftAction(); - // mSendAction->monitorProperty(); - // mSendAction->monitorProperty([] (const QString &) -> bool{ - // //validate - // }); - // registerAction(&ComposerController::send); - // actionDepends(); - // TODO do in constructor - - QObject::connect(this, &ComposerController::toChanged, &ComposerController::updateSendAction); - QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSendAction); - QObject::connect(this, &ComposerController::accountIdChanged, &ComposerController::updateSendAction); - QObject::connect(this, &ComposerController::toChanged, &ComposerController::updateSaveAsDraftAction); - QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSaveAsDraftAction); - QObject::connect(this, &ComposerController::accountIdChanged, &ComposerController::updateSaveAsDraftAction); - updateSendAction(); -} - -void ComposerController::clear() -{ - Controller::clear(); - //Reapply account and identity from selection - mIdentitySelector->reapplyCurrentIndex(); -} - -Completer *ComposerController::recipientCompleter() const -{ - return mRecipientCompleter.data(); -} - -Selector *ComposerController::identitySelector() const -{ - return mIdentitySelector.data(); -} - -void ComposerController::setMessage(const KMime::Message::Ptr &msg) -{ - setTo(msg->to(true)->asUnicodeString()); - setCc(msg->cc(true)->asUnicodeString()); - setSubject(msg->subject(true)->asUnicodeString()); - setBody(msg->body()); - setExistingMessage(msg); -} - -void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) -{ - using namespace Sink; - using namespace Sink::ApplicationDomain; - - Query query(*message.value()); - query.request(); - Store::fetchOne(query).then([this, loadAsDraft](const Mail &mail) { - setExistingMail(mail); - - //TODO this should probably happen as reaction to the property being set. - const auto mailData = KMime::CRLFtoLF(mail.getMimeMessage()); - if (!mailData.isEmpty()) { - KMime::Message::Ptr mail(new KMime::Message); - mail->setContent(mailData); - mail->parse(); - if (loadAsDraft) { - setMessage(mail); - } else { - auto reply = MailTemplates::reply(mail); - //We assume reply - setMessage(reply); - } - } else { - qWarning() << "Retrieved empty message"; - } - }).exec(); -} - -void ComposerController::recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName) -{ - if (auto model = static_cast(recipientCompleter()->model())) { - model->addEntry(addrSpec, displayName); - } -} - -void applyAddresses(const QString &list, std::function callback) -{ - for (const auto &to : KEmailAddress::splitAddressList(list)) { - QByteArray displayName; - QByteArray addrSpec; - QByteArray comment; - KEmailAddress::splitAddress(to.toUtf8(), displayName, addrSpec, comment); - callback(addrSpec, displayName); - } -} - -KMime::Message::Ptr ComposerController::assembleMessage() -{ - auto mail = mExistingMessage; - if (!mail) { - mail = KMime::Message::Ptr::create(); - } - applyAddresses(getTo(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { - mail->to(true)->addAddress(addrSpec, displayName); - recordForAutocompletion(addrSpec, displayName); - }); - applyAddresses(getCc(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { - mail->cc(true)->addAddress(addrSpec, displayName); - recordForAutocompletion(addrSpec, displayName); - }); - applyAddresses(getBcc(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { - mail->bcc(true)->addAddress(addrSpec, displayName); - recordForAutocompletion(addrSpec, displayName); - }); - - mail->from(true)->addAddress(getIdentity()); - - mail->subject(true)->fromUnicodeString(getSubject(), "utf-8"); - mail->setBody(getBody().toUtf8()); - if (!mail->messageID()) { - mail->messageID(true)->generate("org.kde.kube"); - } - if (!mail->date(true)->dateTime().isValid()) { - mail->date(true)->setDateTime(QDateTime::currentDateTimeUtc()); - } - - mail->assemble(); - return mail; -} - -void ComposerController::updateSendAction() -{ - auto enabled = !getTo().isEmpty() && !getSubject().isEmpty() && !getAccountId().isEmpty(); - sendAction()->setEnabled(enabled); -} - -void ComposerController::send() -{ - // verify() - // && verify(); - auto message = assembleMessage(); - - auto accountId = getAccountId(); - //SinkLog() << "Sending a mail: " << *this; - using namespace Sink; - using namespace Sink::ApplicationDomain; - - Q_ASSERT(!accountId.isEmpty()); - Query query; - query.containsFilter(ResourceCapabilities::Mail::transport); - query.filter(accountId); - auto job = Store::fetchAll(query) - .then([=](const QList &resources) { - if (!resources.isEmpty()) { - auto resourceId = resources[0]->identifier(); - SinkLog() << "Sending message via resource: " << resourceId; - Mail mail(resourceId); - mail.setMimeMessage(message->encodedContent()); - return Store::create(mail) - .then([=] { - //Trigger a sync, but don't wait for it. - Store::synchronize(Sink::SyncScope{}.resourceFilter(resourceId)).exec(); - }); - } - SinkWarning() << "Failed to find a mailtransport resource"; - return KAsync::error(0, "Failed to find a MailTransport resource."); - }) - .then([&] (const KAsync::Error &error) { - SinkLog() << "Message was sent: "; - emit done(); - }); - run(job); -} - -void ComposerController::updateSaveAsDraftAction() -{ - bool enabled = !getAccountId().isEmpty(); - sendAction()->setEnabled(enabled); -} - -void ComposerController::saveAsDraft() -{ - SinkLog() << "Save as draft"; - const auto accountId = getAccountId(); - auto existingMail = getExistingMail(); - - auto message = assembleMessage(); - //FIXME this is something for the validation - if (!message) { - SinkWarning() << "Failed to get the mail: "; - return; - } - - using namespace Sink; - using namespace Sink::ApplicationDomain; - - auto job = [&] { - if (existingMail.identifier().isEmpty()) { - SinkLog() << "Creating a new draft" << existingMail.identifier(); - Query query; - query.containsFilter(ResourceCapabilities::Mail::drafts); - query.filter(accountId); - return Store::fetchOne(query) - .then([=](const SinkResource &resource) { - Mail mail(resource.identifier()); - mail.setDraft(true); - mail.setMimeMessage(message->encodedContent()); - return Store::create(mail); - }) - .onError([] (const KAsync::Error &error) { - SinkWarning() << "Error while creating draft: " << error.errorMessage; - }); - } else { - SinkLog() << "Modifying an existing mail" << existingMail.identifier(); - existingMail.setDraft(true); - existingMail.setMimeMessage(message->encodedContent()); - return Store::modify(existingMail); - } - }(); - job = job.then([&] (const KAsync::Error &) { - emit done(); - }); - run(job); -} diff --git a/framework/domain/composercontroller.h b/framework/domain/composercontroller.h deleted file mode 100644 index 92467d05..00000000 --- a/framework/domain/composercontroller.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - Copyright (c) 2016 Michael Bohlender - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "completer.h" -#include "selector.h" -#include "controller.h" - -inline bool operator !=(const KMime::Types::Mailbox &l, const KMime::Types::Mailbox &r) -{ - return !(l.prettyAddress() == r.prettyAddress()); -} - -Q_DECLARE_METATYPE(KMime::Types::Mailbox); - -namespace KMime { -class Message; -} - -class ComposerController : public Kube::Controller -{ - Q_OBJECT - - //Interface properties - KUBE_CONTROLLER_PROPERTY(QString, To, to) - KUBE_CONTROLLER_PROPERTY(QString, Cc, cc) - KUBE_CONTROLLER_PROPERTY(QString, Bcc, bcc) - KUBE_CONTROLLER_PROPERTY(QString, Subject, subject) - KUBE_CONTROLLER_PROPERTY(QString, Body, body) - - //Set by identitySelector - KUBE_CONTROLLER_PROPERTY(KMime::Types::Mailbox, Identity, identity) - KUBE_CONTROLLER_PROPERTY(QByteArray, AccountId, accountId) - - //Set by loadMessage - KUBE_CONTROLLER_PROPERTY(KMime::Message::Ptr, ExistingMessage, existingMessage) - KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail, ExistingMail, existingMail) - - Q_PROPERTY (Completer* recipientCompleter READ recipientCompleter CONSTANT) - Q_PROPERTY (Selector* identitySelector READ identitySelector CONSTANT) - //Q_PROPERTY (QValidator* subjectValidator READ subjectValidator CONSTANT) - - KUBE_CONTROLLER_ACTION(send) - KUBE_CONTROLLER_ACTION(saveAsDraft) - -public: - explicit ComposerController(); - - Completer *recipientCompleter() const; - Selector *identitySelector() const; - - Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft); - -public slots: - virtual void clear() Q_DECL_OVERRIDE; - -private slots: - void updateSendAction(); - void updateSaveAsDraftAction(); - -private: - void recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName); - void setMessage(const QSharedPointer &msg); - KMime::Message::Ptr assembleMessage(); - - QScopedPointer mRecipientCompleter; - QScopedPointer mIdentitySelector; -}; diff --git a/framework/domain/contactcontroller.cpp b/framework/domain/contactcontroller.cpp deleted file mode 100644 index 49f78b40..00000000 --- a/framework/domain/contactcontroller.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2017 Michael Bohlender, - * - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "contactcontroller.h" - -#include - -ContactController::ContactController() - : Kube::Controller(), - action_save{new Kube::ControllerAction{this, &ContactController::save}} -{ - updateSaveAction(); -} - -void ContactController::save() { - qWarning() << "Saving is not implemented"; -} - -void ContactController::updateSaveAction() -{ - saveAction()->setEnabled(!getName().isEmpty()); -} - -void ContactController::loadContact(const QVariant &contact) -{ - if (auto c = contact.value()) { - setName(c->getFn()); - QStringList emails; - for (const auto &e : c->getEmails()) { - emails << e.email; - } - setEmails(emails); - } -} - -QVariant ContactController::contact() const -{ - return QVariant{}; -} - -void ContactController::removeEmail(const QString &email) -{ - auto emails = getEmails(); - emails.removeAll(email); - setEmails(emails); -} - -void ContactController::addEmail(const QString &email) -{ - auto emails = getEmails(); - emails.append(email); - setEmails(emails); -} diff --git a/framework/domain/contactcontroller.h b/framework/domain/contactcontroller.h deleted file mode 100644 index d1973d9c..00000000 --- a/framework/domain/contactcontroller.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2017 Michael Bohlender, - * - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - - -#pragma once - -#include -#include -#include - -#include "controller.h" - -class ContactController : public Kube::Controller -{ - Q_OBJECT - - // Input properties - Q_PROPERTY(QVariant contact READ contact WRITE loadContact) - - //Interface properties - KUBE_CONTROLLER_PROPERTY(QString, Name, name) - KUBE_CONTROLLER_PROPERTY(QStringList, Emails, emails) - - KUBE_CONTROLLER_ACTION(save) - -public: - explicit ContactController(); - - Q_INVOKABLE void loadContact(const QVariant &contact); - Q_INVOKABLE void removeEmail(const QString &email); - Q_INVOKABLE void addEmail(const QString &email); - - QVariant contact() const; - -private slots: - void updateSaveAction(); -}; diff --git a/framework/domain/controller.cpp b/framework/domain/controller.cpp deleted file mode 100644 index 52f4cd1f..00000000 --- a/framework/domain/controller.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#include "controller.h" - -#include -#include -#include - -using namespace Kube; - -ControllerAction::ControllerAction() - : QObject() -{ - QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); -} - -void ControllerAction::execute() -{ - emit triggered(); -} - -void Controller::clear() -{ - auto meta = metaObject(); - for (auto i = meta->propertyOffset(); i < meta->propertyCount(); i++) { - auto property = meta->property(i); - setProperty(property.name(), QVariant()); - } - for (const auto &p : dynamicPropertyNames()) { - setProperty(p, QVariant()); - } -} - -void Controller::run(const KAsync::Job &job) -{ - auto jobToExec = job; - jobToExec.onError([] (const KAsync::Error &error) { - SinkWarningCtx(Sink::Log::Context{"controller"}) << "Error while executing job: " << error.errorMessage; - }); - //TODO handle error - //TODO attach a log context to the execution that we can gather from the job? - jobToExec.exec(); -} diff --git a/framework/domain/controller.h b/framework/domain/controller.h deleted file mode 100644 index bf2a7ace..00000000 --- a/framework/domain/controller.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#pragma once - -#include -#include -#include - -#define KUBE_CONTROLLER_PROPERTY(TYPE, NAME, LOWERCASENAME) \ - public: Q_PROPERTY(TYPE LOWERCASENAME MEMBER m##NAME NOTIFY LOWERCASENAME##Changed) \ - Q_SIGNALS: void LOWERCASENAME##Changed(); \ - private: TYPE m##NAME; \ - public: \ - struct NAME { \ - static constexpr const char *name = #LOWERCASENAME; \ - typedef TYPE Type; \ - }; \ - void set##NAME(const TYPE &value) { setProperty(NAME::name, QVariant::fromValue(value)); } \ - void clear##NAME() { setProperty(NAME::name, QVariant{}); } \ - TYPE get##NAME() const { return m##NAME; } \ - - -#define KUBE_CONTROLLER_ACTION(NAME) \ - Q_PROPERTY (Kube::ControllerAction* NAME##Action READ NAME##Action CONSTANT) \ - private: QScopedPointer action_##NAME; \ - public: Kube::ControllerAction* NAME##Action() const { Q_ASSERT(action_##NAME); return action_##NAME.data(); } \ - private slots: void NAME(); \ - - -namespace Kube { - -class ControllerAction : public QObject { - Q_OBJECT - Q_PROPERTY(bool enabled MEMBER mEnabled NOTIFY enabledChanged) -public: - ControllerAction(); - template - ControllerAction(const typename QtPrivate::FunctionPointer::Object *obj, Func slot) - : ControllerAction() - { - QObject::connect(this, &ControllerAction::triggered, obj, slot); - } - - ~ControllerAction() = default; - - Q_INVOKABLE void execute(); - void setEnabled(bool enabled) { setProperty("enabled", enabled); } - -signals: - void enabledChanged(); - void triggered(); - -private: - bool mEnabled = true; -}; - -class Controller : public QObject { - Q_OBJECT -public: - Controller() = default; - virtual ~Controller() = default; - -public slots: - virtual void clear(); - -signals: - void done(); - void error(); - -protected: - void run(const KAsync::Job &job); -}; - -} diff --git a/framework/domain/foldercontroller.cpp b/framework/domain/foldercontroller.cpp deleted file mode 100644 index 3c10f773..00000000 --- a/framework/domain/foldercontroller.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#include "foldercontroller.h" - -#include -#include - -SINK_DEBUG_AREA("foldercontroller"); - -using namespace Sink; -using namespace Sink::ApplicationDomain; - -FolderController::FolderController() - : Kube::Controller(), - action_synchronize{new Kube::ControllerAction{this, &FolderController::synchronize}}, - action_moveToFolder{new Kube::ControllerAction{this, &FolderController::moveToFolder}} -{ -} - -void FolderController::synchronize() -{ - auto job = [&] { - auto accountId = getAccountId(); - if (auto folder = getFolder()) { - SinkLog() << "Synchronizing folder " << folder->resourceInstanceIdentifier() << folder->identifier(); - auto scope = SyncScope().resourceFilter(folder->resourceInstanceIdentifier()).filter(QVariant::fromValue(folder->identifier())); - scope.setType(); - return Store::synchronize(scope); - } else if (!accountId.isEmpty()) { - return Store::synchronize(SyncScope{}.resourceFilter(accountId)); - } else { - SinkLog() << "Synchronizing all"; - return Store::synchronize(SyncScope()); - } - }(); - run(job); -} - -void FolderController::moveToFolder() -{ - auto mail = getMail(); - auto targetFolder = getFolder(); - mail->setFolder(*targetFolder); - SinkLog() << "Moving to folder " << mail->identifier() << targetFolder->identifier(); - run(Store::modify(*mail)); -} diff --git a/framework/domain/foldercontroller.h b/framework/domain/foldercontroller.h deleted file mode 100644 index c0815546..00000000 --- a/framework/domain/foldercontroller.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#pragma once - -#include -#include "controller.h" -#include "sink/applicationdomaintype.h" - -class FolderController : public Kube::Controller -{ - Q_OBJECT - KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Folder::Ptr, Folder, folder) - KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail::Ptr, Mail, mail) - KUBE_CONTROLLER_PROPERTY(QByteArray, AccountId, accountId) - KUBE_CONTROLLER_ACTION(synchronize) - KUBE_CONTROLLER_ACTION(moveToFolder) - -public: - explicit FolderController(); -}; diff --git a/framework/domain/folderlistmodel.cpp b/framework/domain/folderlistmodel.cpp deleted file mode 100644 index 14405beb..00000000 --- a/framework/domain/folderlistmodel.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - Copyright (c) 2016 Michael Bohlender - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#include "folderlistmodel.h" -#include -#include -#include - -using namespace Sink; -using namespace Sink::ApplicationDomain; - -FolderListModel::FolderListModel(QObject *parent) : QSortFilterProxyModel() -{ - setDynamicSortFilter(true); - sort(0, Qt::AscendingOrder); - - Query query; - query.setFlags(Sink::Query::LiveQuery | Sink::Query::UpdateStatus); - query.request().request().request().request(); - query.requestTree(); - query.setId("foldertree"); - runQuery(query); -} - -FolderListModel::~FolderListModel() -{ - -} - -QHash< int, QByteArray > FolderListModel::roleNames() const -{ - QHash roles; - - roles[Name] = "name"; - roles[Icon] = "icon"; - roles[Id] = "id"; - roles[DomainObject] = "domainObject"; - roles[Status] = "status"; - - return roles; -} - -QVariant FolderListModel::data(const QModelIndex &idx, int role) const -{ - auto srcIdx = mapToSource(idx); - auto folder = srcIdx.data(Sink::Store::DomainObjectRole).value(); - switch (role) { - case Name: - return folder->getName(); - case Icon: - return folder->getIcon(); - case Id: - return folder->identifier(); - case DomainObject: - return QVariant::fromValue(folder); - case Status: { - switch (srcIdx.data(Sink::Store::StatusRole).toInt()) { - case Sink::ApplicationDomain::SyncStatus::SyncInProgress: - return InProgressStatus; - case Sink::ApplicationDomain::SyncStatus::SyncError: - return ErrorStatus; - case Sink::ApplicationDomain::SyncStatus::SyncSuccess: - return SuccessStatus; - } - return NoStatus; - } - } - return QSortFilterProxyModel::data(idx, role); -} - -void FolderListModel::runQuery(const Query &query) -{ - mModel = Store::loadModel(query); - setSourceModel(mModel.data()); -} - -void FolderListModel::setAccountId(const QVariant &accountId) -{ - const auto account = accountId.toString().toUtf8(); - - //Get all folders of an account - auto query = Query(); - query.resourceFilter(account); - query.setFlags(Sink::Query::LiveQuery | Sink::Query::UpdateStatus); - query.requestTree(); - query.request() - .request() - .request() - .request(); - query.requestTree(); - query.setId("foldertree" + account); - runQuery(query); -} - -static int getPriority(const Sink::ApplicationDomain::Folder &folder) -{ - auto specialPurpose = folder.getSpecialPurpose(); - if (specialPurpose.contains(Sink::ApplicationDomain::SpecialPurpose::Mail::inbox)) { - return 5; - } else if (specialPurpose.contains(Sink::ApplicationDomain::SpecialPurpose::Mail::drafts)) { - return 6; - } else if (specialPurpose.contains(Sink::ApplicationDomain::SpecialPurpose::Mail::sent)) { - return 7; - } else if (specialPurpose.contains(Sink::ApplicationDomain::SpecialPurpose::Mail::trash)) { - return 8; - } else if (!specialPurpose.isEmpty()) { - return 9; - } - return 10; -} - -bool FolderListModel::lessThan(const QModelIndex &left, const QModelIndex &right) const -{ - const auto leftFolder = left.data(Sink::Store::DomainObjectRole).value(); - const auto rightFolder = right.data(Sink::Store::DomainObjectRole).value(); - const auto leftPriority = getPriority(*leftFolder); - const auto rightPriority = getPriority(*rightFolder); - if (leftPriority == rightPriority) { - return leftFolder->getName() < rightFolder->getName(); - } - return leftPriority < rightPriority; -} - -QVariant FolderListModel::accountId() const -{ - return QVariant(); -} - diff --git a/framework/domain/folderlistmodel.h b/framework/domain/folderlistmodel.h deleted file mode 100644 index 17645bb5..00000000 --- a/framework/domain/folderlistmodel.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - Copyright (c) 2016 Michael Bohlender - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#pragma once - -#include -#include -#include -#include - -namespace Sink { - class Query; -} - -class FolderListModel : public QSortFilterProxyModel -{ - Q_OBJECT - - Q_PROPERTY (QVariant accountId READ accountId WRITE setAccountId) - -public: - enum Status { - NoStatus, - InProgressStatus, - ErrorStatus, - SuccessStatus, - }; - Q_ENUMS(Status) - - FolderListModel(QObject *parent = Q_NULLPTR); - ~FolderListModel(); - - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; - - enum Roles { - Name = Qt::UserRole + 1, - Icon, - Id, - DomainObject, - Status - }; - Q_ENUMS(Roles) - - QHash roleNames() const Q_DECL_OVERRIDE; - - void setAccountId(const QVariant &accountId); - QVariant accountId() const; -protected: - bool lessThan(const QModelIndex &left, const QModelIndex &right) const Q_DECL_OVERRIDE; - -private: - void runQuery(const Sink::Query &query); - QSharedPointer mModel; -}; diff --git a/framework/domain/identitiesmodel.cpp b/framework/domain/identitiesmodel.cpp deleted file mode 100644 index 0d97fc6a..00000000 --- a/framework/domain/identitiesmodel.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#include "identitiesmodel.h" -#include -#include - -using namespace Sink; - -IdentitiesModel::IdentitiesModel(QObject *parent) : QIdentityProxyModel() -{ - Sink::Query query; - query.setFlags(Sink::Query::LiveQuery); - query.request() - .request() - .request(); - runQuery(query); -} - -IdentitiesModel::~IdentitiesModel() -{ - -} - -QHash< int, QByteArray > IdentitiesModel::roleNames() const -{ - QHash roles; - - roles[Name] = "name"; - roles[Username] = "username"; - roles[Address] = "address"; - roles[IdentityId] = "identityId"; - roles[AccountId] = "accountId"; - roles[AccountName] = "accountName"; - roles[AccountIcon] = "accountIcon"; - roles[DisplayName] = "displayName"; - - return roles; -} - -QVariant IdentitiesModel::data(const QModelIndex &idx, int role) const -{ - auto srcIdx = mapToSource(idx); - switch (role) { - case Name: - return srcIdx.data(Sink::Store::DomainObjectRole).value()->getName(); - case Username: - return srcIdx.data(Sink::Store::DomainObjectRole).value()->getName(); - case Address: - return srcIdx.data(Sink::Store::DomainObjectRole).value()->getAddress(); - case IdentityId: - return srcIdx.data(Sink::Store::DomainObjectRole).value()->identifier(); - case AccountId: - return srcIdx.data(Sink::Store::DomainObjectRole).value()->getAccount(); - case AccountName: { - const auto accountId = srcIdx.data(Sink::Store::DomainObjectRole).value()->getAccount(); - return mAccountNames.value(accountId); - } - case AccountIcon: { - const auto accountId = srcIdx.data(Sink::Store::DomainObjectRole).value()->getAccount(); - return mAccountIcons.value(accountId); - } - case DisplayName: { - return data(idx, AccountName).toString() + ": " + data(idx, Username).toString() + ", " + data(idx, Address).toString(); - } - } - return QIdentityProxyModel::data(idx, role); -} - -void IdentitiesModel::runQuery(const Sink::Query &query) -{ - using namespace Sink::ApplicationDomain; - mModel = Sink::Store::loadModel(query); - setSourceModel(mModel.data()); - - Sink::Store::fetchAll(Sink::Query{}.request().request()) - .then([this](const QList &accounts) { - for (const auto &account : accounts) { - mAccountNames.insert(account->identifier(), account->getName()); - mAccountIcons.insert(account->identifier(), account->getIcon()); - } - emit layoutChanged(); - }).exec(); -} diff --git a/framework/domain/identitiesmodel.h b/framework/domain/identitiesmodel.h deleted file mode 100644 index 9b172251..00000000 --- a/framework/domain/identitiesmodel.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#pragma once - -#include -#include -#include -#include - -namespace Sink { - class Query; -} - -class IdentitiesModel : public QIdentityProxyModel -{ - Q_OBJECT - -public: - IdentitiesModel(QObject *parent = Q_NULLPTR); - ~IdentitiesModel(); - - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - - enum Roles { - Name = Qt::UserRole + 1, - Username, - Address, - IdentityId, - AccountId, - AccountName, - AccountIcon, - DisplayName - }; - Q_ENUMS(Roles) - - QHash roleNames() const; - -private: - void runQuery(const Sink::Query &query); - QSharedPointer mModel; - QHash mAccountNames; - QHash mAccountIcons; -}; diff --git a/framework/domain/mailcontroller.cpp b/framework/domain/mailcontroller.cpp deleted file mode 100644 index b912567a..00000000 --- a/framework/domain/mailcontroller.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#include "mailcontroller.h" - -#include -#include -#include - -SINK_DEBUG_AREA("mailcontroller"); - -using namespace Sink; -using namespace Sink::ApplicationDomain; - -MailController::MailController() - : Kube::Controller(), - action_markAsRead{new Kube::ControllerAction{this, &MailController::markAsRead}}, - action_markAsUnread{new Kube::ControllerAction{this, &MailController::markAsUnread}}, - action_markAsImportant{new Kube::ControllerAction{this, &MailController::markAsImportant}}, - action_moveToTrash{new Kube::ControllerAction{this, &MailController::moveToTrash}}, - action_restoreFromTrash{new Kube::ControllerAction{this, &MailController::restoreFromTrash}}, - action_remove{new Kube::ControllerAction{this, &MailController::remove}}, - action_moveToFolder{new Kube::ControllerAction{this, &MailController::moveToFolder}} -{ - QObject::connect(this, &MailController::mailChanged, &MailController::updateActions); - updateActions(); -} - -void MailController::runModification(const std::function &f) -{ - if (auto mail = getMail()) { - f(*mail); - run(Store::modify(*mail)); - } else if (auto mail = getThreadLeader()) { - f(*mail); - run(Store::modify(Sink::StandardQueries::completeThread(*mail), *mail)); - } -} - -void MailController::updateActions() -{ - auto mail = getMail(); - if (!mail) { - mail= getThreadLeader(); - } - if (mail) { - action_moveToTrash->setEnabled(!mail->getTrash()); - action_restoreFromTrash->setEnabled(mail->getTrash()); - action_markAsRead->setEnabled(mail->getUnread()); - action_markAsUnread->setEnabled(!mail->getUnread()); - } -} - -void MailController::markAsRead() -{ - runModification([] (ApplicationDomain::Mail &mail) { - mail.setUnread(false); - SinkLog() << "Mark as read " << mail.identifier(); - }); -} - -void MailController::markAsUnread() -{ - runModification([] (ApplicationDomain::Mail &mail) { - mail.setUnread(true); - SinkLog() << "Mark as unread " << mail.identifier(); - }); -} - -void MailController::markAsImportant() -{ - runModification([] (ApplicationDomain::Mail &mail) { - mail.setImportant(true); - SinkLog() << "Mark as important " << mail.identifier(); - }); -} - -void MailController::moveToTrash() -{ - runModification([] (ApplicationDomain::Mail &mail) { - mail.setTrash(true); - SinkLog() << "Move to trash " << mail.identifier(); - }); -} - -void MailController::restoreFromTrash() -{ - runModification([] (ApplicationDomain::Mail &mail) { - mail.setTrash(false); - SinkLog() << "Restore from trash " << mail.identifier(); - }); -} - -void MailController::remove() -{ - runModification([] (ApplicationDomain::Mail &mail) { - mail.setTrash(true); - SinkLog() << "Remove " << mail.identifier(); - }); -} - -void MailController::moveToFolder() -{ - runModification([&] (ApplicationDomain::Mail &mail) { - auto targetFolder = getTargetFolder(); - mail.setFolder(*targetFolder); - SinkLog() << "Moving to folder " << mail.identifier() << targetFolder->identifier(); - }); -} - diff --git a/framework/domain/mailcontroller.h b/framework/domain/mailcontroller.h deleted file mode 100644 index ae0f32d5..00000000 --- a/framework/domain/mailcontroller.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#pragma once - -#include -#include "controller.h" -#include "sink/applicationdomaintype.h" - -class MailController : public Kube::Controller -{ - Q_OBJECT - KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail::Ptr, Mail, mail) - KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail::Ptr, ThreadLeader, threadLeader) - KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Folder::Ptr, TargetFolder, targetFolder) - KUBE_CONTROLLER_ACTION(markAsRead) - KUBE_CONTROLLER_ACTION(markAsUnread) - KUBE_CONTROLLER_ACTION(markAsImportant) - KUBE_CONTROLLER_ACTION(moveToTrash) - KUBE_CONTROLLER_ACTION(restoreFromTrash) - KUBE_CONTROLLER_ACTION(remove) - KUBE_CONTROLLER_ACTION(moveToFolder) - -public: - explicit MailController(); -private slots: - void updateActions(); - void runModification(const std::function &f); -}; diff --git a/framework/domain/maillistmodel.cpp b/framework/domain/maillistmodel.cpp deleted file mode 100644 index 83340f69..00000000 --- a/framework/domain/maillistmodel.cpp +++ /dev/null @@ -1,265 +0,0 @@ -/* - Copyright (c) 2016 Michael Bohlender - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#include "maillistmodel.h" - -#include - -MailListModel::MailListModel(QObject *parent) - : QSortFilterProxyModel() -{ - setDynamicSortFilter(true); - sort(0, Qt::DescendingOrder); - setFilterCaseSensitivity(Qt::CaseInsensitive); -} - -MailListModel::~MailListModel() -{ - -} - -void MailListModel::setFilter(const QString &filter) -{ - setFilterWildcard(filter); -} - -QString MailListModel::filter() const -{ - return {}; -} - -QHash< int, QByteArray > MailListModel::roleNames() const -{ - QHash roles; - - roles[Subject] = "subject"; - roles[Sender] = "sender"; - roles[SenderName] = "senderName"; - roles[To] = "to"; - roles[Cc] = "cc"; - roles[Bcc] = "bcc"; - roles[Date] = "date"; - roles[Unread] = "unread"; - roles[Important] = "important"; - roles[Draft] = "draft"; - roles[Sent] = "sent"; - roles[Trash] = "trash"; - roles[Id] = "id"; - roles[MimeMessage] = "mimeMessage"; - roles[DomainObject] = "domainObject"; - roles[ThreadSize] = "threadSize"; - roles[Mail] = "mail"; - roles[Incomplete] = "incomplete"; - roles[Status] = "status"; - - return roles; -} - -static QString join(const QList &contacts) -{ - QStringList list; - for (const auto &contact : contacts) { - if (!contact.name.isEmpty()) { - list << QString("%1 <%2>").arg(contact.name).arg(contact.emailAddress); - } else { - list << contact.emailAddress; - } - } - return list.join(", "); -} - -void MailListModel::fetchMail(Sink::ApplicationDomain::Mail::Ptr mail) -{ - if (mail && !mail->getFullPayloadAvailable() && !mFetchedMails.contains(mail->identifier())) { - qDebug() << "Fetching mail: " << mail->identifier() << mail->getSubject(); - mFetchedMails.insert(mail->identifier()); - Sink::Store::synchronize(Sink::SyncScope{*mail}).exec(); - } -} - -QVariant MailListModel::data(const QModelIndex &idx, int role) const -{ - auto srcIdx = mapToSource(idx); - auto mail = srcIdx.data(Sink::Store::DomainObjectRole).value(); - switch (role) { - case Subject: - return mail->getSubject(); - case Sender: - return mail->getSender().emailAddress; - case SenderName: - return mail->getSender().name; - case To: - return join(mail->getTo()); - case Cc: - return join(mail->getCc()); - case Bcc: - return join(mail->getBcc()); - case Date: - return mail->getDate(); - case Unread: - return mail->getProperty("unreadCollected").toList().contains(true); - case Important: - return mail->getProperty("importantCollected").toList().contains(true); - case Draft: - return mail->getDraft(); - case Sent: - return mail->getSent(); - case Trash: - return mail->getTrash(); - case Id: - return mail->identifier(); - case DomainObject: - return QVariant::fromValue(mail); - case MimeMessage: - if (mFetchMails) { - const_cast(this)->fetchMail(mail); - } - return mail->getMimeMessage(); - case ThreadSize: - return mail->getProperty("count").toInt(); - case Mail: - return QVariant::fromValue(mail); - case Incomplete: - return !mail->getFullPayloadAvailable(); - case Status: - const auto status = srcIdx.data(Sink::Store::StatusRole).toInt(); - if (status == Sink::ApplicationDomain::SyncStatus::SyncInProgress) { - return InProgressStatus; - } - if (status == Sink::ApplicationDomain::SyncStatus::SyncError) { - return ErrorStatus; - } - return NoStatus; - } - return QSortFilterProxyModel::data(idx, role); -} - -bool MailListModel::lessThan(const QModelIndex &left, const QModelIndex &right) const -{ - const auto leftDate = left.data(Sink::Store::DomainObjectRole).value()->getDate(); - const auto rightDate = right.data(Sink::Store::DomainObjectRole).value()->getDate(); - return leftDate < rightDate; -} - -bool MailListModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const -{ - auto idx = sourceModel()->index(sourceRow, 0, sourceParent); - auto regExp = filterRegExp(); - if (regExp.isEmpty()) { - return true; - } - auto mail = idx.data(Sink::Store::DomainObjectRole).value(); - return mail->getSubject().contains(regExp) || - mail->getSender().name.contains(regExp); -} - -void MailListModel::runQuery(const Sink::Query &query) -{ - m_model = Sink::Store::loadModel(query); - setSourceModel(m_model.data()); -} - -void MailListModel::setParentFolder(const QVariant &parentFolder) -{ - using namespace Sink::ApplicationDomain; - auto folder = parentFolder.value(); - if (!folder) { - mCurrentQueryItem.clear(); - setSourceModel(nullptr); - return; - } - if (mCurrentQueryItem == folder->identifier()) { - return; - } - mCurrentQueryItem = folder->identifier(); - Sink::Query query = Sink::StandardQueries::threadLeaders(*folder); - if (!folder->getSpecialPurpose().contains(Sink::ApplicationDomain::SpecialPurpose::Mail::trash)) { - //Filter trash if this is not a trash folder - query.filter(false); - } - query.setFlags(Sink::Query::LiveQuery); - query.limit(100); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - mFetchMails = false; - qDebug() << "Running folder query: " << folder->resourceInstanceIdentifier() << folder->identifier(); - //Latest mail on top - sort(0, Qt::DescendingOrder); - runQuery(query); -} - -QVariant MailListModel::parentFolder() const -{ - return QVariant(); -} - -void MailListModel::setMail(const QVariant &variant) -{ - using namespace Sink::ApplicationDomain; - auto mail = variant.value(); - if (!mail) { - mCurrentQueryItem.clear(); - setSourceModel(nullptr); - return; - } - if (mCurrentQueryItem == mail->identifier()) { - return; - } - mCurrentQueryItem = mail->identifier(); - Sink::Query query = Sink::StandardQueries::completeThread(*mail); - query.setFlags(Sink::Query::LiveQuery | Sink::Query::UpdateStatus); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - mFetchMails = true; - mFetchedMails.clear(); - qDebug() << "Running mail query: " << mail->resourceInstanceIdentifier() << mail->identifier(); - //Latest mail at the bottom - sort(0, Qt::AscendingOrder); - runQuery(query); -} - -QVariant MailListModel::mail() const -{ - return QVariant(); -} - - diff --git a/framework/domain/maillistmodel.h b/framework/domain/maillistmodel.h deleted file mode 100644 index 44347661..00000000 --- a/framework/domain/maillistmodel.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - Copyright (c) 2016 Michael Bohlender - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#pragma once - -#include - -#include -#include -#include - -class MailListModel : public QSortFilterProxyModel -{ - Q_OBJECT - Q_PROPERTY (QVariant parentFolder READ parentFolder WRITE setParentFolder) - Q_PROPERTY (QVariant mail READ mail WRITE setMail) - Q_PROPERTY (QString filter READ filter WRITE setFilter) - -public: - enum Status { - NoStatus, - InProgressStatus, - ErrorStatus - }; - Q_ENUMS(Status) - - MailListModel(QObject *parent = Q_NULLPTR); - ~MailListModel(); - - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; - - bool lessThan(const QModelIndex &left, const QModelIndex &right) const Q_DECL_OVERRIDE; - bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_OVERRIDE; - - enum Roles { - Subject = Qt::UserRole + 1, - Sender, - SenderName, - To, - Cc, - Bcc, - Date, - Unread, - Important, - Draft, - Sent, - Trash, - Id, - MimeMessage, - DomainObject, - ThreadSize, - Mail, - Incomplete, - Status - }; - - QHash roleNames() const Q_DECL_OVERRIDE; - - void runQuery(const Sink::Query &query); - - void setParentFolder(const QVariant &parentFolder); - QVariant parentFolder() const; - - void setMail(const QVariant &mail); - QVariant mail() const; - - void setFilter(const QString &mail); - QString filter() const; - -private: - void fetchMail(Sink::ApplicationDomain::Mail::Ptr mail); - - QSharedPointer m_model; - bool mFetchMails = false; - QSet mFetchedMails; - QByteArray mCurrentQueryItem; -}; diff --git a/framework/domain/mailtemplates.cpp b/framework/domain/mailtemplates.cpp deleted file mode 100644 index 6af381d2..00000000 --- a/framework/domain/mailtemplates.cpp +++ /dev/null @@ -1,801 +0,0 @@ -/* - Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com - Copyright (c) 2010 Leo Franchi - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#include "mailtemplates.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "stringhtmlwriter.h" -#include "objecttreesource.h" - -#include - -namespace KMime { - namespace Types { -static bool operator==(const KMime::Types::AddrSpec &left, const KMime::Types::AddrSpec &right) -{ - return (left.asString() == right.asString()); -} - -static bool operator==(const KMime::Types::Mailbox &left, const KMime::Types::Mailbox &right) -{ - return (left.addrSpec().asString() == right.addrSpec().asString()); -} - } -} - -static KMime::Types::Mailbox::List stripMyAddressesFromAddressList(const KMime::Types::Mailbox::List &list, const KMime::Types::AddrSpecList me) -{ - KMime::Types::Mailbox::List addresses(list); - for (KMime::Types::Mailbox::List::Iterator it = addresses.begin(); it != addresses.end();) { - if (me.contains(it->addrSpec())) { - it = addresses.erase(it); - } else { - ++it; - } - } - - return addresses; -} - -void initHeader(const KMime::Message::Ptr &message) -{ - message->removeHeader(); - message->removeHeader(); - message->date()->setDateTime(QDateTime::currentDateTime()); - - const QStringList extraInfo = QStringList() << QSysInfo::prettyProductName(); - message->userAgent()->fromUnicodeString(QString("%1/%2(%3)").arg(QString::fromLocal8Bit("Kube")).arg("0.1").arg(extraInfo.join(",")), "utf-8"); - // This will allow to change Content-Type: - message->contentType()->setMimeType("text/plain"); -} - -QString replacePrefixes(const QString &str, const QStringList &prefixRegExps, - bool replace, const QString &newPrefix) -{ - bool recognized = false; - // construct a big regexp that - // 1. is anchored to the beginning of str (sans whitespace) - // 2. matches at least one of the part regexps in prefixRegExps - QString bigRegExp = QStringLiteral("^(?:\\s+|(?:%1))+\\s*").arg(prefixRegExps.join(QStringLiteral(")|(?:"))); - QRegExp rx(bigRegExp, Qt::CaseInsensitive); - if (rx.isValid()) { - QString tmp = str; - if (rx.indexIn(tmp) == 0) { - recognized = true; - if (replace) { - return tmp.replace(0, rx.matchedLength(), newPrefix + QLatin1String(" ")); - } - } - } else { - qWarning() << "bigRegExp = \"" - << bigRegExp << "\"\n" - << "prefix regexp is invalid!"; - // try good ole Re/Fwd: - recognized = str.startsWith(newPrefix); - } - - if (!recognized) { - return newPrefix + QLatin1String(" ") + str; - } else { - return str; - } -} - -QString cleanSubject(const KMime::Message::Ptr &msg, const QStringList &prefixRegExps, bool replace, const QString &newPrefix) -{ - return replacePrefixes(msg->subject()->asUnicodeString(), prefixRegExps, replace, newPrefix); -} - -QString forwardSubject(const KMime::Message::Ptr &msg) -{ - bool replaceForwardPrefix = true; - QStringList forwardPrefixes; - forwardPrefixes << "Fwd:"; - forwardPrefixes << "FW:"; - return cleanSubject(msg, forwardPrefixes, replaceForwardPrefix, QStringLiteral("Fwd:")); -} - -QString replySubject(const KMime::Message::Ptr &msg) -{ - bool replaceReplyPrefix = true; - QStringList replyPrefixes; - //We're escaping the regex escape sequences. awesome - replyPrefixes << "Re\\\\s*:"; - replyPrefixes << "Re[\\\\d+\\\\]:"; - replyPrefixes << "Re\\\\d+:"; - return cleanSubject(msg, replyPrefixes, replaceReplyPrefix, QStringLiteral("Re:")); -} - -QByteArray getRefStr(const KMime::Message::Ptr &msg) -{ - QByteArray firstRef, lastRef, refStr, retRefStr; - int i, j; - - if (auto hdr = msg->references(false)) { - refStr = hdr->as7BitString(false).trimmed(); - } - - if (refStr.isEmpty()) { - return msg->messageID()->as7BitString(false); - } - - i = refStr.indexOf('<'); - j = refStr.indexOf('>'); - firstRef = refStr.mid(i, j - i + 1); - if (!firstRef.isEmpty()) { - retRefStr = firstRef + ' '; - } - - i = refStr.lastIndexOf('<'); - j = refStr.lastIndexOf('>'); - - lastRef = refStr.mid(i, j - i + 1); - if (!lastRef.isEmpty() && lastRef != firstRef) { - retRefStr += lastRef + ' '; - } - - retRefStr += msg->messageID()->as7BitString(false); - return retRefStr; -} - -KMime::Content *createPlainPartContent(const KMime::Message::Ptr &msg, const QString &plainBody) -{ - KMime::Content *textPart = new KMime::Content(msg.data()); - textPart->contentType()->setMimeType("text/plain"); - //FIXME This is supposed to select a charset out of the available charsets that contains all necessary characters to render the text - // QTextCodec *charset = selectCharset(m_charsets, plainBody); - // textPart->contentType()->setCharset(charset->name()); - textPart->contentType()->setCharset("utf-8"); - textPart->contentTransferEncoding()->setEncoding(KMime::Headers::CE8Bit); - textPart->fromUnicodeString(plainBody); - return textPart; -} - -KMime::Content *createMultipartAlternativeContent(const KMime::Message::Ptr &msg, const QString &plainBody, const QString &htmlBody) -{ - KMime::Content *multipartAlternative = new KMime::Content(msg.data()); - multipartAlternative->contentType()->setMimeType("multipart/alternative"); - const QByteArray boundary = KMime::multiPartBoundary(); - multipartAlternative->contentType()->setBoundary(boundary); - - KMime::Content *textPart = createPlainPartContent(msg, plainBody); - multipartAlternative->addContent(textPart); - - KMime::Content *htmlPart = new KMime::Content(msg.data()); - htmlPart->contentType()->setMimeType("text/html"); - //FIXME This is supposed to select a charset out of the available charsets that contains all necessary characters to render the text - // QTextCodec *charset = selectCharset(m_charsets, htmlBody); - // htmlPart->contentType()->setCharset(charset->name()); - textPart->contentType()->setCharset("utf-8"); - htmlPart->contentTransferEncoding()->setEncoding(KMime::Headers::CE8Bit); - htmlPart->fromUnicodeString(htmlBody); - multipartAlternative->addContent(htmlPart); - - return multipartAlternative; -} - -void addProcessedBodyToMessage(const KMime::Message::Ptr &msg, const QString &plainBody, const QString &htmlBody, bool forward) -{ - //FIXME - // MessageCore::ImageCollector ic; - // ic.collectImagesFrom(mOrigMsg.data()); - - // Now, delete the old content and set the new content, which - // is either only the new text or the new text with some attachments. - auto parts = msg->contents(); - foreach (KMime::Content *content, parts) { - msg->removeContent(content, true/*delete*/); - } - - msg->contentType()->clear(); // to get rid of old boundary - - const QByteArray boundary = KMime::multiPartBoundary(); - KMime::Content *const mainTextPart = - htmlBody.isEmpty() ? - createPlainPartContent(msg, plainBody) : - createMultipartAlternativeContent(msg, plainBody, htmlBody); - mainTextPart->assemble(); - - KMime::Content *textPart = mainTextPart; - // if (!ic.images().empty()) { - // textPart = createMultipartRelated(ic, mainTextPart); - // textPart->assemble(); - // } - - // If we have some attachments, create a multipart/mixed mail and - // add the normal body as well as the attachments - KMime::Content *mainPart = textPart; - //FIXME - // if (forward) { - // auto attachments = mOrigMsg->attachments(); - // attachments += mOtp->nodeHelper()->attachmentsOfExtraContents(); - // if (!attachments.isEmpty()) { - // mainPart = createMultipartMixed(attachments, textPart); - // mainPart->assemble(); - // } - // } - - msg->setBody(mainPart->encodedBody()); - msg->setHeader(mainPart->contentType()); - msg->setHeader(mainPart->contentTransferEncoding()); - msg->assemble(); - msg->parse(); -} - -QString plainToHtml(const QString &body) -{ - QString str = body; - str = str.toHtmlEscaped(); - str.replace(QStringLiteral("\n"), QStringLiteral("
\n")); - return str; -} - -//TODO implement this function using a DOM tree parser -void makeValidHtml(QString &body, const QString &headElement) -{ - QRegExp regEx; - regEx.setMinimal(true); - regEx.setPattern(QStringLiteral("")); - - if (!body.isEmpty() && !body.contains(regEx)) { - regEx.setPattern(QStringLiteral("")); - if (!body.contains(regEx)) { - body = QLatin1String("") + body + QLatin1String("
"); - } - regEx.setPattern(QStringLiteral("")); - if (!body.contains(regEx)) { - body = QLatin1String("") + headElement + QLatin1String("") + body; - } - body = QLatin1String("") + body + QLatin1String(""); - } -} - -QString stripSignature(const QString &msg) -{ - // Following RFC 3676, only > before -- - // I prefer to not delete a SB instead of delete good mail content. - const QRegExp sbDelimiterSearch = QRegExp(QLatin1String("(^|\n)[> ]*-- \n")); - // The regular expression to look for prefix change - const QRegExp commonReplySearch = QRegExp(QLatin1String("^[ ]*>")); - - QString res = msg; - int posDeletingStart = 1; // to start looking at 0 - - // While there are SB delimiters (start looking just before the deleted SB) - while ((posDeletingStart = res.indexOf(sbDelimiterSearch, posDeletingStart - 1)) >= 0) { - QString prefix; // the current prefix - QString line; // the line to check if is part of the SB - int posNewLine = -1; - - // Look for the SB beginning - int posSignatureBlock = res.indexOf(QLatin1Char('-'), posDeletingStart); - // The prefix before "-- "$ - if (res.at(posDeletingStart) == QLatin1Char('\n')) { - ++posDeletingStart; - } - - prefix = res.mid(posDeletingStart, posSignatureBlock - posDeletingStart); - posNewLine = res.indexOf(QLatin1Char('\n'), posSignatureBlock) + 1; - - // now go to the end of the SB - while (posNewLine < res.size() && posNewLine > 0) { - // handle the undefined case for mid ( x , -n ) where n>1 - int nextPosNewLine = res.indexOf(QLatin1Char('\n'), posNewLine); - - if (nextPosNewLine < 0) { - nextPosNewLine = posNewLine - 1; - } - - line = res.mid(posNewLine, nextPosNewLine - posNewLine); - - // check when the SB ends: - // * does not starts with prefix or - // * starts with prefix+(any substring of prefix) - if ((prefix.isEmpty() && line.indexOf(commonReplySearch) < 0) || - (!prefix.isEmpty() && line.startsWith(prefix) && - line.mid(prefix.size()).indexOf(commonReplySearch) < 0)) { - posNewLine = res.indexOf(QLatin1Char('\n'), posNewLine) + 1; - } else { - break; // end of the SB - } - } - - // remove the SB or truncate when is the last SB - if (posNewLine > 0) { - res.remove(posDeletingStart, posNewLine - posDeletingStart); - } else { - res.truncate(posDeletingStart); - } - } - - return res; -} - -QString plainMessageText(MimeTreeParser::ObjectTreeParser &otp, bool aStripSignature) -{ - QString result = otp.plainTextContent(); - if (result.isEmpty()) { //HTML-only mails - QWebPage doc; - doc.mainFrame()->setHtml(otp.htmlContent()); - result = doc.mainFrame()->toPlainText(); - } - - if (aStripSignature) { - result = stripSignature(result); - } - - return result; -} - -QString htmlMessageText(MimeTreeParser::ObjectTreeParser &otp, bool aStripSignature, QString &headElement) -{ - QString htmlElement = otp.htmlContent(); - - if (htmlElement.isEmpty()) { //plain mails only - QString htmlReplace = otp.plainTextContent().toHtmlEscaped(); - htmlReplace = htmlReplace.replace(QStringLiteral("\n"), QStringLiteral("
")); - htmlElement = QStringLiteral("%1\n").arg(htmlReplace); - } - - //QWebPage relies on this - Q_ASSERT(QApplication::style()); - QWebPage page; - page.settings()->setAttribute(QWebSettings::JavascriptEnabled, false); - page.settings()->setAttribute(QWebSettings::JavaEnabled, false); - page.settings()->setAttribute(QWebSettings::PluginsEnabled, false); - page.settings()->setAttribute(QWebSettings::AutoLoadImages, false); - - page.currentFrame()->setHtml(htmlElement); - - //TODO to be tested/verified if this is not an issue - page.settings()->setAttribute(QWebSettings::JavascriptEnabled, true); - const QString bodyElement = page.currentFrame()->evaluateJavaScript( - QStringLiteral("document.getElementsByTagName('body')[0].innerHTML")).toString(); - - headElement = page.currentFrame()->evaluateJavaScript( - QStringLiteral("document.getElementsByTagName('head')[0].innerHTML")).toString(); - - page.settings()->setAttribute(QWebSettings::JavascriptEnabled, false); - - if (!bodyElement.isEmpty()) { - if (aStripSignature) { - //FIXME strip signature works partially for HTML mails - return stripSignature(bodyElement); - } - return bodyElement; - } - - if (aStripSignature) { - //FIXME strip signature works partially for HTML mails - return stripSignature(htmlElement); - } - return htmlElement; -} - -QString formatQuotePrefix(const QString &wildString, const QString &fromDisplayString) -{ - QString result; - - if (wildString.isEmpty()) { - return wildString; - } - - unsigned int strLength(wildString.length()); - for (uint i = 0; i < strLength;) { - QChar ch = wildString[i++]; - if (ch == QLatin1Char('%') && i < strLength) { - ch = wildString[i++]; - switch (ch.toLatin1()) { - case 'f': { // sender's initals - if (fromDisplayString.isEmpty()) { - break; - } - - uint j = 0; - const unsigned int strLength(fromDisplayString.length()); - for (; j < strLength && fromDisplayString[j] > QLatin1Char(' '); ++j) - ; - for (; j < strLength && fromDisplayString[j] <= QLatin1Char(' '); ++j) - ; - result += fromDisplayString[0]; - if (j < strLength && fromDisplayString[j] > QLatin1Char(' ')) { - result += fromDisplayString[j]; - } else if (strLength > 1) { - if (fromDisplayString[1] > QLatin1Char(' ')) { - result += fromDisplayString[1]; - } - } - } - break; - case '_': - result += QLatin1Char(' '); - break; - case '%': - result += QLatin1Char('%'); - break; - default: - result += QLatin1Char('%'); - result += ch; - break; - } - } else { - result += ch; - } - } - return result; -} - -QString quotedPlainText(const QString &selection, const QString &fromDisplayString) -{ - QString content = selection; - // Remove blank lines at the beginning: - const int firstNonWS = content.indexOf(QRegExp(QLatin1String("\\S"))); - const int lineStart = content.lastIndexOf(QLatin1Char('\n'), firstNonWS); - if (lineStart >= 0) { - content.remove(0, static_cast(lineStart)); - } - - const auto quoteString = QStringLiteral("> "); - const QString indentStr = formatQuotePrefix(quoteString, fromDisplayString); - //FIXME - // if (TemplateParserSettings::self()->smartQuote() && mWrap) { - // content = MessageCore::StringUtil::smartQuote(content, mColWrap - indentStr.length()); - // } - content.replace(QLatin1Char('\n'), QLatin1Char('\n') + indentStr); - content.prepend(indentStr); - content += QLatin1Char('\n'); - - return content; -} - -QString quotedHtmlText(const QString &selection) -{ - QString content = selection; - //TODO 1) look for all the variations of
and remove the blank lines - //2) implement vertical bar for quoted HTML mail. - //3) After vertical bar is implemented, If a user wants to edit quoted message, - // then the
tags below should open and close as when required. - - //Add blockquote tag, so that quoted message can be differentiated from normal message - content = QLatin1String("
") + content + QLatin1String("
"); - return content; -} - -void applyCharset(const KMime::Message::Ptr msg, const KMime::Message::Ptr &origMsg) -{ - // first convert the body from its current encoding to unicode representation - QTextCodec *bodyCodec = KCharsets::charsets()->codecForName(QString::fromLatin1(msg->contentType()->charset())); - if (!bodyCodec) { - bodyCodec = KCharsets::charsets()->codecForName(QStringLiteral("UTF-8")); - } - - const QString body = bodyCodec->toUnicode(msg->body()); - - // then apply the encoding of the original message - msg->contentType()->setCharset(origMsg->contentType()->charset()); - - QTextCodec *codec = KCharsets::charsets()->codecForName(QString::fromLatin1(msg->contentType()->charset())); - if (!codec) { - qCritical() << "Could not get text codec for charset" << msg->contentType()->charset(); - } else if (!codec->canEncode(body)) { // charset can't encode body, fall back to preferred - const QStringList charsets /*= preferredCharsets() */; - - QList chars; - chars.reserve(charsets.count()); - foreach (const QString &charset, charsets) { - chars << charset.toLatin1(); - } - - //FIXME - QByteArray fallbackCharset/* = selectCharset(chars, body)*/; - if (fallbackCharset.isEmpty()) { // UTF-8 as fall-through - fallbackCharset = "UTF-8"; - } - - codec = KCharsets::charsets()->codecForName(QString::fromLatin1(fallbackCharset)); - msg->setBody(codec->fromUnicode(body)); - } else { - msg->setBody(codec->fromUnicode(body)); - } -} - -enum ReplyStrategy { - ReplyList, - ReplySmart, - ReplyAll, - ReplyAuthor, - ReplyNone -}; - -KMime::Message::Ptr MailTemplates::reply(const KMime::Message::Ptr &origMsg) -{ - //FIXME - const bool alwaysPlain = true; - //FIXME - const ReplyStrategy replyStrategy = ReplySmart; - KMime::Message::Ptr msg(new KMime::Message); - //FIXME - //Personal email addresses - KMime::Types::AddrSpecList me; - KMime::Types::Mailbox::List toList; - KMime::Types::Mailbox::List replyToList; - KMime::Types::Mailbox::List mailingListAddresses; - - // const uint originalIdentity = identityUoid(origMsg); - initHeader(msg); - replyToList = origMsg->replyTo()->mailboxes(); - - msg->contentType()->setCharset("utf-8"); - - if (origMsg->headerByType("List-Post") && - origMsg->headerByType("List-Post")->asUnicodeString().contains(QStringLiteral("mailto:"), Qt::CaseInsensitive)) { - - const QString listPost = origMsg->headerByType("List-Post")->asUnicodeString(); - QRegExp rx(QStringLiteral("]+)@([^>]+)>"), Qt::CaseInsensitive); - if (rx.indexIn(listPost, 0) != -1) { // matched - KMime::Types::Mailbox mailbox; - mailbox.fromUnicodeString(rx.cap(1) + QLatin1Char('@') + rx.cap(2)); - mailingListAddresses << mailbox; - } - } - - switch (replyStrategy) { - case ReplySmart: { - if (auto hdr = origMsg->headerByType("Mail-Followup-To")) { - toList << KMime::Types::Mailbox::listFrom7BitString(hdr->as7BitString(false)); - } else if (!replyToList.isEmpty()) { - toList = replyToList; - } else if (!mailingListAddresses.isEmpty()) { - toList = (KMime::Types::Mailbox::List() << mailingListAddresses.at(0)); - } else { - // doesn't seem to be a mailing list, reply to From: address - toList = origMsg->from()->mailboxes(); - - bool listContainsMe = false; - for (const auto &m : me) { - KMime::Types::Mailbox mailbox; - mailbox.setAddress(m); - if (toList.contains(mailbox)) { - listContainsMe = true; - } - } - if (listContainsMe) { - // sender seems to be one of our own identities, so we assume that this - // is a reply to a "sent" mail where the users wants to add additional - // information for the recipient. - toList = origMsg->to()->mailboxes(); - } - } - // strip all my addresses from the list of recipients - const KMime::Types::Mailbox::List recipients = toList; - - toList = stripMyAddressesFromAddressList(recipients, me); - - // ... unless the list contains only my addresses (reply to self) - if (toList.isEmpty() && !recipients.isEmpty()) { - toList << recipients.first(); - } - } - break; - case ReplyList: { - if (auto hdr = origMsg->headerByType("Mail-Followup-To")) { - KMime::Types::Mailbox mailbox; - mailbox.from7BitString(hdr->as7BitString(false)); - toList << mailbox; - } else if (!mailingListAddresses.isEmpty()) { - toList << mailingListAddresses[ 0 ]; - } else if (!replyToList.isEmpty()) { - // assume a Reply-To header mangling mailing list - toList = replyToList; - } - - //FIXME - // strip all my addresses from the list of recipients - const KMime::Types::Mailbox::List recipients = toList; - toList = stripMyAddressesFromAddressList(recipients, me); - } - break; - case ReplyAll: { - KMime::Types::Mailbox::List recipients; - KMime::Types::Mailbox::List ccRecipients; - - // add addresses from the Reply-To header to the list of recipients - if (!replyToList.isEmpty()) { - recipients = replyToList; - - // strip all possible mailing list addresses from the list of Reply-To addresses - foreach (const KMime::Types::Mailbox &mailbox, mailingListAddresses) { - foreach (const KMime::Types::Mailbox &recipient, recipients) { - if (mailbox == recipient) { - recipients.removeAll(recipient); - } - } - } - } - - if (!mailingListAddresses.isEmpty()) { - // this is a mailing list message - if (recipients.isEmpty() && !origMsg->from()->asUnicodeString().isEmpty()) { - // The sender didn't set a Reply-to address, so we add the From - // address to the list of CC recipients. - ccRecipients += origMsg->from()->mailboxes(); - qDebug() << "Added" << origMsg->from()->asUnicodeString() << "to the list of CC recipients"; - } - - // if it is a mailing list, add the posting address - recipients.prepend(mailingListAddresses[ 0 ]); - } else { - // this is a normal message - if (recipients.isEmpty() && !origMsg->from()->asUnicodeString().isEmpty()) { - // in case of replying to a normal message only then add the From - // address to the list of recipients if there was no Reply-to address - recipients += origMsg->from()->mailboxes(); - qDebug() << "Added" << origMsg->from()->asUnicodeString() << "to the list of recipients"; - } - } - - // strip all my addresses from the list of recipients - toList = stripMyAddressesFromAddressList(recipients, me); - - // merge To header and CC header into a list of CC recipients - if (!origMsg->cc()->asUnicodeString().isEmpty() || !origMsg->to()->asUnicodeString().isEmpty()) { - KMime::Types::Mailbox::List list; - if (!origMsg->to()->asUnicodeString().isEmpty()) { - list += origMsg->to()->mailboxes(); - } - if (!origMsg->cc()->asUnicodeString().isEmpty()) { - list += origMsg->cc()->mailboxes(); - } - - foreach (const KMime::Types::Mailbox &mailbox, list) { - if (!recipients.contains(mailbox) && - !ccRecipients.contains(mailbox)) { - ccRecipients += mailbox; - qDebug() << "Added" << mailbox.prettyAddress() << "to the list of CC recipients"; - } - } - } - - if (!ccRecipients.isEmpty()) { - // strip all my addresses from the list of CC recipients - ccRecipients = stripMyAddressesFromAddressList(ccRecipients, me); - - // in case of a reply to self, toList might be empty. if that's the case - // then propagate a cc recipient to To: (if there is any). - if (toList.isEmpty() && !ccRecipients.isEmpty()) { - toList << ccRecipients.at(0); - ccRecipients.pop_front(); - } - - foreach (const KMime::Types::Mailbox &mailbox, ccRecipients) { - msg->cc()->addAddress(mailbox); - } - } - - if (toList.isEmpty() && !recipients.isEmpty()) { - // reply to self without other recipients - toList << recipients.at(0); - } - } - break; - case ReplyAuthor: { - if (!replyToList.isEmpty()) { - KMime::Types::Mailbox::List recipients = replyToList; - - // strip the mailing list post address from the list of Reply-To - // addresses since we want to reply in private - foreach (const KMime::Types::Mailbox &mailbox, mailingListAddresses) { - foreach (const KMime::Types::Mailbox &recipient, recipients) { - if (mailbox == recipient) { - recipients.removeAll(recipient); - } - } - } - - if (!recipients.isEmpty()) { - toList = recipients; - } else { - // there was only the mailing list post address in the Reply-To header, - // so use the From address instead - toList = origMsg->from()->mailboxes(); - } - } else if (!origMsg->from()->asUnicodeString().isEmpty()) { - toList = origMsg->from()->mailboxes(); - } - } - break; - case ReplyNone: - // the addressees will be set by the caller - break; - } - - foreach (const KMime::Types::Mailbox &mailbox, toList) { - msg->to()->addAddress(mailbox); - } - - const QByteArray refStr = getRefStr(origMsg); - if (!refStr.isEmpty()) { - msg->references()->fromUnicodeString(QString::fromLocal8Bit(refStr), "utf-8"); - } - - //In-Reply-To = original msg-id - msg->inReplyTo()->from7BitString(origMsg->messageID()->as7BitString(false)); - - msg->subject()->fromUnicodeString(replySubject(origMsg), "utf-8"); - - auto definedLocale = QLocale::system(); - - //TODO set empty source instead - StringHtmlWriter htmlWriter; - MimeTreeParser::NodeHelper nodeHelper; - ObjectTreeSource source(&htmlWriter); - MimeTreeParser::ObjectTreeParser otp(&source, &nodeHelper); - otp.setAllowAsync(false); - otp.parseObjectTree(origMsg.data()); - - //Add quoted body - QString plainBody; - QString htmlBody; - - //On $datetime you wrote: - const QDateTime date = origMsg->date()->dateTime(); - const auto dateTimeString = QString("%1 %2").arg(definedLocale.toString(date.date(), QLocale::LongFormat)).arg(definedLocale.toString(date.time(), QLocale::LongFormat)); - const auto onDateYouWroteLine = QString("On %1 you wrote:").arg(dateTimeString); - plainBody.append(onDateYouWroteLine); - htmlBody.append(plainToHtml(onDateYouWroteLine)); - - //Strip signature for replies - const bool stripSignature = true; - - //Quoted body - QString plainQuote = quotedPlainText(plainMessageText(otp, stripSignature), origMsg->from()->displayString()); - if (plainQuote.endsWith(QLatin1Char('\n'))) { - plainQuote.chop(1); - } - plainBody.append(plainQuote); - QString headElement; - htmlBody.append(quotedHtmlText(htmlMessageText(otp, stripSignature, headElement))); - - if (alwaysPlain) { - htmlBody.clear(); - } else { - makeValidHtml(htmlBody, headElement); - } - - addProcessedBodyToMessage(msg, plainBody, htmlBody, false); - - applyCharset(msg, origMsg); - - msg->assemble(); - - return msg; -} diff --git a/framework/domain/mailtemplates.h b/framework/domain/mailtemplates.h deleted file mode 100644 index 6519122a..00000000 --- a/framework/domain/mailtemplates.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#pragma once - -#include -#include - -namespace MailTemplates -{ - KMime::Message::Ptr reply(const KMime::Message::Ptr &message); -}; diff --git a/framework/domain/messageparser.cpp b/framework/domain/messageparser.cpp deleted file mode 100644 index ea2ecbf1..00000000 --- a/framework/domain/messageparser.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#include "messageparser.h" - -#include "modeltest.h" -#include "stringhtmlwriter.h" -#include "objecttreesource.h" - -#include "mimetreeparser/interface.h" - -#include - -#include -#include -#include -#include -#include -#include -#include - - -class MessagePartPrivate -{ -public: - QSharedPointer mPartTree; - QString mHtml; - QMap mEmbeddedPartMap; - std::shared_ptr mNodeHelper; - std::shared_ptr mParser; -}; - -MessageParser::MessageParser(QObject *parent) - : QObject(parent) - , d(std::unique_ptr(new MessagePartPrivate)) -{ - -} - -MessageParser::~MessageParser() -{ - -} - -QString MessageParser::html() const -{ - return d->mHtml; -} - -QVariant MessageParser::message() const -{ - return QVariant(); -} - -void MessageParser::setMessage(const QVariant &message) -{ - // QTime time; - // time.start(); - d->mParser = std::shared_ptr(new Parser(message.toByteArray())); - - const auto mailData = KMime::CRLFtoLF(message.toByteArray()); - KMime::Message::Ptr msg(new KMime::Message); - msg->setContent(mailData); - msg->parse(); - // qWarning() << "parsed: " << time.elapsed(); - - // render the mail - StringHtmlWriter htmlWriter; - //temporary files only have the lifetime of the nodehelper, so we keep it around until the mail changes. - d->mNodeHelper = std::make_shared(); - ObjectTreeSource source(&htmlWriter); - MimeTreeParser::ObjectTreeParser otp(&source, d->mNodeHelper.get()); - - otp.parseObjectTree(msg.data()); - d->mPartTree = otp.parsedPart().dynamicCast(); - - d->mEmbeddedPartMap = htmlWriter.embeddedParts(); - d->mHtml = htmlWriter.html(); - emit htmlChanged(); -} - -QAbstractItemModel *MessageParser::partTree() const -{ - return new PartModel(d->mPartTree, d->mParser); -} - -QAbstractItemModel *MessageParser::newTree() const -{ - const auto model = new NewModel(d->mParser); - // new ModelTest(model, model); - return model; -} - -QAbstractItemModel *MessageParser::attachments() const -{ - const auto model = new AttachmentModel(d->mParser); - // new ModelTest(model, model); - return model; -} diff --git a/framework/domain/messageparser.h b/framework/domain/messageparser.h deleted file mode 100644 index aeeed93c..00000000 --- a/framework/domain/messageparser.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#pragma once - -#include -#include -#include - -#include -#include - -#include -#include - -class QAbstractItemModel; - -class Parser; -class Part; -class Encryption; -class Signature; -typedef std::shared_ptr PartPtr; -class Content; -typedef std::shared_ptr ContentPtr; -class MessagePartPrivate; - -class NewModelPrivate; -class AttachmentModelPrivate; - -class MessageParser : public QObject -{ - Q_OBJECT - Q_PROPERTY (QVariant message READ message WRITE setMessage) - Q_PROPERTY (QString html READ html NOTIFY htmlChanged) - Q_PROPERTY (QAbstractItemModel* partTree READ partTree NOTIFY htmlChanged) - Q_PROPERTY (QAbstractItemModel* newTree READ newTree NOTIFY htmlChanged) - Q_PROPERTY (QAbstractItemModel* attachments READ attachments NOTIFY htmlChanged) - -public: - explicit MessageParser(QObject *parent = Q_NULLPTR); - ~MessageParser(); - - QString html() const; - - QVariant message() const; - void setMessage(const QVariant &to); - QAbstractItemModel *partTree() const; - QAbstractItemModel *newTree() const; - QAbstractItemModel *attachments() const; - -signals: - void htmlChanged(); - -private: - std::unique_ptr d; -}; - -class PartModel : public QAbstractItemModel { - Q_OBJECT -public: - PartModel(QSharedPointer partTree, std::shared_ptr parser); - -public: - enum Roles { - Text = Qt::UserRole + 1, - IsHtml, - IsEncrypted, - IsAttachment, - HasContent, - Type, - IsHidden - }; - - QHash roleNames() const Q_DECL_OVERRIDE; - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; - QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE; - int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; - int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; - -private: - QSharedPointer mPartTree; - QMap mEmbeddedPartMap; - std::shared_ptr mParser; -}; - - -class NewModel : public QAbstractItemModel { - Q_OBJECT -public: - NewModel(std::shared_ptr parser); - ~NewModel(); - -public: - enum Roles { - TypeRole = Qt::UserRole + 1, - ContentsRole, - ContentRole, - IsEmbededRole, - SecurityLevelRole, - EncryptionErrorType, - EncryptionErrorString - }; - - QHash roleNames() const Q_DECL_OVERRIDE; - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; - QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE; - int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; - int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; - -private: - std::unique_ptr d; -}; - -class AttachmentModel : public QAbstractItemModel { - Q_OBJECT -public: - AttachmentModel(std::shared_ptr parser); - ~AttachmentModel(); - -public: - enum Roles { - TypeRole = Qt::UserRole + 1, - IconRole, - NameRole, - SizeRole, - IsEncryptedRole, - IsSignedRole - }; - - QHash roleNames() const Q_DECL_OVERRIDE; - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; - QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE; - int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; - int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; - -private: - std::unique_ptr d; -}; diff --git a/framework/domain/messageparser_new.cpp b/framework/domain/messageparser_new.cpp deleted file mode 100644 index cb399523..00000000 --- a/framework/domain/messageparser_new.cpp +++ /dev/null @@ -1,486 +0,0 @@ -/* - Copyright (c) 2016 Sandro Knauß - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#include "messageparser.h" -#include "mimetreeparser/interface.h" - -#include - -Q_DECLARE_METATYPE(Part *) -Q_DECLARE_METATYPE(Content *) -Q_DECLARE_METATYPE(Signature *) -Q_DECLARE_METATYPE(Encryption *) - -class Entry; - -class NewModelPrivate -{ -public: - NewModelPrivate(NewModel *q_ptr, const std::shared_ptr &parser); - ~NewModelPrivate(); - - void createTree(); - - QSharedPointer getVar(const std::shared_ptr &sig); - QSharedPointer getVar(const std::shared_ptr &enc); - QSharedPointer getVar(const std::shared_ptr &part); - QSharedPointer getVar(Part *part); - QSharedPointer getVar(const std::shared_ptr &content); - QSharedPointer getVar(Content *content); - - int getPos(Signature *sig); - int getPos(Encryption *enc); - int getPos(Part *part); - int getPos(Content *content); - - NewModel *q; - QVector mParts; - std::unique_ptr mRoot; - - std::shared_ptr mParser; -private: - QMap, QSharedPointer> mSignatureMap; - QMap, QSharedPointer> mEncryptionMap; - QMap> mPartMap; - QMap> mCMap; -}; - -class Entry -{ -public: - Entry(NewModelPrivate *model) - : mParent(nullptr) - , mNewModelPrivate(model) - { - } - - ~Entry() - { - foreach(auto child, mChildren) { - delete child; - } - mChildren.clear(); - } - - void addChild(Entry *entry) - { - mChildren.append(entry); - entry->mParent = this; - } - - Entry *addSignatures(QVector signatures) - { - auto ret = this; - foreach(const auto &sig, signatures) { - auto entry = new Entry(mNewModelPrivate); - entry->mData = mNewModelPrivate->getVar(sig); - ret->addChild(entry); - ret = entry; - } - return ret; - } - - Entry *addEncryptions(QVector encryptions) - { - auto ret = this; - foreach(const auto &enc, encryptions) { - auto entry = new Entry(mNewModelPrivate); - entry->mData = mNewModelPrivate->getVar(enc); - ret->addChild(entry); - ret = entry; - } - return ret; - } - - Entry *addPart(Part *part) - { - auto entry = new Entry(mNewModelPrivate); - entry->mData = mNewModelPrivate->getVar(part); - addChild(entry); - - foreach(const auto &content, part->content()) { - auto _entry = entry; - _entry = _entry->addEncryptions(content->encryptions().mid(part->encryptions().size())); - _entry = _entry->addSignatures(content->signatures().mid(part->signatures().size())); - auto c = new Entry(mNewModelPrivate); - c->mData = mNewModelPrivate->getVar(content); - _entry->addChild(c); - } -// foreach(const auto &content, part->availableContents()) { -// foreach(const auto &contentPart, part->content(content)) { -// auto _entry = entry; -// _entry = _entry->addEncryptions(contentPart->encryptions().mid(part->encryptions().size())); -// _entry = _entry->addSignatures(contentPart->signatures().mid(part->signatures().size())); -// auto c = new Entry(mNewModelPrivate); -// c->mData = mNewModelPrivate->getVar(contentPart); -// _entry->addChild(c); -// } -// } - foreach(const auto &sp, part->subParts()) { - auto _entry = entry; - _entry = _entry->addEncryptions(sp->encryptions().mid(part->encryptions().size())); - _entry = _entry->addSignatures(sp->signatures().mid(part->signatures().size())); - _entry->addPart(sp.get()); - } - return entry; - } - - int pos() - { - if(!mParent) { - return -1; - } - int i=0; - foreach(const auto &child, mParent->mChildren) { - if (child == this) { - return i; - } - i++; - } - return -1; - } - - QSharedPointer mData; - - Entry *mParent; - QVector mChildren; - NewModelPrivate *mNewModelPrivate; -}; - - -NewModelPrivate::NewModelPrivate(NewModel *q_ptr, const std::shared_ptr &parser) - : q(q_ptr) - , mRoot(std::unique_ptr(new Entry(this))) - , mParser(parser) -{ - mParts = mParser->collectContentParts(); - createTree(); -} - -NewModelPrivate::~NewModelPrivate() -{ -} - -void NewModelPrivate::createTree() -{ - auto root = mRoot.get(); - auto parent = root; - Part *pPart = nullptr; - QVector signatures; - QVector encryptions; - foreach(const auto part, mParts) { - auto _parent = parent; - if (pPart != part->parent()) { - auto _parent = root; - _parent = _parent->addEncryptions(part->parent()->encryptions()); - _parent = _parent->addSignatures(part->parent()->signatures()); - signatures = part->parent()->signatures(); - encryptions = part->parent()->encryptions(); - parent = _parent; - pPart = part->parent(); - } - _parent = _parent->addEncryptions(part->encryptions().mid(encryptions.size())); - _parent = _parent->addSignatures(part->signatures().mid(signatures.size())); - _parent->addPart(part.get()); - } -} - -QSharedPointer NewModelPrivate::getVar(const std::shared_ptr &sig) -{ - if (!mSignatureMap.contains(sig)) { - auto var = new QVariant(); - var->setValue(sig.get()); - mSignatureMap.insert(sig, QSharedPointer(var)); - } - return mSignatureMap.value(sig); -} - -QSharedPointer NewModelPrivate::getVar(const std::shared_ptr &enc) -{ - if (!mEncryptionMap.contains(enc)) { - auto var = new QVariant(); - var->setValue(enc.get()); - mEncryptionMap.insert(enc, QSharedPointer(var)); - } - return mEncryptionMap.value(enc); -} - -QSharedPointer NewModelPrivate::getVar(const std::shared_ptr &part) -{ - return getVar(part.get()); -} - -QSharedPointer NewModelPrivate::getVar(Part *part) -{ - if (!mPartMap.contains(part)) { - auto var = new QVariant(); - var->setValue(part); - mPartMap.insert(part, QSharedPointer(var)); - } - return mPartMap.value(part); -} - -QSharedPointer NewModelPrivate::getVar(const std::shared_ptr &content) -{ - return getVar(content.get()); -} - -QSharedPointer NewModelPrivate::getVar(Content *content) -{ - if (!mCMap.contains(content)) { - auto var = new QVariant(); - var->setValue(content); - mCMap.insert(content, QSharedPointer(var)); - } - return mCMap.value(content); -} - -int NewModelPrivate::getPos(Signature *signature) -{ - const auto first = mParts.first(); - int i = 0; - foreach(const auto &sig, first->signatures()) { - if (sig.get() == signature) { - break; - } - i++; - } - return i; -} - -int NewModelPrivate::getPos(Encryption *encryption) -{ - const auto first = mParts.first(); - int i = 0; - foreach(const auto &enc, first->encryptions()) { - if (enc.get() == encryption) { - break; - } - i++; - } - return i; -} - -int NewModelPrivate::getPos(Part *part) -{ - int i = 0; - foreach(const auto &p, mParts) { - if (p.get() == part) { - break; - } - i++; - } - return i; -} - -int NewModelPrivate::getPos(Content *content) -{ - int i = 0; - foreach(const auto &c, content->parent()->content()) { - if (c.get() == content) { - break; - } - i++; - } - return i; -} - -NewModel::NewModel(std::shared_ptr parser) - : d(std::unique_ptr(new NewModelPrivate(this, parser))) -{ -} - -NewModel::~NewModel() -{ -} - -QHash NewModel::roleNames() const -{ - QHash roles; - roles[TypeRole] = "type"; - roles[ContentRole] = "content"; - roles[IsEmbededRole] = "embeded"; - roles[SecurityLevelRole] = "securityLevel"; - roles[EncryptionErrorType] = "errorType"; - roles[EncryptionErrorString] = "errorString"; - return roles; -} - -QModelIndex NewModel::index(int row, int column, const QModelIndex &parent) const -{ - if (row < 0 || column != 0) { - return QModelIndex(); - } - Entry *entry = d->mRoot.get(); - if (parent.isValid()) { - entry = static_cast(parent.internalPointer()); - } - - if (row < entry->mChildren.size()) { - return createIndex(row, column, entry->mChildren.at(row)); - } - return QModelIndex(); -} - -QVariant NewModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) { - switch (role) { - case Qt::DisplayRole: - return QString("root"); - case IsEmbededRole: - return false; - } - return QVariant(); - } - - if (index.internalPointer()) { - const auto entry = static_cast(index.internalPointer()); - const auto _data = entry->mData; - if (entry == d->mRoot.get()|| !_data) { - switch (role) { - case Qt::DisplayRole: - return QString("root"); - case IsEmbededRole: - return false; - } - return QVariant(); - } - if (_data->userType() == qMetaTypeId()) { - const auto signature = _data->value(); - int i = d->getPos(signature); - switch(role) { - case Qt::DisplayRole: - return QStringLiteral("Signature%1").arg(i); - case TypeRole: - return QStringLiteral("Signature"); - case SecurityLevelRole: - return QStringLiteral("RED"); - case IsEmbededRole: - return data(index.parent(), IsEmbededRole); - } - } else if (_data->userType() == qMetaTypeId()) { - const auto encryption = _data->value(); - int i = d->getPos(encryption); - switch(role) { - case Qt::DisplayRole: - return QStringLiteral("Encryption%1").arg(i); - case TypeRole: - return QStringLiteral("Encryption"); - case SecurityLevelRole: - return QStringLiteral("GREEN"); - case IsEmbededRole: - return data(index.parent(), IsEmbededRole); - case EncryptionErrorType: - { - switch(encryption->errorType()) { - case Encryption::NoError: - return QString(); - case Encryption::PassphraseError: - return QStringLiteral("PassphraseError"); - case Encryption::KeyMissing: - return QStringLiteral("KeyMissing"); - default: - return QStringLiteral("UnknownError"); - } - } - case EncryptionErrorString: - return encryption->errorString(); - } - } else if (_data->userType() == qMetaTypeId()) { - const auto part = _data->value(); - switch (role) { - case Qt::DisplayRole: - case TypeRole: - return QString::fromLatin1(part->type()); - case IsEmbededRole: - return data(index.parent(), IsEmbededRole); - } - } else if (_data->userType() == qMetaTypeId()) { - const auto content = _data->value(); - int i = d->getPos(content); - switch(role) { - case Qt::DisplayRole: - return QStringLiteral("Content%1").arg(i); - case TypeRole: - return QString::fromLatin1(content->type()); - case IsEmbededRole: - return data(index.parent(), IsEmbededRole); - case ContentRole: { - auto text = content->encodedContent(); - if (data(index, TypeRole).toString() == "HtmlContent") { - const auto rx = QRegExp("(src)\\s*=\\s*(\"|')(cid:[^\"']+)\\2"); - int pos = 0; - while ((pos = rx.indexIn(text, pos)) != -1) { - const auto link = QUrl(rx.cap(3).toUtf8()); - pos += rx.matchedLength(); - const auto repl = d->mParser->getPart(link); - if (!repl) { - continue; - } - const auto content = repl->content(); - if(content.size() < 1) { - continue; - } - const auto mailMime = content.first()->mailMime(); - const auto mimetype = mailMime->mimetype().name(); - if (mimetype.startsWith("image/")) { - const auto data = content.first()->content(); - text.replace(rx.cap(0), QString("src=\"data:%1;base64,%2\"").arg(mimetype, QString::fromLatin1(data.toBase64()))); - } - } - } - return text; - } - } - } - } - return QVariant(); -} - -QModelIndex NewModel::parent(const QModelIndex &index) const -{ - if (!index.internalPointer()) { - return QModelIndex(); - } - const auto entry = static_cast(index.internalPointer()); - if (entry->mParent && entry->mParent != d->mRoot.get()) { - return createIndex(entry->pos(), 0, entry->mParent); - } - return QModelIndex(); -} - -int NewModel::rowCount(const QModelIndex &parent) const -{ - if (!parent.isValid()) { - return d->mRoot->mChildren.size(); - } else { - if (!parent.internalPointer()) { - return 0; - } - const auto entry = static_cast(parent.internalPointer()); - return entry->mChildren.size(); - } - return 0; -} - -int NewModel::columnCount(const QModelIndex &parent) const -{ - return 1; -} diff --git a/framework/domain/messageparser_old.cpp b/framework/domain/messageparser_old.cpp deleted file mode 100644 index a4247d8c..00000000 --- a/framework/domain/messageparser_old.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#include "messageparser.h" -#include "mimetreeparser/interface.h" - -PartModel::PartModel(QSharedPointer partTree, std::shared_ptr parser) - : mPartTree(partTree) - , mParser(parser) -{ -} - -QHash PartModel::roleNames() const -{ - QHash roles; - roles[Text] = "text"; - roles[IsHtml] = "isHtml"; - roles[IsHidden] = "isHidden"; - roles[IsEncrypted] = "isEncrypted"; - roles[IsAttachment] = "isAttachment"; - roles[HasContent] = "hasContent"; - roles[Type] = "type"; - roles[IsHidden] = "isHidden"; - return roles; -} - -QModelIndex PartModel::index(int row, int column, const QModelIndex &parent) const -{ - // qDebug() << "index " << parent << row << column << mPartTree->subParts().size(); - if (!parent.isValid()) { - if (row < mPartTree->subParts().size()) { - auto part = mPartTree->subParts().at(row); - return createIndex(row, column, part.data()); - } - } else { - auto part = static_cast(parent.internalPointer()); - auto subPart = part->subParts().at(row); - return createIndex(row, column, subPart.data()); - } - return QModelIndex(); -} - -QVariant PartModel::data(const QModelIndex &index, int role) const -{ - // qDebug() << "Getting data for index"; - if (index.isValid()) { - auto part = static_cast(index.internalPointer()); - switch (role) { - case Text: { - // qDebug() << "Getting text: " << part->property("text").toString(); - // FIXME: we should have a list per part, and not one for all parts. - auto text = part->property("htmlContent").toString(); - const auto rx = QRegExp("(src)\\s*=\\s*(\"|')(cid:[^\"']+)\\2"); - int pos = 0; - while ((pos = rx.indexIn(text, pos)) != -1) { - const auto link = QUrl(rx.cap(3).toUtf8()); - pos += rx.matchedLength(); - const auto repl = mParser->getPart(link); - if (!repl) { - continue; - } - const auto content = repl->content(); - if(content.size() < 1) { - continue; - } - const auto mailMime = content.first()->mailMime(); - const auto mimetype = mailMime->mimetype().name(); - if (mimetype.startsWith("image/")) { - const auto data = content.first()->content(); - text.replace(rx.cap(0), QString("src=\"data:%1;base64,%2\"").arg(mimetype, QString::fromLatin1(data.toBase64()))); - } - } - return text; - } - case IsAttachment: - return part->property("attachment").toBool(); - case IsEncrypted: - return part->property("isEncrypted").toBool(); - case IsHtml: - return part->property("isHtml").toBool(); - case HasContent: - return !part->property("htmlContent").toString().isEmpty(); - case Type: - return part->metaObject()->className(); - case IsHidden: - return false; - //return part->property("isHidden").toBool(); - - } - } - return QVariant(); -} - -QModelIndex PartModel::parent(const QModelIndex &index) const -{ - // qDebug() << "parent " << index; - if (index.isValid()) { - auto part = static_cast(index.internalPointer()); - auto parentPart = static_cast(part->parentPart()); - auto row = 0;//get the parents parent to find the index - if (!parentPart) { - parentPart = mPartTree.data(); - } - int i = 0; - for (const auto &p : parentPart->subParts()) { - if (p.data() == part) { - row = i; - break; - } - i++; - } - return createIndex(row, index.column(), parentPart); - } - return QModelIndex(); -} - -int PartModel::rowCount(const QModelIndex &parent) const -{ - // qDebug() << "Row count " << parent; - if (!parent.isValid()) { - // qDebug() << "Row count " << mPartTree->subParts().size(); - return mPartTree->subParts().size(); - } else { - auto part = static_cast(parent.internalPointer()); - if (part) { - return part->subParts().size(); - } - } - return 0; -} - -int PartModel::columnCount(const QModelIndex &parent) const -{ - // qDebug() << "Column count " << parent; - return 1; -} - diff --git a/framework/domain/mimetreeparser/CMakeLists.txt b/framework/domain/mimetreeparser/CMakeLists.txt deleted file mode 100644 index 64da2656..00000000 --- a/framework/domain/mimetreeparser/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -set(mimetreeparser_SRCS - interface.cpp - objecttreesource.cpp - stringhtmlwriter.cpp -) - -add_library(mimetreeparser SHARED ${mimetreeparser_SRCS}) - -qt5_use_modules(mimetreeparser Core Gui) -target_link_libraries(mimetreeparser KF5::Mime KF5::MimeTreeParser) - -install(TARGETS mimetreeparser - DESTINATION ${LIB_INSTALL_DIR}) - -add_subdirectory(tests) diff --git a/framework/domain/mimetreeparser/interface.cpp b/framework/domain/mimetreeparser/interface.cpp deleted file mode 100644 index 663a2013..00000000 --- a/framework/domain/mimetreeparser/interface.cpp +++ /dev/null @@ -1,1179 +0,0 @@ -/* - Copyright (c) 2016 Sandro Knauß - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#include "interface.h" -#include "interface_p.h" - -#include "stringhtmlwriter.h" -#include "objecttreesource.h" - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -class MailMimePrivate -{ -public: - MailMimePrivate(MailMime *p); - - MailMime *q; - KMime::Content *mNode; - std::shared_ptr parent; -}; - -MailMimePrivate::MailMimePrivate(MailMime* p) - : q(p) - , mNode(nullptr) - , parent(nullptr) -{ -} - - -MailMime::MailMime() - : d(std::unique_ptr(new MailMimePrivate(this))) -{ -} - -QByteArray MailMime::cid() const -{ - if (!d->mNode || !d->mNode->contentID()) { - return QByteArray(); - } - return d->mNode->contentID()->identifier(); -} - -QByteArray MailMime::charset() const -{ - if(!d->mNode || !d->mNode->contentType(false)) { - return QByteArray(); - } - if (d->mNode->contentType(false)) { - return d->mNode->contentType(false)->charset(); - } - return d->mNode->defaultCharset(); -} - -bool MailMime::isFirstTextPart() const -{ - if (!d->mNode || !d->mNode->topLevel()) { - return false; - } - return (d->mNode->topLevel()->textContent() == d->mNode); -} - -bool MailMime::isFirstPart() const -{ - if (!d->mNode || !d->mNode->parent()) { - return false; - } - return (d->mNode->parent()->contents().first() == d->mNode); -} - -bool MailMime::isTopLevelPart() const -{ - if (!d->mNode) { - return false; - } - return (d->mNode->topLevel() == d->mNode); -} - -MailMime::Disposition MailMime::disposition() const -{ - if (!d->mNode) { - return Invalid; - } - const auto cd = d->mNode->contentDisposition(false); - if (!cd) { - return Invalid; - } - switch (cd->disposition()){ - case KMime::Headers::CDinline: - return Inline; - case KMime::Headers::CDattachment: - return Attachment; - default: - return Invalid; - } -} - -QString MailMime::filename() const -{ - if (!d->mNode) { - return QString(); - } - const auto cd = d->mNode->contentDisposition(false); - if (!cd) { - return QString(); - } - return cd->filename(); -} - -QMimeType MailMime::mimetype() const -{ - if (!d->mNode) { - return QMimeType(); - } - - const auto ct = d->mNode->contentType(false); - if (!ct) { - return QMimeType(); - } - - QMimeDatabase mimeDb; - return mimeDb.mimeTypeForName(ct->mimeType()); -} - -MailMime::Ptr MailMime::parent() const -{ - if (!d->parent) { - d->parent = std::shared_ptr(new MailMime()); - d->parent->d->mNode = d->mNode->parent(); - } - return d->parent; -} - -QByteArray MailMime::decodedContent() const -{ - if (!d->mNode) { - return QByteArray(); - } - return d->mNode->decodedContent(); -} - -class KeyPrivate -{ -public: - Key *q; - GpgME::Key mKey; - QByteArray mKeyID; -}; - -Key::Key() - :d(std::unique_ptr(new KeyPrivate)) -{ - d->q = this; -} - - -Key::Key(KeyPrivate *d_ptr) - :d(std::unique_ptr(d_ptr)) -{ - d->q = this; -} - -Key::~Key() -{ - -} - -QString Key::keyid() const -{ - if (!d->mKey.isNull()) { - return d->mKey.keyID(); - } - - return d->mKeyID; -} - -QString Key::name() const -{ - //FIXME: is this the correct way to get the primary UID? - if (!d->mKey.isNull()) { - return d->mKey.userID(0).name(); - } - - return QString(); -} - -QString Key::email() const -{ - if (!d->mKey.isNull()) { - return d->mKey.userID(0).email(); - } - return QString(); -} - -QString Key::comment() const -{ - if (!d->mKey.isNull()) { - return d->mKey.userID(0).comment(); - } - return QString(); -} - -class SignaturePrivate -{ -public: - Signature *q; - GpgME::Signature mSignature; - Key::Ptr mKey; -}; - -Signature::Signature() - :d(std::unique_ptr(new SignaturePrivate)) -{ - d->q = this; -} - - -Signature::Signature(SignaturePrivate *d_ptr) - :d(std::unique_ptr(d_ptr)) -{ - d->q = this; -} - -Signature::~Signature() -{ - -} - -QDateTime Signature::creationDateTime() const -{ - QDateTime dt; - dt.setTime_t(d->mSignature.creationTime()); - return dt; -} - -QDateTime Signature::expirationDateTime() const -{ - QDateTime dt; - dt.setTime_t(d->mSignature.expirationTime()); - return dt; -} - -bool Signature::neverExpires() const -{ - return d->mSignature.neverExpires(); -} - -Key::Ptr Signature::key() const -{ - return d->mKey; -} - -class EncryptionPrivate -{ -public: - Encryption *q; - std::vector mRecipients; - Encryption::ErrorType mErrorType; - QString mErrorString; -}; - -Encryption::Encryption(EncryptionPrivate *d_ptr) - :d(std::unique_ptr(d_ptr)) -{ - d->q = this; -} - -Encryption::Encryption() - :d(std::unique_ptr(new EncryptionPrivate)) -{ - d->q = this; - d->mErrorType = Encryption::NoError; -} - -Encryption::~Encryption() -{ - -} - -std::vector Encryption::recipients() const -{ - return d->mRecipients; -} - -QString Encryption::errorString() -{ - return d->mErrorString; -} - -Encryption::ErrorType Encryption::errorType() -{ - return d->mErrorType; -} - - -class PartPrivate -{ -public: - PartPrivate(Part *part); - void appendSubPart(Part::Ptr subpart); - - QVector subParts(); - - Part *parent() const; - - const MailMime::Ptr &mailMime() const; - void createMailMime(const MimeTreeParser::MimeMessagePart::Ptr &part); - void createMailMime(const MimeTreeParser::TextMessagePart::Ptr &part); - void createMailMime(const MimeTreeParser::AlternativeMessagePart::Ptr &part); - void createMailMime(const MimeTreeParser::HtmlMessagePart::Ptr &part); - void createMailMime(const MimeTreeParser::EncryptedMessagePart::Ptr &part); - - static Encryption::Ptr createEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr& part); - void appendEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr &part); - static QVector createSignature(const MimeTreeParser::SignedMessagePart::Ptr& part); - void appendSignature(const MimeTreeParser::SignedMessagePart::Ptr &part); - - void setSignatures(const QVector &sigs); - void setEncryptions(const QVector &encs); - - const QVector &encryptions() const; - const QVector &signatures() const; -private: - Part *q; - Part *mParent; - QVector mSubParts; - QVector mEncryptions; - QVector mSignatures; - MailMime::Ptr mMailMime; -}; - -PartPrivate::PartPrivate(Part* part) - : q(part) - , mParent(Q_NULLPTR) -{ - -} - -void PartPrivate::createMailMime(const MimeTreeParser::HtmlMessagePart::Ptr& part) -{ - mMailMime = MailMime::Ptr(new MailMime); - mMailMime->d->mNode = part->mNode; -} - -void PartPrivate::createMailMime(const MimeTreeParser::AlternativeMessagePart::Ptr& part) -{ - mMailMime = MailMime::Ptr(new MailMime); - mMailMime->d->mNode = part->mNode; -} - -void PartPrivate::createMailMime(const MimeTreeParser::TextMessagePart::Ptr& part) -{ - mMailMime = MailMime::Ptr(new MailMime); - mMailMime->d->mNode = part->mNode; -} - -void PartPrivate::createMailMime(const MimeTreeParser::MimeMessagePart::Ptr& part) -{ - mMailMime = MailMime::Ptr(new MailMime); - mMailMime->d->mNode = part->mNode; -} - -void PartPrivate::createMailMime(const MimeTreeParser::EncryptedMessagePart::Ptr& part) -{ - mMailMime = MailMime::Ptr(new MailMime); - mMailMime->d->mNode = part->mNode; -} - -void PartPrivate::appendSubPart(Part::Ptr subpart) -{ - subpart->d->mParent = q; - mSubParts.append(subpart); -} - -Encryption::Ptr PartPrivate::createEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr& part) -{ - QGpgME::KeyListJob *job = part->mCryptoProto->keyListJob(false); // local, no sigs - if (!job) { - qWarning() << "The Crypto backend does not support listing keys. "; - return Encryption::Ptr(); - } - - auto encpriv = new EncryptionPrivate(); - if (part->passphraseError()) { - encpriv->mErrorType = Encryption::PassphraseError; - encpriv->mErrorString = part->mMetaData.errorText; - } else if (part->isEncrypted() && !part->isDecryptable()) { - encpriv->mErrorType = Encryption::KeyMissing; - encpriv->mErrorString = part->mMetaData.errorText; - } else if (!part->isEncrypted() && !part->isDecryptable()) { - encpriv->mErrorType = Encryption::UnknownError; - encpriv->mErrorString = part->mMetaData.errorText; - } else { - encpriv->mErrorType = Encryption::NoError; - } - - foreach(const auto &recipient, part->mDecryptRecipients) { - std::vector found_keys; - const auto &keyid = recipient.keyID(); - GpgME::KeyListResult res = job->exec(QStringList(QLatin1String(keyid)), false, found_keys); - if (res.error()) { - qWarning() << "Error while searching key for Fingerprint: " << keyid; - continue; - } - if (found_keys.size() > 1) { - // Should not Happen - qWarning() << "Oops: Found more then one Key for Fingerprint: " << keyid; - } - if (found_keys.size() != 1) { - // Should not Happen at this point - qWarning() << "Oops: Found no Key for Fingerprint: " << keyid; - auto keypriv = new KeyPrivate; - keypriv->mKeyID = keyid; - encpriv->mRecipients.push_back(Key::Ptr(new Key(keypriv))); - } else { - auto key = found_keys[0]; - auto keypriv = new KeyPrivate; - keypriv->mKey = key; - encpriv->mRecipients.push_back(Key::Ptr(new Key(keypriv))); - } - } - return Encryption::Ptr(new Encryption(encpriv)); -} - -void PartPrivate::appendEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr& part) -{ - mEncryptions.append(createEncryption(part)); -} - -void PartPrivate::setEncryptions(const QVector< Encryption::Ptr >& encs) -{ - mEncryptions = encs; -} - -QVector PartPrivate::createSignature(const MimeTreeParser::SignedMessagePart::Ptr& part) -{ - QVector sigs; - QGpgME::KeyListJob *job = part->mCryptoProto->keyListJob(false); // local, no sigs - if (!job) { - qWarning() << "The Crypto backend does not support listing keys. "; - return sigs; - } - - foreach(const auto &sig, part->mSignatures) { - auto sigpriv = new SignaturePrivate(); - sigpriv->mSignature = sig; - auto signature = std::make_shared(sigpriv); - sigs.append(signature); - - std::vector found_keys; - const auto &keyid = sig.fingerprint(); - GpgME::KeyListResult res = job->exec(QStringList(QLatin1String(keyid)), false, found_keys); - if (res.error()) { - qWarning() << "Error while searching key for Fingerprint: " << keyid; - continue; - } - if (found_keys.size() > 1) { - // Should not Happen - qWarning() << "Oops: Found more then one Key for Fingerprint: " << keyid; - continue; - } - if (found_keys.size() != 1) { - // Should not Happen at this point - qWarning() << "Oops: Found no Key for Fingerprint: " << keyid; - continue; - } else { - auto key = found_keys[0]; - auto keypriv = new KeyPrivate; - keypriv->mKey = key; - sigpriv->mKey = Key::Ptr(new Key(keypriv)); - } - } - return sigs; -} - -void PartPrivate::appendSignature(const MimeTreeParser::SignedMessagePart::Ptr& part) -{ - mSignatures.append(createSignature(part)); -} - - -void PartPrivate::setSignatures(const QVector< Signature::Ptr >& sigs) -{ - mSignatures = sigs; -} - -Part *PartPrivate::parent() const -{ - return mParent; -} - -QVector< Part::Ptr > PartPrivate::subParts() -{ - return mSubParts; -} - -const MailMime::Ptr& PartPrivate::mailMime() const -{ - return mMailMime; -} - -const QVector< Encryption::Ptr >& PartPrivate::encryptions() const -{ - return mEncryptions; -} - -const QVector< Signature::Ptr >& PartPrivate::signatures() const -{ - return mSignatures; -} - -Part::Part() - : d(std::unique_ptr(new PartPrivate(this))) -{ - -} - -bool Part::hasSubParts() const -{ - return !subParts().isEmpty(); -} - -QVector Part::subParts() const -{ - return d->subParts(); -} - -QByteArray Part::type() const -{ - return "Part"; -} - -QVector Part::availableContents() const -{ - return QVector(); -} - -QVector Part::content() const -{ - return content(availableContents().first()); -} - -QVector Part::content(const QByteArray& ct) const -{ - return QVector(); -} - -QVector Part::encryptions() const -{ - auto ret = d->encryptions(); - auto parent = d->parent(); - if (parent) { - ret.append(parent->encryptions()); - } - return ret; -} - -QVector Part::signatures() const -{ - auto ret = d->signatures(); - auto parent = d->parent(); - if (parent) { - ret.append(parent->signatures()); - } - return ret; -} - -MailMime::Ptr Part::mailMime() const -{ - return d->mailMime(); -} - -Part *Part::parent() const -{ - return d->parent(); -} - -class ContentPrivate -{ -public: - QByteArray mContent; - QByteArray mCodec; - Part *mParent; - Content *q; - MailMime::Ptr mMailMime; - QVector mEncryptions; - QVector mSignatures; - void appendSignature(const MimeTreeParser::SignedMessagePart::Ptr &sig); - void appendEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr &enc); -}; - -void ContentPrivate::appendEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr& enc) -{ - mEncryptions.append(PartPrivate::createEncryption(enc)); -} - -void ContentPrivate::appendSignature(const MimeTreeParser::SignedMessagePart::Ptr& sig) -{ - mSignatures.append(PartPrivate::createSignature(sig)); -} - - -Content::Content(const QByteArray& content, Part *parent) - : d(std::unique_ptr(new ContentPrivate)) -{ - d->q = this; - d->mContent = content; - d->mCodec = "utf-8"; - d->mParent = parent; -} - -Content::Content(ContentPrivate* d_ptr) - : d(std::unique_ptr(d_ptr)) -{ - d->q = this; -} - -Content::~Content() -{ -} - -QVector Content::encryptions() const -{ - auto ret = d->mEncryptions; - if (d->mParent) { - ret.append(d->mParent->encryptions()); - } - return ret; -} - -QVector Content::signatures() const -{ - auto ret = d->mSignatures; - if (d->mParent) { - ret.append(d->mParent->signatures()); - } - return ret; -} - -QByteArray Content::content() const -{ - return d->mContent; -} - -QByteArray Content::charset() const -{ - return d->mCodec; -} - -QString Content::encodedContent() const -{ - return QString::fromUtf8(content()); - // TODO: should set "raw" content not the already utf8 encoded content - // return encodedContent(charset()); -} - -QString Content::encodedContent(const QByteArray &charset) const -{ - QTextCodec *codec = QTextCodec::codecForName(charset); - return codec->toUnicode(content()); -} - -QByteArray Content::type() const -{ - return "Content"; -} - -MailMime::Ptr Content::mailMime() const -{ - if (d->mMailMime) { - return d->mMailMime; - } else { - return d->mParent->mailMime(); - } -} - -Part *Content::parent() const -{ - return d->mParent; -} - -HtmlContent::HtmlContent(const QByteArray& content, Part* parent) - : Content(content, parent) -{ - -} - -QByteArray HtmlContent::type() const -{ - return "HtmlContent"; -} - -PlainTextContent::PlainTextContent(const QByteArray& content, Part* parent) - : Content(content, parent) -{ - -} - -PlainTextContent::PlainTextContent(ContentPrivate* d_ptr) - : Content(d_ptr) -{ - -} - -HtmlContent::HtmlContent(ContentPrivate* d_ptr) - : Content(d_ptr) -{ - -} - - -QByteArray PlainTextContent::type() const -{ - return "PlainTextContent"; -} - -class AlternativePartPrivate -{ -public: - void fillFrom(MimeTreeParser::AlternativeMessagePart::Ptr part); - - QVector content(const QByteArray &ct) const; - - AlternativePart *q; - - QVector types() const; - -private: - QMap> mContent; - QVector mTypes; -}; - -void AlternativePartPrivate::fillFrom(MimeTreeParser::AlternativeMessagePart::Ptr part) -{ - mTypes = QVector() << "html" << "plaintext"; - - Content::Ptr content = std::make_shared(part->htmlContent().toLocal8Bit(), q); - mContent["html"].append(content); - content = std::make_shared(part->plaintextContent().toLocal8Bit(), q); - mContent["plaintext"].append(content); - q->reachParentD()->createMailMime(part); -} - -QVector AlternativePartPrivate::types() const -{ - return mTypes; -} - -QVector AlternativePartPrivate::content(const QByteArray& ct) const -{ - return mContent[ct]; -} - -AlternativePart::AlternativePart() - : d(std::unique_ptr(new AlternativePartPrivate)) -{ - d->q = this; -} - -AlternativePart::~AlternativePart() -{ - -} - -QByteArray AlternativePart::type() const -{ - return "AlternativePart"; -} - -QVector AlternativePart::availableContents() const -{ - return d->types(); -} - -QVector AlternativePart::content(const QByteArray& ct) const -{ - return d->content(ct); -} - -PartPrivate* AlternativePart::reachParentD() const -{ - return Part::d.get(); -} - -class SinglePartPrivate -{ -public: - void fillFrom(const MimeTreeParser::TextMessagePart::Ptr &part); - void fillFrom(const MimeTreeParser::HtmlMessagePart::Ptr &part); - void fillFrom(const MimeTreeParser::AttachmentMessagePart::Ptr &part); - void createEncryptionFailBlock(const MimeTreeParser::EncryptedMessagePart::Ptr &part); - SinglePart *q; - - QVector mContent; - QByteArray mType; -}; - -void SinglePartPrivate::fillFrom(const MimeTreeParser::TextMessagePart::Ptr &part) -{ - mType = "plaintext"; - mContent.clear(); - foreach (const auto &mp, part->subParts()) { - auto d_ptr = new ContentPrivate; - d_ptr->mContent = mp->text().toUtf8(); // TODO: should set "raw" content not the already utf8 encoded content - d_ptr->mParent = q; - const auto enc = mp.dynamicCast(); - auto sig = mp.dynamicCast(); - if (enc) { - d_ptr->appendEncryption(enc); - if (!enc->isDecryptable()) { - d_ptr->mContent = QByteArray(); - } - const auto s = enc->subParts(); - if (s.size() == 1) { - sig = s[0].dynamicCast(); - } - } - if (sig) { - d_ptr->appendSignature(sig); - } - mContent.append(std::make_shared(d_ptr)); - q->reachParentD()->createMailMime(part); - d_ptr->mCodec = q->mailMime()->charset(); - } -} - -void SinglePartPrivate::fillFrom(const MimeTreeParser::HtmlMessagePart::Ptr &part) -{ - mType = "html"; - mContent.clear(); - mContent.append(std::make_shared(part->text().toUtf8(), q)); - q->reachParentD()->createMailMime(part); -} - -void SinglePartPrivate::fillFrom(const MimeTreeParser::AttachmentMessagePart::Ptr &part) -{ - QMimeDatabase mimeDb; - q->reachParentD()->createMailMime(part.staticCast()); - const auto mimetype = q->mailMime()->mimetype(); - const auto content = q->mailMime()->decodedContent(); - mContent.clear(); - if (mimetype == mimeDb.mimeTypeForName("text/plain")) { - mType = "plaintext"; - mContent.append(std::make_shared(content, q)); - } else if (mimetype == mimeDb.mimeTypeForName("text/html")) { - mType = "html"; - mContent.append(std::make_shared(content, q)); - } else { - mType = mimetype.name().toUtf8(); - mContent.append(std::make_shared(content, q)); - } -} - -void SinglePartPrivate::createEncryptionFailBlock(const MimeTreeParser::EncryptedMessagePart::Ptr &part) -{ - mType = "plaintext"; - mContent.clear(); - mContent.append(std::make_shared(QByteArray(), q)); - q->reachParentD()->createMailMime(part); -} - -SinglePart::SinglePart() - : d(std::unique_ptr(new SinglePartPrivate)) -{ - d->q = this; -} - -SinglePart::~SinglePart() -{ - -} - -QVector SinglePart::availableContents() const -{ - return QVector() << d->mType; -} - -QVector< Content::Ptr > SinglePart::content(const QByteArray &ct) const -{ - if (ct == d->mType) { - return d->mContent; - } - return QVector(); -} - -QByteArray SinglePart::type() const -{ - return "SinglePart"; -} - -PartPrivate* SinglePart::reachParentD() const -{ - return Part::d.get(); -} - -ParserPrivate::ParserPrivate(Parser* parser) - : q(parser) - , mNodeHelper(std::make_shared()) -{ - -} - -void ParserPrivate::setMessage(const QByteArray& mimeMessage) -{ - const auto mailData = KMime::CRLFtoLF(mimeMessage); - mMsg = KMime::Message::Ptr(new KMime::Message); - mMsg->setContent(mailData); - mMsg->parse(); - - // render the mail - StringHtmlWriter htmlWriter; - ObjectTreeSource source(&htmlWriter); - MimeTreeParser::ObjectTreeParser otp(&source, mNodeHelper.get()); - - otp.parseObjectTree(mMsg.data()); - mPartTree = otp.parsedPart().dynamicCast(); - - mEmbeddedPartMap = htmlWriter.embeddedParts(); - mHtml = htmlWriter.html(); - - mTree = std::make_shared(); - createTree(mPartTree, mTree); -} - - -void ParserPrivate::createTree(const MimeTreeParser::MessagePart::Ptr &start, const Part::Ptr &tree) -{ - foreach (const auto &mp, start->subParts()) { - const auto m = mp.dynamicCast(); - const auto text = mp.dynamicCast(); - const auto alternative = mp.dynamicCast(); - const auto html = mp.dynamicCast(); - const auto attachment = mp.dynamicCast(); - if (attachment) { - auto part = std::make_shared(); - part->d->fillFrom(attachment); - tree->d->appendSubPart(part); - } else if (text) { - auto part = std::make_shared(); - part->d->fillFrom(text); - tree->d->appendSubPart(part); - } else if (alternative) { - auto part = std::make_shared(); - part->d->fillFrom(alternative); - tree->d->appendSubPart(part); - } else if (html) { - auto part = std::make_shared(); - part->d->fillFrom(html); - tree->d->appendSubPart(part); - } else { - const auto enc = mp.dynamicCast(); - const auto sig = mp.dynamicCast(); - if (enc || sig) { - auto subTree = std::make_shared(); - if (enc) { - subTree->d->appendEncryption(enc); - if (!enc->isDecryptable()) { - auto part = std::make_shared(); - part->d->createEncryptionFailBlock(enc); - part->reachParentD()->setEncryptions(subTree->d->encryptions()); - tree->d->appendSubPart(part); - return; - } - } - if (sig) { - subTree->d->appendSignature(sig); - } - createTree(m, subTree); - foreach(const auto &p, subTree->subParts()) { - tree->d->appendSubPart(p); - if (enc) { - p->d->setEncryptions(subTree->d->encryptions()); - } - if (sig) { - p->d->setSignatures(subTree->d->signatures()); - } - } - } else { - createTree(m, tree); - } - } - } -} - -Parser::Parser(const QByteArray& mimeMessage) - :d(std::unique_ptr(new ParserPrivate(this))) -{ - d->setMessage(mimeMessage); -} - -Parser::~Parser() -{ -} - -Part::Ptr Parser::getPart(const QUrl &url) -{ - if (url.scheme() == QStringLiteral("cid") && !url.path().isEmpty()) { - const auto cid = url.path(); - return find(d->mTree, [&cid](const Part::Ptr &p){ - const auto mime = p->mailMime(); - return mime->cid() == cid; - }); - } - return Part::Ptr(); -} - -QVector Parser::collectContentParts() const -{ - return collect(d->mTree, [](const Part::Ptr &p){return p->type() != "EncapsulatedPart";}, - [](const Content::Ptr &content){ - const auto mime = content->mailMime(); - - if (!mime) { - return true; - } - - if (mime->isFirstTextPart()) { - return true; - } - - { - auto _mime = content->parent()->mailMime(); - while (_mime) { - if (_mime && (_mime->isTopLevelPart() || _mime->isFirstTextPart())) { - return true; - } - if (_mime->isFirstPart()) { - _mime = _mime->parent(); - } else { - break; - } - } - } - - const auto ctname = mime->mimetype().name().trimmed().toLower(); - bool mightContent = (content->type() != "Content"); //Content we understand - - const auto cd = mime->disposition(); - if (cd && cd == MailMime::Inline) { - return mightContent; - } - - if (cd && cd == MailMime::Attachment) { - return false; - } - - if ((ctname.startsWith("text/") || ctname.isEmpty()) && - (!mime || mime->filename().trimmed().isEmpty())) { - // text/* w/o filename parameter: - return true; - } - return false; - }); -} - - -QVector Parser::collectAttachmentParts() const -{ - return collect(d->mTree, [](const Part::Ptr &p){return p->type() != "EncapsulatedPart";}, - [](const Content::Ptr &content){ - const auto mime = content->mailMime(); - - if (!mime) { - return false; - } - - if (mime->isFirstTextPart()) { - return false; - } - - { - QMimeDatabase mimeDb; - auto _mime = content->parent()->mailMime(); - const auto parent = _mime->parent(); - if (parent) { - const auto mimetype = parent->mimetype(); - if (mimetype == mimeDb.mimeTypeForName("multipart/related")) { - return false; - } - } - while (_mime) { - if (_mime && (_mime->isTopLevelPart() || _mime->isFirstTextPart())) { - return false; - } - if (_mime->isFirstPart()) { - _mime = _mime->parent(); - } else { - break; - } - } - } - - const auto ctname = mime->mimetype().name().trimmed().toLower(); - bool mightContent = (content->type() != "Content"); //Content we understand - - const auto cd = mime->disposition(); - if (cd && cd == MailMime::Inline) { - // explict "inline" disposition: - return !mightContent; - } - if (cd && cd == MailMime::Attachment) { - // explicit "attachment" disposition: - return true; - } - - const auto ct = mime->mimetype(); - if ((ctname.startsWith("text/") || ctname.isEmpty()) && - (!mime || mime->filename().trimmed().isEmpty())) { - // text/* w/o filename parameter: - return false; - } - return true; - }); -} - -QVector Parser::collect(const Part::Ptr &start, std::function select, std::function filter) const -{ - QVector ret; - foreach (const auto &part, start->subParts()) { - QVector contents; - foreach(const auto &ct, part->availableContents()) { - foreach(const auto &content, part->content(ct)) { - if (filter(content)) { - contents.append(ct); - break; - } - } - } - if (!contents.isEmpty()) { - ret.append(part); - } - if (select(part)){ - ret += collect(part, select, filter); - } - } - return ret; -} - -Part::Ptr Parser::find(const Part::Ptr &start, std::function select) const -{ - foreach (const auto &part, start->subParts()) { - if (select(part)) { - return part; - } - const auto ret = find(part, select); - if (ret) { - return ret; - } - } - return Part::Ptr(); -} diff --git a/framework/domain/mimetreeparser/interface.h b/framework/domain/mimetreeparser/interface.h deleted file mode 100644 index 7c3ea28b..00000000 --- a/framework/domain/mimetreeparser/interface.h +++ /dev/null @@ -1,378 +0,0 @@ -/* - Copyright (c) 2016 Sandro Knauß - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#pragma once - -#include -#include -#include - -#include -#include -#include - -class Part; -class PartPrivate; - -class MailMime; -class MailMimePrivate; - -class AlternativePart; -class AlternativePartPrivate; - -class SinglePart; -class SinglePartPrivate; - -class EncryptionPart; -class EncryptionPartPrivate; - -class EncapsulatedPart; -class EncapsulatedPartPrivate; - -class Content; -class ContentPrivate; - -class CertContent; -class CertContentPrivate; - -class EncryptionError; - -class Key; -class KeyPrivate; -class Signature; -class SignaturePrivate; -class Encryption; -class EncryptionPrivate; - -typedef std::shared_ptr SignaturePtr; -typedef std::shared_ptr EncryptionPtr; - -class Parser; -class ParserPrivate; - -/* - * A MessagePart that is based on a KMime::Content - */ -class MailMime -{ -public: - typedef std::shared_ptr Ptr; - /** - * Various possible values for the "Content-Disposition" header. - */ - enum Disposition { - Invalid, ///< Default, invalid value - Inline, ///< inline - Attachment ///< attachment - }; - - MailMime(); - - // interessting header parts of a KMime::Content - QMimeType mimetype() const; - Disposition disposition() const; - QUrl label() const; - QByteArray cid() const; - QByteArray charset() const; - QString filename() const; - - // Unique identifier to ecactly this KMime::Content - QByteArray link() const; - - QByteArray content() const; - //Use default charset - QString encodedContent() const; - - // overwrite default charset with given charset - QString encodedContent(QByteArray charset) const; - - QByteArray decodedContent() const; - - bool isFirstTextPart() const; - bool isFirstPart() const; - bool isTopLevelPart() const; - - MailMime::Ptr parent() const; - -private: - std::unique_ptr d; - - friend class PartPrivate; -}; - -class Content -{ -public: - typedef std::shared_ptr Ptr; - Content(const QByteArray &content, Part *parent); - Content(ContentPrivate *d_ptr); - virtual ~Content(); - - QByteArray content() const; - - QByteArray charset() const; - - //Use default charset - QString encodedContent() const; - - // overwrite default charset with given charset - QString encodedContent(const QByteArray &charset) const; - - QVector signatures() const; - QVector encryptions() const; - MailMime::Ptr mailMime() const; - virtual QByteArray type() const; - Part* parent() const; -private: - std::unique_ptr d; -}; - -class PlainTextContent : public Content -{ -public: - PlainTextContent(const QByteArray &content, Part *parent); - PlainTextContent(ContentPrivate *d_ptr); - QByteArray type() const Q_DECL_OVERRIDE; -}; - -class HtmlContent : public Content -{ -public: - HtmlContent(const QByteArray &content, Part *parent); - HtmlContent(ContentPrivate* d_ptr); - QByteArray type() const Q_DECL_OVERRIDE; -}; - -/* - * importing a cert GpgMe::ImportResult - * checking a cert (if it is a valid cert) - */ - -class CertContent : public Content -{ -public: - typedef std::shared_ptr Ptr; - CertContent(const QByteArray &content, Part *parent); - - QByteArray type() const Q_DECL_OVERRIDE; - enum CertType { - Pgp, - SMime - }; - - enum CertSubType { - Public, - Private - }; - - CertType certType() const; - CertSubType certSubType() const; - int keyLength() const; - -private: - std::unique_ptr d; -}; - -class Part -{ -public: - typedef std::shared_ptr Ptr; - Part(); - virtual QByteArray type() const; - - virtual QVector availableContents() const; - virtual QVector content(const QByteArray& ct) const; - QVector content() const; - - bool hasSubParts() const; - QVector subParts() const; - Part *parent() const; - - QVector signatures() const; - QVector encryptions() const; - virtual MailMime::Ptr mailMime() const; -protected: - std::unique_ptr d; -private: - friend class ParserPrivate; - friend class PartPrivate; -}; - -class AlternativePart : public Part -{ -public: - typedef std::shared_ptr Ptr; - - AlternativePart(); - virtual ~AlternativePart(); - - QVector availableContents() const Q_DECL_OVERRIDE; - QVector content(const QByteArray& ct) const Q_DECL_OVERRIDE; - - QByteArray type() const Q_DECL_OVERRIDE; - -private: - PartPrivate *reachParentD() const; - std::unique_ptr d; - - friend class ParserPrivate; - friend class AlternativePartPrivate; -}; - -class SinglePart : public Part -{ - public: - typedef std::shared_ptr Ptr; - - SinglePart(); - virtual ~SinglePart(); - - QVector content(const QByteArray& ct) const Q_DECL_OVERRIDE; - QVector availableContents() const Q_DECL_OVERRIDE; - - QByteArray type() const Q_DECL_OVERRIDE; -private: - PartPrivate *reachParentD() const; - std::unique_ptr d; - - friend class ParserPrivate; - friend class SinglePartPrivate; -}; - -/* - * we want to request complete headers like: - * from/to... - */ - -class EncapsulatedPart : public SinglePart -{ -public: - typedef std::shared_ptr Ptr; - QByteArray type() const Q_DECL_OVERRIDE; - - //template QByteArray header(); -private: - std::unique_ptr d; -}; - -class EncryptionError -{ -public: - int errorId() const; - QString errorString() const; -}; - -class Key -{ -public: - typedef std::shared_ptr Ptr; - Key(); - Key(KeyPrivate *); - ~Key(); - - QString keyid() const; - QString name() const; - QString email() const; - QString comment() const; - QVector emails() const; - enum KeyTrust { - Unknown, Undefined, Never, Marginal, Full, Ultimate - }; - KeyTrust keyTrust() const; - - bool isRevokation() const; - bool isInvalid() const; - bool isExpired() const; - - std::vector subkeys(); - Key parentkey() const; -private: - std::unique_ptr d; -}; - -class Signature -{ -public: - typedef std::shared_ptr Ptr; - Signature(); - Signature(SignaturePrivate *); - ~Signature(); - - Key::Ptr key() const; - QDateTime creationDateTime() const; - QDateTime expirationDateTime() const; - bool neverExpires() const; - - //template <> StatusObject verify() const; - private: - std::unique_ptr d; -}; - -/* - * Normally the Keys for encryption are subkeys - * for clients the parentkeys are "more interessting", because they store the name, email etc. - * but a client may also wants show to what subkey the mail is really encrypted, an if this subkey isRevoked or something else - */ -class Encryption -{ -public: - enum ErrorType { - NoError, - PassphraseError, - KeyMissing, - UnknownError - }; - typedef std::shared_ptr Ptr; - Encryption(); - Encryption(EncryptionPrivate *); - ~Encryption(); - std::vector recipients() const; - QString errorString(); - ErrorType errorType(); -private: - std::unique_ptr d; -}; - -class Parser -{ -public: - typedef std::shared_ptr Ptr; - Parser(const QByteArray &mimeMessage); - ~Parser(); - - Part::Ptr getPart(const QUrl &url); - - QVector collect(const Part::Ptr &start, std::function select, std::function filter) const; - Part::Ptr find(const Part::Ptr &start, std::function select) const; - QVector collectContentParts() const; - QVector collectAttachmentParts() const; - //template <> QVector collect() const; - - //template <> static StatusObject verifySignature(const Signature signature) const; - //template <> static StatusObject decrypt(const EncryptedPart part) const; - -signals: - void partsChanged(); - -private: - std::unique_ptr d; - - friend class InterfaceTest; -}; - diff --git a/framework/domain/mimetreeparser/interface_p.h b/framework/domain/mimetreeparser/interface_p.h deleted file mode 100644 index 8fab221a..00000000 --- a/framework/domain/mimetreeparser/interface_p.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - Copyright (c) 2016 Sandro Knauß - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#pragma once - -#include "interface.h" - -#include -#include - -namespace KMime -{ - class Message; - typedef QSharedPointer MessagePtr; -} - -namespace MimeTreeParser -{ - class MessagePart; - class NodeHelper; - typedef QSharedPointer MessagePartPtr; -} - -class ParserPrivate -{ -public: - ParserPrivate(Parser *parser); - - void setMessage(const QByteArray &mimeMessage); - void createTree(const MimeTreeParser::MessagePartPtr& start, const Part::Ptr& tree); - - Part::Ptr mTree; - Parser *q; - - MimeTreeParser::MessagePartPtr mPartTree; - KMime::MessagePtr mMsg; - std::shared_ptr mNodeHelper; - QString mHtml; - QMap mEmbeddedPartMap; -}; diff --git a/framework/domain/mimetreeparser/objecttreesource.cpp b/framework/domain/mimetreeparser/objecttreesource.cpp deleted file mode 100644 index 567f3516..00000000 --- a/framework/domain/mimetreeparser/objecttreesource.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net - Copyright (c) 2009 Andras Mantia - - 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., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "objecttreesource.h" - -#include -#include -#include -#include - -class ObjectSourcePrivate -{ -public: - ObjectSourcePrivate() - : mWriter(0) - , mAllowDecryption(true) - , mHtmlLoadExternal(true) - , mPreferredMode(MimeTreeParser::Util::Html) - { - - } - MimeTreeParser::HtmlWriter *mWriter; - MimeTreeParser::BodyPartFormatterBaseFactory mBodyPartFormatterBaseFactory; - bool mAllowDecryption; - bool mHtmlLoadExternal; - MimeTreeParser::Util::HtmlMode mPreferredMode; -}; - -ObjectTreeSource::ObjectTreeSource(MimeTreeParser::HtmlWriter *writer) - : MimeTreeParser::Interface::ObjectTreeSource() - , d(new ObjectSourcePrivate) - { - d->mWriter = writer; - } - -ObjectTreeSource::~ObjectTreeSource() -{ - delete d; -} - -void ObjectTreeSource::setAllowDecryption(bool allowDecryption) -{ - d->mAllowDecryption = allowDecryption; -} - -MimeTreeParser::HtmlWriter *ObjectTreeSource::htmlWriter() -{ - return d->mWriter; -} - -bool ObjectTreeSource::htmlLoadExternal() const -{ - return d->mHtmlLoadExternal; -} - -void ObjectTreeSource::setHtmlLoadExternal(bool loadExternal) -{ - d->mHtmlLoadExternal = loadExternal; -} - -bool ObjectTreeSource::decryptMessage() const -{ - return d->mAllowDecryption; -} - -bool ObjectTreeSource::showSignatureDetails() const -{ - return true; -} - -int ObjectTreeSource::levelQuote() const -{ - return 1; -} - -const QTextCodec *ObjectTreeSource::overrideCodec() -{ - return Q_NULLPTR; -} - -QString ObjectTreeSource::createMessageHeader(KMime::Message *message) -{ - return QString(); -} - -const MimeTreeParser::AttachmentStrategy *ObjectTreeSource::attachmentStrategy() -{ - return MimeTreeParser::AttachmentStrategy::smart(); -} - -QObject *ObjectTreeSource::sourceObject() -{ - return Q_NULLPTR; -} - -void ObjectTreeSource::setHtmlMode(MimeTreeParser::Util::HtmlMode mode, const QList &availableModes) -{ - Q_UNUSED(mode); - Q_UNUSED(availableModes); -} - -MimeTreeParser::Util::HtmlMode ObjectTreeSource::preferredMode() const -{ - return d->mPreferredMode; -} - -bool ObjectTreeSource::autoImportKeys() const -{ - return false; -} - -bool ObjectTreeSource::showEmoticons() const -{ - return false; -} - -bool ObjectTreeSource::showExpandQuotesMark() const -{ - return false; -} - -bool ObjectTreeSource::isPrinting() const -{ - return false; -} - -const MimeTreeParser::BodyPartFormatterBaseFactory *ObjectTreeSource::bodyPartFormatterFactory() -{ - return &(d->mBodyPartFormatterBaseFactory); -} - -MimeTreeParser::Interface::MessagePartRenderer::Ptr ObjectTreeSource::messagePartTheme(MimeTreeParser::Interface::MessagePart::Ptr msgPart) -{ - Q_UNUSED(msgPart); - return MimeTreeParser::Interface::MessagePartRenderer::Ptr(); -} diff --git a/framework/domain/mimetreeparser/objecttreesource.h b/framework/domain/mimetreeparser/objecttreesource.h deleted file mode 100644 index 93812dc3..00000000 --- a/framework/domain/mimetreeparser/objecttreesource.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net - Copyright (c) 2009 Andras Mantia - - 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., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef MAILVIEWER_OBJECTTREEEMPTYSOURCE_H -#define MAILVIEWER_OBJECTTREEEMPTYSOURCE_H - -#include - -class QString; - -class ObjectSourcePrivate; -class ObjectTreeSource : public MimeTreeParser::Interface::ObjectTreeSource -{ -public: - ObjectTreeSource(MimeTreeParser::HtmlWriter *writer); - virtual ~ObjectTreeSource(); - void setHtmlLoadExternal(bool loadExternal); - bool decryptMessage() const Q_DECL_OVERRIDE; - bool htmlLoadExternal() const Q_DECL_OVERRIDE; - bool showSignatureDetails() const Q_DECL_OVERRIDE; - void setHtmlMode(MimeTreeParser::Util::HtmlMode mode, const QList &availableModes) Q_DECL_OVERRIDE; - MimeTreeParser::Util::HtmlMode preferredMode() const Q_DECL_OVERRIDE; - void setAllowDecryption(bool allowDecryption); - int levelQuote() const Q_DECL_OVERRIDE; - const QTextCodec *overrideCodec() Q_DECL_OVERRIDE; - QString createMessageHeader(KMime::Message *message) Q_DECL_OVERRIDE; - const MimeTreeParser::AttachmentStrategy *attachmentStrategy() Q_DECL_OVERRIDE; - MimeTreeParser::HtmlWriter *htmlWriter() Q_DECL_OVERRIDE; - QObject *sourceObject() Q_DECL_OVERRIDE; - bool autoImportKeys() const Q_DECL_OVERRIDE; - bool showEmoticons() const Q_DECL_OVERRIDE; - bool showExpandQuotesMark() const Q_DECL_OVERRIDE; - bool isPrinting() const Q_DECL_OVERRIDE; - const MimeTreeParser::BodyPartFormatterBaseFactory *bodyPartFormatterFactory() Q_DECL_OVERRIDE; - MimeTreeParser::Interface::MessagePartRendererPtr messagePartTheme(MimeTreeParser::Interface::MessagePartPtr msgPart) Q_DECL_OVERRIDE; -private: - ObjectSourcePrivate *const d; -}; - -#endif - diff --git a/framework/domain/mimetreeparser/stringhtmlwriter.cpp b/framework/domain/mimetreeparser/stringhtmlwriter.cpp deleted file mode 100644 index 88034492..00000000 --- a/framework/domain/mimetreeparser/stringhtmlwriter.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* -*- c++ -*- - filehtmlwriter.cpp - - This file is part of KMail, the KDE mail client. - Copyright (c) 2003 Marc Mutz - - KMail is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License, version 2, as - published by the Free Software Foundation. - - KMail is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - - In addition, as a special exception, the copyright holders give - permission to link the code of this program with any edition of - the Qt library by Trolltech AS, Norway (or with modified versions - of Qt that use the same license as Qt), and distribute linked - combinations including the two. You must obey the GNU General - Public License in all respects for all of the code used other than - Qt. If you modify this file, you may extend this exception to - your version of the file, but you are not obligated to do so. If - you do not wish to do so, delete this exception statement from - your version. -*/ - -#include "stringhtmlwriter.h" - -#include -#include -#include - -StringHtmlWriter::StringHtmlWriter() - : MimeTreeParser::HtmlWriter() - , mState(Ended) -{ -} - -StringHtmlWriter::~StringHtmlWriter() -{ -} - -void StringHtmlWriter::begin(const QString &css) -{ - if (mState != Ended) { - qWarning() << "begin() called on non-ended session!"; - reset(); - } - - mState = Begun; - mExtraHead.clear(); - mHtml.clear(); - - if (!css.isEmpty()) { - write(QLatin1String("\n")); - } -} - -void StringHtmlWriter::end() -{ - if (mState != Begun) { - qWarning() << "Called on non-begun or queued session!"; - } - - if (!mExtraHead.isEmpty()) { - insertExtraHead(); - mExtraHead.clear(); - } - resolveCidUrls(); - mState = Ended; -} - -void StringHtmlWriter::reset() -{ - if (mState != Ended) { - mHtml.clear(); - mExtraHead.clear(); - mState = Begun; // don't run into end()'s warning - end(); - mState = Ended; - } -} - -void StringHtmlWriter::write(const QString &str) -{ - if (mState != Begun) { - qWarning() << "Called in Ended or Queued state!"; - } - mHtml.append(str); -} - -void StringHtmlWriter::queue(const QString &str) -{ - write(str); -} - -void StringHtmlWriter::flush() -{ - mState = Begun; // don't run into end()'s warning - end(); -} - -void StringHtmlWriter::embedPart(const QByteArray &contentId, const QString &url) -{ - write("\n"); - mEmbeddedPartMap.insert(contentId, url); -} - -void StringHtmlWriter::resolveCidUrls() -{ - for (const auto &cid : mEmbeddedPartMap.keys()) { - mHtml.replace(QString("src=\"cid:%1\"").arg(QString(cid)), QString("src=\"%1\"").arg(mEmbeddedPartMap.value(cid).toString())); - } -} - -void StringHtmlWriter::extraHead(const QString &extraHead) -{ - if (mState != Ended) { - qWarning() << "Called on non-started session!"; - } - mExtraHead.append(extraHead); -} - - -void StringHtmlWriter::insertExtraHead() -{ - const QString headTag(QStringLiteral("")); - const int index = mHtml.indexOf(headTag); - if (index != -1) { - mHtml.insert(index + headTag.length(), mExtraHead); - } -} - -QMap StringHtmlWriter::embeddedParts() const -{ - return mEmbeddedPartMap; -} - -QString StringHtmlWriter::html() const -{ - if (mState != Ended) { - qWarning() << "Called on non-ended session!"; - } - return mHtml; -} diff --git a/framework/domain/mimetreeparser/stringhtmlwriter.h b/framework/domain/mimetreeparser/stringhtmlwriter.h deleted file mode 100644 index fa5b760e..00000000 --- a/framework/domain/mimetreeparser/stringhtmlwriter.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -*- c++ -*- - - Copyright (c) 2016 Sandro Knauß - - Kube is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License, version 2, as - published by the Free Software Foundation. - - Kube is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - - In addition, as a special exception, the copyright holders give - permission to link the code of this program with any edition of - the Qt library by Trolltech AS, Norway (or with modified versions - of Qt that use the same license as Qt), and distribute linked - combinations including the two. You must obey the GNU General - Public License in all respects for all of the code used other than - Qt. If you modify this file, you may extend this exception to - your version of the file, but you are not obligated to do so. If - you do not wish to do so, delete this exception statement from - your version. -*/ - -#ifndef __KUBE_FRAMEWORK_MAIL_STRINGHTMLWRITER_H__ -#define __KUBE_FRAMEWORK_MAIL_STRINGHTMLWRITER_H__ - -#include - -#include -#include - -class QString; - -class StringHtmlWriter : public MimeTreeParser::HtmlWriter -{ -public: - explicit StringHtmlWriter(); - virtual ~StringHtmlWriter(); - - void begin(const QString &cssDefs) Q_DECL_OVERRIDE; - void end() Q_DECL_OVERRIDE; - void reset() Q_DECL_OVERRIDE; - void write(const QString &str) Q_DECL_OVERRIDE; - void queue(const QString &str) Q_DECL_OVERRIDE; - void flush() Q_DECL_OVERRIDE; - void embedPart(const QByteArray &contentId, const QString &url) Q_DECL_OVERRIDE; - void extraHead(const QString &str) Q_DECL_OVERRIDE; - - QString html() const; - QMap embeddedParts() const; -private: - void insertExtraHead(); - void resolveCidUrls(); - - QString mHtml; - QString mExtraHead; - enum State { - Begun, - Queued, - Ended - } mState; - QMap mEmbeddedPartMap; -}; - -#endif // __MESSAGEVIEWER_FILEHTMLWRITER_H__ diff --git a/framework/domain/mimetreeparser/tests/CMakeLists.txt b/framework/domain/mimetreeparser/tests/CMakeLists.txt deleted file mode 100644 index 71afb903..00000000 --- a/framework/domain/mimetreeparser/tests/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -add_subdirectory(gnupg_home) -add_definitions( -DMAIL_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) -include(${CMAKE_CURRENT_SOURCE_DIR}/kdepim_add_gpg_crypto_test.cmake) -include_directories( - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/.. - ) - -include(ECMAddTests) - -add_executable(mimetreeparsertest interfacetest.cpp) -add_gpg_crypto_test(mimetreeparsertest mimetreeparsertest) -qt5_use_modules(mimetreeparsertest Core Test) -target_link_libraries(mimetreeparsertest mimetreeparser) - -find_package(Gpgmepp 1.7.1 CONFIG) -find_package(QGpgme 1.7.1 CONFIG) - -ecm_add_test(gpgerrortest.cpp - TEST_NAME "gpgerrortest" - NAME_PREFIX "mimetreeparser-" - LINK_LIBRARIES Qt5::Core Qt5::Test mimetreeparser Gpgmepp QGpgme -) diff --git a/framework/domain/mimetreeparser/tests/data/alternative.mbox b/framework/domain/mimetreeparser/tests/data/alternative.mbox deleted file mode 100644 index 6522c34b..00000000 --- a/framework/domain/mimetreeparser/tests/data/alternative.mbox +++ /dev/null @@ -1,28 +0,0 @@ -Return-Path: -Date: Wed, 8 Jun 2016 20:34:44 -0700 -From: Konqi -To: konqi@kde.org -Subject: A random subject with alternative contenttype -MIME-Version: 1.0 -Content-Type: multipart/alternative; - boundary="----=_Part_12345678_12345678" - - -------=_Part_12345678_12345678 -Content-Type: text/plain; charset=utf-8 -Content-Transfer-Encoding: quoted-printable - -If you can see this text it means that your email client couldn't display o= -ur newsletter properly. -Please visit this link to view the newsletter on our website: http://www.go= -g.com/newsletter/ - - -------=_Part_12345678_12345678 -Content-Transfer-Encoding: 7Bit -Content-Type: text/html; charset="windows-1252" - -

HTML text

- - -------=_Part_12345678_12345678-- diff --git a/framework/domain/mimetreeparser/tests/data/cid-links.mbox b/framework/domain/mimetreeparser/tests/data/cid-links.mbox deleted file mode 100644 index 40ff5282..00000000 --- a/framework/domain/mimetreeparser/tests/data/cid-links.mbox +++ /dev/null @@ -1,1384 +0,0 @@ -Message-ID: <851f01d15e53$31734730$790bc9ad@info> -From: "OculusLab" -To: -Subject: CID links for images -Date: Wed, 03 Feb 2016 07:19:17 +0200 -MIME-Version: 1.0 -Content-Type: multipart/related; - type="multipart/alternative"; - boundary="----=_NextPart_000_000F_01D15E52.0BD654A0" -X-MSMail-Priority: Normal -X-Mailer: Microsoft Windows Live Mail 14.0.8117.416 -X-MimeOLE: Produced By Microsoft MimeOLE V14.0.8117.416 - - This is a multi-part message in MIME format. - -------=_NextPart_000_000F_01D15E52.0BD654A0 -Content-Type: multipart/alternative; - boundary="----=_NextPart_000_0010_01D15E52.0BD654A0" - -------=_NextPart_000_0010_01D15E52.0BD654A0 -Content-Type: text/plain; - charset="windows-1251" -Content-Transfer-Encoding: quoted-printable - -=0D=0A=0D=0A=0D=0A=0D=0ASuperkombipackung für nur 45 Euro=0D= -=0A=0D=0A -------=_NextPart_000_0010_01D15E52.0BD654A0 -Content-Type: text/html; - charset="windows-1251" -Content-Transfer-Encoding: quoted-printable - -=0D=0A=0D=0A=0D=0A=0D=0A - -------=_NextPart_000_0010_01D15E52.0BD654A0-- - -------=_NextPart_000_000F_01D15E52.0BD654A0 -Content-Type: image/jpeg; - name="aqnaozisxya.jpeg" -Content-Transfer-Encoding: base64 -Content-ID: <9359201d15e53f31a68c307b3369b6@info> - -/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QMqaHR0cDov -L25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENl -aGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4 -OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAxNCA3OS4xNTE0ODEsIDIwMTMvMDMvMTMtMTI6 -MDk6MTUgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5 -OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHht -bG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6 -Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUu -Y29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBo -b3Rvc2hvcCBDQyAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QjdCRTg5MTBD -OUNGMTFFNUJBOTdEMkQyNzU0ODI3RDciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QjdCRTg5 -MTFDOUNGMTFFNUJBOTdEMkQyNzU0ODI3RDciPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5z -dGFuY2VJRD0ieG1wLmlpZDpCN0JFODkwRUM5Q0YxMUU1QkE5N0QyRDI3NTQ4MjdENyIgc3RSZWY6 -ZG9jdW1lbnRJRD0ieG1wLmRpZDpCN0JFODkwRkM5Q0YxMUU1QkE5N0QyRDI3NTQ4MjdENyIvPiA8 -L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0i -ciI/Pv/uAA5BZG9iZQBkwAAAAAH/2wCEAAYEBAQFBAYFBQYJBgUGCQsIBgYICwwKCgsKCgwQDAwM -DAwMEAwODxAPDgwTExQUExMcGxsbHB8fHx8fHx8fHx8BBwcHDQwNGBAQGBoVERUaHx8fHx8fHx8f -Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fH//AABEIAvsCigMBEQACEQED -EQH/xADJAAEAAgMBAQEAAAAAAAAAAAAAAwQBAgUGBwgBAQEBAQEBAQAAAAAAAAAAAAABAgMEBQYQ -AAEEAgEDAgQCBAgKCQMACwIAAQMEEQUSIRMGMUFRIjIUYXGBQhUHkaGxUiMzFhfB0WJy0+OkZZVW -8ILSsyQ0lFU24UN1U4MlssJzhLQ1djcRAQABAgMEBQkFBwQBBAIDAAABEQIhMQNBUWEScYGRoQTw -scHRIjJSYhPhQpKyFfFygqIzUwXCI2Nzk9JDsxSDNPLD0//aAAwDAQACEQMRAD8A/VKAgICAgICA -gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA -gICAgICAgICAgICAgICAgICAg1y6Bl0DLoGXQMugZdBrIZMBOz9WZBpWkM68Zk+SIWd3QS8nQOTo -HJ0Dk6BydA5OgcnQOToGXQMugZdAy6Bl0DLoGXQMugOT4dAYnwyA7ughaWTPqgkIyaNnz16INQkN -3bL+6BJIbHhn6INozJ2fL+6DbLoI5JDYmZn6KDJGTR8s9VQiM39Xyg1GWRy6v0ygkMiYXdn6sghG -aRxd8/yItGAnldupfxMoUZ70nx/kQO9J8f5EKAzSO3r/ABMhRnuyfH+RCg0snXqgNNI/v/Igd2T4 -/wAiDLSyfFCjDSyfH+RUZ7snx/kUBpTz6oMd2TPr/Igz3JPj/Igy0knx/kQY7smX6oMd2T4/yIMj -Kb+6B3T5O2eiVKHdP4oUO5J8UqtGHlk5Yz/IpUoz3ZPiqUO7J8UKDynluqIy8h/FA7h/FKlApDZv -VAaU/ilSjPcP4oDyHjOUGO4eG6oUZ5l8UGeZY9VUatIePVRR5Dz6pUoyxnn1QBkN3fqgy5l8UQYy -+KDcXd2fPxVGUBBqgICAgICDSb+qL8nQR0//ACkP+YyEpkBAQEBAQEBEEBAQEBAQEBAL0dAb0ZFH -9HQQD6oJJP6pv0INY/Vvz/wIMS/1n8CCSL6X/N0G6CKT62UGS/qv0/4VQhQRh9Tfmglm/qyQQD9D -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 -6/oo2w6Iy3oisug1QYRGW9UVsiCKwgwgICDCAgwgyyCnfiCRmYxYhJsOzrF00lu3Fw7Ol6u8J4b+ -aX+NI1Fmxz5YZIvkkbBN8PgukTViYom1sxMfa9Xd8MuV8OlsvSRBxFh+H8qwqYWVgSMqyytDUzYR -cnfDN1d/wZQeQ2Ft7Vo5f1fQG/yW9F2tjBiZVSdlWXpfH6ghSad2/pJny7v8GfDKDqY9kG3p0Qbe -35oCDP4INmUBAd8MgrkOCz8VpGweuP4FBszoMOzKg3wRWRfOVEbCitmUGyAoMICAgINx9EVlAQao -CAgwgICDDogisIMOyDCDCBhBhAQYwgYQZwgccoMPGLoNeyPwZBloRb2QSMDMg2ZsIMogg1k+h0FM -v6+D/Of+R1Wdq6/oo2wiMt6IrLoNVRhRGW9UVsiCKwgwgwgygwgwgINZJBBsv1z6MpMrEOfanPPM -vpb1b4MuV0u1sNc8myyzRXK2kfUT/Q66acsakMaOtztFM/pE2G/zn/xLWpLNr0IsuTaRmWkbsqg6 -DjeQX+3C1YH+eX6/wD/6rdsJMudrNTLaxJJmOv8AH3L/ADf8a6ObuHq6nZEBiBhB+RM45d2/P1QW -w6C2McW6fL6N+hBI2PX+BQFUG9VFbfigMg2woDvhkGj/ABVRgmz/AIFRoPq6Df2yorHtj4KoIp+s -ojLP1RWzOg3ZQZ9lBrl1FMqh1QMug3D0/Sg2QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQRWbVWpAdi1MEFeNsyTS -kwALZx1InZmQYK5TG2FMp42tyAUsddzFpCAHZiMQzycWcmZ3SPMSji2urmnGCK5BJOTyCMQSARu8 -BMMrMLPn+jJ2Yvg/qkYk4eXX5sVpSZpFRW12zo7LXwbGlL3adgGkhlwQZB/fBsJN+llq6KZm2nGn -Zglq2q1uvHZqzBYryixRTRExgQv6OJDlnZJigRWq0ss0MUwSS13YZ4xJnKMiFiZjZuou4uztn2U2 -VEiAg5Nvy3xinParWNrVG3SiOe1TaUDsBHGHcMngF3l6B830+ibK7PKPOR70W7ZdSKWOWIJY35Ry -CxA/plibLeqt1sxNJS26JisbWyiiAgIIq1uraAjrTRzgBlEZRkxsxxvxMHcXfBCTYdvZNlTglQEB -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 -gkD6gLD9CH3Z0nKuw20ZrWq1qvHZqyhPXlFiimiJjAhf0cSHLOysxQSKAgIK02ypQ7Ctr5JONy2E -sleLiT8hh49x+TNxbj3B9XSMa8CcIrxp559CygICAgICAgICAgICAgICAgICAgICAgICAgICAgIC -AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgION5nsL2u8V2l6g/G3Xrmc -R45cMN1k4v68GyWPwUwmYicIm62J6JmInuWK40is0mkb5phHXLx/kwjrquz19C/Zu07vjuxtWgs2 -JLeCjABhmE5SNwaRpDbiLsL46N0V1MromKcs29VZnDu27uLWh71k1xm7tjfwph2tNtJd00FiGnet -8bfjF66by2JZHCzVGFo5YeRYhfEz9I+LenToumvnqcLre+bq+aHLwsf0p34T2W+XnN1sdx48xy62 -3ZszWtBZvSNamksM1iucAtMAydwY8DOTkICwvj6VdSI5r4yiL7I6IuuuiceiOpnSmZtsuzmYu66W -xMd+HGubt+JU9/X2wzTzwfsu1UeRoW2tnaSSysYOE8b2YYe2HEiYmB+PUejJdSImJzrGzLOvHdnu -IrNJ8p9H7XK8mtWNLutvqKhPHL5dHC+rJv1bhENS0Tf5kJRy/od1zssi+OScou/kmt135buu6HW6 -7kmNThT+KPd7a06LUVvlU1HkloL9ija8ZJq2mpxTyRwxxw14yrCdcSaOfvkX/wBwSznA4wt88zy3 -Zzffj+OlOHs0nfjXczZZETyThbbbGP8ADWbuqa8PZ6Vye92rXl+z2dq+0GreLs1q1iSNou7Qi59s -GIQzykd255ES+bo/VZiPZimMzfNvfbTy3LF2MTOyyLp6pvr5striW9hvtZftUCmlpwsOqtFD+07G -xkjaTYhHIRzTsJxscb4IGJwx+lb06TdSdl8Rlvtuw44xGbN2Vd9l89lMeGc5eh293a3F7yPyHXan -YM0kFfU4qlZKAXI5rBTwhIHJ4ZZohZuQjy9PwdYs92JnH257OS3zTjTpavwmNnsf6vKHm9luNpBs -aBaitdju0G2cF+G5O9+esDDSOc60hnL9w4RnzASP6untxWraYzM+zNueX36eeM8cMcUpNKRHtRfF -I2TPJdMfsw6s3U8kvxyUrRaK1bsNqNVFabZzbexVhYZBkOGZmBpfupC45LujwfDD8Vi+Zt5rqUpd -TfjSMIjdjHGV06Xctudcd22mM7Mpw2bkhXttDcg221msS1Ls1SOjsKV4ghrSTRxi0NqhyCIh7zk5 -lgy4l+rjpu+2k3Wxhd7eM4xhzdlIjthzsurbbdOMUtrGU407azPfk5tzbb/SVoqVl7Me6thB9zsT -2ck+vmgOzFFNZAyaUqbu8jM3GFmFid2zx6WIi6aRFIrlt926Yiu2s203zwqszMRzZzSaTsziuHCJ -rt68p9j4y2y09m5HurlatSmKsOvqHsp9hKE0rmDs89yOGXEzsPbD5urFj4KTSYiNtZ2bKVpxpjPQ -tJz2U9OfoesXNoQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE -BAQEBAQEBAQEBAQEBAQEBAQEBAdmdsP1Z/VkHPqeO+P04LNeprKlevcy1uGKCMAmYmdn7giLMeWd -26pM1imwjCa7U82r1k/9dUhl/oTrfPGBf0EmOcXVvoLi3IfR8JM1rXaRhSmzLg3alTaaOdoI+9FG -8MUvAeQxE4uQCWMsLuA5ZunRlZmteOaRGXBBrtJptY8r62hWovYLlO9aEIuZN7nwYeT9fdKzSmxa -Y12p5qNKeeCxPXjlnquRVpTASOIiHiTxk7ZF3F8Pj2UjAnKiCzpNLavQ7CzQrT363/l7ckMZzR4f -PySEzkP6HViaZE44Sm+wo5sP9vFm3/5t+A/0uBYP6Tp8/wAjcevt0UphTYtca7VSt4145VgOvW1V -OCCWN4ZIYq8QAUZO7kBCIszi7vl2Vma5+VEjDGGB8X8aGqdQdTSGrKAxyV2rxNGQRu5ABBx4uIuZ -Oze2XSZmcyIonqabT0xgGpRr1hqiYVRiiAGiGR2cxj4s3FicW5Y9UmZlIiFc/FfFzeIj09IngYxh -cq0TuDSu5SMOR+Xm5O5Y9cqeqnVuX1169/S3Dxvx0Lw3w1dQbwj2xttBE0zBx48Wk48scemM+itZ -x45pTLgzS8c8eox2I6WrqVY7fS0EMEcYys+W/pGEW5+r+qkzWKbFjCa7Sv474/WqhUraypDVjlGx -HXjgjCMZhfIyiDCwsbO2WL1V5pw4JSMeLoKKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg -ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg0nngrwSTzyDDBELnLLI7CAALZIiJ -+jMzerukysRXCFHV+S+ObaQ4tVtaewkjblIFWxFMQi74yTRkWGWptmlaM80OisqjO1WCxHWOYBsT -MRQwuTMZiGObiL9XYeTZx6ZSBIpM0io5ur8k0u1OMKFjvFLWjug3CQcwSkQAfziPqUZdPVa5Z7Kd -8VjuJwmnTH4cJdJQEHOm8j8eg2I6ybaVItkbsw0TniGd3L6WaJy59fborbEzkThmuVrdW0BHWmjn -ADKIyjJjZjjfiYO4u+CEmw7eymypwa3rtWhSsXrZ9urVjOaeTDlxjjFyJ8Czu+Gb2ZSZottszNIZ -O7UjpvdlmCKoMfeOeR2ABjxy5E5Y4tjr1Wro5ZpLNk80RMbUoGJixg7EBMziTPlnZ/R2dJihE1xh -lRRAQEEclqtHNFBJKATTuTQRETMRuLci4C/UsN1fCQSjl2OvhtRVJrUUdufLwVzMRkNm9eAO/Iv0 -JGJOCczAAIzJhAWdyInwzM3q7u6TIqNutO+vLZNervrgy53WlDsszPh8yZ4+v4pOGZGOTfXbPW7K -s1rXW4btUncWnryBLG7t6tyByborMTGaRMTksqKIK2y2NLW0J792TtVKwvJNJgi4i3vgWIn/AEMk -YzEb5iO3CDZM7sew2Ozpa6o9u5J2q7HHG58SL5ppBjBsCzv1M2ZIisxG2TZXdFezFZQVrmypUpKs -dmTtndmatWbiT8pXAjYflZ8fLGT5fokYzTywJyr5YzTzysoCAgICAgO7M2X6M3q6Dl1vK/FrP3H2 -24oz/aAUlvt2YT7QB9RScSfgze7urTCuw202umBiYsYOxATM4kz5Z2f0dnSYokTXGGVFRHbqhZjq -nNGNmYSOKByZpDEMcyEXfLsPJs49MpGJKVBV2W21Wrr/AHGzuwUa+ePesyhCGX9uRuLZTgUb0dhQ -2FYbVCzFbrH9E8BjJG+PgQO7OrMTGaRMSnUUQR2LFetBJYsyhDXhFzlmkJgABFsuRE+GZmb3dJlY -iqGntdfcsWq9WZpZaZAFlmZ8C8kYyhgnbiWQNnyLurSaV407Ga+avVj6lpRVDa7/AEOoaN9tsquv -abPZe1NHBz445ce4Q5xls4SMcDZVrqvI/HtuUganaVNgcTM8o1Z4pnBi9HJoyLGce6s2ynNGToqK -jitVpZZoYpgklruwzxiTOUZELEzGzdRdxdnbPsmyokQEFabZUodhW18knG5bCWSvFxJ+Qw8e4/Jm -4tx7g+rpGNeBOEV4088+hZQRxWq0ss0MUwSS13YZ4xJnKMiFiZjZuou4uztn2TZUQ29pRqWKlaeT -jYvG8dWIRIyMhFyJ8CxYEWbqT9G93SMZpwqThFUVDyDQ7GzLV1+yq3LMHWeCvPHKYdcfOIE7j1+K -tJpXYThNNqeDY6+xYmrQWopbNfDWIAMSON39OYs+R/SpGVSUG13+h1DRvttlV17TZ7L2po4OfHHL -j3CHOMtnCRjgbKsWfINJWqfdy3oex9vJbAgNjc4IR5SSRiHIjEWds8WdJwz2LbHNSm3BJQ2+uvy2 -IqkvckqPGNgeJDxeWMZQ+pmzkDZ+i1Nsx2zHXGbMXRNOMV6p/YuLKqtnaUa1yrTnk7di65jVZxLi -ZRjzIeeODFxZ3ZnfL4fHo6RjNOFScIqtICAgIObsvJvG9XYCts9rTo2ZRYo4LNiKEyF3dmcRMhd2 -y2FYiuROEVl0RISFiF2cXbLO3VnZ1BpYsV60ElizKENeEXOWaQmAAEWy5ET4ZmZvd0mViKtwMTFj -B2ICZnEmfLOz+js6sxRmJrjDKiiAgO7Mzu74Zuru6Cnrdzp9oMh629XvDCXCUq0oTMBfzScHLD/m -rMYV2JXGiSnsdfdaR6dqKy0RPHK8JjJxNvUS4u+H/BSmFV20Lex19N4mt2oqzzl24GlMQczf9UeT -tyf8GSMZoTlUt7HX03ia3airPOXbgaUxBzN/1R5O3J/wZIxmhOVVhAQcTzn/AOFb/wD/AB1v/uDX -PUy7PO66H9S3ph87317yPSVtNs5J616+OnthrBqwnXKs328chzzAclrvCIxs2flZn/VfPT16lPqX -xvmKzw54iejOtccnl0f6Vk57o3zyXU6d1MM83TPW+aNH/wCCvVoa16GMxrPurduW0QzRk7wTzQRl -X7kLmGYumXHDN6rE0iaTGU5ZbLtuc7Jx+GccZatmZisbs+zZl+3hCvr6uv2fmGohmbaVJqn7SrWK -9jZWZDjmjCrKwR2I53KQHE+XUsv6F9LM105xm6Pg3br6Yx5bC+PZ5Z+ONu+y6fLd142NNL5ftJx2 -7TQVzi2hwWyl2llhGGOyURVX13Y+2Y3i6A/Pk74Ll1XPCNOJn71ld9Zm3upduypTFq+s3XRH3Zw2 -YVz41jz4UwiPOa7Z7HX6vXy0SGM5tNp6805yPAMcU1+cDJ5mCV42dn482F+Oc+y6xFZpvmz/AOOZ -jtmnTkupNKzti7V/PZ5orPVlOT0c1TyansNfQt3nqUbmzrAFSrtLV6wIFWtPMx2LEcMzRyvGDiOX -w7O4u3TGbaTMRPz8PuxTLbE17WcYtmY3W8fvxj2TR6jwyWcZd9rjnlng1mxevUKxIc0rRHWhn4lL -I5SHgpiw5O74UnGy2ds17rro80E4XzGz2e+HDmK74tWksAVHd+PXdm0jg+RuDNdtN9JN3Y7BRyH0 -bAPhvXopp48lk9ET319c9a6n3ro646IpMeiI6nKi2fkew3LacJHnry29wYDNsbOuKQq9xgCILFeO -aXEUb5aMXFsfgKacVtid1sd911Z45R0V4rqzS6YjbMfksmnCtZnjTPOvo70e1j/dbt4trYitXY9f -fA5oJXnFxEZWAXlIInMxBmEycWyTOsa8xSKfL24VnrdPDRMakfvelxNrJb09OSLW7G3L9z43et2+ -5YkleKWCKP7exFksQuTmbM0fEXx6dF115xvjdMds3Th1x5nLwkRMac75iOqm7hh24rA2NnV29a/t -Dt2qFqzTgo3aN82CApRjjaCzRcgjPlLnmeDLBe2Om6RzzbGdb+iac09VIjthyif9uLvlt6caY8az -P2Oz51XG4VPW1JrUe7vc46hVrlusEMTYea1KFeWITaJnbjy9ScR91wsit3Db0euco7crXeZpbXs4 -z6ozn1y4W22NrVeMeeQvsrAya0QioWJ7BvMGaELRuMhFy5HJl8t6ln3XS2eabJpnqf64w7O4i2k0 -/wCP/wBWPaj30myebym9Hs7sE+tua4KAxWDGKJpYq3c/ocvGfPuPkZBIffGcqaf3eOpNvVWIYr7P -Rpc3X7fqem8bGap5RvdW1mxPTgip2IRszSWCA52laTicrkTCXbZ+OcN7MykY2dF0x1ctk+mS7C+O -NtevmuhFvqFEPPvGLwV4huyvciltMAtKQDWJxAjxycWd+jZTSmk3Rvsn82masVi2fnj8uo4G9Y/2 -R+8LvY/aP3Mf2P8A+k/8rB9lw9/6/PDH6+VdLLT/AOzHp5//AEU/hav966uX0+7lur/NV3vPbkEm -maoE8RlXu6wtvBzFyjqyW4+byiz5ECEXzno7ZTT/AKls7Oae3lnl668vczj9Oa+9yf8A8u6qOO3r -qXlXlFq4QDqoa+uOdybkH3Y91+g4fMvDs4Zmz9OPZS2aWceeafhty6697V0Vvj9zH8V32rviGvvN -Y2u8uwvTl3cwTR0CxyihijaKPu46d02bkfw6D7K05bYt21mZ6Z2dVOuapM1urspER1Vx7+yjgeRb -q5U0f7wpCvSV5ahM1A3lICi7lGF4+y+WcOUjvx4+pfimljyf9lP54w7O5uY9qf8Arr+ZX382waLz -bZhsLkc+k7M+tjjsShDGYUYpnZ4hJgMTL6hNnH8Mq6f3eOpTq5rY9LnGMRH/AB16/bx7vWg8uJtl -oPMLuxuzxTa6X7SnTCzLBAEfaiKPnCBjHK8xSO+ZGL4N6JpRSdOYznUju1KeaK96TMzF1co0/PZW -vbW3q6XqP3lDz8OmHuPFys0G7o4Yhzdh+Zss7Zb8WWbP6ln70NR7l37l35Zec8i2W10dza67U25p -qP8A+zO/Nctyl9qduwcczfdSNYkiE4xD2fhnkzMlntUifjmOn2K07acZrSq3ezFYz5a/zRFadE3c -PZyzrS38HmerOk0IVrFgdlXl1OsPY2NgYyvTtsbyWLUcMvA8C4i74yz9Wz0sTFY30v2bOWO2Yxns -hIjCa5ezt289vZudjWWIdvc02uLa3pNbapWrssz2Ja1me6EwDJEZwmBxdjkX9CBMzfDAqzEVuplb -FvL0Tze1xyjGd+WTPNNIrnMzzcJilLeG3p5c86wa6xe3F7x+lY2VwqRtuo3mgnkrlahqWI4q0hyQ -uBO/DrzF2d/jh3znOJnb9O2euaYt3YViP7lP5bpmOqcOrez4zY2Qf2QvS7G3Zn2v3UF5p5jOM44o -JTj/AKLpEJC8Q/Ow8n/Wd0unCf8Aq5uv2Oz3pwyZvinVqTb1e325RnireM1rtqHxH7jbbKT9tUrJ -7PNyb+l7QgUbC7FmJxd/qi4m/wCsT9Vuac0xs5Inr9n15ZNXznP/ACTb1e36vUjp7PyTYtpNQ0j2 -IpIti/KbY2ddJOdS52Ix+6rRTTGUcTZccty+p84Wbfax+TTn8Ue1NOmnCK5Ys3YViPjvjsnCK9v4 -c8629ZS3dzZ2dbudtOZ1NQMgvr707RtI1u0EZvKDVyOQIwETdxbk7fMzrGpdSy+6M45e3lxwypM4 -0ydLLfattnKZu7K2UiueFel6KkT+Q/u0rPtLf2xbbVRtbuM4hxKxAzEfXAt1JdPE2RGpMRsuw7cI -c/D3zSJny4uDvNvvNRSt6e+FOS22lvz6zba7nBLENaIW+aEubwsTkPEgldst6LGpdzRdMYTFK9dz -poW8t1kZ280R3eXbxUhseYbeXayVbENezqziCrPY2lqoMAfbxSjLNUjgkhnCVyInKUnz1ZuOF3pE -XV+ea7cIumKcPZ68a1eeyZm2I+SO+3PjSerDLOZ9N55e20R6ShT4jFsrRQWTK1LRYsQmYRNZhjmk -jeQx6OLZfHHLZXCyK30+WZ747cJmacODrMzFldtYjtr6aRXj1x5Xa2PKtVFYq2thwePX7yWtFWuz -2yhGOvXOIZbEoQyHJGZmQEQ8mF26pdMTE7+WOH3/AFYS6aVvtW7pvt/LdXqmYq9LpWs0fKtbVG5Z -sQ7PUS27Q2Z5J2eeCSuIyA0jkMeWnLIgwj+C7XxFdSPhmKdfPXzQ81kzy2Xbbq17I8vO3c2b9417 -viEl8dXC+gjnLgD/ADy/ctGTCbi7l2u47C78cdHXGz3bqZ82P7tIp1V5ut2v962vu0n8VceulKdf -F52Tfb3c78dNFr61HElxrUEG0sUgs2qzwtyG3WrDObiEmeHEct1fPFastiYrsphw9q+Jw6bf5ssW -brpjDjSfw23Rjsz/AJc98+spbu5s7Ot3O2nM6moGQX196do2ka3aCM3lBq5HIEYCJu4tydvmZ1jU -upZfdGccvby44ZUmcaZOllvtW2zlM3dlbKRXPCvSp0Np5X5K9SHmPcbS0Lsf/wC0rOrJ5bMZPLYZ -q0E3eYTZmcTfiP8AN+ZdtWzluvphS+YjbSKRMYT19NODjp3YWxOOHbjMZ9FN2e3ClgJNzJqPL7t7 -aTS7HV0geCSnZlCqMxakCklhEHjYhKQnMeTYZ/mZmfquerMRZM2xT27o6q24O2jbPPZbO63813lK -vR2F4PMbcFuaSro7dui1q/DI4yHb+wrvBBKbOxRxyuz5Jn+YsB0Z+vWIisx899I2T+yMYjb1UnhW -eS2f+O2s8K3enOdnfFltvefd6vaa9546Wx20tNyubKaQphZ5RMB17icEQCYfK4uxizNlurrlp7I+ -KyZ/lrEzOzZlhsdNSc/lmI/mi2cNu3Ppe1KbdyabYPtalapI0MnbGrZktC48Hy5FJBW4v+GHXHxF -Pp3dE+Z00K/Ujph5Ce9tYvEPCKFLiMWyirwWTO1JRYuNNzji+5hjmkjeQh6cGy+OOWyvZrxXXuj9 -6e+O3CZmnCux5tGaaMT0R1Y+mkdfXEMtDzCBnC5K1/X0jsHJqqG4sBdhiIYijI7ZjVln7b9z5ZCH -oQ5csLjN9sRWd2dON2zLKkfwzhjLtFszhG2ct+EbenHrU9aFQtb5tvaFu+M7UQs05ZbdkZMTamMx -kkiaTtvJn0Lj8rt8uMK6sTZZMbee6O+3y6F0aX6lk7OW3810dflOeLpX9g2imjke/fs1bWksW9qD -WTmlAw7Iwzw90nGAjeQ2bjxD3x8q1qR7V9sRhzWxHTN0xSu6e6mDnpT7Fl857eiLazhww6aubY2m -/wBXtZ9UU50KdkNeVl32U2ylrR2LbwyS96yLFCRg/HAk4t9TOpZEXTSfi6Pu3TSueMxHHHiXTNsc -0fDPnsitMsIm6d2HBc81/aGl3OmHRHNakgq7DvlLMduxBAT1Xmkj7xGcsghkgAz/AIvlWbJiZu5s -LeWKz/F5VnZm6THsxTGeaKRvnlv8t3Rm2sS7Xabc9Vp7P3Ouq62rY1tiXb26UsjTdzlac4IZys9R -FnaQuLfzfmVmJ9qZwmLqb6YRMYZb8ca07cRMezGcTFema4xXhhhFKV6KXP3f6wv7Qby5etHY2gHT -exJBasFVkOWhC8kgwubREBHy4O4dG6DjGFqZiLZ5YpHPd6Em2eaK58kee+PLjjm7kLBJ+8C9JO7Z -p6qs1bl6CNieZ5ybPx7EefyXO2nJdPzY9ERh55dLory/xf6fLrcvZhJF5dpSiOvPDJWuBo4Kgdp6 -7dgXeSV2KRpoy4sLOPARd26E+HbN3Ny3x9/kn80YdPqmlCsTyzPu88flux8/bm5/j+Psv3c9j/zf -bl+94/Xx+yP7ruf/ANVw55/Xx7r0XU+pdT3eTDo5rOXuycpryY+99Tv9uvpdPy6TZx+c+Llra8Fm -12NkzRWZjrx8eMGX5hFYfP4cFy0s7/3Y/M6X+7H78flved8h1l/RauTXSWel3WeRXbdaByGs0krR -yCAA/qMfcdhd2+L9M4WJmOWY+Gy380eXRg66Xvxd8Wpb+W71VnjihO9tYtzYoUuIxbLYU4LJnako -sXHURnHF9zDHNJG8hD04Nl8cctleiYrdMfNqT329uEzNOFdjy2zTTtn5LI6q3+mkdfXHUpUd+/kO -t0222EgVZP2kTVaOxszGMQBVKOKe0415yMDkMhd/mYXZuT9c4tpNeFvR9/1YcXSaxHTdH5bq9sxX -yhKVqzY/dsVmxKc93W3zGnPIWZSOlsihg5G/VyIQYCd/XL59VI97TnbdyV/iiIu7plJwjUjdz06o -mY7Jp2PQ+bnWHWRATWJb88rQ6yrVt2aZS2DZ2FjOtJEXbFsmeejCzuucRMzERn5o2z1fZtbwpMzl -HlEdf2zk4+mG1otpsNbe2s9iKlpa05Wbc8hs8ry2XmmZ5SJ268Wzno3Fs9GV1rq6d8xsnDf7uHb5 -10rfasrtrXtt8zz9OfZ7DUhNPtL4nW8Rp3w7VqaPlbdpn75uBM5l8jZ5Pgv1mfouviZ5J1Jj7t8U -7J2M+Hjm+nE/em6vbb63pvHTuQeS6+I7tmyGy0z3bg2JSkF7ASRMxxg/yRZaUsjGIj+Ct9sRN9sZ -WzbTr56+aHGy6Ztsu23RNf5fX60G0fcv+8q1Hq6tS0UujhCZrsxwgIvambPEIZ+5+Ivx/Ncbbeay -+Jym6Pyy73TSbJ/e/wBCvqtNb1W2HSjctWotJoqk1etHNLBFLZCefBOEZN0Lgw8XfDj0LOFrU1PZ -vujOKU2/dny6cS22K2xOEXTfWnTb5q4OKE/l5+Kf2hG9FCFrWW5bc47W1YkmkenIY9iocEUNaWKY -WfERNxZibqta0RbWIy2dsY1zpTqmuSaFb7rZmMeaK+m2nlOGcuwNjZ1dvWv7Q7dqhas04KN2jfNg -gKUY42gs0XIIz5S55ngywXtjpukc82xnW/omnNPVSI7YcYn/AG4u+W3pxpjxrM/Y7PmW6l8c2NHe -SyyFrCinp26zETh3nDvVjYPTmRxPFn1fmzLhFZmbY966PZ/ejZ1xM/heikTETOEWzj+7OFeqadsu -Rr6l6a1a1vkG2t15NXra9oZIrUsGZbHdOzYIgIe4Ecg8BA8gLN9PVa1Ji226637s8sdEW20mm+6a -1306WLK3XWxMe97VOM3T7Nflinb0IdZLs/ILMT7G9cgI/HKV04qs81RvuZDmzLxiIHZ/lbp6P6Oz -4ZNf2I1JjO2cOHsy1p0mbIziZu64ibaPQVILvlX7s60U9jtXNxqou7Z45ZpJ4WcicWceju/VlvxN -vLqTT7t3mmtPQ5+Hu9nHdMcd1ena8l5K/kklncjHWq1rtLxu1DYHXTSTNkzB67E5RQOJ8AlcAw+G -9+qzE2zzTPuzfp1r0zzdPszHN1N2RMTZEZxF1OyKfzZdE7np4JKUXmuvlpFGFANFKVkwdmiGJpoX -quT+jCw93h+HJWZp9Sbt9vb7dft6nOyK26cR83mt9NFeaxr7Pku/sW5YZtZNoq0lKZyE4jrEVh7B -AWXFxf5OTt7cVx1YmNK+Pvc3X7scvfzU41d7JrqadMse3mivdRyvGml42f7Q4z/ZXX5+49eHCb7z -PL/K4c/+rldvGUpq0z5583s9/NTjVy8LnpbqT+aP9PK9p4Z95/ZDSfe8vvPsK33Hc+vudoeXLPvn -1W/E0+pdTfLnoe5DsLg7NJ4ILEEkE8YzQSi4SxSMxAYE2CEhfo7O3qzpMLE0xhS1fjnj2peR9Vq6 -mveVmaV6sEULmzejF2xHP6VZumYpLMREYo4/FfF469itHp6QVrbsVuAa0TBKTejyCw4N/wA1K4RG -5dtdstj8a8cOjBQPVUyo1i7lao9eJ4ozbL8gj48Rfr6syvNNa1xhOWKTGyUv7E0v7T/av7PrftPH -H7/sx9/jjGO7jnjHT1UjCJjZKzjnsZi02nijeOKjXjjeFqzgMQMzwM7u0WGb6G5P8vp1ScVrjXyx -z7WlPQaGlDFBT1tWtDDL34YoYY4xCZ2ce4IiLMx8XduTdVeaWaR2rcNWtAcxwwhEdg+5OQCwvIfF -g5m7fUXEWbL+zKbKLxUovGvHYti+zi1VOPZO7u94a8TT5Lo791h59fzViZiKQTjjLa1oNFbqlUt6 -2rYqnIU515YYzjKU3cikcCFxcnd3dy9VN3ArnxWXpU3pvSeCN6bx9l6zgPaeN248OGOPHj0xjCTN -cy3DLBVq+O+P1IbMFXWVK8N1na5FFBGAzM7OztKIizHlndvmVmaxSciMJrGYHj2gDYDsg1lQdiLc -RutBG07DjjhpGHnjHT1TmnHilIw4MbTxrx3byBJtdVT2EkTOMZ2q8UxCLvl2F5BLDKRhNYWccGsn -i3jMrg8uopG8UH2sTlXifjXxx7I5HpHjpx9Frmmta4ylMKLR6vWSNO0lSE2skB2WKMH7hRszAR5b -5nFgHGfTDLNfX171p5qdW7oxShVrBYksBEA2JmEZpmFmMxDPBiJursPJ8Z9MoKN7xjxq/dG9e1NK -3dDiwWp68Uko8HyODIXJuL+nVW2ZtywS6InPFal12vmtRW5qsUluDLQWDASkBn9eBu3If0KRgs4s -tr6DTzztWiae0Ix2ZeA85QBnYRkLGSYeT4Z/imymw4q8nj+hk1r6uTW1T1rvyeiUEbwO7Pyz2nHh -9XX0SZr1HpNV49oNR3f2Trauv73HvfawRwc+OePLtiOccnxlam6ZilUi2C74/ob1h7N3W1bVl43g -eeaCOQ3iLLFHyIXfi+eo+ikTTJZxTSavWSBajkqQnHdbFwCjB2mZhYMSs7fP8jMPze3RSJ89evef -s6kFzx3x+7Z+6u6ypZs8O135oI5JO2/6nIhd+PX0ViZjJJjCmxat0qdyuVa3BHZrk7OUMoCYO4ux -DkSZ26EzOyixggp6TTUqR0adCtWpS8nkqwwhHEXNsFyAWYX5e/RLprmRhNYza09BoaUMUFPW1a0M -MvfhihhjjEJnZx7giIszHxd25N1V5pSkdrFrx3x+3DLBb1lSxDPL9xNFLBGYnNhm7pCQuxHhscn6 -qRs4L6VkaFETgkGvEJ1QeKsbALPGBYYgB8fKL8WyzfBlZnOd6Uwo1j1mtjGuMdSEBqOT1WGMWaJy -ZxJ48N8uWJ2fCnqp1bujCFn01695DrNbA1doakMTVBcKjBGI9oSbBDHhvkZ8dWZWp669e/vlDZ0G -itUmoWtbVnosbyNVlhjOLmTuTlwIXHk5E75x7qbuBv4p4tdr4S5xVYYy7Q1+QRiL9kMuMWWb6B5P -gfRJxrXbnxIwpTY2CjSCmNEK8Y0hjaEarALRNGzcWBgxx446Ywl082eJbhkp0vGfG6MFiClqadWC -2LhbihrxRhKLs7OMgiLMbYd/VWZmYpJGE1jNJPodHYt17k+uqzW6jM1SxJDGUkTD6ds3bkGPwSLp -rM7ZSkUpshYuUqV6sdW7XjtVZWxJBMAyRk3rghJnZ1KNRKuGg0QV46wa6qNaGOSCKBoY2AIpsd2M -RYcMJ4+YW6P7qzNc0jDLp61hqVNp452gjaeGN4YpWAeYRk4uQCWMsLuA5ZvgyVnHilIpEbkWy1Gp -2kDV9nSgvQM/JobMQTBn48TYmyoqKXxzx6bXBrJtXUk10bs8dI4IigF29HaNx4N/ArMzM1nMjCKQ -sRa7Xwlziqwxl2hr8gjEX7IZcYss30DyfA+ik41rtz4kYUpsVrPjnj1qCtXtaupPXpMzU4ZYIjCF -hZmZohIXYMMzfSrzTXm270phTYslrteQ2RKrCQ3WxcZ4xdpm4dvEvT5/kbj83t0U2UWJpNWkmn1E -kNiCSjXOG3x+6iKIHGXgLCPcF2wXERZmz7MrWe+vXv6SIp2U6t3QiDx3x+O4d4NZUC7IbSyWhgja -UpBzxNzYeTk2Xw+UiaZJML5gBgQGLEBM7ELtlnZ+js7OszFcJaiaK82r1k1D9nTVIZNfwGP7M4wK -HgOOI9t248Wx0bCt01ms5pbhkqS+KeLTVq9WbT0ZK1TP2kB1oSCLL5fti44Dr8Feaa12pSKU2I99 -4rqdtVuM9aCLY2qc1KPZPCBzRBNGQfKXylxbl9PJlmYwmN+bdk0utn4ckup8a0erpnVp0KsITgwW -+zBHG07sPF3kYW+bPX1yumpdzTO7c56dvLEb42tqnjnj1OE4amrqVoZAeKSOKCIBKMndyB2EWZxd -3y7LMzXNqMMYS09JpqQwDSoVqw1mMazQxBG0bSuzyMHFm4sbi3LHqk3TKUhDP4x41PBDBPqaUsFc -ykrxHXiIIzMuREAuOBci6u7e6RNJrGxZxrxWC1OqLYhsipQFsYweOO68QPOIPnIjJjmw9fTKRNK0 -2pMVpwyVb2kefdUttXn7FisJwWRcOYz1pME8ZfMOHExYhLrjr06pbhXdMebLzz2rOMRvicPT2+eI -S67x/Q6yWWbW62rSmn/r5K0EcRH1z87gIuX6UrNKbCYxrtTwa7X17E1mCrFFZsYexOACJyO3pzJm -yX6VIyoS3kqVZLEVmSGM7MDEMExCzmDSY5sBO2R5cWzj1SMBpa12vtvm1VisO0ckLPLGJ/0czM0g -fMz/ACmwtyb390WJRy6XTzQ2IJaFeSC1x+6iOICCXgLCPcF2wXERZmz7Mk459PWkYZbqdW5mrp9T -UGuNWlXrjUEwqtFEANEEjs5jHxZuLE4tlm9VZunNIiMlK/45BakoRRuFbWVLL3Z6MUTC007E8gER -M7MzNM/cL5ckWHz65WzSa7opHDZ5sIW7GJjfn5cdvDDat7TRaTbxhHtdfW2EcTuUQWoY5mF3bDuL -SMWHU21NlEI+LeMDHViHUUhjouT0gavEwwub8ieJuPycn6vxVmZnPo6iIosR6fUxxvHHSrhG8A1H -AYgZvtwzxhwzf1bcnwPp1Uumta7cyMKU2JQo0gljmCvGM0UfYikYBYhid2fti7NlhyLdPTorWceP -l6UiIw4MtUqtae20MbWyBoiscW7jxs7kwOeOXFid3wpCstVrNZK00INaIGiKfi3ceMXchBy9eLOT -uzfigpj474+E9qcNZUGe8JR3ZWgjY5wP6hlLjk2L3Yk2U2Fca7QPHtAGwHZBrKg7EW4jdaCNp2HH -HDSMPPGOnqrzTjxSkYcFuzUq2ou1ahjniYhNo5RYx5ATEBYJnbIkzOz+zqRnVVfY6TTbMoS2VCtd -KuXKB7EMcrxl8Q5sXF+nskTSaxmTjFNix9nU752OxH9xJG0JzcR5lGLu7A5Yy4s5O+PxSmExvI2c -GI6VOOoNKOCMKYRtCFYQFomjZuLAwM3Hjx6Ywl3tVrjUtwywR67VavWVvtdbTgo1suXYrRhEHJ/V -+IMLZdWZmc0iIjIr6rV1opoa9OCGKw5FYjjjARkcmwTmzMzFn3ypOMUnJqJxrtanptOcNaA6NcoK -XH7OJ4gcYeDYHtDjAcWbpxV5prXazSKU2JLeu19x4nt1YrLwF3IHlATcDb9YeTPxf8WUjCarOVFh -AQaTzwV4JJ55BhgiFzllkdhAAFskRE/RmZvV3SZWIrhDna3yvxbaWftdZuKN6zxc+xWswzHxb1Li -BE+Gytcs7meaN7qLKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg -ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICDiec//AArf/wD463/3BrnqZdnnddD+ -pb0w8/O3llX93t2zNsaxhHpjOo1KrNWsRyNBkC7z2p8uLN7A3Xr0Xp8RMRfNfj/1YuHg4rFm6keZ -X8u3lkLUkdO+YkPi2zuOEMzs7SN2OzPgX+pvm4H+eFjUin1OF1nnur6GvD4/SrtntwhZCmUu41On -tX7wUblCW/LI1yxHLYtD2QcWmAwMBACc+3G4i+c4XS+I57/lpTrm6s8aUiMcq9DlZdM2WT8WfVEU -jrxnfPL0ut4bugm01YLt4ZppbNytr5ZjFpbUVaeQYzH07hdoGJ3Fuv1LE4xE7ZtiZ7se/vdJik3b -oup9nVNY6npFhRAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ -EBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBBpPBBYgkgnjGaCUXCWKRmIDAmwQkL9HZ29WdJh -YmmMObrvEvFdZO9jXaajSsOLg81etDEbgXqPIBF8P8FZumYozyxWrev4x43WjKKtqacEZhJEYR14 -gEo5sd0HYRbIycW5N746pMzMU8sF212pr+m1GxqjU2FGvcqA7OFexEEsbOLYZ2A2cWwpM412kYRS -Mkj67XuVY3qwuVLP2Zdscw5Hg/afHyZD5fl9uitZrXalMKbFhRRAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEBAQEBAQEBAQEBAQEBAQEBBpPPBXgknnkGGCIXOWWR2EAAWyRET9GZm9XdJlYiuEI5r9GGsNqax -FHWNwYJzMRB3kdhDBO+H5OTMPxVpNabWYmJiuxOoqhqt7rdqU7UDklCuXApnhmCEny4v2pTAY5cO -Ls7xkWFaYRO8nCaL6gICAghu3qVGsdq7Yiq1o+sk8xjHGLfiROzMlViHP/tf4k1H9oftuh9hzaL7 -v7qHs9x2d2DucuPLDP0yrNswzE16l6hsdfsao29faiuVTzwsQGMsb46Pgwd2dJiYzImJyVZvJvG4 -KA7GbbU4tecjwhcOxEMLyM7s4NI5ceTcX6Z9lN3Fd/DPgqD574MYmQ+RawhjblI7Xa7sI5Ycv8/R -skzK8spzQsz+V+LV6le7PuKMNO3n7WzJZhGKXD4ftm5cSx+DpNs1ptImJiux0YZoZogmhMZIpGYo -5AdiEhfqzs7dHZ0mKZrE1Qa3aUdnXezRk71djONpeJCJFGTiTg5M3Ict0Iej+zpTCJ34m2Y3LSgI -Kmz2+p1VdrO0uwUK5EwDNZlCEHN2d2FiNxbOGfonAba/Z67ZVmta61DcrE7sM9eQZY3dvXBA7srM -TGaRMSsqKjmtVoCiGeYIinPtQMZMLnI7OXAM/UWBd8N8EjcJEFbY7Klrar27snarsccbnxIvmmkG -IGwLO/UzZkjGYjbJsmd0V7DY7Klrar27snarsccbnxIvmmkGIGwLO/UzZkjGYjbJsmd0V7FlAQVr -OypVrVSpPJwsXjOOqHEn5lGDyE2WZ2bAC79UiK4cK+aPTBM0ivGnl2LKAgIKG13+h1DRvttlV17T -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 -0bkDbJqkdTZ3ZTijEasgQyWP/CyGfIjL5my4ceTlhlrTnGZjPl3fPuyypE8eLN9s8tNnNG35Lq49 -VY3Y0b2r22raDZbSK5Odo95PrnkntyxQQVD2Hbdmw0oRszNxaXtkQM/TDLFkVjTj4ox405qds0jj -ludL5pN8/Dy067bK9lZnvpnWeCPybVb7VQXLQwa+xsYWClHsrOxkHnUtcmlmsxwyPHIYA4AXJuTP -hbtmJmm2l2z93vjGd9GJieWZ/d/Nn30bQbq5L5IbR35JKY+TvUdhlJ42jbUs/Z6Px49/rx9Of4qa -cYW8bb/zzTu7l1MJu4fT9FUU+yuXtvPWi2Vhqp+UjScq85jiFtWxHAJC/wAo91nyzehdehdVNOKx -ZXbbqfmup9i3zSb+EaffNtW0Fu8+0fxqS9aDVftuWn9y9iX7nsjrwtx1/unLvfNKb/Nz5YbjlW2O -aImfhvnri/lj+XzY7Wb55ZmI32dVbce+Ij+Lodrw7YVarbuGzsikrQ7gqVKS5ZKZ8vDCwQDJMRET -8ydmHOc/ipnZbvnm66XXeiOwmKX3bo5fy2+l65YaEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ -EBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBBpPBBYgkgnjGaCUXCWKRmIDAmwQkL9HZ29WdJh -YmmMIj1uukKsUlWEyp5+0Ioxd4cjwft5b5Pl6dPZWZrNdssxFIpsV6njvj9OCzXqaypXr3Mtbhig -jAJmJnZ+4IizHlnduqkzWKbFjCa7Vn7Cj3Hk+3i7jxdhz4Dnst17ecfR1+n0Sca1258SMKU2ZcPK -jiTeDaGzvW2VunVs14qcFOpRlrRmEPYkkMTj5ZYeknFmYWxhatvmKztma1SYikRsivfTzUdXY6TS -7PtftKhWvdh+UH3MMcvAviHNi4v09lImk1jNdlNjYtRqT2AbI6UBbGMHijuvEDzCD5yDSY5sPX0y -pGFeOfEmMuGSGt4147VjmiraqnBFYMZbAR14gGSSMuQGbCLMRCTZZ39HVrOHDImMZnesTazWzvYe -apDK9uNobTnGJd2IeXEJMt8wtzLDP06upsoVxqqy+L+MzVa1SXUUpKtN81K514ijhd3z/Rg44Dr/ -ADVeaa1rikRFKbF61UqW4HgtQx2IHcXeKUWMHcCYhfiTO3ykzO34qbarwRWtTqrcry2qcFiUoirl -JLGBk8Jvk43cmd+BY6j6Oi1y4IYPHfH68LwQaypFC7Ri8QQRiLtCbyRNxYcYjN3Ifg/Vlead7NIT -zazWzvYeapDK9uNobTnGJd2IeXEJMt8wtzLDP06upsotcaoqmh0VOCSvU11WtBMDRSxRQxgBxtnA -EIizOLcn6P8AF1bpmc/LyoRhNYYp6DQ0oYoKetq1oYZe/DFDDHGITOzj3BERZmPi7tybqnNKUjtZ -taHR2wkjta6rYCaTvzBLDGbHLw7XcJiZ8l2/l5P1x09FPL0tVZ/Yml/Zn7K+wrfsvHH7Dsx9jjnO -O1jhjPX0VumuaW4ZI5PG/HZKUFCTV1Do1SY61QoIniiJsuxRxuPEX6+rMnNNebbvTlikxslaGhQG -CWuNaIYJyMp4mAWAyld3kcxxgnN3fln1UmMKbGonGu1UDxnxsNaerDVUx1khc5KI14mgIss/IomH -g79G9lZmZpXYkYZbW5+PaA6ctE9ZUKlYcSnqvBG8RkAiIuYceJOwgLNlvRm+CTMzmRhk3h0umgYW -hoV4mCRpwYIgHjKMfaGRsN0Jo/kYvXj09E5p8uOfalI8uGSttvH6V6pNXGCqw2ZRntx2K0diKchF -hzNG/Hm+BHBZz8re3RTdwaic+LTVeKabX0yrfbQzCdn71xKIGjGcccCijZuMfbYBYMdWx656rU3T -hwr31r21lmmfH0REeh2FlRAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ -EBAQEBAQEBAQEBAQEBAQaTzwV4JJ55BhgiFzllkdhAAFskRE/RmZvV3SZWIrhCOa/RhrDamsRR1j -cGCczEQd5HYQwTvh+TkzD8VaTWm1mJiYrsTqKoare63alO1A5JQrlwKZ4ZghJ8uL9qUwGOXDi7O8 -ZFhWmETvJwmi+oKGq3ut2pTtQOSUK5cCmeGYISfLi/alMBjlw4uzvGRYVphE7ycJotVrdW0BHWmj -nADKIyjJjZjjfiYO4u+CEmw7eymypwZitVpZZoYpgklruwzxiTOUZELEzGzdRdxdnbPsmyokQEGs -kkcUZSykwRgzkZk7MIizZd3d/RmSZWIqqazd6baxnJq79a/HEXGQ6swTMJfAnByw6s2zDNYTtdpv -LPE08by1mErMfMeUYkzuLm2cizszu2VJmkV2LTGm1oGz1pw1pwtwlDddmpyjIDjM5C5i0RM+DyIu -7cfZWYmtNqRNYrsWVFVptlSh2FbXyScblsJZK8XEn5DDx7j8mbi3HuD6ukY14E4RXjTzz6ErWqz2 -SqtMD2hBpSg5N3GjJ3ETcfXi7i7M/wCCCRAQEHMseS6atsDoTTkM8Mfesm0UpQwR8SPlPOIvDDkQ -d27htlIxiZ3eXpWmUb1ytfo2nkarYineJxaVojE3FzFjHlxd8chJib8OqsxLMTCSeeCvBJPPIMME -QucssjsIAAtkiIn6MzN6u6ky1EVwhS1fkfj22Ix1W0qbAo2zINWeKZxZ/wCd2yLCs2zmzzQ6CitJ -54K8Ek88gwwRC5yyyOwgAC2SIifozM3q7pMrEVwhsBiYsYOxATM4kz5Z2f0dnVmKMxNcYZWZmkVV -W1uypbLXwbClJ3adkGkhl4kPIX9H4kwk36WVu9nM203TTswNbsqWzoQbCjJ3qlkGkgl4kPIX9H4k -wk36WVmJjM38Jp2YLKgrbLY0tbQnv3ZO1UrC8k0mCLiLe+BYif8AQyRjMRvmI7cINkzux7Fn1QQ2 -rlOoAyWp468ZmMQHKYgznI/EAZydvmInwze6RnQ2VZntVa7RvYmCFpTGKJ5CYeUhvgQHLtki9mTg -TvYG7TO1LUCeMrUIjJNXYxeQAPPEiDOWYuL4d/gmyuwbVrVa1XjsVZQnrysxRTRExgQv6OJDlnZW -YoNL12rQpWL1s+3VqxnNPJhy4xxi5E+BZ3fDN7MszNFttmZpAd+lHVC3LOEVY+HGaQmAf6V2EGyW -OpOTMzfFam2Ymm3Jm26JisZUr1J1FEBBU2u2oaqr9zdkcInMYwYAOWQzN8CARxiZmT+zCLum2i02 -tIN7p5njAbcYTSEADXlLtTtJJH3QjKGTjIEjx/NwIWLHsryzWnT3Z9jNfLu868oqtstjS1tCe/dk -7VSsLyTSYIuIt74FiJ/0MkYzEb5iO3CDZM7sexJDbqTyzRQzRyS1yYLEYExFGRCxMJsz5F3F2fr7 -JxKpUHOh8j8en2JayDaVJdkDux0gniKdnH1Z42Lm2PyViJmKxkThNJWNjsqWtqvbuydquxxxufEi -+aaQYgbAs79TNmUjGYjbJsmd0V7FlAQR2LFetBJYsyhDXhFzlmkJgABFsuRE+GZmb3dJlYircDEx -YwdiAmZxJnyzs/o7OrMUZia4wyoogIK2u2VLZVWt0pO7Xc5I2PiQ/NEbxm2CZn6GDslMp3xE9uMF -cZjdNOxAW6hbZfs5onKxyFsNJXzwIHJ5eDytJwF24v8AJnL9GduqW4+Xln+0nDy4+U/bg6CTIjrW -q1qvHYqyhPXlblFNETGBC/uJDlnZWYoJFAQRT26sBwhPNHEdg+1AJkwvIeHLgDO/zFxF3wyRnQ4o -9jsqWtqvbuydquxxxufEi+aaQYgbAs79TNmSMZiNsmyZ3RXsWUBAQEBAQEBAQEBAQEBAQEBBxPOf -/hW//wDx1v8A7g1z1Muzzuuh/Ut6YcrfWrNX931CatMcErfssWkjJwLidiASbI4fBCTs/wCC9ep/ -+xEfP63k0P6P/wCOfyuBrH8s3JftN54qztspa10y21uPhANgoTq/YBAMASPF0Amk58sFy6rhSPpx -X71ld9Z5d+yl27dTHF1vmea6n3Zw6p276xv37Mo5njcGyDWeL6ig8h19jrZLkkc+2vUu7PEQDwim -iGxIHACcu1HwF85fOF2uxmeFtnfGPTlHCK5YpfhM0233x2Th248Zpnv73j9PcXN+Wt3mzmn+11kU -jhRuztE5tdsgDlLH9uZyDGAhI+G5O3zM65XzHJfP7v5McOObWMTbHG//AEUjqr5YuZ4bqx+08MpB -dvxVNhrbk12ELtpmMo2gYOL9zMbDyfHbcf43XavtTwstn8vluS6KV/7Zjq/3PLvzWO55BbGxP3bG -x19K9tmtUINjLRtsIW3aGQJBOPmEQAQtGUgj191wi6Isi6fgjHdjdWsccOxq62t02xvjr9i3b0zj -0qmvnGvQ8w8l08t2S7FXrWaT2bNgn4T66Eu7NByKIyHLl1jfGMD0bC63xNscs0/qTbP4ra9HTuYt -pdMXR/brEb59ukZ49ueOc1TbSLymhrjshfGrRuDT49jb2tjOch3649+ErEMPbB45CE2B+D5b5VYi -OeLZ+ONmWdYnbjhnuSJmbZuj4b/y4dFODvRRR19n5RpJ9raqakKFW197LbN5qr2fuAmkjszlIUbM -0Ik2X4i/ouWdlZ2X07rJp2z3tThfHG2fPOPluetkKlHrSezKB0Qh/pppyEgKJh+YpCf5XZx6u6Xz -SZmcDTjCIjF5/wAcgn2XkVzyh4Sq0Zq0dHWxGLhJNDGZSPZkF8OLE5YjF+vHr+thaiJttmJ966az -wph27+qNhM1mKZWxPXWndFMN+Oyjj+dSFr/IeEb8H8qofsgXb1+4GcQjf82itSl/1VNKOaZsnfbd -1R7/APLytXXTbEXxnbzR1zFbP5ontVNMxxb0NFVi7n9kG2NmKD8Z8fYAzfDsTmLfkpdfP07tTbFn -L11n/wDzif4kiy2Los+7N3N1Ux/mumn7rnBP5efin9oRvRQha1luW3OO1tWJJpHpyGPYqHBFDWli -mFnxETcWYm6retEW1iMtnbGNc6U6prkaFb7rZmMeaK+m2nlOGcrflL7fVW/HrWqKe3blo3ZrliWQ -7E4Rk1TvzQRyO4uYAzkEQ8Rz7ez27ljUvicLadkc/ljsjoY0qzpWzndW2nGeS7y47Z2r+upaVvNS -2EN25aqwaKpbr2Gu2jeYAmm+YmaTEzOLM7iTOOX9MusXXclupMxSkx+WfKJai3n+nETnzeez149m -URDgW/Id7qoa9/XHNDBstXbtwtb2MuxnIQADjsnXkEooXBicsRG4v6O3Rb5famy7D3a02Vvttn2u -iZ86TfWOe2K1macfZuuiKdMRx2bXV39q347euvpdhatyBoLNzhZtzWxaVpohGxwlKVhwLkTcR4+u -GWc+aJwjm046KzdzY9BH3ZjGZi+emkWzGGG3odzxKnv6+2GaeeD9l2qjyNC21s7SSWVjBwnjezDD -2w4kTEwPx6j0ZaupETE51jZlnXjuz3MxWaT5T6P2vP7Kr9nuPKdnWs24J4txqQMht2WjaKb7R5eU -fc7btxJ26j0Ho2G6LOhOFsb77o7vW6asVmZ3aVfz+XfmteWbi9+1vJK9PYzRNWj0QC0Ers8Mk9+Q -ZcMzuwkcbjy6dWxnomlFeXjq06uW301TUw/8V09eL0vllOKn4Dva8RSmA6647FPLJObuURk+TlIz -fq/Tr09G6Ljqzh2O3h/6kdLz/k8gxaPxWahGMnkcclQtbGGO8UQgz2h6fN23gYuXt6e+F317pjVv -mPnr2XUr/Fy04vN4eInRtifkp01j0V6quF3vMpPEm8giuxxNa1tqW1OG1tWJZpHqGbdioUEUVeWK -UWfERNxZibqrqRbZMxsw/NbjzbqV4TXJ00a3zEzGNce/2aeU4Zy63nN57bz68LhnFJ4tsrUteKY2 -5Gz1+zIQgTZz8zM7+rcm9Hdc9WMNThdZ57q+hfDz/S4z6IT39Xeq6/VFrjtX9RFUKxdox7azBccj -GNwnjmOXkcYCJN23lEOq6a98W3382Ud2M1rvrvms4OWhbXTtiM578Iy3dW9P4Zci3j29pY2F0Ps5 -YI9fBLOcXGtJWikiOeEXaKU5+47uUgl16DjCxr2zbbO+eeJ6rpikbsIid+JbdzdFImOyszXbSaxu -9ly/Dgm12t8FngtWX/agnVuQSTSHA8bVJpw4wu/aBwKEcEIs7+7utav3o/4q9fseuW9TOZ/5Zjtu -ucenfsH4XB+z3sR2tNpIbE1gtlNr60Lm0hRmEUIyNYN+PVpR4dGHPqpq3Ujm/djfjy2zSmW2OLcR -E3zbO26+d2HPdGfV1Ondu7SfU+WbstjbC3qYatuhHFYljgjkbXwzlmECYDEzd+QGzj+GV05Yi6I/ -5Zt6ua2KOVkzdbFdulXr9vFt5cTbLQeYXdjdnim10v2lOmFmWCAI+1EUfOEDGOV5ikd8yMXwb0WN -KKTpzGc6kd2pTzRXvJmZi6uUafnsrXtrb1dL6ZFcqHYOoE8ZW4gGSWuxi8gAeWEiDPJmLi+Hf4LC -xlHQ+a/vBLdS2Xt39Lbmr0thRHTnFJTeuzfdQ8pcHYCXvS/1Y8o2YW6ZZnJ1rQwutn71Z7KThHTn -PZsx1qe7dH3eXvpnPRl37qeu88qzWvELssIO1ymAX6wPhyaamY2AHpls5jx0WJuiy6Lpytnuyu/l -qtls3xNvxRMdc5dk0eFv7loQs+ZUSz/aUbutolnoRiAR0P4TryO3+etzpT/SnO+lfxU/JfE/wpbq -RhqZxZ5uXmuj8ccvW6dulvH3JeMa/iFTT6yp9gD7OzrC6sYFYZq0E3fYXARdjfi2Pp+ZW66bue/K -ebpphExhltnppwYtjli22ccMeM1xx7PxbdlaQNlc0Xmc222Elu1RoMIjXsSNT7kmoB5jjjFwEwMz -cmYhx7szOsa1OSZiPvznura7aET9SyJ3W/muU70s+z8R2A7ueevtqtnWDLrorEsUEFT7qLsyxdsg -5tIBORSv15Nj5eDY73RH1baf3Mendw4b8+jz6c/7cx/xTTjHLn24TuyymZu6vmBnWbZVtSdxptFr -2nktWNvbrRROfcOM2b+nO1I7i+e9kOjDn1Xnm+aTdxplwjCmW2OMzLvbbFYs349UzTPPZPQpbLZX -dh4t5FvLeytVNlru3FSir2pqscbFXhkEniiMAkKYpXfJs/wHGF35Ytvtpt1Kb8tTlp+HHrcLbpus -muzTr22Vr+LDqW78vl212u/KlNBVn1NgYqcs+0s1ArxtFHIEktOKCSGcJHd3cpSfPVm44WNKlLZn -bdNduV1KcMOvGtW7qzPL8sU64z40nqwpTOZ7fn+ugvWfF45zmFi2og7155q74KrO74KE4yzkWw+c -t1+LrGn/AFP4bvMsz/tz/D+a3y73Ifb26/i8IFflG0/kw0gc5zeYom2rD2eRFzJux+r/ADfwW9P2 -rtPjbNfwXenvNSKRqU2Up/K73hEctgtnfs2rNicNlsK0QSzyFEEIWiYQGLl2/l49Hdss3Rnx0WY/ -p28bfTK3+/MbqfkteY8xcdjo/MrexuzxS62Z6lOoFmWCEI+1EUfOEDGOV5ikd8yMXwb0V0c9Odt1 -8d2pTzRXvTU+/GyLJ77K17a27sOlnd7jff2mu6qvM5VLW0grO0tyakAj+zAmGALEQTSQ92Tr8gs5 -P0z8ymlFYx+fum3tpEzNC+aREx8NvfN+PdEV49Ex67xWtt6usvV9xZido55Ow0duS4deB4xLty2Z -44ZCISciZzbPF26upq0mzqms5bZ80YdS2RMXZbsM/KrztefbeJ09LqXjo7vXyOVfTWYWeO0xhBJJ -GZRv3QkywYOQDH1zhNS+Zrsvi2Z4YRu2d+4stjOvszdFf4rt+2mfVVV2UMc/7uquyLZ2bN7avrZb -JnORi8pXIHN4oTco4e2RY4xizfzsrrdbFutbbHuxfH7a8ezcxZdM2XXThdy3YbvZu9nq7cE+2nva -i9vYat629LUDqttiaxNMTRlPK1wOchGbxlDBng78W9mWLJikTOXPNvVNtsd03VW+MZiNtkz12zM4 -dNKUhTDfeQFZjrNZnll31gNzqwYyFxpxNLK9YcOzsDhXhY2+Mr/FZmJttnD2tO2Znrtw6aXzMdFs -N4TNa0tvmLY6roiZj96z2u1UCfy8/FP7QjeihC1rLctucdrasSTSPTkMexUOCKGtLFMLPiIm4sxN -1W9aItrEZbO2Ma50p1TXJNCt91szGPNFfTbTynDOXoqtFtt5JTenbvgGuCG1uZxv3OycxRs8VRoO -72OrYkl+T0w36zrU+zddP3a3RHTt6rfP+7MONuNlvxTETPR67vNWdsS7Wxt2IvONNB3jjqTUb7nF -ydozkjOu4u454uQi5Y+DZXKyntV3R55r6He73Y/e/wBMvJaK1a3VrRQS7O3JStyb8pCr2pY+7HDe -EYP6WMhPiAP8jiTYbo3Rbtt3/wBqyevBm6aTd/2U6uW5L4/cu7ezrdPs79kKUUe0dpY7EteawdG+ -9aNpLERBK/bhbkXzfM75LKkYxzTnyac/iieaadUcIr0JdWJ5Yy57o7KUjvnj7PS7f7vOX9hA+zk7 -pdy/9tK5c+T/AHU3AuT55Z+Kxr83JFPe+nb28kNaVOea5c935nIZ9d/c4/X/AMd9n1z/AOZ/bHH8 -Pn+5+6/63JdNf345N8cvR+zPrrtNHb9Tjz+n7Oqmxiz+16+1Cxvjtz1bdmtUguazYSRDXklGOIq8 -9MDjB8zcsyDyLBZ+XHTN/LNYjbz04xHNPVSI7Yc4mYtiZ2RbXfE4dtZns2OTpxuUPDPEausORw3R -tFeexsbdcOUcMhBBHOzWSrczH0iEcu3HpldLsb4jZyV66W9uFZpk1dHLzTHxzHVzXY+aK8d+MdSj -S37+Ra3TbbYSDVk/aRNVpbGzMYxCFUo4p7TjXnIwOQyEn+Zhdm5euZZMYznMW7tvPu6MJ37VmsR0 -3R+S6vbMV8oQWr22raDZbOK5PJZPeT655LFuaKCCod/tv1ZpRjwzMLS9siBn6YZYsxjTj4ox405q -ds0jjluW/Cb6fdi2n4bKz1Vm7vpONc2n8u01umBziMMt+F6uti2VrYy5+ztkYyzWY4ZXilKMHEC5 -NlnwpddhhnFt+z5cOmYnzlttc8I9n89uPZNGdlDHP+7qrsi2dmze2r62WyZzkYvKVyBzeKE3KOHt -kWOMYs387K63WxbrW2x7sXx+2vHs3MWXTNl104Xct2G72bvZ6u3B6DU24dN5Hv6Vm/IOoqVaV1pb -9k5WiOwU4Sf01gicQfsi/Hlhn9MLFuNnHn5e62kccZ6Wro9qONteyZ9HmetZ2dst6LCxIgICAgIC -AgICAgICAgICDSeCCxBJBPGM0EouEsUjMQGBNghIX6Ozt6s6TCxNMYcqp4Z4fTIjqaLX1zNmYyiq -wA7sJjILO4g3oYCTfizOtc872eWFv9iaX9p/tX9n1v2njj9/2Y+/xxjHdxzxjp6rMYRMbJWcc9jW -xoNFZ18eus62rPr4sdqnJDGcI8fTjGQuLY/JNtTfxWIdfQgkaSCtFFIMQwMYAIu0Mbu4R5ZvoHk+ -B9GSZrWu1Ijuaw6zWwfb9ipDF9oBR1eEYD2gPHII8N8olxbLN8Far669e/vVbfjHjVxha3qaVhgk -OYGlrxHiWQuRm3IX+Yi6u/q7pE0y2E45rBajUlsA2JUq5bCMHijuPEDzDG+cgMmOTD19MqVz458U -plwy4Ia3jXjtWOaKtqqcEVgxlsBHXiAZJIy5AZsIsxEJNlnf0dWs4cMlmMZnesT6vWWPuO/Uhm+7 -jaC33IwLuxDy4xyZb5xbmWGfp1dTZTr6/KBvPTp2Kh054I5akgPFJXMBKMgdsODg7cXHHsk45kYZ -KWs8W8Z1VgrGr1FKhYIXjKarXihNwd2dxcgEXxlm6LXNNKVTlhdsUqdk4ZLEEc0lY+7XOQBJ45MO -PMHdn4lh3bLLMYTVZxihHSpRWprcdeMLVhgGxYEBaSRo8sDGbNkuOXxn0TZQlVHx3x8J7U4ayoM9 -4SjuytBGxzgf1DKXHJsXuxJspsK412rX2VPuwzdiPu1xKOvJwHlGB45CD4yLFxbLN8Fa5zvSIwps -VW8d8fYqxtrKnOkZSUy7EeYTMuRlE/H5HIuruPq6RNMt1Ord0LMVz3169/Sji8Z0NUZS1+vqUbEj -SO1iCvCJsco8SP6erv759fdScqLGdeNVTReHazU3SvRQ1orDwvXYKdcKkLARsZl2xcsmbiPJ3L2b -DN1zqbsJjfTur65ZmMYndXvp6nS12k02seV9bQrUXsFynetCEXMm9z4MPJ+vupWaU2LTGu1Ket10 -g2RkqwmN3pcEoxdpvlYP6XLfP8jMPze3RTZRa41V4PHfH68LwQaypFC7Ri8QQRiLtCbyRNxYcYjN -3Ifg/Vlead7NIXZ4ILEEkE8YzQSi4SxSMxAYE2CEhfo7O3qzqTDUTTGFLV+OePakjLVauprykbEh -VYIoXJm/ndsRyrN05M8sN6+i0la5Pdra+tDdtM7WbMcMYSys75dpDZmIv0upspsWc67WlHx7QUHZ -6GsqVHYTFnggjj+WV2eRvkFuhuAuXxwyszMxScvL1m2qu/hviBQRwPo9e8ERvJFE9WHgBljkYjxw -xPjq7JzTWqUjtXZdPqJb0Owlo15L9ceFe2UQFNGL+rBI7chbr7Opv45rMbNzaPV6yMK0cdSEI6T5 -pgMYM0LuLhmJmb5PlJx+X2dJnzU6txP29arL4x41K8Dy6mlI9UHiq8q8T9qN2dnCPI/KL59GSvee -uvXvWH1GpeGxA9KB4LYsFqLtBwlEQaNhkHGCZgFh6+3RWs99evf0kRTsp1bkVzx3x+7Z+6u6ypZs -8O135oI5JO2/6nIhd+PX0SJmMkmMKbG9LUVKd2/dj5FZ2MgSTmbs+GjjaMAHDNgBYcs3xd/ikThT -p7/KI6IhZjGvCnl3z1rNirWsxtHZiCaNiE2CQWMeQExAWHz1EmZ2f2dSN4kIRIXEmZxdsOz9WdnU -mKkTRTbTahqtem1Gu1SoQSVK/aDtxHG+QKMMcQcX9Hb0Wuaa12+UeZIiKU2MbLSabadr9pUK17sF -yh+5hjm4F8R5sXF+nspE0msZrOMU2JS12vIbIlVhIbrYuM8Yu0zcO3iXp8/yNx+b26JsoRNJq0n0 -+pndynpV5SeF6rucQE7wE7OUXVvofDZH0Sca12kYUpsyVn8U8Wca4vp6LjUAoqrPWhxFGeeQR/L8 -ovyfLMrMzNeKRFMtmPW5+38G1e1skVqGq8BAEQu1WL7mOIMZhiseoRljDtxz1fDt0xbb5ia7a164 -xx3l0RNvLspMduGG517Ok0tu7Beta+tPdrY+2tSwxnLHh8twMmch6/B1ImmSzFYpOSzNVrTlEU8I -SlAfdgcxYnCRmceYZ+ksE7Zb4qRvFQ/HtAdw7x6yoV2VwKS0UEbykURMUbkbjydwIWcevR2ViZjI -nHNbr1a1cTGvCEIyGUptGLCxSSPyM3xjJE75d/dTgKd3x7QXrP3V3WVLVrh2u/NBHJJ239Q5ELvx -6+its0yJxzS2NRqbEdmKxSrzR3HF7gSRAQzOLMIvIzs/PAizNy+ChX1JKdCjSqhTp14q1SNuMdeE -BjjFn64EBZhZW6a5pEUyVaHjfjuutSW9fq6lO1KztLYrwRRSEzvl2IwFidOaaU2ExWa7Ww+PaADs -SDrKoyWzGS2bQRs8pxlzApH4/OQk3Jnf0dImlIjYs4zWejqTy67XylYKWrFIVuNoLREAu8sQ8sRy -Zb5hbmXR+nV/ipsosTjXbDLUKLSwStXiaWsDx1pOA8owLDEIPjIi/Fss3wVrNZnezEYU2QrD474+ -E9qcNZUGe8JR3ZWgjY5wP6hlLjk2L3YlNlNi1xrtQP4f4k94b76Sg98TGQbf2sPeYwxxJpOPLk2G -w+VqLpjKfKc0m2JXdhqdVsowi2NOC7FGbSRhYjCURNvQhY2LDt8VmMJrGa7KEGq1cBxnBTgiOHuv -CQRgLh3y5y8XZuncL5ix6v6q19XUft60Vjx7QWawVbOtqz1o5SnCCSCM4xlMnMpGEhdmNyJ3cvXL -pE0mJ3G/itValWpC0FWEK8AuTjFELADOTuROwizN1J3d1KlGv2FH7v7z7eL7vHH7jgPcx8OeOWEj -AlB+wdF+0/2r+zqv7Uxj7/sx/cYxj+t48/Tp6qxNMicc2jeOePNFbhbV1GivlzvR9iLjOWc8pW44 -N/xJTZEbINtdspqun1NQa41aVeuNQTCq0UQA0QSOzmMfFm4sTi2Wb1Vm6c0iIySDQoDBLXGtEME5 -GU8TALAZSu7yOY4wTm7vyz6qTGFNjUTjXar09BoaUMUFPW1a0MMvfhihhjjEJnZx7giIszHxd25N -1V5pZpHaD49oAOxIOsqjJbMZLZtBGzynGXMCkfj85CTcmd/R0iaUiNizjNZ6OpLZ1OqtfcNZpwT/ -AHYDFa7kQH3Ywd3AJOTPyEXJ8M/plSJ89evf04QLTMzNhvRCIEBAQEBAQEBAQEBAQEBAQR2rVapX -ks2pggrQi5zTSkwAAi2XIiLDMzfF0qOfT8o8dvT14aGxgunbaV65VjaYC+34d1u5HyBnHuj0d89V -rlnur1Vp505o76ddK+Z1FlRBDdvUqNY7V2xFVrR9ZJ5jGOMW/EidmZKrEItZuNRtYXn1l6vfgF+L -y1pQmBn+HIHJlZtmM2YmJW1FEBBW2Wxpa2hPfuydqpWF5JpMEXEW98CxE/6GSMZiN8xHbhBsmd2P -YVdnStWrdWCTnPRMI7QcSbgUkYyi2XZmfIGz9EphXZ6j1V8/qWUFWHa6uaUYYbkEspvKIRhIBE5Q -OwyszM+cxk7Mfw90jHzk4eXX5lpBG1qs9kqrTA9oQaUoOTdxoydxE3H14u4uzP8Agg0vXatClYvW -z7dWrGc08mHLjHGLkT4Fnd8M3sykzRbbZmaQyVyqFN7kkox1Bj7xTyPwAY2Hk5E5Y4szdXytXRyz -SWbJ5qU2pRMCBjEmICbkJM+Wdn65Z2Uuwz2ETXJX1uypbOhBsKMneqWQaSCXiQ8hf0fiTCTfpZWY -mM138Jp2YJbFivWgksWZQhrwi5yzSEwAAi2XIifDMzN7upMrEVbgYmLGDsQEzOJM+Wdn9HZ1ZijM -TXGGVFEBAQEBAQRzWq0BRDPMERTn2oGMmFzkdnLgGfqLAu+G+CRuEiAg5+w8j8e1tiOtsdpUpWJm -zFDYniiM2d8fKJkLv1+CtsVmkE4YytxWqs0ssUMwSSwOwzxgTEQOQsYsbM+RdxJnbPsoI9jsqWtq -vbuydquxxxufEi+aaQYgbAs79TNmSMZiNsmyZ3RXsKGypbCOWSpJ3QgmlrSvxIcSwm4SD8zN9JNj -PomyJ3k503eqvmlZQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA -QEAvpf8AJY1PdnoIfLPHKEd6DwGvLLNFGWpvc3rynAbs323TuROMg/8AVJnXon3rv+uz0F85/wDb -d/8A2LOmsbDaW/Htfb2Ft67/ALcrzvFYlikmClbCCB5JIyE3IQb6uXL8er5lmdf+Oy7rmnluS7Cs -R/cp1Uu8vtY0kvl2zsR7dpoK5xbQ4LZS7SzgYY7LxFVfXdj7ZpHiwwPz5O+C5dU06UtmfvW131mb -e6l27KlMU1K1uiPuzh0ROfGsb9+zKO/vXr/2+8fbYO32b17b0Gkx2/2gxRcPXp3Oz3OH/Wwsaed1 -Pe5Yp0Y83+mvBdT3bd3Nj2ez6eunB57e+S7+behpP2dUoWZLUMF6xBsZoRnCSvNLBC9yOqE0ROQd -OI5f6WJsq6dsT/Nhxjk7cLuHu8C+7lr/AA4/LM3x1Y28fe34ktDzCBnC5K1/X0jsHJqqG4sBdhiI -YijI7ZjVln7b9z5ZCHoQ5csJN9sRWd2dON2zLKkfwzhjKxbM4RtnLfhG3px60cvkkr+EeZX4tjZj -EIIZNXNZlcLEYz66AonZ8twM5Cd/l9Tzjquk20utic/q0n8eXZ3JZMTjGX06/m8qpt/NsGi822Yb -C5HPpOzPrY47EoQxmFGKZ2eISYDEy+oTZx/DKzp/d46lOrmtj0pGMRH/AB16/bx7vWg8uJtloPML -uxuzxTa6X7SnTCzLBAEfaiKPnCBjHK8xSO+ZGL4N6JpRSdOYznUju1KeaK96TMzF1co0/PZWvbW3 -q6W+8jmgLz3cV7Vmvc1ZQWajQzSRR9yKhCf9JGDiMrFjDjJybHsppZW8dWnVN1sS3MVw/wCP/wBb -e9N5dttpvjpzQVZ9VYGOnLNtLNQK8bQxyBJLTigkhnCR3J8yk+WyLccK6URERM7bprtyupThNOvG -tWJmbsPlimzO3PjSerClM5mvr6jbXy/WlsLVsyG3v4oyjuWYcDBaj7cYvFIHysOfl92br0ZsNHCK -/wDHE/ztasVik/Fblx0q+XXvl7HzStuLEFGPWl3Gadzt0I7Z0J7MLRk3GKxH84uJuJuzO2cYd2XO -PexypPox8t7Wzy7HkPHBpXvJrG0qftEp4NQMletau2HkeavctRFFJwlcJgEwYW5chf16u7u+r7pt -077o+WYw32YYeXBIti66yJw9q6Jx3TZt8t05UUZP7UW/DZN1Lag+1v6q4WwItpZtPZc6UhcYqcle -KCCSOVsuMRNxZiZ8q69tttbdmFNv3oxrup1TXJfDzddfbOU80V4b4p5ThnK95DVlq0LeujtW5q2x -8YvW7EUtiY/6es0LAUbcv6JnaZ2II+Iu3qyviP8A3Plut75ur5oZ8J/7U/Fh3W+Vc3vfGqNanoqs -daSWSKSIJWOeeayWSBvQ5jkLj8GZ8fBTxk43Rur6XPw8exE74jzPn3jJWtV454fc1tmzavXo5Ip9 -ec5nDJCFaWX5YHftx9qQAbkIs/sTvlNa6YrT+3XriLad+HW78sTdMzh/uTHbfNe6t3UgCfy8/FP7 -QjeihC1rLctucdrasSTSPTkMexUOCKGtLFMLPiIm4sxN1V1oi2sRls7YxrnSnVNcmdCt91szGPNF -fTbTynDOXtPKLtuDxClainkik7+t704mQlwO1C0nImfPEhd2LPt6rV0R9aI2c0+lz0Zro128nocH -yzcXv2t5JXp7GaJq0eiAWgldnhknvyDLxZndhI43Hl06tjPRY0ory126tOrlt9NW9TD/AMV09eKW -9BuodlvdFqL8riMettQR3LkzSE88szWIIrRvLLF3Qr4Hj9L/AE4UtxtiZ2XzHVyRPXSZr0LdhPTb -381PsdzxHZV21GxcorsB62eSO5Wu2CuyRmEQSEMU7nKUgcSZ2yWcu7dPRTWuiLObZSenCZj0Jp21 -v5duHe8La2+3qVWsVJrNWrtdJsbcLz7Se7aPt12khneIm7dYxz/9k8dcezK6kTHNbOdsR1e1EZ57 -+lvSui6+y6Mrr+6kzl1Rxjreu0rWaPlWtqjcs2IdnqJbdobM8k7PPBJXEZAaRyGPLTlkQYR/Bdb4 -iupHwzFOvnr5oeeyZ5bLtt1a9keXnUvIZPItn5be1NVxGKnSgnqC+zs6wuUryMc7NWgm77CQsLib -8Rx9PzLhb7t07YupvphE5ZZ16acHe6cbYphMV6ccq8Ip27dlfWReQ34vIjm3ONxUjrR07AWZB14W -JtdFzkEG4gQHIbk3IHZn+Zhyul9KViMJvmONK24cJ2b2baxMRPwV663492Ll3K0Fra6nV349nRuV -tpV+6hk2tmzGw2K1rhJBZGVpWcyjxh+Lt7M3J82yk3RMZe3HHC2J8utmaxbMTutn+eI9fn2YXrf9 -rL+x3oUJ4q0ulnCCjNZ21uu1eIIYzCSeqME0dgZMuTnMbuXVumFNOcrp23TX8VKU2Ybt9dy3Rjyx -stinXGddtJ82Wcz6PweOax+09hatWbE47LYVogknlKEIgtEwgMXLt/Lx+V3bLN0Z8dFmPct4x6ZW -fenhy/ktc/eBf0BeReQ0ZaGz1k3/AIjba627hKLwQDGUQTj3B6iDYjOP1f16rNsxFsWzlzYU4z34 -+rY3SZurGF1I9fVv73IMioF5zv8AWfcx7GsMM9aE7FjtxtLQiJyOs5lEXbyWMxvx44b0wt05beWv -/uTbM8Oa2s12dLFvtTbdT/24mI4+3SKYdm/jih3NPf19Y8088H7LtfYyNC21s7SSWVtjWcJ43sww -9sOJExMD8eo9GW7aRfbE589uzLOvHdnuYxmyZ+S/r9ns/a0p2d1Y25aWoIvVsXt1Y7ZX7GseaaK9 -hhGerFLK7gBOXBsZzl84XPTitkcLI77r64dUdFeLpqzS6eMx+Synbj2Z7/e+K2bMWtq63bX61nci -MxOEM/eIoY5nAX5EMRyODOIGfBvmVupOW6K9NM+FcaMxExnvmnq6ndWGhAQEBAQEBAQEBAQEBAQE -BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEEdqrWt15K1qEJ60wuE0MosYGJNhxISyzs/wd -KCpQ8f0OuaNtfratNoebxNXgji4d3j3OPAWxz4Dyx64ZWbpSLYjz+jzJodXrITjkhqQxyRPK8RhG -AuLzlzmcXZuncL5j/nP6qV81Orcv7etH+xNL+0/2r9hW/amOP3/Zj7/HGMd3HPGOnqrE0yJxzTXa -FG/WOrerxWq0n9ZBOAyRlj4iTOzqUKq0Xjnj0OuPWRaupHrZf6ykEEQwF7/NGw8H/gVmZnMjDJHL -4p4tNWr1ZtPRkrVM/aQHWhIIsvl+2LjgOvwTmmtdqUilNiS549oLtj7i5rKlmw8Twd6aCOQ+0WWe -PkQu/B8/T6JEzGSp5NXrJAtRyVITjuti4BRg7TMwsGJWdvn+RmH5vbopE+evXvP2dSC5474/ds/d -XdZUs2eHa780Ecknbf8AU5ELvx6+isTMZJMYU2J5NZrZQtBJUhMLrYuiUYO0zcWD+lZ2+f5GYfm9 -uikTTtr171/Z1I7Oj0tq7Beta+tPdq4+2tSwxnLHh8twMmch/Q6sTTJJisU2NbPj+htQtDZ1tWeF -pnstFJBGYtOTuRS8SF25u7u7l6pE0mJjZlwWYrExO3NJstRqdpA1fZ0oL0DPyaGzEEwMTe/E2Jsq -bajQ9FpJDpmevrGevx9gRQxu8GMY7Lu39H6fq4V5prXbKUilNjUfHfHwntThrKgz3hKO7K0EbHOB -/UMpccmxe7EpspsWuNdq19jSeUJnrxd2KMoI5OA8hiPDlGL4ywvwHLenRkma1rtz4+VSIpSmzJHr -tVrNZXetrqcFKu5Obw14wiDkXqXEGFsv8VZmuEpEIdf49oNbOdjXaypTsSC0ZzV4I4jIG9BcgFnd -mx6JzTSizjNZzB8d8fCe1OGsqDPeEo7srQRsc4H9QylxybF7sSmymwrjXaty1Ks1Uqk0ISVTDtnA -YsUbg7Y4uLth2x7JOOZbhlgqQeO+P14Xgg1lSKF2jF4ggjEXaE3kibiw4xGbuQ/B+rK8070pCS3p -tPdadrlGvZayIR2WmiCTuBG7uAnyZ+TC5O7M/pl1P29apaNCjQqhUo1oqlWPLR14AGOMcvl+ICzC -yszM5pERGSnD4t4zA0jQ6ilE03caZgrxDz7rcZOWB68x6Fn191NlF2125rrUqbTxztBG08MbwxSs -A8wjJxcgEsZYXcByzfBlazjxSkUiNyDZaTTbTtftKhWvdguUP3MMc3AviPNi4v09lImk1jNZximx -KWu15NZYqsJNcx92zxi/ewLA3c6fP8rMPX2TZTYQqB4v41HrpNZHqaQa2UuctIa8TQEXT5iiYeDv -0b2VmZmldhEUy2pJPH9BLYq2ZdbVOxSYRpzFBG5wiH0tETjkGb24pzTWZ2ylIpTZC3Xq1q4mNeEI -RkMpTaMWFikkfkZvjGSJ3y7+6nBVKx4147Z2AbGxqqc2wB2ILkleIphcfR2kcXJsfmrbMxkTjmnP -Uao9gGyOlAWxjB4wuvEDziD5yLSO3Nh6+mVIwrxzJxpwQVvGvHasc0VbVU4IrBjLYCOvEAySRlyA -zYRZiISbLO/o6tZw4ZExjM721rQaK3VKpb1tWxVOQpzrywxnGUpu5FI4ELi5O7u7l6qbuBXPini1 -uuiminiqwxzwRfbwyjGLGEOWftCTNlgyLfK3TorWceKUwpuWFFEBAQEBAQEBAQEBAQEBAQEBAQEB -AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB -AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB -AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB -AQEBAQEBAQEBAQEBAQEBAQEBBX2Owqa6hYv3JGiqVYymnlfL8QBuRPhuvoykysRWXJi8qleGeezp -b9KvFAdkJrL1AjMI25O3NrBDE7t1/puH44w61dFM/L0Jb7UxTb5dPcvyb/SQ2KtWxfrV7lwRKrUl -miGWTl6cA5ZP/q5V5ZrMRsZi7CJ2Ss3b1KjWO1dsR1asTZlnmMY4xb0yRE7MyzVuIVD8l8cCpFcP -a0xqTAUkNh7ETRmEbsJkJ8uLiLkzO7emVeWa02+vJImsVhuO+0ZSyQjsapSwjJJLG00bkAQlwlIm -zlmAvlJ39H9VNldh6f2oYvJtNZjqy0LUF+vbsfahPWnrnG0nAjxl5B5PgfpDkX4Yy7XlmtOFexKx -SZ3fsVdz5jqaNK1NVnr37FKevBbqRThziexOEGZGHm4ceecO3XCWxWbd100qs4V3xbM9kVdGpu9L -cqzW6d+tZq13IZ7EM0ZxxuDZJjMXcR4t65UnCKzkRjNNqKLybxuamN2La05KZG8Q2QsRFE8gi5uD -GxceTCLu7Z9FZiYzIxyW6GwobCqFuhZit1ZM9uxAYyxlh8PgwdxfDpMTGaRMS1/ams7Pf+7h7Pd+ -37vcDj3ufa7XLOOfc+Tj68unqkRlxWcK8EIeQaE7wUA2VUr0jmIVGnjeUiid2kZo+XJ3Bxdi6dEi -K5E4Zsx77Ry7I9XFsap7OPLyURmjecWZsvmJn5t/AkRWKxkTNJpKz93U78lfvR9+IGllh5NzGMnd -hMhzlhdxfD/g6lcK7Fp3qNjyjxqtFHNY21KGKUI5YpJLEQiUcue2Yu5MzifF+L+/sryzWm1K4V2K -5+ZeOx+Sf2dluxRbJ4opowkliHuPMRCMYM58yk+Xlx4+js/ults3Vps/b3bUumLaV+99nnrh0Sth -5F4+c9qANnUKeiJSXomnjc4AD6ilHlkGH3clNldi0xptbRb3STEIxbCtIRzPWARmjJ3nYO48TYfq -fD5uPrjqryz5cM+xKx5u/Lt2LyiiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC -AgICAgICAgICAgICAgICAgICAgICAgICAgp7mOrLqrcVuqV2rJEQT1AHmUgE2CFhy2css3RE5tWz -MTg+eXK20OltaOgfb2dGeovR2KmzgsM4WHiYa0dV7cYWpCL5mdmchxj3wt31m2ebhTfWuOWym80a -RfZTD2sd1P20yRj47dkl2ut21jcwR7Y4jgjoVa8sM0X28YAJWDqzFXkiKNx/pJQZuhMu3NHNhnbf -M/zTdE8cKccKbnCyJi2K5TZEfy0mKdNZ3Y76vTeeUNjMekt15LYVKFspbhUYorFkGKE4wlCGWKw0 -nAi6sMblh8t6LjZNL6z8Mx11j0RMfZV0mPYpG+OyK+mk9W+jl6XQDF5XqdhCOwuV5v2lamubGuEJ -DNMFUGfthDX7PPtlhijEnfk/XOVu2aVjCPY/119KXYxX5o7rbo9XcgtaHbSeKbMK9eeGZ/IJr08c -McbWJqwXnk5RjOJBI/BmIGIXYsYWbJpGnXZGPD3vTMTv2w3fFZviNsW0/DZWOvGN2/ahtaGzat09 -jrrG2uWrV+EZ7mwqjV7XZqWwCTshXqGzCUosUhhh/lZn6KXRNKRSK239s2xHoS2YznGnL2RfbPrT -WKr2f3f1NGGnsjsNf+z4bcB1ZOLOFqDv8JHHhMJcCMijcmx1LC63XROrbfHu80dUbursYtibbLrZ -xu5buueWcevtxZ8u0e5s7ndHRhsDWcNLPI9eMHKYa1icpxhaYThkkAGAuLs+cM2OrLnpzSImf7l0 -9tkRE/i82GMNXxXCPgp/NWnXFY68cGv7CazttZtIS2uzebaVSuTbKoFZhGtWtMJ9ka1Qm4vILPIY -Yf5WYui3ZPLdGUR7c9c2xHoScbZ30tj+eJ9b0/ilWzX2Xk3dhOGGbaPLWchcRMCqV+RhlvmZ5GLq -3vlYj+nbH7357lu9+Z4W+Z5axFsIta2kbXXZLgeSRXDkCvK8DVj2jWWmabHbIe2/zMLuQ/rMzM7r -WjnZPw2zE9PJdHl6zWyv+aIp/KmfT3h0U3CjKNovKht9IiaR4f2oLvN0bPDs5fl6cfwU0p/p12W3 -flv+w1cfqcYtp1RZ6YWNBFfp+Rw0deNqxqHs2rFuDY0DhemUvcNzr3XGMJecp8eLOZcS+rDJpzW3 -HZbERv2YTHR5jU97DObsd3T5b9i9tTsa7yzYWypWrMOx1kNeo9WCSZnngknIozIGcYstMOCkcR/F -cborp327Zx/lp5bdzrbPtWTsivnhyPDtJcjep95QkBx8UoUyeaImxKzy92H5m6G2W5B6+mV38XNY -1qbbsOOEuXh8J067Ju89rXxqttqRatrNe3BPe8ao0IrH280nZuQ9xyGdwEuy491nzJhvXqr4mOad -WLc7prHZdtTRpbGnMxhbN1eubaeZyg8ctTeKfZznu5dvqdZbiDWyVII6wTSU5ICGOeKrF9wJkXys -ExkT8XJvVTWuia3W7cONKxNKcKbqbmtCJi62Lpyuiftr1znNccX0FhpVKugrzaqSwbFHHWKOuJjT -kaAv6U3fHZHDOHJvd8e6upNdWafNj6OvvcdOKaVtY+HDjv6ncXJ2EBAQEBAQEBAQEBAQEBAQEBAQ -EBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ -EBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ -EBAQEBAQEBAQEBAQEHLk8m00E1kLdqGpHWk7Tzzz1xApBi70gt/SOQlGHzExiL46+nVemPCakxE2 -xN1caRE76Rs2zlSu7NOaK08q506aY9DleSfvH8X02toXG2VGd9sbR6lztwxQTZ9ZO+7kLRB+sbM/ -szM5OzP6fC/4rX1b7reW6OT3vZmZjhTfOyMOyJlJvti3mrh5R+3dDqavyGhasNrbFyiO/jj7lzVV -bY2Dib44cYZXH/KKMV5tbwt1sc8Rf9KuF020r5475SLt9Kyjv+SjDLLX1+vt7m1XdhsQ0WhZo3fr -xKWzLWg5Y6uDScmbDu2HZXS8LzRE33W6ds5TdXHqti67rpTi1M7NqTXeS6u5rJ9hIb0YqZnFfC5x -hKvJHhzGbLuLYZ2fkxOLs7Ozuz5U1fC323xbHtc3u8uPNXd5Vrglt1ZmNseqvmxUtf51othsrMVS -3Wm1VaKs/wC1wsRlAdi0ZiFcCbIkXEGf6v1mbC7av+O1dOyJui6L5m72eWa0tiK3eW6U+pbWkT5V -pHbNfKXVrbzSWoJJ6uwrTwQxtNLLFNGYBEXLEhELuzC/Aur9Oj/Bea/w+pbNLrbomZplOe7vatmJ -mkZ+UeeJjqRX/J/G9f8Ab/tDbU6f3bcqnfsRRd0cZzHzJubY69FrT8JraleSy67lzpEzTp3HNFIn -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 -uv1Zw1tS2JjdZE804ZxHuxHCJoxFl0W2x712N08bojCv7113c5uy8ad/EgnoXPIdjDSb7aCtaoRV -pIBuO1S3OEMNGrdmkjrzSHluXJ+vzEvTpeK/36XW6Nk3YzMXzdE8vtW21nUusiJuiI2U4QzbbPLW -JnmtrdH71JiJxzzrtq+s0Pt/sa/2wFFW7QdmMwKIhDi3EXjNhMHZv1SZnZfmNSvNPNjNenv2uunE -RbERknWGxAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE -BAQEBAQEBAQEBAQEFbZavWbSlJQ2dSG9Rm496rZjCaI+JMY8gNiF8ELO2W9V00ta/Tui6yZtujbE -0nthJiubhf3Yfu1/5S03/D6v+jXt/WPGf3tX8d3rZ+nbug/uw/dr/wApab/h9X/Rp+seM/vav47v -WfTt3Qf3Yfu1/wCUtN/w+r/o0/WPGf3tX8d3rPp27oP7sP3a/wDKWm/4fV/0afrHjP72r+O71n07 -d0H92H7tf+UtN/w+r/o0/WPGf3tX8d3rPp27oP7sP3a/8pab/h9X/Rp+seM/vav47vWfTt3QP+6/ -92js7P4npsP06a+q38kafrHjP72r+O71n07d0Iav7pf3X1gIY/FNUTE+X7tSGZ8/g8gm7Ld/+b8b -dnran4pjzH0rdyb+7D92v/KWm/4fV/0ax+seM/vav47vWfTt3Qf3Yfu1/wCUtN/w+r/o0/WPGf3t -X8d3rPp27oP7sP3a/wDKWm/4fV/0afrHjP72r+O71n07d0H92H7tf+UtN/w+r/o0/WPGf3tX8d3r -Pp27oP7sP3a/8pab/h9X/Rp+seM/vav47vWfTt3Qf3Yfu1/5S03/AA+r/o0/WPGf3tX8d3rPp27o -P7sP3a/8pab/AIfV/wBGn6x4z+9q/ju9Z9O3dB/dh+7X/lLTf8Pq/wCjT9Y8Z/e1fx3es+nbuhf0 -/h3iGlsla02j1+stGDxHPTqwwSPG7sTg5RiLuLuLPj8Fx1/H6+tHLqal98Z0uumfPKxbEZQ668jQ -gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA -gICAgICAgqjttUQAY3IHCScqoE0oOxWAJxKFnz1kEgJnH1yzrp9G/wCGcq5bN/RxJmnV5emGo7rT -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 -3rPp27oP7sP3a/8AKWm/4fV/0afrHjP72r+O71n07d0H92H7tf8AlLTf8Pq/6NP1jxn97V/Hd6z6 -du6D+7D92v8Aylpv+H1f9Gn6x4z+9q/ju9Z9O3dCWr+7r931SzDaq+MamvarmMsE8VGsEkcgPyEw -IQZxIXbLOyzf/lfFXRNt2rqTE4TE33Y968lu56FeBoQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE -BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE -BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE -BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE -BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE -BAQEBAQEHwOx5Z5NPMcpbS0Lm7u4xzGAtn2YRdmZl+ijw+nEU5YfBnXvmc5R/wBpvJP/AHa5/wCo -l/7Sv0NP4Y7E+tf8U9p/abyT/wB2uf8AqJf+0n0NP4Y7D61/xT2n9pvJP/drn/qJf+0n0NP4Y7D6 -1/xT2n9pvJP/AHa5/wCol/7SfQ0/hjsPrX/FPaf2m8k/92uf+ol/7SfQ0/hjsPrX/FPaf2m8k/8A -drn/AKiX/tJ9DT+GOw+tf8U9p/abyT/3a5/6iX/tJ9DT+GOw+tf8U9p/abyT/wB2uf8AqJf+0n0N -P4Y7D61/xT2n9pvJP/drn/qJf+0n0NP4Y7D61/xT2n9pvJP/AHa5/wCol/7SfQ0/hjsPrX/FPaf2 -m8k/92uf+ol/7SfQ0/hjsPrX/FPaf2m8k/8Adrn/AKiX/tJ9DT+GOw+tf8U9q/s7PneraMr9u/XG -XrGRTyOz++MsTtn8Fzst0b/di2ep0vnVtzme1VLd+WjWGyWwvtXMnAJnmm4OTerMXLGVv6WlWlLa -9EMfU1KVrNEX9pvJP/drn/qJf+0r9DT+GOxPrX/FPaf2m8k/92uf+ol/7SfQ0/hjsPrX/FPaf2m8 -k/8Adrn/AKiX/tJ9DT+GOw+tf8U9r0n7vfI95N5RWqWb09ivYGQZI5pCkb5YyNnbk74fI+y8vjNG -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 -pf3c/wDzPX//AK7/ALiReTxv9KerzvT4T+pHlsfbV8F9oQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB -AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEHzqf9z0JTGUG0KOJ3dwjKHm4t -7M5dwc/wL6cf5KaY29750/4+K4Sj/ub/AN7/AOz/AOtV/Uvl7/sT9P8Am7vtP7m/97/7P/rU/Uvl -7/sP0/5u77T+5v8A3v8A7P8A61P1L5e/7D9P+bu+0/ub/wB7/wCz/wCtT9S+Xv8AsP0/5u77T+5v -/e/+z/61P1L5e/7D9P8Am7vtP7m/97/7P/rU/Uvl7/sP0/5u77T+5v8A3v8A7P8A61P1L5e/7D9P -+bu+0/ub/wB7/wCz/wCtT9S+Xv8AsP0/5u77T+5v/e/+z/61P1L5e/7D9P8Am7vtP7m/97/7P/rU -/Uvl7/sP0/5u77T+5v8A3v8A7P8A61P1L5e/7D9P+bu+0/ub/wB7/wCz/wCtT9S+Xv8AsP0/5u77 -T+5v/e/+z/61P1L5e/7D9P8Am7vtP7m/97/7P/rU/Uvl7/sP0/5u77T+5v8A3v8A7P8A61P1L5e/ -7D9P+bu+0/ub/wB7/wCz/wCtT9S+Xv8AsP0/5u77T+5v/e/+z/61P1L5e/7D9P8Am7vtdbxj928O -k2obE7z2pIhJogaPtszmLi7v8x5+V3XHX8bOpby0o7aPg4surWr2a8L2CAgICAgICAgICAgICAgI -CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI -CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI -CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI -CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI -CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI -CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI -CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI -CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI -CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI -CAg81e2k4bO1Bc2MmpjEhGiXaB4ZGcWdzOWQDbPJ8cWIVLcemq3YTwWp/IJ42tzQ1WsUte/C3Y7n -A3cWYpHij4kxcGfrkm/BWu2cImfs86RGzOaObb2F79sSDBMZB+0K4RxvIQg4lTc+D4zgSLq/T8VL -axH/AJO6PQzM17LfzJaHk9uHSUbGxaD7i5gYJDnaICwzuRyu8YjGzM36vLK3dnEcPRDUbZ4+mU1b -yyW5NDWp1Yp7EhzAZDYZ4WeFgLIyiBchIZG/Vz+CzGPZXvokzTy4OrrNi9/XDaGLhI/MShd84kjJ -wIeXTLch9VLpwrG6rUZ0nZLj6baTWZoQtbSSHYvkrGrlhjib0fIxcgE34+vLmSXTERMxjgzPHDFF -B5pG1ODrCU/24zzfd2Y4CflniIYjZjN+P80W9Fq7Du81ViPPPno7w3pbGthu0YwkaYBlAZzeJmAm -5dSEJerN+Cl8csls1UNV5FPdsV4pagwjaGY4ZQleQSCFxFjbIRvg+XT8PzVjHsie1JmnbRQs+TMN -3W2jY44j+9h+1AnJ5ZIpBijZm+VnIn9PhlZsmteNsU65W7CZj4bvRLo/tC5PalrSMdOavUaxIMRx -yNyl5Mw5OJ+ocOjt0+LOpdNIun4f2/YtsVutjf8AsQQ+SXZghatSGUyox3jeSbhhj5Nw6Rvkvl+D -N+S1dNOadlv7WbMaRtmvc5m122ztzy2qeQrQ6wb1dvuJInZz5FzMABxkceGOBPx/hUuw5uExC2zW -kb6+h0ZfILNIaUuxHg0leaYxgNjEu2MbjnlEBcic8MzOzN+Ptu6PauiNn/qozZNYifLKq7T2189m -FC5TCucld7AkEzy4ZiEeJM4B1+b2d1N/Bd072m12ttjuVKEDSyVoHksTHK8TBzZ+DBgDciwLv7N+ -K53T7My6WR7UcXNr+S/Y1az2jKR3oVJGeSQBEpZicMkRDyb0yREbtj2XW7O796ndMuVuUdE+j1pG -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 -7BdijszgMdtngcoj4O/AhDusQEzPj6nb8FPMstZ/Hilr7WIrTuWzEReRwbIOMTR5dmcWLPHPsmyI -+avfE+hbZpNeFPP613Za8b2smpEfDuhwGTGeJN9JYy3o7ZUuxZsikU4Uc5vFK3foSFK5BUgeCWNx -6TO4kLGT56O3cN/0qzSZndMU6PKMFjZvia+XXikq6GxGdMbFzv1dfn7SJo+B54uAvKfIufEHdmwI -pdjWZzmKJTCmxUi8OEIoh+8dpa8EMUEogzOMkEhSDJhyJn+rDj/GrX0d1vL3lPT3zXuX5NTesHVk -t3AM6tgZxaOHtg7CBDjDmZZfn1fl7eiRhNenvJisU8s6oz1uxr2DelNhrt0bFmTiP9HCMbCQMxcu -Tk8bNlm91LcKRsx/Z39y3Y1nbSI+1Z22pi2TVRldu1XmaY4yHkxswEDg/VvXmpTGvCe82eW9zj8U -kOAK5XnKKGGzWr8o8kMVgREWIuXzdvj+lJxrXOYjumpGE4ZVqtUtLZpSm1W2wVZnE54ii5F3GFhI -oz5YFj4tlnEvwVv9qvGvekRSI6PMpQeGQQ1RrDYdopIPt7zCDD3uOXjkbr8hg/v16dFZn0dsUxX7 -eydi3Y02yt0nq3L4yNmJxIIOGe3IMmTyZZJ+GPl4t19Eifaid01SYwpwXIdf29pZv9zP3McUfbxj -j2nN85z1zz+CkYV6fQt2Mxwj0q2x1ezuR2Kz3gGlZZxMHg5SiBNghCTmw/k7g6lK5rWmSu/i7BaK -3Vs9mwMkclYnDmwMELQEBtybmxC3xZ1qs9899PUzEYU4R3V9br1orQRO1mZppSd3cgDtg34COSfH -5k6zdFYosOTrvHLFcKMNm4M9bXZKvGEXbdzcXFiMnM88WJ8MzMrdj00okx56+lnW+OT6+WqcFsS7 -Ncathjid+4Am5s44NuBfM/ryVnGsbJp3RQp5575aweN2oKtWKO6LSa83KhN2eogTOxBK3PEjOL46 -cUrOE7aU83qXf01dMfvYKkhzO92x1fhCIRZ9uICZ4b/rH+lZvisUIeaqUNhXq6aD9mXS/ZBEXL/w -TdzlGcfp92/H68+6t/tT/Dy+b1GyY33V75lX1ms3mqCL9nU7YSdgILXdjpyBJ2nfgYi10HAmYnb6 -nb8FZmvd5oj0H2981drRRXYtlemnp2o2vPGZSz/aswvFEMfXszSO7lxz0BmSMLacZntSmNeHr9bu -qKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC -AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC -AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC -D//Z -------=_NextPart_000_000F_01D15E52.0BD654A0-- - - diff --git a/framework/domain/mimetreeparser/tests/data/html.mbox b/framework/domain/mimetreeparser/tests/data/html.mbox deleted file mode 100644 index bf5c685d..00000000 --- a/framework/domain/mimetreeparser/tests/data/html.mbox +++ /dev/null @@ -1,15 +0,0 @@ -From foo@example.com Thu, 26 May 2011 01:16:54 +0100 -From: Thomas McGuire -Subject: HTML test -Date: Thu, 26 May 2011 01:16:54 +0100 -Message-ID: <1501334.pROlBb7MZF@herrwackelpudding.localhost> -X-KMail-Transport: GMX -X-KMail-Fcc: 28 -X-KMail-Drafts: 7 -X-KMail-Templates: 9 -User-Agent: KMail/4.6 beta5 (Linux/2.6.34.7-0.7-desktop; KDE/4.6.41; x86_64; git-0269848; 2011-04-19) -MIME-Version: 1.0 -Content-Transfer-Encoding: 7Bit -Content-Type: text/html; charset="windows-1252" - -

HTML text

\ No newline at end of file diff --git a/framework/domain/mimetreeparser/tests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox b/framework/domain/mimetreeparser/tests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox deleted file mode 100644 index 2d9726ea..00000000 --- a/framework/domain/mimetreeparser/tests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox +++ /dev/null @@ -1,115 +0,0 @@ -From test@kolab.org Fri May 01 15:12:47 2015 -From: testkey -To: you@you.com -Subject: enc & non enc attachment -Date: Fri, 01 May 2015 17:12:47 +0200 -Message-ID: <13897561.XENKdJMSlR@tabin.local> -X-KMail-Identity: 1197256126 -User-Agent: KMail/4.13.0.1 (Linux/3.19.1-towo.1-siduction-amd64; KDE/4.14.2; x86_64; git-cd33034; 2015-04-11) -MIME-Version: 1.0 -Content-Type: multipart/mixed; boundary="nextPart1939768.sIoLGH0PD8" -Content-Transfer-Encoding: 7Bit - -This is a multi-part message in MIME format. - ---nextPart1939768.sIoLGH0PD8 -Content-Type: multipart/encrypted; boundary="nextPart2814166.CHKktCGlQ3"; protocol="application/pgp-encrypted" - - ---nextPart2814166.CHKktCGlQ3 -Content-Type: application/pgp-encrypted -Content-Disposition: attachment -Content-Transfer-Encoding: 7Bit - -Version: 1 ---nextPart2814166.CHKktCGlQ3 -Content-Type: application/octet-stream -Content-Disposition: inline; filename="msg.asc" -Content-Transfer-Encoding: 7Bit - ------BEGIN PGP MESSAGE----- -Version: GnuPG v2 - -hIwDGJlthTT7oq0BA/9cXFQ6mN9Vxnc2B9M10odS3/6z1tsIY9oJdsiOjpfxqapX -P7nOzR/jNWdFQanXoG1SjAcY2FeZEN0c3SkxEM6R5QVF1vMh/Xsni1clI+peZyVT -Z4OSU74YCfYLg+cgDnPCF3kyNPVe6Z1pnfWOCZNCG3rpApw6UVLN63ScWC6eQIUB -DAMMzkNap8zaOwEIANKHn1svvj+hBOIZYf8R+q2Bw7cd4xEChiJ7uQLnD98j0Fh1 -85v7/8JbZx6rEDDenPp1mCciDodb0aCmi0XLuzJz2ANGTVflfq+ZA+v1pwLksWCs -0YcHLEjOJzjr3KKmvu6wqnun5J2yV69K3OW3qTTGhNvcYZulqQ617pPa48+sFCgh -nM8TMAD0ElVEwmMtrS3AWoJz52Af+R3YzpAnX8NzV317/JG+b6e2ksl3tR7TWp1q -2FOqC1sXAxuv+DIz4GgRfaK1+xYr2ckkg+H/3HJqa5LmJ7rGCyv+Epfp9u+OvdBG -PBvuCtO3tm0crmnttMw57Gy35BKutRf/8MpBj/nS6QFX0t7XOLeL4Me7/a2H20wz -HZsuRGDXMCh0lL0FYCBAwdbbYvvy0gz/5iaNvoADtaIu+VtbFNrTUN0SwuL+AIFS -+WIiaSbFt4Ng3t9YmqL6pqB7fjxI10S+PK0s7ABqe4pgbzUWWt1yzBcxfk8l/47Q -JrlvcE7HuDOhNOHfZIgUP2Dbeu+pVvHIJbmLsNWpl4s+nHhoxc9HrVhYG/MTZtQ3 -kkUWviegO6mwEZjQvgBxjWib7090sCxkO847b8A93mfQNHnuy2ZEEJ+9xyk7nIWs -4RsiNR8pYc/SMvdocyAvQMH/qSvmn/IFJ+jHhtT8UJlXJ0bHvXTHjHMqBp6fP69z -Jh1ERadWQdMaTkzQ+asl+kl/x3p6RZP8MEVbZIl/3pcV+xiFCYcFu2TETKMtbW+b -NYOlrltFxFDvyu3WeNNp0g9k0nFpD/T1OXHRBRcbUDWE4QF6NWTm6NO9wy2UYHCi -7QTSecBWgMaw7cUdwvnW6chIVoov1pm69BI9D0PoV76zCI7KzpiDsTFxdilKwbQf -K/PDnv9Adx3ERh0/F8llBHrj2UGsRs4aHSEBDBJIHDCp8+lqtsRcINQBKEU3qIjt -wf5vizdaVIgQnsD2z8QmBQ7QCCipI0ur6GKl+YWDDOSDLDUs9dK4A6xo/4Q0bsnI -rH63ti5HslGq6uArfFkewH2MWff/8Li3uGEqzpK5NhP5UpbArelK+QaQQP5SdsmW -XFwUqDS4QTCKNJXw/5SQMl8UE10l2Xaav3TkiOYTcBcvPNDovYgnMyRff/tTeFa8 -83STkvpGtkULkCntp22fydv5rg6DZ7eJrYfC2oZXdM87hHhUALUO6Y/VtVmNdNYw -F3Uim4PDuLIKt+mFqRtFqnWm+5X/AslC31qLkjH+Fbb83TY+mC9gbIn7CZGJRCjn -zzzMX2h15V/VHzNUgx9V/h28T0/z25FxoozZiJxpmhOtqoxMHp+y6nXXfMoIAD1D -963Pc7u1HS0ny54A7bqc6KKd4W9IF7HkXn3SoBwCyn0IOPoKQTDD8mW3lbBI6+h9 -vP+MAQpfD8s+3VZ9r7OKYCVmUv47ViTRlf428Co6WT7rTHjGM09tqz826fTOXA== -=6Eu9 ------END PGP MESSAGE----- - ---nextPart2814166.CHKktCGlQ3-- - ---nextPart1939768.sIoLGH0PD8 -Content-Disposition: attachment; filename="image.png" -Content-Transfer-Encoding: base64 -Content-Type: image/png; name="image.png" - -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 -o/0d59+Lh3Y1HHcAAAAASUVORK5CYII= - ---nextPart1939768.sIoLGH0PD8-- - diff --git a/framework/domain/mimetreeparser/tests/data/openpgp-inline-charset-encrypted.mbox b/framework/domain/mimetreeparser/tests/data/openpgp-inline-charset-encrypted.mbox deleted file mode 100644 index 8bd06910..00000000 --- a/framework/domain/mimetreeparser/tests/data/openpgp-inline-charset-encrypted.mbox +++ /dev/null @@ -1,40 +0,0 @@ -From test@example.com Thu, 17 Oct 2013 02:13:03 +0200 -Return-Path: -Delivered-To: you@you.com -Received: from localhost (localhost [127.0.0.1]) - by test@example.com (Postfix) with ESMTP id B30D8120030 - for ; Thu, 17 Oct 2013 02:13:05 +0200 (CEST) -From: test -To: you@you.com -Subject: charset -Date: Thu, 17 Oct 2013 02:13:03 +0200 -Message-ID: <4081645.yGjUJ4o4Se@example.local> -User-Agent: KMail/4.12 pre (Linux/3.11-4.towo-siduction-amd64; KDE/4.11.2; x86_64; git-f7f14e3; 2013-10-15) -MIME-Version: 1.0 -Content-Transfer-Encoding: 7Bit -Content-Type: text/plain; charset="ISO-8859-15" - ------BEGIN PGP MESSAGE----- -Version: GnuPG v2.0.22 (GNU/Linux) - -hIwDGJlthTT7oq0BBACbaRZudMigMTetPZNRgkfEXv4QQowR1jborw0dcgKKqMQ1 -6o67NkpxvmXKGJTfTVCLBX3nk6FKYo6NwlPCyU7X9X0DDk8hvaBdR9wGfrdm5YWX -GKOzcqJY1EypiMsspXeZvjzEW7O8I956c3vBb/2pM3xqYEK1kh8+d9bVH+cjf4UB -DAMMzkNap8zaOwEH/1rPShyYL8meJN+/GGgS8+Nf1BW5pSHdAPCg0dnX4QCLEx7u -GkBU6N4JGYayaCBofibOLacQPhYZdnR5Xb/Pvrx03GrzyzyDp0WyeI9nGNfkani7 -sCRWbzlMPsEvGEvJVnMLNRSk4xhPIWumL4APkw+Mgi6mf+Br8z0RhfnGwyMA53Mr -pG9VQKlq3v7/aaN40pMjAsxiytcHS515jXrb3Ko4pWbTlAr/eytOEfkLRJgSOpQT -BY7lWs+UQJqiG8Yn65vS9LMDNJgX9EOGx77Z4u9wvv4ZieOxzgbHGg5kYCoae7ba -hxZeNjYKscH+E6epbOxM/wlTdr4UTiiW9dMsH0zSwMUB891gToeXq+LDGEPTKVSX -tsJm4HS/kISJBwrCI4EUqWZML6xQ427NkZGmF2z/sD3kmL66GjspIKnb4zHmXacp -84n2KrI9s7p6AnKnQjsxvB/4/lpXPCIY5GH7KjySEJiMsHECzeN1dJSL6keykBsx -DtmYDA+dhZ6UWbwzx/78+mjNREhyp/UiSAmLzlJh89OH/xelAPvKcIosYwz4cY9N -wjralTmL+Y0aHKeZJOeqPLaXADcPFiZrCNPCH65Ey5GEtDpjLpEbjVbykPV9+YkK -7JKW6bwMraOl5zmAoR77PWMo3IoYb9q4GuqDr1V2ZGlb7eMH1gj1nfgfVintKC1X -3jFfy7aK6LIQDVKEwbi0SxVXTKStuliVUy5oX4woDOxmTEotJf1QlKZpn5oF20UP -tumYrp0SPoP8Bo4EVRVaLupduI5cYce1q/kFj9Iho/wk56MoG9PxMMfsH7oKg3AA -CqQ6/kM4oJNdN5xIf1EH5HeaNFkDy1jlLznnhwVAZKPo/9ffpg== -=bPqu ------END PGP MESSAGE----- - - diff --git a/framework/domain/mimetreeparser/tests/data/openpgp-inline-encrypted+nonenc.mbox b/framework/domain/mimetreeparser/tests/data/openpgp-inline-encrypted+nonenc.mbox deleted file mode 100644 index b98dc336..00000000 --- a/framework/domain/mimetreeparser/tests/data/openpgp-inline-encrypted+nonenc.mbox +++ /dev/null @@ -1,31 +0,0 @@ -From test@kolab.org Wed, 25 May 2011 23:49:40 +0100 -From: OpenPGP Test -To: test@kolab.org -Subject: inlinepgpencrypted + non enc text -Date: Wed, 25 May 2011 23:49:40 +0100 -Message-ID: <1786696.yKXrOjjflF@herrwackelpudding.localhost> -X-KMail-Transport: GMX -X-KMail-Fcc: 28 -X-KMail-Drafts: 7 -X-KMail-Templates: 9 -User-Agent: KMail/4.6 beta5 (Linux/2.6.34.7-0.7-desktop; KDE/4.6.41; x86_64; git-0269848; 2011-04-19) -MIME-Version: 1.0 -Content-Transfer-Encoding: 7Bit -Content-Type: text/plain; charset="us-ascii" - -Not encrypted not signed :( - ------BEGIN PGP MESSAGE----- -Version: GnuPG v2.0.15 (GNU/Linux) - -hQEMAwzOQ1qnzNo7AQf/a3aNTLpQBfcUr+4AKsZQLj4h6z7e7a5AaCW8AG0wrbxN -kBYB7E5jdZh45DX/99gvoZslthWryUCX2kKZ3LtIllxKVjqNuK5hSt+SAuKkwiMR -Xcbf1KFKENKupgGSO9B2NJRbjoExdJ+fC3mGXnO3dT7xJJAo3oLE8Nivu+Bj1peY -E1wCf+vcTwVHFrA7SV8eMRb9Z9wBXmU8Q8e9ekJ7ZsRX3tMeBs6jvscVvfMf6DYY -N14snZBZuGNKT9a3DPny7IC1S0lHcaam34ogWwMi3FxPGJt/Lg52kARlkF5TDhcP -N6H0EB/iqDRjOOUoEVm8um5XOSR1FpEiAdD0DON3y9JPATnrYq7sgYZz3BVImYY+ -N/jV8fEiN0a34pcOq8NQedMuOsJHNBS5MtbQH/kJLq0MXBpXekGlHo4MKw0trISc -Rw3pW6/BFfhPJLni29g9tw== -=fRFW ------END PGP MESSAGE----- - diff --git a/framework/domain/mimetreeparser/tests/data/plaintext.mbox b/framework/domain/mimetreeparser/tests/data/plaintext.mbox deleted file mode 100644 index d185b1c1..00000000 --- a/framework/domain/mimetreeparser/tests/data/plaintext.mbox +++ /dev/null @@ -1,13 +0,0 @@ -Return-Path: -Date: Wed, 8 Jun 2016 20:34:44 -0700 -From: Konqi -To: konqi@kde.org -Subject: A random subject with alternative contenttype -MIME-Version: 1.0 -Content-Type: text/plain; charset=utf-8 -Content-Transfer-Encoding: quoted-printable - -If you can see this text it means that your email client couldn't display o= -ur newsletter properly. -Please visit this link to view the newsletter on our website: http://www.go= -g.com/newsletter/ diff --git a/framework/domain/mimetreeparser/tests/data/smime-encrypted.mbox b/framework/domain/mimetreeparser/tests/data/smime-encrypted.mbox deleted file mode 100644 index 6b6d6a0d..00000000 --- a/framework/domain/mimetreeparser/tests/data/smime-encrypted.mbox +++ /dev/null @@ -1,22 +0,0 @@ -From test@example.com Sat, 13 Apr 2013 01:54:30 +0200 -From: test -To: you@you.com -Subject: test -Date: Sat, 13 Apr 2013 01:54:30 +0200 -Message-ID: <1576646.QQxzHWx8dA@tabin> -X-KMail-Identity: 505942601 -User-Agent: KMail/4.10.2 (Linux/3.9.0-rc4-experimental-amd64; KDE/4.10.60; x86_64; git-fc9b82c; 2013-04-11) -MIME-Version: 1.0 -Content-Type: application/pkcs7-mime; name="smime.p7m"; smime-type="enveloped-data" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; filename="smime.p7m" - -MIAGCSqGSIb3DQEHA6CAMIACAQAxgfwwgfkCAQAwYjBVMQswCQYDVQQGEwJVUzENMAsGA1UECgwE -S0RBQjEWMBQGA1UEAwwNdW5pdHRlc3QgY2VydDEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxl -LmNvbQIJANNFIDoYY4XJMA0GCSqGSIb3DQEBAQUABIGAJwmmaOeidXUHSQGOf2OBIsPYafVqdORe -y54pEXbXiAfSVUWgI4a9CsiWwcDX8vlaX9ZLLr+L2VmOfr6Yc5214yxzausZVvnUFjy6LUXotuEX -tSar4EW7XI9DjaZc1l985naMsTx9JUa5GyQ9J6PGqhosAKpKMGgKkFAHaOwE1/IwgAYJKoZIhvcN -AQcBMBQGCCqGSIb3DQMHBAieDfmz3WGbN6CABHgEpsLrNn0PAZTDUfNomDypvSCl5bQH+9cKm80m -upMV2r8RBiXS7OaP4SpCxq18afDTTPatvboHIoEX92taTbq8soiAgEs6raSGtEYZNvFL0IYqm7MA -o5HCOmjiEcInyPf14lL3HnPk10FaP3hh58qTHUh4LPYtL7UECOZELYnUfUVhAAAAAAAAAAAAAA== - diff --git a/framework/domain/mimetreeparser/tests/gnupg_home/CMakeLists.txt b/framework/domain/mimetreeparser/tests/gnupg_home/CMakeLists.txt deleted file mode 100644 index 9c64a008..00000000 --- a/framework/domain/mimetreeparser/tests/gnupg_home/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -configure_file( gpg-agent.conf.in - "${CMAKE_CURRENT_BINARY_DIR}/gpg-agent.conf" @ONLY ) - -configure_file( gpgsm.conf.in - "${CMAKE_CURRENT_BINARY_DIR}/gpgsm.conf" @ONLY ) - -file( COPY - ${CMAKE_CURRENT_SOURCE_DIR} - DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/../" -) diff --git a/framework/domain/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/DIR.txt b/framework/domain/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/DIR.txt deleted file mode 100644 index 1a45a6b3..00000000 --- a/framework/domain/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/DIR.txt +++ /dev/null @@ -1,3 +0,0 @@ -v:1: -c:4E31CEB57DDD4A7B9991AB05507B1ED4293FF952:CN=Test-ZS 7,O=Intevation GmbH,C=DE:ldap%3A//ca.intevation.org/cn=Test-ZS 7, o=Intevation GmbH, c=DE?certificateRevocationList:20100615T181523:20100707T181523:72FEF3FD88455A1D4C6796A6499D4422:::: -c:7F2A402CBB016A9146D613568C89D3596A4111AA:CN=Wurzel ZS 3,O=Intevation GmbH,C=DE:ldap%3A//ca.intevation.org/cn=Wurzel ZS 3, o=Intevation GmbH, c=DE?certificateRevocationList:20100625T102134:20100814T102134:44E60EEC02EF2FBF7A5C77E9BD565667:::: diff --git a/framework/domain/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/crl-4E31CEB57DDD4A7B9991AB05507B1ED4293FF952.db b/framework/domain/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/crl-4E31CEB57DDD4A7B9991AB05507B1ED4293FF952.db deleted file mode 100644 index 0b7e2dd4..00000000 Binary files a/framework/domain/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/crl-4E31CEB57DDD4A7B9991AB05507B1ED4293FF952.db and /dev/null differ diff --git a/framework/domain/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/crl-7F2A402CBB016A9146D613568C89D3596A4111AA.db b/framework/domain/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/crl-7F2A402CBB016A9146D613568C89D3596A4111AA.db deleted file mode 100644 index 47474a26..00000000 Binary files a/framework/domain/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/crl-7F2A402CBB016A9146D613568C89D3596A4111AA.db and /dev/null differ diff --git a/framework/domain/mimetreeparser/tests/gnupg_home/dirmngr.conf b/framework/domain/mimetreeparser/tests/gnupg_home/dirmngr.conf deleted file mode 100644 index a17a0354..00000000 --- a/framework/domain/mimetreeparser/tests/gnupg_home/dirmngr.conf +++ /dev/null @@ -1,8 +0,0 @@ - -###+++--- GPGConf ---+++### -debug-level basic -log-file socket:///home/leo/kde/src/kdepim/messagecomposer/tests/gnupg_home/log-socket -###+++--- GPGConf ---+++### Tue 29 Jun 2010 10:23:13 AM EDT -# GPGConf edited this configuration file. -# It will disable options before this marked block, but it will -# never change anything below these lines. diff --git a/framework/domain/mimetreeparser/tests/gnupg_home/gpg-agent.conf.in b/framework/domain/mimetreeparser/tests/gnupg_home/gpg-agent.conf.in deleted file mode 100644 index ece69255..00000000 --- a/framework/domain/mimetreeparser/tests/gnupg_home/gpg-agent.conf.in +++ /dev/null @@ -1,10 +0,0 @@ -pinentry-program @CMAKE_CURRENT_BINARY_DIR@/pinentry-fake.sh -###+++--- GPGConf ---+++### -allow-mark-trusted -debug-level basic -faked-system-time 20130110T154812 -log-file @CMAKE_CURRENT_BINARY_DIR@/gpg-agent.log -###+++--- GPGConf ---+++### Tue 29 Jun 2010 10:23:13 AM EDT -# GPGConf edited this configuration file. -# It will disable options before this marked block, but it will -# never change anything below these lines. diff --git a/framework/domain/mimetreeparser/tests/gnupg_home/gpg.conf b/framework/domain/mimetreeparser/tests/gnupg_home/gpg.conf deleted file mode 100644 index f1760823..00000000 --- a/framework/domain/mimetreeparser/tests/gnupg_home/gpg.conf +++ /dev/null @@ -1,244 +0,0 @@ -# Options for GnuPG -# Copyright 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. -# -# This file is free software; as a special exception the author gives -# unlimited permission to copy and/or distribute it, with or without -# modifications, as long as this notice is preserved. -# -# This file is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# -# Unless you specify which option file to use (with the command line -# option "--options filename"), GnuPG uses the file ~/.gnupg/gpg.conf -# by default. -# -# An options file can contain any long options which are available in -# GnuPG. If the first non white space character of a line is a '#', -# this line is ignored. Empty lines are also ignored. -# -# See the man page for a list of options. - -# Uncomment the following option to get rid of the copyright notice - -#no-greeting - -# If you have more than 1 secret key in your keyring, you may want to -# uncomment the following option and set your preferred keyid. - -#default-key 621CC013 - -# If you do not pass a recipient to gpg, it will ask for one. Using -# this option you can encrypt to a default key. Key validation will -# not be done in this case. The second form uses the default key as -# default recipient. - -#default-recipient some-user-id -#default-recipient-self - -# Use --encrypt-to to add the specified key as a recipient to all -# messages. This is useful, for example, when sending mail through a -# mail client that does not automatically encrypt mail to your key. -# In the example, this option allows you to read your local copy of -# encrypted mail that you've sent to others. - -#encrypt-to some-key-id - -# By default GnuPG creates version 3 signatures for data files. This -# is not strictly OpenPGP compliant but PGP 6 and most versions of PGP -# 7 require them. To disable this behavior, you may use this option -# or --openpgp. - -#no-force-v3-sigs - -# Because some mailers change lines starting with "From " to ">From " -# it is good to handle such lines in a special way when creating -# cleartext signatures; all other PGP versions do it this way too. - -#no-escape-from-lines - -# If you do not use the Latin-1 (ISO-8859-1) charset, you should tell -# GnuPG which is the native character set. Please check the man page -# for supported character sets. This character set is only used for -# metadata and not for the actual message which does not undergo any -# translation. Note that future version of GnuPG will change to UTF-8 -# as default character set. In most cases this option is not required -# as GnuPG is able to figure out the correct charset at runtime. - -#charset utf-8 - -# Group names may be defined like this: -# group mynames = paige 0x12345678 joe patti -# -# Any time "mynames" is a recipient (-r or --recipient), it will be -# expanded to the names "paige", "joe", and "patti", and the key ID -# "0x12345678". Note there is only one level of expansion - you -# cannot make an group that points to another group. Note also that -# if there are spaces in the recipient name, this will appear as two -# recipients. In these cases it is better to use the key ID. - -#group mynames = paige 0x12345678 joe patti - -# Lock the file only once for the lifetime of a process. If you do -# not define this, the lock will be obtained and released every time -# it is needed, which is usually preferable. - -#lock-once - -# GnuPG can send and receive keys to and from a keyserver. These -# servers can be HKP, email, or LDAP (if GnuPG is built with LDAP -# support). -# -# Example HKP keyserver: -# hkp://keys.gnupg.net -# hkp://subkeys.pgp.net -# -# Example email keyserver: -# mailto:pgp-public-keys@keys.pgp.net -# -# Example LDAP keyservers: -# ldap://keyserver.pgp.com -# -# Regular URL syntax applies, and you can set an alternate port -# through the usual method: -# hkp://keyserver.example.net:22742 -# -# Most users just set the name and type of their preferred keyserver. -# Note that most servers (with the notable exception of -# ldap://keyserver.pgp.com) synchronize changes with each other. Note -# also that a single server name may actually point to multiple -# servers via DNS round-robin. hkp://keys.gnupg.net is an example of -# such a "server", which spreads the load over a number of physical -# servers. To see the IP address of the server actually used, you may use -# the "--keyserver-options debug". - -keyserver hkp://keys.gnupg.net -#keyserver mailto:pgp-public-keys@keys.nl.pgp.net -#keyserver ldap://keyserver.pgp.com - -# Common options for keyserver functions: -# -# include-disabled : when searching, include keys marked as "disabled" -# on the keyserver (not all keyservers support this). -# -# no-include-revoked : when searching, do not include keys marked as -# "revoked" on the keyserver. -# -# verbose : show more information as the keys are fetched. -# Can be used more than once to increase the amount -# of information shown. -# -# use-temp-files : use temporary files instead of a pipe to talk to the -# keyserver. Some platforms (Win32 for one) always -# have this on. -# -# keep-temp-files : do not delete temporary files after using them -# (really only useful for debugging) -# -# http-proxy="proxy" : set the proxy to use for HTTP and HKP keyservers. -# This overrides the "http_proxy" environment variable, -# if any. -# -# auto-key-retrieve : automatically fetch keys as needed from the keyserver -# when verifying signatures or when importing keys that -# have been revoked by a revocation key that is not -# present on the keyring. -# -# no-include-attributes : do not include attribute IDs (aka "photo IDs") -# when sending keys to the keyserver. - -#keyserver-options auto-key-retrieve - -# Display photo user IDs in key listings - -# list-options show-photos - -# Display photo user IDs when a signature from a key with a photo is -# verified - -# verify-options show-photos - -# Use this program to display photo user IDs -# -# %i is expanded to a temporary file that contains the photo. -# %I is the same as %i, but the file isn't deleted afterwards by GnuPG. -# %k is expanded to the key ID of the key. -# %K is expanded to the long OpenPGP key ID of the key. -# %t is expanded to the extension of the image (e.g. "jpg"). -# %T is expanded to the MIME type of the image (e.g. "image/jpeg"). -# %f is expanded to the fingerprint of the key. -# %% is %, of course. -# -# If %i or %I are not present, then the photo is supplied to the -# viewer on standard input. If your platform supports it, standard -# input is the best way to do this as it avoids the time and effort in -# generating and then cleaning up a secure temp file. -# -# If no photo-viewer is provided, GnuPG will look for xloadimage, eog, -# or display (ImageMagick). On Mac OS X and Windows, the default is -# to use your regular JPEG image viewer. -# -# Some other viewers: -# photo-viewer "qiv %i" -# photo-viewer "ee %i" -# -# This one saves a copy of the photo ID in your home directory: -# photo-viewer "cat > ~/photoid-for-key-%k.%t" -# -# Use your MIME handler to view photos: -# photo-viewer "metamail -q -d -b -c %T -s 'KeyID 0x%k' -f GnuPG" - -# Passphrase agent -# -# We support the old experimental passphrase agent protocol as well as -# the new Assuan based one (currently available in the "newpg" package -# at ftp.gnupg.org/gcrypt/alpha/aegypten/). To make use of the agent, -# you have to run an agent as daemon and use the option -# -# use-agent -# -# which tries to use the agent but will fallback to the regular mode -# if there is a problem connecting to the agent. The normal way to -# locate the agent is by looking at the environment variable -# GPG_AGENT_INFO which should have been set during gpg-agent startup. -# In certain situations the use of this variable is not possible, thus -# the option -# -# --gpg-agent-info=::1 -# -# may be used to override it. - -# Automatic key location -# -# GnuPG can automatically locate and retrieve keys as needed using the -# auto-key-locate option. This happens when encrypting to an email -# address (in the "user@example.com" form), and there are no -# user@example.com keys on the local keyring. This option takes the -# following arguments, in the order they are to be tried: -# -# cert = locate a key using DNS CERT, as specified in RFC-4398. -# GnuPG can handle both the PGP (key) and IPGP (URL + fingerprint) -# CERT methods. -# -# pka = locate a key using DNS PKA. -# -# ldap = locate a key using the PGP Universal method of checking -# "ldap://keys.(thedomain)". For example, encrypting to -# user@example.com will check ldap://keys.example.com. -# -# keyserver = locate a key using whatever keyserver is defined using -# the keyserver option. -# -# You may also list arbitrary keyservers here by URL. -# -# Try CERT, then PKA, then LDAP, then hkp://subkeys.net: -#auto-key-locate cert pka ldap hkp://subkeys.pgp.net - -###+++--- GPGConf ---+++### -utf8-strings -#debug-level basic -#log-file socket:///home/leo/kde/src/kdepim/messagecomposer/tests/gnupg_home/log-socket -###+++--- GPGConf ---+++### Tue 29 Jun 2010 10:23:13 AM EDT -# GPGConf edited this configuration file. -# It will disable options before this marked block, but it will -# never change anything below these lines. diff --git a/framework/domain/mimetreeparser/tests/gnupg_home/gpgsm.conf.in b/framework/domain/mimetreeparser/tests/gnupg_home/gpgsm.conf.in deleted file mode 100644 index 92b6119d..00000000 --- a/framework/domain/mimetreeparser/tests/gnupg_home/gpgsm.conf.in +++ /dev/null @@ -1,10 +0,0 @@ - -###+++--- GPGConf ---+++### -disable-crl-checks -debug-level basic -faked-system-time 20130110T154812 -log-file @CMAKE_CURRENT_BINARY_DIR@/gpgsm.log -###+++--- GPGConf ---+++### Tue 29 Jun 2010 10:23:13 AM EDT -# GPGConf edited this configuration file. -# It will disable options before this marked block, but it will -# never change anything below these lines. diff --git a/framework/domain/mimetreeparser/tests/gnupg_home/pinentry-fake.sh b/framework/domain/mimetreeparser/tests/gnupg_home/pinentry-fake.sh deleted file mode 100755 index 7135a942..00000000 --- a/framework/domain/mimetreeparser/tests/gnupg_home/pinentry-fake.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -echo "OK Your orders please" -while : -do - read cmd - echo "OK" - [ "$cmd" = "BYE" ] && break -done diff --git a/framework/domain/mimetreeparser/tests/gnupg_home/private-keys-v1.d/1AA8BA52430E51AE249AF0DA97D59F869E4101A8.key b/framework/domain/mimetreeparser/tests/gnupg_home/private-keys-v1.d/1AA8BA52430E51AE249AF0DA97D59F869E4101A8.key deleted file mode 100644 index 39ac307b..00000000 Binary files a/framework/domain/mimetreeparser/tests/gnupg_home/private-keys-v1.d/1AA8BA52430E51AE249AF0DA97D59F869E4101A8.key and /dev/null differ diff --git a/framework/domain/mimetreeparser/tests/gnupg_home/pubring.gpg b/framework/domain/mimetreeparser/tests/gnupg_home/pubring.gpg deleted file mode 100644 index 2e00fa24..00000000 Binary files a/framework/domain/mimetreeparser/tests/gnupg_home/pubring.gpg and /dev/null differ diff --git a/framework/domain/mimetreeparser/tests/gnupg_home/pubring.kbx b/framework/domain/mimetreeparser/tests/gnupg_home/pubring.kbx deleted file mode 100644 index 0230f313..00000000 Binary files a/framework/domain/mimetreeparser/tests/gnupg_home/pubring.kbx and /dev/null differ diff --git a/framework/domain/mimetreeparser/tests/gnupg_home/scdaemon.conf b/framework/domain/mimetreeparser/tests/gnupg_home/scdaemon.conf deleted file mode 100644 index a17a0354..00000000 --- a/framework/domain/mimetreeparser/tests/gnupg_home/scdaemon.conf +++ /dev/null @@ -1,8 +0,0 @@ - -###+++--- GPGConf ---+++### -debug-level basic -log-file socket:///home/leo/kde/src/kdepim/messagecomposer/tests/gnupg_home/log-socket -###+++--- GPGConf ---+++### Tue 29 Jun 2010 10:23:13 AM EDT -# GPGConf edited this configuration file. -# It will disable options before this marked block, but it will -# never change anything below these lines. diff --git a/framework/domain/mimetreeparser/tests/gnupg_home/secring.gpg b/framework/domain/mimetreeparser/tests/gnupg_home/secring.gpg deleted file mode 100644 index cfd3387d..00000000 Binary files a/framework/domain/mimetreeparser/tests/gnupg_home/secring.gpg and /dev/null differ diff --git a/framework/domain/mimetreeparser/tests/gnupg_home/trustdb.gpg b/framework/domain/mimetreeparser/tests/gnupg_home/trustdb.gpg deleted file mode 100644 index 70089c15..00000000 Binary files a/framework/domain/mimetreeparser/tests/gnupg_home/trustdb.gpg and /dev/null differ diff --git a/framework/domain/mimetreeparser/tests/gnupg_home/trustlist.txt b/framework/domain/mimetreeparser/tests/gnupg_home/trustlist.txt deleted file mode 100644 index bbb0442d..00000000 --- a/framework/domain/mimetreeparser/tests/gnupg_home/trustlist.txt +++ /dev/null @@ -1,11 +0,0 @@ -5E:7C:B2:F4:9F:70:05:43:42:32:5D:75:74:70:00:09:B9:D8:08:61 S - - - -# CN=unittest cert -# O=KDAB -# C=US -# EMail=test@example.com -24:D2:FC:A2:2E:B3:B8:0A:1E:37:71:D1:4C:C6:58:E3:21:2B:49:DC S - - diff --git a/framework/domain/mimetreeparser/tests/gpgerrortest.cpp b/framework/domain/mimetreeparser/tests/gpgerrortest.cpp deleted file mode 100644 index 4254d972..00000000 --- a/framework/domain/mimetreeparser/tests/gpgerrortest.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* - Copyright (c) 2016 Sandro Knauß - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#include "interface.h" -#include "interface_p.h" - -#include -#include -#include -#include - -#include -#include -#include -#include - -QByteArray readMailFromFile(const QString &mailFile) -{ - QFile file(QLatin1String(MAIL_DATA_DIR) + QLatin1Char('/') + mailFile); - file.open(QIODevice::ReadOnly); - Q_ASSERT(file.isOpen()); - return file.readAll(); -} - -void killAgent(const QString& dir) -{ - QProcess proc; - proc.setProgram(QStringLiteral("gpg-connect-agent")); - QStringList arguments; - arguments << "-S " << dir + "/S.gpg-agent"; - proc.start(); - proc.waitForStarted(); - proc.write("KILLAGENT\n"); - proc.write("BYE\n"); - proc.closeWriteChannel(); - proc.waitForFinished(); -} - -class GpgErrorTest : public QObject -{ - Q_OBJECT - -private slots: - - void testGpgConfiguredCorrectly() - { - setEnv("GNUPGHOME", GNUPGHOME); - - Parser parser(readMailFromFile("openpgp-inline-charset-encrypted.mbox")); - - auto contentPartList = parser.collectContentParts(); - QCOMPARE(contentPartList.size(), 1); - auto contentPart = contentPartList[0]; - QCOMPARE(contentPart->availableContents(), QVector() << "plaintext"); - auto contentList = contentPart->content("plaintext"); - QVERIFY(contentList[0]->content().startsWith("asdasd")); - QCOMPARE(contentList[0]->encryptions().size(), 1); - auto enc = contentList[0]->encryptions()[0]; - QCOMPARE(enc->errorType(), Encryption::NoError); - QCOMPARE(enc->errorString(), QString()); - QCOMPARE((int) enc->recipients().size(), 2); - } - - void testNoGPGInstalled_data() - { - QTest::addColumn("mailFileName"); - - QTest::newRow("openpgp-inline-charset-encrypted") << "openpgp-inline-charset-encrypted.mbox"; - QTest::newRow("openpgp-encrypted-attachment-and-non-encrypted-attachment") << "openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox"; - QTest::newRow("smime-encrypted") << "smime-encrypted.mbox"; - } - - void testNoGPGInstalled() - { - QFETCH(QString, mailFileName); - - setEnv("PATH", "/nonexististing"); - setGpgMEfname("/nonexisting/gpg", ""); - - Parser parser(readMailFromFile(mailFileName)); - auto contentPartList = parser.collectContentParts(); - - QCOMPARE(contentPartList.size(), 1); - auto contentPart = contentPartList[0]; - QCOMPARE(contentPart->availableContents(), QVector() << "plaintext"); - auto contentList = contentPart->content("plaintext"); - QCOMPARE(contentList[0]->encryptions().size(), 1); - QVERIFY(contentList[0]->content().isEmpty()); - auto enc = contentList[0]->encryptions()[0]; - qDebug() << "HUHU"<< enc->errorType(); - QCOMPARE(enc->errorType(), Encryption::UnknownError); - QCOMPARE(enc->errorString(), QString("Crypto plug-in \"OpenPGP\" could not decrypt the data.
Error: No data")); - QCOMPARE((int) enc->recipients().size(), 0); - } - - void testGpgIncorrectGPGHOME_data() - { - QTest::addColumn("mailFileName"); - - QTest::newRow("openpgp-inline-charset-encrypted") << "openpgp-inline-charset-encrypted.mbox"; - QTest::newRow("openpgp-encrypted-attachment-and-non-encrypted-attachment") << "openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox"; - QTest::newRow("smime-encrypted") << "smime-encrypted.mbox"; - } - - void testGpgIncorrectGPGHOME() - { - QFETCH(QString, mailFileName); - setEnv("GNUPGHOME", QByteArray(GNUPGHOME) + QByteArray("noexist")); - - Parser parser(readMailFromFile(mailFileName)); - - auto contentPartList = parser.collectContentParts(); - QCOMPARE(contentPartList.size(), 1); - auto contentPart = contentPartList[0]; - QCOMPARE(contentPart->availableContents(), QVector() << "plaintext"); - auto contentList = contentPart->content("plaintext"); - QCOMPARE(contentList[0]->encryptions().size(), 1); - QCOMPARE(contentList[0]->signatures().size(), 0); - QVERIFY(contentList[0]->content().isEmpty()); - auto enc = contentList[0]->encryptions()[0]; - qDebug() << enc->errorType(); - QCOMPARE(enc->errorType(), Encryption::KeyMissing); - QCOMPARE(enc->errorString(), QString("Crypto plug-in \"OpenPGP\" could not decrypt the data.
Error: Decryption failed")); - QCOMPARE((int) enc->recipients().size(), 2); - } - -public Q_SLOTS: - void init() - { - mResetGpgmeEngine = false; - mModifiedEnv.clear(); - { - QGpgME::openpgp(); // We need to intialize it, otherwise ctx will be a nullpointer - const GpgME::Context *ctx = GpgME::Context::createForProtocol(GpgME::Protocol::OpenPGP); - const auto engineinfo = ctx->engineInfo(); - mGpgmeEngine_fname = engineinfo.fileName(); - } - mEnv = QProcessEnvironment::systemEnvironment(); - unsetEnv("GNUPGHOME"); - } - - void cleanup() - { - QCoreApplication::sendPostedEvents(); - - const QString &gnupghome = qgetenv("GNUPGHOME"); - if (!gnupghome.isEmpty()) { - killAgent(gnupghome); - } - - resetGpgMfname(); - resetEnv(); - } -private: - void unsetEnv(const QByteArray &name) - { - mModifiedEnv << name; - unsetenv(name); - } - - void setEnv(const QByteArray &name, const QByteArray &value) - { - mModifiedEnv << name; - setenv(name, value , 1); - } - - void resetEnv() - { - foreach(const auto &i, mModifiedEnv) { - if (mEnv.contains(i)) { - setenv(i, mEnv.value(i).toUtf8(), 1); - } else { - unsetenv(i); - } - } - } - - void resetGpgMfname() - { - if (mResetGpgmeEngine) { - gpgme_set_engine_info (GPGME_PROTOCOL_OpenPGP, mGpgmeEngine_fname, NULL); - } - } - - void setGpgMEfname(const QByteArray &fname, const QByteArray &homedir) - { - mResetGpgmeEngine = true; - gpgme_set_engine_info (GPGME_PROTOCOL_OpenPGP, fname, homedir); - } - - QSet mModifiedEnv; - QProcessEnvironment mEnv; - bool mResetGpgmeEngine; - QByteArray mGpgmeEngine_fname; -}; - -QTEST_GUILESS_MAIN(GpgErrorTest) -#include "gpgerrortest.moc" diff --git a/framework/domain/mimetreeparser/tests/interfacetest.cpp b/framework/domain/mimetreeparser/tests/interfacetest.cpp deleted file mode 100644 index 3ae32a4a..00000000 --- a/framework/domain/mimetreeparser/tests/interfacetest.cpp +++ /dev/null @@ -1,310 +0,0 @@ -/* - Copyright (c) 2016 Sandro Knauß - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#include "interface.h" -#include "interface_p.h" - -#include - -QByteArray readMailFromFile(const QString &mailFile) -{ - QFile file(QLatin1String(MAIL_DATA_DIR) + QLatin1Char('/') + mailFile); - file.open(QIODevice::ReadOnly); - Q_ASSERT(file.isOpen()); - return file.readAll(); -} - -QByteArray join(QVector vec, QByteArray sep) -{ - QByteArray ret; - bool bInit = true; - foreach(const auto &entry, vec) { - if (!bInit) { - ret += sep; - } - bInit = false; - ret += entry; - } - return ret; -} - -class InterfaceTest : public QObject -{ - Q_OBJECT -private: - void printTree(const Part::Ptr &start, QString pre) - { - foreach (const auto &part, start->subParts()) { - qWarning() << QStringLiteral("%1* %2(%3)") - .arg(pre) - .arg(QString::fromLatin1(part->type())) - .arg(QString::fromLatin1(join(part->availableContents(),", "))); - printTree(part,pre + QStringLiteral(" ")); - } - } - -private slots: - - void testTextMail() - { - Parser parser(readMailFromFile("plaintext.mbox")); - printTree(parser.d->mTree,QString()); - auto contentPartList = parser.collectContentParts(); - QCOMPARE(contentPartList.size(), 1); - auto contentPart = contentPartList[0]; - QVERIFY((bool)contentPart); - QCOMPARE(contentPart->availableContents(), QVector() << "plaintext"); - auto contentList = contentPart->content("plaintext"); - QCOMPARE(contentList.size(), 1); - QCOMPARE(contentList[0]->content(), QStringLiteral("If you can see this text it means that your email client couldn't display our newsletter properly.\nPlease visit this link to view the newsletter on our website: http://www.gog.com/newsletter/").toLocal8Bit()); - QCOMPARE(contentList[0]->charset(), QStringLiteral("utf-8").toLocal8Bit()); - QCOMPARE(contentList[0]->encryptions().size(), 0); - QCOMPARE(contentList[0]->signatures().size(), 0); - - contentList = contentPart->content("html"); - QCOMPARE(contentList.size(), 0); - auto contentAttachmentList = parser.collectAttachmentParts(); - QCOMPARE(contentAttachmentList.size(), 0); - } - - void testTextAlternative() - { - Parser parser(readMailFromFile("alternative.mbox")); - printTree(parser.d->mTree,QString()); - auto contentPartList = parser.collectContentParts(); - QCOMPARE(contentPartList.size(), 1); - auto contentPart = contentPartList[0]; - QVERIFY((bool)contentPart); - QCOMPARE(contentPart->availableContents(), QVector() << "html" << "plaintext"); - auto contentList = contentPart->content("plaintext"); - QCOMPARE(contentList.size(), 1); - QCOMPARE(contentList[0]->content(), QStringLiteral("If you can see this text it means that your email client couldn't display our newsletter properly.\nPlease visit this link to view the newsletter on our website: http://www.gog.com/newsletter/\n").toLocal8Bit()); - QCOMPARE(contentList[0]->charset(), QStringLiteral("utf-8").toLocal8Bit()); - QCOMPARE(contentList[0]->encryptions().size(), 0); - QCOMPARE(contentList[0]->signatures().size(), 0); - - contentList = contentPart->content("html"); - QCOMPARE(contentList.size(), 1); - QCOMPARE(contentList[0]->content(), QStringLiteral("

HTML text

\n\n").toLocal8Bit()); - QCOMPARE(contentList[0]->charset(), QStringLiteral("utf-8").toLocal8Bit()); - QCOMPARE(contentList[0]->encryptions().size(), 0); - QCOMPARE(contentList[0]->signatures().size(), 0); - auto contentAttachmentList = parser.collectAttachmentParts(); - QCOMPARE(contentAttachmentList.size(), 0); - } - - void testTextHtml() - { - Parser parser(readMailFromFile("html.mbox")); - printTree(parser.d->mTree,QString()); - auto contentPartList = parser.collectContentParts(); - QCOMPARE(contentPartList.size(), 1); - auto contentPart = contentPartList[0]; - QVERIFY((bool)contentPart); - QCOMPARE(contentPart->availableContents(), QVector() << "html"); - - auto contentList = contentPart->content("plaintext"); - QCOMPARE(contentList.size(), 0); - - contentList = contentPart->content("html"); - QCOMPARE(contentList.size(), 1); - QCOMPARE(contentList[0]->content(), QStringLiteral("

HTML text

").toLocal8Bit()); - QCOMPARE(contentList[0]->charset(), QStringLiteral("utf-8").toLocal8Bit()); - QCOMPARE(contentList[0]->encryptions().size(), 0); - QCOMPARE(contentList[0]->signatures().size(), 0); - auto contentAttachmentList = parser.collectAttachmentParts(); - QCOMPARE(contentAttachmentList.size(), 0); - } - - void testSMimeEncrypted() - { - Parser parser(readMailFromFile("smime-encrypted.mbox")); - printTree(parser.d->mTree,QString()); - auto contentPartList = parser.collectContentParts(); - QCOMPARE(contentPartList.size(), 1); - auto contentPart = contentPartList[0]; - QVERIFY((bool)contentPart); - QCOMPARE(contentPart->availableContents(), QVector() << "plaintext"); - auto contentList = contentPart->content("plaintext"); - QCOMPARE(contentList.size(), 1); - QCOMPARE(contentList[0]->content(), QStringLiteral("The quick brown fox jumped over the lazy dog.").toLocal8Bit()); - QCOMPARE(contentList[0]->charset(), QStringLiteral("us-ascii").toLocal8Bit()); - QCOMPARE(contentList[0]->encryptions().size(), 1); - QCOMPARE(contentList[0]->signatures().size(), 0); - auto contentAttachmentList = parser.collectAttachmentParts(); - QCOMPARE(contentAttachmentList.size(), 0); - } - - void testOpenPGPEncryptedAttachment() - { - Parser parser(readMailFromFile("openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox")); - printTree(parser.d->mTree,QString()); - auto contentPartList = parser.collectContentParts(); - QCOMPARE(contentPartList.size(), 1); - auto contentPart = contentPartList[0]; - QVERIFY((bool)contentPart); - QCOMPARE(contentPart->availableContents(), QVector() << "plaintext"); - auto contentList = contentPart->content("plaintext"); - QCOMPARE(contentList.size(), 1); - QCOMPARE(contentList[0]->content(), QStringLiteral("test text").toLocal8Bit()); - QCOMPARE(contentList[0]->charset(), QStringLiteral("us-ascii").toLocal8Bit()); - QCOMPARE(contentList[0]->encryptions().size(), 1); - QCOMPARE(contentList[0]->signatures().size(), 1); - auto contentAttachmentList = parser.collectAttachmentParts(); - QCOMPARE(contentAttachmentList.size(), 2); - QCOMPARE(contentAttachmentList[0]->availableContents(), QVector() << "text/plain"); - QCOMPARE(contentAttachmentList[0]->content().size(), 1); - QCOMPARE(contentAttachmentList[0]->encryptions().size(), 1); - QCOMPARE(contentAttachmentList[0]->signatures().size(), 1); - QCOMPARE(contentAttachmentList[1]->availableContents(), QVector() << "image/png"); - QCOMPARE(contentAttachmentList[1]->content().size(), 1); - QCOMPARE(contentAttachmentList[1]->encryptions().size(), 0); - QCOMPARE(contentAttachmentList[1]->signatures().size(), 0); - } - - void testOpenPGPInline() - { - Parser parser(readMailFromFile("openpgp-inline-charset-encrypted.mbox")); - printTree(parser.d->mTree,QString()); - auto contentPartList = parser.collectContentParts(); - QCOMPARE(contentPartList.size(), 1); - auto contentPart = contentPartList[0]; - QVERIFY((bool)contentPart); - QCOMPARE(contentPart->availableContents(), QVector() << "plaintext"); - QCOMPARE(contentPart->encryptions().size(), 0); - QCOMPARE(contentPart->signatures().size(), 0); - auto contentList = contentPart->content("plaintext"); - QCOMPARE(contentList.size(), 1); - QCOMPARE(contentList[0]->content(), QStringLiteral("asdasd asd asd asdf sadf sdaf sadf äöü").toLocal8Bit()); - QCOMPARE(contentList[0]->charset(), QStringLiteral("ISO-8859-15").toLocal8Bit()); - QCOMPARE(contentList[0]->encryptions().size(), 1); - QCOMPARE(contentList[0]->signatures().size(), 1); - auto contentAttachmentList = parser.collectAttachmentParts(); - QCOMPARE(contentAttachmentList.size(), 0); - } - - void testOpenPPGInlineWithNonEncText() - { - Parser parser(readMailFromFile("openpgp-inline-encrypted+nonenc.mbox")); - printTree(parser.d->mTree,QString()); - auto contentPartList = parser.collectContentParts(); - QCOMPARE(contentPartList.size(), 1); - auto contentPart = contentPartList[0]; - QVERIFY((bool)contentPart); - QCOMPARE(contentPart->availableContents(), QVector() << "plaintext"); - QCOMPARE(contentPart->encryptions().size(), 0); - QCOMPARE(contentPart->signatures().size(), 0); - auto contentList = contentPart->content("plaintext"); - QCOMPARE(contentList.size(), 2); - QCOMPARE(contentList[0]->content(), QStringLiteral("Not encrypted not signed :(\n\n").toLocal8Bit()); - QCOMPARE(contentList[0]->charset(), QStringLiteral("us-ascii").toLocal8Bit()); - QCOMPARE(contentList[0]->encryptions().size(), 0); - QCOMPARE(contentList[0]->signatures().size(), 0); - QCOMPARE(contentList[1]->content(), QStringLiteral("some random text").toLocal8Bit()); - QCOMPARE(contentList[1]->charset(), QStringLiteral("us-ascii").toLocal8Bit()); - QCOMPARE(contentList[1]->encryptions().size(), 1); - QCOMPARE(contentList[1]->signatures().size(), 0); - auto contentAttachmentList = parser.collectAttachmentParts(); - QCOMPARE(contentAttachmentList.size(), 0); - } - - void testEncryptionBlock() - { - Parser parser(readMailFromFile("openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox")); - auto contentPartList = parser.collectContentParts(); - auto contentPart = contentPartList[0]; - auto contentList = contentPart->content("plaintext"); - QCOMPARE(contentList.size(), 1); - QCOMPARE(contentList[0]->encryptions().size(), 1); - auto enc = contentList[0]->encryptions()[0]; - QCOMPARE((int) enc->recipients().size(), 2); - - auto r = enc->recipients()[0]; - QCOMPARE(r->keyid(),QStringLiteral("14B79E26050467AA")); - QCOMPARE(r->name(),QStringLiteral("kdetest")); - QCOMPARE(r->email(),QStringLiteral("you@you.com")); - QCOMPARE(r->comment(),QStringLiteral("")); - - r = enc->recipients()[1]; - QCOMPARE(r->keyid(),QStringLiteral("8D9860C58F246DE6")); - QCOMPARE(r->name(),QStringLiteral("unittest key")); - QCOMPARE(r->email(),QStringLiteral("test@kolab.org")); - QCOMPARE(r->comment(),QStringLiteral("no password")); - } - - void testSignatureBlock() - { - Parser parser(readMailFromFile("openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox")); - auto contentPartList = parser.collectContentParts(); - auto contentPart = contentPartList[0]; - auto contentList = contentPart->content("plaintext"); - QCOMPARE(contentList.size(), 1); - QCOMPARE(contentList[0]->signatures().size(), 1); - auto sig = contentList[0]->signatures()[0]; - QCOMPARE(sig->creationDateTime(), QDateTime(QDate(2015,05,01),QTime(15,12,47))); - QCOMPARE(sig->expirationDateTime(), QDateTime()); - QCOMPARE(sig->neverExpires(), true); - - auto key = sig->key(); - QCOMPARE(key->keyid(),QStringLiteral("8D9860C58F246DE6")); - QCOMPARE(key->name(),QStringLiteral("unittest key")); - QCOMPARE(key->email(),QStringLiteral("test@kolab.org")); - QCOMPARE(key->comment(),QStringLiteral("no password")); - } - - void testRelatedAlternative() - { - Parser parser(readMailFromFile("cid-links.mbox")); - printTree(parser.d->mTree,QString()); - auto contentPartList = parser.collectContentParts(); - QCOMPARE(contentPartList.size(), 1); - auto contentPart = contentPartList[0]; - QVERIFY((bool)contentPart); - QCOMPARE(contentPart->availableContents(), QVector() << "html" << "plaintext"); - QCOMPARE(contentPart->encryptions().size(), 0); - QCOMPARE(contentPart->signatures().size(), 0); - auto contentList = contentPart->content("plaintext"); - QCOMPARE(contentList.size(), 1); - auto contentAttachmentList = parser.collectAttachmentParts(); - QCOMPARE(contentAttachmentList.size(), 0); - } - - void testAttachmentPart() - { - Parser parser(readMailFromFile("cid-links.mbox")); - const auto relatedImage = parser.d->mTree->subParts().at(1); - QCOMPARE(relatedImage->availableContents(), QVector() << "image/jpeg"); - auto contentList = relatedImage->content(); - QCOMPARE(contentList.size(), 1); - contentList = relatedImage->content("image/jpeg"); - QCOMPARE(contentList.size(), 1); - } - - void testCidLink() - { - Parser parser(readMailFromFile("cid-links.mbox")); - printTree(parser.d->mTree,QString()); - QCOMPARE(parser.getPart(QUrl("cid:9359201d15e53f31a68c307b3369b6@info")), parser.d->mTree->subParts().at(1)); - QVERIFY(!parser.getPart(QUrl("cid:"))); - QVERIFY(!parser.getPart(QUrl("cid:unknown"))); - } -}; - -QTEST_GUILESS_MAIN(InterfaceTest) -#include "interfacetest.moc" diff --git a/framework/domain/mimetreeparser/tests/kdepim_add_gpg_crypto_test.cmake b/framework/domain/mimetreeparser/tests/kdepim_add_gpg_crypto_test.cmake deleted file mode 100644 index 281752a7..00000000 --- a/framework/domain/mimetreeparser/tests/kdepim_add_gpg_crypto_test.cmake +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) 2013 Sandro Knauß -# -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. - -set( GNUPGHOME ${CMAKE_BINARY_DIR}/framework/domain/mimetreeparser/tests/gnupg_home ) -add_definitions( -DGNUPGHOME="${GNUPGHOME}" ) - -macro (ADD_GPG_CRYPTO_TEST _target _testname) - if (UNIX) - if (APPLE) - set(_library_path_variable "DYLD_LIBRARY_PATH") - elseif (CYGWIN) - set(_library_path_variable "PATH") - else (APPLE) - set(_library_path_variable "LD_LIBRARY_PATH") - endif (APPLE) - - if (APPLE) - # DYLD_LIBRARY_PATH does not work like LD_LIBRARY_PATH - # OSX already has the RPATH in libraries and executables, putting runtime directories in - # DYLD_LIBRARY_PATH actually breaks things - set(_ld_library_path "${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}/") - else (APPLE) - set(_ld_library_path "${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}/:${LIB_INSTALL_DIR}:${QT_LIBRARY_DIR}") - endif (APPLE) - set(_executable "$") - - # use add_custom_target() to have the sh-wrapper generated during build time instead of cmake time - add_custom_command(TARGET ${_target} POST_BUILD - COMMAND ${CMAKE_COMMAND} - -D_filename=${_executable}.shell -D_library_path_variable=${_library_path_variable} - -D_ld_library_path="${_ld_library_path}" -D_executable=$ - -D_gnupghome="${GNUPGHOME}" - -P ${CMAKE_SOURCE_DIR}/framework/domain/mimetreeparser/tests/kdepim_generate_crypto_test_wrapper.cmake - ) - - set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${_executable}.shell" ) - add_test(NAME ${_testname} COMMAND ${_executable}.shell) - - else (UNIX) - # under windows, set the property WRAPPER_SCRIPT just to the name of the executable - # maybe later this will change to a generated batch file (for setting the PATH so that the Qt libs are found) - set(_ld_library_path "${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}\;${LIB_INSTALL_DIR}\;${QT_LIBRARY_DIR}") - set(_executable "$") - - # use add_custom_target() to have the batch-file-wrapper generated during build time instead of cmake time - add_custom_command(TARGET ${_target} POST_BUILD - COMMAND ${CMAKE_COMMAND} - -D_filename="${_executable}.bat" - -D_ld_library_path="${_ld_library_path}" -D_executable="${_executable}" - -D_gnupghome="${GNUPGHOME}" - -P ${CMAKE_SOURCE_DIR}/framework/domain/mimetreeparser/tests/kdepim_generate_crypto_test_wrapper.cmake - ) - - add_test(NAME ${_testname} COMMAND ${_executable}.bat) - - endif (UNIX) -endmacro (ADD_GPG_CRYPTO_TEST) - diff --git a/framework/domain/mimetreeparser/tests/kdepim_generate_crypto_test_wrapper.cmake b/framework/domain/mimetreeparser/tests/kdepim_generate_crypto_test_wrapper.cmake deleted file mode 100644 index e1412f37..00000000 --- a/framework/domain/mimetreeparser/tests/kdepim_generate_crypto_test_wrapper.cmake +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) 2006, Alexander Neundorf, -# Copyright (c) 2013, Sandro Knauß -# -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. - - -if (UNIX) - -file(WRITE "${_filename}" -"#!/bin/sh -# created by cmake, don't edit, changes will be lost - -# don't mess with a gpg-agent already running on the system -unset GPG_AGENT_INFO - -${_library_path_variable}=${_ld_library_path}\${${_library_path_variable}:+:\$${_library_path_variable}} GNUPGHOME=${_gnupghome} gpg-agent --daemon \"${_executable}\" \"$@\" -_result=$? -_pid=`echo GETINFO pid | GNUPGHOME=${_gnupghome} gpg-connect-agent | grep 'D' | cut -d' ' -f2` -if [ ! -z \"\$_pid\" ]; then - echo \"Waiting for gpg-agent to terminate (PID: $_pid)...\" - while kill -0 \"\$_pid\"; do - sleep 1 - done -fi -exit \$_result -") - -# make it executable -# since this is only executed on UNIX, it is safe to call chmod -exec_program(chmod ARGS ug+x \"${_filename}\" OUTPUT_VARIABLE _dummy ) - -else (UNIX) - -file(TO_NATIVE_PATH "${_ld_library_path}" win_path) -file(TO_NATIVE_PATH "${_gnupghome}" win_gnupghome) - -file(WRITE "${_filename}" -" -set PATH=${win_path};$ENV{PATH} -set GNUPGHOME=${win_gnupghome};$ENV{GNUPGHOME} -gpg-agent --daemon \"${_executable}\" %* -") - -endif (UNIX) diff --git a/framework/domain/mimetreeparser/thoughts.txt b/framework/domain/mimetreeparser/thoughts.txt deleted file mode 100644 index 3340347a..00000000 --- a/framework/domain/mimetreeparser/thoughts.txt +++ /dev/null @@ -1,148 +0,0 @@ -Usecases: - -# plaintext msg + attachment -* ContentPart => cp1 -* AttachmentPart => ap1 - -(cp1) == collect(select=NoEncapsulatedMessages) -(ap1) == collect(select=NoEncapsulatedMessages) - -(PlainText) == cp1.availableContent() - -# html msg + related attachment + normal attachment -* ContentPart => cp1 -* AttachmentPart(mimetype="*/related", cid="12345678") => ap1 -* AttachmentPart => ap2 - -(cp1) == collect(select=NoEncapsulatedMessages) -(ap1, ap2) == collect(select=NoEncapsulatedMessages) -(ap2) == collect(select=NoEncapsulatedMessages, filter=filterelated) - -ap1 == getPart("cid:12345678") - -(Html) == cp1.availableContent() - -# alternative msg + attachment -* ContentPart(html=[Content("HTML"),], plaintext=[Content("Text"),]) => cp1 -* AttachmentPart => ap1 - -(cp1) == collect(select=NoEncapsulatedMessages) -(ap1) == collect(select=NoEncapsulatedMessages) - -(Html, PlainText) == cp1.availableContent() -[Content("HTML"),] == cp1.content(Html) -[Content("Text"),] == cp1.content(Plaintext) - -# alternative msg with GPGInlin -* ContentPart( - plaintext=[Content("Text"), Content("foo", encryption=(enc1))], - html=[Content("HTML"),] - ) => cp1 - -(Html, PlainText) == cp1.availableContent() - -[Content("HTML"),] == cp1.content(Html) -[Content("Text"),Content("foo", encryption=(enc1))] == cp1.content(Plaintext) - - -# encrypted msg (not encrypted/error) with unencrypted attachment -* EncryptionErrorPart => cp1 -* AttachmentPart => ap1 - -(cp1) == collect(select=NoEncapsulatedMessages) -(ap1) == collect(select=NoEncapsulatedMessages) - -#encrypted msg (decrypted with attachment) + unencrypted attachment -* encrytion=(rec1,rec2) => enc1 - * ContentPart(encrytion = (enc1,)) => cp1 - * AttachmentPart(encryption = (enc1,)) => ap1 -* AttachmentPart => ap2 - -(cp1) == collect(select=NoEncapsulatedMessages) -(ap1, ap2) == collect(select=NoEncapsulatedMessages) - -#INLINE GPG encrypted msg + attachment -* ContentPart => cp1 with - plaintext=[Content, Content(encrytion = (enc1(rec1,rec2),)), Content(signed = (sig1,)), Content] -* AttachmentPart => ap1 - -(cp1) == collect(select=NoEncapsulatedMessages) -(ap1) == collect(select=NoEncapsulatedMessages) - -[Content, Content(encrytion = (enc1(rec1,rec2),)), Content(signed = (sig1,)), Content] == cp1.content(Plaintext) - -#forwared encrypted msg + attachments -* ContentPart => cp1 -* EncapsulatedPart => ep1 - * Encrytion=(rec1,rec2) => enc1 - * Signature => sig1 - * ContentPart(encrytion = (enc1,), signature = (sig1,)) => cp2 - * Content(encrytion = (enc1,), signature = (sig1,)) - * Content(encrytion = (enc1, enc2(rec3,rec4),), signature = (sig1,)) - * AttachmentPart(encrytion = (enc1,), signature = (sig1,)) => ap1 -* AttachmentPart => ap2 - -(cp1) = collect(select=NoEncapsulatedMessages) -(ap2) = collect(select=NoEncapsulatedMessages) - -(cp2) = collect(ep1, select=NoEncapsulatedMessages) -(ap1) = collect(ep1, select=NoEncapsulatedMessages) - -(cp1, cp2) == collect() -(ap1, ap2) == collect()[Content, Content(encrytion = (enc1(rec1,rec2),)), Content(signed = (sig1,)), Content] - - -# plaintext msg + attachment + cert -* ContentPart => cp1 -* AttachmentPart => ap1 -* CertPart => cep1 - -(cp1) == collect(select=NoEncapsulatedMessages) -(ap1, cep1) == collect(select=NoEncapsulatedMessages) -(ap1) == collect(select=NoEncapsulatedMessages, filter=filterSubAttachmentParts) - -(cep1) == collect(select=NoEncapsulatedMessages) - - -collect function: - -bool noEncapsulatedMessages(Part part) -{ - if (is(part)) { - return false; - } - return true; -} - -bool filterRelated(T part) -{ - if (part.mimetype == related && !part.cid.isEmpty()) { - return false; //filter out related parts - } - return true; -} - -bool filterSubAttachmentParts(AttachmentPart part) -{ - if (isSubPart(part)) { - return false; // filter out CertPart f.ex. - } - return true; -} - -List collect(Part start, std::function select, std::function &)> filter) { - List col; - if (!select(start)) { - return col; - } - - if(isOrSubTypeIs(start) && filter(start.staticCast)){ - col.append(p); - } - foreach(childs as child) { - if (select(child)) { - col.expand(collect(child,select,filter); - } - } - return col; -} \ No newline at end of file diff --git a/framework/domain/modeltest.cpp b/framework/domain/modeltest.cpp deleted file mode 100644 index 2bd3d27f..00000000 --- a/framework/domain/modeltest.cpp +++ /dev/null @@ -1,588 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "modeltest.h" - -#include -#include - -#include -/*! - Connect to all of the models signals. Whenever anything happens recheck everything. -*/ -ModelTest::ModelTest ( QAbstractItemModel *_model, QObject *parent ) : QObject ( parent ), model ( _model ), fetchingMore ( false ) -{ - if (!model) - qFatal("%s: model must not be null", Q_FUNC_INFO); - - connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(runAllTests()) ); - connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(runAllTests()) ); - connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)), - this, SLOT(runAllTests()) ); - connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)), - this, SLOT(runAllTests()) ); - connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), - this, SLOT(runAllTests()) ); - connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), - this, SLOT(runAllTests()) ); - connect(model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(runAllTests()) ); - connect(model, SIGNAL(layoutChanged()), this, SLOT(runAllTests()) ); - connect(model, SIGNAL(modelReset()), this, SLOT(runAllTests()) ); - connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(runAllTests()) ); - connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(runAllTests()) ); - connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), - this, SLOT(runAllTests()) ); - connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(runAllTests()) ); - - // Special checks for changes - connect(model, SIGNAL(layoutAboutToBeChanged()), - this, SLOT(layoutAboutToBeChanged()) ); - connect(model, SIGNAL(layoutChanged()), - this, SLOT(layoutChanged()) ); - - connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), - this, SLOT(rowsAboutToBeInserted(QModelIndex,int,int)) ); - connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)) ); - connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), - this, SLOT(rowsInserted(QModelIndex,int,int)) ); - connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(rowsRemoved(QModelIndex,int,int)) ); - connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), - this, SLOT(dataChanged(QModelIndex,QModelIndex)) ); - connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), - this, SLOT(headerDataChanged(Qt::Orientation,int,int)) ); - - runAllTests(); -} - -void ModelTest::runAllTests() -{ - if ( fetchingMore ) - return; - nonDestructiveBasicTest(); - rowCount(); - columnCount(); - hasIndex(); - index(); - parent(); - data(); -} - -/*! - nonDestructiveBasicTest tries to call a number of the basic functions (not all) - to make sure the model doesn't outright segfault, testing the functions that makes sense. -*/ -void ModelTest::nonDestructiveBasicTest() -{ - QVERIFY( model->buddy ( QModelIndex() ) == QModelIndex() ); - model->canFetchMore ( QModelIndex() ); - QVERIFY( model->columnCount ( QModelIndex() ) >= 0 ); - QVERIFY( model->data ( QModelIndex() ) == QVariant() ); - fetchingMore = true; - model->fetchMore ( QModelIndex() ); - fetchingMore = false; - Qt::ItemFlags flags = model->flags ( QModelIndex() ); - QVERIFY( flags == Qt::ItemIsDropEnabled || flags == 0 ); - model->hasChildren ( QModelIndex() ); - model->hasIndex ( 0, 0 ); - model->headerData ( 0, Qt::Horizontal ); - model->index ( 0, 0 ); - model->itemData ( QModelIndex() ); - QVariant cache; - model->match ( QModelIndex(), -1, cache ); - model->mimeTypes(); - QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() ); - QVERIFY( model->rowCount() >= 0 ); - QVariant variant; - model->setData ( QModelIndex(), variant, -1 ); - model->setHeaderData ( -1, Qt::Horizontal, QVariant() ); - model->setHeaderData ( 999999, Qt::Horizontal, QVariant() ); - QMap roles; - model->sibling ( 0, 0, QModelIndex() ); - model->span ( QModelIndex() ); - model->supportedDropActions(); -} - -/*! - Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() - - Models that are dynamically populated are not as fully tested here. - */ -void ModelTest::rowCount() -{ -// qDebug() << "rc"; - // check top row - QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); - int rows = model->rowCount ( topIndex ); - QVERIFY( rows >= 0 ); - if ( rows > 0 ) - QVERIFY( model->hasChildren ( topIndex ) ); - - QModelIndex secondLevelIndex = model->index ( 0, 0, topIndex ); - if ( secondLevelIndex.isValid() ) { // not the top level - // check a row count where parent is valid - rows = model->rowCount ( secondLevelIndex ); - QVERIFY( rows >= 0 ); - if ( rows > 0 ) - QVERIFY( model->hasChildren ( secondLevelIndex ) ); - } - - // The models rowCount() is tested more extensively in checkChildren(), - // but this catches the big mistakes -} - -/*! - Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() - */ -void ModelTest::columnCount() -{ - // check top row - QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); - QVERIFY( model->columnCount ( topIndex ) >= 0 ); - - // check a column count where parent is valid - QModelIndex childIndex = model->index ( 0, 0, topIndex ); - if ( childIndex.isValid() ) - QVERIFY( model->columnCount ( childIndex ) >= 0 ); - - // columnCount() is tested more extensively in checkChildren(), - // but this catches the big mistakes -} - -/*! - Tests model's implementation of QAbstractItemModel::hasIndex() - */ -void ModelTest::hasIndex() -{ -// qDebug() << "hi"; - // Make sure that invalid values returns an invalid index - QVERIFY( !model->hasIndex ( -2, -2 ) ); - QVERIFY( !model->hasIndex ( -2, 0 ) ); - QVERIFY( !model->hasIndex ( 0, -2 ) ); - - int rows = model->rowCount(); - int columns = model->columnCount(); - - // check out of bounds - QVERIFY( !model->hasIndex ( rows, columns ) ); - QVERIFY( !model->hasIndex ( rows + 1, columns + 1 ) ); - - if ( rows > 0 ) - QVERIFY( model->hasIndex ( 0, 0 ) ); - - // hasIndex() is tested more extensively in checkChildren(), - // but this catches the big mistakes -} - -/*! - Tests model's implementation of QAbstractItemModel::index() - */ -void ModelTest::index() -{ -// qDebug() << "i"; - // Make sure that invalid values returns an invalid index - QVERIFY( model->index ( -2, -2 ) == QModelIndex() ); - QVERIFY( model->index ( -2, 0 ) == QModelIndex() ); - QVERIFY( model->index ( 0, -2 ) == QModelIndex() ); - - int rows = model->rowCount(); - int columns = model->columnCount(); - - if ( rows == 0 ) - return; - - // Catch off by one errors - QVERIFY( model->index ( rows, columns ) == QModelIndex() ); - QVERIFY( model->index ( 0, 0 ).isValid() ); - - // Make sure that the same index is *always* returned - QModelIndex a = model->index ( 0, 0 ); - QModelIndex b = model->index ( 0, 0 ); - QVERIFY( a == b ); - - // index() is tested more extensively in checkChildren(), - // but this catches the big mistakes -} - -/*! - Tests model's implementation of QAbstractItemModel::parent() - */ -void ModelTest::parent() -{ -// qDebug() << "p"; - // Make sure the model won't crash and will return an invalid QModelIndex - // when asked for the parent of an invalid index. - QVERIFY( model->parent ( QModelIndex() ) == QModelIndex() ); - - if ( model->rowCount() == 0 ) - return; - - // Column 0 | Column 1 | - // QModelIndex() | | - // \- topIndex | topIndex1 | - // \- childIndex | childIndex1 | - - // Common error test #1, make sure that a top level index has a parent - // that is a invalid QModelIndex. - QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); - QVERIFY( model->parent ( topIndex ) == QModelIndex() ); - - // Common error test #2, make sure that a second level index has a parent - // that is the first level index. - if ( model->rowCount ( topIndex ) > 0 ) { - QModelIndex childIndex = model->index ( 0, 0, topIndex ); - QVERIFY( model->parent ( childIndex ) == topIndex ); - } - - // Common error test #3, the second column should NOT have the same children - // as the first column in a row. - // Usually the second column shouldn't have children. - QModelIndex topIndex1 = model->index ( 0, 1, QModelIndex() ); - if ( model->rowCount ( topIndex1 ) > 0 ) { - QModelIndex childIndex = model->index ( 0, 0, topIndex ); - QModelIndex childIndex1 = model->index ( 0, 0, topIndex1 ); - QVERIFY( childIndex != childIndex1 ); - } - - // Full test, walk n levels deep through the model making sure that all - // parent's children correctly specify their parent. - checkChildren ( QModelIndex() ); -} - -/*! - Called from the parent() test. - - A model that returns an index of parent X should also return X when asking - for the parent of the index. - - This recursive function does pretty extensive testing on the whole model in an - effort to catch edge cases. - - This function assumes that rowCount(), columnCount() and index() already work. - If they have a bug it will point it out, but the above tests should have already - found the basic bugs because it is easier to figure out the problem in - those tests then this one. - */ -void ModelTest::checkChildren ( const QModelIndex &parent, int currentDepth ) -{ - // First just try walking back up the tree. - QModelIndex p = parent; - while ( p.isValid() ) - p = p.parent(); - - // For models that are dynamically populated - if ( model->canFetchMore ( parent ) ) { - fetchingMore = true; - model->fetchMore ( parent ); - fetchingMore = false; - } - - int rows = model->rowCount ( parent ); - int columns = model->columnCount ( parent ); - - if ( rows > 0 ) - QVERIFY( model->hasChildren ( parent ) ); - - // Some further testing against rows(), columns(), and hasChildren() - QVERIFY( rows >= 0 ); - QVERIFY( columns >= 0 ); - if ( rows > 0 ) - QVERIFY( model->hasChildren ( parent ) ); - - qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows - << "columns:" << columns << "parent column:" << parent.column(); - - const QModelIndex topLeftChild = model->index( 0, 0, parent ); - - QVERIFY( !model->hasIndex ( rows + 1, 0, parent ) ); - for ( int r = 0; r < rows; ++r ) { - if ( model->canFetchMore ( parent ) ) { - fetchingMore = true; - model->fetchMore ( parent ); - fetchingMore = false; - } - QVERIFY( !model->hasIndex ( r, columns + 1, parent ) ); - for ( int c = 0; c < columns; ++c ) { - QVERIFY( model->hasIndex ( r, c, parent ) ); - QModelIndex index = model->index ( r, c, parent ); - // rowCount() and columnCount() said that it existed... - QVERIFY( index.isValid() ); - - qDebug() << "\tchild("<< r <<", " << c << ", " << index.column() << "):" << model->data(index).toString() << index.internalPointer(); - - // index() should always return the same index when called twice in a row - QModelIndex modifiedIndex = model->index ( r, c, parent ); - QVERIFY( index == modifiedIndex ); - - // Make sure we get the same index if we request it twice in a row - QModelIndex a = model->index ( r, c, parent ); - QModelIndex b = model->index ( r, c, parent ); - QVERIFY( a == b ); - - { - const QModelIndex sibling = model->sibling( r, c, topLeftChild ); - QVERIFY( index == sibling ); - } - { - const QModelIndex sibling = topLeftChild.sibling( r, c ); - QVERIFY( index == sibling ); - } - - // Some basic checking on the index that is returned - QVERIFY( index.model() == model ); - QCOMPARE( index.row(), r ); - QCOMPARE( index.column(), c ); - // While you can technically return a QVariant usually this is a sign - // of a bug in data(). Disable if this really is ok in your model. -// QVERIFY( model->data ( index, Qt::DisplayRole ).isValid() ); - - // If the next test fails here is some somewhat useful debug you play with. - - if (model->parent(index) != parent) { - qDebug() << r << c << currentDepth << model->data(index).toString() - << model->data(parent).toString(); - qDebug() << index << parent << model->parent(index); -// And a view that you can even use to show the model. -// QTreeView view; -// view.setModel(model); -// view.show(); - } - - // Check that we can get back our real parent. - QCOMPARE( model->parent ( index ), parent ); - - // recursively go down the children - if ( model->hasChildren ( index ) && currentDepth < 10 ) { - qDebug() << r << c << "has children" << model->rowCount(index); - checkChildren ( index, ++currentDepth ); - }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ - - // make sure that after testing the children that the index doesn't change. - QModelIndex newerIndex = model->index ( r, c, parent ); - QVERIFY( index == newerIndex ); - } - } -} - -/*! - Tests model's implementation of QAbstractItemModel::data() - */ -void ModelTest::data() -{ - // Invalid index should return an invalid qvariant - QVERIFY( !model->data ( QModelIndex() ).isValid() ); - - if ( model->rowCount() == 0 ) - return; - - // A valid index should have a valid QVariant data - QVERIFY( model->index ( 0, 0 ).isValid() ); - - // shouldn't be able to set data on an invalid index - QVERIFY( !model->setData ( QModelIndex(), QLatin1String ( "foo" ), Qt::DisplayRole ) ); - - // General Purpose roles that should return a QString - QVariant variant = model->data ( model->index ( 0, 0 ), Qt::ToolTipRole ); - if ( variant.isValid() ) { - QVERIFY( variant.canConvert() ); - } - variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole ); - if ( variant.isValid() ) { - QVERIFY( variant.canConvert() ); - } - variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole ); - if ( variant.isValid() ) { - QVERIFY( variant.canConvert() ); - } - - // General Purpose roles that should return a QSize - variant = model->data ( model->index ( 0, 0 ), Qt::SizeHintRole ); - if ( variant.isValid() ) { - QVERIFY( variant.canConvert() ); - } - - // General Purpose roles that should return a QFont - QVariant fontVariant = model->data ( model->index ( 0, 0 ), Qt::FontRole ); - if ( fontVariant.isValid() ) { - QVERIFY( fontVariant.canConvert() ); - } - - // Check that the alignment is one we know about - QVariant textAlignmentVariant = model->data ( model->index ( 0, 0 ), Qt::TextAlignmentRole ); - if ( textAlignmentVariant.isValid() ) { - int alignment = textAlignmentVariant.toInt(); - QCOMPARE( alignment, ( alignment & ( Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask ) ) ); - } - - // General Purpose roles that should return a QColor - QVariant colorVariant = model->data ( model->index ( 0, 0 ), Qt::BackgroundColorRole ); - if ( colorVariant.isValid() ) { - QVERIFY( colorVariant.canConvert() ); - } - - colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole ); - if ( colorVariant.isValid() ) { - QVERIFY( colorVariant.canConvert() ); - } - - // Check that the "check state" is one we know about. - QVariant checkStateVariant = model->data ( model->index ( 0, 0 ), Qt::CheckStateRole ); - if ( checkStateVariant.isValid() ) { - int state = checkStateVariant.toInt(); - QVERIFY( state == Qt::Unchecked || - state == Qt::PartiallyChecked || - state == Qt::Checked ); - } -} - -/*! - Store what is about to be inserted to make sure it actually happens - - \sa rowsInserted() - */ -void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, int /* end */) -{ -// Q_UNUSED(end); -// qDebug() << "rowsAboutToBeInserted" << "start=" << start << "end=" << end << "parent=" << model->data ( parent ).toString() -// << "current count of parent=" << model->rowCount ( parent ); // << "display of last=" << model->data( model->index(start-1, 0, parent) ); -// qDebug() << model->index(start-1, 0, parent) << model->data( model->index(start-1, 0, parent) ); - Changing c; - c.parent = parent; - c.oldSize = model->rowCount ( parent ); - c.last = model->data ( model->index ( start - 1, 0, parent ) ); - c.next = model->data ( model->index ( start, 0, parent ) ); - insert.push ( c ); -} - -/*! - Confirm that what was said was going to happen actually did - - \sa rowsAboutToBeInserted() - */ -void ModelTest::rowsInserted ( const QModelIndex & parent, int start, int end ) -{ - Changing c = insert.pop(); - QVERIFY( c.parent == parent ); -// qDebug() << "rowsInserted" << "start=" << start << "end=" << end << "oldsize=" << c.oldSize -// << "parent=" << model->data ( parent ).toString() << "current rowcount of parent=" << model->rowCount ( parent ); - -// for (int ii=start; ii <= end; ii++) -// { -// qDebug() << "itemWasInserted:" << ii << model->data ( model->index ( ii, 0, parent )); -// } -// qDebug(); - - QVERIFY( c.oldSize + ( end - start + 1 ) == model->rowCount ( parent ) ); - QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) ); - - if (c.next != model->data(model->index(end + 1, 0, c.parent))) { - qDebug() << start << end; - for (int i=0; i < model->rowCount(); ++i) - qDebug() << model->index(i, 0).data().toString(); - qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); - } - - QVERIFY( c.next == model->data ( model->index ( end + 1, 0, c.parent ) ) ); -} - -void ModelTest::layoutAboutToBeChanged() -{ - for ( int i = 0; i < qBound ( 0, model->rowCount(), 100 ); ++i ) - changing.append ( QPersistentModelIndex ( model->index ( i, 0 ) ) ); -} - -void ModelTest::layoutChanged() -{ - for ( int i = 0; i < changing.count(); ++i ) { - QPersistentModelIndex p = changing[i]; - QVERIFY( p == model->index ( p.row(), p.column(), p.parent() ) ); - } - changing.clear(); -} - -/*! - Store what is about to be inserted to make sure it actually happens - - \sa rowsRemoved() - */ -void ModelTest::rowsAboutToBeRemoved ( const QModelIndex &parent, int start, int end ) -{ -qDebug() << "ratbr" << parent << start << end; - Changing c; - c.parent = parent; - c.oldSize = model->rowCount ( parent ); - c.last = model->data ( model->index ( start - 1, 0, parent ) ); - c.next = model->data ( model->index ( end + 1, 0, parent ) ); - remove.push ( c ); -} - -/*! - Confirm that what was said was going to happen actually did - - \sa rowsAboutToBeRemoved() - */ -void ModelTest::rowsRemoved ( const QModelIndex & parent, int start, int end ) -{ - qDebug() << "rr" << parent << start << end; - Changing c = remove.pop(); - QVERIFY( c.parent == parent ); - QVERIFY( c.oldSize - ( end - start + 1 ) == model->rowCount ( parent ) ); - QVERIFY( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) ); - QVERIFY( c.next == model->data ( model->index ( start, 0, c.parent ) ) ); -} - -void ModelTest::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) -{ - QVERIFY(topLeft.isValid()); - QVERIFY(bottomRight.isValid()); - QModelIndex commonParent = bottomRight.parent(); - QCOMPARE(topLeft.parent(), commonParent); - QVERIFY(topLeft.row() <= bottomRight.row()); - QVERIFY(topLeft.column() <= bottomRight.column()); - int rowCount = model->rowCount(commonParent); - int columnCount = model->columnCount(commonParent); - QVERIFY(bottomRight.row() < rowCount); - QVERIFY(bottomRight.column() < columnCount); -} - -void ModelTest::headerDataChanged(Qt::Orientation orientation, int start, int end) -{ - QVERIFY(start >= 0); - QVERIFY(end >= 0); - QVERIFY(start <= end); - int itemCount = orientation == Qt::Vertical ? model->rowCount() : model->columnCount(); - QVERIFY(start < itemCount); - QVERIFY(end < itemCount); -} - diff --git a/framework/domain/modeltest.h b/framework/domain/modeltest.h deleted file mode 100644 index 735a4227..00000000 --- a/framework/domain/modeltest.h +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#ifndef MODELTEST_H -#define MODELTEST_H - -#include -#include -#include - -class ModelTest : public QObject -{ - Q_OBJECT - -public: - ModelTest( QAbstractItemModel *model, QObject *parent = 0 ); - -private Q_SLOTS: - void nonDestructiveBasicTest(); - void rowCount(); - void columnCount(); - void hasIndex(); - void index(); - void parent(); - void data(); - -protected Q_SLOTS: - void runAllTests(); - void layoutAboutToBeChanged(); - void layoutChanged(); - void rowsAboutToBeInserted( const QModelIndex &parent, int start, int end ); - void rowsInserted( const QModelIndex & parent, int start, int end ); - void rowsAboutToBeRemoved( const QModelIndex &parent, int start, int end ); - void rowsRemoved( const QModelIndex & parent, int start, int end ); - void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); - void headerDataChanged(Qt::Orientation orientation, int start, int end); - -private: - void checkChildren( const QModelIndex &parent, int currentDepth = 0 ); - - QAbstractItemModel *model; - - struct Changing { - QModelIndex parent; - int oldSize; - QVariant last; - QVariant next; - }; - QStack insert; - QStack remove; - - bool fetchingMore; - - QList changing; -}; - -#endif diff --git a/framework/domain/mouseproxy.cpp b/framework/domain/mouseproxy.cpp deleted file mode 100644 index 9944acb6..00000000 --- a/framework/domain/mouseproxy.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - Copyright (c) 2017 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#include "mouseproxy.h" - -MouseProxy::MouseProxy(QQuickItem *parent) - : QQuickItem(parent) -{ - -} - -MouseProxy::~MouseProxy() -{ - -} - -void MouseProxy::wheelEvent(QWheelEvent *event) -{ - if (mTarget && mForwardWheelEvents) { - mTarget->event(event); - } else { - QQuickItem::wheelEvent(event); - } -} diff --git a/framework/domain/mouseproxy.h b/framework/domain/mouseproxy.h deleted file mode 100644 index 67b25d06..00000000 --- a/framework/domain/mouseproxy.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - Copyright (c) 2017 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#include - -class MouseProxy : public QQuickItem -{ - Q_OBJECT - - Q_PROPERTY(QObject* target MEMBER mTarget) - Q_PROPERTY(bool forwardWheelEvents MEMBER mForwardWheelEvents) - -public: - MouseProxy(QQuickItem *parent=0); - ~MouseProxy(); - -protected: - virtual void wheelEvent(QWheelEvent *event) override; - -private: - QObject *mTarget = nullptr; - bool mForwardWheelEvents = false; -}; - diff --git a/framework/domain/objecttreesource.cpp b/framework/domain/objecttreesource.cpp deleted file mode 100644 index 567f3516..00000000 --- a/framework/domain/objecttreesource.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net - Copyright (c) 2009 Andras Mantia - - 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., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "objecttreesource.h" - -#include -#include -#include -#include - -class ObjectSourcePrivate -{ -public: - ObjectSourcePrivate() - : mWriter(0) - , mAllowDecryption(true) - , mHtmlLoadExternal(true) - , mPreferredMode(MimeTreeParser::Util::Html) - { - - } - MimeTreeParser::HtmlWriter *mWriter; - MimeTreeParser::BodyPartFormatterBaseFactory mBodyPartFormatterBaseFactory; - bool mAllowDecryption; - bool mHtmlLoadExternal; - MimeTreeParser::Util::HtmlMode mPreferredMode; -}; - -ObjectTreeSource::ObjectTreeSource(MimeTreeParser::HtmlWriter *writer) - : MimeTreeParser::Interface::ObjectTreeSource() - , d(new ObjectSourcePrivate) - { - d->mWriter = writer; - } - -ObjectTreeSource::~ObjectTreeSource() -{ - delete d; -} - -void ObjectTreeSource::setAllowDecryption(bool allowDecryption) -{ - d->mAllowDecryption = allowDecryption; -} - -MimeTreeParser::HtmlWriter *ObjectTreeSource::htmlWriter() -{ - return d->mWriter; -} - -bool ObjectTreeSource::htmlLoadExternal() const -{ - return d->mHtmlLoadExternal; -} - -void ObjectTreeSource::setHtmlLoadExternal(bool loadExternal) -{ - d->mHtmlLoadExternal = loadExternal; -} - -bool ObjectTreeSource::decryptMessage() const -{ - return d->mAllowDecryption; -} - -bool ObjectTreeSource::showSignatureDetails() const -{ - return true; -} - -int ObjectTreeSource::levelQuote() const -{ - return 1; -} - -const QTextCodec *ObjectTreeSource::overrideCodec() -{ - return Q_NULLPTR; -} - -QString ObjectTreeSource::createMessageHeader(KMime::Message *message) -{ - return QString(); -} - -const MimeTreeParser::AttachmentStrategy *ObjectTreeSource::attachmentStrategy() -{ - return MimeTreeParser::AttachmentStrategy::smart(); -} - -QObject *ObjectTreeSource::sourceObject() -{ - return Q_NULLPTR; -} - -void ObjectTreeSource::setHtmlMode(MimeTreeParser::Util::HtmlMode mode, const QList &availableModes) -{ - Q_UNUSED(mode); - Q_UNUSED(availableModes); -} - -MimeTreeParser::Util::HtmlMode ObjectTreeSource::preferredMode() const -{ - return d->mPreferredMode; -} - -bool ObjectTreeSource::autoImportKeys() const -{ - return false; -} - -bool ObjectTreeSource::showEmoticons() const -{ - return false; -} - -bool ObjectTreeSource::showExpandQuotesMark() const -{ - return false; -} - -bool ObjectTreeSource::isPrinting() const -{ - return false; -} - -const MimeTreeParser::BodyPartFormatterBaseFactory *ObjectTreeSource::bodyPartFormatterFactory() -{ - return &(d->mBodyPartFormatterBaseFactory); -} - -MimeTreeParser::Interface::MessagePartRenderer::Ptr ObjectTreeSource::messagePartTheme(MimeTreeParser::Interface::MessagePart::Ptr msgPart) -{ - Q_UNUSED(msgPart); - return MimeTreeParser::Interface::MessagePartRenderer::Ptr(); -} diff --git a/framework/domain/objecttreesource.h b/framework/domain/objecttreesource.h deleted file mode 100644 index 93812dc3..00000000 --- a/framework/domain/objecttreesource.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net - Copyright (c) 2009 Andras Mantia - - 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., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef MAILVIEWER_OBJECTTREEEMPTYSOURCE_H -#define MAILVIEWER_OBJECTTREEEMPTYSOURCE_H - -#include - -class QString; - -class ObjectSourcePrivate; -class ObjectTreeSource : public MimeTreeParser::Interface::ObjectTreeSource -{ -public: - ObjectTreeSource(MimeTreeParser::HtmlWriter *writer); - virtual ~ObjectTreeSource(); - void setHtmlLoadExternal(bool loadExternal); - bool decryptMessage() const Q_DECL_OVERRIDE; - bool htmlLoadExternal() const Q_DECL_OVERRIDE; - bool showSignatureDetails() const Q_DECL_OVERRIDE; - void setHtmlMode(MimeTreeParser::Util::HtmlMode mode, const QList &availableModes) Q_DECL_OVERRIDE; - MimeTreeParser::Util::HtmlMode preferredMode() const Q_DECL_OVERRIDE; - void setAllowDecryption(bool allowDecryption); - int levelQuote() const Q_DECL_OVERRIDE; - const QTextCodec *overrideCodec() Q_DECL_OVERRIDE; - QString createMessageHeader(KMime::Message *message) Q_DECL_OVERRIDE; - const MimeTreeParser::AttachmentStrategy *attachmentStrategy() Q_DECL_OVERRIDE; - MimeTreeParser::HtmlWriter *htmlWriter() Q_DECL_OVERRIDE; - QObject *sourceObject() Q_DECL_OVERRIDE; - bool autoImportKeys() const Q_DECL_OVERRIDE; - bool showEmoticons() const Q_DECL_OVERRIDE; - bool showExpandQuotesMark() const Q_DECL_OVERRIDE; - bool isPrinting() const Q_DECL_OVERRIDE; - const MimeTreeParser::BodyPartFormatterBaseFactory *bodyPartFormatterFactory() Q_DECL_OVERRIDE; - MimeTreeParser::Interface::MessagePartRendererPtr messagePartTheme(MimeTreeParser::Interface::MessagePartPtr msgPart) Q_DECL_OVERRIDE; -private: - ObjectSourcePrivate *const d; -}; - -#endif - diff --git a/framework/domain/outboxcontroller.cpp b/framework/domain/outboxcontroller.cpp deleted file mode 100644 index 590b6c49..00000000 --- a/framework/domain/outboxcontroller.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#include "outboxcontroller.h" - -#include -#include - -OutboxController::OutboxController() - : Kube::Controller(), - mSynchronizeOutboxAction{new Kube::ControllerAction{this, &OutboxController::sendOutbox}} -{ -} - -Kube::ControllerAction* OutboxController::sendOutboxAction() const -{ - return mSynchronizeOutboxAction.data(); -} - -void OutboxController::sendOutbox() -{ - using namespace Sink; - using namespace Sink::ApplicationDomain; - Query query; - query.containsFilter(ResourceCapabilities::Mail::transport); - auto job = Store::fetchAll(query) - .each([=](const SinkResource::Ptr &resource) -> KAsync::Job { - return Store::synchronize(SyncScope{}.resourceFilter(resource->identifier())); - }); - run(job); -} - diff --git a/framework/domain/outboxcontroller.h b/framework/domain/outboxcontroller.h deleted file mode 100644 index 4c24ad59..00000000 --- a/framework/domain/outboxcontroller.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#pragma once - -#include -#include "controller.h" - -class OutboxController : public Kube::Controller -{ - Q_OBJECT - Q_PROPERTY (Kube::ControllerAction* sendOutboxAction READ sendOutboxAction CONSTANT) - -public: - explicit OutboxController(); - - Kube::ControllerAction* sendOutboxAction() const; - -private slots: - void sendOutbox(); - -private: - QScopedPointer mSynchronizeOutboxAction; -}; diff --git a/framework/domain/outboxmodel.cpp b/framework/domain/outboxmodel.cpp deleted file mode 100644 index 237648b1..00000000 --- a/framework/domain/outboxmodel.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - Copyright (c) 2016 Michael Bohlender - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#include "outboxmodel.h" - -#include -#include -#include - -#include -#include -#include - - -OutboxModel::OutboxModel(QObject *parent) - : QSortFilterProxyModel(), - mNotifier(new Sink::Notifier{Sink::Query{}.containsFilter(Sink::ApplicationDomain::ResourceCapabilities::Mail::transport)}), - mStatus(NoStatus) -{ - setDynamicSortFilter(true); - sort(0, Qt::DescendingOrder); - - using namespace Sink::ApplicationDomain; - auto query = Sink::StandardQueries::outboxMails(); - query.setFlags(Sink::Query::LiveQuery | Sink::Query::UpdateStatus); - query.request(); - query.request(); - query.request(); - runQuery(query); - connect(this, &QAbstractItemModel::rowsInserted, this, &OutboxModel::countChanged); - connect(this, &QAbstractItemModel::rowsRemoved, this, &OutboxModel::countChanged); - - mNotifier->registerHandler([this] (const Sink::Notification &n) { - //TODO aggregate status from multiple resources - if (n.type == Sink::Notification::Status) { - switch (n.code) { - case Sink::ApplicationDomain::Status::ErrorStatus: - mStatus = ErrorStatus; - break; - case Sink::ApplicationDomain::Status::BusyStatus: - mStatus = InProgressStatus; - break; - default: - mStatus = NoStatus; - break; - } - emit statusChanged(); - } - - }); - -} - -OutboxModel::~OutboxModel() -{ - -} - -QHash< int, QByteArray > OutboxModel::roleNames() const -{ - QHash roles; - - roles[Subject] = "subject"; - roles[Date] = "date"; - roles[Status] = "status"; - roles[Id] = "id"; - roles[MimeMessage] = "mimeMessage"; - roles[DomainObject] = "domainObject"; - - return roles; -} - -QVariant OutboxModel::data(const QModelIndex &idx, int role) const -{ - auto srcIdx = mapToSource(idx); - auto mail = srcIdx.data(Sink::Store::DomainObjectRole).value(); - switch (role) { - case Subject: - return mail->getSubject(); - case Date: - return mail->getDate(); - case Status: { - const auto status = srcIdx.data(Sink::Store::StatusRole).toInt(); - if (status == Sink::ApplicationDomain::SyncStatus::SyncInProgress) { - return InProgressStatus; - } - if (status == Sink::ApplicationDomain::SyncStatus::SyncError) { - return ErrorStatus; - } - return PendingStatus; - } - case Id: - return mail->identifier(); - case DomainObject: - return QVariant::fromValue(mail); - case MimeMessage: { - return mail->getMimeMessage(); - } - } - return QSortFilterProxyModel::data(idx, role); -} - -bool OutboxModel::lessThan(const QModelIndex &left, const QModelIndex &right) const -{ - const auto leftDate = left.data(Sink::Store::DomainObjectRole).value()->getDate(); - const auto rightDate = right.data(Sink::Store::DomainObjectRole).value()->getDate(); - return leftDate < rightDate; -} - -void OutboxModel::runQuery(const Sink::Query &query) -{ - mModel = Sink::Store::loadModel(query); - setSourceModel(mModel.data()); -} - -int OutboxModel::count() const -{ - return rowCount(); -} - -int OutboxModel::status() const -{ - return mStatus; -} diff --git a/framework/domain/outboxmodel.h b/framework/domain/outboxmodel.h deleted file mode 100644 index 4be9c7f8..00000000 --- a/framework/domain/outboxmodel.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - Copyright (c) 2016 Michael Bohlender - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#pragma once - -#include -#include - -#include -#include -#include - -class OutboxModel : public QSortFilterProxyModel -{ - Q_OBJECT - - Q_PROPERTY (int count READ count NOTIFY countChanged) - Q_PROPERTY (int status READ status NOTIFY statusChanged) - -public: - enum Status { - NoStatus, - PendingStatus, - InProgressStatus, - ErrorStatus - }; - Q_ENUMS(Status) - - OutboxModel(QObject *parent = Q_NULLPTR); - ~OutboxModel(); - - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; - - bool lessThan(const QModelIndex &left, const QModelIndex &right) const Q_DECL_OVERRIDE; - - enum Roles { - Subject = Qt::UserRole + 1, - Date, - Status, - Id, - MimeMessage, - DomainObject - }; - - QHash roleNames() const Q_DECL_OVERRIDE; - - void runQuery(const Sink::Query &query); - - int count() const; - int status() const; - -signals: - void statusChanged(); - void countChanged(); - -private: - QSharedPointer mModel; - QSharedPointer mNotifier; - int mStatus; -}; diff --git a/framework/domain/peoplemodel.cpp b/framework/domain/peoplemodel.cpp deleted file mode 100644 index c9e7248c..00000000 --- a/framework/domain/peoplemodel.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - Copyright (c) 2016 Michael Bohlender - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#include "peoplemodel.h" - -#include -#include - -PeopleModel::PeopleModel(QObject *parent) - : QSortFilterProxyModel() -{ - using namespace Sink::ApplicationDomain; - - setDynamicSortFilter(true); - sort(0, Qt::DescendingOrder); - setFilterCaseSensitivity(Qt::CaseInsensitive); - Sink::Query query; - query.setFlags(Sink::Query::LiveQuery); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - query.request(); - runQuery(query); -} - -PeopleModel::~PeopleModel() -{ - -} - -void PeopleModel::setFilter(const QString &filter) -{ - setFilterWildcard(filter); -} - -QString PeopleModel::filter() const -{ - return {}; -} - -QHash< int, QByteArray > PeopleModel::roleNames() const -{ - static QHash roles = { - {Name, "name"}, - {Emails, "emails"}, - {Addressbook, "addressbook"}, - {Type, "type"}, - {DomainObject, "domainObject"}, - {FirstName, "firstName"}, - {LastName, "lastName"} - }; - return roles; -} - -static QStringList toStringList(const QList &list) -{ - QStringList out; - std::transform(list.constBegin(), list.constEnd(), std::back_inserter(out), [] (const Sink::ApplicationDomain::Contact::Email &s) { return s.email; }); - return out; -} - -QVariant PeopleModel::data(const QModelIndex &idx, int role) const -{ - auto srcIdx = mapToSource(idx); - auto contact = srcIdx.data(Sink::Store::DomainObjectRole).value(); - switch (role) { - case Name: - return contact->getFn(); - case Emails: - return QVariant::fromValue(toStringList(contact->getEmails())); - case Addressbook: - return contact->getAddressbook(); - case Type: - return "contact"; - case DomainObject: - return QVariant::fromValue(contact); - case FirstName: - return contact->getFirstname(); - case LastName: - return contact->getLastname(); - } - return QSortFilterProxyModel::data(idx, role); -} - -bool PeopleModel::lessThan(const QModelIndex &left, const QModelIndex &right) const -{ - const auto leftName = left.data(Sink::Store::DomainObjectRole).value()->getFn(); - const auto rightName = right.data(Sink::Store::DomainObjectRole).value()->getFn(); - return leftName < rightName; -} - -void PeopleModel::runQuery(const Sink::Query &query) -{ - mModel = Sink::Store::loadModel(query); - setSourceModel(mModel.data()); -} - -void PeopleModel::setAddressbook(const QVariant &parentFolder) -{ - //TODO filter query by addressbook - qWarning() << "The addressbook filter is not yet implemented"; -} - -QVariant PeopleModel::addressbook() const -{ - return QVariant(); -} - - diff --git a/framework/domain/peoplemodel.h b/framework/domain/peoplemodel.h deleted file mode 100644 index a59e752c..00000000 --- a/framework/domain/peoplemodel.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - Copyright (c) 2016 Michael Bohlender - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#pragma once - -#include -#include - -namespace Sink { - class Query; -}; - -/** - * A model that mixes addressbooks, contact groups and contacts - */ -class PeopleModel : public QSortFilterProxyModel -{ - Q_OBJECT - Q_PROPERTY (QVariant addressbook READ addressbook WRITE setAddressbook) - Q_PROPERTY (QString filter READ filter WRITE setFilter) - -public: - PeopleModel(QObject *parent = Q_NULLPTR); - ~PeopleModel(); - - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; - - bool lessThan(const QModelIndex &left, const QModelIndex &right) const Q_DECL_OVERRIDE; - - enum Roles { - Name = Qt::UserRole + 1, - Type, - Emails, - Addressbook, - DomainObject, - FirstName, - LastName - }; - - QHash roleNames() const Q_DECL_OVERRIDE; - - void runQuery(const Sink::Query &query); - - void setAddressbook(const QVariant &parentFolder); - QVariant addressbook() const; - - void setFilter(const QString &mail); - QString filter() const; - -private: - QSharedPointer mModel; -}; diff --git a/framework/domain/recepientautocompletionmodel.cpp b/framework/domain/recepientautocompletionmodel.cpp deleted file mode 100644 index 2f52db07..00000000 --- a/framework/domain/recepientautocompletionmodel.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#include "recepientautocompletionmodel.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace Sink::ApplicationDomain; - -RecipientAutocompletionModel::RecipientAutocompletionModel(QObject *parent) - : QSortFilterProxyModel(), - mSourceModel(new QStandardItemModel), - mTimer(new QTimer) -{ - setSourceModel(mSourceModel.data()); - setDynamicSortFilter(true); - setFilterCaseSensitivity(Qt::CaseInsensitive); - mTimer->setSingleShot(true); - QObject::connect(mTimer.data(), &QTimer::timeout, this, &RecipientAutocompletionModel::save); - - load(); -} - -RecipientAutocompletionModel::~RecipientAutocompletionModel() -{ - save(); -} - -static QString getPath() -{ - return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kube/recepientautocompletion.ini"; -} - -void RecipientAutocompletionModel::save() -{ - QSet list; - for (int row = 0; row < mSourceModel->rowCount(); row++) { - list << mSourceModel->item(row)->data(Text).toString(); - } - - qWarning() << "Path " << getPath(); - QSettings settings(getPath(), QSettings::IniFormat); - settings.setValue("list", QStringList{list.toList()}); -} - -void RecipientAutocompletionModel::load() -{ - qWarning() << "Path " << getPath(); - QSettings settings(getPath(), QSettings::IniFormat); - auto list = settings.value("list").toStringList(); - auto add = [] (const QString &n) { - auto item = new QStandardItem{n}; - item->setData(n, Text); - return item; - }; - for (const auto &entry : list) { - mSourceModel->appendRow(add(entry)); - } - Sink::Query query; - query.request(); - query.request(); - Sink::Store::fetchAll(query) - .then([this] (const QList &list) { - for (const auto &c : list) { - for (const auto &email : c->getEmails()) { - addToModel(email.email, c->getFn()); - } - } - }).exec(); -} - -QHash< int, QByteArray > RecipientAutocompletionModel::roleNames() const -{ - QHash roles; - roles[Text] = "text"; - roles[Color] = "color"; - return roles; -} - - -bool RecipientAutocompletionModel::addToModel(const QString &address, const QString &name) -{ - auto add = [] (const QString &n) { - auto item = new QStandardItem{n}; - item->setData(n, Text); - return item; - }; - auto formattedName = [&] () { - if (name.isEmpty()) { - return QString(address); - } - return QString("%1 <%2>").arg(QString(address), QString(name)); - }(); - auto matches = mSourceModel->findItems(formattedName); - if (matches.isEmpty()) { - mSourceModel->appendRow(add(formattedName)); - return true; - } - return false; -} - -void RecipientAutocompletionModel::addEntry(const QByteArray &address, const QByteArray &name) -{ - if (addToModel(address, name)) { - mTimer->start(100); - } -} - -void RecipientAutocompletionModel::setFilter(const QString &filter) -{ - setFilterWildcard("*" + filter +"*"); -} diff --git a/framework/domain/recepientautocompletionmodel.h b/framework/domain/recepientautocompletionmodel.h deleted file mode 100644 index 22c6f108..00000000 --- a/framework/domain/recepientautocompletionmodel.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#pragma once - -#include -#include - -class QStandardItemModel; -class QTimer; - -class RecipientAutocompletionModel : public QSortFilterProxyModel -{ - Q_OBJECT - -public: - RecipientAutocompletionModel(QObject *parent = Q_NULLPTR); - ~RecipientAutocompletionModel(); - - enum Roles { - Text = Qt::UserRole + 1, - Color - }; - Q_ENUMS(Roles) - - QHash roleNames() const Q_DECL_OVERRIDE; - - bool addToModel(const QString &address, const QString &name); - void addEntry(const QByteArray &address, const QByteArray &name); - void setFilter(const QString &); - -private slots: - void save(); - -private: - void load(); - - QScopedPointer mSourceModel; - QScopedPointer mTimer; -}; diff --git a/framework/domain/retriever.cpp b/framework/domain/retriever.cpp deleted file mode 100644 index b8e29523..00000000 --- a/framework/domain/retriever.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - - -#include "retriever.h" - -Retriever::Retriever(QObject *parent) : QObject(parent) -{ -} - -QAbstractItemModel* Retriever::model() const -{ - return mModel; -} - -void Retriever::setModel(QAbstractItemModel* model) -{ - mValue = QVariant(); - mModel = model; - connect(model, &QAbstractItemModel::rowsInserted, this, &Retriever::onRowsInserted); - connect(model, &QAbstractItemModel::modelReset, this, &Retriever::onModelReset); - if (model->rowCount(QModelIndex())) { - mValue = model->index(0, 0, QModelIndex()).data(mModel->roleNames().key(mPropertyName.toLatin1())); - emit valueChanged(); - } -} - -void Retriever::onRowsInserted(const QModelIndex &parent, int first, int last) -{ - if (!mValue.isValid()) { - mValue = mModel->index(0, 0, QModelIndex()).data(mModel->roleNames().key(mPropertyName.toLatin1())); - emit valueChanged(); - } -} - -void Retriever::onModelReset() -{ - mValue = QVariant(); -} diff --git a/framework/domain/retriever.h b/framework/domain/retriever.h deleted file mode 100644 index e454532d..00000000 --- a/framework/domain/retriever.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#pragma once - -#include -#include -#include - -/** - * A wrapper for a QAbstractItemModel to retrieve a value from a single index via property binding - * - * Assign a model that retrieves the index, set the property your interested in, and propery-bind "value". - */ -class Retriever : public QObject -{ - Q_OBJECT - Q_PROPERTY(QAbstractItemModel* model READ model WRITE setModel) - Q_PROPERTY(QString propertyName MEMBER mPropertyName) - Q_PROPERTY(QVariant value MEMBER mValue NOTIFY valueChanged) - -public: - explicit Retriever(QObject *parent = Q_NULLPTR); - - QAbstractItemModel* model() const; - void setModel(QAbstractItemModel* model); - -signals: - void valueChanged(); - -private slots: - void onRowsInserted(const QModelIndex &parent, int first, int last); - void onModelReset(); - -private: - QString mPropertyName; - QVariant mValue; - QAbstractItemModel *mModel; -}; diff --git a/framework/domain/selector.cpp b/framework/domain/selector.cpp deleted file mode 100644 index d021095b..00000000 --- a/framework/domain/selector.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekofp - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#include "selector.h" - -#include - -Selector::Selector(QAbstractItemModel *model) : mModel{model} -{ - QQmlEngine::setObjectOwnership(mModel, QQmlEngine::CppOwnership); -} - -void Selector::reapplyCurrentIndex() -{ - setCurrentIndex(currentIndex()); -} diff --git a/framework/domain/selector.h b/framework/domain/selector.h deleted file mode 100644 index 358eaa9a..00000000 --- a/framework/domain/selector.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekofp - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#pragma once - -#include -#include - -/** - * Exposes a model and maintains a current index selection. - */ -class Selector : public QObject { - Q_OBJECT - Q_PROPERTY (int currentIndex READ currentIndex WRITE setCurrentIndex) - Q_PROPERTY (QAbstractItemModel* model READ model CONSTANT) - -public: - Selector(QAbstractItemModel *model); - - virtual QAbstractItemModel *model() { return mModel; } - - void setCurrentIndex(int i) { - mCurrentIndex = i; - Q_ASSERT(mModel); - if (i >= 0) { - setCurrent(mModel->index(mCurrentIndex, 0)); - } else { - setCurrent(QModelIndex()); - } - } - - void reapplyCurrentIndex(); - - int currentIndex() { return mCurrentIndex; } - - virtual void setCurrent(const QModelIndex &) = 0; -private: - QAbstractItemModel *mModel = nullptr; - int mCurrentIndex = 0; -}; - diff --git a/framework/domain/settings/CMakeLists.txt b/framework/domain/settings/CMakeLists.txt deleted file mode 100644 index dc9d01b1..00000000 --- a/framework/domain/settings/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -include_directories(${CMAKE_CURRENT_BINARY_DIR}) -cmake_policy(SET CMP0063 NEW) -add_executable(sinkactiontest sinkactiontest.cpp) -add_test(sinkactiontest sinkactiontest) -qt5_use_modules(sinkactiontest Core Test Concurrent) -target_link_libraries(sinkactiontest sink actionplugin KF5::Mime mailplugin) diff --git a/framework/domain/settings/accountsettings.cpp b/framework/domain/settings/accountsettings.cpp deleted file mode 100644 index d1019e1f..00000000 --- a/framework/domain/settings/accountsettings.cpp +++ /dev/null @@ -1,391 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#include "accountsettings.h" - -#include -#include -#include -#include -#include - -using namespace Sink; -using namespace Sink::ApplicationDomain; - -SINK_DEBUG_AREA("accountsettings") - -AccountSettings::AccountSettings(QObject *parent) - : QObject(parent) -{ -} - -void AccountSettings::setAccountType(const QByteArray &type) -{ - mAccountType = type; -} - -QByteArray AccountSettings::accountType() const -{ - return mAccountType; -} - -void AccountSettings::setAccountIdentifier(const QByteArray &id) -{ - if (id.isEmpty()) { - return; - } - mAccountIdentifier = id; - - //Clear - mIcon = QString(); - mName = QString(); - mImapServer = QString(); - mImapUsername = QString(); - mImapPassword = QString(); - mSmtpServer = QString(); - mSmtpUsername = QString(); - mSmtpPassword = QString(); - mCardDavServer = QString(); - mCardDavUsername = QString(); - mCardDavPassword = QString(); - emit changed(); - emit imapResourceChanged(); - emit smtpResourceChanged(); - emit cardDavResourceChanged(); - - load(); - -} - -QByteArray AccountSettings::accountIdentifier() const -{ - return mAccountIdentifier; -} - -void AccountSettings::setPath(const QUrl &path) -{ - auto normalizedPath = path.path(); - if (mPath != normalizedPath) { - mPath = normalizedPath; - emit pathChanged(); - } -} - -QUrl AccountSettings::path() const -{ - return QUrl(mPath); -} - -QValidator *AccountSettings::pathValidator() const -{ - class PathValidator : public QValidator { - State validate(QString &input, int &pos) const { - Q_UNUSED(pos); - if (!input.isEmpty() && QDir(input).exists()) { - return Acceptable; - } else { - return Intermediate; - } - } - }; - static PathValidator *pathValidator = new PathValidator; - return pathValidator; -} - -QValidator *AccountSettings::imapServerValidator() const -{ - class ImapServerValidator : public QValidator { - State validate(QString &input, int &pos) const { - Q_UNUSED(pos); - // imaps://mainserver.example.net:475 - const QUrl url(input); - static QSet validProtocols = QSet() << "imap" << "imaps"; - if (url.isValid() && validProtocols.contains(url.scheme().toLower())) { - return Acceptable; - } else { - return Intermediate; - } - } - }; - static ImapServerValidator *validator = new ImapServerValidator; - return validator; -} - -QValidator *AccountSettings::smtpServerValidator() const -{ - class SmtpServerValidator : public QValidator { - State validate(QString &input, int &pos) const { - Q_UNUSED(pos); - // smtps://mainserver.example.net:475 - const QUrl url(input); - static QSet validProtocols = QSet() << "smtp" << "smtps"; - if (url.isValid() && validProtocols.contains(url.scheme().toLower())) { - return Acceptable; - } else { - return Intermediate; - } - } - }; - static SmtpServerValidator *validator = new SmtpServerValidator; - return validator; -} - -void AccountSettings::saveAccount() -{ - if (mAccountIdentifier.isEmpty()) { - auto account = ApplicationDomainType::createEntity(); - mAccountIdentifier = account.identifier(); - Q_ASSERT(!mAccountType.isEmpty()); - account.setAccountType(mAccountType); - account.setName(mName); - account.setIcon(mIcon); - Store::create(account) - .onError([](const KAsync::Error &error) { - qWarning() << "Error while creating account: " << error.errorMessage;; - }) - .exec(); - } else { - qDebug() << "Saving account " << mAccountIdentifier << mMailtransportIdentifier; - Q_ASSERT(!mAccountIdentifier.isEmpty()); - SinkAccount account(mAccountIdentifier); - account.setAccountType(mAccountType); - account.setName(mName); - account.setIcon(mIcon); - Q_ASSERT(!account.identifier().isEmpty()); - Store::modify(account) - .onError([](const KAsync::Error &error) { - qWarning() << "Error while creating account: " << error.errorMessage;; - }) - .exec(); - } -} - -void AccountSettings::loadAccount() -{ - Q_ASSERT(!mAccountIdentifier.isEmpty()); - Store::fetchOne(Query().filter(mAccountIdentifier).request().request().request()) - .then([this](const SinkAccount &account) { - mAccountType = account.getAccountType().toLatin1(); - mIcon = account.getIcon(); - mName = account.getName(); - emit changed(); - }).exec(); -} - -void AccountSettings::loadImapResource() -{ - Store::fetchOne(Query().filter(mAccountIdentifier).containsFilter(ResourceCapabilities::Mail::storage)) - .then([this](const SinkResource &resource) { - mImapIdentifier = resource.identifier(); - mImapServer = resource.getProperty("server").toString(); - mImapUsername = resource.getProperty("username").toString(); - mImapPassword = resource.getProperty("password").toString(); - emit imapResourceChanged(); - }).onError([](const KAsync::Error &error) { - qWarning() << "Failed to find the imap resource: " << error.errorMessage; - }).exec(); -} - -void AccountSettings::loadMaildirResource() -{ - Store::fetchOne(Query().filter(mAccountIdentifier).containsFilter(ResourceCapabilities::Mail::storage)) - .then([this](const SinkResource &resource) { - mMaildirIdentifier = resource.identifier(); - auto path = resource.getProperty("path").toString(); - if (mPath != path) { - mPath = path; - emit pathChanged(); - } - }).onError([](const KAsync::Error &error) { - SinkWarning() << "Failed to find the maildir resource: " << error.errorMessage; - }).exec(); -} - -void AccountSettings::loadMailtransportResource() -{ - Store::fetchOne(Query().filter(mAccountIdentifier).containsFilter(ResourceCapabilities::Mail::transport)) - .then([this](const SinkResource &resource) { - mMailtransportIdentifier = resource.identifier(); - mSmtpServer = resource.getProperty("server").toString(); - mSmtpUsername = resource.getProperty("username").toString(); - mSmtpPassword = resource.getProperty("password").toString(); - emit smtpResourceChanged(); - }).onError([](const KAsync::Error &error) { - SinkWarning() << "Failed to find the smtp resource: " << error.errorMessage; - }).exec(); -} - -void AccountSettings::loadIdentity() -{ - //FIXME this assumes that we only ever have one identity per account - Store::fetchOne(Query().filter(mAccountIdentifier)) - .then([this](const Identity &identity) { - mIdentityIdentifier = identity.identifier(); - mUsername = identity.getName(); - mEmailAddress = identity.getAddress(); - emit identityChanged(); - }).onError([](const KAsync::Error &error) { - SinkWarning() << "Failed to find the identity resource: " << error.errorMessage; - }).exec(); -} - -void AccountSettings::loadCardDavResource() -{ - Store::fetchOne(Query().filter(mAccountIdentifier).containsFilter(ResourceCapabilities::Mail::storage)) - .then([this](const SinkResource &resource) { - mCardDavIdentifier = resource.identifier(); - mCardDavServer = resource.getProperty("server").toString(); - mCardDavUsername = resource.getProperty("username").toString(); - mCardDavPassword = resource.getProperty("password").toString(); - emit cardDavResourceChanged(); - }).onError([](const KAsync::Error &error) { - qWarning() << "Failed to find the CardDAV resource: " << error.errorMessage; - }).exec(); -} - - -template -static QByteArray saveResource(const QByteArray &accountIdentifier, const QByteArray &identifier, const std::map &properties) -{ - if (!identifier.isEmpty()) { - SinkResource resource(identifier); - for (const auto &pair : properties) { - resource.setProperty(pair.first, pair.second); - } - Store::modify(resource) - .onError([](const KAsync::Error &error) { - SinkWarning() << "Error while modifying resource: " << error.errorMessage; - }) - .exec(); - } else { - auto resource = ResourceType::create(accountIdentifier); - auto newIdentifier = resource.identifier(); - for (const auto &pair : properties) { - resource.setProperty(pair.first, pair.second); - } - Store::create(resource) - .onError([](const KAsync::Error &error) { - SinkWarning() << "Error while creating resource: " << error.errorMessage; - }) - .exec(); - return newIdentifier; - } - return identifier; -} - -void AccountSettings::saveImapResource() -{ - mImapIdentifier = saveResource(mAccountIdentifier, mImapIdentifier, { - {"server", mImapServer}, - {"username", mImapUsername}, - {"password", mImapPassword}, - }); -} - -void AccountSettings::saveCardDavResource() -{ - mCardDavIdentifier = saveResource(mAccountIdentifier, mCardDavIdentifier, { - {"server", mCardDavServer}, - {"username", mCardDavUsername}, - {"password", mCardDavPassword}, - }); -} - -void AccountSettings::saveMaildirResource() -{ - mMaildirIdentifier = saveResource(mAccountIdentifier, mMaildirIdentifier, { - {"path", mPath}, - }); -} - -void AccountSettings::saveMailtransportResource() -{ - mMailtransportIdentifier = saveResource(mAccountIdentifier, mMailtransportIdentifier, { - {"server", mSmtpServer}, - {"username", mSmtpUsername}, - {"password", mSmtpPassword}, - }); -} - -void AccountSettings::saveIdentity() -{ - if (!mIdentityIdentifier.isEmpty()) { - Identity identity(mMailtransportIdentifier); - identity.setName(mUsername); - identity.setAddress(mEmailAddress); - Store::modify(identity) - .onError([](const KAsync::Error &error) { - SinkWarning() << "Error while modifying identity: " << error.errorMessage; - }) - .exec(); - } else { - auto identity = ApplicationDomainType::createEntity(); - mIdentityIdentifier = identity.identifier(); - identity.setAccount(mAccountIdentifier); - identity.setName(mUsername); - identity.setAddress(mEmailAddress); - Store::create(identity) - .onError([](const KAsync::Error &error) { - SinkWarning() << "Error while creating identity: " << error.errorMessage; - }) - .exec(); - } -} - -void AccountSettings::removeResource(const QByteArray &identifier) -{ - if (identifier.isEmpty()) { - SinkWarning() << "We're missing an identifier"; - } else { - SinkResource resource(identifier); - Store::remove(resource) - .onError([](const KAsync::Error &error) { - SinkWarning() << "Error while removing resource: " << error.errorMessage; - }) - .exec(); - } -} - -void AccountSettings::removeAccount() -{ - if (mAccountIdentifier.isEmpty()) { - SinkWarning() << "We're missing an identifier"; - } else { - SinkAccount account(mAccountIdentifier); - Store::remove(account) - .onError([](const KAsync::Error &error) { - SinkWarning() << "Error while removing account: " << error.errorMessage; - }) - .exec(); - } -} - -void AccountSettings::removeIdentity() -{ - if (mIdentityIdentifier.isEmpty()) { - SinkWarning() << "We're missing an identifier"; - } else { - Identity identity(mIdentityIdentifier); - Store::remove(identity) - .onError([](const KAsync::Error &error) { - SinkWarning() << "Error while removing identity: " << error.errorMessage; - }) - .exec(); - } -} - diff --git a/framework/domain/settings/accountsettings.h b/framework/domain/settings/accountsettings.h deleted file mode 100644 index 077b7784..00000000 --- a/framework/domain/settings/accountsettings.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - Copyright (c) 2016 Christian Mollekopf - - This library is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This library 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 Library General Public - License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ -#pragma once - -#include -#include - -class AccountSettings : public QObject -{ - Q_OBJECT - Q_PROPERTY(QByteArray accountIdentifier READ accountIdentifier WRITE setAccountIdentifier) - Q_PROPERTY(QByteArray accountType READ accountType WRITE setAccountType) - Q_PROPERTY(QString icon MEMBER mIcon NOTIFY changed) - Q_PROPERTY(QString accountName MEMBER mName NOTIFY changed) - - Q_PROPERTY(QString userName MEMBER mUsername NOTIFY identityChanged) - Q_PROPERTY(QString emailAddress MEMBER mEmailAddress NOTIFY identityChanged) - - Q_PROPERTY(QString imapServer MEMBER mImapServer NOTIFY imapResourceChanged) - Q_PROPERTY(QValidator* imapServerValidator READ imapServerValidator CONSTANT) - Q_PROPERTY(QString imapUsername MEMBER mImapUsername NOTIFY imapResourceChanged) - Q_PROPERTY(QString imapPassword MEMBER mImapPassword NOTIFY imapResourceChanged) - - Q_PROPERTY(QString smtpServer MEMBER mSmtpServer NOTIFY smtpResourceChanged) - Q_PROPERTY(QValidator* smtpServerValidator READ smtpServerValidator CONSTANT) - Q_PROPERTY(QString smtpUsername MEMBER mSmtpUsername NOTIFY smtpResourceChanged) - Q_PROPERTY(QString smtpPassword MEMBER mSmtpPassword NOTIFY smtpResourceChanged) - - Q_PROPERTY(QUrl path READ path WRITE setPath NOTIFY pathChanged) - Q_PROPERTY(QValidator* pathValidator READ pathValidator CONSTANT) - -public: - AccountSettings(QObject *parent = 0); - - void setAccountIdentifier(const QByteArray &); - QByteArray accountIdentifier() const; - - void setAccountType(const QByteArray &); - QByteArray accountType() const; - - void setPath(const QUrl &); - QUrl path() const; - - virtual QValidator *imapServerValidator() const; - virtual QValidator *smtpServerValidator() const; - virtual QValidator *pathValidator() const; - - Q_INVOKABLE virtual void load() = 0; - Q_INVOKABLE virtual void save() = 0; - Q_INVOKABLE virtual void remove() = 0; - -signals: - void imapResourceChanged(); - void smtpResourceChanged(); - void identityChanged(); - void pathChanged(); - void changed(); - void cardDavResourceChanged(); - -protected: - void saveAccount(); - void saveImapResource(); - void saveMaildirResource(); - void saveMailtransportResource(); - void saveIdentity(); - void saveCardDavResource(); - - void loadAccount(); - void loadImapResource(); - void loadMaildirResource(); - void loadMailtransportResource(); - void loadIdentity(); - void loadCardDavResource(); - - void removeAccount(); - void removeResource(const QByteArray &identifier); - - void removeIdentity(); - - QByteArray mAccountIdentifier; - QByteArray mAccountType; - QString mIcon; - QString mName; - - QByteArray mImapIdentifier; - QString mImapServer; - QString mImapUsername; - QString mImapPassword; - - QByteArray mMaildirIdentifier; - QString mPath; - - QByteArray mMailtransportIdentifier; - QString mSmtpServer; - QString mSmtpUsername; - QString mSmtpPassword; - - QByteArray mIdentityIdentifier; - QString mUsername; - QString mEmailAddress; - - QByteArray mCardDavIdentifier; - QString mCardDavServer; - QString mCardDavUsername; - QString mCardDavPassword; -}; - diff --git a/framework/domain/stringhtmlwriter.cpp b/framework/domain/stringhtmlwriter.cpp deleted file mode 100644 index 88034492..00000000 --- a/framework/domain/stringhtmlwriter.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* -*- c++ -*- - filehtmlwriter.cpp - - This file is part of KMail, the KDE mail client. - Copyright (c) 2003 Marc Mutz - - KMail is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License, version 2, as - published by the Free Software Foundation. - - KMail is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - - In addition, as a special exception, the copyright holders give - permission to link the code of this program with any edition of - the Qt library by Trolltech AS, Norway (or with modified versions - of Qt that use the same license as Qt), and distribute linked - combinations including the two. You must obey the GNU General - Public License in all respects for all of the code used other than - Qt. If you modify this file, you may extend this exception to - your version of the file, but you are not obligated to do so. If - you do not wish to do so, delete this exception statement from - your version. -*/ - -#include "stringhtmlwriter.h" - -#include -#include -#include - -StringHtmlWriter::StringHtmlWriter() - : MimeTreeParser::HtmlWriter() - , mState(Ended) -{ -} - -StringHtmlWriter::~StringHtmlWriter() -{ -} - -void StringHtmlWriter::begin(const QString &css) -{ - if (mState != Ended) { - qWarning() << "begin() called on non-ended session!"; - reset(); - } - - mState = Begun; - mExtraHead.clear(); - mHtml.clear(); - - if (!css.isEmpty()) { - write(QLatin1String("\n")); - } -} - -void StringHtmlWriter::end() -{ - if (mState != Begun) { - qWarning() << "Called on non-begun or queued session!"; - } - - if (!mExtraHead.isEmpty()) { - insertExtraHead(); - mExtraHead.clear(); - } - resolveCidUrls(); - mState = Ended; -} - -void StringHtmlWriter::reset() -{ - if (mState != Ended) { - mHtml.clear(); - mExtraHead.clear(); - mState = Begun; // don't run into end()'s warning - end(); - mState = Ended; - } -} - -void StringHtmlWriter::write(const QString &str) -{ - if (mState != Begun) { - qWarning() << "Called in Ended or Queued state!"; - } - mHtml.append(str); -} - -void StringHtmlWriter::queue(const QString &str) -{ - write(str); -} - -void StringHtmlWriter::flush() -{ - mState = Begun; // don't run into end()'s warning - end(); -} - -void StringHtmlWriter::embedPart(const QByteArray &contentId, const QString &url) -{ - write("\n"); - mEmbeddedPartMap.insert(contentId, url); -} - -void StringHtmlWriter::resolveCidUrls() -{ - for (const auto &cid : mEmbeddedPartMap.keys()) { - mHtml.replace(QString("src=\"cid:%1\"").arg(QString(cid)), QString("src=\"%1\"").arg(mEmbeddedPartMap.value(cid).toString())); - } -} - -void StringHtmlWriter::extraHead(const QString &extraHead) -{ - if (mState != Ended) { - qWarning() << "Called on non-started session!"; - } - mExtraHead.append(extraHead); -} - - -void StringHtmlWriter::insertExtraHead() -{ - const QString headTag(QStringLiteral("")); - const int index = mHtml.indexOf(headTag); - if (index != -1) { - mHtml.insert(index + headTag.length(), mExtraHead); - } -} - -QMap StringHtmlWriter::embeddedParts() const -{ - return mEmbeddedPartMap; -} - -QString StringHtmlWriter::html() const -{ - if (mState != Ended) { - qWarning() << "Called on non-ended session!"; - } - return mHtml; -} diff --git a/framework/domain/stringhtmlwriter.h b/framework/domain/stringhtmlwriter.h deleted file mode 100644 index fa5b760e..00000000 --- a/framework/domain/stringhtmlwriter.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -*- c++ -*- - - Copyright (c) 2016 Sandro Knauß - - Kube is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License, version 2, as - published by the Free Software Foundation. - - Kube is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - - In addition, as a special exception, the copyright holders give - permission to link the code of this program with any edition of - the Qt library by Trolltech AS, Norway (or with modified versions - of Qt that use the same license as Qt), and distribute linked - combinations including the two. You must obey the GNU General - Public License in all respects for all of the code used other than - Qt. If you modify this file, you may extend this exception to - your version of the file, but you are not obligated to do so. If - you do not wish to do so, delete this exception statement from - your version. -*/ - -#ifndef __KUBE_FRAMEWORK_MAIL_STRINGHTMLWRITER_H__ -#define __KUBE_FRAMEWORK_MAIL_STRINGHTMLWRITER_H__ - -#include - -#include -#include - -class QString; - -class StringHtmlWriter : public MimeTreeParser::HtmlWriter -{ -public: - explicit StringHtmlWriter(); - virtual ~StringHtmlWriter(); - - void begin(const QString &cssDefs) Q_DECL_OVERRIDE; - void end() Q_DECL_OVERRIDE; - void reset() Q_DECL_OVERRIDE; - void write(const QString &str) Q_DECL_OVERRIDE; - void queue(const QString &str) Q_DECL_OVERRIDE; - void flush() Q_DECL_OVERRIDE; - void embedPart(const QByteArray &contentId, const QString &url) Q_DECL_OVERRIDE; - void extraHead(const QString &str) Q_DECL_OVERRIDE; - - QString html() const; - QMap embeddedParts() const; -private: - void insertExtraHead(); - void resolveCidUrls(); - - QString mHtml; - QString mExtraHead; - enum State { - Begun, - Queued, - Ended - } mState; - QMap mEmbeddedPartMap; -}; - -#endif // __MESSAGEVIEWER_FILEHTMLWRITER_H__ -- cgit v1.2.3