modify.cpp 12.4 KB
Newer Older
Volker Krause's avatar
Volker Krause committed
1
/*
2
    Copyright (c) 2006 Volker Krause <vkrause@kde.org>
Volker Krause's avatar
Volker Krause committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "modify.h"

22
#include "akonadi.h"
23
#include "connection.h"
24
25
26
27
#include "handlerhelper.h"
#include "cachecleaner.h"
#include "collectionreferencemanager.h"
#include "intervalcheck.h"
28
29
30
31
32
33
#include "storage/datastore.h"
#include "storage/transaction.h"
#include "storage/itemretriever.h"
#include "storage/selectquerybuilder.h"
#include "storage/collectionqueryhelper.h"
#include "search/searchmanager.h"
Volker Krause's avatar
Volker Krause committed
34
35

using namespace Akonadi;
36
using namespace Akonadi::Server;
Volker Krause's avatar
Volker Krause committed
37

38
39
bool Modify::parseStream()
{
40
    Protocol::ModifyCollectionCommand cmd(m_command);
41

42
    Collection collection = HandlerHelper::collectionFromScope(cmd.collection(), connection());
43
    if (!collection.isValid()) {
44
        return failureResponse("No such collection");
45
    }
46

47
    CacheCleanerInhibitor inhibitor(false);
48

49
50
    if (cmd.modifiedParts() & Protocol::ModifyCollectionCommand::ParentID) {
        const Collection newParent = Collection::retrieveById(cmd.parentId());
51
52
53
54
55
56
57
58
59
60
        if (newParent.isValid() && collection.parentId() != newParent.id()
            && collection.resourceId() != newParent.resourceId()) {
            inhibitor.inhibit();
            ItemRetriever retriever(connection());
            retriever.setCollection(collection, true);
            retriever.setRetrieveFullPayload(true);
            if (!retriever.exec()) {
                throw HandlerException(retriever.lastError());
            }
        }
61
    }
62

63
64
65
    DataStore *db = connection()->storageBackend();
    Transaction transaction(db);
    QList<QByteArray> changes;
66
    bool referencedChanged = false;
67

68
69
    if (cmd.modifiedParts() & Protocol::ModifyCollectionCommand::MimeTypes) {
        QStringList mts = cmd.mimeTypes();
70
        const MimeType::List currentMts = collection.mimeTypes();
Daniel Vrátil's avatar
Daniel Vrátil committed
71
        bool equal = true;
72
        Q_FOREACH (const MimeType &currentMt, currentMts) {
73
74
            if (mts.contains(currentMt.name())) {
                mts.removeAll(currentMt.name());
75
76
                continue;
            }
77
78
            equal = false;
            if (!collection.removeMimeType(currentMt)) {
79
                return failureResponse("Unable to remove collection mimetype");
80
            }
81
82
        }
        if (!db->appendMimeTypeForCollection(collection.id(), mts)) {
83
            return failureResponse("Unable to add collection mimetypes");
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
        }
        if (!equal || !mts.isEmpty()) {
            changes.append(AKONADI_PARAM_MIMETYPE);
        }
    }

    if (cmd.modifiedParts() & Protocol::ModifyCollectionCommand::CachePolicy) {
        bool changed = false;
        const Protocol::CachePolicy newCp = cmd.cachePolicy();
        if (collection.cachePolicyCacheTimeout() != newCp.cacheTimeout()) {
            collection.setCachePolicyCacheTimeout(newCp.cacheTimeout());
            changed = true;
        }
        if (collection.cachePolicyCheckInterval() != newCp.checkInterval()) {
            collection.setCachePolicyCheckInterval(newCp.checkInterval());
            changed = true;
        }
        if (collection.cachePolicyInherit() != newCp.inherit()) {
            collection.setCachePolicyInherit(newCp.inherit());
            changed = true;
        }
105
106
107
108
109
110

        QStringList parts = newCp.localParts();
        qSort(parts);
        const QString localParts = parts.join(QLatin1Char(' '));
        if (collection.cachePolicyLocalParts() != localParts) {
            collection.setCachePolicyLocalParts(localParts);
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
            changed = true;
        }
        if (collection.cachePolicySyncOnDemand() != newCp.syncOnDemand()) {
            collection.setCachePolicySyncOnDemand(newCp.syncOnDemand());
            changed = true;
        }

        if (changed) {
            changes.append(AKONADI_PARAM_CACHEPOLICY);
        }
    }

    if (cmd.modifiedParts() & Protocol::ModifyCollectionCommand::Name) {
        if (cmd.name() != collection.name()) {
            if (!CollectionQueryHelper::hasAllowedName(collection, cmd.name(), collection.parentId())) {
126
                return failureResponse("Collection with the same name exists already");
127
            }
128
129
130
131
132
133
134
135
            collection.setName(cmd.name());
            changes.append(AKONADI_PARAM_NAME);
        }
    }

    if (cmd.modifiedParts() & Protocol::ModifyCollectionCommand::ParentID) {
        if (collection.parentId() != cmd.parentId()) {
            if (!db->moveCollection(collection, Collection::retrieveById(cmd.parentId()))) {
136
                return failureResponse("Unable to reparent collection");
137
138
            }
            changes.append(AKONADI_PARAM_PARENT);
139
140
141
142
143
144
        }
    }


    if (cmd.modifiedParts() & Protocol::ModifyCollectionCommand::RemoteID) {
        if (cmd.remoteId() != collection.remoteId()) {
145
            if (!connection()->isOwnerResource(collection)) {
146
                return failureResponse("Only resources can modify remote identifiers");
147
            }
148
            collection.setRemoteId(cmd.remoteId());
149
            changes.append(AKONADI_PARAM_REMOTEID);
150
151
        }
    }
Daniel Vrátil's avatar
Daniel Vrátil committed
152

153
154
155
156
157
158
    if (cmd.modifiedParts() & Protocol::ModifyCollectionCommand::RemoteRevision) {
        if (cmd.remoteRevision() != collection.remoteRevision()) {
            collection.setRemoteRevision(cmd.remoteRevision());
            changes.append(AKONADI_PARAM_REMOTEREVISION);
        }
    }
159

160
161
162
163
164
165
    if (cmd.modifiedParts() & Protocol::ModifyCollectionCommand::PersistentSearch) {
        bool changed = false;
        if (cmd.persistentSearchQuery() != collection.queryString()) {
            collection.setQueryString(cmd.persistentSearchQuery());
            changed = true;
        }
166

167
        QList<QByteArray> queryAttributes = collection.queryAttributes().toUtf8().split(' ');
168
169
170
171
172
        if (cmd.persistentSearchRemote() != queryAttributes.contains(AKONADI_PARAM_REMOTE)) {
            if (cmd.persistentSearchRemote()) {
                queryAttributes.append(AKONADI_PARAM_REMOTE);
            } else {
                queryAttributes.removeOne(AKONADI_PARAM_REMOTE);
173
            }
174
175
176
177
178
179
            changed = true;
        }
        if (cmd.persistentSearchRecursive() != queryAttributes.contains(AKONADI_PARAM_RECURSIVE)) {
            if (cmd.persistentSearchRecursive()) {
                queryAttributes.append(AKONADI_PARAM_RECURSIVE);
            } else {
Laurent Montel's avatar
Laurent Montel committed
180
                queryAttributes.removeOne(AKONADI_PARAM_RECURSIVE);
181
            }
182
183
184
185
186
187
188
            changed = true;
        }

        QStringList cols;
        cols.reserve(cmd.persistentSearchCollections().size());
        QVector<qint64> inCols = cmd.persistentSearchCollections();
        qSort(inCols);
189
        Q_FOREACH (qint64 col, cmd.persistentSearchCollections()) {
190
191
            cols.append(QString::number(col));
        }
192
        const QString colStr = cols.join(QLatin1Char(' '));
193
194
195
196
197
198
199
200
201
        if (colStr != collection.queryCollections()) {
            collection.setQueryCollections(colStr);
            changed = true;
        }

        if (changed || cmd.modifiedParts() & Protocol::ModifyCollectionCommand::MimeTypes) {
            SearchManager::instance()->updateSearch(collection);
            if (changed) {
                changes.append(AKONADI_PARAM_PERSISTENTSEARCH);
202
            }
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
        }
    }

    if (cmd.modifiedParts() & Protocol::ModifyCollectionCommand::ListPreferences) {
        if (cmd.enabled() != collection.enabled()) {
            collection.setEnabled(cmd.enabled());
            changes.append(AKONADI_PARAM_ENABLED);
        }
        if (cmd.syncPref() != collection.syncPref()) {
            collection.setSyncPref(cmd.syncPref());
            changes.append(AKONADI_PARAM_SYNC);
        }
        if (cmd.displayPref() != collection.displayPref()) {
            collection.setDisplayPref(cmd.displayPref());
            changes.append(AKONADI_PARAM_DISPLAY);
        }
        if (cmd.indexPref() != collection.indexPref()) {
            collection.setIndexPref(cmd.indexPref());
            changes.append(AKONADI_PARAM_INDEX);
        }
    }

    if (cmd.modifiedParts() & Protocol::ModifyCollectionCommand::Referenced) {
        const bool wasReferencedFromSession = connection()->collectionReferenceManager()->isReferenced(collection.id(), connection()->sessionId());
        connection()->collectionReferenceManager()->referenceCollection(connection()->sessionId(), collection, cmd.referenced());
        const bool referenced = connection()->collectionReferenceManager()->isReferenced(collection.id());
        if (cmd.referenced() != wasReferencedFromSession) {
            changes.append(AKONADI_PARAM_REFERENCED);
        }
        if (referenced != collection.referenced()) {
            referencedChanged = true;
            collection.setReferenced(referenced);
        }
    }

    if (cmd.modifiedParts() & Protocol::ModifyCollectionCommand::RemovedAttributes) {
239
        Q_FOREACH (const QByteArray &attr, cmd.removedAttributes()) {
240
241
            if (db->removeCollectionAttribute(collection, attr)) {
                changes.append(attr);
242
            }
243
244
245
246
        }
    }

    if (cmd.modifiedParts() & Protocol::ModifyCollectionCommand::Attributes) {
247
248
        const QMap<QByteArray, QByteArray> attrs = cmd.attributes();
        for (auto iter = attrs.cbegin(), end = attrs.cend(); iter != end; ++iter) {
249
250
            SelectQueryBuilder<CollectionAttribute> qb;
            qb.addValueCondition(CollectionAttribute::collectionIdColumn(), Query::Equals, collection.id());
Daniel Vrátil's avatar
Daniel Vrátil committed
251
            qb.addValueCondition(CollectionAttribute::typeColumn(), Query::Equals, iter.key());
252
            if (!qb.exec()) {
253
                return failureResponse("Unable to retrieve collection attribute");
254
            }
255
256
257
258
259
260


            const CollectionAttribute::List attrs = qb.result();
            if (attrs.isEmpty()) {
                CollectionAttribute newAttr;
                newAttr.setCollectionId(collection.id());
261
262
                newAttr.setType(iter.key());
                newAttr.setValue(iter.value());
263
                if (!newAttr.insert()) {
264
                    return failureResponse("Unable to add collection attribute");
265
                }
266
                changes.append(iter.key());
267
268
            } else if (attrs.size() == 1) {
                CollectionAttribute currAttr = attrs.first();
269
                if (currAttr.value() == iter.value()) {
270
                    continue;
271
                }
272
                currAttr.setValue(iter.value());
273
                if (!currAttr.update()) {
274
                    return failureResponse("Unable to update collection attribute");
275
                }
276
                changes.append(iter.key());
277
            } else {
278
                return failureResponse("WTF: more than one attribute with the same name");
279
            }
280
        }
281
    }
Volker Krause's avatar
Volker Krause committed
282

283
284
    if (!changes.isEmpty()) {
        if (collection.hasPendingChanges() && !collection.update()) {
285
            return failureResponse("Unable to update collection");
286
        }
287
288
289
290
        //This must be after the collection was updated in the db. The resource will immediately request a copy of the collection.
        if (AkonadiServer::instance()->intervalChecker() && collection.referenced() && referencedChanged) {
            AkonadiServer::instance()->intervalChecker()->requestCollectionSync(collection);
        }
291
292
293
294
295
296
297
298
299
300
        db->notificationCollector()->collectionChanged(collection, changes);
        //For backwards compatibility. Must be after the changed notification (otherwise the compression removes it).
        if (changes.contains(AKONADI_PARAM_ENABLED)) {
            if (collection.enabled()) {
                db->notificationCollector()->collectionSubscribed(collection);
            } else {
                db->notificationCollector()->collectionUnsubscribed(collection);
            }
        }
        if (!transaction.commit()) {
301
            return failureResponse("Unable to commit transaction");
302
        }
303
    }
304

305
    return successResponse<Protocol::ModifyCollectionResponse>();
Volker Krause's avatar
Volker Krause committed
306
}