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"
36 using namespace Akonadi;
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();
58 mParts.remove( label );
64 if ( mPendingData.size() > 0 ) {
65 command +=
" {" + QByteArray::number( mPendingData.size() ) +
"}\n";
67 if ( mPendingData.isNull() )
71 command += nextPartHeader();
79 void ItemModifyJobPrivate::conflictResolved()
83 q->setError( KJob::NoError );
84 q->setErrorText( QString() );
88 void ItemModifyJobPrivate::conflictResolveError(
const QString &message )
92 q->setErrorText( q->errorText() + message );
96 void ItemModifyJobPrivate::doUpdateItemRevision( Akonadi::Item::Id itemId,
int oldRevision,
int newRevision )
98 Item::List::iterator it = std::find_if( mItems.begin(), mItems.end(), boost::bind( &Item::id, _1 ) == itemId );
99 if ( it != mItems.end() && (*it).revision() == oldRevision )
100 (*it).setRevision( newRevision );
103 QString ItemModifyJobPrivate::jobDebuggingString()
const
106 return QString::fromUtf8( fullCommand() );
108 return QString::fromUtf8( e.
what() );
112 void ItemModifyJobPrivate::setSilent(
bool silent )
122 d->mItems.append( item );
123 d->mParts = item.loadedPayloadParts();
125 d->mOperations.insert( ItemModifyJobPrivate::RemoteId );
126 d->mOperations.insert( ItemModifyJobPrivate::RemoteRevision );
132 Q_ASSERT( !items.isEmpty() );
137 if ( d->mItems.size() == 1 ) {
138 d->mParts = items.first().loadedPayloadParts();
139 d->mOperations.insert( ItemModifyJobPrivate::RemoteId );
140 d->mOperations.insert( ItemModifyJobPrivate::RemoteRevision );
142 d->mIgnorePayload =
true;
143 d->mRevCheck =
false;
151 QByteArray ItemModifyJobPrivate::fullCommand()
const
153 const Akonadi::Item item = mItems.first();
154 QList<QByteArray> changes;
155 foreach (
int op, mOperations ) {
157 case ItemModifyJobPrivate::RemoteId:
158 if ( !item.remoteId().isNull() ) {
159 changes <<
"REMOTEID";
160 changes << ImapParser::quote( item.remoteId().toUtf8() );
163 case ItemModifyJobPrivate::Gid: {
165 if ( !gid.isNull() ) {
167 changes << ImapParser::quote( gid.toUtf8() );
171 case ItemModifyJobPrivate::RemoteRevision:
172 if ( !item.remoteRevision().isNull() ) {
173 changes <<
"REMOTEREVISION";
174 changes << ImapParser::quote( item.remoteRevision().toUtf8() );
177 case ItemModifyJobPrivate::Dirty:
184 if ( item.d_func()->mClearPayload )
185 changes <<
"INVALIDATECACHE";
191 if ( item.d_func()->mFlagsOverwritten ) {
193 changes <<
'(' + ImapParser::join( item.flags(),
" " ) +
')';
195 if ( !item.d_func()->mAddedFlags.isEmpty() ) {
197 changes <<
'(' + ImapParser::join( item.d_func()->mAddedFlags,
" " ) +
')';
199 if ( !item.d_func()->mDeletedFlags.isEmpty() ) {
201 changes <<
'(' + ImapParser::join( item.d_func()->mDeletedFlags,
" " ) +
')';
205 if ( item.d_func()->mTagsOverwritten ) {
207 changes <<
' ' + ProtocolHelper::tagSetToImapSequenceSet( item.tags() );
209 if ( !item.d_func()->mAddedTags.isEmpty() ) {
211 changes <<
' ' + ProtocolHelper::tagSetToImapSequenceSet( item.d_func()->mAddedTags );
213 if ( !item.d_func()->mDeletedTags.isEmpty() ) {
215 changes <<
' ' + ProtocolHelper::tagSetToImapSequenceSet( item.d_func()->mDeletedTags );
219 if ( !item.d_func()->mDeletedAttributes.isEmpty() ) {
221 QList<QByteArray> attrs;
222 foreach (
const QByteArray &attr, item.d_func()->mDeletedAttributes )
224 changes <<
'(' + ImapParser::join( attrs,
" " ) +
')';
228 if ( changes.isEmpty() && mParts.isEmpty() && item.attributes().isEmpty() ) {
235 if ( !mRevCheck || item.revision() < 0 ) {
238 command +=
"REV " + QByteArray::number( item.revision() ) +
' ';
241 if ( item.d_func()->mSizeChanged )
242 command +=
"SIZE " + QByteArray::number( item.size() );
244 command +=
" (" + ImapParser::join( changes,
" " );
246 if ( !attrs.isEmpty() )
247 command +=
' ' + attrs;
257 command = d->fullCommand();
260 setErrorText( QString::fromUtf8( e.
what() ) );
264 if ( command.isEmpty() ) {
269 d->mTag = d->newTag();
270 command.prepend( d->mTag );
272 command += d->nextPartHeader();
274 d->writeData( command );
283 d->writeData( d->mPendingData );
284 d->writeData( d->nextPartHeader() );
288 if ( _tag == d->mTag ) {
289 if ( data.startsWith(
"OK" ) ) {
290 QDateTime modificationDateTime;
291 int dateTimePos = data.indexOf(
"DATETIME" );
292 if ( dateTimePos != -1 ) {
293 int resultPos = ImapParser::parseDateTime( data, modificationDateTime, dateTimePos + 8 );
294 if ( resultPos == (dateTimePos + 8) ) {
295 kDebug() <<
"Invalid DATETIME response to STORE command: " << _tag << data;
299 Item &item = d->mItems.first();
300 item.setModificationTime( modificationDateTime );
301 item.d_ptr->resetChangeLog();
304 setErrorText( QString::fromUtf8( data ) );
306 if ( data.contains(
"[LLCONFLICT]" ) ) {
307 if ( d->mAutomaticConflictHandlingEnabled ) {
310 connect( handler, SIGNAL(conflictResolved()), SLOT(conflictResolved()) );
311 connect( handler, SIGNAL(error(QString)), SLOT(conflictResolveError(QString)) );
313 QMetaObject::invokeMethod( handler,
"start", Qt::QueuedConnection );
319 foreach (
const Item &item, d->mItems ) {
320 ChangeMediator::invalidateItem(item);
328 Akonadi::Item::Id id;
329 ImapParser::parseNumber( data,
id );
330 int pos = data.indexOf(
'(' );
331 if ( pos <= 0 ||
id <= 0 ) {
332 kDebug() <<
"Ignoring strange response: " << _tag << data;
335 Item::List::iterator it = std::find_if( d->mItems.begin(), d->mItems.end(), boost::bind( &Item::id, _1 ) == id );
336 if ( it == d->mItems.end() ) {
337 kDebug() <<
"Received STORE response for an item we did not modify: " << _tag << data;
340 QList<QByteArray> attrs;
341 ImapParser::parseParenthesizedList( data, attrs, pos );
342 for (
int i = 0; i < attrs.size() - 1; i += 2 ) {
343 const QByteArray key = attrs.at( i );
344 if ( key ==
"REV" ) {
345 const int newRev = attrs.at( i + 1 ).toInt();
346 const int oldRev = (*it).revision();
347 if ( newRev < oldRev || newRev < 0 )
349 d->itemRevisionChanged( (*it).id(), oldRev, newRev );
350 (*it).setRevision( newRev );
356 kDebug() <<
"Unhandled response: " << _tag << data;
363 if ( d->mIgnorePayload == ignore )
366 d->mIgnorePayload = ignore;
367 if ( d->mIgnorePayload )
368 d->mParts = QSet<QByteArray>();
370 Q_ASSERT( !d->mItems.first().mimeType().isEmpty() );
371 d->mParts = d->mItems.first().loadedPayloadParts();
379 return d->mIgnorePayload;
386 d->mOperations.insert( ItemModifyJobPrivate::Gid );
388 d->mOperations.remove( ItemModifyJobPrivate::Gid );
395 return d->mOperations.contains( ItemModifyJobPrivate::Gid );
402 d->mRevCheck =
false;
409 d->mAutomaticConflictHandlingEnabled =
false;
415 Q_ASSERT( d->mItems.size() == 1 );
417 return d->mItems.first();
426 #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...
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.