File: removecollectionrecursivetask.cpp

package info (click to toggle)
kdepim-runtime 4%3A24.12.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 24,012 kB
  • sloc: cpp: 90,562; xml: 1,020; javascript: 60; sh: 58; makefile: 13
file content (151 lines) | stat: -rw-r--r-- 5,768 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
    SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
    SPDX-FileContributor: Tobias Koenig <tokoe@kdab.com>

    SPDX-License-Identifier: LGPL-2.0-or-later
*/

#include "removecollectionrecursivetask.h"

#include "imapresource_debug.h"
#include <Akonadi/MessageFlags>
#include <KIMAP/CloseJob>
#include <KIMAP/DeleteJob>
#include <KIMAP/SelectJob>
#include <KIMAP/StoreJob>
#include <KLocalizedString>

Q_DECLARE_METATYPE(KIMAP::DeleteJob *)

RemoveCollectionRecursiveTask::RemoveCollectionRecursiveTask(const ResourceStateInterface::Ptr &resource, QObject *parent)
    : ResourceTask(DeferIfNoSession, resource, parent)
{
}

RemoveCollectionRecursiveTask::~RemoveCollectionRecursiveTask() = default;

void RemoveCollectionRecursiveTask::doStart(KIMAP::Session *session)
{
    mSession = session;

    mFolderFound = false;
    auto listJob = new KIMAP::ListJob(session);
    listJob->setIncludeUnsubscribed(!isSubscriptionEnabled());
    listJob->setQueriedNamespaces(serverNamespaces());
    connect(listJob, &KIMAP::ListJob::mailBoxesReceived, this, &RemoveCollectionRecursiveTask::onMailBoxesReceived);
    connect(listJob, &KIMAP::ListJob::result, this, &RemoveCollectionRecursiveTask::onJobDone);
    listJob->start();
}

void RemoveCollectionRecursiveTask::onMailBoxesReceived(const QList<KIMAP::MailBoxDescriptor> &descriptors, const QList<QList<QByteArray>> &)
{
    const QString mailBox = mailBoxForCollection(collection());

    // We have to delete the deepest-nested folders first, so
    // we use a map here that has the level of nesting as key.
    QMultiMap<int, KIMAP::MailBoxDescriptor> foldersToDelete;

    for (int i = 0, total = descriptors.size(); i < total; ++i) {
        const KIMAP::MailBoxDescriptor descriptor = descriptors[i];

        if (descriptor.name == mailBox || descriptor.name.startsWith(mailBox + descriptor.separator)) { // a sub folder to delete
            const QStringList pathParts = descriptor.name.split(descriptor.separator);
            foldersToDelete.insert(pathParts.count(), descriptor);
        }
    }

    if (foldersToDelete.isEmpty()) {
        return;
    }

    mFolderFound = true;

    // Now start the actual deletion work
    mFolderIterator.reset(new QMultiMapIterator<int, KIMAP::MailBoxDescriptor>(foldersToDelete));
    mFolderIterator->toBack(); // we start with largest nesting value first

    deleteNextMailbox();
}

void RemoveCollectionRecursiveTask::deleteNextMailbox()
{
    if (!mFolderIterator->hasPrevious()) {
        changeProcessed(); // finish the job
        return;
    }

    mFolderIterator->previous();
    const KIMAP::MailBoxDescriptor &descriptor = mFolderIterator->value();
    qCDebug(IMAPRESOURCE_LOG) << descriptor.name;

    // first select the mailbox
    auto selectJob = new KIMAP::SelectJob(mSession);
    selectJob->setMailBox(descriptor.name);
    connect(selectJob, &KIMAP::SelectJob::result, this, &RemoveCollectionRecursiveTask::onJobDone);
    selectJob->start();

    // mark all items as deleted
    // This step shouldn't be required, but apparently some servers don't allow deleting, non empty mailboxes (although they should).
    KIMAP::ImapSet allItems;
    allItems.add(KIMAP::ImapInterval(1, 0)); // means 1:*
    auto storeJob = new KIMAP::StoreJob(mSession);
    storeJob->setSequenceSet(allItems);
    storeJob->setFlags(KIMAP::MessageFlags() << Akonadi::MessageFlags::Deleted);
    storeJob->setMode(KIMAP::StoreJob::AppendFlags);
    // The result is explicitly ignored, since this can fail in the case of an empty folder
    storeJob->start();

    // Some IMAP servers don't allow deleting an opened mailbox, so make sure
    // it's not opened (https://bugs.kde.org/show_bug.cgi?id=324932). CLOSE will
    // also trigger EXPUNGE to take care of the messages deleted above
    auto closeJob = new KIMAP::CloseJob(mSession);
    closeJob->setProperty("folderDescriptor", descriptor.name);
    connect(closeJob, &KIMAP::CloseJob::result, this, &RemoveCollectionRecursiveTask::onCloseJobDone);
    closeJob->start();
}

void RemoveCollectionRecursiveTask::onCloseJobDone(KJob *job)
{
    if (job->error()) {
        changeProcessed();
        qCDebug(IMAPRESOURCE_LOG) << "Failed to close the folder, resync the folder tree";
        emitWarning(i18n("Failed to delete the folder, restoring folder list."));
        synchronizeCollectionTree();
    } else {
        auto deleteJob = new KIMAP::DeleteJob(mSession);
        deleteJob->setMailBox(job->property("folderDescriptor").toString());
        connect(deleteJob, &KIMAP::DeleteJob::result, this, &RemoveCollectionRecursiveTask::onDeleteJobDone);
        deleteJob->start();
    }
}

void RemoveCollectionRecursiveTask::onDeleteJobDone(KJob *job)
{
    if (job->error()) {
        changeProcessed();

        qCDebug(IMAPRESOURCE_LOG) << "Failed to delete the folder, resync the folder tree";
        emitWarning(i18n("Failed to delete the folder, restoring folder list."));
        synchronizeCollectionTree();
    } else {
        deleteNextMailbox();
    }
}

void RemoveCollectionRecursiveTask::onJobDone(KJob *job)
{
    if (job->error()) {
        changeProcessed();

        qCDebug(IMAPRESOURCE_LOG) << "Failed to delete the folder, resync the folder tree";
        emitWarning(i18n("Failed to delete the folder, restoring folder list."));
        synchronizeCollectionTree();
    } else if (!mFolderFound) {
        changeProcessed();
        qCDebug(IMAPRESOURCE_LOG) << "Failed to find the folder to be deleted, resync the folder tree";
        emitWarning(i18n("Failed to find the folder to be deleted, restoring folder list."));
        synchronizeCollectionTree();
    }
}

#include "moc_removecollectionrecursivetask.cpp"