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 )
46 void ItemModifyJobPrivate::setClean()
48 mOperations.insert( Dirty );
51 QByteArray ItemModifyJobPrivate::nextPartHeader()
54 if ( !mParts.isEmpty() ) {
55 QSetIterator<QByteArray> it( mParts );
56 const QByteArray label = it.next();
57 mParts.remove( label );
63 if ( mPendingData.size() > 0 ) {
64 command +=
" {" + QByteArray::number( mPendingData.size() ) +
"}\n";
66 if ( mPendingData.isNull() )
70 command += nextPartHeader();
78 void ItemModifyJobPrivate::conflictResolved()
82 q->setError( KJob::NoError );
83 q->setErrorText( QString() );
87 void ItemModifyJobPrivate::conflictResolveError(
const QString &message )
91 q->setErrorText( q->errorText() + message );
95 void ItemModifyJobPrivate::doUpdateItemRevision( Akonadi::Item::Id itemId,
int oldRevision,
int newRevision )
97 Item::List::iterator it = std::find_if( mItems.begin(), mItems.end(), boost::bind( &Item::id, _1 ) == itemId );
98 if ( it != mItems.end() && (*it).revision() == oldRevision )
99 (*it).setRevision( newRevision );
102 QString ItemModifyJobPrivate::jobDebuggingString()
const
105 return QString::fromUtf8( fullCommand() );
107 return QString::fromUtf8( e.
what() );
116 d->mItems.append( item );
117 d->mParts = item.loadedPayloadParts();
119 d->mOperations.insert( ItemModifyJobPrivate::RemoteId );
120 d->mOperations.insert( ItemModifyJobPrivate::RemoteRevision );
126 Q_ASSERT( !items.isEmpty() );
131 if ( d->mItems.size() == 1 ) {
132 d->mParts = items.first().loadedPayloadParts();
133 d->mOperations.insert( ItemModifyJobPrivate::RemoteId );
134 d->mOperations.insert( ItemModifyJobPrivate::RemoteRevision );
136 d->mIgnorePayload =
true;
137 d->mRevCheck =
false;
145 QByteArray ItemModifyJobPrivate::fullCommand()
const
147 const Akonadi::Item item = mItems.first();
148 QList<QByteArray> changes;
149 foreach (
int op, mOperations ) {
151 case ItemModifyJobPrivate::RemoteId:
152 if ( !item.remoteId().isNull() ) {
153 changes <<
"REMOTEID";
154 changes << ImapParser::quote( item.remoteId().toUtf8() );
157 case ItemModifyJobPrivate::Gid: {
159 if ( !gid.isNull() ) {
161 changes << ImapParser::quote( gid.toUtf8() );
165 case ItemModifyJobPrivate::RemoteRevision:
166 if ( !item.remoteRevision().isNull() ) {
167 changes <<
"REMOTEREVISION";
168 changes << ImapParser::quote( item.remoteRevision().toUtf8() );
171 case ItemModifyJobPrivate::Dirty:
178 if ( item.d_func()->mClearPayload )
179 changes <<
"INVALIDATECACHE";
181 if ( item.d_func()->mFlagsOverwritten ) {
183 changes <<
'(' + ImapParser::join( item.flags(),
" " ) +
')';
185 if ( !item.d_func()->mAddedFlags.isEmpty() ) {
187 changes <<
'(' + ImapParser::join( item.d_func()->mAddedFlags,
" " ) +
')';
189 if ( !item.d_func()->mDeletedFlags.isEmpty() ) {
191 changes <<
'(' + ImapParser::join( item.d_func()->mDeletedFlags,
" " ) +
')';
195 if ( !item.d_func()->mDeletedAttributes.isEmpty() ) {
197 QList<QByteArray> attrs;
198 foreach (
const QByteArray &attr, item.d_func()->mDeletedAttributes )
200 changes <<
'(' + ImapParser::join( attrs,
" " ) +
')';
204 if ( changes.isEmpty() && mParts.isEmpty() && item.attributes().isEmpty() ) {
211 if ( !mRevCheck || item.revision() < 0 ) {
214 command +=
"REV " + QByteArray::number( item.revision() ) +
' ';
217 if ( item.d_func()->mSizeChanged )
218 command +=
"SIZE " + QByteArray::number( item.size() );
220 command +=
" (" + ImapParser::join( changes,
" " );
222 if ( !attrs.isEmpty() )
223 command +=
' ' + attrs;
233 command = d->fullCommand();
236 setErrorText( QString::fromUtf8( e.
what() ) );
240 if ( command.isEmpty() ) {
245 d->mTag = d->newTag();
246 command.prepend( d->mTag );
248 command += d->nextPartHeader();
250 d->writeData( command );
259 d->writeData( d->mPendingData );
260 d->writeData( d->nextPartHeader() );
264 if ( _tag == d->mTag ) {
265 if ( data.startsWith(
"OK" ) ) {
266 QDateTime modificationDateTime;
267 int dateTimePos = data.indexOf(
"DATETIME" );
268 if ( dateTimePos != -1 ) {
269 int resultPos = ImapParser::parseDateTime( data, modificationDateTime, dateTimePos + 8 );
270 if ( resultPos == (dateTimePos + 8) ) {
271 kDebug() <<
"Invalid DATETIME response to STORE command: " << _tag << data;
275 Item &item = d->mItems.first();
276 item.setModificationTime( modificationDateTime );
277 item.d_ptr->resetChangeLog();
280 setErrorText( QString::fromUtf8( data ) );
282 if ( data.contains(
"[LLCONFLICT]" ) ) {
283 if ( d->mAutomaticConflictHandlingEnabled ) {
286 connect( handler, SIGNAL(conflictResolved()), SLOT(conflictResolved()) );
287 connect( handler, SIGNAL(error(QString)), SLOT(conflictResolveError(QString)) );
289 QMetaObject::invokeMethod( handler,
"start", Qt::QueuedConnection );
295 foreach (
const Item &item, d->mItems ) {
296 ChangeMediator::invalidateItem(item);
304 Akonadi::Item::Id id;
305 ImapParser::parseNumber( data,
id );
306 int pos = data.indexOf(
'(' );
307 if ( pos <= 0 ||
id <= 0 ) {
308 kDebug() <<
"Ignoring strange response: " << _tag << data;
311 Item::List::iterator it = std::find_if( d->mItems.begin(), d->mItems.end(), boost::bind( &Item::id, _1 ) == id );
312 if ( it == d->mItems.end() ) {
313 kDebug() <<
"Received STORE response for an item we did not modify: " << _tag << data;
316 QList<QByteArray> attrs;
317 ImapParser::parseParenthesizedList( data, attrs, pos );
318 for (
int i = 0; i < attrs.size() - 1; i += 2 ) {
319 const QByteArray key = attrs.at( i );
320 if ( key ==
"REV" ) {
321 const int newRev = attrs.at( i + 1 ).toInt();
322 const int oldRev = (*it).revision();
323 if ( newRev < oldRev || newRev < 0 )
325 d->itemRevisionChanged( (*it).id(), oldRev, newRev );
326 (*it).setRevision( newRev );
332 kDebug() <<
"Unhandled response: " << _tag << data;
339 if ( d->mIgnorePayload == ignore )
342 d->mIgnorePayload = ignore;
343 if ( d->mIgnorePayload )
344 d->mParts = QSet<QByteArray>();
346 Q_ASSERT( !d->mItems.first().mimeType().isEmpty() );
347 d->mParts = d->mItems.first().loadedPayloadParts();
355 return d->mIgnorePayload;
362 d->mOperations.insert( ItemModifyJobPrivate::Gid );
364 d->mOperations.remove( ItemModifyJobPrivate::Gid );
371 return d->mOperations.contains( ItemModifyJobPrivate::Gid );
378 d->mRevCheck =
false;
385 d->mAutomaticConflictHandlingEnabled =
false;
391 Q_ASSERT( d->mItems.size() == 1 );
393 return d->mItems.first();
402 #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.