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