diff --git a/akonadi/item.cpp b/akonadi/item.cpp index b2efea564..5668d531c 100644 --- a/akonadi/item.cpp +++ b/akonadi/item.cpp @@ -1,200 +1,200 @@ /* 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.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 ); + ItemSerializer::deserialize( *this, FullPayload, data, 0, false ); } int Item::revision() const { return d_func()->mRevision; } void Item::setRevision( int rev ) { d_func()->mRevision = rev; } QString Item::mimeType() const { return d_func()->mMimeType; } void Item::setSize( qint64 size ) { d_func()->mSize = size; } 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/itemfetchjob.cpp b/akonadi/itemfetchjob.cpp index b01476aba..c30251233 100644 --- a/akonadi/itemfetchjob.cpp +++ b/akonadi/itemfetchjob.cpp @@ -1,279 +1,291 @@ /* 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.h" #include "imapparser_p.h" #include "itemfetchscope.h" #include "itemserializer.h" #include "itemserializerplugin.h" #include "job_p.h" #include "entity_p.h" #include "protocol_p.h" #include "protocolhelper.h" #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_ITEMFETCH " 1:*"; else command += " " AKONADI_CMD_UID " " AKONADI_CMD_ITEMFETCH " " + QByteArray::number( mItem.id() ); 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 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; 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" ) rid = QString::fromUtf8( value ); 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 ); 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" ) 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: - ItemSerializer::deserialize( item, plainKey, fetchResponse.value( i + 1 ), version ); + { + 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 ); 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; } #include "itemfetchjob.moc" diff --git a/akonadi/itemserializer.cpp b/akonadi/itemserializer.cpp index 29b2a154c..e9a830f3f 100644 --- a/akonadi/itemserializer.cpp +++ b/akonadi/itemserializer.cpp @@ -1,215 +1,225 @@ /* Copyright (c) 2007 Till Adam Copyright (c) 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 "itemserializer.h" #include "item.h" #include "itemserializerplugin.h" #include "attributefactory.h" // KDE core #include #include #include // Qt #include +#include #include #include #include #include // temporary #include "pluginloader.h" namespace Akonadi { class DefaultItemSerializerPlugin; class DefaultItemSerializerPlugin : public ItemSerializerPlugin { public: DefaultItemSerializerPlugin() { } bool deserialize( Item& item, const QByteArray& label, QIODevice& data, int ) { if ( label != Item::FullPayload ) return false; item.setPayload( data.readAll() ); return true; } void serialize( const Item& item, const QByteArray& label, QIODevice& data, int& ) { Q_ASSERT( label == Item::FullPayload ); if ( item.hasPayload() ) data.write( item.payload() ); } }; K_GLOBAL_STATIC( DefaultItemSerializerPlugin, s_defaultItemSerializerPlugin ) } using namespace Akonadi; class PluginEntry { public: PluginEntry() : mPlugin( 0 ) { } PluginEntry( const QString &identifier ) : mIdentifier( identifier ), mPlugin( 0 ) { } inline ItemSerializerPlugin* plugin() const { if ( mPlugin ) return mPlugin; QObject *object = PluginLoader::self()->createForName( mIdentifier ); if ( !object ) { kWarning( 5250 ) << "ItemSerializerPluginLoader: " << "plugin" << mIdentifier << "is not valid!" << endl; // we try to use the default in that case mPlugin = s_defaultItemSerializerPlugin; } mPlugin = qobject_cast( object ); if ( !mPlugin ) { kWarning( 5250 ) << "ItemSerializerPluginLoader: " << "plugin" << mIdentifier << "doesn't provide interface ItemSerializerPlugin!" << endl; // we try to use the default in that case mPlugin = s_defaultItemSerializerPlugin; } Q_ASSERT( mPlugin ); return mPlugin; } private: QString mIdentifier; mutable ItemSerializerPlugin *mPlugin; }; static QHash * all = 0; static void loadPlugins() { const PluginLoader* pl = PluginLoader::self(); if ( !pl ) { kWarning( 5250 ) << "Cannot instantiate plugin loader!" << endl; return; } const QStringList types = pl->types(); kDebug( 5250 ) << "ItemSerializerPluginLoader: " << "found" << types.size() << "plugins." << endl; for ( QStringList::const_iterator it = types.begin() ; it != types.end() ; ++it ) { all->insert( *it, PluginEntry( *it ) ); } } static void setup() { if (!all) { all = new QHash(); loadPlugins(); } } /*static*/ -void ItemSerializer::deserialize( Item& item, const QByteArray& label, const QByteArray& data, int version ) +void ItemSerializer::deserialize( Item& item, const QByteArray& label, const QByteArray& data, int version, bool external ) { - QBuffer buffer; - buffer.setData( data ); - buffer.open( QIODevice::ReadOnly ); - buffer.seek( 0 ); - deserialize( item, label, buffer, version ); - buffer.close(); + + if ( external ) { + QFile file( QString::fromUtf8(data) ); + if ( file.open( QIODevice:: ReadOnly ) ) { + deserialize( item, label, file, version ); + file.close(); + } + } else { + QBuffer buffer; + buffer.setData( data ); + buffer.open( QIODevice::ReadOnly ); + buffer.seek( 0 ); + deserialize( item, label, buffer, version ); + buffer.close(); + } } /*static*/ void ItemSerializer::deserialize( Item& item, const QByteArray& label, QIODevice& data, int version ) { setup(); if ( !ItemSerializer::pluginForMimeType( item.mimeType() ).deserialize( item, label, data, version ) ) kWarning() << "Unable to deserialize payload part:" << label; } /*static*/ void ItemSerializer::serialize( const Item& item, const QByteArray& label, QByteArray& data, int &version ) { QBuffer buffer; buffer.setBuffer( &data ); buffer.open( QIODevice::WriteOnly ); buffer.seek( 0 ); serialize( item, label, buffer, version ); buffer.close(); } /*static*/ void ItemSerializer::serialize( const Item& item, const QByteArray& label, QIODevice& data, int &version ) { if ( !item.hasPayload() ) return; setup(); ItemSerializerPlugin& plugin = pluginForMimeType( item.mimeType() ); plugin.serialize( item, label, data, version ); } QSet ItemSerializer::parts(const Item & item) { if ( !item.hasPayload() ) return QSet(); setup(); return pluginForMimeType( item.mimeType() ).parts( item ); } /*static*/ ItemSerializerPlugin& ItemSerializer::pluginForMimeType( const QString & mimetype ) { if ( all->contains( mimetype ) ) return *(all->value( mimetype ).plugin()); KMimeType::Ptr mimeType = KMimeType::mimeType( mimetype, KMimeType::ResolveAliases ); if ( !mimeType.isNull() ) { foreach ( const QString &type, all->keys() ) { if ( mimeType->is( type ) ) { return *(all->value( type ).plugin() ); } } } kDebug( 5250 ) << "No plugin for mimetype " << mimetype << " found!"; kDebug( 5250 ) << "Available plugins are: " << all->keys(); ItemSerializerPlugin *plugin = s_defaultItemSerializerPlugin; Q_ASSERT(plugin); return *plugin; } diff --git a/akonadi/itemserializer.h b/akonadi/itemserializer.h index 8c4aeeed0..72d49268a 100644 --- a/akonadi/itemserializer.h +++ b/akonadi/itemserializer.h @@ -1,61 +1,61 @@ /* Copyright (c) 2007 Till Adam Copyright (c) 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_ITEM_SERIALIZER_H #define AKONADI_ITEM_SERIALIZER_H #include #include #include "akonadiprivate_export.h" class QIODevice; namespace Akonadi { class Item; class ItemSerializerPlugin; /** @internal Serialization/Deserialization of item parts, serializer plugin management. */ class AKONADI_TESTS_EXPORT ItemSerializer { public: /** throws ItemSerializerException on failure */ - static void deserialize( Item& item, const QByteArray& label, const QByteArray& data, int version ); + static void deserialize( Item& item, const QByteArray& label, const QByteArray& data, int version, bool external ); /** throws ItemSerializerException on failure */ static void deserialize( Item& item, const QByteArray& label, QIODevice& data, int version ); /** throws ItemSerializerException on failure */ static void serialize( const Item& item, const QByteArray& label, QByteArray& data, int &version ); /** throws ItemSerializerException on failure */ static void serialize( const Item& item, const QByteArray& label, QIODevice& data, int &version ); /** Returns a list of parts available in the item payload. */ static QSet parts( const Item &item ); private: static ItemSerializerPlugin& pluginForMimeType( const QString& mimetype ); }; } #endif diff --git a/akonadi/session_p.h b/akonadi/session_p.h index 8ef4e2002..313d79364 100644 --- a/akonadi/session_p.h +++ b/akonadi/session_p.h @@ -1,112 +1,112 @@ /* Copyright (c) 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_SESSION_P_H #define AKONADI_SESSION_P_H #include "session.h" #include "imapparser_p.h" #include #include #include class QLocalSocket; namespace Akonadi { /** * @internal */ class SessionPrivate { public: SessionPrivate( Session *parent ) : mParent( parent ), mConnectionSettings( 0 ), protocolVersion( 0 ) { parser = new ImapParser(); } ~SessionPrivate() { delete parser; delete mConnectionSettings; } void startNext(); void reconnect(); void socketError(); void dataReceived(); void doStartNext(); void startJob( Job* job ); void jobDone( KJob* job ); void jobWriteFinished( Akonadi::Job* job ); void jobDestroyed( QObject *job ); bool canPipelineNext(); /** * Creates a new default session for this thread with * the given @p sessionId. The session can be accessed * later by defaultSession(). * * You only need to call this method if you want that the * default session has a special custom id, otherwise a random unique * id is used automatically. */ static void createDefaultSession( const QByteArray &sessionId ); /** Associates the given Job object with this session. */ void addJob( Job* job ); /** Returns the next IMAP tag. */ int nextTag(); /** Sends the given raw data. */ void writeData( const QByteArray &data ); - static int minimumProtocolVersion() { return 6; } + static int minimumProtocolVersion() { return 7; } Session *mParent; QByteArray sessionId; QSettings *mConnectionSettings; QLocalSocket* socket; bool connected; int theNextTag; int protocolVersion; // job management QQueue queue; QQueue pipeline; Job* currentJob; bool jobRunning; // parser stuff ImapParser *parser; }; } #endif