Akonadi

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

KDE's Doxygen guidelines are available online.