File indexing completed on 2024-09-15 04:36:25

0001 /*
0002  *  SPDX-FileCopyrightText: 2015 Daniel Vrátil <dvratil@redhat.com>
0003  *  SPDX-FileCopyrightText: 2016 Daniel Vrátil <dvratil@kde.org>
0004  *
0005  *  SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 
0008 #include "datastream_p_p.h"
0009 #include "imapset_p.h"
0010 #include "protocol_p.h"
0011 #include "scope_p.h"
0012 
0013 #include <type_traits>
0014 #include <typeinfo>
0015 
0016 #include <QHash>
0017 #include <QJsonArray>
0018 #include <QJsonObject>
0019 
0020 #include <cassert>
0021 
0022 // clazy:excludeall=function-args-by-value
0023 
0024 #undef AKONADI_DECLARE_PRIVATE
0025 #define AKONADI_DECLARE_PRIVATE(Class)                                                                                                                         \
0026     inline Class##Private *Class::d_func()                                                                                                                     \
0027     {                                                                                                                                                          \
0028         return reinterpret_cast<Class##Private *>(d_ptr.data());                                                                                               \
0029     }                                                                                                                                                          \
0030     inline const Class##Private *Class::d_func() const                                                                                                         \
0031     {                                                                                                                                                          \
0032         return reinterpret_cast<const Class##Private *>(d_ptr.constData());                                                                                    \
0033     }
0034 
0035 namespace Akonadi
0036 {
0037 namespace Protocol
0038 {
0039 QDebug operator<<(QDebug _dbg, Command::Type type)
0040 {
0041     QDebug dbg(_dbg.noquote());
0042 
0043     switch (type) {
0044     case Command::Invalid:
0045         return dbg << "Invalid";
0046 
0047     case Command::Hello:
0048         return dbg << "Hello";
0049     case Command::Login:
0050         return dbg << "Login";
0051     case Command::Logout:
0052         return dbg << "Logout";
0053 
0054     case Command::Transaction:
0055         return dbg << "Transaction";
0056 
0057     case Command::CreateItem:
0058         return dbg << "CreateItem";
0059     case Command::CopyItems:
0060         return dbg << "CopyItems";
0061     case Command::DeleteItems:
0062         return dbg << "DeleteItems";
0063     case Command::FetchItems:
0064         return dbg << "FetchItems";
0065     case Command::LinkItems:
0066         return dbg << "LinkItems";
0067     case Command::ModifyItems:
0068         return dbg << "ModifyItems";
0069     case Command::MoveItems:
0070         return dbg << "MoveItems";
0071 
0072     case Command::CreateCollection:
0073         return dbg << "CreateCollection";
0074     case Command::CopyCollection:
0075         return dbg << "CopyCollection";
0076     case Command::DeleteCollection:
0077         return dbg << "DeleteCollection";
0078     case Command::FetchCollections:
0079         return dbg << "FetchCollections";
0080     case Command::FetchCollectionStats:
0081         return dbg << "FetchCollectionStats";
0082     case Command::ModifyCollection:
0083         return dbg << "ModifyCollection";
0084     case Command::MoveCollection:
0085         return dbg << "MoveCollection";
0086 
0087     case Command::Search:
0088         return dbg << "Search";
0089     case Command::SearchResult:
0090         return dbg << "SearchResult";
0091     case Command::StoreSearch:
0092         return dbg << "StoreSearch";
0093 
0094     case Command::CreateTag:
0095         return dbg << "CreateTag";
0096     case Command::DeleteTag:
0097         return dbg << "DeleteTag";
0098     case Command::FetchTags:
0099         return dbg << "FetchTags";
0100     case Command::ModifyTag:
0101         return dbg << "ModifyTag";
0102 
0103     case Command::FetchRelations:
0104         return dbg << "FetchRelations";
0105     case Command::ModifyRelation:
0106         return dbg << "ModifyRelation";
0107     case Command::RemoveRelations:
0108         return dbg << "RemoveRelations";
0109 
0110     case Command::SelectResource:
0111         return dbg << "SelectResource";
0112 
0113     case Command::StreamPayload:
0114         return dbg << "StreamPayload";
0115     case Command::ItemChangeNotification:
0116         return dbg << "ItemChangeNotification";
0117     case Command::CollectionChangeNotification:
0118         return dbg << "CollectionChangeNotification";
0119     case Command::TagChangeNotification:
0120         return dbg << "TagChangeNotification";
0121     case Command::RelationChangeNotification:
0122         return dbg << "RelationChangeNotification";
0123     case Command::SubscriptionChangeNotification:
0124         return dbg << "SubscriptionChangeNotification";
0125     case Command::DebugChangeNotification:
0126         return dbg << "DebugChangeNotification";
0127     case Command::CreateSubscription:
0128         return dbg << "CreateSubscription";
0129     case Command::ModifySubscription:
0130         return dbg << "ModifySubscription";
0131 
0132     case Command::_ResponseBit:
0133         Q_ASSERT(false);
0134         return dbg << static_cast<int>(type);
0135     }
0136 
0137     Q_ASSERT(false);
0138     return dbg << static_cast<int>(type);
0139 }
0140 
0141 template<typename T>
0142 DataStream &operator<<(DataStream &stream, const QSharedPointer<T> &ptr)
0143 {
0144     Protocol::serialize(stream, ptr);
0145     return stream;
0146 }
0147 
0148 template<typename T>
0149 DataStream &operator>>(DataStream &stream, QSharedPointer<T> &ptr)
0150 {
0151     ptr = Protocol::deserialize(stream.device()).staticCast<T>();
0152     return stream;
0153 }
0154 
0155 /******************************************************************************/
0156 
0157 Command::Command(quint8 type)
0158     : mType(type)
0159 {
0160 }
0161 
0162 bool Command::operator==(const Command &other) const
0163 {
0164     return mType == other.mType;
0165 }
0166 
0167 void Command::toJson(QJsonObject &json) const
0168 {
0169     json[QStringLiteral("response")] = static_cast<bool>(mType & Command::_ResponseBit);
0170 
0171 #define case_label(x)                                                                                                                                          \
0172     case Command::x: {                                                                                                                                         \
0173         json[QStringLiteral("type")] = QStringLiteral(#x);                                                                                                     \
0174     } break;
0175 
0176     switch (mType & ~Command::_ResponseBit) {
0177         case_label(Invalid) case_label(Hello)
0178 
0179             case_label(Login) case_label(Logout)
0180 
0181                 case_label(Transaction)
0182 
0183                     case_label(CreateItem) case_label(CopyItems) case_label(DeleteItems) case_label(FetchItems) case_label(LinkItems) case_label(ModifyItems)
0184                         case_label(MoveItems)
0185 
0186                             case_label(CreateCollection) case_label(CopyCollection) case_label(DeleteCollection) case_label(FetchCollections)
0187                                 case_label(FetchCollectionStats) case_label(ModifyCollection) case_label(MoveCollection)
0188 
0189                                     case_label(Search) case_label(SearchResult) case_label(StoreSearch)
0190 
0191                                         case_label(CreateTag) case_label(DeleteTag) case_label(FetchTags) case_label(ModifyTag)
0192 
0193                                             case_label(FetchRelations) case_label(ModifyRelation) case_label(RemoveRelations)
0194 
0195                                                 case_label(SelectResource)
0196 
0197                                                     case_label(StreamPayload) case_label(CreateSubscription) case_label(ModifySubscription)
0198 
0199                                                         case_label(DebugChangeNotification) case_label(ItemChangeNotification)
0200                                                             case_label(CollectionChangeNotification) case_label(TagChangeNotification)
0201                                                                 case_label(RelationChangeNotification) case_label(SubscriptionChangeNotification)
0202     }
0203 #undef case_label
0204 }
0205 
0206 DataStream &operator<<(DataStream &stream, const Command &cmd)
0207 {
0208     return stream << cmd.mType;
0209 }
0210 
0211 DataStream &operator>>(DataStream &stream, Command &cmd)
0212 {
0213     return stream >> cmd.mType;
0214 }
0215 
0216 QDebug operator<<(QDebug dbg, const Command &cmd)
0217 {
0218     return dbg.noquote() << ((cmd.mType & Command::_ResponseBit) ? "Response:" : "Command:") << static_cast<Command::Type>(cmd.mType & ~Command::_ResponseBit)
0219                          << "\n";
0220 }
0221 
0222 void toJson(const Akonadi::Protocol::Command *command, QJsonObject &json)
0223 {
0224 #define case_notificationlabel(x, class)                                                                                                                       \
0225     case Command::x: {                                                                                                                                         \
0226         static_cast<const Akonadi::Protocol::class *>(command)->toJson(json);                                                                                  \
0227     } break;
0228 #define case_commandlabel(x, cmd, resp)                                                                                                                        \
0229     case Command::x: {                                                                                                                                         \
0230         static_cast<const Akonadi::Protocol::cmd *>(command)->toJson(json);                                                                                    \
0231     } break;                                                                                                                                                   \
0232     case Command::x | Command::_ResponseBit: {                                                                                                                 \
0233         static_cast<const Akonadi::Protocol::resp *>(command)->toJson(json);                                                                                   \
0234     } break;
0235 
0236     switch (command->mType) {
0237     case Command::Invalid:
0238         break;
0239 
0240     case Command::Hello | Command::_ResponseBit: {
0241         static_cast<const Akonadi::Protocol::HelloResponse *>(command)->toJson(json);
0242     } break;
0243         case_commandlabel(Login, LoginCommand, LoginResponse) case_commandlabel(Logout, LogoutCommand, LogoutResponse)
0244 
0245             case_commandlabel(Transaction, TransactionCommand, TransactionResponse)
0246 
0247                 case_commandlabel(CreateItem, CreateItemCommand, CreateItemResponse) case_commandlabel(CopyItems, CopyItemsCommand, CopyItemsResponse)
0248                     case_commandlabel(DeleteItems, DeleteItemsCommand, DeleteItemsResponse) case_commandlabel(FetchItems, FetchItemsCommand, FetchItemsResponse)
0249                         case_commandlabel(LinkItems, LinkItemsCommand, LinkItemsResponse) case_commandlabel(
0250                             ModifyItems,
0251                             ModifyItemsCommand,
0252                             ModifyItemsResponse) case_commandlabel(MoveItems, MoveItemsCommand, MoveItemsResponse)
0253 
0254                             case_commandlabel(CreateCollection, CreateCollectionCommand, CreateCollectionResponse) case_commandlabel(
0255                                 CopyCollection,
0256                                 CopyCollectionCommand,
0257                                 CopyCollectionResponse) case_commandlabel(DeleteCollection, DeleteCollectionCommand, DeleteCollectionResponse)
0258                                 case_commandlabel(FetchCollections, FetchCollectionsCommand, FetchCollectionsResponse) case_commandlabel(
0259                                     FetchCollectionStats,
0260                                     FetchCollectionStatsCommand,
0261                                     FetchCollectionStatsResponse) case_commandlabel(ModifyCollection, ModifyCollectionCommand, ModifyCollectionResponse)
0262                                     case_commandlabel(MoveCollection, MoveCollectionCommand, MoveCollectionResponse)
0263 
0264                                         case_commandlabel(Search, SearchCommand, SearchResponse) case_commandlabel(
0265                                             SearchResult,
0266                                             SearchResultCommand,
0267                                             SearchResultResponse) case_commandlabel(StoreSearch, StoreSearchCommand, StoreSearchResponse)
0268 
0269                                             case_commandlabel(CreateTag, CreateTagCommand, CreateTagResponse)
0270                                                 case_commandlabel(DeleteTag, DeleteTagCommand, DeleteTagResponse)
0271                                                     case_commandlabel(FetchTags, FetchTagsCommand, FetchTagsResponse)
0272                                                         case_commandlabel(ModifyTag, ModifyTagCommand, ModifyTagResponse)
0273 
0274                                                             case_commandlabel(FetchRelations, FetchRelationsCommand, FetchRelationsResponse)
0275                                                                 case_commandlabel(ModifyRelation, ModifyRelationCommand, ModifyRelationResponse)
0276                                                                     case_commandlabel(RemoveRelations, RemoveRelationsCommand, RemoveRelationsResponse)
0277 
0278                                                                         case_commandlabel(SelectResource, SelectResourceCommand, SelectResourceResponse)
0279 
0280                                                                             case_commandlabel(StreamPayload, StreamPayloadCommand, StreamPayloadResponse)
0281                                                                                 case_commandlabel(
0282                                                                                     CreateSubscription,
0283                                                                                     CreateSubscriptionCommand,
0284                                                                                     CreateSubscriptionResponse) case_commandlabel(ModifySubscription,
0285                                                                                                                                   ModifySubscriptionCommand,
0286                                                                                                                                   ModifySubscriptionResponse)
0287 
0288                                                                                     case_notificationlabel(DebugChangeNotification, DebugChangeNotification)
0289                                                                                         case_notificationlabel(ItemChangeNotification, ItemChangeNotification)
0290                                                                                             case_notificationlabel(CollectionChangeNotification,
0291                                                                                                                    CollectionChangeNotification)
0292                                                                                                 case_notificationlabel(TagChangeNotification,
0293                                                                                                                        TagChangeNotification)
0294                                                                                                     case_notificationlabel(RelationChangeNotification,
0295                                                                                                                            RelationChangeNotification)
0296                                                                                                         case_notificationlabel(SubscriptionChangeNotification,
0297                                                                                                                                SubscriptionChangeNotification)
0298     }
0299 #undef case_notificationlabel
0300 #undef case_commandlabel
0301 }
0302 
0303 /******************************************************************************/
0304 
0305 Response::Response()
0306     : Command(Command::Invalid | Command::_ResponseBit)
0307     , mErrorCode(0)
0308 {
0309 }
0310 
0311 Response::Response(Command::Type type)
0312     : Command(type | Command::_ResponseBit)
0313     , mErrorCode(0)
0314 {
0315 }
0316 
0317 bool Response::operator==(const Response &other) const
0318 {
0319     return *static_cast<const Command *>(this) == static_cast<const Command &>(other) && mErrorCode == other.mErrorCode && mErrorMsg == other.mErrorMsg;
0320 }
0321 
0322 void Response::toJson(QJsonObject &json) const
0323 {
0324     static_cast<const Command *>(this)->toJson(json);
0325     if (isError()) {
0326         QJsonObject error;
0327         error[QStringLiteral("code")] = errorCode();
0328         error[QStringLiteral("message")] = errorMessage();
0329         json[QStringLiteral("error")] = error;
0330     } else {
0331         json[QStringLiteral("error")] = false;
0332     }
0333 }
0334 
0335 DataStream &operator<<(DataStream &stream, const Response &cmd)
0336 {
0337     return stream << static_cast<const Command &>(cmd) << cmd.mErrorCode << cmd.mErrorMsg;
0338 }
0339 
0340 DataStream &operator>>(DataStream &stream, Response &cmd)
0341 {
0342     return stream >> static_cast<Command &>(cmd) >> cmd.mErrorCode >> cmd.mErrorMsg;
0343 }
0344 
0345 QDebug operator<<(QDebug dbg, const Response &resp)
0346 {
0347     return dbg.noquote() << static_cast<const Command &>(resp) << "Error code:" << resp.mErrorCode << "\n"
0348                          << "Error msg:" << resp.mErrorMsg << "\n";
0349 }
0350 
0351 /******************************************************************************/
0352 
0353 class FactoryPrivate
0354 {
0355 public:
0356     typedef CommandPtr (*CommandFactoryFunc)();
0357     typedef ResponsePtr (*ResponseFactoryFunc)();
0358 
0359     FactoryPrivate()
0360     {
0361         // Session management
0362         registerType<Command::Hello, Command /* invalid */, HelloResponse>();
0363         registerType<Command::Login, LoginCommand, LoginResponse>();
0364         registerType<Command::Logout, LogoutCommand, LogoutResponse>();
0365 
0366         // Transactions
0367         registerType<Command::Transaction, TransactionCommand, TransactionResponse>();
0368 
0369         // Items
0370         registerType<Command::CreateItem, CreateItemCommand, CreateItemResponse>();
0371         registerType<Command::CopyItems, CopyItemsCommand, CopyItemsResponse>();
0372         registerType<Command::DeleteItems, DeleteItemsCommand, DeleteItemsResponse>();
0373         registerType<Command::FetchItems, FetchItemsCommand, FetchItemsResponse>();
0374         registerType<Command::LinkItems, LinkItemsCommand, LinkItemsResponse>();
0375         registerType<Command::ModifyItems, ModifyItemsCommand, ModifyItemsResponse>();
0376         registerType<Command::MoveItems, MoveItemsCommand, MoveItemsResponse>();
0377 
0378         // Collections
0379         registerType<Command::CreateCollection, CreateCollectionCommand, CreateCollectionResponse>();
0380         registerType<Command::CopyCollection, CopyCollectionCommand, CopyCollectionResponse>();
0381         registerType<Command::DeleteCollection, DeleteCollectionCommand, DeleteCollectionResponse>();
0382         registerType<Command::FetchCollections, FetchCollectionsCommand, FetchCollectionsResponse>();
0383         registerType<Command::FetchCollectionStats, FetchCollectionStatsCommand, FetchCollectionStatsResponse>();
0384         registerType<Command::ModifyCollection, ModifyCollectionCommand, ModifyCollectionResponse>();
0385         registerType<Command::MoveCollection, MoveCollectionCommand, MoveCollectionResponse>();
0386 
0387         // Search
0388         registerType<Command::Search, SearchCommand, SearchResponse>();
0389         registerType<Command::SearchResult, SearchResultCommand, SearchResultResponse>();
0390         registerType<Command::StoreSearch, StoreSearchCommand, StoreSearchResponse>();
0391 
0392         // Tag
0393         registerType<Command::CreateTag, CreateTagCommand, CreateTagResponse>();
0394         registerType<Command::DeleteTag, DeleteTagCommand, DeleteTagResponse>();
0395         registerType<Command::FetchTags, FetchTagsCommand, FetchTagsResponse>();
0396         registerType<Command::ModifyTag, ModifyTagCommand, ModifyTagResponse>();
0397 
0398         // Relation
0399         registerType<Command::FetchRelations, FetchRelationsCommand, FetchRelationsResponse>();
0400         registerType<Command::ModifyRelation, ModifyRelationCommand, ModifyRelationResponse>();
0401         registerType<Command::RemoveRelations, RemoveRelationsCommand, RemoveRelationsResponse>();
0402 
0403         // Resources
0404         registerType<Command::SelectResource, SelectResourceCommand, SelectResourceResponse>();
0405 
0406         // Other...?
0407         registerType<Command::StreamPayload, StreamPayloadCommand, StreamPayloadResponse>();
0408         registerType<Command::ItemChangeNotification, ItemChangeNotification, Response /* invalid */>();
0409         registerType<Command::CollectionChangeNotification, CollectionChangeNotification, Response /* invalid */>();
0410         registerType<Command::TagChangeNotification, TagChangeNotification, Response /* invalid */>();
0411         registerType<Command::RelationChangeNotification, RelationChangeNotification, Response /* invalid */>();
0412         registerType<Command::SubscriptionChangeNotification, SubscriptionChangeNotification, Response /* invalid */>();
0413         registerType<Command::DebugChangeNotification, DebugChangeNotification, Response /* invalid */>();
0414         registerType<Command::CreateSubscription, CreateSubscriptionCommand, CreateSubscriptionResponse>();
0415         registerType<Command::ModifySubscription, ModifySubscriptionCommand, ModifySubscriptionResponse>();
0416     }
0417 
0418     // clang has problem resolving the right qHash() overload for Command::Type,
0419     // so use its underlying integer type instead
0420     QHash<std::underlying_type<Command::Type>::type, QPair<CommandFactoryFunc, ResponseFactoryFunc>> registrar;
0421 
0422 private:
0423     template<typename T>
0424     static CommandPtr commandFactoryFunc()
0425     {
0426         return QSharedPointer<T>::create();
0427     }
0428     template<typename T>
0429     static ResponsePtr responseFactoryFunc()
0430     {
0431         return QSharedPointer<T>::create();
0432     }
0433 
0434     template<Command::Type T, typename CmdType, typename RespType>
0435     void registerType()
0436     {
0437         CommandFactoryFunc cmdFunc = &commandFactoryFunc<CmdType>;
0438         ResponseFactoryFunc respFunc = &responseFactoryFunc<RespType>;
0439         registrar.insert(T, qMakePair(cmdFunc, respFunc));
0440     }
0441 };
0442 
0443 Q_GLOBAL_STATIC(FactoryPrivate, sFactoryPrivate) // NOLINT(readability-redundant-member-init)
0444 
0445 CommandPtr Factory::command(Command::Type type)
0446 {
0447     auto iter = sFactoryPrivate->registrar.constFind(type);
0448     if (iter == sFactoryPrivate->registrar.constEnd()) {
0449         return QSharedPointer<Command>::create();
0450     }
0451     return iter->first();
0452 }
0453 
0454 ResponsePtr Factory::response(Command::Type type)
0455 {
0456     auto iter = sFactoryPrivate->registrar.constFind(type);
0457     if (iter == sFactoryPrivate->registrar.constEnd()) {
0458         return QSharedPointer<Response>::create();
0459     }
0460     return iter->second();
0461 }
0462 
0463 /******************************************************************************/
0464 
0465 /******************************************************************************/
0466 
0467 bool ItemFetchScope::operator==(const ItemFetchScope &other) const
0468 {
0469     return mRequestedParts == other.mRequestedParts && mChangedSince == other.mChangedSince && mAncestorDepth == other.mAncestorDepth && mFlags == other.mFlags;
0470 }
0471 
0472 QList<QByteArray> ItemFetchScope::requestedPayloads() const
0473 {
0474     QList<QByteArray> rv;
0475     std::copy_if(mRequestedParts.begin(), mRequestedParts.end(), std::back_inserter(rv), [](const QByteArray &ba) {
0476         return ba.startsWith("PLD:");
0477     });
0478     return rv;
0479 }
0480 
0481 void ItemFetchScope::setFetch(FetchFlags attributes, bool fetch)
0482 {
0483     if (fetch) {
0484         mFlags |= attributes;
0485         if (attributes & FullPayload) {
0486             if (!mRequestedParts.contains(AKONADI_PARAM_PLD_RFC822)) {
0487                 mRequestedParts << AKONADI_PARAM_PLD_RFC822;
0488             }
0489         }
0490     } else {
0491         mFlags &= ~attributes;
0492     }
0493 }
0494 
0495 bool ItemFetchScope::fetch(FetchFlags flags) const
0496 {
0497     if (flags == None) {
0498         return mFlags == None;
0499     } else {
0500         return mFlags & flags;
0501     }
0502 }
0503 
0504 void ItemFetchScope::toJson(QJsonObject &json) const
0505 {
0506     json[QStringLiteral("flags")] = static_cast<int>(mFlags);
0507     json[QStringLiteral("ChangedSince")] = mChangedSince.toString();
0508     json[QStringLiteral("AncestorDepth")] = static_cast<std::underlying_type<AncestorDepth>::type>(mAncestorDepth);
0509 
0510     QJsonArray requestedPartsArray;
0511     for (const auto &part : std::as_const(mRequestedParts)) {
0512         requestedPartsArray.append(QString::fromUtf8(part));
0513     }
0514     json[QStringLiteral("RequestedParts")] = requestedPartsArray;
0515 }
0516 
0517 QDebug operator<<(QDebug dbg, ItemFetchScope::AncestorDepth depth)
0518 {
0519     switch (depth) {
0520     case ItemFetchScope::NoAncestor:
0521         return dbg << "No ancestor";
0522     case ItemFetchScope::ParentAncestor:
0523         return dbg << "Parent ancestor";
0524     case ItemFetchScope::AllAncestors:
0525         return dbg << "All ancestors";
0526     }
0527     Q_UNREACHABLE();
0528 }
0529 
0530 DataStream &operator<<(DataStream &stream, const ItemFetchScope &scope)
0531 {
0532     return stream << scope.mRequestedParts << scope.mChangedSince << scope.mAncestorDepth << scope.mFlags;
0533 }
0534 
0535 DataStream &operator>>(DataStream &stream, ItemFetchScope &scope)
0536 {
0537     return stream >> scope.mRequestedParts >> scope.mChangedSince >> scope.mAncestorDepth >> scope.mFlags;
0538 }
0539 
0540 QDebug operator<<(QDebug dbg, const ItemFetchScope &scope)
0541 {
0542     return dbg.noquote() << "FetchScope(\n"
0543                          << "Fetch Flags:" << scope.mFlags << "\n"
0544                          << "Changed Since:" << scope.mChangedSince << "\n"
0545                          << "Ancestor Depth:" << scope.mAncestorDepth << "\n"
0546                          << "Requested Parts:" << scope.mRequestedParts << ")\n";
0547 }
0548 
0549 /******************************************************************************/
0550 
0551 ScopeContext::ScopeContext(Type type, qint64 id)
0552 {
0553     if (type == ScopeContext::Tag) {
0554         mTagCtx = id;
0555     } else if (type == ScopeContext::Collection) {
0556         mColCtx = id;
0557     }
0558 }
0559 
0560 ScopeContext::ScopeContext(Type type, const QString &ctx)
0561 {
0562     if (type == ScopeContext::Tag) {
0563         mTagCtx = ctx;
0564     } else if (type == ScopeContext::Collection) {
0565         mColCtx = ctx;
0566     }
0567 }
0568 
0569 bool ScopeContext::operator==(const ScopeContext &other) const
0570 {
0571     return mColCtx == other.mColCtx && mTagCtx == other.mTagCtx;
0572 }
0573 
0574 void ScopeContext::toJson(QJsonObject &json) const
0575 {
0576     if (isEmpty()) {
0577         json[QStringLiteral("scopeContext")] = false;
0578     } else if (hasContextId(ScopeContext::Tag)) {
0579         json[QStringLiteral("scopeContext")] = QStringLiteral("tag");
0580         json[QStringLiteral("TagID")] = contextId(ScopeContext::Tag);
0581     } else if (hasContextId(ScopeContext::Collection)) {
0582         json[QStringLiteral("scopeContext")] = QStringLiteral("collection");
0583         json[QStringLiteral("ColID")] = contextId(ScopeContext::Collection);
0584     } else if (hasContextRID(ScopeContext::Tag)) {
0585         json[QStringLiteral("scopeContext")] = QStringLiteral("tagrid");
0586         json[QStringLiteral("TagRID")] = contextRID(ScopeContext::Tag);
0587     } else if (hasContextRID(ScopeContext::Collection)) {
0588         json[QStringLiteral("scopeContext")] = QStringLiteral("colrid");
0589         json[QStringLiteral("ColRID")] = contextRID(ScopeContext::Collection);
0590     }
0591 }
0592 
0593 DataStream &operator<<(DataStream &stream, const ScopeContext &context)
0594 {
0595     // We don't have a custom generic DataStream streaming operator for QVariant
0596     // because it's very hard, esp. without access to QVariant private
0597     // stuff, so we have to decompose it manually here.
0598     auto vType = context.mColCtx.typeId();
0599     stream << vType;
0600     if (vType == QMetaType::LongLong) {
0601         stream << context.mColCtx.toLongLong();
0602     } else if (vType == QMetaType::QString) {
0603         stream << context.mColCtx.toString();
0604     }
0605 
0606     vType = context.mTagCtx.typeId();
0607     stream << vType;
0608     if (vType == QMetaType::LongLong) {
0609         stream << context.mTagCtx.toLongLong();
0610     } else if (vType == QMetaType::QString) {
0611         stream << context.mTagCtx.toString();
0612     }
0613 
0614     return stream;
0615 }
0616 
0617 DataStream &operator>>(DataStream &stream, ScopeContext &context)
0618 {
0619     int vType;
0620     qint64 id;
0621     QString rid;
0622 
0623     for (ScopeContext::Type type : {ScopeContext::Collection, ScopeContext::Tag}) {
0624         stream >> vType;
0625         if (vType == QMetaType::LongLong) {
0626             stream >> id;
0627             context.setContext(type, id);
0628         } else if (vType == QMetaType::QString) {
0629             stream >> rid;
0630             context.setContext(type, rid);
0631         }
0632     }
0633 
0634     return stream;
0635 }
0636 
0637 QDebug operator<<(QDebug _dbg, const ScopeContext &ctx)
0638 {
0639     QDebug dbg(_dbg.noquote());
0640     dbg << "ScopeContext(";
0641     if (ctx.isEmpty()) {
0642         dbg << "empty";
0643     } else if (ctx.hasContextId(ScopeContext::Tag)) {
0644         dbg << "Tag ID:" << ctx.contextId(ScopeContext::Tag);
0645     } else if (ctx.hasContextId(ScopeContext::Collection)) {
0646         dbg << "Col ID:" << ctx.contextId(ScopeContext::Collection);
0647     } else if (ctx.hasContextRID(ScopeContext::Tag)) {
0648         dbg << "Tag RID:" << ctx.contextRID(ScopeContext::Tag);
0649     } else if (ctx.hasContextRID(ScopeContext::Collection)) {
0650         dbg << "Col RID:" << ctx.contextRID(ScopeContext::Collection);
0651     }
0652     return dbg << ")\n";
0653 }
0654 
0655 /******************************************************************************/
0656 
0657 ChangeNotification::ChangeNotification(Command::Type type)
0658     : Command(type)
0659 {
0660 }
0661 
0662 bool ChangeNotification::operator==(const ChangeNotification &other) const
0663 {
0664     return static_cast<const Command &>(*this) == other && mSessionId == other.mSessionId;
0665     // metadata are not compared
0666 }
0667 
0668 QList<qint64> ChangeNotification::itemsToUids(const QList<FetchItemsResponse> &items)
0669 {
0670     QList<qint64> rv;
0671     rv.reserve(items.size());
0672     std::transform(items.cbegin(), items.cend(), std::back_inserter(rv), [](const FetchItemsResponse &item) {
0673         return item.id();
0674     });
0675     return rv;
0676 }
0677 
0678 bool ChangeNotification::isRemove() const
0679 {
0680     switch (type()) {
0681     case Command::Invalid:
0682         return false;
0683     case Command::ItemChangeNotification:
0684         return static_cast<const class ItemChangeNotification *>(this)->operation() == ItemChangeNotification::Remove;
0685     case Command::CollectionChangeNotification:
0686         return static_cast<const class CollectionChangeNotification *>(this)->operation() == CollectionChangeNotification::Remove;
0687     case Command::TagChangeNotification:
0688         return static_cast<const class TagChangeNotification *>(this)->operation() == TagChangeNotification::Remove;
0689     case Command::RelationChangeNotification:
0690         return static_cast<const class RelationChangeNotification *>(this)->operation() == RelationChangeNotification::Remove;
0691     case Command::SubscriptionChangeNotification:
0692         return static_cast<const class SubscriptionChangeNotification *>(this)->operation() == SubscriptionChangeNotification::Remove;
0693     case Command::DebugChangeNotification:
0694         return false;
0695     default:
0696         Q_ASSERT_X(false, __FUNCTION__, "Unknown ChangeNotification type");
0697     }
0698 
0699     return false;
0700 }
0701 
0702 bool ChangeNotification::isMove() const
0703 {
0704     switch (type()) {
0705     case Command::Invalid:
0706         return false;
0707     case Command::ItemChangeNotification:
0708         return static_cast<const class ItemChangeNotification *>(this)->operation() == ItemChangeNotification::Move;
0709     case Command::CollectionChangeNotification:
0710         return static_cast<const class CollectionChangeNotification *>(this)->operation() == CollectionChangeNotification::Move;
0711     case Command::TagChangeNotification:
0712     case Command::RelationChangeNotification:
0713     case Command::SubscriptionChangeNotification:
0714     case Command::DebugChangeNotification:
0715         return false;
0716     default:
0717         Q_ASSERT_X(false, __FUNCTION__, "Unknown ChangeNotification type");
0718     }
0719 
0720     return false;
0721 }
0722 
0723 bool ChangeNotification::appendAndCompress(ChangeNotificationList &list, const ChangeNotificationPtr &msg)
0724 {
0725     // It is likely that compressible notifications are within the last few notifications, so avoid searching a list that is potentially huge
0726     static const int maxCompressionSearchLength = 10;
0727     int searchCounter = 0;
0728     // There are often multiple Collection Modify notifications in the queue,
0729     // so we optimize for this case.
0730 
0731     if (msg->type() == Command::CollectionChangeNotification) {
0732         const auto &cmsg = Protocol::cmdCast<class CollectionChangeNotification>(msg);
0733         if (cmsg.operation() == CollectionChangeNotification::Modify) {
0734             // We are iterating from end, since there's higher probability of finding
0735             // matching notification
0736             for (auto iter = list.end(), begin = list.begin(); iter != begin;) {
0737                 --iter;
0738                 if ((*iter)->type() == Protocol::Command::CollectionChangeNotification) {
0739                     auto &it = Protocol::cmdCast<class CollectionChangeNotification>(*iter);
0740                     const auto &msgCol = cmsg.collection();
0741                     const auto &itCol = it.collection();
0742                     if (msgCol.id() == itCol.id() && msgCol.remoteId() == itCol.remoteId() && msgCol.remoteRevision() == itCol.remoteRevision()
0743                         && msgCol.resource() == itCol.resource() && cmsg.destinationResource() == it.destinationResource()
0744                         && cmsg.parentCollection() == it.parentCollection() && cmsg.parentDestCollection() == it.parentDestCollection()) {
0745                         // both are modifications, merge them together and drop the new one
0746                         if (cmsg.operation() == CollectionChangeNotification::Modify && it.operation() == CollectionChangeNotification::Modify) {
0747                             const auto parts = it.changedParts();
0748                             it.setChangedParts(parts + cmsg.changedParts());
0749                             return false;
0750                         }
0751 
0752                         // we found Add notification, which means we can drop this modification
0753                         if (it.operation() == CollectionChangeNotification::Add) {
0754                             return false;
0755                         }
0756                     }
0757                 }
0758                 searchCounter++;
0759                 if (searchCounter >= maxCompressionSearchLength) {
0760                     break;
0761                 }
0762             }
0763         }
0764     }
0765 
0766     // All other cases are just append, as the compression becomes too expensive in large batches
0767     list.append(msg);
0768     return true;
0769 }
0770 
0771 void ChangeNotification::toJson(QJsonObject &json) const
0772 {
0773     static_cast<const Command *>(this)->toJson(json);
0774     json[QStringLiteral("session")] = QString::fromUtf8(mSessionId);
0775 
0776     QJsonArray metadata;
0777     for (const auto &m : std::as_const(mMetaData)) {
0778         metadata.append(QString::fromUtf8(m));
0779     }
0780     json[QStringLiteral("metadata")] = metadata;
0781 }
0782 
0783 DataStream &operator<<(DataStream &stream, const ChangeNotification &ntf)
0784 {
0785     return stream << static_cast<const Command &>(ntf) << ntf.mSessionId;
0786 }
0787 
0788 DataStream &operator>>(DataStream &stream, ChangeNotification &ntf)
0789 {
0790     return stream >> static_cast<Command &>(ntf) >> ntf.mSessionId;
0791 }
0792 
0793 QDebug operator<<(QDebug dbg, const ChangeNotification &ntf)
0794 {
0795     return dbg.noquote() << static_cast<const Command &>(ntf) << "Session:" << ntf.mSessionId << "\n"
0796                          << "MetaData:" << ntf.mMetaData << "\n";
0797 }
0798 
0799 DataStream &operator>>(DataStream &stream, ChangeNotification::Relation &relation)
0800 {
0801     return stream >> relation.type >> relation.leftId >> relation.rightId;
0802 }
0803 
0804 DataStream &operator<<(DataStream &stream, const ChangeNotification::Relation &relation)
0805 {
0806     return stream << relation.type << relation.leftId << relation.rightId;
0807 }
0808 
0809 QDebug operator<<(QDebug _dbg, const ChangeNotification::Relation &rel)
0810 {
0811     QDebug dbg(_dbg.noquote());
0812     return dbg << "Left: " << rel.leftId << ", Right:" << rel.rightId << ", Type: " << rel.type;
0813 }
0814 
0815 } // namespace Protocol
0816 } // namespace Akonadi
0817 
0818 // Helpers for the generated code
0819 namespace Akonadi
0820 {
0821 namespace Protocol
0822 {
0823 template<typename Value, template<typename> class Container>
0824 inline bool containerComparator(const Container<Value> &c1, const Container<Value> &c2)
0825 {
0826     return c1 == c2;
0827 }
0828 
0829 template<typename T, template<typename> class Container>
0830 inline bool containerComparator(const Container<QSharedPointer<T>> &c1, const Container<QSharedPointer<T>> &c2)
0831 {
0832     if (c1.size() != c2.size()) {
0833         return false;
0834     }
0835 
0836     for (auto it1 = c1.cbegin(), it2 = c2.cbegin(), end1 = c1.cend(); it1 != end1; ++it1, ++it2) {
0837         if (**it1 != **it2) {
0838             return false;
0839         }
0840     }
0841     return true;
0842 }
0843 
0844 } // namespace Protocol
0845 } // namespace Akonadi
0846 
0847 /******************************************************************************/
0848 
0849 // Here comes the generated protocol implementation
0850 #include "protocol_gen.cpp"
0851 
0852 /******************************************************************************/