diff --git a/akonadi/itemfetchjob.cpp b/akonadi/itemfetchjob.cpp index 5659cd995..9f6345691 100644 --- a/akonadi/itemfetchjob.cpp +++ b/akonadi/itemfetchjob.cpp @@ -1,319 +1,326 @@ /* Copyright (c) 2006 - 2007 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "itemfetchjob.h" #include "attributefactory.h" #include "collection.h" #include "collectionselectjob_p.h" #include "imapparser_p.h" #include "itemfetchscope.h" #include "itemserializer_p.h" #include "itemserializerplugin.h" #include "job_p.h" #include "entity_p.h" #include "protocol_p.h" #include "protocolhelper_p.h" #include #include #include #include #include using namespace Akonadi; class Akonadi::ItemFetchJobPrivate : public JobPrivate { public: ItemFetchJobPrivate( ItemFetchJob *parent ) : JobPrivate( parent ) { } void timeout() { Q_Q( ItemFetchJob ); mEmitTimer->stop(); // in case we are called by result() if ( !mPendingItems.isEmpty() ) { emit q->itemsReceived( mPendingItems ); mPendingItems.clear(); } } void startFetchJob(); void selectDone( KJob * job ); Q_DECLARE_PUBLIC( ItemFetchJob ) Collection mCollection; Item mItem; Item::List mItems; ItemFetchScope mFetchScope; Item::List mPendingItems; // items pending for emitting itemsReceived() QTimer* mEmitTimer; }; void ItemFetchJobPrivate::startFetchJob() { QByteArray command = newTag(); if ( mItem.isValid() ) command += " " AKONADI_CMD_UID " " AKONADI_CMD_ITEMFETCH " " + QByteArray::number( mItem.id() ); else if ( !mItem.remoteId().isEmpty() ) command += " " AKONADI_CMD_RID " " AKONADI_CMD_ITEMFETCH " " + mItem.remoteId().toUtf8(); else command += " " AKONADI_CMD_ITEMFETCH " 1:*"; if ( mFetchScope.fullPayload() ) command += " " AKONADI_PARAM_FULLPAYLOAD; if ( mFetchScope.allAttributes() ) command += " " AKONADI_PARAM_ALLATTRIBUTES; if ( mFetchScope.cacheOnly() ) command += " " AKONADI_PARAM_CACHEONLY; //TODO: detect somehow if server supports external payload attribute command += " " AKONADI_PARAM_EXTERNALPAYLOAD; command += " (UID REMOTEID COLLECTIONID FLAGS SIZE DATETIME"; foreach ( const QByteArray &part, mFetchScope.payloadParts() ) command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartPayload, part ); foreach ( const QByteArray &part, mFetchScope.attributes() ) command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartAttribute, part ); command += ")\n"; writeData( command ); } void ItemFetchJobPrivate::selectDone( KJob * job ) { if ( !job->error() ) // the collection is now selected, fetch the message(s) startFetchJob(); } ItemFetchJob::ItemFetchJob( const Collection &collection, QObject * parent ) : Job( new ItemFetchJobPrivate( this ), parent ) { Q_D( ItemFetchJob ); d->mEmitTimer = new QTimer( this ); d->mEmitTimer->setSingleShot( true ); d->mEmitTimer->setInterval( 100 ); connect( d->mEmitTimer, SIGNAL(timeout()), this, SLOT(timeout()) ); connect( this, SIGNAL(result(KJob*)), this, SLOT(timeout()) ); d->mCollection = collection; } ItemFetchJob::ItemFetchJob( const Item & item, QObject * parent) : Job( new ItemFetchJobPrivate( this ), parent ) { Q_D( ItemFetchJob ); d->mEmitTimer = new QTimer( this ); d->mEmitTimer->setSingleShot( true ); d->mEmitTimer->setInterval( 100 ); connect( d->mEmitTimer, SIGNAL(timeout()), this, SLOT(timeout()) ); connect( this, SIGNAL(result(KJob*)), this, SLOT(timeout()) ); d->mCollection = Collection::root(); d->mItem = item; } ItemFetchJob::~ItemFetchJob() { } void ItemFetchJob::doStart() { Q_D( ItemFetchJob ); if ( !d->mItem.isValid() ) { // collection content listing if ( d->mCollection == Collection::root() ) { setErrorText( QLatin1String("Cannot list root collection.") ); setError( Unknown ); emitResult(); } CollectionSelectJob *job = new CollectionSelectJob( d->mCollection, this ); connect( job, SIGNAL(result(KJob*)), SLOT(selectDone(KJob*)) ); addSubjob( job ); } else d->startFetchJob(); } void ItemFetchJob::doHandleResponse( const QByteArray & tag, const QByteArray & data ) { Q_D( ItemFetchJob ); if ( tag == "*" ) { int begin = data.indexOf( "FETCH" ); if ( begin >= 0 ) { // split fetch response into key/value pairs QList fetchResponse; ImapParser::parseParenthesizedList( data, fetchResponse, begin + 6 ); // create a new item object Item::Id uid = -1; int rev = -1; QString rid; QString mimeType; Entity::Id cid = -1; for ( int i = 0; i < fetchResponse.count() - 1; i += 2 ) { const QByteArray key = fetchResponse.value( i ); const QByteArray value = fetchResponse.value( i + 1 ); if ( key == "UID" ) uid = value.toLongLong(); else if ( key == "REV" ) rev = value.toInt(); else if ( key == "REMOTEID" ) { if ( !value.isEmpty() ) rid = QString::fromUtf8( value ); else rid.clear(); } else if ( key == "COLLECTIONID" ) { cid = value.toInt(); } else if ( key == "MIMETYPE" ) mimeType = QString::fromLatin1( value ); } if ( uid < 0 || rev < 0 || mimeType.isEmpty() ) { kWarning() << "Broken fetch response: UID, RID, REV or MIMETYPE missing!"; return; } Item item( uid ); item.setRemoteId( rid ); item.setRevision( rev ); item.setMimeType( mimeType ); item.setStorageCollectionId( cid ); if ( !item.isValid() ) return; // parse fetch response fields for ( int i = 0; i < fetchResponse.count() - 1; i += 2 ) { const QByteArray key = fetchResponse.value( i ); // skip stuff we dealt with already if ( key == "UID" || key == "REV" || key == "REMOTEID" || key == "MIMETYPE" || key == "COLLECTIONID") continue; // flags if ( key == "FLAGS" ) { QList flags; ImapParser::parseParenthesizedList( fetchResponse[i + 1], flags ); foreach ( const QByteArray &flag, flags ) { item.setFlag( flag ); } } else if ( key == "SIZE" ) { const quint64 size = fetchResponse[i + 1].toLongLong(); item.setSize( size ); } else if ( key == "DATETIME" ) { QDateTime datetime; ImapParser::parseDateTime( fetchResponse[i + 1], datetime ); item.setModificationTime( datetime ); } else { int version = 0; QByteArray plainKey( key ); ProtocolHelper::PartNamespace ns; ImapParser::splitVersionedKey( key, plainKey, version ); plainKey = ProtocolHelper::decodePartIdentifier( plainKey, ns ); switch ( ns ) { case ProtocolHelper::PartPayload: { bool isExternal = false; QByteArray fileKey = fetchResponse.value( i + 1 ); if (fileKey == "[FILE]") { isExternal = true; i++; kDebug() << "Payload is external: " << isExternal << " filename: " << fetchResponse.value( i + 1 ); } ItemSerializer::deserialize( item, plainKey, fetchResponse.value( i + 1 ), version, isExternal ); break; } case ProtocolHelper::PartAttribute: { Attribute* attr = AttributeFactory::createAttribute( plainKey ); Q_ASSERT( attr ); if ( fetchResponse.value( i + 1 ) == "[FILE]" ) { ++i; QFile f( QString::fromUtf8( fetchResponse.value( i + 1 ) ) ); if ( f.open( QFile::ReadOnly ) ) attr->deserialize( f.readAll() ); else { kWarning() << "Failed to open attribute file: " << fetchResponse.value( i + 1 ); delete attr; } } else { attr->deserialize( fetchResponse.value( i + 1 ) ); } item.addAttribute( attr ); break; } case ProtocolHelper::PartGlobal: default: kWarning() << "Unknown item part type:" << key; } } } item.d_ptr->resetChangeLog(); d->mItems.append( item ); d->mPendingItems.append( item ); if ( !d->mEmitTimer->isActive() ) d->mEmitTimer->start(); return; } } kDebug() << "Unhandled response: " << tag << data; } Item::List ItemFetchJob::items() const { Q_D( const ItemFetchJob ); return d->mItems; } void ItemFetchJob::setFetchScope( ItemFetchScope &fetchScope ) { Q_D( ItemFetchJob ); d->mFetchScope = fetchScope; } +void ItemFetchJob::setFetchScope( const ItemFetchScope &fetchScope ) +{ + Q_D( ItemFetchJob ); + + d->mFetchScope = fetchScope; +} + ItemFetchScope &ItemFetchJob::fetchScope() { Q_D( ItemFetchJob ); return d->mFetchScope; } void ItemFetchJob::setCollection(const Akonadi::Collection& collection) { Q_D( ItemFetchJob ); d->mCollection = collection; } #include "itemfetchjob.moc" diff --git a/akonadi/itemfetchjob.h b/akonadi/itemfetchjob.h index a4b8bd700..499beab25 100644 --- a/akonadi/itemfetchjob.h +++ b/akonadi/itemfetchjob.h @@ -1,157 +1,170 @@ /* Copyright (c) 2006 - 2007 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_ITEMFETCHJOB_H #define AKONADI_ITEMFETCHJOB_H #include #include namespace Akonadi { class Collection; class ItemFetchJobPrivate; class ItemFetchScope; /** * @short Job that fetches items from the Akonadi storage. * * This class is used to fetch items from the Akonadi storage. * Which parts of the items (e.g. headers only, attachments or all) * can be specified by the ItemFetchScope. * * Example: * * @code * * // Fetch all items with full payload from the root collection * Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob( Akonadi::Collection::root() ); * job->fetchScope().fetchFullPayload(); * * if ( job->exec() ) { * Akonadi::Item::List items = job->items(); * foreach( const Akonadi::Item &item, items ) { * qDebug() << "Item ID:" << item.id(); * } * } else { * qDebug() << "Error occurred"; * } * * @endcode * * @author Volker Krause */ class AKONADI_EXPORT ItemFetchJob : public Job { Q_OBJECT public: /** * Creates a new item fetch job that retrieves all items inside the given collection. * * @param collection The parent collection to fetch all items from. * @param parent The parent object. */ explicit ItemFetchJob( const Collection &collection, QObject *parent = 0 ); /** * Creates a new item fetch job that retrieves the specified item. * If the item has an uid set, this is used to identify the item on the Akonadi * server. If only a remote identifier is available, that one is used. * However, as remote identifier are not necessarily globally unique, you * need to specify the resource and/or collection to search in in that case, * using setCollection() or Akonadi::ResourceSelectJob. * * @param item The item to fetch. * @param parent The parent object. */ explicit ItemFetchJob( const Item &item, QObject *parent = 0 ); /** * Destroys the item fetch job. */ virtual ~ItemFetchJob(); /** * Returns the fetched item. * * @note The items are invalid before the result( KJob* ) * signal has been emitted or if an error occurred. */ Item::List items() const; /** * Sets the item fetch scope. * * The ItemFetchScope controls how much of an item's data is fetched * from the server, e.g. whether to fetch the full item payload or * only meta data. * * @param fetchScope The new scope for item fetch operations. * * @see fetchScope() */ - void setFetchScope( ItemFetchScope &fetchScope ); + void setFetchScope( ItemFetchScope &fetchScope ); // KDE5: remove + + /** + * Sets the item fetch scope. + * + * The ItemFetchScope controls how much of an item's data is fetched + * from the server, e.g. whether to fetch the full item payload or + * only meta data. + * + * @param fetchScope The new scope for item fetch operations. + * + * @see fetchScope() + */ + void setFetchScope( const ItemFetchScope &fetchScope ); /** * Returns the item fetch scope. * * Since this returns a reference it can be used to conveniently modify the * current scope in-place, i.e. by calling a method on the returned reference * without storing it in a local variable. See the ItemFetchScope documentation * for an example. * * @return a reference to the current item fetch scope * * @see setFetchScope() for replacing the current item fetch scope */ ItemFetchScope &fetchScope(); /** * Specifies the collection the item is in. * This is only required when retrieving an item based on its remote id which might not be * unique globally. * * @see Akonadi::ResourceSelectJob */ void setCollection( const Collection &collection ); Q_SIGNALS: /** * This signal is emitted when the items are fetched completely. * * @param items The fetched items. */ void itemsReceived( const Akonadi::Item::List &items ); protected: virtual void doStart(); virtual void doHandleResponse( const QByteArray &tag, const QByteArray &data ); private: Q_DECLARE_PRIVATE( ItemFetchJob ) //@cond PRIVATE Q_PRIVATE_SLOT( d_func(), void selectDone( KJob* ) ) Q_PRIVATE_SLOT( d_func(), void timeout() ) //@endcond }; } #endif