File indexing completed on 2024-11-10 04:40:44

0001 /*
0002     SPDX-FileCopyrightText: 2008 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "akonadicore_debug.h"
0008 #include "attributefactory.h"
0009 #include "collection_p.h"
0010 #include "collectionstatistics.h"
0011 #include "exceptionbase.h"
0012 #include "item_p.h"
0013 #include "itemfetchscope.h"
0014 #include "itemserializer_p.h"
0015 #include "persistentsearchattribute.h"
0016 #include "protocolhelper_p.h"
0017 #include "tag_p.h"
0018 #include "tagfetchscope.h"
0019 
0020 #include "private/externalpartstorage_p.h"
0021 #include "private/protocol_p.h"
0022 
0023 #include "shared/akranges.h"
0024 
0025 #include <QFile>
0026 #include <QVarLengthArray>
0027 
0028 using namespace Akonadi;
0029 using namespace AkRanges;
0030 
0031 CachePolicy ProtocolHelper::parseCachePolicy(const Protocol::CachePolicy &policy)
0032 {
0033     CachePolicy cp;
0034     cp.setCacheTimeout(policy.cacheTimeout());
0035     cp.setIntervalCheckTime(policy.checkInterval());
0036     cp.setInheritFromParent(policy.inherit());
0037     cp.setSyncOnDemand(policy.syncOnDemand());
0038     cp.setLocalParts(policy.localParts());
0039     return cp;
0040 }
0041 
0042 Protocol::CachePolicy ProtocolHelper::cachePolicyToProtocol(const CachePolicy &policy)
0043 {
0044     Protocol::CachePolicy proto;
0045     proto.setCacheTimeout(policy.cacheTimeout());
0046     proto.setCheckInterval(policy.intervalCheckTime());
0047     proto.setInherit(policy.inheritFromParent());
0048     proto.setSyncOnDemand(policy.syncOnDemand());
0049     proto.setLocalParts(policy.localParts());
0050     return proto;
0051 }
0052 
0053 template<typename T>
0054 inline static void parseAttributesImpl(const Protocol::Attributes &attributes, T *entity)
0055 {
0056     for (auto iter = attributes.cbegin(), end = attributes.cend(); iter != end; ++iter) {
0057         Attribute *attribute = AttributeFactory::createAttribute(iter.key());
0058         if (!attribute) {
0059             qCWarning(AKONADICORE_LOG) << "Warning: unknown attribute" << iter.key();
0060             continue;
0061         }
0062         attribute->deserialize(iter.value());
0063         entity->addAttribute(attribute);
0064     }
0065 }
0066 
0067 template<typename T>
0068 inline static void
0069 parseAncestorsCachedImpl(const QList<Protocol::Ancestor> &ancestors, T *entity, Collection::Id parentCollection, ProtocolHelperValuePool *pool)
0070 {
0071     if (!pool || parentCollection == -1) {
0072         // if no pool or parent collection id is provided we can't cache anything, so continue as usual
0073         ProtocolHelper::parseAncestors(ancestors, entity);
0074         return;
0075     }
0076 
0077     if (pool->ancestorCollections.contains(parentCollection)) {
0078         // ancestor chain is cached already, so use the cached value
0079         entity->setParentCollection(pool->ancestorCollections.value(parentCollection));
0080     } else {
0081         // not cached yet, parse the chain
0082         ProtocolHelper::parseAncestors(ancestors, entity);
0083         pool->ancestorCollections.insert(parentCollection, entity->parentCollection());
0084     }
0085 }
0086 
0087 template<typename T>
0088 inline static Protocol::Attributes attributesToProtocolImpl(const T &entity, bool ns)
0089 {
0090     Protocol::Attributes attributes;
0091     const auto attrs = entity.attributes();
0092     for (const auto attr : attrs) {
0093         attributes.insert(ProtocolHelper::encodePartIdentifier(ns ? ProtocolHelper::PartAttribute : ProtocolHelper::PartGlobal, attr->type()),
0094                           attr->serialized());
0095     }
0096     return attributes;
0097 }
0098 
0099 void ProtocolHelper::parseAncestorsCached(const QList<Protocol::Ancestor> &ancestors,
0100                                           Item *item,
0101                                           Collection::Id parentCollection,
0102                                           ProtocolHelperValuePool *pool)
0103 {
0104     parseAncestorsCachedImpl(ancestors, item, parentCollection, pool);
0105 }
0106 
0107 void ProtocolHelper::parseAncestorsCached(const QList<Protocol::Ancestor> &ancestors,
0108                                           Collection *collection,
0109                                           Collection::Id parentCollection,
0110                                           ProtocolHelperValuePool *pool)
0111 {
0112     parseAncestorsCachedImpl(ancestors, collection, parentCollection, pool);
0113 }
0114 
0115 void ProtocolHelper::parseAncestors(const QList<Protocol::Ancestor> &ancestors, Item *item)
0116 {
0117     Collection fakeCollection;
0118     parseAncestors(ancestors, &fakeCollection);
0119 
0120     item->setParentCollection(fakeCollection.parentCollection());
0121 }
0122 
0123 void ProtocolHelper::parseAncestors(const QList<Protocol::Ancestor> &ancestors, Collection *collection)
0124 {
0125     static const Collection::Id rootCollectionId = Collection::root().id();
0126 
0127     Collection *current = collection;
0128     for (const Protocol::Ancestor &ancestor : ancestors) {
0129         if (ancestor.id() == rootCollectionId) {
0130             current->setParentCollection(Collection::root());
0131             break;
0132         }
0133 
0134         Akonadi::Collection parentCollection(ancestor.id());
0135         parentCollection.setName(ancestor.name());
0136         parentCollection.setRemoteId(ancestor.remoteId());
0137         parseAttributesImpl(ancestor.attributes(), &parentCollection);
0138         current->setParentCollection(parentCollection);
0139         current = &current->parentCollection();
0140     }
0141 }
0142 
0143 static Collection::ListPreference parsePreference(Tristate value)
0144 {
0145     switch (value) {
0146     case Tristate::True:
0147         return Collection::ListEnabled;
0148     case Tristate::False:
0149         return Collection::ListDisabled;
0150     case Tristate::Undefined:
0151         return Collection::ListDefault;
0152     }
0153 
0154     Q_ASSERT(false);
0155     return Collection::ListDefault;
0156 }
0157 
0158 CollectionStatistics ProtocolHelper::parseCollectionStatistics(const Protocol::FetchCollectionStatsResponse &stats)
0159 {
0160     CollectionStatistics cs;
0161     cs.setCount(stats.count());
0162     cs.setSize(stats.size());
0163     cs.setUnreadCount(stats.unseen());
0164     return cs;
0165 }
0166 
0167 void ProtocolHelper::parseAttributes(const Protocol::Attributes &attributes, Item *item)
0168 {
0169     parseAttributesImpl(attributes, item);
0170 }
0171 
0172 void ProtocolHelper::parseAttributes(const Protocol::Attributes &attributes, Collection *collection)
0173 {
0174     parseAttributesImpl(attributes, collection);
0175 }
0176 
0177 void ProtocolHelper::parseAttributes(const Protocol::Attributes &attributes, Tag *tag)
0178 {
0179     parseAttributesImpl(attributes, tag);
0180 }
0181 
0182 Protocol::Attributes ProtocolHelper::attributesToProtocol(const Item &item, bool ns)
0183 {
0184     return attributesToProtocolImpl(item, ns);
0185 }
0186 
0187 Protocol::Attributes ProtocolHelper::attributesToProtocol(const Collection &collection, bool ns)
0188 {
0189     return attributesToProtocolImpl(collection, ns);
0190 }
0191 
0192 Protocol::Attributes ProtocolHelper::attributesToProtocol(const Tag &tag, bool ns)
0193 {
0194     return attributesToProtocolImpl(tag, ns);
0195 }
0196 
0197 Protocol::Attributes ProtocolHelper::attributesToProtocol(const std::vector<Attribute *> &modifiedAttributes, bool ns)
0198 {
0199     Protocol::Attributes attributes;
0200     for (const Attribute *attr : modifiedAttributes) {
0201         attributes.insert(ProtocolHelper::encodePartIdentifier(ns ? ProtocolHelper::PartAttribute : ProtocolHelper::PartGlobal, attr->type()),
0202                           attr->serialized());
0203     }
0204     return attributes;
0205 }
0206 
0207 Collection ProtocolHelper::parseCollection(const Protocol::FetchCollectionsResponse &data, bool requireParent)
0208 {
0209     Collection collection(data.id());
0210 
0211     if (requireParent) {
0212         collection.setParentCollection(Collection(data.parentId()));
0213     }
0214 
0215     collection.setName(data.name());
0216     collection.setRemoteId(data.remoteId());
0217     collection.setRemoteRevision(data.remoteRevision());
0218     collection.setResource(data.resource());
0219     collection.setContentMimeTypes(data.mimeTypes());
0220     collection.setVirtual(data.isVirtual());
0221     collection.setStatistics(parseCollectionStatistics(data.statistics()));
0222     collection.setCachePolicy(parseCachePolicy(data.cachePolicy()));
0223     parseAncestors(data.ancestors(), &collection);
0224     collection.setEnabled(data.enabled());
0225     collection.setLocalListPreference(Collection::ListDisplay, parsePreference(data.displayPref()));
0226     collection.setLocalListPreference(Collection::ListIndex, parsePreference(data.indexPref()));
0227     collection.setLocalListPreference(Collection::ListSync, parsePreference(data.syncPref()));
0228 
0229     if (!data.searchQuery().isEmpty()) {
0230         auto attr = collection.attribute<PersistentSearchAttribute>(Collection::AddIfMissing);
0231         attr->setQueryString(data.searchQuery());
0232         const auto cols = data.searchCollections() | Views::transform([](const auto id) {
0233                               return Collection{id};
0234                           })
0235             | Actions::toQVector;
0236         attr->setQueryCollections(cols);
0237     }
0238 
0239     parseAttributes(data.attributes(), &collection);
0240 
0241     collection.d_ptr->resetChangeLog();
0242     return collection;
0243 }
0244 
0245 Tag ProtocolHelper::parseTag(const Protocol::FetchTagsResponse &data)
0246 {
0247     Tag tag(data.id());
0248     tag.setRemoteId(data.remoteId());
0249     tag.setGid(data.gid());
0250     tag.setType(data.type());
0251     tag.setParent(Tag(data.parentId()));
0252     parseAttributes(data.attributes(), &tag);
0253     tag.d_ptr->resetChangeLog();
0254 
0255     return tag;
0256 }
0257 
0258 QByteArray ProtocolHelper::encodePartIdentifier(PartNamespace ns, const QByteArray &label)
0259 {
0260     switch (ns) {
0261     case PartGlobal:
0262         return label;
0263     case PartPayload:
0264         return "PLD:" + label;
0265     case PartAttribute:
0266         return "ATR:" + label;
0267     default:
0268         Q_ASSERT(false);
0269     }
0270     return QByteArray();
0271 }
0272 
0273 QByteArray ProtocolHelper::decodePartIdentifier(const QByteArray &data, PartNamespace &ns)
0274 {
0275     if (data.startsWith("PLD:")) { // krazy:exclude=strings
0276         ns = PartPayload;
0277         return data.mid(4);
0278     } else if (data.startsWith("ATR:")) { // krazy:exclude=strings
0279         ns = PartAttribute;
0280         return data.mid(4);
0281     } else {
0282         ns = PartGlobal;
0283         return data;
0284     }
0285 }
0286 
0287 Protocol::ScopeContext
0288 ProtocolHelper::commandContextToProtocol(const Akonadi::Collection &collection, const Akonadi::Tag &tag, const Item::List &requestedItems)
0289 {
0290     Protocol::ScopeContext ctx;
0291     if (tag.isValid()) {
0292         ctx.setContext(Protocol::ScopeContext::Tag, tag.id());
0293     }
0294 
0295     if (collection == Collection::root()) {
0296         if (requestedItems.isEmpty() && !tag.isValid()) { // collection content listing
0297             throw Exception("Cannot perform item operations on root collection.");
0298         }
0299     } else {
0300         if (collection.isValid()) {
0301             ctx.setContext(Protocol::ScopeContext::Collection, collection.id());
0302         } else if (!collection.remoteId().isEmpty()) {
0303             ctx.setContext(Protocol::ScopeContext::Collection, collection.remoteId());
0304         }
0305     }
0306 
0307     return ctx;
0308 }
0309 
0310 Scope ProtocolHelper::hierarchicalRidToScope(const Collection &col)
0311 {
0312     if (col == Collection::root()) {
0313         return Scope({Scope::HRID(0)});
0314     }
0315     if (col.remoteId().isEmpty()) {
0316         return Scope();
0317     }
0318 
0319     QList<Scope::HRID> chain;
0320     Collection c = col;
0321     while (!c.remoteId().isEmpty()) {
0322         chain.append(Scope::HRID(c.id(), c.remoteId()));
0323         c = c.parentCollection();
0324     }
0325     return Scope(chain + QList<Scope::HRID>{Scope::HRID(0)});
0326 }
0327 
0328 Scope ProtocolHelper::hierarchicalRidToScope(const Item &item)
0329 {
0330     return Scope(QList<Scope::HRID>({Scope::HRID(item.id(), item.remoteId())}) + hierarchicalRidToScope(item.parentCollection()).hridChain());
0331 }
0332 
0333 Protocol::ItemFetchScope ProtocolHelper::itemFetchScopeToProtocol(const ItemFetchScope &fetchScope)
0334 {
0335     Protocol::ItemFetchScope fs;
0336     QList<QByteArray> parts;
0337     parts.reserve(fetchScope.payloadParts().size() + fetchScope.attributes().size());
0338     parts += fetchScope.payloadParts() | Views::transform(std::bind(encodePartIdentifier, PartPayload, std::placeholders::_1)) | Actions::toQVector;
0339     parts += fetchScope.attributes() | Views::transform(std::bind(encodePartIdentifier, PartAttribute, std::placeholders::_1)) | Actions::toQVector;
0340     fs.setRequestedParts(parts);
0341 
0342     // The default scope
0343     fs.setFetch(Protocol::ItemFetchScope::Flags | Protocol::ItemFetchScope::Size | Protocol::ItemFetchScope::RemoteID | Protocol::ItemFetchScope::RemoteRevision
0344                 | Protocol::ItemFetchScope::MTime);
0345 
0346     fs.setFetch(Protocol::ItemFetchScope::FullPayload, fetchScope.fullPayload());
0347     fs.setFetch(Protocol::ItemFetchScope::AllAttributes, fetchScope.allAttributes());
0348     fs.setFetch(Protocol::ItemFetchScope::CacheOnly, fetchScope.cacheOnly());
0349     fs.setFetch(Protocol::ItemFetchScope::CheckCachedPayloadPartsOnly, fetchScope.checkForCachedPayloadPartsOnly());
0350     fs.setFetch(Protocol::ItemFetchScope::IgnoreErrors, fetchScope.ignoreRetrievalErrors());
0351     switch (fetchScope.ancestorRetrieval()) {
0352     case ItemFetchScope::Parent:
0353         fs.setAncestorDepth(Protocol::ItemFetchScope::ParentAncestor);
0354         break;
0355     case ItemFetchScope::All:
0356         fs.setAncestorDepth(Protocol::ItemFetchScope::AllAncestors);
0357         break;
0358     case ItemFetchScope::None:
0359         fs.setAncestorDepth(Protocol::ItemFetchScope::NoAncestor);
0360         break;
0361     default:
0362         Q_ASSERT(false);
0363         break;
0364     }
0365 
0366     if (fetchScope.fetchChangedSince().isValid()) {
0367         fs.setChangedSince(fetchScope.fetchChangedSince());
0368     }
0369 
0370     fs.setFetch(Protocol::ItemFetchScope::RemoteID, fetchScope.fetchRemoteIdentification());
0371     fs.setFetch(Protocol::ItemFetchScope::RemoteRevision, fetchScope.fetchRemoteIdentification());
0372     fs.setFetch(Protocol::ItemFetchScope::GID, fetchScope.fetchGid());
0373     fs.setFetch(Protocol::ItemFetchScope::Tags, fetchScope.fetchTags());
0374     fs.setFetch(Protocol::ItemFetchScope::VirtReferences, fetchScope.fetchVirtualReferences());
0375     fs.setFetch(Protocol::ItemFetchScope::MTime, fetchScope.fetchModificationTime());
0376     fs.setFetch(Protocol::ItemFetchScope::Relations, fetchScope.fetchRelations());
0377 
0378     return fs;
0379 }
0380 
0381 ItemFetchScope ProtocolHelper::parseItemFetchScope(const Protocol::ItemFetchScope &fetchScope)
0382 {
0383     ItemFetchScope ifs;
0384     const auto parts = fetchScope.requestedParts();
0385     for (const auto &part : parts) {
0386         if (part.startsWith("PLD:")) {
0387             ifs.fetchPayloadPart(part.mid(4), true);
0388         } else if (part.startsWith("ATR:")) {
0389             ifs.fetchAttribute(part.mid(4), true);
0390         }
0391     }
0392 
0393     if (fetchScope.fetch(Protocol::ItemFetchScope::FullPayload)) {
0394         ifs.fetchFullPayload(true);
0395     }
0396     if (fetchScope.fetch(Protocol::ItemFetchScope::AllAttributes)) {
0397         ifs.fetchAllAttributes(true);
0398     }
0399     if (fetchScope.fetch(Protocol::ItemFetchScope::CacheOnly)) {
0400         ifs.setCacheOnly(true);
0401     }
0402     if (fetchScope.fetch(Protocol::ItemFetchScope::CheckCachedPayloadPartsOnly)) {
0403         ifs.setCheckForCachedPayloadPartsOnly(true);
0404     }
0405     if (fetchScope.fetch(Protocol::ItemFetchScope::IgnoreErrors)) {
0406         ifs.setIgnoreRetrievalErrors(true);
0407     }
0408     switch (fetchScope.ancestorDepth()) {
0409     case Protocol::ItemFetchScope::ParentAncestor:
0410         ifs.setAncestorRetrieval(ItemFetchScope::Parent);
0411         break;
0412     case Protocol::ItemFetchScope::AllAncestors:
0413         ifs.setAncestorRetrieval(ItemFetchScope::All);
0414         break;
0415     default:
0416         ifs.setAncestorRetrieval(ItemFetchScope::None);
0417         break;
0418     }
0419     if (fetchScope.changedSince().isValid()) {
0420         ifs.setFetchChangedSince(fetchScope.changedSince());
0421     }
0422     if (fetchScope.fetch(Protocol::ItemFetchScope::RemoteID) || fetchScope.fetch(Protocol::ItemFetchScope::RemoteRevision)) {
0423         ifs.setFetchRemoteIdentification(true);
0424     }
0425     if (fetchScope.fetch(Protocol::ItemFetchScope::GID)) {
0426         ifs.setFetchGid(true);
0427     }
0428     if (fetchScope.fetch(Protocol::ItemFetchScope::Tags)) {
0429         ifs.setFetchTags(true);
0430     }
0431     if (fetchScope.fetch(Protocol::ItemFetchScope::VirtReferences)) {
0432         ifs.setFetchVirtualReferences(true);
0433     }
0434     if (fetchScope.fetch(Protocol::ItemFetchScope::MTime)) {
0435         ifs.setFetchModificationTime(true);
0436     }
0437     if (fetchScope.fetch(Protocol::ItemFetchScope::Relations)) {
0438         ifs.setFetchRelations(true);
0439     }
0440 
0441     return ifs;
0442 }
0443 
0444 Protocol::CollectionFetchScope ProtocolHelper::collectionFetchScopeToProtocol(const CollectionFetchScope &fetchScope)
0445 {
0446     Protocol::CollectionFetchScope cfs;
0447     switch (fetchScope.listFilter()) {
0448     case CollectionFetchScope::NoFilter:
0449         cfs.setListFilter(Protocol::CollectionFetchScope::NoFilter);
0450         break;
0451     case CollectionFetchScope::Display:
0452         cfs.setListFilter(Protocol::CollectionFetchScope::Display);
0453         break;
0454     case CollectionFetchScope::Sync:
0455         cfs.setListFilter(Protocol::CollectionFetchScope::Sync);
0456         break;
0457     case CollectionFetchScope::Index:
0458         cfs.setListFilter(Protocol::CollectionFetchScope::Index);
0459         break;
0460     case CollectionFetchScope::Enabled:
0461         cfs.setListFilter(Protocol::CollectionFetchScope::Enabled);
0462         break;
0463     }
0464     cfs.setIncludeStatistics(fetchScope.includeStatistics());
0465     cfs.setResource(fetchScope.resource());
0466     cfs.setContentMimeTypes(fetchScope.contentMimeTypes());
0467     cfs.setAttributes(fetchScope.attributes());
0468     cfs.setFetchIdOnly(fetchScope.fetchIdOnly());
0469     switch (fetchScope.ancestorRetrieval()) {
0470     case CollectionFetchScope::None:
0471         cfs.setAncestorRetrieval(Protocol::CollectionFetchScope::None);
0472         break;
0473     case CollectionFetchScope::Parent:
0474         cfs.setAncestorRetrieval(Protocol::CollectionFetchScope::Parent);
0475         break;
0476     case CollectionFetchScope::All:
0477         cfs.setAncestorRetrieval(Protocol::CollectionFetchScope::All);
0478         break;
0479     }
0480     if (cfs.ancestorRetrieval() != Protocol::CollectionFetchScope::None) {
0481         cfs.setAncestorAttributes(fetchScope.ancestorFetchScope().attributes());
0482         cfs.setAncestorFetchIdOnly(fetchScope.ancestorFetchScope().fetchIdOnly());
0483     }
0484     cfs.setIgnoreRetrievalErrors(fetchScope.ignoreRetrievalErrors());
0485 
0486     return cfs;
0487 }
0488 
0489 CollectionFetchScope ProtocolHelper::parseCollectionFetchScope(const Protocol::CollectionFetchScope &fetchScope)
0490 {
0491     CollectionFetchScope cfs;
0492     switch (fetchScope.listFilter()) {
0493     case Protocol::CollectionFetchScope::NoFilter:
0494         cfs.setListFilter(CollectionFetchScope::NoFilter);
0495         break;
0496     case Protocol::CollectionFetchScope::Display:
0497         cfs.setListFilter(CollectionFetchScope::Display);
0498         break;
0499     case Protocol::CollectionFetchScope::Sync:
0500         cfs.setListFilter(CollectionFetchScope::Sync);
0501         break;
0502     case Protocol::CollectionFetchScope::Index:
0503         cfs.setListFilter(CollectionFetchScope::Index);
0504         break;
0505     case Protocol::CollectionFetchScope::Enabled:
0506         cfs.setListFilter(CollectionFetchScope::Enabled);
0507         break;
0508     }
0509     cfs.setIncludeStatistics(fetchScope.includeStatistics());
0510     cfs.setResource(fetchScope.resource());
0511     cfs.setContentMimeTypes(fetchScope.contentMimeTypes());
0512     switch (fetchScope.ancestorRetrieval()) {
0513     case Protocol::CollectionFetchScope::None:
0514         cfs.setAncestorRetrieval(CollectionFetchScope::None);
0515         break;
0516     case Protocol::CollectionFetchScope::Parent:
0517         cfs.setAncestorRetrieval(CollectionFetchScope::Parent);
0518         break;
0519     case Protocol::CollectionFetchScope::All:
0520         cfs.setAncestorRetrieval(CollectionFetchScope::All);
0521         break;
0522     }
0523     if (cfs.ancestorRetrieval() != CollectionFetchScope::None) {
0524         cfs.ancestorFetchScope().setFetchIdOnly(fetchScope.ancestorFetchIdOnly());
0525         const auto attrs = fetchScope.ancestorAttributes();
0526         for (const auto &attr : attrs) {
0527             cfs.ancestorFetchScope().fetchAttribute(attr, true);
0528         }
0529     }
0530     const auto attrs = fetchScope.attributes();
0531     for (const auto &attr : attrs) {
0532         cfs.fetchAttribute(attr, true);
0533     }
0534     cfs.setFetchIdOnly(fetchScope.fetchIdOnly());
0535     cfs.setIgnoreRetrievalErrors(fetchScope.ignoreRetrievalErrors());
0536 
0537     return cfs;
0538 }
0539 
0540 Protocol::TagFetchScope ProtocolHelper::tagFetchScopeToProtocol(const TagFetchScope &fetchScope)
0541 {
0542     Protocol::TagFetchScope tfs;
0543     tfs.setFetchIdOnly(fetchScope.fetchIdOnly());
0544     tfs.setAttributes(fetchScope.attributes());
0545     tfs.setFetchRemoteID(fetchScope.fetchRemoteId());
0546     tfs.setFetchAllAttributes(fetchScope.fetchAllAttributes());
0547     return tfs;
0548 }
0549 
0550 TagFetchScope ProtocolHelper::parseTagFetchScope(const Protocol::TagFetchScope &fetchScope)
0551 {
0552     TagFetchScope tfs;
0553     tfs.setFetchIdOnly(fetchScope.fetchIdOnly());
0554     tfs.setFetchRemoteId(fetchScope.fetchRemoteID());
0555     tfs.setFetchAllAttributes(fetchScope.fetchAllAttributes());
0556     const auto attrs = fetchScope.attributes();
0557     for (const auto &attr : attrs) {
0558         tfs.fetchAttribute(attr, true);
0559     }
0560     return tfs;
0561 }
0562 
0563 static Item::Flags convertFlags(const QList<QByteArray> &flags, ProtocolHelperValuePool *valuePool)
0564 {
0565 #if __cplusplus >= 201103L || defined(__GNUC__) || defined(__clang__)
0566     // When the compiler supports thread-safe static initialization (mandated by the C++11 memory model)
0567     // then use it to share the common case of a single-item set only containing the \SEEN flag.
0568     // NOTE: GCC and clang has threadsafe static initialization for some time now, even without C++11.
0569     if (flags.size() == 1 && flags.first() == "\\SEEN") {
0570         static const Item::Flags sharedSeen = Item::Flags() << QByteArray("\\SEEN");
0571         return sharedSeen;
0572     }
0573 #endif
0574 
0575     Item::Flags convertedFlags;
0576     convertedFlags.reserve(flags.size());
0577     for (const QByteArray &flag : flags) {
0578         if (valuePool) {
0579             convertedFlags.insert(valuePool->flagPool.sharedValue(flag));
0580         } else {
0581             convertedFlags.insert(flag);
0582         }
0583     }
0584     return convertedFlags;
0585 }
0586 
0587 Item ProtocolHelper::parseItemFetchResult(const Protocol::FetchItemsResponse &data,
0588                                           const Akonadi::ItemFetchScope *fetchScope,
0589                                           ProtocolHelperValuePool *valuePool)
0590 {
0591     Item item;
0592     item.setId(data.id());
0593     item.setRevision(data.revision());
0594     if (!fetchScope || fetchScope->fetchRemoteIdentification()) {
0595         item.setRemoteId(data.remoteId());
0596         item.setRemoteRevision(data.remoteRevision());
0597     }
0598     item.setGid(data.gid());
0599     item.setStorageCollectionId(data.parentId());
0600 
0601     if (valuePool) {
0602         item.setMimeType(valuePool->mimeTypePool.sharedValue(data.mimeType()));
0603     } else {
0604         item.setMimeType(data.mimeType());
0605     }
0606 
0607     if (!item.isValid()) {
0608         return Item();
0609     }
0610 
0611     item.setFlags(convertFlags(data.flags(), valuePool));
0612 
0613     const auto fetchedTags = data.tags();
0614     if ((!fetchScope || fetchScope->fetchTags()) && !fetchedTags.isEmpty()) {
0615         Tag::List tags;
0616         tags.reserve(fetchedTags.size());
0617         for (const Protocol::FetchTagsResponse &tag : fetchedTags) {
0618             tags.append(parseTagFetchResult(tag));
0619         }
0620         item.setTags(tags);
0621     }
0622 
0623     const auto fetchedRelations = data.relations();
0624     if ((!fetchScope || fetchScope->fetchRelations()) && !fetchedRelations.isEmpty()) {
0625         Relation::List relations;
0626         relations.reserve(fetchedRelations.size());
0627         for (const Protocol::FetchRelationsResponse &rel : fetchedRelations) {
0628             relations.append(parseRelationFetchResult(rel));
0629         }
0630         item.d_ptr->mRelations = relations;
0631     }
0632 
0633     const auto virtualReferences = data.virtualReferences();
0634     if ((!fetchScope || fetchScope->fetchVirtualReferences()) && !virtualReferences.isEmpty()) {
0635         Collection::List virtRefs;
0636         virtRefs.reserve(virtualReferences.size());
0637         for (qint64 colId : virtualReferences) {
0638             virtRefs.append(Collection(colId));
0639         }
0640         item.setVirtualReferences(virtRefs);
0641     }
0642 
0643     const auto cachedParts = data.cachedParts();
0644     if (!cachedParts.isEmpty()) {
0645         QSet<QByteArray> cp;
0646         cp.reserve(cachedParts.size());
0647         for (const QByteArray &ba : cachedParts) {
0648             cp.insert(ba);
0649         }
0650         item.setCachedPayloadParts(cp);
0651     }
0652 
0653     item.setSize(data.size());
0654     item.setModificationTime(data.mTime());
0655     parseAncestorsCached(data.ancestors(), &item, data.parentId(), valuePool);
0656     const auto parts = data.parts();
0657     for (const Protocol::StreamPayloadResponse &part : parts) {
0658         ProtocolHelper::PartNamespace ns;
0659         const QByteArray plainKey = decodePartIdentifier(part.payloadName(), ns);
0660         const auto metaData = part.metaData();
0661         switch (ns) {
0662         case ProtocolHelper::PartPayload:
0663             if (fetchScope && !fetchScope->fullPayload() && !fetchScope->payloadParts().contains(plainKey)) {
0664                 continue;
0665             }
0666             ItemSerializer::deserialize(item, plainKey, part.data(), metaData.version(), static_cast<ItemSerializer::PayloadStorage>(metaData.storageType()));
0667             if (metaData.storageType() == Protocol::PartMetaData::Foreign) {
0668                 item.d_ptr->mPayloadPath = QString::fromUtf8(part.data());
0669             }
0670             break;
0671         case ProtocolHelper::PartAttribute: {
0672             if (fetchScope && !fetchScope->allAttributes() && !fetchScope->attributes().contains(plainKey)) {
0673                 continue;
0674             }
0675             Attribute *attr = AttributeFactory::createAttribute(plainKey);
0676             Q_ASSERT(attr);
0677             if (metaData.storageType() == Protocol::PartMetaData::External) {
0678                 const QString filename = ExternalPartStorage::resolveAbsolutePath(part.data());
0679                 QFile file(filename);
0680                 if (file.open(QFile::ReadOnly)) {
0681                     attr->deserialize(file.readAll());
0682                 } else {
0683                     qCWarning(AKONADICORE_LOG) << "Failed to open attribute file: " << filename;
0684                     delete attr;
0685                     attr = nullptr;
0686                 }
0687             } else {
0688                 attr->deserialize(part.data());
0689             }
0690             if (attr) {
0691                 item.addAttribute(attr);
0692             }
0693             break;
0694         }
0695         case ProtocolHelper::PartGlobal:
0696         default:
0697             qCWarning(AKONADICORE_LOG) << "Unknown item part type:" << part.payloadName();
0698         }
0699     }
0700 
0701     item.d_ptr->resetChangeLog();
0702     return item;
0703 }
0704 
0705 Tag ProtocolHelper::parseTagFetchResult(const Protocol::FetchTagsResponse &data)
0706 {
0707     Tag tag;
0708     tag.setId(data.id());
0709     tag.setGid(data.gid());
0710     tag.setRemoteId(data.remoteId());
0711     tag.setType(data.type());
0712     tag.setParent(data.parentId() > 0 ? Tag(data.parentId()) : Tag());
0713 
0714     parseAttributes(data.attributes(), &tag);
0715     tag.d_ptr->resetChangeLog();
0716     return tag;
0717 }
0718 
0719 Relation ProtocolHelper::parseRelationFetchResult(const Protocol::FetchRelationsResponse &data)
0720 {
0721     Relation relation;
0722     relation.setLeft(Item(data.left()));
0723     relation.setRight(Item(data.right()));
0724     relation.setRemoteId(data.remoteId());
0725     relation.setType(data.type());
0726     return relation;
0727 }
0728 
0729 bool ProtocolHelper::streamPayloadToFile(const QString &fileName, const QByteArray &data, QByteArray &error)
0730 {
0731     const QString filePath = ExternalPartStorage::resolveAbsolutePath(fileName);
0732     // qCDebug(AKONADICORE_LOG) << filePath << fileName;
0733     if (!filePath.startsWith(ExternalPartStorage::akonadiStoragePath())) {
0734         qCWarning(AKONADICORE_LOG) << "Invalid file path" << fileName;
0735         error = "Invalid file path";
0736         return false;
0737     }
0738     QFile file(filePath);
0739     if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
0740         qCWarning(AKONADICORE_LOG) << "Failed to open destination payload file" << file.errorString();
0741         error = "Failed to store payload into file";
0742         return false;
0743     }
0744     if (file.write(data) != data.size()) {
0745         qCWarning(AKONADICORE_LOG) << "Failed to write all payload data to file";
0746         error = "Failed to store payload into file";
0747         return false;
0748     }
0749     // qCDebug(AKONADICORE_LOG) << "Wrote" << data.size() << "bytes to " << file.fileName();
0750 
0751     // Make sure stuff is written to disk
0752     file.close();
0753     return true;
0754 }
0755 
0756 Akonadi::Tristate ProtocolHelper::listPreference(Collection::ListPreference pref)
0757 {
0758     switch (pref) {
0759     case Collection::ListEnabled:
0760         return Tristate::True;
0761     case Collection::ListDisabled:
0762         return Tristate::False;
0763     case Collection::ListDefault:
0764         return Tristate::Undefined;
0765     }
0766 
0767     Q_ASSERT(false);
0768     return Tristate::Undefined;
0769 }