Commit 33a14e7f authored by Volker Krause's avatar Volker Krause

Add item part namespaces. This is necessary to distinguish between

remote parts that can be retrieved from the resource (payload) and local
parts (attributes), which again is necessary to finally implement all
the fetch modes advertised in ItemFetchScope.

Existing data should be converted correctly, but you need to update the
server as well as the client library.

svn path=/trunk/KDE/kdepimlibs/; revision=812135
parent d76c544d
......@@ -25,6 +25,7 @@
#include "item.h"
#include "itemserializer.h"
#include "job_p.h"
#include "protocolhelper.h"
#include <kdebug.h>
......@@ -53,8 +54,6 @@ ItemCreateJob::ItemCreateJob( const Item &item, const Collection &collection, QO
Q_ASSERT( !item.mimeType().isEmpty() );
d->mItem = item;
d->mParts = d->mItem.loadedPayloadParts();
foreach ( const Attribute *attr, item.attributes() )
d->mParts << attr->type();
d->mCollection = collection;
}
......@@ -71,7 +70,7 @@ void ItemCreateJob::doStart()
if ( !d->mItem.remoteId().isEmpty() )
remoteId = ' ' + ImapParser::quote( "\\RemoteId[" + d->mItem.remoteId().toUtf8() + ']' );
// switch between a normal APPEND and a multipart X-AKAPPEND, based on the number of parts
if ( d->mParts.isEmpty() || (d->mParts.size() == 1 && d->mParts.contains( Item::FullPayload )) ) {
if ( d->mItem.attributes().isEmpty() && ( d->mParts.isEmpty() || (d->mParts.size() == 1 && d->mParts.contains( Item::FullPayload )) ) ) {
if ( d->mItem.hasPayload() ) {
int version = 0;
ItemSerializer::serialize( d->mItem, Item::FullPayload, d->mData, version );
......@@ -93,11 +92,17 @@ void ItemCreateJob::doStart()
int version = 0;
ItemSerializer::serialize( d->mItem, partName, partData, version );
totalSize += partData.size();
QByteArray versionString( version != 0 ? '[' + QByteArray::number( version ) + ']' : "" );
partSpecs.append( ImapParser::quote( partName + versionString ) + ':' +
QByteArray::number( partData.size() ) );
const QByteArray partId = ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartPayload, partName, version );
partSpecs.append( ImapParser::quote( partId ) + ':' + QByteArray::number( partData.size() ) );
d->mData += partData;
}
foreach ( const Attribute* attr, d->mItem.attributes() ) {
const QByteArray data = attr->serialized();
totalSize += data.size();
const QByteArray partId = ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartAttribute, attr->type() );
partSpecs.append( ImapParser::quote( partId ) + ':' + QByteArray::number( data.size() ) );
d->mData += data;
}
command += '(' + ImapParser::join( partSpecs, "," ) + ") " +
'{' + QByteArray::number( totalSize ) + "}\n";
......
......@@ -19,6 +19,7 @@
#include "itemfetchjob.h"
#include "attributefactory.h"
#include "collection.h"
#include "collectionselectjob.h"
#include "imapparser_p.h"
......@@ -28,6 +29,7 @@
#include "job_p.h"
#include "entity_p.h"
#include "protocol_p.h"
#include "protocolhelper.h"
#include <kdebug.h>
......@@ -85,9 +87,9 @@ void ItemFetchJobPrivate::startFetchJob()
command += " (UID REMOTEID FLAGS";
foreach ( const QByteArray &part, mFetchScope.payloadParts() )
command += ' ' + part;
command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartPayload, part );
foreach ( const QByteArray &part, mFetchScope.attributes() )
command += ' ' + part;
command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartAttribute, part );
command += ")\n";
writeData( command );
......@@ -210,10 +212,27 @@ void ItemFetchJob::doHandleResponse( const QByteArray & tag, const QByteArray &
} else {
int version = 0;
QByteArray plainKey( key );
ProtocolHelper::PartNamespace ns;
ImapParser::splitVersionedKey( key, plainKey, version );
ItemSerializer::deserialize( item, plainKey, fetchResponse.value( i + 1 ), version );
plainKey = ProtocolHelper::decodePartIdentifier( plainKey, ns );
switch ( ns ) {
case ProtocolHelper::PartPayload:
ItemSerializer::deserialize( item, plainKey, fetchResponse.value( i + 1 ), version );
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;
}
}
}
......
......@@ -57,9 +57,7 @@ QByteArray ItemModifyJobPrivate::nextPartHeader()
mPendingData.clear();
int version = 0;
ItemSerializer::serialize( mItem, label, mPendingData, version );
command += ' ' + label;
if ( version != 0 ) // '0' is the default
command += '[' + QByteArray::number( version ) + ']';
command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartPayload, label, version );
command += ".SILENT {" + QByteArray::number( mPendingData.size() ) + '}';
if ( mPendingData.size() > 0 )
command += '\n';
......@@ -120,7 +118,10 @@ void ItemModifyJob::doStart()
if ( !d->mItem.d_func()->mDeletedAttributes.isEmpty() ) {
changes << "-PARTS.SILENT";
changes << '(' + ImapParser::join( d->mItem.d_func()->mDeletedAttributes, " " ) + ')';
QList<QByteArray> attrs;
foreach ( const QByteArray &attr, d->mItem.d_func()->mDeletedAttributes )
attrs << ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartAttribute, attr );
changes << '(' + ImapParser::join( attrs, " " ) + ')';
}
// nothing to do
......@@ -139,7 +140,7 @@ void ItemModifyJob::doStart()
}
command += " (" + ImapParser::join( changes, " " );
const QByteArray attrs = ProtocolHelper::attributesToByteArray( d->mItem );
const QByteArray attrs = ProtocolHelper::attributesToByteArray( d->mItem, true );
if ( !attrs.isEmpty() )
command += ' ' + attrs;
command += d->nextPartHeader();
......
......@@ -157,14 +157,9 @@ void ItemSerializer::deserialize( Item& item, const QByteArray& label, const QBy
/*static*/
void ItemSerializer::deserialize( Item& item, const QByteArray& label, QIODevice& data, int version )
{
setup();
if ( !ItemSerializer::pluginForMimeType( item.mimeType() ).deserialize( item, label, data, version ) ) {
data.seek( 0 );
Attribute* attr = AttributeFactory::createAttribute( label );
Q_ASSERT( attr );
attr->deserialize( data.readAll() );
item.addAttribute( attr );
}
setup();
if ( !ItemSerializer::pluginForMimeType( item.mimeType() ).deserialize( item, label, data, version ) )
kWarning() << "Unable to deserialize payload part:" << label;
}
/*static*/
......@@ -181,18 +176,11 @@ void ItemSerializer::serialize( const Item& item, const QByteArray& label, QByte
/*static*/
void ItemSerializer::serialize( const Item& item, const QByteArray& label, QIODevice& data, int &version )
{
setup();
ItemSerializerPlugin& plugin = pluginForMimeType( item.mimeType() );
const QSet<QByteArray> supportedParts = plugin.parts( item );
if ( !supportedParts.contains( label ) ) {
Attribute* attr = item.attribute( label );
if ( attr )
data.write( attr->serialized() );
return;
}
if ( !item.hasPayload() )
return;
plugin.serialize( item, label, data, version );
if ( !item.hasPayload() )
return;
setup();
ItemSerializerPlugin& plugin = pluginForMimeType( item.mimeType() );
plugin.serialize( item, label, data, version );
}
QSet<QByteArray> ItemSerializer::parts(const Item & item)
......
......@@ -48,7 +48,7 @@ int ProtocolHelper::parseCachePolicy(const QByteArray & data, CachePolicy & poli
QVarLengthArray<QByteArray,16> tmp;
QStringList parts;
Akonadi::ImapParser::parseParenthesizedList( value, tmp );
for ( int j=0; j<tmp.size(); j++ )
for ( int j=0; j<tmp.size(); j++ )
parts << QString::fromLatin1( tmp[j] );
policy.setLocalParts( parts );
}
......@@ -113,7 +113,7 @@ int ProtocolHelper::parseCollection(const QByteArray & data, Collection & collec
QVarLengthArray<QByteArray,16> ct;
ImapParser::parseParenthesizedList( value, ct );
QStringList ct2;
for ( int j=0; j<ct.size(); j++ )
for ( int j = 0; j < ct.size(); j++ )
ct2 << QString::fromLatin1( ct[j] );
collection.setContentMimeTypes( ct2 );
} else if ( key == "CACHEPOLICY" ) {
......@@ -131,12 +131,42 @@ int ProtocolHelper::parseCollection(const QByteArray & data, Collection & collec
return pos;
}
QByteArray ProtocolHelper::attributesToByteArray(const Entity & entity)
QByteArray ProtocolHelper::attributesToByteArray(const Entity & entity, bool ns )
{
QList<QByteArray> l;
foreach ( const Attribute *attr, entity.attributes() ) {
l << attr->type();
l << encodePartIdentifier( ns ? PartAttribute : PartGlobal, attr->type() );
l << ImapParser::quote( attr->serialized() );
}
return ImapParser::join( l, " " );
}
QByteArray ProtocolHelper::encodePartIdentifier(PartNamespace ns, const QByteArray & label, int version )
{
const QByteArray versionString( version != 0 ? '[' + QByteArray::number( version ) + ']' : "" );
switch ( ns ) {
case PartGlobal:
return label + versionString;
case PartPayload:
return "PLD:" + label + versionString;
case PartAttribute:
return "ATR:" + label + versionString;
default:
Q_ASSERT( false );
}
return QByteArray();
}
QByteArray ProtocolHelper::decodePartIdentifier( const QByteArray &data, PartNamespace & ns )
{
if ( data.startsWith( "PLD:" ) ) {
ns = PartPayload;
return data.mid( 4 );
} else if ( data.startsWith( "ATR:" ) ) {
ns = PartAttribute;
return data.mid( 4 );
} else {
ns = PartGlobal;
return data;
}
}
......@@ -36,6 +36,13 @@ namespace Akonadi {
class ProtocolHelper
{
public:
/** Part namespaces. */
enum PartNamespace {
PartGlobal,
PartPayload,
PartAttribute
};
/**
Parse a cache policy definition.
@param data The input data.
......@@ -62,8 +69,17 @@ class ProtocolHelper
/**
Convert attributes to their protocol representation.
*/
static QByteArray attributesToByteArray( const Entity &entity );
static QByteArray attributesToByteArray( const Entity &entity, bool ns = false );
/**
Encodes part label and namespace.
*/
static QByteArray encodePartIdentifier( PartNamespace ns, const QByteArray &label, int version = 0 );
/**
Decode part label and namespace.
*/
static QByteArray decodePartIdentifier( const QByteArray &data, PartNamespace &ns );
};
}
......
......@@ -42,7 +42,7 @@ using namespace Akonadi;
//@cond PRIVATE
static const int minimumProtocolVersion = 0;
static const int minimumProtocolVersion = 2;
void SessionPrivate::startNext()
{
......
......@@ -18,7 +18,6 @@
*/
#include "itemserializertest.h"
#include "testattribute.h"
#include <akonadi/attributefactory.h>
#include <akonadi/item.h>
......@@ -55,25 +54,4 @@ void ItemSerializerTest::testDefaultSerializer()
QCOMPARE( serialized, data );
}
void ItemSerializerTest::testExtraPart()
{
AttributeFactory::registerAttribute<TestAttribute>();
QByteArray data = "foo";
Item item;
item.setMimeType( "application/octet-stream" );
ItemSerializer::deserialize( item, "EXTRA", data, 0 );
QVERIFY( !item.hasPayload() );
QVERIFY( item.loadedPayloadParts().isEmpty() );
QCOMPARE( item.attributes().count(), 1 );
QVERIFY( item.hasAttribute<TestAttribute>() );
QCOMPARE( item.attribute<TestAttribute>()->data, data );
QByteArray ser;
int version = 0;
ItemSerializer::serialize( item, "EXTRA", ser, version );
QCOMPARE( ser, data );
}
#include "itemserializertest.moc"
......@@ -28,7 +28,6 @@ class ItemSerializerTest : public QObject
private slots:
void testEmptyPayload();
void testDefaultSerializer();
void testExtraPart();
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment