• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.14.3 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 
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.first().id() >= 0) {
123  c += "TAGS";
124  c += ' ' + ProtocolHelper::tagSetToImapSequenceSet(tags);
125  } else if (std::find_if(tags.constBegin(), tags.constEnd(),
126  boost::bind(&QByteArray::isEmpty, boost::bind(&Tag::remoteId, _1)))
127  == tags.constEnd()) {
128  //All items have a remoteId
129  c += "RTAGS";
130  QList<QByteArray> rids;
131  Q_FOREACH (const Tag &object, tags) {
132  rids << ImapParser::quote(object.remoteId());
133  }
134 
135  c += '(';
136  c += ImapParser::join(rids, " ");
137  c += ')';
138 
139  } else if (std::find_if(tags.constBegin(), tags.constEnd(),
140  boost::bind(&QByteArray::isEmpty, boost::bind(&Tag::gid, _1)))
141  == tags.constEnd()) {
142  //All items have a gid
143  c += "GTAGS";
144  QList<QByteArray> gids;
145  Q_FOREACH (const Tag &object, tags) {
146  gids << ImapParser::quote(object.gid());
147  }
148 
149  c += '(';
150  c += ImapParser::join(gids, " ");
151  c += ')';
152  } else {
153  throw Exception("Cannot identify all tags");
154  }
155  return c;
156 }
157 
158 ItemModifyJob::ItemModifyJob(const Item &item, QObject *parent)
159  : Job(new ItemModifyJobPrivate(this), parent)
160 {
161  Q_D(ItemModifyJob);
162 
163  d->mItems.append(item);
164  d->mParts = item.loadedPayloadParts();
165 
166  d->mOperations.insert(ItemModifyJobPrivate::RemoteId);
167  d->mOperations.insert(ItemModifyJobPrivate::RemoteRevision);
168 }
169 
170 ItemModifyJob::ItemModifyJob(const Akonadi::Item::List &items, QObject *parent)
171  : Job(new ItemModifyJobPrivate(this), parent)
172 {
173  Q_ASSERT(!items.isEmpty());
174  Q_D(ItemModifyJob);
175  d->mItems = items;
176 
177  // same as single item ctor
178  if (d->mItems.size() == 1) {
179  d->mParts = items.first().loadedPayloadParts();
180  d->mOperations.insert(ItemModifyJobPrivate::RemoteId);
181  d->mOperations.insert(ItemModifyJobPrivate::RemoteRevision);
182  } else {
183  d->mIgnorePayload = true;
184  d->mRevCheck = false;
185  }
186 }
187 
188 ItemModifyJob::~ItemModifyJob()
189 {
190 }
191 
192 QByteArray ItemModifyJobPrivate::fullCommand() const
193 {
194  const Akonadi::Item item = mItems.first();
195  QList<QByteArray> changes;
196  foreach (int op, mOperations) {
197  switch (op) {
198  case ItemModifyJobPrivate::RemoteId:
199  if (!item.remoteId().isNull()) {
200  changes << "REMOTEID";
201  changes << ImapParser::quote(item.remoteId().toUtf8());
202  }
203  break;
204  case ItemModifyJobPrivate::Gid: {
205  const QString gid = GidExtractor::getGid(item);
206  if (!gid.isNull()) {
207  changes << "GID";
208  changes << ImapParser::quote(gid.toUtf8());
209  }
210  break;
211  }
212  case ItemModifyJobPrivate::RemoteRevision:
213  if (!item.remoteRevision().isNull()) {
214  changes << "REMOTEREVISION";
215  changes << ImapParser::quote(item.remoteRevision().toUtf8());
216  }
217  break;
218  case ItemModifyJobPrivate::Dirty:
219  changes << "DIRTY";
220  changes << "false";
221  break;
222  }
223  }
224 
225  if (item.d_func()->mClearPayload) {
226  changes << "INVALIDATECACHE";
227  }
228  if ( mSilent ) {
229  changes << "SILENT";
230  }
231 
232  if (item.d_func()->mFlagsOverwritten) {
233  changes << "FLAGS";
234  changes << '(' + ImapParser::join(item.flags(), " ") + ')';
235  } else {
236  if (!item.d_func()->mAddedFlags.isEmpty()) {
237  changes << "+FLAGS";
238  changes << '(' + ImapParser::join(item.d_func()->mAddedFlags, " ") + ')';
239  }
240  if (!item.d_func()->mDeletedFlags.isEmpty()) {
241  changes << "-FLAGS";
242  changes << '(' + ImapParser::join(item.d_func()->mDeletedFlags, " ") + ')';
243  }
244  }
245 
246  if (item.d_func()->mTagsOverwritten) {
247  changes << tagsToCommandParameter(item.tags());
248  } else {
249  if (!item.d_func()->mAddedTags.isEmpty()) {
250  changes << "+" + tagsToCommandParameter(item.d_func()->mAddedTags);
251  }
252  if (!item.d_func()->mDeletedTags.isEmpty()) {
253  changes << "-" + tagsToCommandParameter(item.d_func()->mDeletedTags);
254  }
255  }
256 
257  if (!item.d_func()->mDeletedAttributes.isEmpty()) {
258  changes << "-PARTS";
259  QList<QByteArray> attrs;
260  foreach (const QByteArray &attr, item.d_func()->mDeletedAttributes) {
261  attrs << ProtocolHelper::encodePartIdentifier(ProtocolHelper::PartAttribute, attr);
262  }
263  changes << '(' + ImapParser::join(attrs, " ") + ')';
264  }
265 
266  // nothing to do
267  if (changes.isEmpty() && mParts.isEmpty() && item.attributes().isEmpty()) {
268  return QByteArray();
269  }
270 
271  QByteArray command;
272  command += ProtocolHelper::entitySetToByteArray(mItems, "STORE"); // can throw an exception
273  command += ' ';
274  if (!mRevCheck || item.revision() < 0) {
275  command += "NOREV ";
276  } else {
277  command += "REV " + QByteArray::number(item.revision()) + ' ';
278  }
279 
280  if (item.d_func()->mSizeChanged) {
281  command += "SIZE " + QByteArray::number(item.size());
282  }
283 
284  command += " (" + ImapParser::join(changes, " ");
285  const QByteArray attrs = ProtocolHelper::attributesToByteArray(item, true);
286  if (!attrs.isEmpty()) {
287  command += ' ' + attrs;
288  }
289  return command;
290 }
291 
292 void ItemModifyJob::doStart()
293 {
294  Q_D(ItemModifyJob);
295 
296  QByteArray command;
297  try {
298  command = d->fullCommand();
299  } catch (const Exception &e) {
300  setError(Job::Unknown);
301  setErrorText(QString::fromUtf8(e.what()));
302  emitResult();
303  return;
304  }
305  if (command.isEmpty()) {
306  emitResult();
307  return;
308  }
309 
310  d->mTag = d->newTag();
311  command.prepend(d->mTag);
312 
313  command += d->nextPartHeader();
314 
315  d->writeData(command);
316  d->newTag(); // hack to circumvent automatic response handling
317 }
318 
319 void ItemModifyJob::doHandleResponse(const QByteArray &_tag, const QByteArray &data)
320 {
321  Q_D(ItemModifyJob);
322 
323  if (_tag == "+") { // ready for literal data
324  if (data.startsWith("STREAM")) {
325  QByteArray error;
326  if (!ProtocolHelper::streamPayloadToFile(data, d->mPendingData, error)) {
327  d->writeData("* NO " + error);
328  return;
329  }
330  } else {
331  d->writeData(d->mPendingData);
332  }
333  d->writeData(d->nextPartHeader());
334  return;
335  }
336 
337  if (_tag == d->mTag) {
338  if (data.startsWith("OK")) { //krazy:exclude=strings
339  QDateTime modificationDateTime;
340  int dateTimePos = data.indexOf("DATETIME");
341  if (dateTimePos != -1) {
342  int resultPos = ImapParser::parseDateTime(data, modificationDateTime, dateTimePos + 8);
343  if (resultPos == (dateTimePos + 8)) {
344  kDebug() << "Invalid DATETIME response to STORE command: " << _tag << data;
345  }
346  }
347 
348  Item &item = d->mItems.first();
349  item.setModificationTime(modificationDateTime);
350  item.d_ptr->resetChangeLog();
351  } else {
352  setError(Unknown);
353  setErrorText(QString::fromUtf8(data));
354 
355  if (data.contains("[LLCONFLICT]")) {
356  if (d->mAutomaticConflictHandlingEnabled) {
357  ConflictHandler *handler = new ConflictHandler(ConflictHandler::LocalLocalConflict, this);
358  handler->setConflictingItems(d->mItems.first(), d->mItems.first());
359  connect(handler, SIGNAL(conflictResolved()), SLOT(conflictResolved()));
360  connect(handler, SIGNAL(error(QString)), SLOT(conflictResolveError(QString)));
361 
362  QMetaObject::invokeMethod(handler, "start", Qt::QueuedConnection);
363  return;
364  }
365  }
366  }
367 
368  foreach (const Item &item, d->mItems) {
369  ChangeMediator::invalidateItem(item);
370  }
371 
372  emitResult();
373  return;
374  }
375 
376  if (_tag == "*") {
377  Akonadi::Item::Id id;
378  ImapParser::parseNumber(data, id);
379  int pos = data.indexOf('(');
380  if (pos <= 0 || id <= 0) {
381  kDebug() << "Ignoring strange response: " << _tag << data;
382  return;
383  }
384  Item::List::iterator it = std::find_if(d->mItems.begin(), d->mItems.end(), boost::bind(&Item::id, _1) == id);
385  if (it == d->mItems.end()) {
386  kDebug() << "Received STORE response for an item we did not modify: " << _tag << data;
387  return;
388  }
389  QList<QByteArray> attrs;
390  ImapParser::parseParenthesizedList(data, attrs, pos);
391  for (int i = 0; i < attrs.size() - 1; i += 2) {
392  const QByteArray key = attrs.at(i);
393  if (key == "REV") {
394  const int newRev = attrs.at(i + 1).toInt();
395  const int oldRev = (*it).revision();
396  if (newRev < oldRev || newRev < 0) {
397  continue;
398  }
399  d->itemRevisionChanged((*it).id(), oldRev, newRev);
400  (*it).setRevision(newRev);
401  }
402  }
403  return;
404  }
405 
406  kDebug() << "Unhandled response: " << _tag << data;
407 }
408 
409 void ItemModifyJob::setIgnorePayload(bool ignore)
410 {
411  Q_D(ItemModifyJob);
412 
413  if (d->mIgnorePayload == ignore) {
414  return;
415  }
416 
417  d->mIgnorePayload = ignore;
418  if (d->mIgnorePayload) {
419  d->mParts = QSet<QByteArray>();
420  } else {
421  Q_ASSERT(!d->mItems.first().mimeType().isEmpty());
422  d->mParts = d->mItems.first().loadedPayloadParts();
423  }
424 }
425 
426 bool ItemModifyJob::ignorePayload() const
427 {
428  Q_D(const ItemModifyJob);
429 
430  return d->mIgnorePayload;
431 }
432 
433 void ItemModifyJob::setUpdateGid(bool update)
434 {
435  Q_D(ItemModifyJob);
436  if (update && !updateGid()) {
437  d->mOperations.insert(ItemModifyJobPrivate::Gid);
438  } else {
439  d->mOperations.remove(ItemModifyJobPrivate::Gid);
440  }
441 }
442 
443 bool ItemModifyJob::updateGid() const
444 {
445  Q_D(const ItemModifyJob);
446  return d->mOperations.contains(ItemModifyJobPrivate::Gid);
447 }
448 
449 void ItemModifyJob::disableRevisionCheck()
450 {
451  Q_D(ItemModifyJob);
452 
453  d->mRevCheck = false;
454 }
455 
456 void ItemModifyJob::disableAutomaticConflictHandling()
457 {
458  Q_D(ItemModifyJob);
459 
460  d->mAutomaticConflictHandlingEnabled = false;
461 }
462 
463 Item ItemModifyJob::item() const
464 {
465  Q_D(const ItemModifyJob);
466  Q_ASSERT(d->mItems.size() == 1);
467 
468  return d->mItems.first();
469 }
470 
471 Item::List ItemModifyJob::items() const
472 {
473  Q_D(const ItemModifyJob);
474  return d->mItems;
475 }
476 
477 #include "moc_itemmodifyjob.cpp"
Akonadi::ItemModifyJob::~ItemModifyJob
virtual ~ItemModifyJob()
Destroys the item modify job.
Definition: itemmodifyjob.cpp:188
Akonadi::ItemModifyJob::doStart
virtual void doStart()
This method must be reimplemented in the concrete jobs.
Definition: itemmodifyjob.cpp:292
Akonadi::ItemModifyJob::disableRevisionCheck
void disableRevisionCheck()
Disables the check of the revision number.
Definition: itemmodifyjob.cpp:449
Akonadi::ItemModifyJob::updateGid
bool updateGid() const
Returns wheter the GID should be updated.
Definition: itemmodifyjob.cpp:443
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:433
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
Akonadi::ItemModifyJob::item
Item item() const
Returns the modified and stored item including the changed revision number.
Definition: itemmodifyjob.cpp:463
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:471
Akonadi::Job
Base class for all actions in the Akonadi storage.
Definition: job.h:86
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
Akonadi::ProtocolHelper::encodePartIdentifier
static QByteArray encodePartIdentifier(PartNamespace ns, const QByteArray &label, int version=0)
Encodes part label and namespace.
Definition: protocolhelper.cpp:252
Akonadi::GidExtractor::getGid
static QString getGid(const Item &item)
Extracts the gid from item.
Definition: gidextractor.cpp:39
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:409
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:426
Akonadi
FreeBusyManager::Singleton.
Definition: actionstatemanager_p.h:28
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
Akonadi::Tag
An Akonadi Tag.
Definition: tag.h:43
Akonadi::ItemModifyJob::disableAutomaticConflictHandling
void disableAutomaticConflictHandling()
Disables the automatic handling of conflicts.
Definition: itemmodifyjob.cpp:456
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:319
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:158
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::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
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Thu Nov 13 2014 10:11:03 by doxygen 1.8.8 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-4.14.3 API Reference

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