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"
38 ItemModifyJobPrivate::ItemModifyJobPrivate(
ItemModifyJob *parent)
41 , mIgnorePayload(false)
42 , mAutomaticConflictHandlingEnabled(true)
47 void ItemModifyJobPrivate::setClean()
49 mOperations.insert(Dirty);
52 QByteArray 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();
80 void ItemModifyJobPrivate::conflictResolved()
84 q->setError(KJob::NoError);
85 q->setErrorText(QString());
89 void ItemModifyJobPrivate::conflictResolveError(
const QString &message)
93 q->setErrorText(q->errorText() + message);
97 void 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);
105 QString ItemModifyJobPrivate::jobDebuggingString()
const
108 return QString::fromUtf8(fullCommand());
110 return QString::fromUtf8(e.
what());
114 void ItemModifyJobPrivate::setSilent(
bool silent )
119 QByteArray ItemModifyJobPrivate::tagsToCommandParameter(
const Tag::List &tags)
const
122 if (tags.first().id() >= 0) {
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()) {
130 QList<QByteArray> rids;
131 Q_FOREACH (
const Tag &
object, tags) {
132 rids << ImapParser::quote(
object.remoteId());
136 c += ImapParser::join(rids,
" ");
139 }
else if (std::find_if(tags.constBegin(), tags.constEnd(),
140 boost::bind(&QByteArray::isEmpty, boost::bind(&Tag::gid, _1)))
141 == tags.constEnd()) {
144 QList<QByteArray> gids;
145 Q_FOREACH (
const Tag &
object, tags) {
146 gids << ImapParser::quote(
object.gid());
150 c += ImapParser::join(gids,
" ");
153 throw Exception(
"Cannot identify all tags");
163 d->mItems.append(item);
164 d->mParts = item.loadedPayloadParts();
166 d->mOperations.insert(ItemModifyJobPrivate::RemoteId);
167 d->mOperations.insert(ItemModifyJobPrivate::RemoteRevision);
173 Q_ASSERT(!items.isEmpty());
178 if (d->mItems.size() == 1) {
179 d->mParts = items.first().loadedPayloadParts();
180 d->mOperations.insert(ItemModifyJobPrivate::RemoteId);
181 d->mOperations.insert(ItemModifyJobPrivate::RemoteRevision);
183 d->mIgnorePayload =
true;
184 d->mRevCheck =
false;
192 QByteArray ItemModifyJobPrivate::fullCommand()
const
194 const Akonadi::Item item = mItems.first();
195 QList<QByteArray> changes;
196 foreach (
int op, mOperations) {
198 case ItemModifyJobPrivate::RemoteId:
199 if (!item.remoteId().isNull()) {
200 changes <<
"REMOTEID";
201 changes << ImapParser::quote(item.remoteId().toUtf8());
204 case ItemModifyJobPrivate::Gid: {
208 changes << ImapParser::quote(gid.toUtf8());
212 case ItemModifyJobPrivate::RemoteRevision:
213 if (!item.remoteRevision().isNull()) {
214 changes <<
"REMOTEREVISION";
215 changes << ImapParser::quote(item.remoteRevision().toUtf8());
218 case ItemModifyJobPrivate::Dirty:
225 if (item.d_func()->mClearPayload) {
226 changes <<
"INVALIDATECACHE";
232 if (item.d_func()->mFlagsOverwritten) {
234 changes <<
'(' + ImapParser::join(item.flags(),
" ") +
')';
236 if (!item.d_func()->mAddedFlags.isEmpty()) {
238 changes <<
'(' + ImapParser::join(item.d_func()->mAddedFlags,
" ") +
')';
240 if (!item.d_func()->mDeletedFlags.isEmpty()) {
242 changes <<
'(' + ImapParser::join(item.d_func()->mDeletedFlags,
" ") +
')';
246 if (item.d_func()->mTagsOverwritten) {
247 changes << tagsToCommandParameter(item.tags());
249 if (!item.d_func()->mAddedTags.isEmpty()) {
250 changes <<
"+" + tagsToCommandParameter(item.d_func()->mAddedTags);
252 if (!item.d_func()->mDeletedTags.isEmpty()) {
253 changes <<
"-" + tagsToCommandParameter(item.d_func()->mDeletedTags);
257 if (!item.d_func()->mDeletedAttributes.isEmpty()) {
259 QList<QByteArray> attrs;
260 foreach (
const QByteArray &attr, item.d_func()->mDeletedAttributes) {
263 changes <<
'(' + ImapParser::join(attrs,
" ") +
')';
267 if (changes.isEmpty() && mParts.isEmpty() && item.attributes().isEmpty()) {
274 if (!mRevCheck || item.revision() < 0) {
277 command +=
"REV " + QByteArray::number(item.revision()) +
' ';
280 if (item.d_func()->mSizeChanged) {
281 command +=
"SIZE " + QByteArray::number(item.size());
284 command +=
" (" + ImapParser::join(changes,
" ");
286 if (!attrs.isEmpty()) {
287 command +=
' ' + attrs;
298 command = d->fullCommand();
301 setErrorText(QString::fromUtf8(e.
what()));
305 if (command.isEmpty()) {
310 d->mTag = d->newTag();
311 command.prepend(d->mTag);
313 command += d->nextPartHeader();
315 d->writeData(command);
324 if (data.startsWith(
"STREAM")) {
326 if (!ProtocolHelper::streamPayloadToFile(data, d->mPendingData, error)) {
327 d->writeData(
"* NO " + error);
331 d->writeData(d->mPendingData);
333 d->writeData(d->nextPartHeader());
337 if (_tag == d->mTag) {
338 if (data.startsWith(
"OK")) {
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;
348 Item &item = d->mItems.first();
349 item.setModificationTime(modificationDateTime);
350 item.d_ptr->resetChangeLog();
353 setErrorText(QString::fromUtf8(data));
355 if (data.contains(
"[LLCONFLICT]")) {
356 if (d->mAutomaticConflictHandlingEnabled) {
359 connect(handler, SIGNAL(conflictResolved()), SLOT(conflictResolved()));
360 connect(handler, SIGNAL(error(QString)), SLOT(conflictResolveError(QString)));
362 QMetaObject::invokeMethod(handler,
"start", Qt::QueuedConnection);
368 foreach (
const Item &item, d->mItems) {
369 ChangeMediator::invalidateItem(item);
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;
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;
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);
394 const int newRev = attrs.at(i + 1).toInt();
395 const int oldRev = (*it).revision();
396 if (newRev < oldRev || newRev < 0) {
399 d->itemRevisionChanged((*it).id(), oldRev, newRev);
400 (*it).setRevision(newRev);
406 kDebug() <<
"Unhandled response: " << _tag << data;
413 if (d->mIgnorePayload == ignore) {
417 d->mIgnorePayload = ignore;
418 if (d->mIgnorePayload) {
419 d->mParts = QSet<QByteArray>();
421 Q_ASSERT(!d->mItems.first().mimeType().isEmpty());
422 d->mParts = d->mItems.first().loadedPayloadParts();
430 return d->mIgnorePayload;
437 d->mOperations.insert(ItemModifyJobPrivate::Gid);
439 d->mOperations.remove(ItemModifyJobPrivate::Gid);
446 return d->mOperations.contains(ItemModifyJobPrivate::Gid);
453 d->mRevCheck =
false;
460 d->mAutomaticConflictHandlingEnabled =
false;
466 Q_ASSERT(d->mItems.size() == 1);
468 return d->mItems.first();
477 #include "moc_itemmodifyjob.cpp"
virtual ~ItemModifyJob()
Destroys the item modify job.
virtual void doStart()
This method must be reimplemented in the concrete jobs.
void disableRevisionCheck()
Disables the check of the revision number.
bool updateGid() const
Returns wheter the GID should be updated.
void setUpdateGid(bool update)
Sets whether the GID shall be updated either from the gid parameter or by extracting it from the payl...
static QByteArray entitySetToByteArray(const QList< T > &_objects, const QByteArray &command)
Converts the given set of items into a protocol representation.
Item item() const
Returns the modified and stored item including the changed revision number.
static QByteArray attributesToByteArray(const Entity &entity, bool ns=false)
Convert attributes to their protocol representation.
Item::List items() const
Returns the modified and stored items including the changed revision number.
Base class for all actions in the Akonadi storage.
void setConflictingItems(const Akonadi::Item &changedItem, const Akonadi::Item &conflictingItem)
Sets the items that causes the conflict.
Changes of two Akonadi client applications conflict.
static QByteArray encodePartIdentifier(PartNamespace ns, const QByteArray &label, int version=0)
Encodes part label and namespace.
void setIgnorePayload(bool ignore)
Sets whether the payload of the modified item shall be omitted from transmission to the Akonadi stora...
bool ignorePayload() const
Returns whether the payload of the modified item shall be omitted from transmission to the Akonadi st...
FreeBusyManager::Singleton.
Base class for exceptions used by the Akonadi library.
Job that modifies an existing item in the Akonadi storage.
void disableAutomaticConflictHandling()
Disables the automatic handling of conflicts.
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...
A class to handle conflicts in Akonadi.
ItemModifyJob(const Item &item, QObject *parent=0)
Creates a new item modify job.
static void serialize(const Item &item, const QByteArray &label, QByteArray &data, int &version)
throws ItemSerializerException on failure
const char * what() const
Returns the error message associated with this exception.