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

0001 /*
0002     SPDX-FileCopyrightText: 2007 Till Adam <adam@kde.org>
0003     SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "config_p.h"
0009 #include "item.h"
0010 #include "itemserializer_p.h"
0011 #include "itemserializerplugin.h"
0012 #include "protocolhelper_p.h"
0013 #include "typepluginloader_p.h"
0014 
0015 #include "private/compressionstream_p.h"
0016 #include "private/externalpartstorage_p.h"
0017 
0018 #include "akonadicore_debug.h"
0019 
0020 // Qt
0021 #include <QBuffer>
0022 #include <QFile>
0023 #include <QIODevice>
0024 #include <QString>
0025 
0026 #include <string>
0027 
0028 Q_DECLARE_METATYPE(std::string)
0029 
0030 namespace Akonadi
0031 {
0032 DefaultItemSerializerPlugin::DefaultItemSerializerPlugin() = default;
0033 
0034 bool DefaultItemSerializerPlugin::deserialize(Item &item, const QByteArray &label, QIODevice &data, int /*version*/)
0035 {
0036     if (label != Item::FullPayload) {
0037         return false;
0038     }
0039 
0040     item.setPayload(data.readAll());
0041     return true;
0042 }
0043 
0044 void DefaultItemSerializerPlugin::serialize(const Item &item, const QByteArray &label, QIODevice &data, int &version)
0045 {
0046     Q_UNUSED(version)
0047     Q_ASSERT(label == Item::FullPayload);
0048     Q_UNUSED(label)
0049     data.write(item.payload<QByteArray>());
0050 }
0051 
0052 bool StdStringItemSerializerPlugin::deserialize(Item &item, const QByteArray &label, QIODevice &data, int /*version*/)
0053 {
0054     if (label != Item::FullPayload) {
0055         return false;
0056     }
0057     std::string str;
0058     {
0059         const QByteArray ba = data.readAll();
0060         str.assign(ba.data(), ba.size());
0061     }
0062     item.setPayload(str);
0063     return true;
0064 }
0065 
0066 void StdStringItemSerializerPlugin::serialize(const Item &item, const QByteArray &label, QIODevice &data, int &version)
0067 {
0068     Q_UNUSED(version)
0069     Q_ASSERT(label == Item::FullPayload);
0070     Q_UNUSED(label)
0071     const auto str = item.payload<std::string>();
0072     data.write(QByteArray::fromRawData(str.data(), str.size()));
0073 }
0074 
0075 /*static*/
0076 void ItemSerializer::deserialize(Item &item, const QByteArray &label, const QByteArray &data, int version, PayloadStorage storage)
0077 {
0078     if (storage == Internal) {
0079         QBuffer buffer;
0080         buffer.setData(data);
0081         buffer.open(QIODevice::ReadOnly);
0082         deserialize(item, label, buffer, version);
0083         buffer.close();
0084     } else {
0085         QFile file;
0086         if (storage == External) {
0087             file.setFileName(ExternalPartStorage::resolveAbsolutePath(data));
0088         } else if (storage == Foreign) {
0089             file.setFileName(QString::fromUtf8(data));
0090         }
0091 
0092         if (file.open(QIODevice::ReadOnly)) {
0093             deserialize(item, label, file, version);
0094             file.close();
0095         } else {
0096             qCWarning(AKONADICORE_LOG) << "Failed to open" << ((storage == External) ? "external" : "foreign") << "payload:" << file.fileName()
0097                                        << file.errorString();
0098         }
0099     }
0100 }
0101 
0102 /*static*/
0103 void ItemSerializer::deserialize(Item &item, const QByteArray &label, QIODevice &data, int version)
0104 {
0105     auto plugin = TypePluginLoader::defaultPluginForMimeType(item.mimeType());
0106 
0107     const auto handleError = [&](QIODevice &device, bool compressed) {
0108         device.seek(0);
0109         QByteArray data;
0110         if (compressed) {
0111             CompressionStream decompressor(&device);
0112             decompressor.open(QIODevice::ReadOnly);
0113             data = decompressor.readAll();
0114         } else {
0115             data = device.readAll();
0116         }
0117 
0118         qCWarning(AKONADICORE_LOG) << "Unable to deserialize payload part:" << label << "in item" << item.id() << "collection" << item.parentCollection().id();
0119         qCWarning(AKONADICORE_LOG) << (compressed ? "Decompressed" : "") << "payload data was: " << data;
0120     };
0121 
0122     if (CompressionStream::isCompressed(&data)) {
0123         CompressionStream decompressor(&data);
0124         decompressor.open(QIODevice::ReadOnly);
0125         if (!plugin->deserialize(item, label, decompressor, version)) {
0126             handleError(data, true);
0127         }
0128         if (decompressor.error()) {
0129             qCWarning(AKONADICORE_LOG) << "Deserialization failed due to decompression error:" << QString::fromStdString(decompressor.error().message());
0130         }
0131     } else {
0132         if (!plugin->deserialize(item, label, data, version)) {
0133             handleError(data, false);
0134         }
0135     }
0136 }
0137 
0138 /*static*/
0139 void ItemSerializer::serialize(const Item &item, const QByteArray &label, QByteArray &data, int &version)
0140 {
0141     QBuffer buffer;
0142     buffer.setBuffer(&data);
0143     buffer.open(QIODevice::WriteOnly);
0144     buffer.seek(0);
0145     serialize(item, label, buffer, version);
0146     buffer.close();
0147 }
0148 
0149 /*static*/
0150 void ItemSerializer::serialize(const Item &item, const QByteArray &label, QIODevice &data, int &version)
0151 {
0152     if (!item.hasPayload()) {
0153         return;
0154     }
0155     ItemSerializerPlugin *plugin = TypePluginLoader::pluginForMimeTypeAndClass(item.mimeType(), item.availablePayloadMetaTypeIds());
0156 
0157     if (Config::get().payloadCompression.enabled) {
0158         CompressionStream compressor(&data);
0159         compressor.open(QIODevice::WriteOnly);
0160         plugin->serialize(item, label, compressor, version);
0161     } else {
0162         plugin->serialize(item, label, data, version);
0163     }
0164 }
0165 
0166 void ItemSerializer::apply(Item &item, const Item &other)
0167 {
0168     if (!other.hasPayload()) {
0169         return;
0170     }
0171 
0172     ItemSerializerPlugin *plugin = TypePluginLoader::pluginForMimeTypeAndClass(item.mimeType(), item.availablePayloadMetaTypeIds());
0173     plugin->apply(item, other);
0174 }
0175 
0176 QSet<QByteArray> ItemSerializer::parts(const Item &item)
0177 {
0178     if (!item.hasPayload()) {
0179         return QSet<QByteArray>();
0180     }
0181     return TypePluginLoader::pluginForMimeTypeAndClass(item.mimeType(), item.availablePayloadMetaTypeIds())->parts(item);
0182 }
0183 
0184 QSet<QByteArray> ItemSerializer::availableParts(const Item &item)
0185 {
0186     if (!item.hasPayload()) {
0187         return QSet<QByteArray>();
0188     }
0189     ItemSerializerPlugin *plugin = TypePluginLoader::pluginForMimeTypeAndClass(item.mimeType(), item.availablePayloadMetaTypeIds());
0190     return plugin->availableParts(item);
0191 }
0192 
0193 QSet<QByteArray> ItemSerializer::allowedForeignParts(const Item &item)
0194 {
0195     if (!item.hasPayload()) {
0196         return QSet<QByteArray>();
0197     }
0198 
0199     ItemSerializerPlugin *plugin = TypePluginLoader::pluginForMimeTypeAndClass(item.mimeType(), item.availablePayloadMetaTypeIds());
0200     return plugin->allowedForeignParts(item);
0201 }
0202 
0203 Item ItemSerializer::convert(const Item &item, int mtid)
0204 {
0205     qCDebug(AKONADICORE_LOG) << "asked to convert a" << item.mimeType() << "item to format" << (mtid ? QMetaType(mtid).name() : "<legacy>");
0206     if (!item.hasPayload()) {
0207         qCDebug(AKONADICORE_LOG) << "  -> but item has no payload!";
0208         return Item();
0209     }
0210 
0211     if (ItemSerializerPlugin *const plugin = TypePluginLoader::pluginForMimeTypeAndClass(item.mimeType(), QList<int>(1, mtid), TypePluginLoader::NoDefault)) {
0212         qCDebug(AKONADICORE_LOG) << "  -> found a plugin that feels responsible, trying serialising the payload";
0213         QBuffer buffer;
0214         buffer.open(QIODevice::ReadWrite);
0215         int version = 0;
0216         serialize(item, Item::FullPayload, buffer, version);
0217         buffer.seek(0);
0218         qCDebug(AKONADICORE_LOG) << "    -> serialized payload into" << buffer.size() << "bytes\n"
0219                                  << "  -> going to deserialize";
0220         Item newItem;
0221         if (plugin->deserialize(newItem, Item::FullPayload, buffer, version)) {
0222             qCDebug(AKONADICORE_LOG) << "    -> conversion successful";
0223             return newItem;
0224         } else {
0225             qCDebug(AKONADICORE_LOG) << "    -> conversion FAILED";
0226         }
0227     } else {
0228         //     qCDebug(AKONADICORE_LOG) << "  -> found NO plugin that feels responsible";
0229     }
0230     return Item();
0231 }
0232 
0233 void ItemSerializer::overridePluginLookup(QObject *p)
0234 {
0235     TypePluginLoader::overridePluginLookup(p);
0236 }
0237 
0238 } // namespace Akonadi
0239 
0240 #include "moc_itemserializer_p.cpp"