akonadi
itemmodifyjob.cpp
00001 /* 00002 Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org> 00003 00004 This library is free software; you can redistribute it and/or modify it 00005 under the terms of the GNU Library General Public License as published by 00006 the Free Software Foundation; either version 2 of the License, or (at your 00007 option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, but WITHOUT 00010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00011 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00012 License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to the 00016 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00017 02110-1301, USA. 00018 */ 00019 00020 #include "itemmodifyjob.h" 00021 #include "itemmodifyjob_p.h" 00022 00023 #include "changemediator_p.h" 00024 #include "collection.h" 00025 #include "conflicthandling/conflicthandler_p.h" 00026 #include "entity_p.h" 00027 #include "imapparser_p.h" 00028 #include "item_p.h" 00029 #include "itemserializer_p.h" 00030 #include "job_p.h" 00031 #include "protocolhelper_p.h" 00032 00033 #include <kdebug.h> 00034 00035 using namespace Akonadi; 00036 00037 ItemModifyJobPrivate::ItemModifyJobPrivate( ItemModifyJob *parent ) 00038 : JobPrivate( parent ), 00039 mRevCheck( true ), 00040 mIgnorePayload( false ), 00041 mAutomaticConflictHandlingEnabled( true ) 00042 { 00043 } 00044 00045 void ItemModifyJobPrivate::setClean() 00046 { 00047 mOperations.insert( Dirty ); 00048 } 00049 00050 QByteArray ItemModifyJobPrivate::nextPartHeader() 00051 { 00052 QByteArray command; 00053 if ( !mParts.isEmpty() ) { 00054 QSetIterator<QByteArray> it( mParts ); 00055 const QByteArray label = it.next(); 00056 mParts.remove( label ); 00057 00058 mPendingData.clear(); 00059 int version = 0; 00060 ItemSerializer::serialize( mItems.first(), label, mPendingData, version ); 00061 command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartPayload, label, version ); 00062 if ( mPendingData.size() > 0 ) { 00063 command += " {" + QByteArray::number( mPendingData.size() ) + "}\n"; 00064 } else { 00065 if ( mPendingData.isNull() ) 00066 command += " NIL"; 00067 else 00068 command += " \"\""; 00069 command += nextPartHeader(); 00070 } 00071 } else { 00072 command += ")\n"; 00073 } 00074 return command; 00075 } 00076 00077 void ItemModifyJobPrivate::conflictResolved() 00078 { 00079 Q_Q( ItemModifyJob ); 00080 00081 q->setError( KJob::NoError ); 00082 q->setErrorText( QString() ); 00083 q->emitResult(); 00084 } 00085 00086 void ItemModifyJobPrivate::conflictResolveError( const QString &message ) 00087 { 00088 Q_Q( ItemModifyJob ); 00089 00090 q->setErrorText( q->errorText() + message ); 00091 q->emitResult(); 00092 } 00093 00094 void ItemModifyJobPrivate::doUpdateItemRevision( Akonadi::Item::Id itemId, int oldRevision, int newRevision ) 00095 { 00096 Item::List::iterator it = std::find_if( mItems.begin(), mItems.end(), boost::bind( &Item::id, _1 ) == itemId ); 00097 if ( it != mItems.end() && (*it).revision() == oldRevision ) 00098 (*it).setRevision( newRevision ); 00099 } 00100 00101 00102 ItemModifyJob::ItemModifyJob( const Item &item, QObject * parent ) 00103 : Job( new ItemModifyJobPrivate( this ), parent ) 00104 { 00105 Q_D( ItemModifyJob ); 00106 00107 d->mItems.append( item ); 00108 d->mParts = item.loadedPayloadParts(); 00109 00110 d->mOperations.insert( ItemModifyJobPrivate::RemoteId ); 00111 d->mOperations.insert( ItemModifyJobPrivate::RemoteRevision ); 00112 } 00113 00114 ItemModifyJob::ItemModifyJob( const Akonadi::Item::List &items, QObject *parent) 00115 : Job( new ItemModifyJobPrivate( this ), parent ) 00116 { 00117 Q_ASSERT( !items.isEmpty() ); 00118 Q_D( ItemModifyJob ); 00119 d->mItems = items; 00120 00121 // same as single item ctor 00122 if ( d->mItems.size() == 1 ) { 00123 d->mParts = items.first().loadedPayloadParts(); 00124 d->mOperations.insert( ItemModifyJobPrivate::RemoteId ); 00125 d->mOperations.insert( ItemModifyJobPrivate::RemoteRevision ); 00126 } else { 00127 d->mIgnorePayload = true; 00128 d->mRevCheck = false; 00129 } 00130 } 00131 00132 00133 ItemModifyJob::~ItemModifyJob() 00134 { 00135 } 00136 00137 void ItemModifyJob::doStart() 00138 { 00139 Q_D( ItemModifyJob ); 00140 00141 const Akonadi::Item item = d->mItems.first(); 00142 QList<QByteArray> changes; 00143 foreach ( int op, d->mOperations ) { 00144 switch ( op ) { 00145 case ItemModifyJobPrivate::RemoteId: 00146 if ( !item.remoteId().isNull() ) { 00147 changes << "REMOTEID"; 00148 changes << ImapParser::quote( item.remoteId().toUtf8() ); 00149 } 00150 break; 00151 case ItemModifyJobPrivate::RemoteRevision: 00152 if ( !item.remoteRevision().isNull() ) { 00153 changes << "REMOTEREVISION"; 00154 changes << ImapParser::quote( item.remoteRevision().toUtf8() ); 00155 } 00156 break; 00157 case ItemModifyJobPrivate::Dirty: 00158 changes << "DIRTY"; 00159 changes << "false"; 00160 break; 00161 } 00162 } 00163 00164 if ( item.d_func()->mClearPayload ) 00165 changes << "INVALIDATECACHE"; 00166 00167 if ( item.d_func()->mFlagsOverwritten ) { 00168 changes << "FLAGS"; 00169 changes << '(' + ImapParser::join( item.flags(), " " ) + ')'; 00170 } else { 00171 if ( !item.d_func()->mAddedFlags.isEmpty() ) { 00172 changes << "+FLAGS"; 00173 changes << '(' + ImapParser::join( item.d_func()->mAddedFlags, " " ) + ')'; 00174 } 00175 if ( !item.d_func()->mDeletedFlags.isEmpty() ) { 00176 changes << "-FLAGS"; 00177 changes << '(' + ImapParser::join( item.d_func()->mDeletedFlags, " " ) + ')'; 00178 } 00179 } 00180 00181 if ( !item.d_func()->mDeletedAttributes.isEmpty() ) { 00182 changes << "-PARTS"; 00183 QList<QByteArray> attrs; 00184 foreach ( const QByteArray &attr, item.d_func()->mDeletedAttributes ) 00185 attrs << ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartAttribute, attr ); 00186 changes << '(' + ImapParser::join( attrs, " " ) + ')'; 00187 } 00188 00189 // nothing to do 00190 if ( changes.isEmpty() && d->mParts.isEmpty() && item.attributes().isEmpty() ) { 00191 emitResult(); 00192 return; 00193 } 00194 00195 d->mTag = d->newTag(); 00196 QByteArray command = d->mTag; 00197 try { 00198 command += ProtocolHelper::entitySetToByteArray( d->mItems, "STORE" ); 00199 } catch ( const Exception &e ) { 00200 setError( Job::Unknown ); 00201 setErrorText( QString::fromUtf8( e.what() ) ); 00202 emitResult(); 00203 return; 00204 } 00205 command += ' '; 00206 if ( !d->mRevCheck || item.revision() < 0 ) { 00207 command += "NOREV "; 00208 } else { 00209 command += "REV " + QByteArray::number( item.revision() ) + ' '; 00210 } 00211 00212 if ( item.d_func()->mSizeChanged ) 00213 command += "SIZE " + QByteArray::number( item.size() ); 00214 00215 command += " (" + ImapParser::join( changes, " " ); 00216 const QByteArray attrs = ProtocolHelper::attributesToByteArray( item, true ); 00217 if ( !attrs.isEmpty() ) 00218 command += ' ' + attrs; 00219 command += d->nextPartHeader(); 00220 d->writeData( command ); 00221 d->newTag(); // hack to circumvent automatic response handling 00222 } 00223 00224 void ItemModifyJob::doHandleResponse(const QByteArray &_tag, const QByteArray & data) 00225 { 00226 Q_D( ItemModifyJob ); 00227 00228 if ( _tag == "+" ) { // ready for literal data 00229 d->writeData( d->mPendingData ); 00230 d->writeData( d->nextPartHeader() ); 00231 return; 00232 } 00233 00234 if ( _tag == d->mTag ) { 00235 if ( data.startsWith( "OK" ) ) { //krazy:exclude=strings 00236 QDateTime modificationDateTime; 00237 int dateTimePos = data.indexOf( "DATETIME" ); 00238 if ( dateTimePos != -1 ) { 00239 int resultPos = ImapParser::parseDateTime( data, modificationDateTime, dateTimePos + 8 ); 00240 if ( resultPos == (dateTimePos + 8) ) { 00241 kDebug() << "Invalid DATETIME response to STORE command: " << _tag << data; 00242 } 00243 } 00244 00245 Item &item = d->mItems.first(); 00246 item.setModificationTime( modificationDateTime ); 00247 item.d_ptr->resetChangeLog(); 00248 } else { 00249 setError( Unknown ); 00250 setErrorText( QString::fromUtf8( data ) ); 00251 00252 if ( data.contains( "[LLCONFLICT]" ) ) { 00253 if ( d->mAutomaticConflictHandlingEnabled ) { 00254 ConflictHandler *handler = new ConflictHandler( ConflictHandler::LocalLocalConflict, this ); 00255 handler->setConflictingItems( d->mItems.first(), d->mItems.first() ); 00256 connect( handler, SIGNAL(conflictResolved()), SLOT(conflictResolved()) ); 00257 connect( handler, SIGNAL(error(QString)), SLOT(conflictResolveError(QString)) ); 00258 00259 QMetaObject::invokeMethod( handler, "start", Qt::QueuedConnection ); 00260 return; 00261 } 00262 } 00263 } 00264 00265 foreach ( const Item &item, d->mItems ) { 00266 ChangeMediator::invalidateItem(item); 00267 } 00268 00269 emitResult(); 00270 return; 00271 } 00272 00273 if ( _tag == "*" ) { 00274 Akonadi::Item::Id id; 00275 ImapParser::parseNumber( data, id ); 00276 int pos = data.indexOf( '(' ); 00277 if ( pos <= 0 || id <= 0 ) { 00278 kDebug() << "Ignoring strange response: " << _tag << data; 00279 return; 00280 } 00281 Item::List::iterator it = std::find_if( d->mItems.begin(), d->mItems.end(), boost::bind( &Item::id, _1 ) == id ); 00282 if ( it == d->mItems.end() ) { 00283 kDebug() << "Received STORE response for an item we did not modify: " << _tag << data; 00284 return; 00285 } 00286 QList<QByteArray> attrs; 00287 ImapParser::parseParenthesizedList( data, attrs, pos ); 00288 for ( int i = 0; i < attrs.size() - 1; i += 2 ) { 00289 const QByteArray key = attrs.at( i ); 00290 if ( key == "REV" ) { 00291 const int newRev = attrs.at( i + 1 ).toInt(); 00292 const int oldRev = (*it).revision(); 00293 if ( newRev < oldRev || newRev < 0 ) 00294 continue; 00295 d->itemRevisionChanged( (*it).id(), oldRev, newRev ); 00296 (*it).setRevision( newRev ); 00297 } 00298 } 00299 return; 00300 } 00301 00302 kDebug() << "Unhandled response: " << _tag << data; 00303 } 00304 00305 void ItemModifyJob::setIgnorePayload( bool ignore ) 00306 { 00307 Q_D( ItemModifyJob ); 00308 00309 if ( d->mIgnorePayload == ignore ) 00310 return; 00311 00312 d->mIgnorePayload = ignore; 00313 if ( d->mIgnorePayload ) 00314 d->mParts = QSet<QByteArray>(); 00315 else { 00316 Q_ASSERT( !d->mItems.first().mimeType().isEmpty() ); 00317 d->mParts = d->mItems.first().loadedPayloadParts(); 00318 } 00319 } 00320 00321 bool ItemModifyJob::ignorePayload() const 00322 { 00323 Q_D( const ItemModifyJob ); 00324 00325 return d->mIgnorePayload; 00326 } 00327 00328 void ItemModifyJob::disableRevisionCheck() 00329 { 00330 Q_D( ItemModifyJob ); 00331 00332 d->mRevCheck = false; 00333 } 00334 00335 void ItemModifyJob::disableAutomaticConflictHandling() 00336 { 00337 Q_D( ItemModifyJob ); 00338 00339 d->mAutomaticConflictHandlingEnabled = false; 00340 } 00341 00342 Item ItemModifyJob::item() const 00343 { 00344 Q_D( const ItemModifyJob ); 00345 Q_ASSERT( d->mItems.size() == 1 ); 00346 00347 return d->mItems.first(); 00348 } 00349 00350 Item::List ItemModifyJob::items() const 00351 { 00352 Q_D( const ItemModifyJob ); 00353 return d->mItems; 00354 } 00355 00356 #include "itemmodifyjob.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu Aug 2 2012 15:25:19 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu Aug 2 2012 15:25:19 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.