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"