• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.14.10 API Reference
  • KDE Home
  • Contact Us
 

akonadi

  • akonadi
itemmodifyjob.cpp
1/*
2 Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19
20#include "itemmodifyjob.h"
21#include "itemmodifyjob_p.h"
22
23#include "changemediator_p.h"
24#include "collection.h"
25#include "conflicthandling/conflicthandler_p.h"
26#include "entity_p.h"
27#include "imapparser_p.h"
28#include "item_p.h"
29#include "itemserializer_p.h"
30#include "job_p.h"
31#include "protocolhelper_p.h"
32#include "gid/gidextractor_p.h"
33
34#include <kdebug.h>
35
36using namespace Akonadi;
37
38ItemModifyJobPrivate::ItemModifyJobPrivate(ItemModifyJob *parent)
39 : JobPrivate(parent)
40 , mRevCheck(true)
41 , mIgnorePayload(false)
42 , mAutomaticConflictHandlingEnabled(true)
43 , mSilent(false)
44{
45}
46
47void ItemModifyJobPrivate::setClean()
48{
49 mOperations.insert(Dirty);
50}
51
52QByteArray ItemModifyJobPrivate::nextPartHeader()
53{
54 QByteArray command;
55 if (!mParts.isEmpty()) {
56 QSetIterator<QByteArray> it(mParts);
57 const QByteArray label = it.next();
58 mParts.remove(label);
59
60 mPendingData.clear();
61 int version = 0;
62 ItemSerializer::serialize(mItems.first(), label, mPendingData, version);
63 command += ' ' + ProtocolHelper::encodePartIdentifier(ProtocolHelper::PartPayload, label, version);
64 if (mPendingData.size() > 0) {
65 command += " {" + QByteArray::number(mPendingData.size()) + "}\n";
66 } else {
67 if (mPendingData.isNull()) {
68 command += " NIL";
69 } else {
70 command += " \"\"";
71 }
72 command += nextPartHeader();
73 }
74 } else {
75 command += ")\n";
76 }
77 return command;
78}
79
80void ItemModifyJobPrivate::conflictResolved()
81{
82 Q_Q(ItemModifyJob);
83
84 q->setError(KJob::NoError);
85 q->setErrorText(QString());
86 q->emitResult();
87}
88
89void ItemModifyJobPrivate::conflictResolveError(const QString &message)
90{
91 Q_Q(ItemModifyJob);
92
93 q->setErrorText(q->errorText() + message);
94 q->emitResult();
95}
96
97void ItemModifyJobPrivate::doUpdateItemRevision(Akonadi::Item::Id itemId, int oldRevision, int newRevision)
98{
99 Item::List::iterator it = std::find_if(mItems.begin(), mItems.end(), boost::bind(&Item::id, _1) == itemId);
100 if (it != mItems.end() && (*it).revision() == oldRevision) {
101 (*it).setRevision(newRevision);
102 }
103}
104
105QString ItemModifyJobPrivate::jobDebuggingString() const
106{
107 try {
108 return QString::fromUtf8(fullCommand());
109 } catch (const Exception &e) {
110 return QString::fromUtf8(e.what());
111 }
112}
113
114void ItemModifyJobPrivate::setSilent( bool silent )
115{
116 mSilent = silent;
117}
118
119QByteArray ItemModifyJobPrivate::tagsToCommandParameter(const Tag::List &tags) const
120{
121 QByteArray c;
122 if (tags.isEmpty()) {
123 qWarning() << "Missing implemented method";
124 } else if (tags.first().id() >= 0) {
125 c += "TAGS";
126 c += ' ' + ProtocolHelper::tagSetToImapSequenceSet(tags);
127 } else if (std::find_if(tags.constBegin(), tags.constEnd(),
128 boost::bind(&QByteArray::isEmpty, boost::bind(&Tag::remoteId, _1)))
129 == tags.constEnd()) {
130 //All items have a remoteId
131 c += "RTAGS";
132 QList<QByteArray> rids;
133 Q_FOREACH (const Tag &object, tags) {
134 rids << ImapParser::quote(object.remoteId());
135 }
136
137 c += '(';
138 c += ImapParser::join(rids, " ");
139 c += ')';
140
141 } else if (std::find_if(tags.constBegin(), tags.constEnd(),
142 boost::bind(&QByteArray::isEmpty, boost::bind(&Tag::gid, _1)))
143 == tags.constEnd()) {
144 //All items have a gid
145 c += "GTAGS";
146 QList<QByteArray> gids;
147 Q_FOREACH (const Tag &object, tags) {
148 gids << ImapParser::quote(object.gid());
149 }
150
151 c += '(';
152 c += ImapParser::join(gids, " ");
153 c += ')';
154 } else {
155 throw Exception("Cannot identify all tags");
156 }
157 return c;
158}
159
160ItemModifyJob::ItemModifyJob(const Item &item, QObject *parent)
161 : Job(new ItemModifyJobPrivate(this), parent)
162{
163 Q_D(ItemModifyJob);
164
165 d->mItems.append(item);
166 d->mParts = item.loadedPayloadParts();
167
168 d->mOperations.insert(ItemModifyJobPrivate::RemoteId);
169 d->mOperations.insert(ItemModifyJobPrivate::RemoteRevision);
170}
171
172ItemModifyJob::ItemModifyJob(const Akonadi::Item::List &items, QObject *parent)
173 : Job(new ItemModifyJobPrivate(this), parent)
174{
175 Q_ASSERT(!items.isEmpty());
176 Q_D(ItemModifyJob);
177 d->mItems = items;
178
179 // same as single item ctor
180 if (d->mItems.size() == 1) {
181 d->mParts = items.first().loadedPayloadParts();
182 d->mOperations.insert(ItemModifyJobPrivate::RemoteId);
183 d->mOperations.insert(ItemModifyJobPrivate::RemoteRevision);
184 } else {
185 d->mIgnorePayload = true;
186 d->mRevCheck = false;
187 }
188}
189
190ItemModifyJob::~ItemModifyJob()
191{
192}
193
194QByteArray ItemModifyJobPrivate::fullCommand() const
195{
196 const Akonadi::Item item = mItems.first();
197 QList<QByteArray> changes;
198 foreach (int op, mOperations) {
199 switch (op) {
200 case ItemModifyJobPrivate::RemoteId:
201 if (!item.remoteId().isNull()) {
202 changes << "REMOTEID";
203 changes << ImapParser::quote(item.remoteId().toUtf8());
204 }
205 break;
206 case ItemModifyJobPrivate::Gid: {
207 const QString gid = GidExtractor::getGid(item);
208 if (!gid.isNull()) {
209 changes << "GID";
210 changes << ImapParser::quote(gid.toUtf8());
211 }
212 break;
213 }
214 case ItemModifyJobPrivate::RemoteRevision:
215 if (!item.remoteRevision().isNull()) {
216 changes << "REMOTEREVISION";
217 changes << ImapParser::quote(item.remoteRevision().toUtf8());
218 }
219 break;
220 case ItemModifyJobPrivate::Dirty:
221 changes << "DIRTY";
222 changes << "false";
223 break;
224 }
225 }
226
227 if (item.d_func()->mClearPayload) {
228 changes << "INVALIDATECACHE";
229 }
230 if ( mSilent ) {
231 changes << "SILENT";
232 }
233
234 if (item.d_func()->mFlagsOverwritten) {
235 changes << "FLAGS";
236 changes << '(' + ImapParser::join(item.flags(), " ") + ')';
237 } else {
238 if (!item.d_func()->mAddedFlags.isEmpty()) {
239 changes << "+FLAGS";
240 changes << '(' + ImapParser::join(item.d_func()->mAddedFlags, " ") + ')';
241 }
242 if (!item.d_func()->mDeletedFlags.isEmpty()) {
243 changes << "-FLAGS";
244 changes << '(' + ImapParser::join(item.d_func()->mDeletedFlags, " ") + ')';
245 }
246 }
247
248 if (item.d_func()->mTagsOverwritten) {
249 changes << tagsToCommandParameter(item.tags());
250 } else {
251 if (!item.d_func()->mAddedTags.isEmpty()) {
252 changes << "+" + tagsToCommandParameter(item.d_func()->mAddedTags);
253 }
254 if (!item.d_func()->mDeletedTags.isEmpty()) {
255 changes << "-" + tagsToCommandParameter(item.d_func()->mDeletedTags);
256 }
257 }
258
259 if (!item.d_func()->mDeletedAttributes.isEmpty()) {
260 changes << "-PARTS";
261 QList<QByteArray> attrs;
262 foreach (const QByteArray &attr, item.d_func()->mDeletedAttributes) {
263 attrs << ProtocolHelper::encodePartIdentifier(ProtocolHelper::PartAttribute, attr);
264 }
265 changes << '(' + ImapParser::join(attrs, " ") + ')';
266 }
267
268 // nothing to do
269 if (changes.isEmpty() && mParts.isEmpty() && item.attributes().isEmpty()) {
270 return QByteArray();
271 }
272
273 QByteArray command;
274 command += ProtocolHelper::entitySetToByteArray(mItems, "STORE"); // can throw an exception
275 command += ' ';
276 if (!mRevCheck || item.revision() < 0) {
277 command += "NOREV ";
278 } else {
279 command += "REV " + QByteArray::number(item.revision()) + ' ';
280 }
281
282 if (item.d_func()->mSizeChanged) {
283 command += "SIZE " + QByteArray::number(item.size());
284 }
285
286 command += " (" + ImapParser::join(changes, " ");
287 const QByteArray attrs = ProtocolHelper::attributesToByteArray(item, true);
288 if (!attrs.isEmpty()) {
289 command += ' ' + attrs;
290 }
291 return command;
292}
293
294void ItemModifyJob::doStart()
295{
296 Q_D(ItemModifyJob);
297
298 QByteArray command;
299 try {
300 command = d->fullCommand();
301 } catch (const Exception &e) {
302 setError(Job::Unknown);
303 setErrorText(QString::fromUtf8(e.what()));
304 emitResult();
305 return;
306 }
307 if (command.isEmpty()) {
308 emitResult();
309 return;
310 }
311
312 d->mTag = d->newTag();
313 command.prepend(d->mTag);
314
315 command += d->nextPartHeader();
316
317 d->writeData(command);
318 d->newTag(); // hack to circumvent automatic response handling
319}
320
321void ItemModifyJob::doHandleResponse(const QByteArray &_tag, const QByteArray &data)
322{
323 Q_D(ItemModifyJob);
324
325 if (_tag == "+") { // ready for literal data
326 if (data.startsWith("STREAM")) {
327 QByteArray error;
328 if (!ProtocolHelper::streamPayloadToFile(data, d->mPendingData, error)) {
329 d->writeData("* NO " + error);
330 return;
331 }
332 } else {
333 d->writeData(d->mPendingData);
334 }
335 d->writeData(d->nextPartHeader());
336 return;
337 }
338
339 if (_tag == d->mTag) {
340 if (data.startsWith("OK")) { //krazy:exclude=strings
341 QDateTime modificationDateTime;
342 int dateTimePos = data.indexOf("DATETIME");
343 if (dateTimePos != -1) {
344 int resultPos = ImapParser::parseDateTime(data, modificationDateTime, dateTimePos + 8);
345 if (resultPos == (dateTimePos + 8)) {
346 kDebug() << "Invalid DATETIME response to STORE command: " << _tag << data;
347 }
348 }
349
350 Item &item = d->mItems.first();
351 item.setModificationTime(modificationDateTime);
352 item.d_ptr->resetChangeLog();
353 } else {
354 setError(Unknown);
355 setErrorText(QString::fromUtf8(data));
356
357 if (data.contains("[LLCONFLICT]")) {
358 if (d->mAutomaticConflictHandlingEnabled) {
359 ConflictHandler *handler = new ConflictHandler(ConflictHandler::LocalLocalConflict, this);
360 handler->setConflictingItems(d->mItems.first(), d->mItems.first());
361 connect(handler, SIGNAL(conflictResolved()), SLOT(conflictResolved()));
362 connect(handler, SIGNAL(error(QString)), SLOT(conflictResolveError(QString)));
363
364 QMetaObject::invokeMethod(handler, "start", Qt::QueuedConnection);
365 return;
366 }
367 }
368 }
369
370 foreach (const Item &item, d->mItems) {
371 ChangeMediator::invalidateItem(item);
372 }
373
374 emitResult();
375 return;
376 }
377
378 if (_tag == "*") {
379 Akonadi::Item::Id id;
380 ImapParser::parseNumber(data, id);
381 int pos = data.indexOf('(');
382 if (pos <= 0 || id <= 0) {
383 kDebug() << "Ignoring strange response: " << _tag << data;
384 return;
385 }
386 Item::List::iterator it = std::find_if(d->mItems.begin(), d->mItems.end(), boost::bind(&Item::id, _1) == id);
387 if (it == d->mItems.end()) {
388 kDebug() << "Received STORE response for an item we did not modify: " << _tag << data;
389 return;
390 }
391 QList<QByteArray> attrs;
392 ImapParser::parseParenthesizedList(data, attrs, pos);
393 for (int i = 0; i < attrs.size() - 1; i += 2) {
394 const QByteArray key = attrs.at(i);
395 if (key == "REV") {
396 const int newRev = attrs.at(i + 1).toInt();
397 const int oldRev = (*it).revision();
398 if (newRev < oldRev || newRev < 0) {
399 continue;
400 }
401 d->itemRevisionChanged((*it).id(), oldRev, newRev);
402 (*it).setRevision(newRev);
403 }
404 }
405 return;
406 }
407
408 kDebug() << "Unhandled response: " << _tag << data;
409}
410
411void ItemModifyJob::setIgnorePayload(bool ignore)
412{
413 Q_D(ItemModifyJob);
414
415 if (d->mIgnorePayload == ignore) {
416 return;
417 }
418
419 d->mIgnorePayload = ignore;
420 if (d->mIgnorePayload) {
421 d->mParts = QSet<QByteArray>();
422 } else {
423 Q_ASSERT(!d->mItems.first().mimeType().isEmpty());
424 d->mParts = d->mItems.first().loadedPayloadParts();
425 }
426}
427
428bool ItemModifyJob::ignorePayload() const
429{
430 Q_D(const ItemModifyJob);
431
432 return d->mIgnorePayload;
433}
434
435void ItemModifyJob::setUpdateGid(bool update)
436{
437 Q_D(ItemModifyJob);
438 if (update && !updateGid()) {
439 d->mOperations.insert(ItemModifyJobPrivate::Gid);
440 } else {
441 d->mOperations.remove(ItemModifyJobPrivate::Gid);
442 }
443}
444
445bool ItemModifyJob::updateGid() const
446{
447 Q_D(const ItemModifyJob);
448 return d->mOperations.contains(ItemModifyJobPrivate::Gid);
449}
450
451void ItemModifyJob::disableRevisionCheck()
452{
453 Q_D(ItemModifyJob);
454
455 d->mRevCheck = false;
456}
457
458void ItemModifyJob::disableAutomaticConflictHandling()
459{
460 Q_D(ItemModifyJob);
461
462 d->mAutomaticConflictHandlingEnabled = false;
463}
464
465Item ItemModifyJob::item() const
466{
467 Q_D(const ItemModifyJob);
468 Q_ASSERT(d->mItems.size() == 1);
469
470 return d->mItems.first();
471}
472
473Item::List ItemModifyJob::items() const
474{
475 Q_D(const ItemModifyJob);
476 return d->mItems;
477}
478
479#include "moc_itemmodifyjob.cpp"
Akonadi::ConflictHandler
A class to handle conflicts in Akonadi.
Definition conflicthandler_p.h:40
Akonadi::ConflictHandler::setConflictingItems
void setConflictingItems(const Akonadi::Item &changedItem, const Akonadi::Item &conflictingItem)
Sets the items that causes the conflict.
Definition conflicthandler.cpp:41
Akonadi::ConflictHandler::LocalLocalConflict
@ LocalLocalConflict
Changes of two Akonadi client applications conflict.
Definition conflicthandler_p.h:49
Akonadi::Exception
Base class for exceptions used by the Akonadi library.
Definition exception.h:36
Akonadi::Exception::what
const char * what() const
Returns the error message associated with this exception.
Definition exception.cpp:94
Akonadi::GidExtractor::getGid
static QString getGid(const Item &item)
Extracts the gid from item.
Definition gidextractor.cpp:39
Akonadi::ItemModifyJobPrivate
Definition itemmodifyjob_p.h:31
Akonadi::ItemModifyJob
Job that modifies an existing item in the Akonadi storage.
Definition itemmodifyjob.h:98
Akonadi::ItemModifyJob::setIgnorePayload
void setIgnorePayload(bool ignore)
Sets whether the payload of the modified item shall be omitted from transmission to the Akonadi stora...
Definition itemmodifyjob.cpp:411
Akonadi::ItemModifyJob::disableRevisionCheck
void disableRevisionCheck()
Disables the check of the revision number.
Definition itemmodifyjob.cpp:451
Akonadi::ItemModifyJob::ItemModifyJob
ItemModifyJob(const Item &item, QObject *parent=0)
Creates a new item modify job.
Definition itemmodifyjob.cpp:160
Akonadi::ItemModifyJob::~ItemModifyJob
virtual ~ItemModifyJob()
Destroys the item modify job.
Definition itemmodifyjob.cpp:190
Akonadi::ItemModifyJob::updateGid
bool updateGid() const
Returns wheter the GID should be updated.
Definition itemmodifyjob.cpp:445
Akonadi::ItemModifyJob::doHandleResponse
virtual void doHandleResponse(const QByteArray &tag, const QByteArray &data)
This method should be reimplemented in the concrete jobs in case you want to handle incoming data.
Definition itemmodifyjob.cpp:321
Akonadi::ItemModifyJob::ignorePayload
bool ignorePayload() const
Returns whether the payload of the modified item shall be omitted from transmission to the Akonadi st...
Definition itemmodifyjob.cpp:428
Akonadi::ItemModifyJob::disableAutomaticConflictHandling
void disableAutomaticConflictHandling()
Disables the automatic handling of conflicts.
Definition itemmodifyjob.cpp:458
Akonadi::ItemModifyJob::item
Item item() const
Returns the modified and stored item including the changed revision number.
Definition itemmodifyjob.cpp:465
Akonadi::ItemModifyJob::doStart
virtual void doStart()
This method must be reimplemented in the concrete jobs.
Definition itemmodifyjob.cpp:294
Akonadi::ItemModifyJob::setUpdateGid
void setUpdateGid(bool update)
Sets whether the GID shall be updated either from the gid parameter or by extracting it from the payl...
Definition itemmodifyjob.cpp:435
Akonadi::ItemModifyJob::items
Item::List items() const
Returns the modified and stored items including the changed revision number.
Definition itemmodifyjob.cpp:473
Akonadi::ItemSerializer::serialize
static void serialize(const Item &item, const QByteArray &label, QByteArray &data, int &version)
throws ItemSerializerException on failure
Definition itemserializer.cpp:116
Akonadi::JobPrivate
Definition job_p.h:32
Akonadi::Job
Base class for all actions in the Akonadi storage.
Definition job.h:87
Akonadi::Job::Unknown
@ Unknown
Unknown error.
Definition job.h:108
Akonadi::ProtocolHelper::attributesToByteArray
static QByteArray attributesToByteArray(const Entity &entity, bool ns=false)
Convert attributes to their protocol representation.
Definition protocolhelper.cpp:232
Akonadi::ProtocolHelper::encodePartIdentifier
static QByteArray encodePartIdentifier(PartNamespace ns, const QByteArray &label, int version=0)
Encodes part label and namespace.
Definition protocolhelper.cpp:252
Akonadi::ProtocolHelper::entitySetToByteArray
static QByteArray entitySetToByteArray(const QList< T > &_objects, const QByteArray &command)
Converts the given set of items into a protocol representation.
Definition protocolhelper_p.h:125
Akonadi::Tag
An Akonadi Tag.
Definition tag.h:44
Akonadi
FreeBusyManager::Singleton.
Definition actionstatemanager_p.h:28
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Wed Jan 24 2024 00:00:00 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Related Pages

kdepimlibs-4.14.10 API Reference

Skip menu "kdepimlibs-4.14.10 API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal