File indexing completed on 2024-06-16 04:50:11

0001 /*
0002     SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org>
0003     SPDX-FileCopyrightText: 2018 Daniel Vrátil <dvratil@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "akonadicore_debug.h"
0009 #include "changerecorderjournal_p.h"
0010 
0011 #include <QDataStream>
0012 #include <QFile>
0013 #include <QQueue>
0014 #include <QSettings>
0015 
0016 using namespace Akonadi;
0017 
0018 namespace
0019 {
0020 constexpr quint64 s_currentVersion = Q_UINT64_C(0x000800000000);
0021 constexpr quint64 s_versionMask = Q_UINT64_C(0xFFFF00000000);
0022 constexpr quint64 s_sizeMask = Q_UINT64_C(0x0000FFFFFFFF);
0023 }
0024 
0025 Protocol::ChangeNotificationPtr ChangeRecorderJournalReader::loadQSettingsNotification(QSettings *settings)
0026 {
0027     switch (static_cast<LegacyType>(settings->value(QStringLiteral("type")).toInt())) {
0028     case Item:
0029         return loadQSettingsItemNotification(settings);
0030     case Collection:
0031         return loadQSettingsCollectionNotification(settings);
0032     case Tag:
0033     case Relation:
0034     case InvalidType:
0035     default:
0036         qWarning() << "Unexpected notification type in legacy store";
0037         return {};
0038     }
0039 }
0040 
0041 QQueue<Protocol::ChangeNotificationPtr> ChangeRecorderJournalReader::loadFrom(QFile *device, bool &needsFullSave)
0042 {
0043     QDataStream stream(device);
0044     stream.setVersion(QDataStream::Qt_4_6);
0045 
0046     QByteArray sessionId;
0047     int type;
0048 
0049     QQueue<Protocol::ChangeNotificationPtr> list;
0050 
0051     quint64 sizeAndVersion;
0052     stream >> sizeAndVersion;
0053 
0054     const quint64 size = sizeAndVersion & s_sizeMask;
0055     const quint64 version = (sizeAndVersion & s_versionMask) >> 32;
0056 
0057     quint64 startOffset = 0;
0058     if (version >= 1) {
0059         stream >> startOffset;
0060     }
0061 
0062     // If we skip the first N items, then we'll need to rewrite the file on saving.
0063     // Also, if the file is old, it needs to be rewritten.
0064     needsFullSave = startOffset > 0 || version == 0;
0065 
0066     for (quint64 i = 0; i < size && !stream.atEnd(); ++i) {
0067         Protocol::ChangeNotificationPtr msg;
0068         stream >> sessionId;
0069         stream >> type;
0070 
0071         if (stream.status() != QDataStream::Ok) {
0072             qCWarning(AKONADICORE_LOG) << "Error reading saved notifications! Aborting. Corrupt file:" << device->fileName();
0073             break;
0074         }
0075 
0076         switch (static_cast<LegacyType>(type)) {
0077         case Item:
0078             msg = loadItemNotification(stream, version);
0079             break;
0080         case Collection:
0081             msg = loadCollectionNotification(stream, version);
0082             break;
0083         case Tag:
0084             msg = loadTagNotification(stream, version);
0085             break;
0086         case Relation:
0087             msg = loadRelationNotification(stream, version);
0088             break;
0089         default:
0090             qCWarning(AKONADICORE_LOG) << "Unknown notification type";
0091             break;
0092         }
0093 
0094         if (i < startOffset) {
0095             continue;
0096         }
0097 
0098         if (msg && msg->isValid()) {
0099             msg->setSessionId(sessionId);
0100             list << msg;
0101         }
0102     }
0103 
0104     return list;
0105 }
0106 
0107 void ChangeRecorderJournalWriter::saveTo(const QQueue<Protocol::ChangeNotificationPtr> &notifications, QIODevice *device)
0108 {
0109     // Version 0 of this file format was writing a quint64 count, followed by the notifications.
0110     // Version 1 bundles a version number into that quint64, to be able to detect a version number at load time.
0111 
0112     const quint64 countAndVersion = static_cast<quint64>(notifications.count()) | s_currentVersion;
0113 
0114     QDataStream stream(device);
0115     stream.setVersion(QDataStream::Qt_4_6);
0116 
0117     stream << countAndVersion;
0118     stream << quint64(0); // no start offset
0119 
0120     // qCDebug(AKONADICORE_LOG) << "Saving" << pendingNotifications.count() << "notifications (full save)";
0121 
0122     for (int i = 0; i < notifications.count(); ++i) {
0123         const Protocol::ChangeNotificationPtr &msg = notifications.at(i);
0124 
0125         // We deliberately don't use Factory::serialize(), because the internal
0126         // serialization format could change at any point
0127 
0128         stream << msg->sessionId();
0129         stream << int(mapToLegacyType(msg->type()));
0130         switch (msg->type()) {
0131         case Protocol::Command::ItemChangeNotification:
0132             saveItemNotification(stream, Protocol::cmdCast<Protocol::ItemChangeNotification>(msg));
0133             break;
0134         case Protocol::Command::CollectionChangeNotification:
0135             saveCollectionNotification(stream, Protocol::cmdCast<Protocol::CollectionChangeNotification>(msg));
0136             break;
0137         case Protocol::Command::TagChangeNotification:
0138             saveTagNotification(stream, Protocol::cmdCast<Protocol::TagChangeNotification>(msg));
0139             break;
0140         case Protocol::Command::RelationChangeNotification:
0141             saveRelationNotification(stream, Protocol::cmdCast<Protocol::RelationChangeNotification>(msg));
0142             break;
0143         default:
0144             qCWarning(AKONADICORE_LOG) << "Unexpected type?";
0145             return;
0146         }
0147     }
0148 }
0149 
0150 Protocol::ChangeNotificationPtr ChangeRecorderJournalReader::loadQSettingsItemNotification(QSettings *settings)
0151 {
0152     auto msg = Protocol::ItemChangeNotificationPtr::create();
0153     msg->setSessionId(settings->value(QStringLiteral("sessionId")).toByteArray());
0154     msg->setOperation(mapItemOperation(static_cast<LegacyOp>(settings->value(QStringLiteral("op")).toInt())));
0155     Protocol::FetchItemsResponse item;
0156     item.setId(settings->value(QStringLiteral("uid")).toLongLong());
0157     item.setRemoteId(settings->value(QStringLiteral("rid")).toString());
0158     item.setMimeType(settings->value(QStringLiteral("mimeType")).toString());
0159     msg->setItems({std::move(item)});
0160     msg->addMetadata("FETCH_ITEM");
0161     msg->setResource(settings->value(QStringLiteral("resource")).toByteArray());
0162     msg->setParentCollection(settings->value(QStringLiteral("parentCol")).toLongLong());
0163     msg->setParentDestCollection(settings->value(QStringLiteral("parentDestCol")).toLongLong());
0164     const QStringList list = settings->value(QStringLiteral("itemParts")).toStringList();
0165     QSet<QByteArray> itemParts;
0166     for (const QString &entry : list) {
0167         itemParts.insert(entry.toLatin1());
0168     }
0169     msg->setItemParts(itemParts);
0170     return msg;
0171 }
0172 
0173 Protocol::ChangeNotificationPtr ChangeRecorderJournalReader::loadQSettingsCollectionNotification(QSettings *settings)
0174 {
0175     auto msg = Protocol::CollectionChangeNotificationPtr::create();
0176     msg->setSessionId(settings->value(QStringLiteral("sessionId")).toByteArray());
0177     msg->setOperation(mapCollectionOperation(static_cast<LegacyOp>(settings->value(QStringLiteral("op")).toInt())));
0178     Protocol::FetchCollectionsResponse collection;
0179     collection.setId(settings->value(QStringLiteral("uid")).toLongLong());
0180     collection.setRemoteId(settings->value(QStringLiteral("rid")).toString());
0181     msg->setCollection(std::move(collection));
0182     msg->addMetadata("FETCH_COLLECTION");
0183     msg->setResource(settings->value(QStringLiteral("resource")).toByteArray());
0184     msg->setParentCollection(settings->value(QStringLiteral("parentCol")).toLongLong());
0185     msg->setParentDestCollection(settings->value(QStringLiteral("parentDestCol")).toLongLong());
0186     const QStringList list = settings->value(QStringLiteral("itemParts")).toStringList();
0187     QSet<QByteArray> changedParts;
0188     for (const QString &entry : list) {
0189         changedParts.insert(entry.toLatin1());
0190     }
0191     msg->setChangedParts(changedParts);
0192     return msg;
0193 }
0194 
0195 QSet<Protocol::ItemChangeNotification::Relation> ChangeRecorderJournalReader::extractRelations(QSet<QByteArray> &flags)
0196 {
0197     QSet<Protocol::ItemChangeNotification::Relation> relations;
0198     auto iter = flags.begin();
0199     while (iter != flags.end()) {
0200         if (iter->startsWith("RELATION")) {
0201             const QByteArrayList parts = iter->split(' ');
0202             Q_ASSERT(parts.size() == 4);
0203             Protocol::ItemChangeNotification::Relation relation;
0204             relation.type = QString::fromLatin1(parts[1]);
0205             relation.leftId = parts[2].toLongLong();
0206             relation.rightId = parts[3].toLongLong();
0207             relations.insert(relation);
0208             iter = flags.erase(iter);
0209         } else {
0210             ++iter;
0211         }
0212     }
0213 
0214     return relations;
0215 }
0216 
0217 Protocol::ChangeNotificationPtr ChangeRecorderJournalReader::loadItemNotification(QDataStream &stream, quint64 version)
0218 {
0219     QByteArray resource;
0220     QByteArray destinationResource;
0221     int operation;
0222     int entityCnt;
0223     qint64 uid;
0224     qint64 parentCollection;
0225     qint64 parentDestCollection;
0226     QString remoteId;
0227     QString mimeType;
0228     QString remoteRevision;
0229     QSet<QByteArray> itemParts;
0230     QSet<QByteArray> addedFlags;
0231     QSet<QByteArray> removedFlags;
0232     QSet<qint64> addedTags;
0233     QSet<qint64> removedTags;
0234     QList<Protocol::FetchItemsResponse> items;
0235 
0236     auto msg = Protocol::ItemChangeNotificationPtr::create();
0237 
0238     if (version == 1) {
0239         stream >> operation;
0240         stream >> uid;
0241         stream >> remoteId;
0242         stream >> resource;
0243         stream >> parentCollection;
0244         stream >> parentDestCollection;
0245         stream >> mimeType;
0246         stream >> itemParts;
0247 
0248         Protocol::FetchItemsResponse item;
0249         item.setId(uid);
0250         item.setRemoteId(remoteId);
0251         item.setMimeType(mimeType);
0252         items.push_back(std::move(item));
0253         msg->addMetadata("FETCH_ITEM");
0254     } else if (version >= 2) {
0255         stream >> operation;
0256         stream >> entityCnt;
0257         if (version >= 7) {
0258             QByteArray ba;
0259             qint64 i64;
0260             int i;
0261             QDateTime dt;
0262             QString str;
0263             QList<QByteArray> bav;
0264             QList<qint64> i64v;
0265             QMap<QByteArray, QByteArray> babaMap;
0266             int cnt;
0267             for (int j = 0; j < entityCnt; ++j) {
0268                 Protocol::FetchItemsResponse item;
0269                 stream >> i64;
0270                 item.setId(i64);
0271                 stream >> i;
0272                 item.setRevision(i);
0273                 stream >> i64;
0274                 item.setParentId(i64);
0275                 stream >> str;
0276                 item.setRemoteId(str);
0277                 stream >> str;
0278                 item.setRemoteRevision(str);
0279                 stream >> str;
0280                 item.setGid(str);
0281                 stream >> i64;
0282                 item.setSize(i64);
0283                 stream >> str;
0284                 item.setMimeType(str);
0285                 stream >> dt;
0286                 item.setMTime(dt);
0287                 stream >> bav;
0288                 item.setFlags(bav);
0289                 stream >> cnt;
0290                 QList<Protocol::FetchTagsResponse> tags;
0291                 tags.reserve(cnt);
0292                 for (int k = 0; k < cnt; ++k) {
0293                     Protocol::FetchTagsResponse tag;
0294                     stream >> i64;
0295                     tag.setId(i64);
0296                     stream >> i64;
0297                     tag.setParentId(i64);
0298                     stream >> ba;
0299                     tag.setGid(ba);
0300                     stream >> ba;
0301                     tag.setType(ba);
0302                     stream >> ba;
0303                     tag.setRemoteId(ba);
0304                     stream >> babaMap;
0305                     tag.setAttributes(babaMap);
0306                     tags << tag;
0307                 }
0308                 item.setTags(tags);
0309                 stream >> i64v;
0310                 item.setVirtualReferences(i64v);
0311                 stream >> cnt;
0312                 QList<Protocol::FetchRelationsResponse> relations;
0313                 for (int k = 0; k < cnt; ++k) {
0314                     Protocol::FetchRelationsResponse relation;
0315                     stream >> i64;
0316                     relation.setLeft(i64);
0317                     stream >> ba;
0318                     relation.setLeftMimeType(ba);
0319                     stream >> i64;
0320                     relation.setRight(i64);
0321                     stream >> ba;
0322                     relation.setRightMimeType(ba);
0323                     stream >> ba;
0324                     relation.setType(ba);
0325                     stream >> ba;
0326                     relation.setRemoteId(ba);
0327                     relations << relation;
0328                 }
0329                 item.setRelations(relations);
0330                 stream >> cnt;
0331                 QList<Protocol::Ancestor> ancestors;
0332                 for (int k = 0; k < cnt; ++k) {
0333                     Protocol::Ancestor ancestor;
0334                     stream >> i64;
0335                     ancestor.setId(i64);
0336                     stream >> str;
0337                     ancestor.setRemoteId(str);
0338                     stream >> str;
0339                     ancestor.setName(str);
0340                     stream >> babaMap;
0341                     ancestor.setAttributes(babaMap);
0342                     ancestors << ancestor;
0343                 }
0344                 item.setAncestors(ancestors);
0345                 stream >> cnt;
0346                 QList<Protocol::StreamPayloadResponse> parts;
0347                 for (int k = 0; k < cnt; ++k) {
0348                     Protocol::StreamPayloadResponse part;
0349                     stream >> ba;
0350                     part.setPayloadName(ba);
0351                     Protocol::PartMetaData metaData;
0352                     stream >> ba;
0353                     metaData.setName(ba);
0354                     stream >> i64;
0355                     metaData.setSize(i64);
0356                     stream >> i;
0357                     metaData.setVersion(i);
0358                     stream >> i;
0359                     metaData.setStorageType(static_cast<Protocol::PartMetaData::StorageType>(i));
0360                     part.setMetaData(metaData);
0361                     stream >> ba;
0362                     part.setData(ba);
0363                     parts << part;
0364                 }
0365                 item.setParts(parts);
0366                 stream >> bav;
0367                 item.setCachedParts(bav);
0368                 items.push_back(std::move(item));
0369             }
0370         } else {
0371             for (int j = 0; j < entityCnt; ++j) {
0372                 stream >> uid;
0373                 stream >> remoteId;
0374                 stream >> remoteRevision;
0375                 stream >> mimeType;
0376                 if (stream.status() != QDataStream::Ok) {
0377                     qCWarning(AKONADICORE_LOG) << "Error reading saved notifications! Aborting";
0378                     return msg;
0379                 }
0380                 Protocol::FetchItemsResponse item;
0381                 item.setId(uid);
0382                 item.setRemoteId(remoteId);
0383                 item.setRemoteRevision(remoteRevision);
0384                 item.setMimeType(mimeType);
0385                 items.push_back(std::move(item));
0386             }
0387             msg->addMetadata("FETCH_ITEM");
0388         }
0389         stream >> resource;
0390         stream >> destinationResource;
0391         stream >> parentCollection;
0392         stream >> parentDestCollection;
0393         stream >> itemParts;
0394         stream >> addedFlags;
0395         stream >> removedFlags;
0396         if (version >= 3) {
0397             stream >> addedTags;
0398             stream >> removedTags;
0399         }
0400         if (version >= 8) {
0401             bool boolean;
0402             stream >> boolean;
0403             msg->setMustRetrieve(boolean);
0404         }
0405     } else {
0406         qCWarning(AKONADICORE_LOG) << "Error version is not correct here" << version;
0407         return msg;
0408     }
0409     if (version >= 5) {
0410         msg->setOperation(static_cast<Protocol::ItemChangeNotification::Operation>(operation));
0411     } else {
0412         msg->setOperation(mapItemOperation(static_cast<LegacyOp>(operation)));
0413     }
0414     msg->setItems(items);
0415     msg->setResource(resource);
0416     msg->setDestinationResource(destinationResource);
0417     msg->setParentCollection(parentCollection);
0418     msg->setParentDestCollection(parentDestCollection);
0419     msg->setItemParts(itemParts);
0420     msg->setAddedRelations(extractRelations(addedFlags));
0421     msg->setAddedFlags(addedFlags);
0422     msg->setRemovedRelations(extractRelations(removedFlags));
0423     msg->setRemovedFlags(removedFlags);
0424     msg->setAddedTags(addedTags);
0425     msg->setRemovedTags(removedTags);
0426     return msg;
0427 }
0428 
0429 QSet<QByteArray> ChangeRecorderJournalWriter::encodeRelations(const QSet<Protocol::ItemChangeNotification::Relation> &relations)
0430 {
0431     QSet<QByteArray> rv;
0432     for (const auto &rel : relations) {
0433         rv.insert("RELATION " + rel.type.toLatin1() + ' ' + QByteArray::number(rel.leftId) + ' ' + QByteArray::number(rel.rightId));
0434     }
0435     return rv;
0436 }
0437 
0438 void ChangeRecorderJournalWriter::saveItemNotification(QDataStream &stream, const Protocol::ItemChangeNotification &msg)
0439 {
0440     // Version 8
0441 
0442     stream << int(msg.operation());
0443     const auto &items = msg.items();
0444     stream << items.count();
0445     for (const auto &item : items) {
0446         stream << item.id() << item.revision() << item.parentId() << item.remoteId() << item.remoteRevision() << item.gid() << item.size() << item.mimeType()
0447                << item.mTime() << item.flags();
0448         const auto tags = item.tags();
0449         stream << tags.count();
0450         for (const auto &tag : tags) {
0451             stream << tag.id() << tag.parentId() << tag.gid() << tag.type() << tag.remoteId() << tag.attributes();
0452         }
0453         stream << item.virtualReferences();
0454         const auto relations = item.relations();
0455         stream << relations.count();
0456         for (const auto &relation : relations) {
0457             stream << relation.left() << relation.leftMimeType() << relation.right() << relation.rightMimeType() << relation.type() << relation.remoteId();
0458         }
0459         const auto ancestors = item.ancestors();
0460         stream << ancestors.count();
0461         for (const auto &ancestor : ancestors) {
0462             stream << ancestor.id() << ancestor.remoteId() << ancestor.name() << ancestor.attributes();
0463         }
0464         const auto parts = item.parts();
0465         stream << parts.count();
0466         for (const auto &part : parts) {
0467             const auto metaData = part.metaData();
0468             stream << part.payloadName() << metaData.name() << metaData.size() << metaData.version() << static_cast<int>(metaData.storageType()) << part.data();
0469         }
0470         stream << item.cachedParts();
0471     }
0472     stream << msg.resource();
0473     stream << msg.destinationResource();
0474     stream << quint64(msg.parentCollection());
0475     stream << quint64(msg.parentDestCollection());
0476     stream << msg.itemParts();
0477     stream << msg.addedFlags() + encodeRelations(msg.addedRelations());
0478     stream << msg.removedFlags() + encodeRelations(msg.removedRelations());
0479     stream << msg.addedTags();
0480     stream << msg.removedTags();
0481     stream << msg.mustRetrieve();
0482 }
0483 
0484 Protocol::ChangeNotificationPtr ChangeRecorderJournalReader::loadCollectionNotification(QDataStream &stream, quint64 version)
0485 {
0486     QByteArray resource;
0487     QByteArray destinationResource;
0488     int operation;
0489     int entityCnt;
0490     quint64 uid;
0491     quint64 parentCollection;
0492     quint64 parentDestCollection;
0493     QString remoteId;
0494     QString remoteRevision;
0495     QString dummyString;
0496     QSet<QByteArray> changedParts;
0497     QSet<QByteArray> dummyBa;
0498     QSet<qint64> dummyIv;
0499 
0500     auto msg = Protocol::CollectionChangeNotificationPtr::create();
0501 
0502     if (version == 1) {
0503         stream >> operation;
0504         stream >> uid;
0505         stream >> remoteId;
0506         stream >> resource;
0507         stream >> parentCollection;
0508         stream >> parentDestCollection;
0509         stream >> dummyString;
0510         stream >> changedParts;
0511 
0512         Protocol::FetchCollectionsResponse collection;
0513         collection.setId(uid);
0514         collection.setRemoteId(remoteId);
0515         msg->setCollection(std::move(collection));
0516         msg->addMetadata("FETCH_COLLECTION");
0517     } else if (version >= 2) {
0518         stream >> operation;
0519         stream >> entityCnt;
0520         if (version >= 7) {
0521             QString str;
0522             QStringList stringList;
0523             qint64 i64;
0524             QList<qint64> vb;
0525             QMap<QByteArray, QByteArray> attrs;
0526             bool b;
0527             int i;
0528             Tristate tristate;
0529             Protocol::FetchCollectionsResponse collection;
0530             stream >> uid;
0531             collection.setId(uid);
0532             stream >> uid;
0533             collection.setParentId(uid);
0534             stream >> str;
0535             collection.setName(str);
0536             stream >> stringList;
0537             collection.setMimeTypes(stringList);
0538             stream >> str;
0539             collection.setRemoteId(str);
0540             stream >> str;
0541             collection.setRemoteRevision(str);
0542             stream >> str;
0543             collection.setResource(str);
0544 
0545             Protocol::FetchCollectionStatsResponse stats;
0546             stream >> i64;
0547             stats.setCount(i64);
0548             stream >> i64;
0549             stats.setUnseen(i64);
0550             stream >> i64;
0551             stats.setSize(i64);
0552             collection.setStatistics(stats);
0553 
0554             stream >> str;
0555             collection.setSearchQuery(str);
0556             stream >> vb;
0557             collection.setSearchCollections(vb);
0558             stream >> entityCnt;
0559             QList<Protocol::Ancestor> ancestors;
0560             for (int j = 0; j < entityCnt; ++j) {
0561                 Protocol::Ancestor ancestor;
0562                 stream >> i64;
0563                 ancestor.setId(i64);
0564                 stream >> str;
0565                 ancestor.setRemoteId(str);
0566                 stream >> str;
0567                 ancestor.setName(str);
0568                 stream >> attrs;
0569                 ancestor.setAttributes(attrs);
0570                 ancestors.push_back(ancestor);
0571 
0572                 if (stream.status() != QDataStream::Ok) {
0573                     qCWarning(AKONADICORE_LOG) << "Erorr reading saved notifications! Aborting";
0574                     return msg;
0575                 }
0576             }
0577             collection.setAncestors(ancestors);
0578 
0579             Protocol::CachePolicy cachePolicy;
0580             stream >> b;
0581             cachePolicy.setInherit(b);
0582             stream >> i;
0583             cachePolicy.setCheckInterval(i);
0584             stream >> i;
0585             cachePolicy.setCacheTimeout(i);
0586             stream >> b;
0587             cachePolicy.setSyncOnDemand(b);
0588             stream >> stringList;
0589             cachePolicy.setLocalParts(stringList);
0590             collection.setCachePolicy(cachePolicy);
0591 
0592             stream >> attrs;
0593             collection.setAttributes(attrs);
0594             stream >> b;
0595             collection.setEnabled(b);
0596             stream >> reinterpret_cast<qint8 &>(tristate);
0597             collection.setDisplayPref(tristate);
0598             stream >> reinterpret_cast<qint8 &>(tristate);
0599             collection.setSyncPref(tristate);
0600             stream >> reinterpret_cast<qint8 &>(tristate);
0601             collection.setIndexPref(tristate);
0602             stream >> b; // read the deprecated "isReferenced" value
0603             stream >> b;
0604             collection.setIsVirtual(b);
0605 
0606             msg->setCollection(std::move(collection));
0607         } else {
0608             for (int j = 0; j < entityCnt; ++j) {
0609                 stream >> uid;
0610                 stream >> remoteId;
0611                 stream >> remoteRevision;
0612                 stream >> dummyString;
0613                 if (stream.status() != QDataStream::Ok) {
0614                     qCWarning(AKONADICORE_LOG) << "Error reading saved notifications! Aborting";
0615                     return msg;
0616                 }
0617                 Protocol::FetchCollectionsResponse collection;
0618                 collection.setId(uid);
0619                 collection.setRemoteId(remoteId);
0620                 collection.setRemoteRevision(remoteRevision);
0621                 msg->setCollection(std::move(collection));
0622                 msg->addMetadata("FETCH_COLLECTION");
0623             }
0624         }
0625         stream >> resource;
0626         stream >> destinationResource;
0627         stream >> parentCollection;
0628         stream >> parentDestCollection;
0629         stream >> changedParts;
0630         stream >> dummyBa;
0631         stream >> dummyBa;
0632         if (version >= 3) {
0633             stream >> dummyIv;
0634             stream >> dummyIv;
0635         }
0636     } else {
0637         qCWarning(AKONADICORE_LOG) << "Error version is not correct here" << version;
0638         return msg;
0639     }
0640 
0641     if (version >= 5) {
0642         msg->setOperation(static_cast<Protocol::CollectionChangeNotification::Operation>(operation));
0643     } else {
0644         msg->setOperation(mapCollectionOperation(static_cast<LegacyOp>(operation)));
0645     }
0646     msg->setResource(resource);
0647     msg->setDestinationResource(destinationResource);
0648     msg->setParentCollection(parentCollection);
0649     msg->setParentDestCollection(parentDestCollection);
0650     msg->setChangedParts(changedParts);
0651     return msg;
0652 }
0653 
0654 void Akonadi::ChangeRecorderJournalWriter::saveCollectionNotification(QDataStream &stream, const Protocol::CollectionChangeNotification &msg)
0655 {
0656     // Version 7
0657 
0658     const auto &col = msg.collection();
0659 
0660     stream << int(msg.operation());
0661     stream << int(1);
0662     stream << col.id();
0663     stream << col.parentId();
0664     stream << col.name();
0665     stream << col.mimeTypes();
0666     stream << col.remoteId();
0667     stream << col.remoteRevision();
0668     stream << col.resource();
0669     const auto stats = col.statistics();
0670     stream << stats.count();
0671     stream << stats.unseen();
0672     stream << stats.size();
0673     stream << col.searchQuery();
0674     stream << col.searchCollections();
0675     const auto ancestors = col.ancestors();
0676     stream << ancestors.count();
0677     for (const auto &ancestor : ancestors) {
0678         stream << ancestor.id() << ancestor.remoteId() << ancestor.name() << ancestor.attributes();
0679     }
0680     const auto cachePolicy = col.cachePolicy();
0681     stream << cachePolicy.inherit();
0682     stream << cachePolicy.checkInterval();
0683     stream << cachePolicy.cacheTimeout();
0684     stream << cachePolicy.syncOnDemand();
0685     stream << cachePolicy.localParts();
0686     stream << col.attributes();
0687     stream << col.enabled();
0688     stream << static_cast<qint8>(col.displayPref());
0689     stream << static_cast<qint8>(col.syncPref());
0690     stream << static_cast<qint8>(col.indexPref());
0691     stream << false; // write the deprecated "isReferenced" value
0692     stream << col.isVirtual();
0693 
0694     stream << msg.resource();
0695     stream << msg.destinationResource();
0696     stream << quint64(msg.parentCollection());
0697     stream << quint64(msg.parentDestCollection());
0698     stream << msg.changedParts();
0699     stream << QSet<QByteArray>();
0700     stream << QSet<QByteArray>();
0701     stream << QSet<qint64>();
0702     stream << QSet<qint64>();
0703 }
0704 
0705 Protocol::ChangeNotificationPtr ChangeRecorderJournalReader::loadTagNotification(QDataStream &stream, quint64 version)
0706 {
0707     QByteArray resource;
0708     QByteArray dummyBa;
0709     int operation;
0710     int entityCnt;
0711     quint64 uid;
0712     quint64 dummyI;
0713     QString remoteId;
0714     QString dummyString;
0715     QSet<QByteArray> dummyBaV;
0716     QSet<qint64> dummyIv;
0717 
0718     auto msg = Protocol::TagChangeNotificationPtr::create();
0719 
0720     if (version == 1) {
0721         stream >> operation;
0722         stream >> uid;
0723         stream >> remoteId;
0724         stream >> dummyBa;
0725         stream >> dummyI;
0726         stream >> dummyI;
0727         stream >> dummyString;
0728         stream >> dummyBaV;
0729 
0730         Protocol::FetchTagsResponse tag;
0731         tag.setId(uid);
0732         tag.setRemoteId(remoteId.toLatin1());
0733         msg->setTag(std::move(tag));
0734         msg->addMetadata("FETCH_TAG");
0735     } else if (version >= 2) {
0736         stream >> operation;
0737         stream >> entityCnt;
0738         if (version >= 7) {
0739             QByteArray ba;
0740             QMap<QByteArray, QByteArray> attrs;
0741 
0742             Protocol::FetchTagsResponse tag;
0743 
0744             stream >> uid;
0745             tag.setId(uid);
0746             stream >> ba;
0747             tag.setParentId(uid);
0748             stream >> attrs;
0749             tag.setGid(ba);
0750             stream >> ba;
0751             tag.setType(ba);
0752             stream >> uid;
0753             tag.setRemoteId(ba);
0754             stream >> ba;
0755             tag.setAttributes(attrs);
0756             msg->setTag(std::move(tag));
0757 
0758             stream >> resource;
0759         } else {
0760             for (int j = 0; j < entityCnt; ++j) {
0761                 stream >> uid;
0762                 stream >> remoteId;
0763                 stream >> dummyString;
0764                 stream >> dummyString;
0765                 if (stream.status() != QDataStream::Ok) {
0766                     qCWarning(AKONADICORE_LOG) << "Error reading saved notifications! Aborting";
0767                     return msg;
0768                 }
0769                 Protocol::FetchTagsResponse tag;
0770                 tag.setId(uid);
0771                 tag.setRemoteId(remoteId.toLatin1());
0772                 msg->setTag(std::move(tag));
0773                 msg->addMetadata("FETCH_TAG");
0774             }
0775             stream >> resource;
0776             stream >> dummyBa;
0777             stream >> dummyI;
0778             stream >> dummyI;
0779             stream >> dummyBaV;
0780             stream >> dummyBaV;
0781             stream >> dummyBaV;
0782             if (version >= 3) {
0783                 stream >> dummyIv;
0784                 stream >> dummyIv;
0785             }
0786         }
0787         if (version >= 5) {
0788             msg->setOperation(static_cast<Protocol::TagChangeNotification::Operation>(operation));
0789         } else {
0790             msg->setOperation(mapTagOperation(static_cast<LegacyOp>(operation)));
0791         }
0792     }
0793     msg->setResource(resource);
0794     return msg;
0795 }
0796 
0797 void Akonadi::ChangeRecorderJournalWriter::saveTagNotification(QDataStream &stream, const Protocol::TagChangeNotification &msg)
0798 {
0799     const auto &tag = msg.tag();
0800     stream << int(msg.operation());
0801     stream << int(1);
0802     stream << tag.id();
0803     stream << tag.parentId();
0804     stream << tag.gid();
0805     stream << tag.type();
0806     stream << tag.remoteId();
0807     stream << tag.attributes();
0808     stream << msg.resource();
0809 }
0810 
0811 Protocol::ChangeNotificationPtr ChangeRecorderJournalReader::loadRelationNotification(QDataStream &stream, quint64 version)
0812 {
0813     QByteArray dummyBa;
0814     int operation;
0815     int entityCnt;
0816     quint64 dummyI;
0817     QString dummyString;
0818     QSet<QByteArray> itemParts;
0819     QSet<QByteArray> dummyBaV;
0820     QSet<qint64> dummyIv;
0821 
0822     auto msg = Protocol::RelationChangeNotificationPtr::create();
0823 
0824     if (version == 1) {
0825         qCWarning(AKONADICORE_LOG) << "Invalid version of relation notification";
0826         return msg;
0827     } else if (version >= 2) {
0828         stream >> operation;
0829         stream >> entityCnt;
0830         if (version >= 7) {
0831             Protocol::FetchRelationsResponse relation;
0832             qint64 i64;
0833             QByteArray ba;
0834             stream >> i64;
0835             relation.setLeft(i64);
0836             stream >> ba;
0837             relation.setLeftMimeType(ba);
0838             stream >> i64;
0839             relation.setRight(i64);
0840             stream >> ba;
0841             relation.setRightMimeType(ba);
0842             stream >> ba;
0843             relation.setRemoteId(ba);
0844             stream >> ba;
0845             relation.setType(ba);
0846 
0847             msg->setRelation(std::move(relation));
0848 
0849         } else {
0850             for (int j = 0; j < entityCnt; ++j) {
0851                 stream >> dummyI;
0852                 stream >> dummyString;
0853                 stream >> dummyString;
0854                 stream >> dummyString;
0855                 if (stream.status() != QDataStream::Ok) {
0856                     qCWarning(AKONADICORE_LOG) << "Error reading saved notifications! Aborting";
0857                     return msg;
0858                 }
0859             }
0860             stream >> dummyBa;
0861             if (version == 5) {
0862                 // there was a bug in version 5 serializer that serialized this
0863                 // field as qint64 (8 bytes) instead of empty QByteArray (which is
0864                 // 4 bytes)
0865                 stream >> dummyI;
0866             } else {
0867                 stream >> dummyBa;
0868             }
0869             stream >> dummyI;
0870             stream >> dummyI;
0871             stream >> itemParts;
0872             stream >> dummyBaV;
0873             stream >> dummyBaV;
0874             if (version >= 3) {
0875                 stream >> dummyIv;
0876                 stream >> dummyIv;
0877             }
0878 
0879             Protocol::FetchRelationsResponse relation;
0880             for (const QByteArray &part : std::as_const(itemParts)) {
0881                 const QByteArrayList p = part.split(' ');
0882                 if (p.size() < 2) {
0883                     continue;
0884                 }
0885                 if (p[0] == "LEFT") {
0886                     relation.setLeft(p[1].toLongLong());
0887                 } else if (p[0] == "RIGHT") {
0888                     relation.setRight(p[1].toLongLong());
0889                 } else if (p[0] == "RID") {
0890                     relation.setRemoteId(p[1]);
0891                 } else if (p[0] == "TYPE") {
0892                     relation.setType(p[1]);
0893                 }
0894             }
0895             msg->setRelation(std::move(relation));
0896         }
0897         if (version >= 5) {
0898             msg->setOperation(static_cast<Protocol::RelationChangeNotification::Operation>(operation));
0899         } else {
0900             msg->setOperation(mapRelationOperation(static_cast<LegacyOp>(operation)));
0901         }
0902     }
0903 
0904     return msg;
0905 }
0906 
0907 void Akonadi::ChangeRecorderJournalWriter::saveRelationNotification(QDataStream &stream, const Protocol::RelationChangeNotification &msg)
0908 {
0909     const auto &rel = msg.relation();
0910     stream << int(msg.operation());
0911     stream << int(0);
0912     stream << rel.left();
0913     stream << rel.leftMimeType();
0914     stream << rel.right();
0915     stream << rel.rightMimeType();
0916     stream << rel.remoteId();
0917     stream << rel.type();
0918 }
0919 
0920 Protocol::ItemChangeNotification::Operation ChangeRecorderJournalReader::mapItemOperation(LegacyOp op)
0921 {
0922     switch (op) {
0923     case Add:
0924         return Protocol::ItemChangeNotification::Add;
0925     case Modify:
0926         return Protocol::ItemChangeNotification::Modify;
0927     case Move:
0928         return Protocol::ItemChangeNotification::Move;
0929     case Remove:
0930         return Protocol::ItemChangeNotification::Remove;
0931     case Link:
0932         return Protocol::ItemChangeNotification::Link;
0933     case Unlink:
0934         return Protocol::ItemChangeNotification::Unlink;
0935     case ModifyFlags:
0936         return Protocol::ItemChangeNotification::ModifyFlags;
0937     case ModifyTags:
0938         return Protocol::ItemChangeNotification::ModifyTags;
0939     case ModifyRelations:
0940         return Protocol::ItemChangeNotification::ModifyRelations;
0941     default:
0942         qWarning() << "Unexpected operation type in item notification";
0943         return Protocol::ItemChangeNotification::InvalidOp;
0944     }
0945 }
0946 
0947 Protocol::CollectionChangeNotification::Operation ChangeRecorderJournalReader::mapCollectionOperation(LegacyOp op)
0948 {
0949     switch (op) {
0950     case Add:
0951         return Protocol::CollectionChangeNotification::Add;
0952     case Modify:
0953         return Protocol::CollectionChangeNotification::Modify;
0954     case Move:
0955         return Protocol::CollectionChangeNotification::Move;
0956     case Remove:
0957         return Protocol::CollectionChangeNotification::Remove;
0958     case Subscribe:
0959         return Protocol::CollectionChangeNotification::Subscribe;
0960     case Unsubscribe:
0961         return Protocol::CollectionChangeNotification::Unsubscribe;
0962     default:
0963         qCWarning(AKONADICORE_LOG) << "Unexpected operation type in collection notification";
0964         return Protocol::CollectionChangeNotification::InvalidOp;
0965     }
0966 }
0967 
0968 Protocol::TagChangeNotification::Operation ChangeRecorderJournalReader::mapTagOperation(LegacyOp op)
0969 {
0970     switch (op) {
0971     case Add:
0972         return Protocol::TagChangeNotification::Add;
0973     case Modify:
0974         return Protocol::TagChangeNotification::Modify;
0975     case Remove:
0976         return Protocol::TagChangeNotification::Remove;
0977     default:
0978         qCWarning(AKONADICORE_LOG) << "Unexpected operation type in tag notification";
0979         return Protocol::TagChangeNotification::InvalidOp;
0980     }
0981 }
0982 
0983 Protocol::RelationChangeNotification::Operation ChangeRecorderJournalReader::mapRelationOperation(LegacyOp op)
0984 {
0985     switch (op) {
0986     case Add:
0987         return Protocol::RelationChangeNotification::Add;
0988     case Remove:
0989         return Protocol::RelationChangeNotification::Remove;
0990     default:
0991         qCWarning(AKONADICORE_LOG) << "Unexpected operation type in relation notification";
0992         return Protocol::RelationChangeNotification::InvalidOp;
0993     }
0994 }
0995 
0996 ChangeRecorderJournalReader::LegacyType ChangeRecorderJournalWriter::mapToLegacyType(Protocol::Command::Type type)
0997 {
0998     switch (type) {
0999     case Protocol::Command::ItemChangeNotification:
1000         return ChangeRecorderJournalReader::Item;
1001     case Protocol::Command::CollectionChangeNotification:
1002         return ChangeRecorderJournalReader::Collection;
1003     case Protocol::Command::TagChangeNotification:
1004         return ChangeRecorderJournalReader::Tag;
1005     case Protocol::Command::RelationChangeNotification:
1006         return ChangeRecorderJournalReader::Relation;
1007     default:
1008         qCWarning(AKONADICORE_LOG) << "Unexpected notification type";
1009         return ChangeRecorderJournalReader::InvalidType;
1010     }
1011 }