1 /*
2     SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
3     SPDX-FileContributor: Kevin Ottens <kevin@kdab.com>
4 
5     SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 
8 #include "movecollectiontask.h"
9 
10 #include "imapresource_debug.h"
11 #include <KLocalizedString>
12 
13 #include <KIMAP/RenameJob>
14 #include <KIMAP/SelectJob>
15 #include <KIMAP/Session>
16 #include <KIMAP/SubscribeJob>
17 
18 #include <QUuid>
19 
MoveCollectionTask(const ResourceStateInterface::Ptr & resource,QObject * parent)20 MoveCollectionTask::MoveCollectionTask(const ResourceStateInterface::Ptr &resource, QObject *parent)
21     : ResourceTask(DeferIfNoSession, resource, parent)
22 {
23 }
24 
~MoveCollectionTask()25 MoveCollectionTask::~MoveCollectionTask()
26 {
27 }
28 
doStart(KIMAP::Session * session)29 void MoveCollectionTask::doStart(KIMAP::Session *session)
30 {
31     if (collection().remoteId().isEmpty()) {
32         emitError(i18n("Cannot move IMAP folder '%1', it does not exist on the server.", collection().name()));
33         changeProcessed();
34         return;
35     }
36 
37     if (sourceCollection().remoteId().isEmpty()) {
38         emitError(i18n("Cannot move IMAP folder '%1' out of '%2', '%2' does not exist on the server.", collection().name(), sourceCollection().name()));
39         changeProcessed();
40         return;
41     }
42 
43     if (targetCollection().remoteId().isEmpty()) {
44         emitError(i18n("Cannot move IMAP folder '%1' to '%2', '%2' does not exist on the server.", collection().name(), sourceCollection().name()));
45         changeProcessed();
46         return;
47     }
48 
49     if (session->selectedMailBox() != mailBoxForCollection(collection())) {
50         doRename(session);
51         return;
52     }
53 
54     // Some IMAP servers don't allow moving an opened mailbox, so make sure
55     // it's not opened (https://bugs.kde.org/show_bug.cgi?id=324932) by examining
56     // a non-existent mailbox. We don't use CLOSE in order not to trigger EXPUNGE
57     auto examine = new KIMAP::SelectJob(session);
58     examine->setOpenReadOnly(true); // use EXAMINE instead of SELECT
59     examine->setMailBox(QStringLiteral("IMAP Resource non existing folder %1").arg(QUuid::createUuid().toString()));
60     connect(examine, &KIMAP::SelectJob::result, this, &MoveCollectionTask::onExamineDone);
61     examine->start();
62 }
63 
onExamineDone(KJob * job)64 void MoveCollectionTask::onExamineDone(KJob *job)
65 {
66     // We deliberately ignore any error here, because the SelectJob will always fail
67     // when examining a non-existent mailbox
68 
69     auto examine = static_cast<KIMAP::SelectJob *>(job);
70     doRename(examine->session());
71 }
72 
mailBoxForCollections(const Akonadi::Collection & parent,const Akonadi::Collection & child) const73 QString MoveCollectionTask::mailBoxForCollections(const Akonadi::Collection &parent, const Akonadi::Collection &child) const
74 {
75     const QString parentMailbox = mailBoxForCollection(parent);
76     if (parentMailbox.isEmpty()) {
77         return child.remoteId().mid(1); // Strip separator on toplevel mailboxes
78     }
79     return parentMailbox + child.remoteId();
80 }
81 
doRename(KIMAP::Session * session)82 void MoveCollectionTask::doRename(KIMAP::Session *session)
83 {
84     // collection.remoteId() already includes the separator
85     const QString oldMailBox = mailBoxForCollections(sourceCollection(), collection());
86     const QString newMailBox = mailBoxForCollections(targetCollection(), collection());
87 
88     if (oldMailBox != newMailBox) {
89         auto job = new KIMAP::RenameJob(session);
90         job->setSourceMailBox(oldMailBox);
91         job->setDestinationMailBox(newMailBox);
92 
93         connect(job, &KIMAP::RenameJob::result, this, &MoveCollectionTask::onRenameDone);
94 
95         job->start();
96     } else {
97         changeProcessed();
98     }
99 }
100 
onRenameDone(KJob * job)101 void MoveCollectionTask::onRenameDone(KJob *job)
102 {
103     if (job->error()) {
104         cancelTask(job->errorString());
105     } else {
106         // Automatically subscribe to the new mailbox name
107         auto rename = static_cast<KIMAP::RenameJob *>(job);
108 
109         auto subscribe = new KIMAP::SubscribeJob(rename->session());
110         subscribe->setMailBox(rename->destinationMailBox());
111 
112         connect(subscribe, &KIMAP::SubscribeJob::result, this, &MoveCollectionTask::onSubscribeDone);
113 
114         subscribe->start();
115     }
116 }
117 
onSubscribeDone(KJob * job)118 void MoveCollectionTask::onSubscribeDone(KJob *job)
119 {
120     if (job->error()) {
121         emitWarning(
122             i18n("Failed to subscribe to the folder '%1' on the IMAP server. "
123                  "It will disappear on next sync. Use the subscription dialog to overcome that",
124                  collection().name()));
125     }
126 
127     changeCommitted(collection());
128 }
129