akonadi
itemfetchjob.cpp
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "itemfetchjob.h"
00021
00022 #include "attributefactory.h"
00023 #include "collection.h"
00024 #include "collectionselectjob_p.h"
00025 #include "imapparser_p.h"
00026 #include "itemfetchscope.h"
00027 #include "itemserializer_p.h"
00028 #include "itemserializerplugin.h"
00029 #include "job_p.h"
00030 #include "entity_p.h"
00031 #include "protocol_p.h"
00032 #include "protocolhelper_p.h"
00033
00034 #include <kdebug.h>
00035
00036 #include <QtCore/QDateTime>
00037 #include <QtCore/QStringList>
00038 #include <QtCore/QTimer>
00039 #include <QtCore/QFile>
00040
00041 using namespace Akonadi;
00042
00043 class Akonadi::ItemFetchJobPrivate : public JobPrivate
00044 {
00045 public:
00046 ItemFetchJobPrivate( ItemFetchJob *parent )
00047 : JobPrivate( parent )
00048 {
00049 }
00050
00051 void timeout()
00052 {
00053 Q_Q( ItemFetchJob );
00054
00055 mEmitTimer->stop();
00056 if ( !mPendingItems.isEmpty() ) {
00057 emit q->itemsReceived( mPendingItems );
00058 mPendingItems.clear();
00059 }
00060 }
00061
00062 void startFetchJob();
00063 void selectDone( KJob * job );
00064
00065 Q_DECLARE_PUBLIC( ItemFetchJob )
00066
00067 Collection mCollection;
00068 Item mItem;
00069 Item::List mItems;
00070 ItemFetchScope mFetchScope;
00071 Item::List mPendingItems;
00072 QTimer* mEmitTimer;
00073 };
00074
00075 void ItemFetchJobPrivate::startFetchJob()
00076 {
00077 QByteArray command = newTag();
00078 if ( mItem.isValid() )
00079 command += " " AKONADI_CMD_UID " " AKONADI_CMD_ITEMFETCH " " + QByteArray::number( mItem.id() );
00080 else if ( !mItem.remoteId().isEmpty() )
00081 command += " " AKONADI_CMD_RID " " AKONADI_CMD_ITEMFETCH " " + mItem.remoteId().toUtf8();
00082 else
00083 command += " " AKONADI_CMD_ITEMFETCH " 1:*";
00084
00085 if ( mFetchScope.fullPayload() )
00086 command += " " AKONADI_PARAM_FULLPAYLOAD;
00087 if ( mFetchScope.allAttributes() )
00088 command += " " AKONADI_PARAM_ALLATTRIBUTES;
00089 if ( mFetchScope.cacheOnly() )
00090 command += " " AKONADI_PARAM_CACHEONLY;
00091
00092
00093 command += " " AKONADI_PARAM_EXTERNALPAYLOAD;
00094
00095 command += " (UID REMOTEID COLLECTIONID FLAGS SIZE DATETIME";
00096 foreach ( const QByteArray &part, mFetchScope.payloadParts() )
00097 command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartPayload, part );
00098 foreach ( const QByteArray &part, mFetchScope.attributes() )
00099 command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartAttribute, part );
00100 command += ")\n";
00101
00102 writeData( command );
00103 }
00104
00105 void ItemFetchJobPrivate::selectDone( KJob * job )
00106 {
00107 if ( !job->error() )
00108
00109 startFetchJob();
00110 }
00111
00112 ItemFetchJob::ItemFetchJob( const Collection &collection, QObject * parent )
00113 : Job( new ItemFetchJobPrivate( this ), parent )
00114 {
00115 Q_D( ItemFetchJob );
00116
00117 d->mEmitTimer = new QTimer( this );
00118 d->mEmitTimer->setSingleShot( true );
00119 d->mEmitTimer->setInterval( 100 );
00120 connect( d->mEmitTimer, SIGNAL(timeout()), this, SLOT(timeout()) );
00121 connect( this, SIGNAL(result(KJob*)), this, SLOT(timeout()) );
00122
00123 d->mCollection = collection;
00124 }
00125
00126 ItemFetchJob::ItemFetchJob( const Item & item, QObject * parent)
00127 : Job( new ItemFetchJobPrivate( this ), parent )
00128 {
00129 Q_D( ItemFetchJob );
00130
00131 d->mEmitTimer = new QTimer( this );
00132 d->mEmitTimer->setSingleShot( true );
00133 d->mEmitTimer->setInterval( 100 );
00134 connect( d->mEmitTimer, SIGNAL(timeout()), this, SLOT(timeout()) );
00135 connect( this, SIGNAL(result(KJob*)), this, SLOT(timeout()) );
00136
00137 d->mCollection = Collection::root();
00138 d->mItem = item;
00139 }
00140
00141 ItemFetchJob::~ItemFetchJob()
00142 {
00143 }
00144
00145 void ItemFetchJob::doStart()
00146 {
00147 Q_D( ItemFetchJob );
00148
00149 if ( !d->mItem.isValid() ) {
00150 if ( d->mCollection == Collection::root() ) {
00151 setErrorText( QLatin1String("Cannot list root collection.") );
00152 setError( Unknown );
00153 emitResult();
00154 }
00155 CollectionSelectJob *job = new CollectionSelectJob( d->mCollection, this );
00156 connect( job, SIGNAL(result(KJob*)), SLOT(selectDone(KJob*)) );
00157 addSubjob( job );
00158 } else
00159 d->startFetchJob();
00160 }
00161
00162 void ItemFetchJob::doHandleResponse( const QByteArray & tag, const QByteArray & data )
00163 {
00164 Q_D( ItemFetchJob );
00165
00166 if ( tag == "*" ) {
00167 int begin = data.indexOf( "FETCH" );
00168 if ( begin >= 0 ) {
00169
00170
00171 QList<QByteArray> fetchResponse;
00172 ImapParser::parseParenthesizedList( data, fetchResponse, begin + 6 );
00173
00174
00175 Item::Id uid = -1;
00176 int rev = -1;
00177 QString rid;
00178 QString mimeType;
00179 Entity::Id cid = -1;
00180
00181 for ( int i = 0; i < fetchResponse.count() - 1; i += 2 ) {
00182 const QByteArray key = fetchResponse.value( i );
00183 const QByteArray value = fetchResponse.value( i + 1 );
00184
00185 if ( key == "UID" )
00186 uid = value.toLongLong();
00187 else if ( key == "REV" )
00188 rev = value.toInt();
00189 else if ( key == "REMOTEID" ) {
00190 if ( !value.isEmpty() )
00191 rid = QString::fromUtf8( value );
00192 else
00193 rid.clear();
00194 } else if ( key == "COLLECTIONID" ) {
00195 cid = value.toInt();
00196 } else if ( key == "MIMETYPE" )
00197 mimeType = QString::fromLatin1( value );
00198 }
00199
00200 if ( uid < 0 || rev < 0 || mimeType.isEmpty() ) {
00201 kWarning( 5250 ) << "Broken fetch response: UID, RID, REV or MIMETYPE missing!";
00202 return;
00203 }
00204
00205 Item item( uid );
00206 item.setRemoteId( rid );
00207 item.setRevision( rev );
00208 item.setMimeType( mimeType );
00209 item.setStorageCollectionId( cid );
00210 if ( !item.isValid() )
00211 return;
00212
00213
00214 for ( int i = 0; i < fetchResponse.count() - 1; i += 2 ) {
00215 const QByteArray key = fetchResponse.value( i );
00216
00217 if ( key == "UID" || key == "REV" || key == "REMOTEID" || key == "MIMETYPE" || key == "COLLECTIONID")
00218 continue;
00219
00220 if ( key == "FLAGS" ) {
00221 QList<QByteArray> flags;
00222 ImapParser::parseParenthesizedList( fetchResponse[i + 1], flags );
00223 foreach ( const QByteArray &flag, flags ) {
00224 item.setFlag( flag );
00225 }
00226 } else if ( key == "SIZE" ) {
00227 const quint64 size = fetchResponse[i + 1].toLongLong();
00228 item.setSize( size );
00229 } else if ( key == "DATETIME" ) {
00230 QDateTime datetime;
00231 ImapParser::parseDateTime( fetchResponse[i + 1], datetime );
00232 item.setModificationTime( datetime );
00233 } else {
00234 int version = 0;
00235 QByteArray plainKey( key );
00236 ProtocolHelper::PartNamespace ns;
00237
00238 ImapParser::splitVersionedKey( key, plainKey, version );
00239 plainKey = ProtocolHelper::decodePartIdentifier( plainKey, ns );
00240
00241 switch ( ns ) {
00242 case ProtocolHelper::PartPayload:
00243 {
00244 bool isExternal = false;
00245 QByteArray fileKey = fetchResponse.value( i + 1 );
00246 if (fileKey == "[FILE]") {
00247 isExternal = true;
00248 i++;
00249 kDebug( 5250 ) << "Payload is external: " << isExternal << " filename: " << fetchResponse.value( i + 1 );
00250 }
00251 ItemSerializer::deserialize( item, plainKey, fetchResponse.value( i + 1 ), version, isExternal );
00252 break;
00253 }
00254 case ProtocolHelper::PartAttribute:
00255 {
00256 Attribute* attr = AttributeFactory::createAttribute( plainKey );
00257 Q_ASSERT( attr );
00258 if ( fetchResponse.value( i + 1 ) == "[FILE]" ) {
00259 ++i;
00260 QFile f( QString::fromUtf8( fetchResponse.value( i + 1 ) ) );
00261 if ( f.open( QFile::ReadOnly ) )
00262 attr->deserialize( f.readAll() );
00263 else {
00264 kWarning() << "Failed to open attribute file: " << fetchResponse.value( i + 1 );
00265 delete attr;
00266 }
00267 } else {
00268 attr->deserialize( fetchResponse.value( i + 1 ) );
00269 }
00270 item.addAttribute( attr );
00271 break;
00272 }
00273 case ProtocolHelper::PartGlobal:
00274 default:
00275 kWarning() << "Unknown item part type:" << key;
00276 }
00277 }
00278 }
00279
00280 item.d_ptr->resetChangeLog();
00281 d->mItems.append( item );
00282 d->mPendingItems.append( item );
00283 if ( !d->mEmitTimer->isActive() )
00284 d->mEmitTimer->start();
00285 return;
00286 }
00287 }
00288 kDebug( 5250 ) << "Unhandled response: " << tag << data;
00289 }
00290
00291 Item::List ItemFetchJob::items() const
00292 {
00293 Q_D( const ItemFetchJob );
00294
00295 return d->mItems;
00296 }
00297
00298 void ItemFetchJob::setFetchScope( ItemFetchScope &fetchScope )
00299 {
00300 Q_D( ItemFetchJob );
00301
00302 d->mFetchScope = fetchScope;
00303 }
00304
00305 ItemFetchScope &ItemFetchJob::fetchScope()
00306 {
00307 Q_D( ItemFetchJob );
00308
00309 return d->mFetchScope;
00310 }
00311
00312 void ItemFetchJob::setCollection(const Akonadi::Collection& collection)
00313 {
00314 Q_D( ItemFetchJob );
00315 d->mCollection = collection;
00316 }
00317
00318
00319 #include "itemfetchjob.moc"