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.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;
194 QByteArray 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" 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.
Job(QObject *parent=0)
Creates a new job.