Akonadi

protocol.cpp
1/*
2 * SPDX-FileCopyrightText: 2015 Daniel Vrátil <dvratil@redhat.com>
3 * SPDX-FileCopyrightText: 2016 Daniel Vrátil <dvratil@kde.org>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8#include "datastream_p_p.h"
9#include "protocol_p.h"
10#include "scope_p.h"
11
12#include <type_traits>
13#include <typeinfo>
14
15#include <QHash>
16#include <QJsonArray>
17#include <QJsonObject>
18
19#include <cassert>
20
21// clazy:excludeall=function-args-by-value
22
23#undef AKONADI_DECLARE_PRIVATE
24#define AKONADI_DECLARE_PRIVATE(Class) \
25 inline Class##Private *Class::d_func() \
26 { \
27 return reinterpret_cast<Class##Private *>(d_ptr.data()); \
28 } \
29 inline const Class##Private *Class::d_func() const \
30 { \
31 return reinterpret_cast<const Class##Private *>(d_ptr.constData()); \
32 }
33
34namespace Akonadi
35{
36namespace Protocol
37{
38QDebug operator<<(QDebug _dbg, Command::Type type)
39{
40 QDebug dbg(_dbg.noquote());
41
42 switch (type) {
43 case Command::Invalid:
44 return dbg << "Invalid";
45
46 case Command::Hello:
47 return dbg << "Hello";
48 case Command::Login:
49 return dbg << "Login";
50 case Command::Logout:
51 return dbg << "Logout";
52
53 case Command::Transaction:
54 return dbg << "Transaction";
55
56 case Command::CreateItem:
57 return dbg << "CreateItem";
58 case Command::CopyItems:
59 return dbg << "CopyItems";
60 case Command::DeleteItems:
61 return dbg << "DeleteItems";
62 case Command::FetchItems:
63 return dbg << "FetchItems";
64 case Command::LinkItems:
65 return dbg << "LinkItems";
66 case Command::ModifyItems:
67 return dbg << "ModifyItems";
68 case Command::MoveItems:
69 return dbg << "MoveItems";
70
71 case Command::CreateCollection:
72 return dbg << "CreateCollection";
73 case Command::CopyCollection:
74 return dbg << "CopyCollection";
75 case Command::DeleteCollection:
76 return dbg << "DeleteCollection";
77 case Command::FetchCollections:
78 return dbg << "FetchCollections";
79 case Command::FetchCollectionStats:
80 return dbg << "FetchCollectionStats";
81 case Command::ModifyCollection:
82 return dbg << "ModifyCollection";
83 case Command::MoveCollection:
84 return dbg << "MoveCollection";
85
86 case Command::Search:
87 return dbg << "Search";
88 case Command::SearchResult:
89 return dbg << "SearchResult";
90 case Command::StoreSearch:
91 return dbg << "StoreSearch";
92
93 case Command::CreateTag:
94 return dbg << "CreateTag";
95 case Command::DeleteTag:
96 return dbg << "DeleteTag";
97 case Command::FetchTags:
98 return dbg << "FetchTags";
99 case Command::ModifyTag:
100 return dbg << "ModifyTag";
101
102 case Command::FetchRelations:
103 return dbg << "FetchRelations";
104 case Command::ModifyRelation:
105 return dbg << "ModifyRelation";
106 case Command::RemoveRelations:
107 return dbg << "RemoveRelations";
108
109 case Command::SelectResource:
110 return dbg << "SelectResource";
111
112 case Command::StreamPayload:
113 return dbg << "StreamPayload";
114 case Command::ItemChangeNotification:
115 return dbg << "ItemChangeNotification";
116 case Command::CollectionChangeNotification:
117 return dbg << "CollectionChangeNotification";
118 case Command::TagChangeNotification:
119 return dbg << "TagChangeNotification";
120 case Command::RelationChangeNotification:
121 return dbg << "RelationChangeNotification";
122 case Command::SubscriptionChangeNotification:
123 return dbg << "SubscriptionChangeNotification";
124 case Command::DebugChangeNotification:
125 return dbg << "DebugChangeNotification";
126 case Command::CreateSubscription:
127 return dbg << "CreateSubscription";
128 case Command::ModifySubscription:
129 return dbg << "ModifySubscription";
130
131 case Command::_ResponseBit:
132 Q_ASSERT(false);
133 return dbg << static_cast<int>(type);
134 }
135
136 Q_ASSERT(false);
137 return dbg << static_cast<int>(type);
138}
139
140template<typename T>
141DataStream &operator<<(DataStream &stream, const QSharedPointer<T> &ptr)
142{
143 Protocol::serialize(stream, ptr);
144 return stream;
145}
146
147template<typename T>
148DataStream &operator>>(DataStream &stream, QSharedPointer<T> &ptr)
149{
150 ptr = Protocol::deserialize(stream.device()).staticCast<T>();
151 return stream;
152}
153
154/******************************************************************************/
155
156Command::Command(quint8 type)
157 : mType(type)
158{
159}
160
161bool Command::operator==(const Command &other) const
162{
163 return mType == other.mType;
164}
165
166void Command::toJson(QJsonObject &json) const
167{
168 json[QStringLiteral("response")] = static_cast<bool>(mType & Command::_ResponseBit);
169
170#define case_label(x) \
171 case Command::x: { \
172 json[QStringLiteral("type")] = QStringLiteral(#x); \
173 } break;
174
175 switch (mType & ~Command::_ResponseBit) {
176 case_label(Invalid) case_label(Hello)
177
178 case_label(Login) case_label(Logout)
179
180 case_label(Transaction)
181
182 case_label(CreateItem) case_label(CopyItems) case_label(DeleteItems) case_label(FetchItems) case_label(LinkItems) case_label(ModifyItems)
183 case_label(MoveItems)
184
185 case_label(CreateCollection) case_label(CopyCollection) case_label(DeleteCollection) case_label(FetchCollections)
186 case_label(FetchCollectionStats) case_label(ModifyCollection) case_label(MoveCollection)
187
188 case_label(Search) case_label(SearchResult) case_label(StoreSearch)
189
190 case_label(CreateTag) case_label(DeleteTag) case_label(FetchTags) case_label(ModifyTag)
191
192 case_label(FetchRelations) case_label(ModifyRelation) case_label(RemoveRelations)
193
194 case_label(SelectResource)
195
196 case_label(StreamPayload) case_label(CreateSubscription) case_label(ModifySubscription)
197
198 case_label(DebugChangeNotification) case_label(ItemChangeNotification)
199 case_label(CollectionChangeNotification) case_label(TagChangeNotification)
200 case_label(RelationChangeNotification) case_label(SubscriptionChangeNotification)
201 }
202#undef case_label
203}
204
205DataStream &operator<<(DataStream &stream, const Command &cmd)
206{
207 return stream << cmd.mType;
208}
209
210DataStream &operator>>(DataStream &stream, Command &cmd)
211{
212 return stream >> cmd.mType;
213}
214
215QDebug operator<<(QDebug dbg, const Command &cmd)
216{
217 return dbg.noquote() << ((cmd.mType & Command::_ResponseBit) ? "Response:" : "Command:") << static_cast<Command::Type>(cmd.mType & ~Command::_ResponseBit)
218 << "\n";
219}
220
221void toJson(const Akonadi::Protocol::Command *command, QJsonObject &json)
222{
223#define case_notificationlabel(x, class) \
224 case Command::x: { \
225 static_cast<const Akonadi::Protocol::class *>(command)->toJson(json); \
226 } break;
227#define case_commandlabel(x, cmd, resp) \
228 case Command::x: { \
229 static_cast<const Akonadi::Protocol::cmd *>(command)->toJson(json); \
230 } break; \
231 case Command::x | Command::_ResponseBit: { \
232 static_cast<const Akonadi::Protocol::resp *>(command)->toJson(json); \
233 } break;
234
235 switch (command->mType) {
236 case Command::Invalid:
237 break;
238
239 case Command::Hello | Command::_ResponseBit: {
240 static_cast<const Akonadi::Protocol::HelloResponse *>(command)->toJson(json);
241 } break;
242 case_commandlabel(Login, LoginCommand, LoginResponse) case_commandlabel(Logout, LogoutCommand, LogoutResponse)
243
244 case_commandlabel(Transaction, TransactionCommand, TransactionResponse)
245
246 case_commandlabel(CreateItem, CreateItemCommand, CreateItemResponse) case_commandlabel(CopyItems, CopyItemsCommand, CopyItemsResponse)
247 case_commandlabel(DeleteItems, DeleteItemsCommand, DeleteItemsResponse) case_commandlabel(FetchItems, FetchItemsCommand, FetchItemsResponse)
248 case_commandlabel(LinkItems, LinkItemsCommand, LinkItemsResponse) case_commandlabel(
249 ModifyItems,
250 ModifyItemsCommand,
251 ModifyItemsResponse) case_commandlabel(MoveItems, MoveItemsCommand, MoveItemsResponse)
252
253 case_commandlabel(CreateCollection, CreateCollectionCommand, CreateCollectionResponse) case_commandlabel(
254 CopyCollection,
255 CopyCollectionCommand,
256 CopyCollectionResponse) case_commandlabel(DeleteCollection, DeleteCollectionCommand, DeleteCollectionResponse)
257 case_commandlabel(FetchCollections, FetchCollectionsCommand, FetchCollectionsResponse) case_commandlabel(
258 FetchCollectionStats,
259 FetchCollectionStatsCommand,
260 FetchCollectionStatsResponse) case_commandlabel(ModifyCollection, ModifyCollectionCommand, ModifyCollectionResponse)
261 case_commandlabel(MoveCollection, MoveCollectionCommand, MoveCollectionResponse)
262
263 case_commandlabel(Search, SearchCommand, SearchResponse) case_commandlabel(
264 SearchResult,
265 SearchResultCommand,
266 SearchResultResponse) case_commandlabel(StoreSearch, StoreSearchCommand, StoreSearchResponse)
267
268 case_commandlabel(CreateTag, CreateTagCommand, CreateTagResponse)
269 case_commandlabel(DeleteTag, DeleteTagCommand, DeleteTagResponse)
270 case_commandlabel(FetchTags, FetchTagsCommand, FetchTagsResponse)
271 case_commandlabel(ModifyTag, ModifyTagCommand, ModifyTagResponse)
272
273 case_commandlabel(FetchRelations, FetchRelationsCommand, FetchRelationsResponse)
274 case_commandlabel(ModifyRelation, ModifyRelationCommand, ModifyRelationResponse)
275 case_commandlabel(RemoveRelations, RemoveRelationsCommand, RemoveRelationsResponse)
276
277 case_commandlabel(SelectResource, SelectResourceCommand, SelectResourceResponse)
278
279 case_commandlabel(StreamPayload, StreamPayloadCommand, StreamPayloadResponse)
280 case_commandlabel(
281 CreateSubscription,
282 CreateSubscriptionCommand,
283 CreateSubscriptionResponse) case_commandlabel(ModifySubscription,
284 ModifySubscriptionCommand,
285 ModifySubscriptionResponse)
286
287 case_notificationlabel(DebugChangeNotification, DebugChangeNotification)
288 case_notificationlabel(ItemChangeNotification, ItemChangeNotification)
289 case_notificationlabel(CollectionChangeNotification,
290 CollectionChangeNotification)
291 case_notificationlabel(TagChangeNotification,
292 TagChangeNotification)
293 case_notificationlabel(RelationChangeNotification,
294 RelationChangeNotification)
295 case_notificationlabel(SubscriptionChangeNotification,
296 SubscriptionChangeNotification)
297 }
298#undef case_notificationlabel
299#undef case_commandlabel
300}
301
302/******************************************************************************/
303
304Response::Response()
305 : Command(Command::Invalid | Command::_ResponseBit)
306 , mErrorCode(0)
307{
308}
309
310Response::Response(Command::Type type)
311 : Command(type | Command::_ResponseBit)
312 , mErrorCode(0)
313{
314}
315
316bool Response::operator==(const Response &other) const
317{
318 return *static_cast<const Command *>(this) == static_cast<const Command &>(other) && mErrorCode == other.mErrorCode && mErrorMsg == other.mErrorMsg;
319}
320
321void Response::toJson(QJsonObject &json) const
322{
323 static_cast<const Command *>(this)->toJson(json);
324 if (isError()) {
326 error[QStringLiteral("code")] = errorCode();
327 error[QStringLiteral("message")] = errorMessage();
328 json[QStringLiteral("error")] = error;
329 } else {
330 json[QStringLiteral("error")] = false;
331 }
332}
333
334DataStream &operator<<(DataStream &stream, const Response &cmd)
335{
336 return stream << static_cast<const Command &>(cmd) << cmd.mErrorCode << cmd.mErrorMsg;
337}
338
339DataStream &operator>>(DataStream &stream, Response &cmd)
340{
341 return stream >> static_cast<Command &>(cmd) >> cmd.mErrorCode >> cmd.mErrorMsg;
342}
343
344QDebug operator<<(QDebug dbg, const Response &resp)
345{
346 return dbg.noquote() << static_cast<const Command &>(resp) << "Error code:" << resp.mErrorCode << "\n"
347 << "Error msg:" << resp.mErrorMsg << "\n";
348}
349
350/******************************************************************************/
351
352class FactoryPrivate
353{
354public:
355 typedef CommandPtr (*CommandFactoryFunc)();
356 typedef ResponsePtr (*ResponseFactoryFunc)();
357
358 FactoryPrivate()
359 {
360 // Session management
361 registerType<Command::Hello, Command /* invalid */, HelloResponse>();
362 registerType<Command::Login, LoginCommand, LoginResponse>();
363 registerType<Command::Logout, LogoutCommand, LogoutResponse>();
364
365 // Transactions
366 registerType<Command::Transaction, TransactionCommand, TransactionResponse>();
367
368 // Items
369 registerType<Command::CreateItem, CreateItemCommand, CreateItemResponse>();
370 registerType<Command::CopyItems, CopyItemsCommand, CopyItemsResponse>();
371 registerType<Command::DeleteItems, DeleteItemsCommand, DeleteItemsResponse>();
372 registerType<Command::FetchItems, FetchItemsCommand, FetchItemsResponse>();
373 registerType<Command::LinkItems, LinkItemsCommand, LinkItemsResponse>();
374 registerType<Command::ModifyItems, ModifyItemsCommand, ModifyItemsResponse>();
375 registerType<Command::MoveItems, MoveItemsCommand, MoveItemsResponse>();
376
377 // Collections
378 registerType<Command::CreateCollection, CreateCollectionCommand, CreateCollectionResponse>();
379 registerType<Command::CopyCollection, CopyCollectionCommand, CopyCollectionResponse>();
380 registerType<Command::DeleteCollection, DeleteCollectionCommand, DeleteCollectionResponse>();
381 registerType<Command::FetchCollections, FetchCollectionsCommand, FetchCollectionsResponse>();
382 registerType<Command::FetchCollectionStats, FetchCollectionStatsCommand, FetchCollectionStatsResponse>();
383 registerType<Command::ModifyCollection, ModifyCollectionCommand, ModifyCollectionResponse>();
384 registerType<Command::MoveCollection, MoveCollectionCommand, MoveCollectionResponse>();
385
386 // Search
387 registerType<Command::Search, SearchCommand, SearchResponse>();
388 registerType<Command::SearchResult, SearchResultCommand, SearchResultResponse>();
389 registerType<Command::StoreSearch, StoreSearchCommand, StoreSearchResponse>();
390
391 // Tag
392 registerType<Command::CreateTag, CreateTagCommand, CreateTagResponse>();
393 registerType<Command::DeleteTag, DeleteTagCommand, DeleteTagResponse>();
394 registerType<Command::FetchTags, FetchTagsCommand, FetchTagsResponse>();
395 registerType<Command::ModifyTag, ModifyTagCommand, ModifyTagResponse>();
396
397 // Relation
398 registerType<Command::FetchRelations, FetchRelationsCommand, FetchRelationsResponse>();
399 registerType<Command::ModifyRelation, ModifyRelationCommand, ModifyRelationResponse>();
400 registerType<Command::RemoveRelations, RemoveRelationsCommand, RemoveRelationsResponse>();
401
402 // Resources
403 registerType<Command::SelectResource, SelectResourceCommand, SelectResourceResponse>();
404
405 // Other...?
406 registerType<Command::StreamPayload, StreamPayloadCommand, StreamPayloadResponse>();
407 registerType<Command::ItemChangeNotification, ItemChangeNotification, Response /* invalid */>();
408 registerType<Command::CollectionChangeNotification, CollectionChangeNotification, Response /* invalid */>();
409 registerType<Command::TagChangeNotification, TagChangeNotification, Response /* invalid */>();
410 registerType<Command::RelationChangeNotification, RelationChangeNotification, Response /* invalid */>();
411 registerType<Command::SubscriptionChangeNotification, SubscriptionChangeNotification, Response /* invalid */>();
412 registerType<Command::DebugChangeNotification, DebugChangeNotification, Response /* invalid */>();
413 registerType<Command::CreateSubscription, CreateSubscriptionCommand, CreateSubscriptionResponse>();
414 registerType<Command::ModifySubscription, ModifySubscriptionCommand, ModifySubscriptionResponse>();
415 }
416
417 // clang has problem resolving the right qHash() overload for Command::Type,
418 // so use its underlying integer type instead
419 QHash<std::underlying_type<Command::Type>::type, QPair<CommandFactoryFunc, ResponseFactoryFunc>> registrar;
420
421private:
422 template<typename T>
423 static CommandPtr commandFactoryFunc()
424 {
426 }
427 template<typename T>
428 static ResponsePtr responseFactoryFunc()
429 {
431 }
432
433 template<Command::Type T, typename CmdType, typename RespType>
434 void registerType()
435 {
436 CommandFactoryFunc cmdFunc = &commandFactoryFunc<CmdType>;
437 ResponseFactoryFunc respFunc = &responseFactoryFunc<RespType>;
438 registrar.insert(T, qMakePair(cmdFunc, respFunc));
439 }
440};
441
442Q_GLOBAL_STATIC(FactoryPrivate, sFactoryPrivate) // NOLINT(readability-redundant-member-init)
443
444CommandPtr Factory::command(Command::Type type)
445{
446 auto iter = sFactoryPrivate->registrar.constFind(type);
447 if (iter == sFactoryPrivate->registrar.constEnd()) {
449 }
450 return iter->first();
451}
452
453ResponsePtr Factory::response(Command::Type type)
454{
455 auto iter = sFactoryPrivate->registrar.constFind(type);
456 if (iter == sFactoryPrivate->registrar.constEnd()) {
458 }
459 return iter->second();
460}
461
462/******************************************************************************/
463
464/******************************************************************************/
465
466bool ItemFetchScope::operator==(const ItemFetchScope &other) const
467{
468 return mRequestedParts == other.mRequestedParts && mChangedSince == other.mChangedSince && mAncestorDepth == other.mAncestorDepth && mFlags == other.mFlags;
469}
470
471QList<QByteArray> ItemFetchScope::requestedPayloads() const
472{
474 std::copy_if(mRequestedParts.begin(), mRequestedParts.end(), std::back_inserter(rv), [](const QByteArray &ba) {
475 return ba.startsWith("PLD:");
476 });
477 return rv;
478}
479
480void ItemFetchScope::setFetch(FetchFlags attributes, bool fetch)
481{
482 if (fetch) {
483 mFlags |= attributes;
484 if (attributes & FullPayload) {
485 if (!mRequestedParts.contains(AKONADI_PARAM_PLD_RFC822)) {
486 mRequestedParts << AKONADI_PARAM_PLD_RFC822;
487 }
488 }
489 } else {
490 mFlags &= ~attributes;
491 }
492}
493
494bool ItemFetchScope::fetch(FetchFlags flags) const
495{
496 if (flags == None) {
497 return mFlags == None;
498 } else {
499 return mFlags & flags;
500 }
501}
502
503void ItemFetchScope::toJson(QJsonObject &json) const
504{
505 json[QStringLiteral("flags")] = static_cast<int>(mFlags);
506 json[QStringLiteral("ChangedSince")] = mChangedSince.toString();
507 json[QStringLiteral("AncestorDepth")] = static_cast<std::underlying_type<AncestorDepth>::type>(mAncestorDepth);
508
509 QJsonArray requestedPartsArray;
510 for (const auto &part : std::as_const(mRequestedParts)) {
511 requestedPartsArray.append(QString::fromUtf8(part));
512 }
513 json[QStringLiteral("RequestedParts")] = requestedPartsArray;
514}
515
516QDebug operator<<(QDebug dbg, ItemFetchScope::AncestorDepth depth)
517{
518 switch (depth) {
519 case ItemFetchScope::NoAncestor:
520 return dbg << "No ancestor";
521 case ItemFetchScope::ParentAncestor:
522 return dbg << "Parent ancestor";
523 case ItemFetchScope::AllAncestors:
524 return dbg << "All ancestors";
525 }
526 Q_UNREACHABLE();
527}
528
529DataStream &operator<<(DataStream &stream, const ItemFetchScope &scope)
530{
531 return stream << scope.mRequestedParts << scope.mChangedSince << scope.mAncestorDepth << scope.mFlags;
532}
533
534DataStream &operator>>(DataStream &stream, ItemFetchScope &scope)
535{
536 return stream >> scope.mRequestedParts >> scope.mChangedSince >> scope.mAncestorDepth >> scope.mFlags;
537}
538
539QDebug operator<<(QDebug dbg, const ItemFetchScope &scope)
540{
541 return dbg.noquote() << "FetchScope(\n"
542 << "Fetch Flags:" << scope.mFlags << "\n"
543 << "Changed Since:" << scope.mChangedSince << "\n"
544 << "Ancestor Depth:" << scope.mAncestorDepth << "\n"
545 << "Requested Parts:" << scope.mRequestedParts << ")\n";
546}
547
548/******************************************************************************/
549
550ScopeContext::ScopeContext(Type type, qint64 id)
551{
552 if (type == ScopeContext::Tag) {
553 mTagCtx = id;
554 } else if (type == ScopeContext::Collection) {
555 mColCtx = id;
556 }
557}
558
559ScopeContext::ScopeContext(Type type, const QString &ctx)
560{
561 if (type == ScopeContext::Tag) {
562 mTagCtx = ctx;
563 } else if (type == ScopeContext::Collection) {
564 mColCtx = ctx;
565 }
566}
567
568bool ScopeContext::operator==(const ScopeContext &other) const
569{
570 return mColCtx == other.mColCtx && mTagCtx == other.mTagCtx;
571}
572
573void ScopeContext::toJson(QJsonObject &json) const
574{
575 if (isEmpty()) {
576 json[QStringLiteral("scopeContext")] = false;
577 } else if (hasContextId(ScopeContext::Tag)) {
578 json[QStringLiteral("scopeContext")] = QStringLiteral("tag");
579 json[QStringLiteral("TagID")] = contextId(ScopeContext::Tag);
580 } else if (hasContextId(ScopeContext::Collection)) {
581 json[QStringLiteral("scopeContext")] = QStringLiteral("collection");
582 json[QStringLiteral("ColID")] = contextId(ScopeContext::Collection);
583 } else if (hasContextRID(ScopeContext::Tag)) {
584 json[QStringLiteral("scopeContext")] = QStringLiteral("tagrid");
585 json[QStringLiteral("TagRID")] = contextRID(ScopeContext::Tag);
586 } else if (hasContextRID(ScopeContext::Collection)) {
587 json[QStringLiteral("scopeContext")] = QStringLiteral("colrid");
588 json[QStringLiteral("ColRID")] = contextRID(ScopeContext::Collection);
589 }
590}
591
592DataStream &operator<<(DataStream &stream, const ScopeContext &context)
593{
594 // We don't have a custom generic DataStream streaming operator for QVariant
595 // because it's very hard, esp. without access to QVariant private
596 // stuff, so we have to decompose it manually here.
597 auto vType = context.mColCtx.typeId();
598 stream << vType;
599 if (vType == QMetaType::LongLong) {
600 stream << context.mColCtx.toLongLong();
601 } else if (vType == QMetaType::QString) {
602 stream << context.mColCtx.toString();
603 }
604
605 vType = context.mTagCtx.typeId();
606 stream << vType;
607 if (vType == QMetaType::LongLong) {
608 stream << context.mTagCtx.toLongLong();
609 } else if (vType == QMetaType::QString) {
610 stream << context.mTagCtx.toString();
611 }
612
613 return stream;
614}
615
616DataStream &operator>>(DataStream &stream, ScopeContext &context)
617{
618 int vType;
619 qint64 id;
620 QString rid;
621
622 for (ScopeContext::Type type : {ScopeContext::Collection, ScopeContext::Tag}) {
623 stream >> vType;
624 if (vType == QMetaType::LongLong) {
625 stream >> id;
626 context.setContext(type, id);
627 } else if (vType == QMetaType::QString) {
628 stream >> rid;
629 context.setContext(type, rid);
630 }
631 }
632
633 return stream;
634}
635
636QDebug operator<<(QDebug _dbg, const ScopeContext &ctx)
637{
638 QDebug dbg(_dbg.noquote());
639 dbg << "ScopeContext(";
640 if (ctx.isEmpty()) {
641 dbg << "empty";
642 } else if (ctx.hasContextId(ScopeContext::Tag)) {
643 dbg << "Tag ID:" << ctx.contextId(ScopeContext::Tag);
644 } else if (ctx.hasContextId(ScopeContext::Collection)) {
645 dbg << "Col ID:" << ctx.contextId(ScopeContext::Collection);
646 } else if (ctx.hasContextRID(ScopeContext::Tag)) {
647 dbg << "Tag RID:" << ctx.contextRID(ScopeContext::Tag);
648 } else if (ctx.hasContextRID(ScopeContext::Collection)) {
649 dbg << "Col RID:" << ctx.contextRID(ScopeContext::Collection);
650 }
651 return dbg << ")\n";
652}
653
654/******************************************************************************/
655
656ChangeNotification::ChangeNotification(Command::Type type)
657 : Command(type)
658{
659}
660
661bool ChangeNotification::operator==(const ChangeNotification &other) const
662{
663 return static_cast<const Command &>(*this) == other && mSessionId == other.mSessionId;
664 // metadata are not compared
665}
666
667QList<qint64> ChangeNotification::itemsToUids(const QList<FetchItemsResponse> &items)
668{
669 QList<qint64> rv;
670 rv.reserve(items.size());
671 std::transform(items.cbegin(), items.cend(), std::back_inserter(rv), [](const FetchItemsResponse &item) {
672 return item.id();
673 });
674 return rv;
675}
676
677bool ChangeNotification::isRemove() const
678{
679 switch (type()) {
680 case Command::Invalid:
681 return false;
682 case Command::ItemChangeNotification:
683 return static_cast<const class ItemChangeNotification *>(this)->operation() == ItemChangeNotification::Remove;
684 case Command::CollectionChangeNotification:
685 return static_cast<const class CollectionChangeNotification *>(this)->operation() == CollectionChangeNotification::Remove;
686 case Command::TagChangeNotification:
687 return static_cast<const class TagChangeNotification *>(this)->operation() == TagChangeNotification::Remove;
688 case Command::RelationChangeNotification:
689 return static_cast<const class RelationChangeNotification *>(this)->operation() == RelationChangeNotification::Remove;
690 case Command::SubscriptionChangeNotification:
691 return static_cast<const class SubscriptionChangeNotification *>(this)->operation() == SubscriptionChangeNotification::Remove;
692 case Command::DebugChangeNotification:
693 return false;
694 default:
695 Q_ASSERT_X(false, __FUNCTION__, "Unknown ChangeNotification type");
696 }
697
698 return false;
699}
700
701bool ChangeNotification::isMove() const
702{
703 switch (type()) {
704 case Command::Invalid:
705 return false;
706 case Command::ItemChangeNotification:
707 return static_cast<const class ItemChangeNotification *>(this)->operation() == ItemChangeNotification::Move;
708 case Command::CollectionChangeNotification:
709 return static_cast<const class CollectionChangeNotification *>(this)->operation() == CollectionChangeNotification::Move;
710 case Command::TagChangeNotification:
711 case Command::RelationChangeNotification:
712 case Command::SubscriptionChangeNotification:
713 case Command::DebugChangeNotification:
714 return false;
715 default:
716 Q_ASSERT_X(false, __FUNCTION__, "Unknown ChangeNotification type");
717 }
718
719 return false;
720}
721
722bool ChangeNotification::appendAndCompress(ChangeNotificationList &list, const ChangeNotificationPtr &msg)
723{
724 // It is likely that compressible notifications are within the last few notifications, so avoid searching a list that is potentially huge
725 static const int maxCompressionSearchLength = 10;
726 int searchCounter = 0;
727 // There are often multiple Collection Modify notifications in the queue,
728 // so we optimize for this case.
729
730 if (msg->type() == Command::CollectionChangeNotification) {
731 const auto &cmsg = Protocol::cmdCast<class CollectionChangeNotification>(msg);
732 if (cmsg.operation() == CollectionChangeNotification::Modify) {
733 // We are iterating from end, since there's higher probability of finding
734 // matching notification
735 for (auto iter = list.end(), begin = list.begin(); iter != begin;) {
736 --iter;
737 if ((*iter)->type() == Protocol::Command::CollectionChangeNotification) {
738 auto &it = Protocol::cmdCast<class CollectionChangeNotification>(*iter);
739 const auto &msgCol = cmsg.collection();
740 const auto &itCol = it.collection();
741 if (msgCol.id() == itCol.id() && msgCol.remoteId() == itCol.remoteId() && msgCol.remoteRevision() == itCol.remoteRevision()
742 && msgCol.resource() == itCol.resource() && cmsg.destinationResource() == it.destinationResource()
743 && cmsg.parentCollection() == it.parentCollection() && cmsg.parentDestCollection() == it.parentDestCollection()) {
744 // both are modifications, merge them together and drop the new one
745 if (cmsg.operation() == CollectionChangeNotification::Modify && it.operation() == CollectionChangeNotification::Modify) {
746 const auto parts = it.changedParts();
747 it.setChangedParts(parts + cmsg.changedParts());
748 return false;
749 }
750
751 // we found Add notification, which means we can drop this modification
752 if (it.operation() == CollectionChangeNotification::Add) {
753 return false;
754 }
755 }
756 }
757 searchCounter++;
758 if (searchCounter >= maxCompressionSearchLength) {
759 break;
760 }
761 }
762 }
763 }
764
765 // All other cases are just append, as the compression becomes too expensive in large batches
766 list.append(msg);
767 return true;
768}
769
770void ChangeNotification::toJson(QJsonObject &json) const
771{
772 static_cast<const Command *>(this)->toJson(json);
773 json[QStringLiteral("session")] = QString::fromUtf8(mSessionId);
774
775 QJsonArray metadata;
776 for (const auto &m : std::as_const(mMetaData)) {
777 metadata.append(QString::fromUtf8(m));
778 }
779 json[QStringLiteral("metadata")] = metadata;
780}
781
782DataStream &operator<<(DataStream &stream, const ChangeNotification &ntf)
783{
784 return stream << static_cast<const Command &>(ntf) << ntf.mSessionId;
785}
786
787DataStream &operator>>(DataStream &stream, ChangeNotification &ntf)
788{
789 return stream >> static_cast<Command &>(ntf) >> ntf.mSessionId;
790}
791
792QDebug operator<<(QDebug dbg, const ChangeNotification &ntf)
793{
794 return dbg.noquote() << static_cast<const Command &>(ntf) << "Session:" << ntf.mSessionId << "\n"
795 << "MetaData:" << ntf.mMetaData << "\n";
796}
797
798DataStream &operator>>(DataStream &stream, ChangeNotification::Relation &relation)
799{
800 return stream >> relation.type >> relation.leftId >> relation.rightId;
801}
802
803DataStream &operator<<(DataStream &stream, const ChangeNotification::Relation &relation)
804{
805 return stream << relation.type << relation.leftId << relation.rightId;
806}
807
808QDebug operator<<(QDebug _dbg, const ChangeNotification::Relation &rel)
809{
810 QDebug dbg(_dbg.noquote());
811 return dbg << "Left: " << rel.leftId << ", Right:" << rel.rightId << ", Type: " << rel.type;
812}
813
814} // namespace Protocol
815} // namespace Akonadi
816
817// Helpers for the generated code
818namespace Akonadi
819{
820namespace Protocol
821{
822template<typename Value, template<typename> class Container>
823inline bool containerComparator(const Container<Value> &c1, const Container<Value> &c2)
824{
825 return c1 == c2;
826}
827
828template<typename T, template<typename> class Container>
829inline bool containerComparator(const Container<QSharedPointer<T>> &c1, const Container<QSharedPointer<T>> &c2)
830{
831 if (c1.size() != c2.size()) {
832 return false;
833 }
834
835 for (auto it1 = c1.cbegin(), it2 = c2.cbegin(), end1 = c1.cend(); it1 != end1; ++it1, ++it2) {
836 if (**it1 != **it2) {
837 return false;
838 }
839 }
840 return true;
841}
842
843} // namespace Protocol
844} // namespace Akonadi
845
846/******************************************************************************/
847
848// Here comes the generated protocol implementation
849#include "protocol_gen.cpp"
850
851/******************************************************************************/
Helper integration between Akonadi and Qt.
KCALUTILS_EXPORT QString errorMessage(const KCalendarCore::Exception &exception)
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
const QList< QKeySequence > & begin()
QDebug operator<<(QDebug dbg, const PerceptualColor::LchaDouble &value)
A glue between Qt and the standard library.
QDebug & noquote()
QString toString(T &&object)
iterator insert(const Key &key, const T &value)
void append(const QJsonValue &value)
void append(QList< T > &&value)
iterator begin()
const_iterator cbegin() const const
const_iterator cend() const const
iterator end()
void reserve(qsizetype size)
qsizetype size() const const
QSharedPointer< T > create(Args &&... args)
QSharedPointer< X > staticCast() const const
QString fromUtf8(QByteArrayView str)
qlonglong toLongLong(bool *ok, int base) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:38 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.