File indexing completed on 2024-11-17 04:45:07

0001 /*
0002     SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
0003     SPDX-FileContributor: Tobias Koenig <tokoe@kdab.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "removecollectionrecursivetask.h"
0009 
0010 #include "imapresource_debug.h"
0011 #include <Akonadi/MessageFlags>
0012 #include <KIMAP/CloseJob>
0013 #include <KIMAP/DeleteJob>
0014 #include <KIMAP/SelectJob>
0015 #include <KIMAP/StoreJob>
0016 #include <KLocalizedString>
0017 
0018 Q_DECLARE_METATYPE(KIMAP::DeleteJob *)
0019 
0020 RemoveCollectionRecursiveTask::RemoveCollectionRecursiveTask(const ResourceStateInterface::Ptr &resource, QObject *parent)
0021     : ResourceTask(DeferIfNoSession, resource, parent)
0022 {
0023 }
0024 
0025 RemoveCollectionRecursiveTask::~RemoveCollectionRecursiveTask() = default;
0026 
0027 void RemoveCollectionRecursiveTask::doStart(KIMAP::Session *session)
0028 {
0029     mSession = session;
0030 
0031     mFolderFound = false;
0032     auto listJob = new KIMAP::ListJob(session);
0033     listJob->setIncludeUnsubscribed(!isSubscriptionEnabled());
0034     listJob->setQueriedNamespaces(serverNamespaces());
0035     connect(listJob, &KIMAP::ListJob::mailBoxesReceived, this, &RemoveCollectionRecursiveTask::onMailBoxesReceived);
0036     connect(listJob, &KIMAP::ListJob::result, this, &RemoveCollectionRecursiveTask::onJobDone);
0037     listJob->start();
0038 }
0039 
0040 void RemoveCollectionRecursiveTask::onMailBoxesReceived(const QList<KIMAP::MailBoxDescriptor> &descriptors, const QList<QList<QByteArray>> &)
0041 {
0042     const QString mailBox = mailBoxForCollection(collection());
0043 
0044     // We have to delete the deepest-nested folders first, so
0045     // we use a map here that has the level of nesting as key.
0046     QMultiMap<int, KIMAP::MailBoxDescriptor> foldersToDelete;
0047 
0048     for (int i = 0, total = descriptors.size(); i < total; ++i) {
0049         const KIMAP::MailBoxDescriptor descriptor = descriptors[i];
0050 
0051         if (descriptor.name == mailBox || descriptor.name.startsWith(mailBox + descriptor.separator)) { // a sub folder to delete
0052             const QStringList pathParts = descriptor.name.split(descriptor.separator);
0053             foldersToDelete.insert(pathParts.count(), descriptor);
0054         }
0055     }
0056 
0057     if (foldersToDelete.isEmpty()) {
0058         return;
0059     }
0060 
0061     mFolderFound = true;
0062 
0063     // Now start the actual deletion work
0064     mFolderIterator.reset(new QMultiMapIterator<int, KIMAP::MailBoxDescriptor>(foldersToDelete));
0065     mFolderIterator->toBack(); // we start with largest nesting value first
0066 
0067     deleteNextMailbox();
0068 }
0069 
0070 void RemoveCollectionRecursiveTask::deleteNextMailbox()
0071 {
0072     if (!mFolderIterator->hasPrevious()) {
0073         changeProcessed(); // finish the job
0074         return;
0075     }
0076 
0077     mFolderIterator->previous();
0078     const KIMAP::MailBoxDescriptor &descriptor = mFolderIterator->value();
0079     qCDebug(IMAPRESOURCE_LOG) << descriptor.name;
0080 
0081     // first select the mailbox
0082     auto selectJob = new KIMAP::SelectJob(mSession);
0083     selectJob->setMailBox(descriptor.name);
0084     connect(selectJob, &KIMAP::SelectJob::result, this, &RemoveCollectionRecursiveTask::onJobDone);
0085     selectJob->start();
0086 
0087     // mark all items as deleted
0088     // This step shouldn't be required, but apparently some servers don't allow deleting, non empty mailboxes (although they should).
0089     KIMAP::ImapSet allItems;
0090     allItems.add(KIMAP::ImapInterval(1, 0)); // means 1:*
0091     auto storeJob = new KIMAP::StoreJob(mSession);
0092     storeJob->setSequenceSet(allItems);
0093     storeJob->setFlags(KIMAP::MessageFlags() << Akonadi::MessageFlags::Deleted);
0094     storeJob->setMode(KIMAP::StoreJob::AppendFlags);
0095     // The result is explicitly ignored, since this can fail in the case of an empty folder
0096     storeJob->start();
0097 
0098     // Some IMAP servers don't allow deleting an opened mailbox, so make sure
0099     // it's not opened (https://bugs.kde.org/show_bug.cgi?id=324932). CLOSE will
0100     // also trigger EXPUNGE to take care of the messages deleted above
0101     auto closeJob = new KIMAP::CloseJob(mSession);
0102     closeJob->setProperty("folderDescriptor", descriptor.name);
0103     connect(closeJob, &KIMAP::CloseJob::result, this, &RemoveCollectionRecursiveTask::onCloseJobDone);
0104     closeJob->start();
0105 }
0106 
0107 void RemoveCollectionRecursiveTask::onCloseJobDone(KJob *job)
0108 {
0109     if (job->error()) {
0110         changeProcessed();
0111         qCDebug(IMAPRESOURCE_LOG) << "Failed to close the folder, resync the folder tree";
0112         emitWarning(i18n("Failed to delete the folder, restoring folder list."));
0113         synchronizeCollectionTree();
0114     } else {
0115         auto deleteJob = new KIMAP::DeleteJob(mSession);
0116         deleteJob->setMailBox(job->property("folderDescriptor").toString());
0117         connect(deleteJob, &KIMAP::DeleteJob::result, this, &RemoveCollectionRecursiveTask::onDeleteJobDone);
0118         deleteJob->start();
0119     }
0120 }
0121 
0122 void RemoveCollectionRecursiveTask::onDeleteJobDone(KJob *job)
0123 {
0124     if (job->error()) {
0125         changeProcessed();
0126 
0127         qCDebug(IMAPRESOURCE_LOG) << "Failed to delete the folder, resync the folder tree";
0128         emitWarning(i18n("Failed to delete the folder, restoring folder list."));
0129         synchronizeCollectionTree();
0130     } else {
0131         deleteNextMailbox();
0132     }
0133 }
0134 
0135 void RemoveCollectionRecursiveTask::onJobDone(KJob *job)
0136 {
0137     if (job->error()) {
0138         changeProcessed();
0139 
0140         qCDebug(IMAPRESOURCE_LOG) << "Failed to delete the folder, resync the folder tree";
0141         emitWarning(i18n("Failed to delete the folder, restoring folder list."));
0142         synchronizeCollectionTree();
0143     } else if (!mFolderFound) {
0144         changeProcessed();
0145         qCDebug(IMAPRESOURCE_LOG) << "Failed to find the folder to be deleted, resync the folder tree";
0146         emitWarning(i18n("Failed to find the folder to be deleted, restoring folder list."));
0147         synchronizeCollectionTree();
0148     }
0149 }
0150 
0151 #include "moc_removecollectionrecursivetask.cpp"