Mailcommon

expirejob.cpp
1/**
2 * SPDX-FileCopyrightText: 2004 David Faure <faure@kde.org>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#include "expirejob.h"
8#include "collectionpage/attributes/expirecollectionattribute.h"
9#include "expiredeletejob.h"
10#include "expiremovejob.h"
11#include "kernel/mailkernel.h"
12
13#include <PimCommon/BroadcastStatus>
14using PimCommon::BroadcastStatus;
15#include "mailcommon_debug.h"
16
17#include <KLocalizedString>
18
19#include <Akonadi/ItemDeleteJob>
20#include <Akonadi/ItemFetchJob>
21#include <Akonadi/ItemFetchScope>
22#include <Akonadi/ItemModifyJob>
23#include <Akonadi/ItemMoveJob>
24#include <Akonadi/MessageFlags>
25#include <Akonadi/MessageParts>
26#include <Akonadi/MessageStatus>
27#include <KMime/Message>
28
29/*
30 Testcases for folder expiry:
31 Automatic expiry:
32 - normal case (ensure folder has old mails and expiry settings, wait for auto-expiry)
33 - having the folder selected when the expiry job would run (gets delayed)
34 - selecting a folder while an expiry job is running for it (should interrupt)
35 - exiting kmail while an expiry job is running (should abort & delete things cleanly)
36 Manual expiry:
37 - RMB / expire (for one folder) [KMMainWidget::slotExpireFolder()]
38 - RMB on Local Folders / Expire All Folders [KMFolderMgr::expireAll()]
39 - Expire All Folders [KMMainWidget::slotExpireAll()]
40*/
41
42using namespace MailCommon;
43ExpireJob::ExpireJob(const Akonadi::Collection &folder, bool immediate)
44 : ScheduledJob(folder, immediate)
45{
46}
47
48ExpireJob::~ExpireJob()
49{
50 qCDebug(MAILCOMMON_LOG);
51}
52
53void ExpireJob::kill()
54{
56}
57
58void ExpireJob::execute()
59{
60 const MailCommon::ExpireCollectionAttribute *expirationAttribute = mSrcFolder.attribute<MailCommon::ExpireCollectionAttribute>();
61 if (expirationAttribute) {
62 mMaxUnreadTime = 0;
63 mMaxReadTime = 0;
64 int unreadDays;
65 int readDays;
66 mExpireMessagesWithoutInvalidDate = expirationAttribute->expireMessagesWithValidDate();
67 expirationAttribute->daysToExpire(unreadDays, readDays);
68
69 if (unreadDays > 0) {
70 qCDebug(MAILCOMMON_LOG) << "ExpireJob: deleting unread older than" << unreadDays << "days";
71 mMaxUnreadTime = QDateTime::currentDateTime().toSecsSinceEpoch() - unreadDays * 3600 * 24;
72 }
73 if (readDays > 0) {
74 qCDebug(MAILCOMMON_LOG) << "ExpireJob: deleting read older than" << readDays << "days";
75 mMaxReadTime = QDateTime::currentDateTime().toSecsSinceEpoch() - readDays * 3600 * 24;
76 }
77
78 if ((mMaxUnreadTime == 0) && (mMaxReadTime == 0)) {
79 qCDebug(MAILCOMMON_LOG) << "ExpireJob: nothing to do";
81 return;
82 }
83 } else {
85 return;
86 }
87 qCDebug(MAILCOMMON_LOG) << "ExpireJob: starting to expire in folder" << mSrcFolder.name();
88 slotDoWork();
89 // do nothing here, we might be deleted!
90}
91
92void ExpireJob::slotDoWork()
93{
94 auto job = new Akonadi::ItemFetchJob(mSrcFolder, this);
95 job->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Envelope);
96 connect(job, &Akonadi::ItemFetchJob::result, this, &ExpireJob::itemFetchResult);
97}
98
99void ExpireJob::itemFetchResult(KJob *job)
100{
101 if (job->error()) {
102 qCWarning(MAILCOMMON_LOG) << job->errorString();
103 deleteLater();
104 return;
105 }
106
108 for (const Akonadi::Item &item : items) {
109 if (!item.hasPayload<KMime::Message::Ptr>()) {
110 continue;
111 }
112
113 const auto mb = item.payload<KMime::Message::Ptr>();
115 status.setStatusFromFlags(item.flags());
116 if ((status.isImportant() || status.isToAct() || status.isWatched()) && SettingsIf->excludeImportantMailFromExpiry()) {
117 continue;
118 }
119
120 auto mailDate = mb->date(false);
121 if (!mailDate) {
122 if (mExpireMessagesWithoutInvalidDate) {
123 mRemovedMsgs.append(item);
124 }
125 } else {
126 const time_t maxTime = status.isRead() ? mMaxReadTime : mMaxUnreadTime;
127 if (mailDate->dateTime().toSecsSinceEpoch() < maxTime) {
128 mRemovedMsgs.append(item);
129 }
130 }
131 }
132
133 done();
134}
135
136void ExpireJob::done()
137{
138 QString str;
139 bool moving = false;
140 if (!mRemovedMsgs.isEmpty()) {
141 const int count = mRemovedMsgs.count();
142
143 // The command shouldn't kill us because it opens the folder
144 mCancellable = false;
145
146 const MailCommon::ExpireCollectionAttribute *expirationAttribute = mSrcFolder.attribute<MailCommon::ExpireCollectionAttribute>();
147 if (expirationAttribute) {
148 const QString srcFolderName{mSrcFolder.name()};
149 if (expirationAttribute->expireAction() == MailCommon::ExpireCollectionAttribute::ExpireDelete) {
150 // Expire by deletion, i.e. move to null target folder
151 qCDebug(MAILCOMMON_LOG) << "ExpireJob: finished expiring in folder" << srcFolderName << count << "messages to remove.";
152 auto job = new ExpireDeleteJob(this);
153 job->setRemovedMsgs(mRemovedMsgs);
154 job->setSourceFolderName(srcFolderName);
155 connect(job, &ExpireDeleteJob::expireDeleteDone, this, &ExpireJob::slotExpireDeleteDone);
156 moving = true;
157 str = i18np("Removing 1 old message from folder %2…", "Removing %1 old messages from folder %2...", count, srcFolderName);
158 job->start();
159 } else {
160 // Expire by moving
161 mMoveToFolder = Kernel::self()->collectionFromId(expirationAttribute->expireToFolderId());
162 if (!mMoveToFolder.isValid()) {
163 str = i18n(
164 "Cannot expire messages from folder %1: destination "
165 "folder %2 not found",
166 srcFolderName,
167 expirationAttribute->expireToFolderId());
168 qCWarning(MAILCOMMON_LOG) << str;
169 } else {
170 qCDebug(MAILCOMMON_LOG) << "ExpireJob: finished expiring in folder" << srcFolderName << mRemovedMsgs.count() << "messages to move to"
171 << mMoveToFolder.name();
172
173 auto job = new ExpireMoveJob(this);
174 job->setRemovedMsgs(mRemovedMsgs);
175 job->setSrcFolderName(srcFolderName);
176 job->setMoveToFolder(mMoveToFolder);
177 connect(job, &ExpireMoveJob::expireMovedDone, this, &ExpireJob::slotExpireDeleteDone);
178 job->start();
179 moving = true;
180 str = i18np("Moving 1 old message from folder %2 to folder %3…",
181 "Moving %1 old messages from folder %2 to folder %3…",
182 count,
183 srcFolderName,
184 mMoveToFolder.name());
185 }
186 }
187 }
188 if (!str.isEmpty()) {
189 BroadcastStatus::instance()->setStatusMsg(str);
190 }
191 }
192
193 if (!moving) {
194 deleteLater();
195 }
196}
197
198void ExpireJob::slotExpireDeleteDone()
199{
200 deleteLater();
201}
202
203#include "moc_expirejob.cpp"
bool isValid() const
const T * attribute() const
QString name() const
void setStatusFromFlags(const QSet< QByteArray > &flags)
SPDX-FileCopyrightText: 2024 Laurent Montel montel@kde.org
SPDX-FileCopyrightText: 2024 Laurent Montel montel@kde.org
virtual QString errorString() const
int error() const
virtual Q_SCRIPTABLE void start()=0
virtual void kill()
Interrupt the job.
Definition folderjob.cpp:37
Base class for scheduled jobs.
Q_SCRIPTABLE CaptureState status()
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
AKONADI_MIME_EXPORT const char Envelope[]
The filter dialog.
QDateTime currentDateTime()
qint64 toSecsSinceEpoch() const const
void append(QList< T > &&value)
qsizetype count() const const
bool isEmpty() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
T qobject_cast(QObject *object)
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:00:25 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.