File indexing completed on 2024-11-10 04:40:48
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 /******************************************************************************/