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"
35 using namespace Akonadi;
37 ItemModifyJobPrivate::ItemModifyJobPrivate(
ItemModifyJob *parent )
40 mIgnorePayload( false ),
41 mAutomaticConflictHandlingEnabled( true )
45 void ItemModifyJobPrivate::setClean()
47 mOperations.insert( Dirty );
50 QByteArray ItemModifyJobPrivate::nextPartHeader()
53 if ( !mParts.isEmpty() ) {
54 QSetIterator<QByteArray> it( mParts );
55 const QByteArray label = it.next();
56 mParts.remove( label );
62 if ( mPendingData.size() > 0 ) {
63 command +=
" {" + QByteArray::number( mPendingData.size() ) +
"}\n";
65 if ( mPendingData.isNull() )
69 command += nextPartHeader();
77 void ItemModifyJobPrivate::conflictResolved()
81 q->setError( KJob::NoError );
82 q->setErrorText( QString() );
86 void ItemModifyJobPrivate::conflictResolveError(
const QString &message )
90 q->setErrorText( q->errorText() + message );
94 void ItemModifyJobPrivate::doUpdateItemRevision( Akonadi::Item::Id itemId,
int oldRevision,
int newRevision )
96 Item::List::iterator it = std::find_if( mItems.begin(), mItems.end(), boost::bind( &Item::id, _1 ) == itemId );
97 if ( it != mItems.end() && (*it).revision() == oldRevision )
98 (*it).setRevision( newRevision );
102 QString ItemModifyJobPrivate::jobDebuggingString()
const
105 return QString::fromUtf8( fullCommand() );
107 return QString::fromUtf8( e.
what() );
117 d->mItems.append( item );
118 d->mParts = item.loadedPayloadParts();
120 d->mOperations.insert( ItemModifyJobPrivate::RemoteId );
121 d->mOperations.insert( ItemModifyJobPrivate::RemoteRevision );
127 Q_ASSERT( !items.isEmpty() );
132 if ( d->mItems.size() == 1 ) {
133 d->mParts = items.first().loadedPayloadParts();
134 d->mOperations.insert( ItemModifyJobPrivate::RemoteId );
135 d->mOperations.insert( ItemModifyJobPrivate::RemoteRevision );
137 d->mIgnorePayload =
true;
138 d->mRevCheck =
false;
147 QByteArray ItemModifyJobPrivate::fullCommand()
const
149 const Akonadi::Item item = mItems.first();
150 QList<QByteArray> changes;
151 foreach (
int op, mOperations ) {
153 case ItemModifyJobPrivate::RemoteId:
154 if ( !item.remoteId().isNull() ) {
155 changes <<
"REMOTEID";
156 changes << ImapParser::quote( item.remoteId().toUtf8() );
159 case ItemModifyJobPrivate::RemoteRevision:
160 if ( !item.remoteRevision().isNull() ) {
161 changes <<
"REMOTEREVISION";
162 changes << ImapParser::quote( item.remoteRevision().toUtf8() );
165 case ItemModifyJobPrivate::Dirty:
172 if ( item.d_func()->mClearPayload )
173 changes <<
"INVALIDATECACHE";
175 if ( item.d_func()->mFlagsOverwritten ) {
177 changes <<
'(' + ImapParser::join( item.flags(),
" " ) +
')';
179 if ( !item.d_func()->mAddedFlags.isEmpty() ) {
181 changes <<
'(' + ImapParser::join( item.d_func()->mAddedFlags,
" " ) +
')';
183 if ( !item.d_func()->mDeletedFlags.isEmpty() ) {
185 changes <<
'(' + ImapParser::join( item.d_func()->mDeletedFlags,
" " ) +
')';
189 if ( !item.d_func()->mDeletedAttributes.isEmpty() ) {
191 QList<QByteArray> attrs;
192 foreach (
const QByteArray &attr, item.d_func()->mDeletedAttributes )
194 changes <<
'(' + ImapParser::join( attrs,
" " ) +
')';
198 if ( changes.isEmpty() && mParts.isEmpty() && item.attributes().isEmpty() ) {
205 if ( !mRevCheck || item.revision() < 0 ) {
208 command +=
"REV " + QByteArray::number( item.revision() ) +
' ';
211 if ( item.d_func()->mSizeChanged )
212 command +=
"SIZE " + QByteArray::number( item.size() );
214 command +=
" (" + ImapParser::join( changes,
" " );
216 if ( !attrs.isEmpty() )
217 command +=
' ' + attrs;
227 command = d->fullCommand();
230 setErrorText( QString::fromUtf8( e.
what() ) );
234 if ( command.isEmpty() ) {
239 d->mTag = d->newTag();
240 command.prepend( d->mTag );
242 command += d->nextPartHeader();
244 d->writeData( command );
253 d->writeData( d->mPendingData );
254 d->writeData( d->nextPartHeader() );
258 if ( _tag == d->mTag ) {
259 if ( data.startsWith(
"OK" ) ) {
260 QDateTime modificationDateTime;
261 int dateTimePos = data.indexOf(
"DATETIME" );
262 if ( dateTimePos != -1 ) {
263 int resultPos = ImapParser::parseDateTime( data, modificationDateTime, dateTimePos + 8 );
264 if ( resultPos == (dateTimePos + 8) ) {
265 kDebug() <<
"Invalid DATETIME response to STORE command: " << _tag << data;
269 Item &item = d->mItems.first();
270 item.setModificationTime( modificationDateTime );
271 item.d_ptr->resetChangeLog();
274 setErrorText( QString::fromUtf8( data ) );
276 if ( data.contains(
"[LLCONFLICT]" ) ) {
277 if ( d->mAutomaticConflictHandlingEnabled ) {
280 connect( handler, SIGNAL(conflictResolved()), SLOT(conflictResolved()) );
281 connect( handler, SIGNAL(error(QString)), SLOT(conflictResolveError(QString)) );
283 QMetaObject::invokeMethod( handler,
"start", Qt::QueuedConnection );
289 foreach (
const Item &item, d->mItems ) {
290 ChangeMediator::invalidateItem(item);
298 Akonadi::Item::Id id;
299 ImapParser::parseNumber( data,
id );
300 int pos = data.indexOf(
'(' );
301 if ( pos <= 0 ||
id <= 0 ) {
302 kDebug() <<
"Ignoring strange response: " << _tag << data;
305 Item::List::iterator it = std::find_if( d->mItems.begin(), d->mItems.end(), boost::bind( &Item::id, _1 ) == id );
306 if ( it == d->mItems.end() ) {
307 kDebug() <<
"Received STORE response for an item we did not modify: " << _tag << data;
310 QList<QByteArray> attrs;
311 ImapParser::parseParenthesizedList( data, attrs, pos );
312 for (
int i = 0; i < attrs.size() - 1; i += 2 ) {
313 const QByteArray key = attrs.at( i );
314 if ( key ==
"REV" ) {
315 const int newRev = attrs.at( i + 1 ).toInt();
316 const int oldRev = (*it).revision();
317 if ( newRev < oldRev || newRev < 0 )
319 d->itemRevisionChanged( (*it).id(), oldRev, newRev );
320 (*it).setRevision( newRev );
326 kDebug() <<
"Unhandled response: " << _tag << data;
333 if ( d->mIgnorePayload == ignore )
336 d->mIgnorePayload = ignore;
337 if ( d->mIgnorePayload )
338 d->mParts = QSet<QByteArray>();
340 Q_ASSERT( !d->mItems.first().mimeType().isEmpty() );
341 d->mParts = d->mItems.first().loadedPayloadParts();
349 return d->mIgnorePayload;
356 d->mRevCheck =
false;
363 d->mAutomaticConflictHandlingEnabled =
false;
369 Q_ASSERT( d->mItems.size() == 1 );
371 return d->mItems.first();
380 #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.
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.