1/*
2 Copyright (c) 2007 Volker Krause <vkrause@kde.org>
3 Copyright (C) 2014 Daniel Vrátil <dvratil@redhat.com>
4
5 This library is free software; you can redistribute it and/or modify it
6 under the terms of the GNU Library General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or (at your
8 option) any later version.
9
10 This library is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
13 License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 02110-1301, USA.
19*/
20
21#include "cachecleaner.h"
22#include "akdebug.h"
23#include "storage/parthelper.h"
24#include "storage/datastore.h"
25#include "storage/selectquerybuilder.h"
26#include "storage/entity.h"
27#include "akonadi.h"
28#include "libs/protocol_p.h"
29
30using namespace Akonadi::Server;
31
32QMutex CacheCleanerInhibitor::sLock;
33int CacheCleanerInhibitor::sInhibitCount = 0;
34
35CacheCleanerInhibitor::CacheCleanerInhibitor(bool doInhibit)
36 : mInhibited(false)
37{
38 if (doInhibit) {
39 inhibit();
40 }
41}
42
43CacheCleanerInhibitor::~CacheCleanerInhibitor()
44{
45 if (mInhibited) {
46 uninhibit();
47 }
48}
49
50void CacheCleanerInhibitor::inhibit()
51{
52 if (mInhibited) {
53 akError() << "Cannot recursively inhibit an inhibitor";
54 return;
55 }
56
57 sLock.lock();
58 if (++sInhibitCount == 1) {
59 if (AkonadiServer::instance()->cacheCleaner()) {
60 AkonadiServer::instance()->cacheCleaner()->inhibit(true);
61 }
62 }
63 sLock.unlock();
64 mInhibited = true;
65}
66
67void CacheCleanerInhibitor::uninhibit()
68{
69 if (!mInhibited) {
70 akError() << "Cannot uninhibit an uninhibited inhibitor"; // aaaw yeah
71 return;
72 }
73 mInhibited = false;
74
75 sLock.lock();
76 Q_ASSERT(sInhibitCount > 0);
77 if (--sInhibitCount == 0) {
78 if (AkonadiServer::instance()->cacheCleaner()) {
79 AkonadiServer::instance()->cacheCleaner()->inhibit(false);
80 }
81 }
82 sLock.unlock();
83}
84
85CacheCleaner::CacheCleaner(QObject *parent)
86 : CollectionScheduler(parent)
87{
88 setMinimumInterval(5);
89}
90
91CacheCleaner::~CacheCleaner()
92{
93}
94
95int CacheCleaner::collectionScheduleInterval(const Collection &collection)
96{
97 return collection.cachePolicyCacheTimeout();
98}
99
100bool CacheCleaner::hasChanged(const Collection &collection, const Collection &changed)
101{
102 return collection.cachePolicyLocalParts() != changed.cachePolicyLocalParts()
103 || collection.cachePolicyCacheTimeout() != changed.cachePolicyCacheTimeout()
104 || collection.cachePolicyInherit() != changed.cachePolicyInherit();
105}
106
107bool CacheCleaner::shouldScheduleCollection(const Collection &collection)
108{
109 return collection.cachePolicyLocalParts() != QLatin1String("ALL")
110 && collection.cachePolicyCacheTimeout() >= 0
111 && (collection.enabled() || (collection.displayPref() == Tristate::True) || (collection.syncPref() == Tristate::True) || (collection.indexPref() == Tristate::True))
112 && collection.resourceId() > 0;
113}
114
115void CacheCleaner::collectionExpired(const Collection &collection)
116{
117 SelectQueryBuilder<Part> qb;
118 qb.addJoin(QueryBuilder::InnerJoin, PimItem::tableName(), Part::pimItemIdColumn(), PimItem::idFullColumnName());
119 qb.addJoin(QueryBuilder::InnerJoin, PartType::tableName(), Part::partTypeIdFullColumnName(), PartType::idFullColumnName());
120 qb.addValueCondition(PimItem::collectionIdFullColumnName(), Query::Equals, collection.id());
121 qb.addValueCondition(PimItem::atimeFullColumnName(), Query::Less, QDateTime::currentDateTime().addSecs(-60 * collection.cachePolicyCacheTimeout()));
122 qb.addValueCondition(Part::dataFullColumnName(), Query::IsNot, QVariant());
123 qb.addValueCondition(PartType::nsFullColumnName(), Query::Equals, QLatin1String("PLD"));
124 qb.addValueCondition(PimItem::dirtyFullColumnName(), Query::Equals, false);
125
126 QStringList localParts;
127 Q_FOREACH (QString partName, collection.cachePolicyLocalParts().split(QLatin1String(" "))) {
128 if (partName.startsWith(QLatin1String(AKONADI_PARAM_PLD))) {
129 partName = partName.mid(4);
130 }
131 qb.addValueCondition(PartType::nameFullColumnName(), Query::NotEquals, partName);
132 }
133 if (qb.exec()) {
134 const Part::List parts = qb.result();
135 if (!parts.isEmpty()) {
136 akDebug() << "found" << parts.count() << "item parts to expire in collection" << collection.name();
137 // clear data field
138 Q_FOREACH (Part part, parts) {
139 try {
140 if (!PartHelper::truncate(part)) {
141 akDebug() << "failed to update item part" << part.id();
142 }
143 } catch (const PartHelperException &e) {
144 akError() << e.type() << e.what();
145 }
146 }
147 }
148 }
149}
150