File indexing completed on 2024-11-10 04:40:41
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"