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"