diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2016-11-28 19:33:01 +0100 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2016-11-28 19:33:01 +0100 |
commit | 938554f267193b652478fc12343819fa45d76034 (patch) | |
tree | 1c027f97f3209571740377f1d4b7e6721d8de777 /examples/imapresource/imapresource.cpp | |
parent | 885f185f55249a2e97e9c7c238f89a5d0d99d1df (diff) | |
download | sink-938554f267193b652478fc12343819fa45d76034.tar.gz sink-938554f267193b652478fc12343819fa45d76034.zip |
Moved inspection commands to a separate inspector.
Diffstat (limited to 'examples/imapresource/imapresource.cpp')
-rw-r--r-- | examples/imapresource/imapresource.cpp | 306 |
1 files changed, 165 insertions, 141 deletions
diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 2aa5a2c..40fa75f 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp | |||
@@ -27,6 +27,7 @@ | |||
27 | #include "definitions.h" | 27 | #include "definitions.h" |
28 | #include "inspection.h" | 28 | #include "inspection.h" |
29 | #include "synchronizer.h" | 29 | #include "synchronizer.h" |
30 | #include "inspector.h" | ||
30 | #include "remoteidmap.h" | 31 | #include "remoteidmap.h" |
31 | #include "query.h" | 32 | #include "query.h" |
32 | 33 | ||
@@ -553,169 +554,192 @@ public: | |||
553 | QByteArray mResourceInstanceIdentifier; | 554 | QByteArray mResourceInstanceIdentifier; |
554 | }; | 555 | }; |
555 | 556 | ||
556 | ImapResource::ImapResource(const ResourceContext &resourceContext) | 557 | class ImapInspector : public Sink::Inspector { |
557 | : Sink::GenericResource(resourceContext) | 558 | public: |
558 | { | 559 | ImapInspector(const Sink::ResourceContext &resourceContext) |
559 | auto config = ResourceConfig::getConfiguration(resourceContext.instanceId()); | 560 | : Sink::Inspector(resourceContext) |
560 | mServer = config.value("server").toString(); | 561 | { |
561 | mPort = config.value("port").toInt(); | ||
562 | mUser = config.value("username").toString(); | ||
563 | mPassword = config.value("password").toString(); | ||
564 | if (mServer.startsWith("imap")) { | ||
565 | mServer.remove("imap://"); | ||
566 | mServer.remove("imaps://"); | ||
567 | } | ||
568 | if (mServer.contains(':')) { | ||
569 | auto list = mServer.split(':'); | ||
570 | mServer = list.at(0); | ||
571 | mPort = list.at(1).toInt(); | ||
572 | } | ||
573 | |||
574 | auto synchronizer = QSharedPointer<ImapSynchronizer>::create(resourceContext); | ||
575 | synchronizer->mServer = mServer; | ||
576 | synchronizer->mPort = mPort; | ||
577 | synchronizer->mUser = mUser; | ||
578 | synchronizer->mPassword = mPassword; | ||
579 | setupSynchronizer(synchronizer); | ||
580 | |||
581 | setupPreprocessors(ENTITY_TYPE_MAIL, QVector<Sink::Preprocessor*>() << new SpecialPurposeProcessor(resourceContext.resourceType, resourceContext.instanceId()) << new MimeMessageMover << new MailPropertyExtractor); | ||
582 | setupPreprocessors(ENTITY_TYPE_FOLDER, QVector<Sink::Preprocessor*>()); | ||
583 | } | ||
584 | 562 | ||
585 | KAsync::Job<void> ImapResource::inspect(int inspectionType, const QByteArray &inspectionId, const QByteArray &domainType, const QByteArray &entityId, const QByteArray &property, const QVariant &expectedValue) | 563 | } |
586 | { | ||
587 | auto synchronizationStore = QSharedPointer<Sink::Storage::DataStore>::create(Sink::storageLocation(), mResourceContext.instanceId() + ".synchronization", Sink::Storage::DataStore::ReadOnly); | ||
588 | auto synchronizationTransaction = synchronizationStore->createTransaction(Sink::Storage::DataStore::ReadOnly); | ||
589 | 564 | ||
590 | auto mainStore = QSharedPointer<Sink::Storage::DataStore>::create(Sink::storageLocation(), mResourceContext.instanceId(), Sink::Storage::DataStore::ReadOnly); | 565 | protected: |
591 | auto transaction = mainStore->createTransaction(Sink::Storage::DataStore::ReadOnly); | 566 | KAsync::Job<void> inspect(int inspectionType, const QByteArray &inspectionId, const QByteArray &domainType, const QByteArray &entityId, const QByteArray &property, const QVariant &expectedValue) Q_DECL_OVERRIDE { |
567 | auto synchronizationStore = QSharedPointer<Sink::Storage::DataStore>::create(Sink::storageLocation(), mResourceContext.instanceId() + ".synchronization", Sink::Storage::DataStore::ReadOnly); | ||
568 | auto synchronizationTransaction = synchronizationStore->createTransaction(Sink::Storage::DataStore::ReadOnly); | ||
592 | 569 | ||
593 | Sink::Storage::EntityStore entityStore(mResourceContext); | 570 | auto mainStore = QSharedPointer<Sink::Storage::DataStore>::create(Sink::storageLocation(), mResourceContext.instanceId(), Sink::Storage::DataStore::ReadOnly); |
594 | auto syncStore = QSharedPointer<Sink::RemoteIdMap>::create(synchronizationTransaction); | 571 | auto transaction = mainStore->createTransaction(Sink::Storage::DataStore::ReadOnly); |
595 | 572 | ||
596 | SinkTrace() << "Inspecting " << inspectionType << domainType << entityId << property << expectedValue; | 573 | Sink::Storage::EntityStore entityStore(mResourceContext); |
574 | auto syncStore = QSharedPointer<Sink::RemoteIdMap>::create(synchronizationTransaction); | ||
597 | 575 | ||
598 | if (domainType == ENTITY_TYPE_MAIL) { | 576 | SinkTrace() << "Inspecting " << inspectionType << domainType << entityId << property << expectedValue; |
599 | const auto mail = entityStore.readLatest<Sink::ApplicationDomain::Mail>(entityId); | ||
600 | const auto folder = entityStore.readLatest<Sink::ApplicationDomain::Folder>(mail.getFolder()); | ||
601 | const auto folderRemoteId = syncStore->resolveLocalId(ENTITY_TYPE_FOLDER, mail.getFolder()); | ||
602 | const auto mailRemoteId = syncStore->resolveLocalId(ENTITY_TYPE_MAIL, mail.identifier()); | ||
603 | if (mailRemoteId.isEmpty() || folderRemoteId.isEmpty()) { | ||
604 | SinkWarning() << "Missing remote id for folder or mail. " << mailRemoteId << folderRemoteId; | ||
605 | return KAsync::error<void>(); | ||
606 | } | ||
607 | const auto uid = uidFromMailRid(mailRemoteId); | ||
608 | SinkTrace() << "Mail remote id: " << folderRemoteId << mailRemoteId << mail.identifier() << folder.identifier(); | ||
609 | 577 | ||
610 | KIMAP2::ImapSet set; | 578 | if (domainType == ENTITY_TYPE_MAIL) { |
611 | set.add(uid); | 579 | const auto mail = entityStore.readLatest<Sink::ApplicationDomain::Mail>(entityId); |
612 | if (set.isEmpty()) { | 580 | const auto folder = entityStore.readLatest<Sink::ApplicationDomain::Folder>(mail.getFolder()); |
613 | return KAsync::error<void>(1, "Couldn't determine uid of mail."); | 581 | const auto folderRemoteId = syncStore->resolveLocalId(ENTITY_TYPE_FOLDER, mail.getFolder()); |
614 | } | 582 | const auto mailRemoteId = syncStore->resolveLocalId(ENTITY_TYPE_MAIL, mail.identifier()); |
615 | KIMAP2::FetchJob::FetchScope scope; | 583 | if (mailRemoteId.isEmpty() || folderRemoteId.isEmpty()) { |
616 | scope.mode = KIMAP2::FetchJob::FetchScope::Full; | 584 | SinkWarning() << "Missing remote id for folder or mail. " << mailRemoteId << folderRemoteId; |
617 | auto imap = QSharedPointer<ImapServerProxy>::create(mServer, mPort); | 585 | return KAsync::error<void>(); |
618 | auto messageByUid = QSharedPointer<QHash<qint64, Imap::Message>>::create(); | ||
619 | SinkTrace() << "Connecting to:" << mServer << mPort; | ||
620 | SinkTrace() << "as:" << mUser; | ||
621 | auto inspectionJob = imap->login(mUser, mPassword) | ||
622 | .then<Imap::SelectResult>(imap->select(folderRemoteId)) | ||
623 | .syncThen<void, Imap::SelectResult>([](Imap::SelectResult){}) | ||
624 | .then<void>(imap->fetch(set, scope, [imap, messageByUid](const Imap::Message &message) { | ||
625 | messageByUid->insert(message.uid, message); | ||
626 | })); | ||
627 | |||
628 | if (inspectionType == Sink::ResourceControl::Inspection::PropertyInspectionType) { | ||
629 | if (property == "unread") { | ||
630 | return inspectionJob.then<void>([=]() { | ||
631 | auto msg = messageByUid->value(uid); | ||
632 | if (expectedValue.toBool() && msg.flags.contains(Imap::Flags::Seen)) { | ||
633 | return KAsync::error<void>(1, "Expected unread but couldn't find it."); | ||
634 | } | ||
635 | if (!expectedValue.toBool() && !msg.flags.contains(Imap::Flags::Seen)) { | ||
636 | return KAsync::error<void>(1, "Expected read but couldn't find it."); | ||
637 | } | ||
638 | return KAsync::null<void>(); | ||
639 | }); | ||
640 | } | ||
641 | if (property == "subject") { | ||
642 | return inspectionJob.then<void>([=]() { | ||
643 | auto msg = messageByUid->value(uid); | ||
644 | if (msg.msg->subject(true)->asUnicodeString() != expectedValue.toString()) { | ||
645 | return KAsync::error<void>(1, "Subject not as expected: " + msg.msg->subject(true)->asUnicodeString()); | ||
646 | } | ||
647 | return KAsync::null<void>(); | ||
648 | }); | ||
649 | } | 586 | } |
650 | } | 587 | const auto uid = uidFromMailRid(mailRemoteId); |
651 | if (inspectionType == Sink::ResourceControl::Inspection::ExistenceInspectionType) { | 588 | SinkTrace() << "Mail remote id: " << folderRemoteId << mailRemoteId << mail.identifier() << folder.identifier(); |
652 | return inspectionJob.then<void>([=]() { | ||
653 | if (!messageByUid->contains(uid)) { | ||
654 | SinkWarning() << "Existing messages are: " << messageByUid->keys(); | ||
655 | SinkWarning() << "We're looking for: " << uid; | ||
656 | return KAsync::error<void>(1, "Couldn't find message: " + mailRemoteId); | ||
657 | } | ||
658 | return KAsync::null<void>(); | ||
659 | }); | ||
660 | } | ||
661 | } | ||
662 | if (domainType == ENTITY_TYPE_FOLDER) { | ||
663 | const auto remoteId = syncStore->resolveLocalId(ENTITY_TYPE_FOLDER, entityId); | ||
664 | const auto folder = entityStore.readLatest<Sink::ApplicationDomain::Folder>(entityId); | ||
665 | |||
666 | if (inspectionType == Sink::ResourceControl::Inspection::CacheIntegrityInspectionType) { | ||
667 | SinkLog() << "Inspecting cache integrity" << remoteId; | ||
668 | 589 | ||
669 | int expectedCount = 0; | 590 | KIMAP2::ImapSet set; |
670 | Index index("mail.index.folder", transaction); | 591 | set.add(uid); |
671 | index.lookup(entityId, [&](const QByteArray &sinkId) { | 592 | if (set.isEmpty()) { |
672 | expectedCount++; | 593 | return KAsync::error<void>(1, "Couldn't determine uid of mail."); |
673 | }, | 594 | } |
674 | [&](const Index::Error &error) { | ||
675 | SinkWarning() << "Error in index: " << error.message << property; | ||
676 | }); | ||
677 | |||
678 | auto set = KIMAP2::ImapSet::fromImapSequenceSet("1:*"); | ||
679 | KIMAP2::FetchJob::FetchScope scope; | 595 | KIMAP2::FetchJob::FetchScope scope; |
680 | scope.mode = KIMAP2::FetchJob::FetchScope::Headers; | 596 | scope.mode = KIMAP2::FetchJob::FetchScope::Full; |
681 | auto imap = QSharedPointer<ImapServerProxy>::create(mServer, mPort); | 597 | auto imap = QSharedPointer<ImapServerProxy>::create(mServer, mPort); |
682 | auto messageByUid = QSharedPointer<QHash<qint64, Imap::Message>>::create(); | 598 | auto messageByUid = QSharedPointer<QHash<qint64, Imap::Message>>::create(); |
683 | return imap->login(mUser, mPassword) | 599 | SinkTrace() << "Connecting to:" << mServer << mPort; |
684 | .then<void>(imap->select(remoteId).syncThen<void>([](){})) | 600 | SinkTrace() << "as:" << mUser; |
685 | .then<void>(imap->fetch(set, scope, [=](const Imap::Message message) { | 601 | auto inspectionJob = imap->login(mUser, mPassword) |
602 | .then<Imap::SelectResult>(imap->select(folderRemoteId)) | ||
603 | .syncThen<void, Imap::SelectResult>([](Imap::SelectResult){}) | ||
604 | .then<void>(imap->fetch(set, scope, [imap, messageByUid](const Imap::Message &message) { | ||
686 | messageByUid->insert(message.uid, message); | 605 | messageByUid->insert(message.uid, message); |
687 | })) | 606 | })); |
688 | .then<void>([imap, messageByUid, expectedCount]() { | 607 | |
689 | if (messageByUid->size() != expectedCount) { | 608 | if (inspectionType == Sink::ResourceControl::Inspection::PropertyInspectionType) { |
690 | return KAsync::error<void>(1, QString("Wrong number of messages on the server; found %1 instead of %2.").arg(messageByUid->size()).arg(expectedCount)); | 609 | if (property == "unread") { |
610 | return inspectionJob.then<void>([=]() { | ||
611 | auto msg = messageByUid->value(uid); | ||
612 | if (expectedValue.toBool() && msg.flags.contains(Imap::Flags::Seen)) { | ||
613 | return KAsync::error<void>(1, "Expected unread but couldn't find it."); | ||
614 | } | ||
615 | if (!expectedValue.toBool() && !msg.flags.contains(Imap::Flags::Seen)) { | ||
616 | return KAsync::error<void>(1, "Expected read but couldn't find it."); | ||
617 | } | ||
618 | return KAsync::null<void>(); | ||
619 | }); | ||
620 | } | ||
621 | if (property == "subject") { | ||
622 | return inspectionJob.then<void>([=]() { | ||
623 | auto msg = messageByUid->value(uid); | ||
624 | if (msg.msg->subject(true)->asUnicodeString() != expectedValue.toString()) { | ||
625 | return KAsync::error<void>(1, "Subject not as expected: " + msg.msg->subject(true)->asUnicodeString()); | ||
626 | } | ||
627 | return KAsync::null<void>(); | ||
628 | }); | ||
629 | } | ||
630 | } | ||
631 | if (inspectionType == Sink::ResourceControl::Inspection::ExistenceInspectionType) { | ||
632 | return inspectionJob.then<void>([=]() { | ||
633 | if (!messageByUid->contains(uid)) { | ||
634 | SinkWarning() << "Existing messages are: " << messageByUid->keys(); | ||
635 | SinkWarning() << "We're looking for: " << uid; | ||
636 | return KAsync::error<void>(1, "Couldn't find message: " + mailRemoteId); | ||
691 | } | 637 | } |
692 | return KAsync::null<void>(); | 638 | return KAsync::null<void>(); |
693 | }); | 639 | }); |
640 | } | ||
694 | } | 641 | } |
695 | if (inspectionType == Sink::ResourceControl::Inspection::ExistenceInspectionType) { | 642 | if (domainType == ENTITY_TYPE_FOLDER) { |
696 | auto folderByPath = QSharedPointer<QSet<QString>>::create(); | 643 | const auto remoteId = syncStore->resolveLocalId(ENTITY_TYPE_FOLDER, entityId); |
697 | auto folderByName = QSharedPointer<QSet<QString>>::create(); | 644 | const auto folder = entityStore.readLatest<Sink::ApplicationDomain::Folder>(entityId); |
698 | 645 | ||
699 | auto imap = QSharedPointer<ImapServerProxy>::create(mServer, mPort); | 646 | if (inspectionType == Sink::ResourceControl::Inspection::CacheIntegrityInspectionType) { |
700 | auto inspectionJob = imap->login(mUser, mPassword) | 647 | SinkLog() << "Inspecting cache integrity" << remoteId; |
701 | .then<void>(imap->fetchFolders([=](const Imap::Folder &f) { | 648 | |
702 | *folderByPath << f.normalizedPath(); | 649 | int expectedCount = 0; |
703 | *folderByName << f.name(); | 650 | Index index("mail.index.folder", transaction); |
704 | })) | 651 | index.lookup(entityId, [&](const QByteArray &sinkId) { |
705 | .then<void>([this, folderByName, folderByPath, folder, remoteId, imap]() { | 652 | expectedCount++; |
706 | if (!folderByName->contains(folder.getName())) { | 653 | }, |
707 | SinkWarning() << "Existing folders are: " << *folderByPath; | 654 | [&](const Index::Error &error) { |
708 | SinkWarning() << "We're looking for: " << folder.getName(); | 655 | SinkWarning() << "Error in index: " << error.message << property; |
709 | return KAsync::error<void>(1, "Wrong folder name: " + remoteId); | ||
710 | } | ||
711 | return KAsync::null<void>(); | ||
712 | }); | 656 | }); |
713 | 657 | ||
714 | return inspectionJob; | 658 | auto set = KIMAP2::ImapSet::fromImapSequenceSet("1:*"); |
659 | KIMAP2::FetchJob::FetchScope scope; | ||
660 | scope.mode = KIMAP2::FetchJob::FetchScope::Headers; | ||
661 | auto imap = QSharedPointer<ImapServerProxy>::create(mServer, mPort); | ||
662 | auto messageByUid = QSharedPointer<QHash<qint64, Imap::Message>>::create(); | ||
663 | return imap->login(mUser, mPassword) | ||
664 | .then<void>(imap->select(remoteId).syncThen<void>([](){})) | ||
665 | .then<void>(imap->fetch(set, scope, [=](const Imap::Message message) { | ||
666 | messageByUid->insert(message.uid, message); | ||
667 | })) | ||
668 | .then<void>([imap, messageByUid, expectedCount]() { | ||
669 | if (messageByUid->size() != expectedCount) { | ||
670 | return KAsync::error<void>(1, QString("Wrong number of messages on the server; found %1 instead of %2.").arg(messageByUid->size()).arg(expectedCount)); | ||
671 | } | ||
672 | return KAsync::null<void>(); | ||
673 | }); | ||
674 | } | ||
675 | if (inspectionType == Sink::ResourceControl::Inspection::ExistenceInspectionType) { | ||
676 | auto folderByPath = QSharedPointer<QSet<QString>>::create(); | ||
677 | auto folderByName = QSharedPointer<QSet<QString>>::create(); | ||
678 | |||
679 | auto imap = QSharedPointer<ImapServerProxy>::create(mServer, mPort); | ||
680 | auto inspectionJob = imap->login(mUser, mPassword) | ||
681 | .then<void>(imap->fetchFolders([=](const Imap::Folder &f) { | ||
682 | *folderByPath << f.normalizedPath(); | ||
683 | *folderByName << f.name(); | ||
684 | })) | ||
685 | .then<void>([this, folderByName, folderByPath, folder, remoteId, imap]() { | ||
686 | if (!folderByName->contains(folder.getName())) { | ||
687 | SinkWarning() << "Existing folders are: " << *folderByPath; | ||
688 | SinkWarning() << "We're looking for: " << folder.getName(); | ||
689 | return KAsync::error<void>(1, "Wrong folder name: " + remoteId); | ||
690 | } | ||
691 | return KAsync::null<void>(); | ||
692 | }); | ||
693 | |||
694 | return inspectionJob; | ||
695 | } | ||
696 | |||
715 | } | 697 | } |
698 | return KAsync::null<void>(); | ||
699 | } | ||
700 | |||
701 | public: | ||
702 | QString mServer; | ||
703 | int mPort; | ||
704 | QString mUser; | ||
705 | QString mPassword; | ||
706 | }; | ||
707 | |||
716 | 708 | ||
709 | ImapResource::ImapResource(const ResourceContext &resourceContext) | ||
710 | : Sink::GenericResource(resourceContext) | ||
711 | { | ||
712 | auto config = ResourceConfig::getConfiguration(resourceContext.instanceId()); | ||
713 | auto server = config.value("server").toString(); | ||
714 | auto port = config.value("port").toInt(); | ||
715 | auto user = config.value("username").toString(); | ||
716 | auto password = config.value("password").toString(); | ||
717 | if (server.startsWith("imap")) { | ||
718 | server.remove("imap://"); | ||
719 | server.remove("imaps://"); | ||
720 | } | ||
721 | if (server.contains(':')) { | ||
722 | auto list = server.split(':'); | ||
723 | server = list.at(0); | ||
724 | port = list.at(1).toInt(); | ||
717 | } | 725 | } |
718 | return KAsync::null<void>(); | 726 | |
727 | auto synchronizer = QSharedPointer<ImapSynchronizer>::create(resourceContext); | ||
728 | synchronizer->mServer = server; | ||
729 | synchronizer->mPort = port; | ||
730 | synchronizer->mUser = user; | ||
731 | synchronizer->mPassword = password; | ||
732 | setupSynchronizer(synchronizer); | ||
733 | |||
734 | auto inspector = QSharedPointer<ImapInspector>::create(resourceContext); | ||
735 | inspector->mServer = server; | ||
736 | inspector->mPort = port; | ||
737 | inspector->mUser = user; | ||
738 | inspector->mPassword = password; | ||
739 | setupInspector(inspector); | ||
740 | |||
741 | setupPreprocessors(ENTITY_TYPE_MAIL, QVector<Sink::Preprocessor*>() << new SpecialPurposeProcessor(resourceContext.resourceType, resourceContext.instanceId()) << new MimeMessageMover << new MailPropertyExtractor); | ||
742 | setupPreprocessors(ENTITY_TYPE_FOLDER, QVector<Sink::Preprocessor*>()); | ||
719 | } | 743 | } |
720 | 744 | ||
721 | ImapResourceFactory::ImapResourceFactory(QObject *parent) | 745 | ImapResourceFactory::ImapResourceFactory(QObject *parent) |