2
Copyright (c) 2007 Volker Krause <vkrause@kde.org>
3
Copyright (c) 2013 Daniel Vrátil <dvratil@redhat.com>
5
This library is free software; you can redistribute it and/or modify it
6
under the terms of the GNU Library General Public License as published by
7
the Free Software Foundation; either version 2 of the License, or (at your
8
option) any later version.
10
This library is distributed in the hope that it will be useful, but WITHOUT
11
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
13
License for more details.
15
You should have received a copy of the GNU Library General Public License
16
along with this library; see the file COPYING.LIB. If not, write to the
17
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22
#include "notificationmessagev2_p.h"
23
#include "notificationmessage_p.h"
24
#include "imapparser_p.h"
26
#include <QtCore/QDebug>
27
#include <QtCore/QHash>
28
#include <QtDBus/QDBusMetaType>
29
#include <qdbusconnection.h>
31
using namespace Akonadi;
33
class NotificationMessageV2::Private: public QSharedData
39
operation( InvalidOp ),
40
parentCollection( -1 ),
41
parentDestCollection( -1 )
45
Private( const Private &other )
46
: QSharedData( other )
48
sessionId = other.sessionId;
50
operation = other.operation;
52
resource = other.resource;
53
destResource = other.destResource;
54
parentCollection = other.parentCollection;
55
parentDestCollection = other.parentDestCollection;
57
addedFlags = other.addedFlags;
58
removedFlags = other.removedFlags;
61
bool compareWithoutOpAndParts( const Private &other ) const
63
return items == other.items
65
&& sessionId == other.sessionId
66
&& resource == other.resource
67
&& destResource == other.destResource
68
&& parentCollection == other.parentCollection
69
&& parentDestCollection == other.parentDestCollection;
72
bool operator==( const Private &other ) const
74
return operation == other.operation && parts == other.parts &&
75
addedFlags == other.addedFlags && removedFlags == other.removedFlags &&
76
compareWithoutOpAndParts( other );
80
static bool appendAndCompressImpl( T &list, const NotificationMessageV2 &msg );
83
NotificationMessageV2::Type type;
84
NotificationMessageV2::Operation operation;
85
QMap<Id, NotificationMessageV2::Entity> items;
87
QByteArray destResource;
89
Id parentDestCollection;
90
QSet<QByteArray> parts;
91
QSet<QByteArray> addedFlags;
92
QSet<QByteArray> removedFlags;
95
NotificationMessageV2::NotificationMessageV2():
100
NotificationMessageV2::NotificationMessageV2( const NotificationMessageV2 &other ):
105
NotificationMessageV2::~NotificationMessageV2()
109
NotificationMessageV2& NotificationMessageV2::operator=( const NotificationMessageV2 &other )
111
if ( this != &other ) {
118
bool NotificationMessageV2::operator==( const NotificationMessageV2 &other ) const
123
void NotificationMessageV2::registerDBusTypes()
125
qDBusRegisterMetaType<Akonadi::NotificationMessageV2>();
126
qDBusRegisterMetaType<Akonadi::NotificationMessageV2::Entity>();
127
qDBusRegisterMetaType<Akonadi::NotificationMessageV2::List>();
130
void NotificationMessageV2::addEntity( Id id, const QString &remoteId, const QString &remoteRevision, const QString &mimeType )
132
NotificationMessageV2::Entity item;
134
item.remoteId = remoteId;
135
item.remoteRevision = remoteRevision;
136
item.mimeType = mimeType;
138
d->items.insert( id, item );
141
void NotificationMessageV2::setEntities( const QList<NotificationMessageV2::Entity> &items )
144
Q_FOREACH( const NotificationMessageV2::Entity &item, items ) {
145
d->items.insert( item.id, item );
149
QMap<NotificationMessageV2::Id, NotificationMessageV2::Entity> NotificationMessageV2::entities() const
154
NotificationMessageV2::Entity NotificationMessageV2::entity( NotificationMessageV2::Id id ) const
156
return d->items.value( id );
159
QList<NotificationMessageV2::Id> NotificationMessageV2::uids() const
161
return d->items.keys();
164
QByteArray NotificationMessageV2::sessionId() const
169
void NotificationMessageV2::setSessionId( const QByteArray &sessionId )
171
d->sessionId = sessionId;
174
NotificationMessageV2::Type NotificationMessageV2::type() const
179
void NotificationMessageV2::setType( Type type )
184
NotificationMessageV2::Operation NotificationMessageV2::operation() const
189
void NotificationMessageV2::setOperation( Operation operation )
191
d->operation = operation;
194
QByteArray NotificationMessageV2::resource() const
199
void NotificationMessageV2::setResource( const QByteArray &resource )
201
d->resource = resource;
204
NotificationMessageV2::Id NotificationMessageV2::parentCollection() const
206
return d->parentCollection;
209
NotificationMessageV2::Id NotificationMessageV2::parentDestCollection() const
211
return d->parentDestCollection;
214
void NotificationMessageV2::setParentCollection( Id parent )
216
d->parentCollection = parent;
219
void NotificationMessageV2::setParentDestCollection( Id parent )
221
d->parentDestCollection = parent;
224
void NotificationMessageV2::setDestinationResource( const QByteArray &destResource )
226
d->destResource = destResource;
229
QByteArray NotificationMessageV2::destinationResource() const
231
return d->destResource;
234
QSet<QByteArray> NotificationMessageV2::itemParts() const
239
void NotificationMessageV2::setItemParts( const QSet<QByteArray> &parts )
244
QSet<QByteArray> NotificationMessageV2::addedFlags() const
246
return d->addedFlags;
249
void NotificationMessageV2::setAddedFlags( const QSet<QByteArray> &addedFlags )
251
d->addedFlags = addedFlags;
254
QSet<QByteArray> NotificationMessageV2::removedFlags() const
256
return d->removedFlags;
259
void NotificationMessageV2::setRemovedFlags( const QSet<QByteArray> &removedFlags )
261
d->removedFlags = removedFlags;
264
QString NotificationMessageV2::toString() const
270
rv += QLatin1String( "Items " );
273
rv += QLatin1String( "Collections " );
276
return QLatin1String( "*INVALID TYPE* " );
279
QSet<QByteArray> items;
280
Q_FOREACH ( const NotificationMessageV2::Entity &item, d->items ) {
281
QString itemStr = QString::fromLatin1( "(%1,%2" ).arg( item.id ).arg( item.remoteId );
282
if ( !item.remoteRevision.isEmpty() ) {
283
itemStr += QString::fromLatin1( ",%1" ).arg( item.remoteRevision );
285
if ( !item.mimeType.isEmpty() ) {
286
itemStr += QString::fromLatin1( ",%1" ).arg( item.mimeType );
288
itemStr += QLatin1String( ")" );
289
items << itemStr.toLatin1();
291
rv += QLatin1String( "(" ) + QString::fromLatin1( ImapParser::join( items, ", " ) ) + QLatin1String( ")" );
293
if ( d->parentDestCollection >= 0 ) {
294
rv += QLatin1String( " from " );
296
rv += QLatin1String( " in " );
299
if ( d->parentCollection >= 0 ) {
300
rv += QString::fromLatin1( "collection %1 " ).arg( d->parentCollection );
302
rv += QLatin1String( "unspecified parent collection " );
305
switch ( d->operation ) {
307
rv += QLatin1String( "added" );
310
rv += QLatin1String( "modified parts (" );
311
rv += QString::fromLatin1( ImapParser::join( d->parts.toList(), ", " ) );
312
rv += QLatin1String( ")" );
315
rv += QLatin1String( "added flags (" );
316
rv += QString::fromLatin1( ImapParser::join( d->addedFlags.toList(), ", " ) );
317
rv += QLatin1String ( ") " );
319
rv += QLatin1String( "removed flags (" );
320
rv += QString::fromLatin1( ImapParser::join( d->removedFlags.toList(), ", " ) );
321
rv += QLatin1String ( ") " );
324
rv += QLatin1String( "moved" );
327
rv += QLatin1String( "removed" );
330
rv += QLatin1String( "linked" );
333
rv += QLatin1String( "unlinked" );
336
rv += QLatin1String( "subscribed" );
339
rv += QLatin1String( "unsubscribed" );
342
return QLatin1String( "*INVALID OPERATION*" );
345
if ( d->parentDestCollection >= 0 )
346
rv += QString::fromLatin1( " to collection %1" ).arg( d->parentDestCollection );
351
QDBusArgument& operator<<( QDBusArgument &arg, const Akonadi::NotificationMessageV2 &msg )
353
arg.beginStructure();
354
arg << msg.sessionId();
355
arg << static_cast<int>( msg.type() );
356
arg << static_cast<int>( msg.operation() );
357
arg << msg.entities().values();
358
arg << msg.resource();
359
arg << msg.destinationResource();
360
arg << msg.parentCollection();
361
arg << msg.parentDestCollection();
363
QStringList itemParts;
364
Q_FOREACH ( const QByteArray &itemPart, msg.itemParts() ) {
365
itemParts.append( QString::fromLatin1( itemPart ) );
369
arg << msg.addedFlags().toList();
370
arg << msg.removedFlags().toList();
376
const QDBusArgument& operator>>( const QDBusArgument &arg, Akonadi::NotificationMessageV2 &msg )
380
QList<NotificationMessageV2::Entity> items;
381
NotificationMessageV2::Id id;
384
QList<QByteArray> bal;
386
arg.beginStructure();
388
msg.setSessionId( ba );
390
msg.setType( static_cast<NotificationMessageV2::Type>( i ) );
392
msg.setOperation( static_cast<NotificationMessageV2::Operation>( i ) );
394
msg.setEntities( items );
396
msg.setResource( ba );
398
msg.setDestinationResource( ba );
400
msg.setParentCollection( id );
402
msg.setParentDestCollection( id );
406
QSet<QByteArray> itemParts;
407
Q_FOREACH ( const QString &itemPart, strl ) {
408
itemParts.insert( itemPart.toLatin1() );
410
msg.setItemParts( itemParts );
413
msg.setAddedFlags( bal.toSet() );
415
msg.setRemovedFlags( bal.toSet() );
421
QDBusArgument& operator<<( QDBusArgument &arg, const Akonadi::NotificationMessageV2::Entity &item )
423
arg.beginStructure();
425
arg << item.remoteId;
426
arg << item.remoteRevision;
427
arg << item.mimeType;
433
const QDBusArgument& operator>>( const QDBusArgument &arg, Akonadi::NotificationMessageV2::Entity &item )
435
arg.beginStructure();
437
arg >> item.remoteId;
438
arg >> item.remoteRevision;
439
arg >> item.mimeType;
445
uint qHash( const Akonadi::NotificationMessageV2 &msg )
448
Q_FOREACH( const NotificationMessageV2::Entity &item, msg.entities() ) {
452
return qHash( i + (msg.type() << 31) + (msg.operation() << 28) );
455
QVector<NotificationMessage> NotificationMessageV2::toNotificationV1() const
457
QVector<NotificationMessage> v1;
459
Q_FOREACH( const Entity &item, d->items ) {
460
NotificationMessage msgv1;
461
msgv1.setSessionId( d->sessionId );
462
msgv1.setUid( item.id );
463
msgv1.setRemoteId( item.remoteId );
464
msgv1.setMimeType( item.mimeType );
465
msgv1.setType( static_cast<NotificationMessage::Type>( d->type ) );
466
if ( d->operation == ModifyFlags ) {
467
msgv1.setOperation( NotificationMessage::Modify );
469
msgv1.setOperation( static_cast<NotificationMessage::Operation>( d->operation ) );
472
msgv1.setResource( d->resource );
473
msgv1.setDestinationResource( d->destResource );
474
msgv1.setParentCollection( d->parentCollection );
475
msgv1.setParentDestCollection( d->parentDestCollection );
477
// Backward compatibility hack
478
QSet<QByteArray> parts;
479
if ( d->operation == Remove ) {
480
QByteArray rr = item.remoteRevision.toLatin1();
481
parts << (rr.isEmpty() ? "1" : rr);
482
} else if ( d->operation == ModifyFlags ) {
487
msgv1.setItemParts( parts );
495
template <typename T>
496
bool NotificationMessageV2::Private::appendAndCompressImpl( T &list, const NotificationMessageV2 &msg )
498
// fast-path for stuff that is not considered during O(n) compression below
499
if ( msg.operation() != Add && msg.operation() != Link && msg.operation() != Unlink && msg.operation() != Subscribe && msg.operation() != Unsubscribe && msg.operation() != Move ) {
501
typename T::Iterator end = list.end();
502
for ( typename T::Iterator it = list.begin(); it != end; ) {
503
if ( msg.d.constData()->compareWithoutOpAndParts( *((*it).d.constData()) ) ) {
505
// both are modifications, merge them together and drop the new one
506
if ( msg.operation() == Modify && it->operation() == Modify ) {
507
(*it).setItemParts( (*it).itemParts() + msg.itemParts() );
511
else if ( msg.operation() == ModifyFlags && it->operation() == ModifyFlags ) {
512
(*it).setAddedFlags( (*it).addedFlags() + msg.addedFlags() );
513
(*it).setRemovedFlags( (*it).removedFlags() + msg.removedFlags() );
515
// If merged notifications result in no-change notification, drop both.
516
if ( (*it).addedFlags() == (*it).removedFlags() ) {
517
it = list.erase( it );
523
// new one is a modification, the existing one not, so drop the new one
524
else if ( ( ( msg.operation() == Modify ) || ( msg.operation() == ModifyFlags ) ) && ( (*it).operation() != Modify) && (*it).operation() != ModifyFlags ) {
527
// new on is a deletion, erase the existing modification ones (and keep going, in case there are more)
528
else if ( msg.operation() == Remove && ((*it).operation() == Modify || (*it).operation() == ModifyFlags) ) {
529
it = list.erase( it );
546
bool NotificationMessageV2::appendAndCompress( NotificationMessageV2::List &list, const NotificationMessageV2 &msg )
548
return Private::appendAndCompressImpl<NotificationMessageV2::List>( list, msg );
551
bool NotificationMessageV2::appendAndCompress( QList<NotificationMessageV2> &list, const NotificationMessageV2 &msg )
553
return Private::appendAndCompressImpl< QList<NotificationMessageV2> >( list, msg );