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 );
107 d->mItems.append( item );
108 d->mParts = item.loadedPayloadParts();
110 d->mOperations.insert( ItemModifyJobPrivate::RemoteId );
111 d->mOperations.insert( ItemModifyJobPrivate::RemoteRevision );
117 Q_ASSERT( !items.isEmpty() );
122 if ( d->mItems.size() == 1 ) {
123 d->mParts = items.first().loadedPayloadParts();
124 d->mOperations.insert( ItemModifyJobPrivate::RemoteId );
125 d->mOperations.insert( ItemModifyJobPrivate::RemoteRevision );
127 d->mIgnorePayload =
true;
128 d->mRevCheck =
false;
141 const Akonadi::Item
item = d->mItems.first();
142 QList<QByteArray> changes;
143 foreach (
int op, d->mOperations ) {
145 case ItemModifyJobPrivate::RemoteId:
146 if ( !item.remoteId().isNull() ) {
147 changes <<
"REMOTEID";
148 changes << ImapParser::quote( item.remoteId().toUtf8() );
151 case ItemModifyJobPrivate::RemoteRevision:
152 if ( !item.remoteRevision().isNull() ) {
153 changes <<
"REMOTEREVISION";
154 changes << ImapParser::quote( item.remoteRevision().toUtf8() );
157 case ItemModifyJobPrivate::Dirty:
164 if ( item.d_func()->mClearPayload )
165 changes <<
"INVALIDATECACHE";
167 if ( item.d_func()->mFlagsOverwritten ) {
169 changes <<
'(' + ImapParser::join( item.flags(),
" " ) +
')';
171 if ( !item.d_func()->mAddedFlags.isEmpty() ) {
173 changes <<
'(' + ImapParser::join( item.d_func()->mAddedFlags,
" " ) +
')';
175 if ( !item.d_func()->mDeletedFlags.isEmpty() ) {
177 changes <<
'(' + ImapParser::join( item.d_func()->mDeletedFlags,
" " ) +
')';
181 if ( !item.d_func()->mDeletedAttributes.isEmpty() ) {
183 QList<QByteArray> attrs;
184 foreach (
const QByteArray &attr, item.d_func()->mDeletedAttributes )
186 changes <<
'(' + ImapParser::join( attrs,
" " ) +
')';
190 if ( changes.isEmpty() && d->mParts.isEmpty() && item.attributes().isEmpty() ) {
195 d->mTag = d->newTag();
196 QByteArray command = d->mTag;
201 setErrorText( QString::fromUtf8( e.
what() ) );
206 if ( !d->mRevCheck || item.revision() < 0 ) {
209 command +=
"REV " + QByteArray::number( item.revision() ) +
' ';
212 if ( item.d_func()->mSizeChanged )
213 command +=
"SIZE " + QByteArray::number( item.size() );
215 command +=
" (" + ImapParser::join( changes,
" " );
217 if ( !attrs.isEmpty() )
218 command +=
' ' + attrs;
219 command += d->nextPartHeader();
220 d->writeData( command );
229 d->writeData( d->mPendingData );
230 d->writeData( d->nextPartHeader() );
234 if ( _tag == d->mTag ) {
235 if ( data.startsWith(
"OK" ) ) {
236 QDateTime modificationDateTime;
237 int dateTimePos = data.indexOf(
"DATETIME" );
238 if ( dateTimePos != -1 ) {
239 int resultPos = ImapParser::parseDateTime( data, modificationDateTime, dateTimePos + 8 );
240 if ( resultPos == (dateTimePos + 8) ) {
241 kDebug() <<
"Invalid DATETIME response to STORE command: " << _tag << data;
245 Item &
item = d->mItems.first();
246 item.setModificationTime( modificationDateTime );
247 item.d_ptr->resetChangeLog();
250 setErrorText( QString::fromUtf8( data ) );
252 if ( data.contains(
"[LLCONFLICT]" ) ) {
253 if ( d->mAutomaticConflictHandlingEnabled ) {
256 connect( handler, SIGNAL(conflictResolved()), SLOT(conflictResolved()) );
257 connect( handler, SIGNAL(error(QString)), SLOT(conflictResolveError(QString)) );
259 QMetaObject::invokeMethod( handler,
"start", Qt::QueuedConnection );
265 foreach (
const Item &
item, d->mItems ) {
266 ChangeMediator::invalidateItem(item);
274 Akonadi::Item::Id id;
275 ImapParser::parseNumber( data,
id );
276 int pos = data.indexOf(
'(' );
277 if ( pos <= 0 ||
id <= 0 ) {
278 kDebug() <<
"Ignoring strange response: " << _tag << data;
281 Item::List::iterator it = std::find_if( d->mItems.begin(), d->mItems.end(), boost::bind( &Item::id, _1 ) == id );
282 if ( it == d->mItems.end() ) {
283 kDebug() <<
"Received STORE response for an item we did not modify: " << _tag << data;
286 QList<QByteArray> attrs;
287 ImapParser::parseParenthesizedList( data, attrs, pos );
288 for (
int i = 0; i < attrs.size() - 1; i += 2 ) {
289 const QByteArray key = attrs.at( i );
290 if ( key ==
"REV" ) {
291 const int newRev = attrs.at( i + 1 ).toInt();
292 const int oldRev = (*it).revision();
293 if ( newRev < oldRev || newRev < 0 )
295 d->itemRevisionChanged( (*it).id(), oldRev, newRev );
296 (*it).setRevision( newRev );
302 kDebug() <<
"Unhandled response: " << _tag << data;
309 if ( d->mIgnorePayload == ignore )
312 d->mIgnorePayload = ignore;
313 if ( d->mIgnorePayload )
314 d->mParts = QSet<QByteArray>();
316 Q_ASSERT( !d->mItems.first().mimeType().isEmpty() );
317 d->mParts = d->mItems.first().loadedPayloadParts();
325 return d->mIgnorePayload;
332 d->mRevCheck =
false;
339 d->mAutomaticConflictHandlingEnabled =
false;
345 Q_ASSERT( d->mItems.size() == 1 );
347 return d->mItems.first();
356 #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.