Akonadi

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

KDE's Doxygen guidelines are available online.