diff --git a/akonadi/item.cpp b/akonadi/item.cpp index 0cdb49114..64e593a53 100644 --- a/akonadi/item.cpp +++ b/akonadi/item.cpp @@ -1,212 +1,212 @@ /* Copyright (c) 2006 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 "item.h" #include "item_p.h" #include "itemserializer_p.h" #include "protocol_p.h" #include #include using namespace Akonadi; // Change to something != RFC822 as soon as the server supports it const char* Item::FullPayload = "RFC822"; Item::Item() : Entity( new ItemPrivate ) { } Item::Item( Id id ) : Entity( new ItemPrivate( id ) ) { } Item::Item( const QString & mimeType ) : Entity( new ItemPrivate ) { d_func()->mMimeType = mimeType; } Item::Item( const Item &other ) : Entity( other ) { } Item::~Item() { } Item::Flags Item::flags() const { return d_func()->mFlags; } void Item::setFlag( const QByteArray & name ) { Q_D( Item ); d->mFlags.insert( name ); if ( !d->mFlagsOverwritten ) d->mAddedFlags.insert( name ); } void Item::clearFlag( const QByteArray & name ) { Q_D( Item ); d->mFlags.remove( name ); if ( !d->mFlagsOverwritten ) d->mDeletedFlags.insert( name ); } void Item::setFlags( const Flags &flags ) { Q_D( Item ); d->mFlags = flags; d->mFlagsOverwritten = true; } void Item::clearFlags() { Q_D( Item ); d->mFlags.clear(); d->mFlagsOverwritten = true; } QDateTime Item::modificationTime() const { return d_func()->mModificationTime; } void Item::setModificationTime( const QDateTime &datetime ) { d_func()->mModificationTime = datetime; } bool Item::hasFlag( const QByteArray & name ) const { return d_func()->mFlags.contains( name ); } QSet Item::loadedPayloadParts() const { return ItemSerializer::parts( *this ); } QByteArray Item::payloadData() const { int version = 0; QByteArray data; ItemSerializer::serialize( *this, FullPayload, data, version ); return data; } void Item::setPayloadFromData( const QByteArray &data ) { ItemSerializer::deserialize( *this, FullPayload, data, 0, false ); } int Item::revision() const { return d_func()->mRevision; } void Item::setRevision( int rev ) { d_func()->mRevision = rev; } -Entity::Id Item::collectionId() const +Entity::Id Item::storageCollectionId() const { return d_func()->mCollectionId; } -void Item::setCollectionId( Entity::Id collectionId ) +void Item::setStorageCollectionId( Entity::Id collectionId ) { d_func()->mCollectionId = collectionId; } QString Item::mimeType() const { return d_func()->mMimeType; } void Item::setSize( qint64 size ) { Q_D( Item ); d->mSize = size; d->mSizeChanged = true; } qint64 Item::size() const { return d_func()->mSize; } void Item::setMimeType( const QString & mimeType ) { d_func()->mMimeType = mimeType; } bool Item::hasPayload() const { return d_func()->mPayload != 0; } KUrl Item::url( UrlType type ) const { KUrl url; url.setProtocol( QString::fromLatin1("akonadi") ); url.addQueryItem( QLatin1String( "item" ), QString::number( id() ) ); if ( type == UrlWithMimeType ) url.addQueryItem( QLatin1String( "type" ), mimeType() ); return url; } Item Item::fromUrl( const KUrl &url ) { if ( url.protocol() != QLatin1String( "akonadi" ) ) return Item(); const QString itemStr = url.queryItem( QLatin1String( "item" ) ); bool ok = false; Item::Id itemId = itemStr.toLongLong( &ok ); if ( !ok ) return Item(); return Item( itemId ); } PayloadBase* Item::payloadBase() const { return d_func()->mPayload; } void Item::setPayloadBase( PayloadBase* p ) { Q_D( Item ); delete d->mPayload; d->mPayload = p; } AKONADI_DEFINE_PRIVATE( Item ) diff --git a/akonadi/item.h b/akonadi/item.h index 42226cbe6..0f4b50cfb 100644 --- a/akonadi/item.h +++ b/akonadi/item.h @@ -1,414 +1,417 @@ /* Copyright (c) 2006 Volker Krause 2007 Till Adam 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_ITEM_H #define AKONADI_ITEM_H #include "akonadi_export.h" #include #include #include "itempayloadinternals_p.h" #include #include #include #include #include #include class KUrl; namespace std { template class auto_ptr; } namespace Akonadi { class ItemPrivate; /** * @short Represents a PIM item stored in Akonadi storage. * * A PIM item consists of one or more parts, allowing a fine-grained access on its * content where needed (eg. mail envelope, mail body and attachments). * * There is also a namespace (prefix) for special parts which are local to Akonadi. * These parts, prefixed by "akonadi-" will never be fetched in the resource. * They are useful for local extensions like agents which might want to add meta data * to items in order to handle them but the meta data should not be stored back to the * resource. * * This class contains beside some type-agnostic information (flags, revision) * a single payload object representing its actual data. Which objects these actually * are depends on the mimetype of the item and the corresponding serializer plugin. * * This class is implicitly shared. * *

Payload

* * Technically the only restriction on payload objects is that they have to be copyable. * For safety reasons, pointer payloads are forbidden as well though, as the * ownership would not be clear. In this case, usage of a shared pointer is * recommended (such as boost::shared_ptr or QSharedPointer). * * Using a shared pointer is also recommended in case the payload uses polymorphic * types. For supported shared pointer types implicit casting is provided when possible. * * When using a value-based class as payload, it is recommended to use one that does * support implicit sharing as setting and retrieving a payload as well as copying * an Akonadi::Item object imply copying of the payload object. * * The availability of a payload of a specific type can be checked using hasPayload(), * payloads can be retrieved by using payload() and set by using setPayload(). Refer * to the documentation of those methods for more details. * * @author Volker Krause , Till Adam */ class AKONADI_EXPORT Item : public Entity { public: /** * Describes a list of items. */ typedef QList List; /** * Describes a flag name. */ typedef QByteArray Flag; /** * Describes a set of flag names. */ typedef QSet Flags; /** * Describes the part name that is used to fetch the * full payload of an item. */ static const char* FullPayload; /** * Creates a new item. */ Item(); /** * Creates a new item with the given unique @p id. */ explicit Item( Id id ); /** * Creates a new item with the given mime type. * * @param mimeType The mime type of the item. */ explicit Item( const QString &mimeType ); /** * Creates a new item from an @p other item. */ Item( const Item &other ); /** * Destroys the item. */ ~Item(); /** * Creates an item from the given @p url. */ static Item fromUrl( const KUrl &url ); /** * Returns all flags of this item. */ Flags flags() const; /** * Returns the timestamp of the last modification of this item. * @since 4.2 */ QDateTime modificationTime() const; /** * Sets the timestamp of the last modification of this item. * * @note Do not modify this value from within an application, * it is updated automatically by the revision checking functions. * @since 4.2 */ void setModificationTime( const QDateTime &datetime ); /** * Returns whether the flag with the given @p name is * set in the item. */ bool hasFlag( const QByteArray &name ) const; /** * Sets the flag with the given @p name in the item. */ void setFlag( const QByteArray &name ); /** * Removes the flag with the given @p name from the item. */ void clearFlag( const QByteArray &name ); /** * Overwrites all flags of the item by the given @p flags. */ void setFlags( const Flags &flags ); /** * Removes all flags from the item. */ void clearFlags(); /** * Sets the payload based on the canonical representation normally * used for data of this mime type. * * @param data The encoded data. * @see fullPayloadData */ void setPayloadFromData( const QByteArray &data ); /** * Returns the full payload in its canonical representation, e.g. the * binary or textual format usually used for data with this mime type. * This is useful when communicating with non-Akonadi application by * e.g. drag&drop, copy&paste or stored files. */ QByteArray payloadData() const; /** * Returns the list of loaded payload parts. This is not necessarily * identical to all parts in the cache or to all available parts on the backend. */ QSet loadedPayloadParts() const; /** * Sets the @p revision number of the item. * * @note Do not modify this value from within an application, * it is updated automatically by the revision checking functions. */ void setRevision( int revision ); /** * Returns the revision number of the item. */ int revision() const; /** - * Get the main collection ID of the item. Calling this method makes sense only after running an ItemFetchJob on the item. - * Returns the collection ID if it is know, -1 otherwise. + * Returns the unique identifier of the collection this item is stored in. There is only + * a single such collection, although the item can be linked into arbitrary many + * virtual collections. + * Calling this method makes sense only after running an ItemFetchJob on the item. + * @returns the collection ID if it is know, -1 otherwise. * @since 4.3 */ - Entity::Id collectionId() const; + Entity::Id storageCollectionId() const; /** * Set the size of the item in bytes. * * @since 4.2 */ void setSize( qint64 size ); /** * Returns the size of the items in bytes. * * @since 4.2 */ qint64 size() const; /** * Sets the mime type of the item to @p mimeType. */ void setMimeType( const QString &mimeType ); /** * Returns the mime type of the item. */ QString mimeType() const; /** * Sets the payload object of this PIM item. * * @param p The payload object. Must be copyable and must not be a pointer, * will cause a compilation failure otherwise. Using a type that can be copied * fast (such as implicitly shared classes) is recommended. * If the payload type is polymorphic and you intend to set and retrieve payload * objects with mismatching but castable types, make sure to use a supported * shared pointer implementation (currently boost::shared_ptr and QSharedPointer) * and make sure there is a specialization of Akonadi::super_trait for your class. */ template void setPayload( const T &p ); //@cond PRIVATE template void setPayload( T* p ); template void setPayload( std::auto_ptr p ); //@endcond /** * Returns the payload object of this PIM item. This method will only succeed if either * you requested the exact same payload type that was put in or the payload uses a * supported shared pointer type (currently boost::shared_ptr and QSharedPointer), and * is castable to the requested type. For this to work there needs to be a specialization * of Akonadi::super_trait of the used classes. * * If a mismatching or non-castable payload type is requested, an Akonadi::PayloadException * is thrown. Therefore it is generally recommended to guard calls to payload() with a * corresponding hasPayload() call. * * Trying to retrieve a pointer type will fail to compile. */ template T payload() const; /** * Returns whether the item has a payload object. */ bool hasPayload() const; /** * Returns whether the item has a payload of type @c T. * This method will only return @c true if either you requested the exact same payload type * that was put in or the payload uses a supported shared pointer type (currently boost::shared_ptr * and QSharedPointer), and is castable to the requested type. For this to work there needs * to be a specialization of Akonadi::super_trait of the used classes. * * Trying to retrieve a pointer type will fail to compile. */ template bool hasPayload() const; /** * Describes the type of url which is returned in url(). */ enum UrlType { UrlShort = 0, ///< A short url which contains the identifier only (default) UrlWithMimeType = 1 ///< A url with identifier and mimetype }; /** * Returns the url of the item. */ KUrl url( UrlType type = UrlShort ) const; private: //@cond PRIVATE friend class ItemModifyJob; friend class ItemFetchJob; PayloadBase* payloadBase() const; void setPayloadBase( PayloadBase* ); /** - * Set the collection ID to where the item belongs to. Should be set only by the ItemFetchJob. - * @param collectionId the main collection ID + * Set the collection ID to where the item is stored in. Should be set only by the ItemFetchJob. + * @param collectionId the unique identifier of the the collection where this item is stored in. * @since 4.3 */ - void setCollectionId( Entity::Id collectionId); - + void setStorageCollectionId( Entity::Id collectionId); + //@endcond AKONADI_DECLARE_PRIVATE( Item ) }; template T Item::payload() const { BOOST_STATIC_ASSERT( !boost::is_pointer::value ); if ( !payloadBase() ) throw PayloadException( "No payload set" ); typedef Internal::PayloadTrait PayloadType; if ( PayloadType::isPolymorphic ) { try { const typename PayloadType::SuperType sp = payload(); return PayloadType::castFrom( sp ); } catch ( const Akonadi::PayloadException& ) {} } Payload *p = Internal::payload_cast( payloadBase() ); if ( !p ) { throw PayloadException( QString::fromLatin1( "Wrong payload type (is '%1', requested '%2')" ) .arg( QLatin1String( payloadBase()->typeName() ) ) .arg( QLatin1String( typeid(p).name() ) ) ); } return p->payload; } template bool Item::hasPayload() const { BOOST_STATIC_ASSERT( !boost::is_pointer::value ); if ( !hasPayload() ) return false; typedef Internal::PayloadTrait PayloadType; if ( PayloadType::isPolymorphic ) { try { const typename PayloadType::SuperType sp = payload(); return PayloadType::canCastFrom( sp ); } catch ( const Akonadi::PayloadException& ) {} } return Internal::payload_cast( payloadBase() ); } template void Item::setPayload( const T &p ) { typedef Internal::PayloadTrait PayloadType; if ( PayloadType::isPolymorphic ) { const typename PayloadType::SuperType sp = PayloadType::template castTo( p ); if ( !Internal::PayloadTrait::isNull( sp ) || PayloadType::isNull( p ) ) { setPayload( sp ); return; } } setPayloadBase( new Payload( p ) ); } template void Item::setPayload( T* p ) { p.You_MUST_NOT_use_a_pointer_as_payload; } template void Item::setPayload( std::auto_ptr p ) { p.Nice_try_but_a_std_auto_ptr_is_not_allowed_as_payload_either; } } Q_DECLARE_METATYPE(Akonadi::Item) Q_DECLARE_METATYPE(Akonadi::Item::List) #endif diff --git a/akonadi/itemfetchjob.cpp b/akonadi/itemfetchjob.cpp index 7c1f42dac..ae2fe53ac 100644 --- a/akonadi/itemfetchjob.cpp +++ b/akonadi/itemfetchjob.cpp @@ -1,319 +1,319 @@ /* 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( 5250 ) << "Broken fetch response: UID, RID, REV or MIMETYPE missing!"; return; } Item item( uid ); item.setRemoteId( rid ); item.setRevision( rev ); item.setMimeType( mimeType ); - item.setCollectionId( cid ); + 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( 5250 ) << "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( 5250 ) << "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; } 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"