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

akonadi

  • sources
  • kde-4.14
  • kdepimlibs
  • 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 
36 using namespace Akonadi;
37 
38 ItemModifyJobPrivate::ItemModifyJobPrivate(ItemModifyJob *parent)
39  : JobPrivate(parent)
40  , mRevCheck(true)
41  , mIgnorePayload(false)
42  , mAutomaticConflictHandlingEnabled(true)
43  , mSilent(false)
44 {
45 }
46 
47 void ItemModifyJobPrivate::setClean()
48 {
49  mOperations.insert(Dirty);
50 }
51 
52 QByteArray 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 
80 void ItemModifyJobPrivate::conflictResolved()
81 {
82  Q_Q(ItemModifyJob);
83 
84  q->setError(KJob::NoError);
85  q->setErrorText(QString());
86  q->emitResult();
87 }
88 
89 void ItemModifyJobPrivate::conflictResolveError(const QString &message)
90 {
91  Q_Q(ItemModifyJob);
92 
93  q->setErrorText(q->errorText() + message);
94  q->emitResult();
95 }
96 
97 void 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 
105 QString 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 
114 void ItemModifyJobPrivate::setSilent( bool silent )
115 {
116  mSilent = silent;
117 }
118 
119 QByteArray 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 
160 ItemModifyJob::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 
172 ItemModifyJob::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 
190 ItemModifyJob::~ItemModifyJob()
191 {
192 }
193 
194 QByteArray 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 
294 void 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 
321 void 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 
411 void 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 
428 bool ItemModifyJob::ignorePayload() const
429 {
430  Q_D(const ItemModifyJob);
431 
432  return d->mIgnorePayload;
433 }
434 
435 void 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 
445 bool ItemModifyJob::updateGid() const
446 {
447  Q_D(const ItemModifyJob);
448  return d->mOperations.contains(ItemModifyJobPrivate::Gid);
449 }
450 
451 void ItemModifyJob::disableRevisionCheck()
452 {
453  Q_D(ItemModifyJob);
454 
455  d->mRevCheck = false;
456 }
457 
458 void ItemModifyJob::disableAutomaticConflictHandling()
459 {
460  Q_D(ItemModifyJob);
461 
462  d->mAutomaticConflictHandlingEnabled = false;
463 }
464 
465 Item ItemModifyJob::item() const
466 {
467  Q_D(const ItemModifyJob);
468  Q_ASSERT(d->mItems.size() == 1);
469 
470  return d->mItems.first();
471 }
472 
473 Item::List ItemModifyJob::items() const
474 {
475  Q_D(const ItemModifyJob);
476  return d->mItems;
477 }
478 
479 #include "moc_itemmodifyjob.cpp"
Akonadi::ItemModifyJob::~ItemModifyJob
virtual ~ItemModifyJob()
Destroys the item modify job.
Definition: itemmodifyjob.cpp:190
Akonadi::ItemModifyJob::doStart
virtual void doStart()
This method must be reimplemented in the concrete jobs.
Definition: itemmodifyjob.cpp:294
Akonadi::ItemModifyJob::disableRevisionCheck
void disableRevisionCheck()
Disables the check of the revision number.
Definition: itemmodifyjob.cpp:451
QByteArray::clear
void clear()
Akonadi::ItemModifyJob::updateGid
bool updateGid() const
Returns wheter the GID should be updated.
Definition: itemmodifyjob.cpp:445
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
QByteArray
Akonadi::Job::Unknown
Unknown error.
Definition: job.h:108
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
QList::at
const T & at(int i) const
QByteArray::isEmpty
bool isEmpty() const
Akonadi::ItemModifyJob::item
Item item() const
Returns the modified and stored item including the changed revision number.
Definition: itemmodifyjob.cpp:465
QByteArray::startsWith
bool startsWith(const QByteArray &ba) const
Akonadi::ProtocolHelper::attributesToByteArray
static QByteArray attributesToByteArray(const Entity &entity, bool ns=false)
Convert attributes to their protocol representation.
Definition: protocolhelper.cpp:232
Akonadi::ItemModifyJob::items
Item::List items() const
Returns the modified and stored items including the changed revision number.
Definition: itemmodifyjob.cpp:473
Akonadi::Job
Base class for all actions in the Akonadi storage.
Definition: job.h:86
QSet::insert
const_iterator insert(const T &value)
QList::size
int size() const
QString::isNull
bool isNull() const
QByteArray::indexOf
int indexOf(char ch, int from) const
QString::fromUtf8
QString fromUtf8(const char *str, int size)
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
Changes of two Akonadi client applications conflict.
Definition: conflicthandler_p.h:49
QObject
QByteArray::prepend
QByteArray & prepend(char ch)
QList::isEmpty
bool isEmpty() const
QByteArray::number
QByteArray number(int n, int base)
Akonadi::ProtocolHelper::encodePartIdentifier
static QByteArray encodePartIdentifier(PartNamespace ns, const QByteArray &label, int version=0)
Encodes part label and namespace.
Definition: protocolhelper.cpp:252
QSet< QByteArray >
QList::first
T & first()
QString
QList
QSetIterator
Akonadi::GidExtractor::getGid
static QString getGid(const Item &item)
Extracts the gid from item.
Definition: gidextractor.cpp:39
QMetaObject::invokeMethod
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
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
QSet::remove
bool remove(const T &value)
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::Exception
Base class for exceptions used by the Akonadi library.
Definition: exception.h:35
Akonadi::ItemModifyJob
Job that modifies an existing item in the Akonadi storage.
Definition: itemmodifyjob.h:97
Akonadi::JobPrivate
Definition: job_p.h:31
QByteArray::contains
bool contains(char ch) const
Akonadi::Tag
An Akonadi Tag.
Definition: tag.h:43
Akonadi::ItemModifyJob::disableAutomaticConflictHandling
void disableAutomaticConflictHandling()
Disables the automatic handling of conflicts.
Definition: itemmodifyjob.cpp:458
QSet::isEmpty
bool isEmpty() const
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::ConflictHandler
A class to handle conflicts in Akonadi.
Definition: conflicthandler_p.h:39
Akonadi::ItemModifyJob::ItemModifyJob
ItemModifyJob(const Item &item, QObject *parent=0)
Creates a new item modify job.
Definition: itemmodifyjob.cpp:160
Akonadi::ItemSerializer::serialize
static void serialize(const Item &item, const QByteArray &label, QByteArray &data, int &version)
throws ItemSerializerException on failure
Definition: itemserializer.cpp:116
QList::constEnd
const_iterator constEnd() const
QList::constBegin
const_iterator constBegin() const
Akonadi::ItemModifyJobPrivate
Definition: itemmodifyjob_p.h:30
Akonadi::Exception::what
const char * what() const
Returns the error message associated with this exception.
Definition: exception.cpp:94
QDateTime
QString::toUtf8
QByteArray toUtf8() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:38:03 by doxygen 1.8.7 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
  • Modules
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs 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

Search



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