20#include "itemmodifyjob.h"
21#include "itemmodifyjob_p.h"
23#include "changemediator_p.h"
24#include "collection.h"
25#include "conflicthandling/conflicthandler_p.h"
27#include "imapparser_p.h"
29#include "itemserializer_p.h"
31#include "protocolhelper_p.h"
32#include "gid/gidextractor_p.h"
38ItemModifyJobPrivate::ItemModifyJobPrivate(
ItemModifyJob *parent)
41 , mIgnorePayload(false)
42 , mAutomaticConflictHandlingEnabled(true)
47void ItemModifyJobPrivate::setClean()
49 mOperations.insert(Dirty);
52QByteArray ItemModifyJobPrivate::nextPartHeader()
55 if (!mParts.isEmpty()) {
56 QSetIterator<QByteArray> it(mParts);
57 const QByteArray label = it.next();
64 if (mPendingData.size() > 0) {
65 command +=
" {" + QByteArray::number(mPendingData.size()) +
"}\n";
67 if (mPendingData.isNull()) {
72 command += nextPartHeader();
80void ItemModifyJobPrivate::conflictResolved()
84 q->setError(KJob::NoError);
85 q->setErrorText(QString());
89void ItemModifyJobPrivate::conflictResolveError(
const QString &message)
93 q->setErrorText(q->errorText() + message);
97void ItemModifyJobPrivate::doUpdateItemRevision(Akonadi::Item::Id itemId,
int oldRevision,
int newRevision)
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);
105QString ItemModifyJobPrivate::jobDebuggingString()
const
108 return QString::fromUtf8(fullCommand());
110 return QString::fromUtf8(e.
what());
114void ItemModifyJobPrivate::setSilent(
bool silent )
119QByteArray ItemModifyJobPrivate::tagsToCommandParameter(
const Tag::List &tags)
const
122 if (tags.isEmpty()) {
123 qWarning() <<
"Missing implemented method";
124 }
else if (tags.first().id() >= 0) {
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()) {
132 QList<QByteArray> rids;
133 Q_FOREACH (
const Tag &
object, tags) {
134 rids << ImapParser::quote(
object.remoteId());
138 c += ImapParser::join(rids,
" ");
141 }
else if (std::find_if(tags.constBegin(), tags.constEnd(),
142 boost::bind(&QByteArray::isEmpty, boost::bind(&Tag::gid, _1)))
143 == tags.constEnd()) {
146 QList<QByteArray> gids;
147 Q_FOREACH (
const Tag &
object, tags) {
148 gids << ImapParser::quote(
object.gid());
152 c += ImapParser::join(gids,
" ");
155 throw Exception(
"Cannot identify all tags");
165 d->mItems.append(
item);
166 d->mParts =
item.loadedPayloadParts();
168 d->mOperations.insert(ItemModifyJobPrivate::RemoteId);
169 d->mOperations.insert(ItemModifyJobPrivate::RemoteRevision);
175 Q_ASSERT(!
items.isEmpty());
180 if (d->mItems.size() == 1) {
181 d->mParts =
items.first().loadedPayloadParts();
182 d->mOperations.insert(ItemModifyJobPrivate::RemoteId);
183 d->mOperations.insert(ItemModifyJobPrivate::RemoteRevision);
185 d->mIgnorePayload =
true;
186 d->mRevCheck =
false;
194QByteArray ItemModifyJobPrivate::fullCommand()
const
196 const Akonadi::Item item = mItems.first();
197 QList<QByteArray> changes;
198 foreach (
int op, mOperations) {
200 case ItemModifyJobPrivate::RemoteId:
201 if (!item.remoteId().isNull()) {
202 changes <<
"REMOTEID";
203 changes << ImapParser::quote(item.remoteId().toUtf8());
206 case ItemModifyJobPrivate::Gid: {
210 changes << ImapParser::quote(gid.toUtf8());
214 case ItemModifyJobPrivate::RemoteRevision:
215 if (!item.remoteRevision().isNull()) {
216 changes <<
"REMOTEREVISION";
217 changes << ImapParser::quote(item.remoteRevision().toUtf8());
220 case ItemModifyJobPrivate::Dirty:
227 if (item.d_func()->mClearPayload) {
228 changes <<
"INVALIDATECACHE";
234 if (item.d_func()->mFlagsOverwritten) {
236 changes <<
'(' + ImapParser::join(item.flags(),
" ") +
')';
238 if (!item.d_func()->mAddedFlags.isEmpty()) {
240 changes <<
'(' + ImapParser::join(item.d_func()->mAddedFlags,
" ") +
')';
242 if (!item.d_func()->mDeletedFlags.isEmpty()) {
244 changes <<
'(' + ImapParser::join(item.d_func()->mDeletedFlags,
" ") +
')';
248 if (item.d_func()->mTagsOverwritten) {
249 changes << tagsToCommandParameter(item.tags());
251 if (!item.d_func()->mAddedTags.isEmpty()) {
252 changes <<
"+" + tagsToCommandParameter(item.d_func()->mAddedTags);
254 if (!item.d_func()->mDeletedTags.isEmpty()) {
255 changes <<
"-" + tagsToCommandParameter(item.d_func()->mDeletedTags);
259 if (!item.d_func()->mDeletedAttributes.isEmpty()) {
261 QList<QByteArray> attrs;
262 foreach (
const QByteArray &attr, item.d_func()->mDeletedAttributes) {
265 changes <<
'(' + ImapParser::join(attrs,
" ") +
')';
269 if (changes.isEmpty() && mParts.isEmpty() && item.attributes().isEmpty()) {
276 if (!mRevCheck || item.revision() < 0) {
279 command +=
"REV " + QByteArray::number(item.revision()) +
' ';
282 if (item.d_func()->mSizeChanged) {
283 command +=
"SIZE " + QByteArray::number(item.size());
286 command +=
" (" + ImapParser::join(changes,
" ");
288 if (!attrs.isEmpty()) {
289 command +=
' ' + attrs;
300 command = d->fullCommand();
303 setErrorText(QString::fromUtf8(e.
what()));
307 if (command.isEmpty()) {
312 d->mTag = d->newTag();
313 command.prepend(d->mTag);
315 command += d->nextPartHeader();
317 d->writeData(command);
326 if (data.startsWith(
"STREAM")) {
328 if (!ProtocolHelper::streamPayloadToFile(data, d->mPendingData, error)) {
329 d->writeData(
"* NO " + error);
333 d->writeData(d->mPendingData);
335 d->writeData(d->nextPartHeader());
339 if (_tag == d->mTag) {
340 if (data.startsWith(
"OK")) {
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;
350 Item &
item = d->mItems.first();
351 item.setModificationTime(modificationDateTime);
352 item.d_ptr->resetChangeLog();
355 setErrorText(QString::fromUtf8(data));
357 if (data.contains(
"[LLCONFLICT]")) {
358 if (d->mAutomaticConflictHandlingEnabled) {
361 connect(handler, SIGNAL(conflictResolved()), SLOT(conflictResolved()));
362 connect(handler, SIGNAL(error(QString)), SLOT(conflictResolveError(QString)));
364 QMetaObject::invokeMethod(handler,
"start", Qt::QueuedConnection);
370 foreach (
const Item &
item, d->mItems) {
371 ChangeMediator::invalidateItem(
item);
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;
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;
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);
396 const int newRev = attrs.at(i + 1).toInt();
397 const int oldRev = (*it).revision();
398 if (newRev < oldRev || newRev < 0) {
401 d->itemRevisionChanged((*it).id(), oldRev, newRev);
402 (*it).setRevision(newRev);
408 kDebug() <<
"Unhandled response: " << _tag << data;
415 if (d->mIgnorePayload == ignore) {
419 d->mIgnorePayload = ignore;
420 if (d->mIgnorePayload) {
421 d->mParts = QSet<QByteArray>();
423 Q_ASSERT(!d->mItems.first().mimeType().isEmpty());
424 d->mParts = d->mItems.first().loadedPayloadParts();
432 return d->mIgnorePayload;
439 d->mOperations.insert(ItemModifyJobPrivate::Gid);
441 d->mOperations.remove(ItemModifyJobPrivate::Gid);
448 return d->mOperations.contains(ItemModifyJobPrivate::Gid);
455 d->mRevCheck =
false;
462 d->mAutomaticConflictHandlingEnabled =
false;
468 Q_ASSERT(d->mItems.size() == 1);
470 return d->mItems.first();
479#include "moc_itemmodifyjob.cpp"
A class to handle conflicts in Akonadi.
void setConflictingItems(const Akonadi::Item &changedItem, const Akonadi::Item &conflictingItem)
Sets the items that causes the conflict.
@ LocalLocalConflict
Changes of two Akonadi client applications conflict.
Base class for exceptions used by the Akonadi library.
const char * what() const
Returns the error message associated with this exception.
Job that modifies an existing item in the Akonadi storage.
void setIgnorePayload(bool ignore)
Sets whether the payload of the modified item shall be omitted from transmission to the Akonadi stora...
void disableRevisionCheck()
Disables the check of the revision number.
ItemModifyJob(const Item &item, QObject *parent=0)
Creates a new item modify job.
virtual ~ItemModifyJob()
Destroys the item modify job.
bool updateGid() const
Returns wheter the GID should be updated.
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.
bool ignorePayload() const
Returns whether the payload of the modified item shall be omitted from transmission to the Akonadi st...
void disableAutomaticConflictHandling()
Disables the automatic handling of conflicts.
Item item() const
Returns the modified and stored item including the changed revision number.
virtual void doStart()
This method must be reimplemented in the concrete jobs.
void setUpdateGid(bool update)
Sets whether the GID shall be updated either from the gid parameter or by extracting it from the payl...
Item::List items() const
Returns the modified and stored items including the changed revision number.
static void serialize(const Item &item, const QByteArray &label, QByteArray &data, int &version)
throws ItemSerializerException on failure
Base class for all actions in the Akonadi storage.
static QByteArray attributesToByteArray(const Entity &entity, bool ns=false)
Convert attributes to their protocol representation.
static QByteArray encodePartIdentifier(PartNamespace ns, const QByteArray &label, int version=0)
Encodes part label and namespace.
static QByteArray entitySetToByteArray(const QList< T > &_objects, const QByteArray &command)
Converts the given set of items into a protocol representation.
FreeBusyManager::Singleton.