File indexing completed on 2024-05-12 05:20:40

0001 /*   */
0002 
0003 #include "kmkernel.h"
0004 
0005 #include "job/fillcomposerjob.h"
0006 #include "job/newmessagejob.h"
0007 #include "job/opencomposerhiddenjob.h"
0008 #include "job/opencomposerjob.h"
0009 #include <PIM/indexeditems.h>
0010 #include <PimCommon/BroadcastStatus>
0011 #include <PimCommonAkonadi/ProgressManagerAkonadi>
0012 using PimCommon::BroadcastStatus;
0013 #include "commandlineinfo.h"
0014 #include "editor/composer.h"
0015 #include "kmmainwidget.h"
0016 #include "kmmainwin.h"
0017 #include "kmreadermainwin.h"
0018 #include "undostack.h"
0019 
0020 #include "search/checkindexingmanager.h"
0021 #include <PimCommonAkonadi/RecentAddresses>
0022 using PimCommon::RecentAddresses;
0023 #include "configuredialog/configuredialog.h"
0024 #include "folderarchive/folderarchivemanager.h"
0025 #include "kmcommands.h"
0026 #include "mailfilteragentinterface.h"
0027 #include "sieveimapinterface/kmailsieveimapinstanceinterface.h"
0028 #include "unityservicemanager.h"
0029 #include <MailCommon/FolderTreeView>
0030 #include <MailCommon/KMFilterDialog>
0031 #include <MailCommon/MailUtil>
0032 #include <MessageCore/StringUtil>
0033 #include <PimCommon/PimUtil>
0034 #include <mailcommon/mailcommonsettings_base.h>
0035 #include <mailcommon/pop3settings.h>
0036 
0037 #include <Akonadi/DispatcherInterface>
0038 #include <KIdentityManagementCore/Identity>
0039 #include <KIdentityManagementCore/IdentityManager>
0040 #include <MailTransport/Transport>
0041 #include <MailTransport/TransportManager>
0042 
0043 #include "mailserviceimpl.h"
0044 #include <KSieveCore/SieveImapInstanceInterfaceManager>
0045 using KMail::MailServiceImpl;
0046 #include <MailCommon/JobScheduler>
0047 
0048 #include <MessageComposer/AkonadiSender>
0049 #include <MessageComposer/MessageComposerSettings>
0050 #include <MessageComposer/MessageHelper>
0051 #include <MessageCore/MessageCoreSettings>
0052 #include <MessageList/MessageListUtil>
0053 #include <MessageViewer/MessageViewerSettings>
0054 #include <PimCommon/NetworkManager>
0055 #include <TextAutoCorrectionCore/AutoCorrection>
0056 #include <TextAutoCorrectionCore/TextAutoCorrectionSettings>
0057 #include <gravatar/gravatarsettings.h>
0058 #include <messagelist/messagelistsettings.h>
0059 
0060 #include <TemplateParser/TemplatesUtil>
0061 #include <templateparser/globalsettings_templateparser.h>
0062 
0063 #include <MailCommon/FolderSettings>
0064 
0065 #include <KMessageBox>
0066 #include <KNotification>
0067 
0068 #include "kmail_debug.h"
0069 #include <KConfig>
0070 #include <KConfigGroup>
0071 #include <KCrash>
0072 #include <KIO/JobUiDelegate>
0073 
0074 #include <Akonadi/AgentManager>
0075 #include <Akonadi/AttributeFactory>
0076 #include <Akonadi/ChangeRecorder>
0077 #include <Akonadi/Collection>
0078 #include <Akonadi/CollectionFetchJob>
0079 #include <Akonadi/CollectionStatisticsJob>
0080 #include <Akonadi/EntityMimeTypeFilterModel>
0081 #include <Akonadi/EntityTreeModel>
0082 #include <Akonadi/ItemFetchJob>
0083 #include <Akonadi/ItemFetchScope>
0084 #include <Akonadi/Session>
0085 #include <KMime/Headers>
0086 #include <KMime/KMimeMessage>
0087 
0088 #include <Libkleo/FileSystemWatcher>
0089 #include <Libkleo/GnuPG>
0090 #include <Libkleo/KeyCache>
0091 
0092 #include <QDir>
0093 #include <QFileInfo>
0094 #include <QWidget>
0095 
0096 #include <MailCommon/ResourceReadConfigFile>
0097 
0098 #include <KLocalizedString>
0099 #include <MailCommon/FolderCollectionMonitor>
0100 #include <MailCommon/MailKernel>
0101 #include <QStandardPaths>
0102 #include <kmailadaptor.h>
0103 #include <pimcommon/imapresourcesettings.h>
0104 
0105 #include "kmail_options.h"
0106 #include "searchdialog/searchdescriptionattribute.h"
0107 #ifdef WITH_KUSERFEEDBACK
0108 #include "userfeedback/kmailuserfeedbackprovider.h"
0109 #include <KUserFeedback/Provider>
0110 #endif
0111 #include <chrono>
0112 
0113 using namespace std::chrono_literals;
0114 // #define DEBUG_SCHEDULER 1
0115 using namespace MailCommon;
0116 
0117 static KMKernel *mySelf = nullptr;
0118 static bool s_askingToGoOnline = false;
0119 /********************************************************************/
0120 /*                     Constructor and destructor                   */
0121 /********************************************************************/
0122 KMKernel::KMKernel(QObject *parent)
0123     : QObject(parent)
0124     , mJobScheduler(new JobScheduler(this))
0125     , mFolderArchiveManager(new FolderArchiveManager(this))
0126 {
0127     // Initialize kmail sieveimap interface
0128     KSieveCore::SieveImapInstanceInterfaceManager::self()->setSieveImapInstanceInterface(new KMailSieveImapInstanceInterface);
0129     mDebug = !qEnvironmentVariableIsEmpty("KDEPIM_DEBUGGING");
0130 
0131 #ifdef WITH_KUSERFEEDBACK
0132     mUserFeedbackProvider = new KMailUserFeedbackProvider(this);
0133 #endif
0134     mSystemNetworkStatus = PimCommon::NetworkManager::self()->isOnline();
0135 
0136     Akonadi::AttributeFactory::registerAttribute<Akonadi::SearchDescriptionAttribute>();
0137     QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.kmail"));
0138     qCDebug(KMAIL_LOG) << "Starting up...";
0139 
0140     mySelf = this;
0141     the_firstInstance = true;
0142 
0143     mFilterEditDialog = nullptr;
0144     // make sure that we check for config updates before doing anything else
0145     KMKernel::config();
0146     // this shares the kmailrc parsing too (via KSharedConfig), and reads values from it
0147     // so better do it here, than in some code where changing the group of config()
0148     // would be unexpected
0149     KMailSettings::self();
0150     mAutoCorrection = new TextAutoCorrectionCore::AutoCorrection();
0151 
0152     auto session = new Akonadi::Session("KMail Kernel ETM", this);
0153 
0154     mFolderCollectionMonitor = new FolderCollectionMonitor(session, this);
0155 
0156     connect(mFolderCollectionMonitor->monitor(), &Akonadi::Monitor::collectionRemoved, this, &KMKernel::slotCollectionRemoved);
0157 
0158     mEntityTreeModel = new Akonadi::EntityTreeModel(folderCollectionMonitor(), this);
0159     mEntityTreeModel->setListFilter(Akonadi::CollectionFetchScope::Enabled);
0160     mEntityTreeModel->setItemPopulationStrategy(Akonadi::EntityTreeModel::LazyPopulation);
0161 
0162     mCollectionModel = new Akonadi::EntityMimeTypeFilterModel(this);
0163     mCollectionModel->setSourceModel(mEntityTreeModel);
0164     mCollectionModel->addMimeTypeInclusionFilter(Akonadi::Collection::mimeType());
0165     mCollectionModel->setHeaderGroup(Akonadi::EntityTreeModel::CollectionTreeHeaders);
0166     mCollectionModel->setDynamicSortFilter(true);
0167     mCollectionModel->setSortCaseSensitivity(Qt::CaseInsensitive);
0168 
0169     connect(folderCollectionMonitor(),
0170             qOverload<const Akonadi::Collection &, const QSet<QByteArray> &>(&Akonadi::ChangeRecorder::collectionChanged),
0171             this,
0172             &KMKernel::slotCollectionChanged);
0173 
0174     connect(MailTransport::TransportManager::self(), &MailTransport::TransportManager::transportRemoved, this, &KMKernel::transportRemoved);
0175     connect(MailTransport::TransportManager::self(), &MailTransport::TransportManager::transportRenamed, this, &KMKernel::transportRenamed);
0176 
0177     QDBusConnection::sessionBus().connect(QString(),
0178                                           QStringLiteral("/MailDispatcherAgent"),
0179                                           QStringLiteral("org.freedesktop.Akonadi.MailDispatcherAgent"),
0180                                           QStringLiteral("itemDispatchStarted"),
0181                                           this,
0182                                           SLOT(itemDispatchStarted()));
0183     connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceStatusChanged, this, &KMKernel::instanceStatusChanged);
0184 
0185     connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceError, this, &KMKernel::slotInstanceError);
0186 
0187     connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceWarning, this, &KMKernel::slotInstanceWarning);
0188 
0189     connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceRemoved, this, &KMKernel::slotInstanceRemoved);
0190 
0191     connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceAdded, this, &KMKernel::slotInstanceAdded);
0192 
0193     connect(PimCommon::NetworkManager::self(), &PimCommon::NetworkManager::networkStatusChanged, this, &KMKernel::slotSystemNetworkStatusChanged);
0194 
0195     connect(KPIM::ProgressManager::instance(), &KPIM::ProgressManager::progressItemCompleted, this, &KMKernel::slotProgressItemCompletedOrCanceled);
0196     connect(KPIM::ProgressManager::instance(), &KPIM::ProgressManager::progressItemCanceled, this, &KMKernel::slotProgressItemCompletedOrCanceled);
0197     connect(identityManager(), &KIdentityManagementCore::IdentityManager::deleted, this, &KMKernel::slotDeleteIdentity);
0198     CommonKernel->registerKernelIf(this);
0199     CommonKernel->registerSettingsIf(this);
0200     CommonKernel->registerFilterIf(this);
0201 
0202     mIndexedItems = new Akonadi::Search::PIM::IndexedItems(this);
0203     mCheckIndexingManager = new CheckIndexingManager(mIndexedItems, this);
0204     mUnityServiceManager = new KMail::UnityServiceManager(this);
0205 }
0206 
0207 KMKernel::~KMKernel()
0208 {
0209     delete mMailService;
0210     mMailService = nullptr;
0211 
0212     stopAgentInstance();
0213     saveConfig();
0214 
0215     delete mAutoCorrection;
0216     delete mMailCommonSettings;
0217     mySelf = nullptr;
0218 }
0219 
0220 Akonadi::ChangeRecorder *KMKernel::folderCollectionMonitor() const
0221 {
0222     return mFolderCollectionMonitor->monitor();
0223 }
0224 
0225 Akonadi::EntityTreeModel *KMKernel::entityTreeModel() const
0226 {
0227     return mEntityTreeModel;
0228 }
0229 
0230 Akonadi::EntityMimeTypeFilterModel *KMKernel::collectionModel() const
0231 {
0232     return mCollectionModel;
0233 }
0234 
0235 void KMKernel::setupDBus()
0236 {
0237     (void)new KmailAdaptor(this);
0238     QDBusConnection::sessionBus().registerObject(QStringLiteral("/KMail"), this);
0239     mMailService = new MailServiceImpl();
0240 }
0241 
0242 bool KMKernel::handleCommandLine(bool noArgsOpensReader, const QStringList &args, const QString &workingDir)
0243 {
0244     // qDebug() << " args " << args;
0245     CommandLineInfo commandLineInfo;
0246     commandLineInfo.parseCommandLine(args, workingDir);
0247 
0248     if (commandLineInfo.startInTray()) {
0249         KMailSettings::self()->setSystemTrayEnabled(true);
0250     }
0251 
0252     if (!noArgsOpensReader && !commandLineInfo.mailto() && !commandLineInfo.checkMail() && !commandLineInfo.viewOnly()) {
0253         return false;
0254     }
0255 
0256     if (commandLineInfo.viewOnly()) {
0257         viewMessage(commandLineInfo.messageFile());
0258     } else {
0259         action(commandLineInfo.mailto(),
0260                commandLineInfo.checkMail(),
0261                commandLineInfo.startInTray(),
0262                commandLineInfo.to(),
0263                commandLineInfo.cc(),
0264                commandLineInfo.bcc(),
0265                commandLineInfo.subject(),
0266                commandLineInfo.body(),
0267                commandLineInfo.messageFile(),
0268                commandLineInfo.attachURLs(),
0269                commandLineInfo.customHeaders(),
0270                commandLineInfo.replyTo(),
0271                commandLineInfo.inReplyTo(),
0272                commandLineInfo.identity());
0273     }
0274     return true;
0275 }
0276 
0277 /********************************************************************/
0278 /*             D-Bus-callable, and command line actions              */
0279 /********************************************************************/
0280 void KMKernel::checkMail() // might create a new reader but won't show!!
0281 {
0282     if (!kmkernel->askToGoOnline()) {
0283         return;
0284     }
0285 
0286     const QString resourceGroupPattern(QStringLiteral("Resource %1"));
0287 
0288     const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances();
0289     for (Akonadi::AgentInstance type : lst) {
0290         const QString id = type.identifier();
0291         KConfigGroup group(KMKernel::config(), resourceGroupPattern.arg(id));
0292         if (group.readEntry("IncludeInManualChecks", true)) {
0293             if (!type.isOnline()) {
0294                 type.setIsOnline(true);
0295             }
0296             if (mResourcesBeingChecked.isEmpty()) {
0297                 qCDebug(KMAIL_LOG) << "Starting manual mail check";
0298                 Q_EMIT startCheckMail();
0299             }
0300 
0301             if (!mResourcesBeingChecked.contains(id)) {
0302                 mResourcesBeingChecked.append(id);
0303             }
0304             type.synchronize();
0305         }
0306     }
0307 }
0308 
0309 void KMKernel::openReader()
0310 {
0311     openReader(false, false);
0312 }
0313 
0314 QStringList KMKernel::accounts() const
0315 {
0316     QStringList accountLst;
0317     const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances();
0318     accountLst.reserve(lst.count());
0319     for (const Akonadi::AgentInstance &type : lst) {
0320         // Explicitly make a copy, as we're not changing values of the list but only
0321         // the local copy which is passed to action.
0322         accountLst << type.identifier();
0323     }
0324     return accountLst;
0325 }
0326 
0327 void KMKernel::checkAccount(const QString &account) // might create a new reader but won't show!!
0328 {
0329     if (account.isEmpty()) {
0330         checkMail();
0331     } else {
0332         Akonadi::AgentInstance agent = Akonadi::AgentManager::self()->instance(account);
0333         if (agent.isValid()) {
0334             agent.synchronize();
0335         } else {
0336             qCDebug(KMAIL_LOG) << "- account with name '" << account << "' not found";
0337         }
0338     }
0339 }
0340 
0341 void KMKernel::openReader(bool onlyCheck, bool startInTray)
0342 {
0343     KMainWindow *ktmw = nullptr;
0344 
0345     const auto lst = KMainWindow::memberList();
0346     for (KMainWindow *window : lst) {
0347         if (::qobject_cast<KMMainWin *>(window)) {
0348             ktmw = window;
0349             break;
0350         }
0351     }
0352 
0353     bool activate;
0354     if (ktmw) {
0355         auto win = static_cast<KMMainWin *>(ktmw);
0356         activate = !onlyCheck; // existing window: only activate if not --check
0357         if (activate) {
0358             win->showAndActivateWindow();
0359         }
0360     } else {
0361         auto win = new KMMainWin;
0362         if (!startInTray && !KMailSettings::self()->startInTray()) {
0363             win->showAndActivateWindow();
0364         }
0365         activate = false; // new window: no explicit activation (#73591)
0366     }
0367 }
0368 
0369 void KMKernel::openComposer(const QString &to,
0370                             const QString &cc,
0371                             const QString &bcc,
0372                             const QString &subject,
0373                             const QString &body,
0374                             bool hidden,
0375                             const QString &messageFile,
0376                             const QStringList &attachmentPaths,
0377                             const QStringList &customHeaders,
0378                             const QString &replyTo,
0379                             const QString &inReplyTo,
0380                             const QString &identity)
0381 {
0382     const OpenComposerSettings settings(to, cc, bcc, subject, body, hidden, messageFile, attachmentPaths, customHeaders, replyTo, inReplyTo, identity);
0383     auto job = new OpenComposerJob(this);
0384     job->setOpenComposerSettings(std::move(settings));
0385     job->start();
0386 }
0387 
0388 void KMKernel::openComposer(const QString &to,
0389                             const QString &cc,
0390                             const QString &bcc,
0391                             const QString &subject,
0392                             const QString &body,
0393                             bool hidden,
0394                             const QString &attachName,
0395                             const QByteArray &attachCte,
0396                             const QByteArray &attachData,
0397                             const QByteArray &attachType,
0398                             const QByteArray &attachSubType,
0399                             const QByteArray &attachParamAttr,
0400                             const QString &attachParamValue,
0401                             const QByteArray &attachContDisp,
0402                             const QByteArray &attachCharset,
0403                             unsigned int identity)
0404 {
0405     fillComposer(hidden,
0406                  to,
0407                  cc,
0408                  bcc,
0409                  subject,
0410                  body,
0411                  attachName,
0412                  attachCte,
0413                  attachData,
0414                  attachType,
0415                  attachSubType,
0416                  attachParamAttr,
0417                  attachParamValue,
0418                  attachContDisp,
0419                  attachCharset,
0420                  identity,
0421                  false);
0422 }
0423 
0424 void KMKernel::openComposer(const QString &to,
0425                             const QString &cc,
0426                             const QString &bcc,
0427                             const QString &subject,
0428                             const QString &body,
0429                             const QString &attachName,
0430                             const QByteArray &attachCte,
0431                             const QByteArray &attachData,
0432                             const QByteArray &attachType,
0433                             const QByteArray &attachSubType,
0434                             const QByteArray &attachParamAttr,
0435                             const QString &attachParamValue,
0436                             const QByteArray &attachContDisp,
0437                             const QByteArray &attachCharset,
0438                             unsigned int identity)
0439 {
0440     fillComposer(false,
0441                  to,
0442                  cc,
0443                  bcc,
0444                  subject,
0445                  body,
0446                  attachName,
0447                  attachCte,
0448                  attachData,
0449                  attachType,
0450                  attachSubType,
0451                  attachParamAttr,
0452                  attachParamValue,
0453                  attachContDisp,
0454                  attachCharset,
0455                  identity,
0456                  true);
0457 }
0458 
0459 void KMKernel::fillComposer(bool hidden,
0460                             const QString &to,
0461                             const QString &cc,
0462                             const QString &bcc,
0463                             const QString &subject,
0464                             const QString &body,
0465                             const QString &attachName,
0466                             const QByteArray &attachCte,
0467                             const QByteArray &attachData,
0468                             const QByteArray &attachType,
0469                             const QByteArray &attachSubType,
0470                             const QByteArray &attachParamAttr,
0471                             const QString &attachParamValue,
0472                             const QByteArray &attachContDisp,
0473                             const QByteArray &attachCharset,
0474                             unsigned int identity,
0475                             bool forceShowWindow)
0476 {
0477     const FillComposerJobSettings settings(hidden,
0478                                            to,
0479                                            cc,
0480                                            bcc,
0481                                            subject,
0482                                            body,
0483                                            attachName,
0484                                            attachCte,
0485                                            attachData,
0486                                            attachType,
0487                                            attachSubType,
0488                                            attachParamAttr,
0489                                            attachParamValue,
0490                                            attachContDisp,
0491                                            attachCharset,
0492                                            identity,
0493                                            forceShowWindow);
0494     auto job = new FillComposerJob;
0495     job->setSettings(std::move(settings));
0496     job->start();
0497 }
0498 
0499 void KMKernel::openComposer(const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, bool hidden)
0500 {
0501     const OpenComposerHiddenJobSettings settings(to, cc, bcc, subject, body, hidden);
0502     auto job = new OpenComposerHiddenJob(this);
0503     job->setSettings(settings);
0504     job->start();
0505 }
0506 
0507 void KMKernel::newMessage(const QString &to,
0508                           const QString &cc,
0509                           const QString &bcc,
0510                           bool hidden,
0511                           bool useFolderId,
0512                           const QString & /*messageFile*/,
0513                           const QString &_attachURL)
0514 {
0515     QSharedPointer<FolderSettings> folder;
0516     Akonadi::Collection col;
0517     uint id = 0;
0518     if (useFolderId) {
0519         // create message with required folder identity
0520         folder = currentFolderCollection();
0521         id = folder ? folder->identity() : 0;
0522         col = currentCollection();
0523     }
0524 
0525     const NewMessageJobSettings settings(to, cc, bcc, hidden, _attachURL, folder, id, col);
0526 
0527     auto job = new NewMessageJob(this);
0528     job->setNewMessageJobSettings(settings);
0529     job->start();
0530 }
0531 
0532 void KMKernel::viewMessage(const QUrl &url)
0533 {
0534     auto openCommand = new KMOpenMsgCommand(nullptr, url);
0535 
0536     openCommand->start();
0537 }
0538 
0539 int KMKernel::viewMessage(const QString &messageFile)
0540 {
0541     auto openCommand = new KMOpenMsgCommand(nullptr, QUrl::fromLocalFile(messageFile));
0542 
0543     openCommand->start();
0544     return 1;
0545 }
0546 
0547 void KMKernel::raise()
0548 {
0549     QDBusInterface iface(QStringLiteral("org.kde.kmail"),
0550                          QStringLiteral("/MainApplication"),
0551                          QStringLiteral("org.kde.PIMUniqueApplication"),
0552                          QDBusConnection::sessionBus());
0553     QDBusReply<int> reply;
0554     if (!iface.isValid() || !(reply = iface.call(QStringLiteral("newInstance"))).isValid()) {
0555         QDBusError err = iface.lastError();
0556         qCritical() << "Communication problem with KMail. "
0557                     << "Error message was:" << err.name() << ": \"" << err.message() << "\"";
0558     }
0559 }
0560 
0561 bool KMKernel::showMail(qint64 serialNumber)
0562 {
0563     KMMainWidget *mainWidget = nullptr;
0564 
0565     // First look for a KMainWindow.
0566     const auto lst = KMainWindow::memberList();
0567     for (KMainWindow *window : lst) {
0568         // Then look for a KMMainWidget.
0569         QList<KMMainWidget *> l = window->findChildren<KMMainWidget *>();
0570         if (!l.isEmpty() && l.first()) {
0571             mainWidget = l.first();
0572             if (window->isActiveWindow()) {
0573                 break;
0574             }
0575         }
0576     }
0577     if (mainWidget) {
0578         auto job = new Akonadi::ItemFetchJob(Akonadi::Item(serialNumber), this);
0579         job->fetchScope().fetchFullPayload();
0580         job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
0581         if (job->exec()) {
0582             if (job->items().count() >= 1) {
0583                 auto win = new KMReaderMainWin(MessageViewer::Viewer::UseGlobalSetting, false);
0584                 const auto item = job->items().at(0);
0585                 win->showMessage(MessageCore::MessageCoreSettings::self()->overrideCharacterEncoding(), item, item.parentCollection());
0586                 win->showAndActivateWindow();
0587                 return true;
0588             }
0589         }
0590     }
0591     return false;
0592 }
0593 
0594 void KMKernel::pauseBackgroundJobs()
0595 {
0596     mBackgroundTasksTimer->stop();
0597     mJobScheduler->pause();
0598 }
0599 
0600 void KMKernel::resumeBackgroundJobs()
0601 {
0602     mJobScheduler->resume();
0603     mBackgroundTasksTimer->start(4h);
0604 }
0605 
0606 void KMKernel::stopNetworkJobs()
0607 {
0608     if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Offline) {
0609         return;
0610     }
0611 
0612     setAccountStatus(false);
0613 
0614     KMailSettings::setNetworkState(KMailSettings::EnumNetworkState::Offline);
0615     BroadcastStatus::instance()->setStatusMsg(i18n("KMail is set to be offline; all network jobs are suspended"));
0616     Q_EMIT onlineStatusChanged((KMailSettings::EnumNetworkState::type)KMailSettings::networkState());
0617 }
0618 
0619 void KMKernel::setAccountStatus(bool goOnline)
0620 {
0621     const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances(false);
0622     for (Akonadi::AgentInstance type : lst) {
0623         const QString identifier(type.identifier());
0624         if (PimCommon::Util::isImapResource(identifier) || identifier.contains(POP3_RESOURCE_IDENTIFIER)
0625             || identifier.contains(QLatin1StringView("akonadi_maildispatcher_agent"))
0626             || type.type().capabilities().contains(QLatin1StringView("NeedsNetwork"))) {
0627             type.setIsOnline(goOnline);
0628         }
0629     }
0630     if (goOnline && MessageComposer::MessageComposerSettings::self()->sendImmediate()) {
0631         const auto col = CommonKernel->collectionFromId(CommonKernel->outboxCollectionFolder().id());
0632         const qint64 nbMsgOutboxCollection = col.statistics().count();
0633         if (nbMsgOutboxCollection > 0) {
0634             if (!kmkernel->msgSender()->sendQueued()) {
0635                 KNotification::event(QStringLiteral("sent-mail-error"),
0636                                      i18n("Send Email"),
0637                                      i18n("Impossible to send email"),
0638                                      QStringLiteral("kmail"),
0639                                      KNotification::CloseOnTimeout);
0640             }
0641         }
0642     }
0643 }
0644 
0645 const QString KMKernel::xmlGuiInstanceName() const
0646 {
0647     return mXmlGuiInstance;
0648 }
0649 
0650 void KMKernel::setXmlGuiInstanceName(const QString &instance)
0651 {
0652     mXmlGuiInstance = instance;
0653 }
0654 
0655 KMail::UndoStack *KMKernel::undoStack() const
0656 {
0657     return the_undoStack;
0658 }
0659 
0660 void KMKernel::resumeNetworkJobs()
0661 {
0662     if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Online) {
0663         return;
0664     }
0665 
0666     if (mSystemNetworkStatus) {
0667         setAccountStatus(true);
0668         BroadcastStatus::instance()->setStatusMsg(i18n("KMail is set to be online; all network jobs resumed"));
0669     } else {
0670         BroadcastStatus::instance()->setStatusMsg(i18n("KMail is set to be online; all network jobs will resume when a network connection is detected"));
0671     }
0672     KMailSettings::setNetworkState(KMailSettings::EnumNetworkState::Online);
0673     Q_EMIT onlineStatusChanged((KMailSettings::EnumNetworkState::type)KMailSettings::networkState());
0674     KMMainWidget *widget = getKMMainWidget();
0675     if (widget) {
0676         widget->refreshMessageListSelection();
0677     }
0678 }
0679 
0680 bool KMKernel::isOffline()
0681 {
0682     if ((KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Offline) || !PimCommon::NetworkManager::self()->isOnline()) {
0683         return true;
0684     } else {
0685         return false;
0686     }
0687 }
0688 
0689 void KMKernel::verifyAccount()
0690 {
0691     const QString resourceGroupPattern(QStringLiteral("Resource %1"));
0692 
0693     const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances();
0694     for (Akonadi::AgentInstance type : lst) {
0695         KConfigGroup group(KMKernel::config(), resourceGroupPattern.arg(type.identifier()));
0696         if (group.readEntry("CheckOnStartup", false)) {
0697             if (!type.isOnline()) {
0698                 type.setIsOnline(true);
0699             }
0700             type.synchronize();
0701         }
0702 
0703         // "false" is also hardcoded in ConfigureDialog, don't forget to change there.
0704         if (group.readEntry("OfflineOnShutdown", false)) {
0705             if (!type.isOnline()) {
0706                 type.setIsOnline(true);
0707             }
0708         }
0709     }
0710 }
0711 
0712 void KMKernel::slotCheckAccount(Akonadi::ServerManager::State state)
0713 {
0714     if (state == Akonadi::ServerManager::Running) {
0715         disconnect(Akonadi::ServerManager::self(), SIGNAL(stateChanged(Akonadi::ServerManager::State)));
0716         verifyAccount();
0717     }
0718 }
0719 
0720 void KMKernel::checkMailOnStartup()
0721 {
0722     if (!kmkernel->askToGoOnline()) {
0723         return;
0724     }
0725 
0726     if (Akonadi::ServerManager::state() != Akonadi::ServerManager::Running) {
0727         connect(Akonadi::ServerManager::self(), &Akonadi::ServerManager::stateChanged, this, &KMKernel::slotCheckAccount);
0728     } else {
0729         verifyAccount();
0730     }
0731 }
0732 
0733 bool KMKernel::askToGoOnline()
0734 {
0735     // already asking means we are offline and need to wait anyhow
0736     if (s_askingToGoOnline) {
0737         return false;
0738     }
0739 
0740     if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Offline) {
0741         s_askingToGoOnline = true;
0742         int rc = KMessageBox::questionTwoActions(KMKernel::self()->mainWin(),
0743                                                  i18n("KMail is currently in offline mode. "
0744                                                       "How do you want to proceed?"),
0745                                                  i18nc("@title:window", "Online/Offline"),
0746                                                  KGuiItem(i18n("Work Online")),
0747                                                  KGuiItem(i18n("Work Offline")));
0748 
0749         s_askingToGoOnline = false;
0750         if (rc == KMessageBox::ButtonCode::SecondaryAction) {
0751             return false;
0752         } else {
0753             kmkernel->resumeNetworkJobs();
0754         }
0755     }
0756     if (kmkernel->isOffline()) {
0757         return false;
0758     }
0759 
0760     return true;
0761 }
0762 
0763 void KMKernel::slotSystemNetworkStatusChanged(bool isOnline)
0764 {
0765     mSystemNetworkStatus = isOnline;
0766     if (KMailSettings::self()->networkState() == KMailSettings::EnumNetworkState::Offline) {
0767         return;
0768     }
0769 
0770     if (isOnline) {
0771         BroadcastStatus::instance()->setStatusMsg(i18n("Network connection detected, all network jobs resumed"));
0772         kmkernel->setAccountStatus(true);
0773     } else {
0774         BroadcastStatus::instance()->setStatusMsg(i18n("No network connection detected, all network jobs are suspended"));
0775         kmkernel->setAccountStatus(false);
0776     }
0777 }
0778 
0779 /********************************************************************/
0780 /*                        Kernel methods                            */
0781 /********************************************************************/
0782 
0783 void KMKernel::quit()
0784 {
0785     // Called when all windows are closed. Will take care of compacting,
0786     // sending... should handle session management too!!
0787 }
0788 
0789 /* TODO later:
0790    Assuming that:
0791      - msgsender is nonblocking
0792        (our own, QSocketNotifier based. Pops up errors and sends signal
0793         senderFinished when done)
0794 
0795    o If we are getting mail, stop it (but don't lose something!)
0796          [Done already, see mailCheckAborted]
0797    o If we are sending mail, go on UNLESS this was called by SM,
0798        in which case stop ASAP that too (can we warn? should we continue
0799        on next start?)
0800    o If we are compacting, or expunging, go on UNLESS this was SM call.
0801        In that case stop compacting ASAP and continue on next start, before
0802        touching any folders. [Not needed anymore with CompactionJob]
0803 
0804    KMKernel::quit ()
0805    {
0806      SM call?
0807        if compacting, stop;
0808        if sending, stop;
0809        if receiving, stop;
0810        Windows will take care of themselves (composer should dump
0811         its messages, if any but not in deadMail)
0812        declare us ready for the End of the Session
0813 
0814      No, normal quit call
0815        All windows are off. Anything to do, should compact or sender sends?
0816          Yes, maybe put an icon in panel as a sign of life
0817          if sender sending, connect us to his finished slot, declare us ready
0818                             for quit and wait for senderFinished
0819          if not, Folder manager, go compact sent-mail and outbox
0820 }                (= call slotFinished())
0821 
0822 void KMKernel::slotSenderFinished()
0823 {
0824   good, Folder manager go compact sent-mail and outbox
0825   clean up stage1 (release folders and config, unregister from dcop)
0826     -- another kmail may start now ---
0827   qApp->quit();
0828 }
0829 */
0830 
0831 /********************************************************************/
0832 /*            Init, Exit, and handler  methods                      */
0833 /********************************************************************/
0834 
0835 //-----------------------------------------------------------------------------
0836 // Open a composer for each message found in the dead.letter folder
0837 void KMKernel::recoverDeadLetters()
0838 {
0839     const QString pathName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1StringView("/kmail2/");
0840     QDir dir(pathName);
0841     if (!dir.exists(QStringLiteral("autosave"))) {
0842         return;
0843     }
0844 
0845     dir.cd(pathName + QLatin1StringView("autosave"));
0846     const QFileInfoList autoSaveFiles = dir.entryInfoList();
0847     for (const QFileInfo &file : autoSaveFiles) {
0848         // Disregard the '.' and '..' folders
0849         const QString filename = file.fileName();
0850         if (filename == QLatin1Char('.') || filename == QLatin1StringView("..") || file.isDir()) {
0851             continue;
0852         }
0853         qCDebug(KMAIL_LOG) << "Opening autosave file:" << file.absoluteFilePath();
0854         QFile autoSaveFile(file.absoluteFilePath());
0855         if (autoSaveFile.open(QIODevice::ReadOnly)) {
0856             const KMime::Message::Ptr autoSaveMessage(new KMime::Message());
0857             const QByteArray msgData = autoSaveFile.readAll();
0858             autoSaveMessage->setContent(msgData);
0859             autoSaveMessage->parse();
0860 
0861             // Show the a new composer dialog for the message
0862             KMail::Composer *autoSaveWin = KMail::makeComposer();
0863             autoSaveWin->setMessage(autoSaveMessage, false, false, false);
0864             autoSaveWin->setAutoSaveFileName(filename);
0865             autoSaveWin->show();
0866             autoSaveFile.close();
0867         } else {
0868             KMessageBox::error(nullptr,
0869                                i18n("Failed to open autosave file at %1.\nReason: %2", file.absoluteFilePath(), autoSaveFile.errorString()),
0870                                i18n("Opening Autosave File Failed"));
0871         }
0872     }
0873 }
0874 
0875 void KMKernel::akonadiStateChanged(Akonadi::ServerManager::State state)
0876 {
0877     qCDebug(KMAIL_LOG) << "KMKernel has akonadi state changed to:" << int(state);
0878 
0879     if (state == Akonadi::ServerManager::Running) {
0880         CommonKernel->initFolders();
0881     }
0882 }
0883 
0884 static void kmCrashHandler(int sigId)
0885 {
0886     fprintf(stderr, "*** KMail got signal %d (Exiting)\n", sigId);
0887     // try to cleanup all windows
0888     if (kmkernel) {
0889         kmkernel->dumpDeadLetters();
0890         fprintf(stderr, "*** Dead letters dumped.\n");
0891         kmkernel->stopAgentInstance();
0892         kmkernel->cleanupTemporaryFiles();
0893     }
0894 }
0895 
0896 namespace
0897 {
0898 auto initKeyCache()
0899 {
0900     using namespace Kleo;
0901 
0902     // set up automatic update of the key cache on changes in the key ring
0903     const auto keyCache = KeyCache::mutableInstance();
0904     auto watcher = std::make_shared<FileSystemWatcher>();
0905     watcher->whitelistFiles(gnupgFileWhitelist());
0906     watcher->addPath(gnupgHomeDirectory());
0907     watcher->setDelay(1000);
0908     keyCache->addFileSystemWatcher(watcher);
0909 
0910     return KeyCache::instance();
0911 }
0912 }
0913 
0914 void KMKernel::init()
0915 {
0916     the_shuttingDown = false;
0917 
0918     the_firstStart = KMailSettings::self()->firstStart();
0919     KMailSettings::self()->setFirstStart(false);
0920 
0921     // keep a reference on the key cache to avoid expensive reinitialization on each use
0922     mKeyCache = initKeyCache();
0923 
0924     the_undoStack = new KMail::UndoStack(20);
0925 
0926     the_msgSender = new MessageComposer::AkonadiSender;
0927     // filterMgr->dump();
0928 
0929     mBackgroundTasksTimer = new QTimer(this);
0930     mBackgroundTasksTimer->setSingleShot(true);
0931     connect(mBackgroundTasksTimer, &QTimer::timeout, this, &KMKernel::slotRunBackgroundTasks);
0932 #ifdef DEBUG_SCHEDULER // for debugging, see jobscheduler.h
0933     mBackgroundTasksTimer->start(10s); // 10s, singleshot
0934 #else
0935     mBackgroundTasksTimer->start(5min); // 5 minutes, singleshot
0936 #endif
0937 
0938     KCrash::setEmergencySaveFunction(kmCrashHandler);
0939 
0940     qCDebug(KMAIL_LOG) << "KMail init with akonadi server state:" << int(Akonadi::ServerManager::state());
0941     if (Akonadi::ServerManager::state() == Akonadi::ServerManager::Running) {
0942         CommonKernel->initFolders();
0943     }
0944 
0945     connect(Akonadi::ServerManager::self(), &Akonadi::ServerManager::stateChanged, this, &KMKernel::akonadiStateChanged);
0946 }
0947 
0948 void KMKernel::doSessionManagement()
0949 {
0950     // Do session management
0951     if (qApp->isSessionRestored()) {
0952         int n = 1;
0953         while (KMMainWin::canBeRestored(n)) {
0954             // only restore main windows! (Matthias);
0955             if (KMMainWin::classNameOfToplevel(n) == QLatin1StringView("KMMainWin")) {
0956                 (new KMMainWin)->restoreDockedState(n);
0957             }
0958             ++n;
0959         }
0960     }
0961 }
0962 
0963 bool KMKernel::firstInstance() const
0964 {
0965     return the_firstInstance;
0966 }
0967 
0968 void KMKernel::setFirstInstance(bool value)
0969 {
0970     the_firstInstance = value;
0971 }
0972 
0973 void KMKernel::closeAllKMailWindows()
0974 {
0975     const auto lst = KMainWindow::memberList();
0976     for (KMainWindow *window : lst) {
0977         if (::qobject_cast<KMMainWin *>(window) || ::qobject_cast<KMail::SecondaryWindow *>(window)) {
0978             // close and delete the window
0979             window->setAttribute(Qt::WA_DeleteOnClose);
0980             window->close();
0981         }
0982     }
0983 }
0984 
0985 void KMKernel::cleanup()
0986 {
0987     disconnect(Akonadi::AgentManager::self(), SIGNAL(instanceStatusChanged(Akonadi::AgentInstance)));
0988     // clang-format off
0989     disconnect(Akonadi::AgentManager::self(), SIGNAL(instanceError(Akonadi::AgentInstance,QString)));
0990     disconnect(Akonadi::AgentManager::self(), SIGNAL(instanceWarning(Akonadi::AgentInstance,QString)));
0991     // clang-format on
0992     disconnect(Akonadi::AgentManager::self(), SIGNAL(instanceRemoved(Akonadi::AgentInstance)));
0993     // clang-format off
0994     disconnect(KPIM::ProgressManager::instance(), SIGNAL(progressItemCompleted(KPIM::ProgressItem*)));
0995     disconnect(KPIM::ProgressManager::instance(), SIGNAL(progressItemCanceled(KPIM::ProgressItem*)));
0996     // clang-format on
0997 
0998     dumpDeadLetters();
0999     the_shuttingDown = true;
1000     closeAllKMailWindows();
1001 
1002     // Flush the cache of foldercollection objects. This results
1003     // in configuration writes, so we need to do it early enough.
1004     MailCommon::FolderSettings::clearCache();
1005 
1006     // Write the config while all other managers are alive
1007     delete the_msgSender;
1008     the_msgSender = nullptr;
1009     delete the_undoStack;
1010     the_undoStack = nullptr;
1011     delete mConfigureDialog;
1012     mConfigureDialog = nullptr;
1013 
1014     KSharedConfig::Ptr config = KMKernel::config();
1015     if (RecentAddresses::exists()) {
1016         RecentAddresses::self(config.data())->save(config.data());
1017     }
1018 
1019     Akonadi::Collection trashCollection = CommonKernel->trashCollectionFolder();
1020     if (trashCollection.isValid()) {
1021         if (KMailSettings::self()->emptyTrashOnExit()) {
1022             const auto service = Akonadi::ServerManager::self()->agentServiceName(Akonadi::ServerManager::Agent, QStringLiteral("akonadi_mailfilter_agent"));
1023             OrgFreedesktopAkonadiMailFilterAgentInterface mailFilterInterface(service, QStringLiteral("/MailFilterAgent"), QDBusConnection::sessionBus(), this);
1024             if (mailFilterInterface.isValid()) {
1025                 mailFilterInterface.expunge(static_cast<qlonglong>(trashCollection.id()));
1026             } else {
1027                 qCWarning(KMAIL_LOG) << "Mailfilter is not active";
1028             }
1029         }
1030     }
1031 }
1032 
1033 void KMKernel::dumpDeadLetters()
1034 {
1035     if (shuttingDown()) {
1036         return; // All documents should be saved before shutting down is set!
1037     }
1038 
1039     // make all composer windows autosave their contents
1040     const auto lst = KMainWindow::memberList();
1041     for (KMainWindow *window : lst) {
1042         if (auto win = ::qobject_cast<KMail::Composer *>(window)) {
1043             win->autoSaveMessage(true);
1044 
1045             while (win->isComposing()) {
1046                 qCWarning(KMAIL_LOG) << "Danger, using an event loop, this should no longer be happening!";
1047                 qApp->processEvents();
1048             }
1049         }
1050     }
1051 }
1052 
1053 void KMKernel::action(bool mailto,
1054                       bool check,
1055                       bool startInTray,
1056                       const QString &to,
1057                       const QString &cc,
1058                       const QString &bcc,
1059                       const QString &subj,
1060                       const QString &body,
1061                       const QUrl &messageFile,
1062                       const QList<QUrl> &attachURLs,
1063                       const QStringList &customHeaders,
1064                       const QString &replyTo,
1065                       const QString &inReplyTo,
1066                       const QString &identity)
1067 {
1068     if (mailto) {
1069         openComposer(to, cc, bcc, subj, body, false, messageFile.toLocalFile(), QUrl::toStringList(attachURLs), customHeaders, replyTo, inReplyTo, identity);
1070     } else {
1071         openReader(check, startInTray);
1072     }
1073 
1074     if (check) {
1075         checkMail();
1076     }
1077     // Anything else?
1078 }
1079 
1080 void KMKernel::slotRequestConfigSync()
1081 {
1082     // ### FIXME: delay as promised in the kdoc of this function ;-)
1083     slotSyncConfig();
1084 }
1085 
1086 void KMKernel::slotSyncConfig()
1087 {
1088     saveConfig();
1089     // Laurent investigate why we need to reload them.
1090     TextAutoCorrectionCore::TextAutoCorrectionSettings::self()->load();
1091     MessageCore::MessageCoreSettings::self()->load();
1092     MessageViewer::MessageViewerSettings::self()->load();
1093     MessageComposer::MessageComposerSettings::self()->load();
1094     TemplateParser::TemplateParserSettings::self()->load();
1095     MessageList::MessageListSettings::self()->load();
1096     mMailCommonSettings->load();
1097     Gravatar::GravatarSettings::self()->load();
1098     KMailSettings::self()->load();
1099     KMKernel::config()->reparseConfiguration();
1100     mUnityServiceManager->updateCount();
1101 }
1102 
1103 void KMKernel::saveConfig()
1104 {
1105     TextAutoCorrectionCore::TextAutoCorrectionSettings::self()->save();
1106     MessageCore::MessageCoreSettings::self()->save();
1107     MessageViewer::MessageViewerSettings::self()->save();
1108     MessageComposer::MessageComposerSettings::self()->save();
1109     TemplateParser::TemplateParserSettings::self()->save();
1110     MessageList::MessageListSettings::self()->save();
1111     mMailCommonSettings->save();
1112     Gravatar::GravatarSettings::self()->save();
1113     KMailSettings::self()->save();
1114 }
1115 
1116 void KMKernel::updateConfig()
1117 {
1118     slotConfigChanged();
1119 }
1120 
1121 void KMKernel::slotShowConfigurationDialog()
1122 {
1123     if (KMKernel::getKMMainWidget() == nullptr) {
1124         // ensure that there is a main widget available
1125         // as parts of the configure dialog (identity) rely on this
1126         // and this slot can be called when there is only a KMComposeWin showing
1127         auto win = new KMMainWin;
1128         win->show();
1129     }
1130 
1131     if (!mConfigureDialog) {
1132         mConfigureDialog = new ConfigureDialog(nullptr, false);
1133         mConfigureDialog->setObjectName(QLatin1StringView("configure"));
1134         connect(mConfigureDialog, &ConfigureDialog::configChanged, this, &KMKernel::slotConfigChanged);
1135     }
1136 
1137     // Save all current settings.
1138     if (getKMMainWidget()) {
1139         getKMMainWidget()->writeReaderConfig();
1140     }
1141 
1142     if (mConfigureDialog->isHidden()) {
1143         mConfigureDialog->show();
1144     }
1145     mConfigureDialog->raise();
1146     mConfigureDialog->activateWindow();
1147 }
1148 
1149 void KMKernel::slotConfigChanged()
1150 {
1151     Q_EMIT configChanged();
1152 }
1153 
1154 //-------------------------------------------------------------------------------
1155 
1156 bool KMKernel::haveSystemTrayApplet() const
1157 {
1158     return mUnityServiceManager->haveSystemTrayApplet();
1159 }
1160 
1161 void KMKernel::setSystemTryAssociatedWindow(QWindow *window)
1162 {
1163     mUnityServiceManager->setSystemTryAssociatedWindow(window);
1164 }
1165 
1166 void KMKernel::updateSystemTray()
1167 {
1168     if (!the_shuttingDown) {
1169         mUnityServiceManager->initListOfCollection();
1170     }
1171 }
1172 
1173 KIdentityManagementCore::IdentityManager *KMKernel::identityManager()
1174 {
1175     return KIdentityManagementCore::IdentityManager::self();
1176 }
1177 
1178 JobScheduler *KMKernel::jobScheduler() const
1179 {
1180     return mJobScheduler;
1181 }
1182 
1183 KMainWindow *KMKernel::mainWin()
1184 {
1185     // First look for a KMMainWin.
1186     const auto lst = KMainWindow::memberList();
1187     for (KMainWindow *window : lst) {
1188         if (::qobject_cast<KMMainWin *>(window)) {
1189             return window;
1190         }
1191     }
1192 
1193     // There is no KMMainWin. Use any other KMainWindow instead (e.g. in
1194     // case we are running inside Kontact) because we anyway only need
1195     // it for modal message boxes and for KNotify events.
1196     if (!KMainWindow::memberList().isEmpty()) {
1197         KMainWindow *kmWin = KMainWindow::memberList().constFirst();
1198         if (kmWin) {
1199             return kmWin;
1200         }
1201     }
1202 
1203     // There's not a single KMainWindow. Create a KMMainWin.
1204     // This could happen if we want to pop up an error message
1205     // while we are still doing the startup wizard and no other
1206     // KMainWindow is running.
1207     return new KMMainWin;
1208 }
1209 
1210 KMKernel *KMKernel::self()
1211 {
1212     return mySelf;
1213 }
1214 
1215 KSharedConfig::Ptr KMKernel::config()
1216 {
1217     assert(mySelf);
1218     if (!mySelf->mConfig) {
1219         mySelf->mConfig = KSharedConfig::openConfig(QStringLiteral("kmail2rc"));
1220         // Check that all updates have been run on the config file:
1221         MessageList::MessageListSettings::self()->setSharedConfig(mySelf->mConfig);
1222         MessageList::MessageListSettings::self()->load();
1223         TemplateParser::TemplateParserSettings::self()->setSharedConfig(mySelf->mConfig);
1224         TemplateParser::TemplateParserSettings::self()->load();
1225         MessageComposer::MessageComposerSettings::self()->setSharedConfig(mySelf->mConfig);
1226         MessageComposer::MessageComposerSettings::self()->load();
1227         MessageCore::MessageCoreSettings::self()->setSharedConfig(mySelf->mConfig);
1228         MessageCore::MessageCoreSettings::self()->load();
1229         MessageViewer::MessageViewerSettings::self()->setSharedConfig(mySelf->mConfig);
1230         MessageViewer::MessageViewerSettings::self()->load();
1231         mMailCommonSettings = new MailCommon::MailCommonSettings;
1232 
1233         mMailCommonSettings->setSharedConfig(mySelf->mConfig);
1234         mMailCommonSettings->load();
1235 
1236         TextAutoCorrectionCore::TextAutoCorrectionSettings::self()->setSharedConfig(mySelf->mConfig);
1237         TextAutoCorrectionCore::TextAutoCorrectionSettings::self()->load();
1238         Gravatar::GravatarSettings::self()->setSharedConfig(mySelf->mConfig);
1239         Gravatar::GravatarSettings::self()->load();
1240     }
1241     return mySelf->mConfig;
1242 }
1243 
1244 void KMKernel::syncConfig()
1245 {
1246     slotRequestConfigSync();
1247 }
1248 
1249 void KMKernel::selectCollectionFromId(Akonadi::Collection::Id id)
1250 {
1251     KMMainWidget *widget = getKMMainWidget();
1252     Q_ASSERT(widget);
1253     if (!widget) {
1254         return;
1255     }
1256 
1257     Akonadi::Collection colFolder = CommonKernel->collectionFromId(id);
1258 
1259     if (colFolder.isValid()) {
1260         widget->slotSelectCollectionFolder(colFolder);
1261     }
1262 }
1263 
1264 bool KMKernel::selectFolder(const QString &folder)
1265 {
1266     KMMainWidget *widget = getKMMainWidget();
1267     Q_ASSERT(widget);
1268     if (!widget) {
1269         return false;
1270     }
1271 
1272     const Akonadi::Collection colFolder = CommonKernel->collectionFromId(folder.toLongLong());
1273 
1274     if (colFolder.isValid()) {
1275         widget->slotSelectCollectionFolder(colFolder);
1276         return true;
1277     }
1278     return false;
1279 }
1280 
1281 KMMainWidget *KMKernel::getKMMainWidget() const
1282 {
1283     // This could definitely use a speadup
1284     const QWidgetList l = QApplication::topLevelWidgets();
1285 
1286     for (QWidget *wid : l) {
1287         QList<KMMainWidget *> l2 = wid->window()->findChildren<KMMainWidget *>();
1288         if (!l2.isEmpty() && l2.first()) {
1289             return l2.first();
1290         }
1291     }
1292     return nullptr;
1293 }
1294 
1295 void KMKernel::slotRunBackgroundTasks() // called regularly by timer
1296 {
1297     // Hidden KConfig keys. Not meant to be used, but a nice fallback in case
1298     // a stable kmail release goes out with a nasty bug in CompactionJob...
1299     if (KMailSettings::self()->autoExpiring()) {
1300         mFolderCollectionMonitor->expireAllFolders(false /*scheduled, not immediate*/, entityTreeModel());
1301     }
1302     if (KMailSettings::self()->checkCollectionsIndexing()) {
1303         mCheckIndexingManager->start(entityTreeModel());
1304     }
1305 #ifdef DEBUG_SCHEDULER // for debugging, see jobscheduler.h
1306     mBackgroundTasksTimer->start(1m); // check again in 1 minute
1307 #else
1308     mBackgroundTasksTimer->start(4h); // check again in 4 hours
1309 #endif
1310 }
1311 
1312 static Akonadi::Collection::List collect_collections(const QAbstractItemModel *model, const QModelIndex &parent)
1313 {
1314     Akonadi::Collection::List collections;
1315     QStack<QModelIndex> stack;
1316     stack.push(parent);
1317     while (!stack.isEmpty()) {
1318         const QModelIndex idx = stack.pop();
1319         if (idx.isValid()) {
1320             collections << model->data(idx, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
1321             for (int i = model->rowCount(idx) - 1; i >= 0; --i) {
1322                 stack.push(model->index(i, 0, idx));
1323             }
1324         }
1325     }
1326 
1327     return collections;
1328 }
1329 
1330 Akonadi::Collection::List KMKernel::allFolders() const
1331 {
1332     return collect_collections(collectionModel(), QModelIndex());
1333 }
1334 
1335 Akonadi::Collection::List KMKernel::subfolders(const Akonadi::Collection &col) const
1336 {
1337     const auto idx = collectionModel()->match({}, Akonadi::EntityTreeModel::CollectionRole, QVariant::fromValue(col), 1, Qt::MatchExactly);
1338     if (!idx.isEmpty()) {
1339         return collect_collections(collectionModel(), idx[0]);
1340     }
1341 
1342     return {};
1343 }
1344 
1345 void KMKernel::expireAllFoldersNow() // called by the GUI
1346 {
1347     mFolderCollectionMonitor->expireAllFolders(true /*immediate*/, entityTreeModel());
1348 }
1349 
1350 bool KMKernel::canQueryClose()
1351 {
1352     if (!KMMainWidget::mainWidgetList()) {
1353         return true;
1354     }
1355     return mUnityServiceManager->canQueryClose();
1356 }
1357 
1358 Akonadi::Collection KMKernel::currentCollection() const
1359 {
1360     KMMainWidget *widget = getKMMainWidget();
1361     Akonadi::Collection col;
1362     if (widget) {
1363         col = widget->currentCollection();
1364     }
1365     return col;
1366 }
1367 
1368 QSharedPointer<FolderSettings> KMKernel::currentFolderCollection()
1369 {
1370     KMMainWidget *widget = getKMMainWidget();
1371     QSharedPointer<FolderSettings> folder;
1372     if (widget) {
1373         folder = widget->currentFolder();
1374     }
1375     return folder;
1376 }
1377 
1378 MailCommon::MailCommonSettings *KMKernel::mailCommonSettings() const
1379 {
1380     return mMailCommonSettings;
1381 }
1382 
1383 Akonadi::Search::PIM::IndexedItems *KMKernel::indexedItems() const
1384 {
1385     return mIndexedItems;
1386 }
1387 
1388 // can't be inline, since KMSender isn't known to implement
1389 // KMail::MessageSender outside this .cpp file
1390 MessageComposer::MessageSender *KMKernel::msgSender()
1391 {
1392     return the_msgSender;
1393 }
1394 
1395 void KMKernel::transportRemoved(int id, const QString &name)
1396 {
1397     Q_UNUSED(id)
1398 
1399     // reset all identities using the deleted transport
1400     QStringList changedIdents;
1401     KIdentityManagementCore::IdentityManager *im = identityManager();
1402     KIdentityManagementCore::IdentityManager::Iterator end = im->modifyEnd();
1403     for (KIdentityManagementCore::IdentityManager::Iterator it = im->modifyBegin(); it != end; ++it) {
1404         if (name == (*it).transport()) {
1405             (*it).setTransport(QString());
1406             changedIdents += (*it).identityName();
1407         }
1408     }
1409 
1410     // if the deleted transport is the currently used transport reset it to default
1411     const QString &currentTransport = KMailSettings::self()->currentTransport();
1412     if (name == currentTransport) {
1413         KMailSettings::self()->setCurrentTransport(QString());
1414     }
1415 
1416     if (!changedIdents.isEmpty()) {
1417         QString information = i18np("This identity has been changed to use the default transport:",
1418                                     "These %1 identities have been changed to use the default transport:",
1419                                     changedIdents.count());
1420         // Don't set parent otherwise we will switch to current KMail and we configure it. So not good
1421         KMessageBox::informationList(nullptr, information, changedIdents);
1422         im->commit();
1423     }
1424 }
1425 
1426 void KMKernel::transportRenamed(int id, const QString &oldName, const QString &newName)
1427 {
1428     Q_UNUSED(id)
1429 
1430     QStringList changedIdents;
1431     KIdentityManagementCore::IdentityManager *im = identityManager();
1432     KIdentityManagementCore::IdentityManager::Iterator end = im->modifyEnd();
1433     for (KIdentityManagementCore::IdentityManager::Iterator it = im->modifyBegin(); it != end; ++it) {
1434         if (oldName == (*it).transport()) {
1435             (*it).setTransport(newName);
1436             changedIdents << (*it).identityName();
1437         }
1438     }
1439 
1440     if (!changedIdents.isEmpty()) {
1441         const QString information = i18np("This identity has been changed to use the modified transport:",
1442                                           "These %1 identities have been changed to use the modified transport:",
1443                                           changedIdents.count());
1444         // Don't set parent otherwise we will swith to current KMail and we configure it. So not good
1445         KMessageBox::informationList(nullptr, information, changedIdents);
1446         im->commit();
1447     }
1448 }
1449 
1450 void KMKernel::itemDispatchStarted()
1451 {
1452     // Watch progress of the MDA.
1453     PimCommon::ProgressManagerAkonadi::createProgressItem(nullptr,
1454                                                           Akonadi::DispatcherInterface().dispatcherInstance(),
1455                                                           QStringLiteral("Sender"),
1456                                                           i18n("Sending messages"),
1457                                                           i18n("Initiating sending process..."),
1458                                                           true,
1459                                                           KPIM::ProgressItem::Unknown);
1460 }
1461 
1462 void KMKernel::instanceStatusChanged(const Akonadi::AgentInstance &instance)
1463 {
1464     if (instance.identifier() == QLatin1StringView("akonadi_mailfilter_agent")) {
1465         // Creating a progress item twice is ok, it will simply return the already existing
1466         // item
1467         KPIM::ProgressItem *progress = PimCommon::ProgressManagerAkonadi::createProgressItem(nullptr,
1468                                                                                              instance,
1469                                                                                              instance.identifier(),
1470                                                                                              instance.name(),
1471                                                                                              instance.statusMessage(),
1472                                                                                              false,
1473                                                                                              KPIM::ProgressItem::Encrypted);
1474         progress->setProperty("AgentIdentifier", instance.identifier());
1475         return;
1476     }
1477     if (MailCommon::Util::agentInstances(true).contains(instance)) {
1478         if (instance.status() == Akonadi::AgentInstance::Running) {
1479             if (mResourcesBeingChecked.isEmpty()) {
1480                 qCDebug(KMAIL_LOG) << "A Resource started to synchronize, starting a mail check.";
1481                 Q_EMIT startCheckMail();
1482             }
1483 
1484             const QString identifier(instance.identifier());
1485             if (!mResourcesBeingChecked.contains(identifier)) {
1486                 mResourcesBeingChecked.append(identifier);
1487             }
1488 
1489             KPIM::ProgressItem::CryptoStatus cryptoStatus = KPIM::ProgressItem::Unencrypted;
1490             if (mResourceCryptoSettingCache.contains(identifier)) {
1491                 cryptoStatus = mResourceCryptoSettingCache.value(identifier);
1492             } else {
1493                 if (PimCommon::Util::isImapResource(identifier)) {
1494                     MailCommon::ResourceReadConfigFile resourceFile(identifier);
1495                     const KConfigGroup grp = resourceFile.group(QStringLiteral("network"));
1496                     if (grp.isValid()) {
1497                         const QString imapSafety = grp.readEntry(QStringLiteral("Safety"));
1498                         if (imapSafety == QLatin1StringView("None")) {
1499                             cryptoStatus = KPIM::ProgressItem::Unencrypted;
1500                         } else {
1501                             cryptoStatus = KPIM::ProgressItem::Encrypted;
1502                         }
1503 
1504                         mResourceCryptoSettingCache.insert(identifier, cryptoStatus);
1505                     }
1506                 } else if (identifier.contains(POP3_RESOURCE_IDENTIFIER)) {
1507                     MailCommon::ResourceReadConfigFile resourceFile(identifier);
1508                     const KConfigGroup grp = resourceFile.group(QStringLiteral("General"));
1509                     if (grp.isValid()) {
1510                         if (grp.readEntry(QStringLiteral("useSSL"), false) || grp.readEntry(QStringLiteral("useTLS"), false)) {
1511                             cryptoStatus = KPIM::ProgressItem::Encrypted;
1512                         }
1513                         mResourceCryptoSettingCache.insert(identifier, cryptoStatus);
1514                     }
1515                 }
1516             }
1517 
1518             // Creating a progress item twice is ok, it will simply return the already existing
1519             // item
1520             KPIM::ProgressItem *progress = PimCommon::ProgressManagerAkonadi::createProgressItem(nullptr,
1521                                                                                                  instance,
1522                                                                                                  instance.identifier(),
1523                                                                                                  instance.name(),
1524                                                                                                  instance.statusMessage(),
1525                                                                                                  true,
1526                                                                                                  cryptoStatus);
1527             progress->setProperty("AgentIdentifier", instance.identifier());
1528         } else if (instance.status() == Akonadi::AgentInstance::Broken) {
1529             agentInstanceBroken(instance);
1530         }
1531     }
1532 }
1533 
1534 void KMKernel::agentInstanceBroken(const Akonadi::AgentInstance &instance)
1535 {
1536     const QString summary = i18n("Resource %1 is broken.\n%2", instance.name(), instance.statusMessage());
1537     KNotification::event(QStringLiteral("akonadi-resource-broken"), QString(), summary, QStringLiteral("kmail"), KNotification::CloseOnTimeout);
1538 }
1539 
1540 void KMKernel::slotProgressItemCompletedOrCanceled(KPIM::ProgressItem *item)
1541 {
1542     const QString identifier = item->property("AgentIdentifier").toString();
1543     const Akonadi::AgentInstance agent = Akonadi::AgentManager::self()->instance(identifier);
1544     if (agent.isValid()) {
1545         mResourcesBeingChecked.removeAll(identifier);
1546         if (mResourcesBeingChecked.isEmpty()) {
1547             qCDebug(KMAIL_LOG) << "Last resource finished syncing, mail check done";
1548             Q_EMIT endCheckMail();
1549         }
1550     }
1551 }
1552 
1553 void KMKernel::updatedTemplates()
1554 {
1555     Q_EMIT customTemplatesChanged();
1556 }
1557 
1558 void KMKernel::cleanupTemporaryFiles()
1559 {
1560     QDir dir(QDir::tempPath());
1561     const QStringList lst = dir.entryList(QStringList{QStringLiteral("messageviewer_*")});
1562     qCDebug(KMAIL_LOG) << " list file to delete " << lst;
1563     for (const QString &file : lst) {
1564         QFile tempFile(QDir::tempPath() + QLatin1Char('/') + file);
1565         if (!tempFile.remove()) {
1566             fprintf(stderr, "%s was not removed .\n", qPrintable(tempFile.fileName()));
1567         } else {
1568             fprintf(stderr, "%s was removed .\n", qPrintable(tempFile.fileName()));
1569         }
1570     }
1571     const QStringList lstRepo = dir.entryList(QStringList{QStringLiteral("messageviewer_*.index.*")});
1572     qCDebug(KMAIL_LOG) << " list repo to delete " << lstRepo;
1573     for (const QString &file : lstRepo) {
1574         QDir tempDir(QDir::tempPath() + QLatin1Char('/') + file);
1575         if (!tempDir.removeRecursively()) {
1576             fprintf(stderr, "%s was not removed .\n", qPrintable(tempDir.path()));
1577         } else {
1578             fprintf(stderr, "%s was removed .\n", qPrintable(tempDir.path()));
1579         }
1580     }
1581 }
1582 
1583 void KMKernel::stopAgentInstance()
1584 {
1585     const QString resourceGroupPattern(QStringLiteral("Resource %1"));
1586 
1587     const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances();
1588     for (Akonadi::AgentInstance type : lst) {
1589         const QString identifier = type.identifier();
1590         KConfigGroup group(KMKernel::config(), resourceGroupPattern.arg(identifier));
1591 
1592         // Keep sync in ConfigureDialog, don't forget to change there.
1593         if (group.readEntry("OfflineOnShutdown", identifier.startsWith(QLatin1StringView("akonadi_pop3_resource")) ? true : false)) {
1594             type.setIsOnline(false);
1595         }
1596     }
1597 }
1598 
1599 void KMKernel::slotCollectionRemoved(const Akonadi::Collection &col)
1600 {
1601     KConfigGroup group(KMKernel::config(), MailCommon::FolderSettings::configGroupName(col));
1602     group.deleteGroup();
1603     group.sync();
1604     const QString colStr = QString::number(col.id());
1605     TemplateParser::Util::deleteTemplate(colStr);
1606     MessageList::Util::deleteConfig(colStr);
1607 }
1608 
1609 void KMKernel::slotDeleteIdentity(uint identity)
1610 {
1611     TemplateParser::Util::deleteTemplate(QStringLiteral("IDENTITY_%1").arg(identity));
1612 }
1613 
1614 bool KMKernel::showPopupAfterDnD()
1615 {
1616     return KMailSettings::self()->showPopupAfterDnD();
1617 }
1618 
1619 bool KMKernel::excludeImportantMailFromExpiry()
1620 {
1621     return KMailSettings::self()->excludeImportantMailFromExpiry();
1622 }
1623 
1624 qreal KMKernel::closeToQuotaThreshold()
1625 {
1626     return KMailSettings::self()->closeToQuotaThreshold();
1627 }
1628 
1629 Akonadi::Collection::Id KMKernel::lastSelectedFolder()
1630 {
1631     return KMailSettings::self()->lastSelectedFolder();
1632 }
1633 
1634 void KMKernel::setLastSelectedFolder(Akonadi::Collection::Id col)
1635 {
1636     KMailSettings::self()->setLastSelectedFolder(col);
1637 }
1638 
1639 QStringList KMKernel::customTemplates()
1640 {
1641     return GlobalSettingsBase::self()->customTemplates();
1642 }
1643 
1644 void KMKernel::openFilterDialog(bool createDummyFilter)
1645 {
1646     if (!mFilterEditDialog) {
1647         mFilterEditDialog = new MailCommon::KMFilterDialog(getKMMainWidget()->actionCollections(), nullptr, createDummyFilter);
1648         mFilterEditDialog->setObjectName(QLatin1StringView("filterdialog"));
1649     }
1650     mFilterEditDialog->show();
1651     mFilterEditDialog->raise();
1652     mFilterEditDialog->activateWindow();
1653 }
1654 
1655 void KMKernel::createFilter(const QByteArray &field, const QString &value)
1656 {
1657     mFilterEditDialog->createFilter(field, value);
1658 }
1659 
1660 void KMKernel::checkFolderFromResources(const Akonadi::Collection::List &collectionList)
1661 {
1662     const Akonadi::AgentInstance::List lst = MailCommon::Util::agentInstances();
1663     for (const Akonadi::AgentInstance &type : lst) {
1664         if (type.status() == Akonadi::AgentInstance::Broken) {
1665             continue;
1666         }
1667         const QString typeIdentifier(type.identifier());
1668         if (PimCommon::Util::isImapResource(typeIdentifier)) {
1669             OrgKdeAkonadiImapSettingsInterface *iface = PimCommon::Util::createImapSettingsInterface(typeIdentifier);
1670             if (iface && iface->isValid()) {
1671                 const Akonadi::Collection::Id imapTrashId = iface->trashCollection();
1672                 for (const Akonadi::Collection &collection : collectionList) {
1673                     const Akonadi::Collection::Id collectionId = collection.id();
1674                     if (imapTrashId == collectionId) {
1675                         // Use default trash
1676                         iface->setTrashCollection(CommonKernel->trashCollectionFolder().id());
1677                         iface->save();
1678                         break;
1679                     }
1680                 }
1681             }
1682             delete iface;
1683         } else if (typeIdentifier.contains(POP3_RESOURCE_IDENTIFIER)) {
1684             OrgKdeAkonadiPOP3SettingsInterface *iface = MailCommon::Util::createPop3SettingsInterface(typeIdentifier);
1685             if (iface->isValid()) {
1686                 for (const Akonadi::Collection &collection : std::as_const(collectionList)) {
1687                     const Akonadi::Collection::Id collectionId = collection.id();
1688                     if (iface->targetCollection() == collectionId) {
1689                         // Use default inbox
1690                         iface->setTargetCollection(CommonKernel->inboxCollectionFolder().id());
1691                         iface->save();
1692                         break;
1693                     }
1694                 }
1695             }
1696             delete iface;
1697         }
1698     }
1699 }
1700 
1701 const QAbstractItemModel *KMKernel::treeviewModelSelection()
1702 {
1703     if (getKMMainWidget()) {
1704         return getKMMainWidget()->folderTreeView()->selectionModel()->model();
1705     } else {
1706         return entityTreeModel();
1707     }
1708 }
1709 
1710 void KMKernel::slotInstanceWarning(const Akonadi::AgentInstance &instance, const QString &message)
1711 {
1712     const QString summary = i18nc("<source>: <error message>", "%1: %2", instance.name(), message);
1713     KNotification::event(QStringLiteral("akonadi-instance-warning"), QString(), summary, QStringLiteral("kmail"), KNotification::CloseOnTimeout);
1714 }
1715 
1716 void KMKernel::slotInstanceError(const Akonadi::AgentInstance &instance, const QString &message)
1717 {
1718     const QString summary = i18nc("<source>: <error message>", "%1: %2", instance.name(), message);
1719     KNotification::event(QStringLiteral("akonadi-instance-error"), QString(), summary, QStringLiteral("kmail"), KNotification::CloseOnTimeout);
1720 }
1721 
1722 void KMKernel::slotInstanceRemoved(const Akonadi::AgentInstance &instance)
1723 {
1724     const QString identifier(instance.identifier());
1725     const QString resourceGroup = QStringLiteral("Resource %1").arg(identifier);
1726     if (KMKernel::config()->hasGroup(resourceGroup)) {
1727         KConfigGroup group(KMKernel::config(), resourceGroup);
1728         group.deleteGroup();
1729         group.sync();
1730     }
1731     if (mResourceCryptoSettingCache.contains(identifier)) {
1732         mResourceCryptoSettingCache.remove(identifier);
1733     }
1734     mFolderArchiveManager->slotInstanceRemoved(instance);
1735 
1736     if (MailCommon::Util::isMailAgent(instance)) {
1737         Q_EMIT incomingAccountsChanged();
1738     }
1739 }
1740 
1741 void KMKernel::slotInstanceAdded(const Akonadi::AgentInstance &instance)
1742 {
1743     if (MailCommon::Util::isMailAgent(instance)) {
1744         Q_EMIT incomingAccountsChanged();
1745     }
1746 }
1747 
1748 void KMKernel::savePaneSelection()
1749 {
1750     KMMainWidget *widget = getKMMainWidget();
1751     if (widget) {
1752         widget->savePaneSelection();
1753     }
1754 }
1755 
1756 void KMKernel::updatePaneTagComboBox()
1757 {
1758     KMMainWidget *widget = getKMMainWidget();
1759     if (widget) {
1760         widget->updatePaneTagComboBox();
1761     }
1762 }
1763 
1764 void KMKernel::resourceGoOnLine()
1765 {
1766     KMMainWidget *widget = getKMMainWidget();
1767     if (widget) {
1768         if (widget->currentCollection().isValid()) {
1769             Akonadi::Collection collection = widget->currentCollection();
1770             Akonadi::AgentInstance instance = Akonadi::AgentManager::self()->instance(collection.resource());
1771             instance.setIsOnline(true);
1772             widget->refreshMessageListSelection();
1773         }
1774     }
1775 }
1776 
1777 void KMKernel::makeResourceOnline(MessageViewer::Viewer::ResourceOnlineMode mode)
1778 {
1779     switch (mode) {
1780     case MessageViewer::Viewer::AllResources:
1781         resumeNetworkJobs();
1782         break;
1783     case MessageViewer::Viewer::SelectedResource:
1784         resourceGoOnLine();
1785         break;
1786     }
1787 }
1788 TextAutoCorrectionCore::AutoCorrection *KMKernel::composerAutoCorrection()
1789 {
1790     return mAutoCorrection;
1791 }
1792 
1793 void KMKernel::toggleSystemTray()
1794 {
1795     KMMainWidget *widget = getKMMainWidget();
1796     if (widget) {
1797         mUnityServiceManager->toggleSystemTray(widget);
1798     }
1799 }
1800 
1801 void KMKernel::showFolder(const QString &collectionId)
1802 {
1803     if (!collectionId.isEmpty()) {
1804         const Akonadi::Collection::Id id = collectionId.toLongLong();
1805         selectCollectionFromId(id);
1806     }
1807 }
1808 
1809 void KMKernel::reloadFolderArchiveConfig()
1810 {
1811     mFolderArchiveManager->reloadConfig();
1812 }
1813 
1814 bool KMKernel::replyMail(qint64 serialNumber, bool replyToAll)
1815 {
1816     KMMainWidget *mainWidget = nullptr;
1817 
1818     // First look for a KMainWindow.
1819     const auto lst = KMainWindow::memberList();
1820     for (KMainWindow *window : lst) {
1821         // Then look for a KMMainWidget.
1822         QList<KMMainWidget *> l = window->findChildren<KMMainWidget *>();
1823         if (!l.isEmpty() && l.first()) {
1824             mainWidget = l.first();
1825             if (window->isActiveWindow()) {
1826                 break;
1827             }
1828         }
1829     }
1830     if (mainWidget) {
1831         auto job = new Akonadi::ItemFetchJob(Akonadi::Item(serialNumber), this);
1832         job->fetchScope().fetchFullPayload();
1833         job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
1834         if (job->exec()) {
1835             if (job->items().count() >= 1) {
1836                 const auto item = job->items().at(0);
1837                 mainWidget->replyMessageTo(item, replyToAll);
1838                 return true;
1839             }
1840         }
1841     }
1842     return false;
1843 }
1844 
1845 void KMKernel::slotCollectionChanged(const Akonadi::Collection &, const QSet<QByteArray> &set)
1846 {
1847     if (set.contains("newmailnotifierattribute")) {
1848         mUnityServiceManager->initListOfCollection();
1849     }
1850 }
1851 
1852 FolderArchiveManager *KMKernel::folderArchiveManager() const
1853 {
1854     return mFolderArchiveManager;
1855 }
1856 
1857 bool KMKernel::allowToDebug() const
1858 {
1859     return mDebug;
1860 }
1861 
1862 bool KMKernel::firstStart() const
1863 {
1864     return the_firstStart;
1865 }
1866 
1867 bool KMKernel::shuttingDown() const
1868 {
1869     return the_shuttingDown;
1870 }
1871 
1872 void KMKernel::setShuttingDown(bool flag)
1873 {
1874     the_shuttingDown = flag;
1875 }
1876 
1877 void KMKernel::expunge(Akonadi::Collection::Id col, bool sync)
1878 {
1879     Q_UNUSED(col)
1880     Q_UNUSED(sync)
1881 }
1882 
1883 #ifdef WITH_KUSERFEEDBACK
1884 KUserFeedback::Provider *KMKernel::userFeedbackProvider() const
1885 {
1886     return mUserFeedbackProvider;
1887 }
1888 
1889 #endif
1890 
1891 #include "moc_kmkernel.cpp"