File indexing completed on 2024-11-10 04:40:40
0001 /* 0002 SPDX-FileCopyrightText: 2006 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "item.h" 0008 #include "akonadicore_debug.h" 0009 #include "item_p.h" 0010 #include "itemserializer_p.h" 0011 #include "private/protocol_p.h" 0012 0013 #include <QUrl> 0014 #include <QUrlQuery> 0015 0016 #include <QReadWriteLock> 0017 #include <QScopedValueRollback> 0018 #include <QStringList> 0019 0020 #include <algorithm> 0021 #include <map> 0022 #include <utility> 0023 0024 using namespace Akonadi; 0025 0026 Q_GLOBAL_STATIC(Akonadi::Collection, s_defaultParentCollection) // NOLINT(readability-redundant-member-init) 0027 0028 size_t Akonadi::qHash(const Akonadi::Item &item, size_t seed) noexcept 0029 { 0030 return ::qHash(item.id(), seed); 0031 } 0032 0033 // Change to something != RFC822 as soon as the server supports it 0034 const char Item::FullPayload[] = "RFC822"; 0035 0036 Item::Item() 0037 : d_ptr(new ItemPrivate) 0038 { 0039 } 0040 0041 Item::Item(Id id) 0042 : d_ptr(new ItemPrivate(id)) 0043 { 0044 } 0045 0046 Item::Item(const QString &mimeType) 0047 : d_ptr(new ItemPrivate) 0048 { 0049 d_ptr->mMimeType = mimeType; 0050 } 0051 0052 Item::Item(const Item &other) = default; 0053 0054 Item::Item(Item &&other) noexcept = default; 0055 0056 Item::~Item() = default; 0057 0058 void Item::setId(Item::Id identifier) 0059 { 0060 d_ptr->mId = identifier; 0061 } 0062 0063 Item::Id Item::id() const 0064 { 0065 return d_ptr->mId; 0066 } 0067 0068 void Item::setRemoteId(const QString &id) 0069 { 0070 d_ptr->mRemoteId = id; 0071 } 0072 0073 QString Item::remoteId() const 0074 { 0075 return d_ptr->mRemoteId; 0076 } 0077 0078 void Item::setRemoteRevision(const QString &revision) 0079 { 0080 d_ptr->mRemoteRevision = revision; 0081 } 0082 0083 QString Item::remoteRevision() const 0084 { 0085 return d_ptr->mRemoteRevision; 0086 } 0087 0088 bool Item::isValid() const 0089 { 0090 return (d_ptr->mId >= 0); 0091 } 0092 0093 bool Item::operator==(const Item &other) const 0094 { 0095 // Invalid collections are the same, no matter what their internal ID is 0096 return (!isValid() && !other.isValid()) || (d_ptr->mId == other.d_ptr->mId); 0097 } 0098 0099 bool Akonadi::Item::operator!=(const Item &other) const 0100 { 0101 return (isValid() || other.isValid()) && (d_ptr->mId != other.d_ptr->mId); 0102 } 0103 0104 Item &Item ::operator=(const Item &other) 0105 { 0106 if (this != &other) { 0107 d_ptr = other.d_ptr; 0108 } 0109 0110 return *this; 0111 } 0112 0113 bool Akonadi::Item::operator<(const Item &other) const 0114 { 0115 return d_ptr->mId < other.d_ptr->mId; 0116 } 0117 0118 void Item::addAttribute(Attribute *attr) 0119 { 0120 ItemChangeLog::instance()->attributeStorage(d_ptr).addAttribute(attr); 0121 } 0122 0123 void Item::removeAttribute(const QByteArray &type) 0124 { 0125 ItemChangeLog::instance()->attributeStorage(d_ptr).removeAttribute(type); 0126 } 0127 0128 bool Item::hasAttribute(const QByteArray &type) const 0129 { 0130 return ItemChangeLog::instance()->attributeStorage(d_ptr).hasAttribute(type); 0131 } 0132 0133 Attribute::List Item::attributes() const 0134 { 0135 return ItemChangeLog::instance()->attributeStorage(d_ptr).attributes(); 0136 } 0137 0138 void Akonadi::Item::clearAttributes() 0139 { 0140 ItemChangeLog::instance()->attributeStorage(d_ptr).clearAttributes(); 0141 } 0142 0143 Attribute *Item::attribute(const QByteArray &type) 0144 { 0145 return ItemChangeLog::instance()->attributeStorage(d_ptr).attribute(type); 0146 } 0147 0148 const Attribute *Item::attribute(const QByteArray &type) const 0149 { 0150 return ItemChangeLog::instance()->attributeStorage(d_ptr).attribute(type); 0151 } 0152 0153 Collection &Item::parentCollection() 0154 { 0155 if (!d_ptr->mParent) { 0156 d_ptr->mParent.reset(new Collection()); 0157 } 0158 return *(d_ptr->mParent); 0159 } 0160 0161 Collection Item::parentCollection() const 0162 { 0163 if (!d_ptr->mParent) { 0164 return *(s_defaultParentCollection); 0165 } else { 0166 return *(d_ptr->mParent); 0167 } 0168 } 0169 0170 void Item::setParentCollection(const Collection &parent) 0171 { 0172 d_ptr->mParent.reset(new Collection(parent)); 0173 } 0174 0175 Item::Flags Item::flags() const 0176 { 0177 return d_ptr->mFlags; 0178 } 0179 0180 void Item::setFlag(const QByteArray &name) 0181 { 0182 d_ptr->mFlags.insert(name); 0183 if (!d_ptr->mFlagsOverwritten) { 0184 Item::Flags &deletedFlags = ItemChangeLog::instance()->deletedFlags(d_ptr); 0185 auto iter = deletedFlags.find(name); 0186 if (iter != deletedFlags.end()) { 0187 deletedFlags.erase(iter); 0188 } else { 0189 ItemChangeLog::instance()->addedFlags(d_ptr).insert(name); 0190 } 0191 } 0192 } 0193 0194 void Item::clearFlag(const QByteArray &name) 0195 { 0196 d_ptr->mFlags.remove(name); 0197 if (!d_ptr->mFlagsOverwritten) { 0198 Item::Flags &addedFlags = ItemChangeLog::instance()->addedFlags(d_ptr); 0199 auto iter = addedFlags.find(name); 0200 if (iter != addedFlags.end()) { 0201 addedFlags.erase(iter); 0202 } else { 0203 ItemChangeLog::instance()->deletedFlags(d_ptr).insert(name); 0204 } 0205 } 0206 } 0207 0208 void Item::setFlags(const Flags &flags) 0209 { 0210 d_ptr->mFlags = flags; 0211 d_ptr->mFlagsOverwritten = true; 0212 } 0213 0214 void Item::clearFlags() 0215 { 0216 d_ptr->mFlags.clear(); 0217 d_ptr->mFlagsOverwritten = true; 0218 } 0219 0220 QDateTime Item::modificationTime() const 0221 { 0222 return d_ptr->mModificationTime; 0223 } 0224 0225 void Item::setModificationTime(const QDateTime &datetime) 0226 { 0227 d_ptr->mModificationTime = datetime; 0228 } 0229 0230 bool Item::hasFlag(const QByteArray &name) const 0231 { 0232 return d_ptr->mFlags.contains(name); 0233 } 0234 0235 void Item::setTags(const Tag::List &list) 0236 { 0237 d_ptr->mTags = list; 0238 d_ptr->mTagsOverwritten = true; 0239 } 0240 0241 void Item::setTag(const Tag &tag) 0242 { 0243 d_ptr->mTags << tag; 0244 if (!d_ptr->mTagsOverwritten) { 0245 Tag::List &deletedTags = ItemChangeLog::instance()->deletedTags(d_ptr); 0246 if (deletedTags.contains(tag)) { 0247 deletedTags.removeOne(tag); 0248 } else { 0249 ItemChangeLog::instance()->addedTags(d_ptr).push_back(tag); 0250 } 0251 } 0252 } 0253 0254 void Item::clearTags() 0255 { 0256 d_ptr->mTags.clear(); 0257 d_ptr->mTagsOverwritten = true; 0258 } 0259 0260 void Item::clearTag(const Tag &tag) 0261 { 0262 d_ptr->mTags.removeOne(tag); 0263 if (!d_ptr->mTagsOverwritten) { 0264 Tag::List &addedTags = ItemChangeLog::instance()->addedTags(d_ptr); 0265 if (addedTags.contains(tag)) { 0266 addedTags.removeOne(tag); 0267 } else { 0268 ItemChangeLog::instance()->deletedTags(d_ptr).push_back(tag); 0269 } 0270 } 0271 } 0272 0273 bool Item::hasTag(const Tag &tag) const 0274 { 0275 return d_ptr->mTags.contains(tag); 0276 } 0277 0278 Tag::List Item::tags() const 0279 { 0280 return d_ptr->mTags; 0281 } 0282 0283 Relation::List Item::relations() const 0284 { 0285 return d_ptr->mRelations; 0286 } 0287 0288 QSet<QByteArray> Item::loadedPayloadParts() const 0289 { 0290 return ItemSerializer::parts(*this); 0291 } 0292 0293 QByteArray Item::payloadData() const 0294 { 0295 int version = 0; 0296 QByteArray data; 0297 ItemSerializer::serialize(*this, FullPayload, data, version); 0298 return data; 0299 } 0300 0301 void Item::setPayloadFromData(const QByteArray &data) 0302 { 0303 ItemSerializer::deserialize(*this, FullPayload, data, 0, ItemSerializer::Internal); 0304 } 0305 0306 void Item::clearPayload() 0307 { 0308 d_ptr->mClearPayload = true; 0309 } 0310 0311 int Item::revision() const 0312 { 0313 return d_ptr->mRevision; 0314 } 0315 0316 void Item::setRevision(int rev) 0317 { 0318 d_ptr->mRevision = rev; 0319 } 0320 0321 Collection::Id Item::storageCollectionId() const 0322 { 0323 return d_ptr->mCollectionId; 0324 } 0325 0326 void Item::setStorageCollectionId(Collection::Id collectionId) 0327 { 0328 d_ptr->mCollectionId = collectionId; 0329 } 0330 0331 QString Item::mimeType() const 0332 { 0333 return d_ptr->mMimeType; 0334 } 0335 0336 void Item::setSize(qint64 size) 0337 { 0338 d_ptr->mSize = size; 0339 d_ptr->mSizeChanged = true; 0340 } 0341 0342 qint64 Item::size() const 0343 { 0344 return d_ptr->mSize; 0345 } 0346 0347 void Item::setMimeType(const QString &mimeType) 0348 { 0349 d_ptr->mMimeType = mimeType; 0350 } 0351 0352 void Item::setGid(const QString &id) 0353 { 0354 d_ptr->mGid = id; 0355 } 0356 0357 QString Item::gid() const 0358 { 0359 return d_ptr->mGid; 0360 } 0361 0362 void Item::setVirtualReferences(const Collection::List &collections) 0363 { 0364 d_ptr->mVirtualReferences = collections; 0365 } 0366 0367 Collection::List Item::virtualReferences() const 0368 { 0369 return d_ptr->mVirtualReferences; 0370 } 0371 0372 bool Item::hasPayload() const 0373 { 0374 return d_ptr->hasMetaTypeId(-1); 0375 } 0376 0377 QUrl Item::url(UrlType type) const 0378 { 0379 QUrlQuery query; 0380 query.addQueryItem(QStringLiteral("item"), QString::number(id())); 0381 if (type == UrlWithMimeType) { 0382 query.addQueryItem(QStringLiteral("type"), mimeType()); 0383 } 0384 0385 QUrl url; 0386 url.setScheme(QStringLiteral("akonadi")); 0387 url.setQuery(query); 0388 return url; 0389 } 0390 0391 Item Item::fromUrl(const QUrl &url) 0392 { 0393 if (url.scheme() != QLatin1StringView("akonadi")) { 0394 return Item(); 0395 } 0396 0397 const QString itemStr = QUrlQuery(url).queryItemValue(QStringLiteral("item")); 0398 bool ok = false; 0399 Item::Id itemId = itemStr.toLongLong(&ok); 0400 if (!ok) { 0401 return Item(); 0402 } 0403 0404 return Item(itemId); 0405 } 0406 0407 Internal::PayloadBase *Item::payloadBaseV2(int spid, int mtid) const 0408 { 0409 return d_ptr->payloadBaseImpl(spid, mtid); 0410 } 0411 0412 bool Item::ensureMetaTypeId(int mtid) const 0413 { 0414 // 0. Nothing there - nothing to convert from, either 0415 if (d_ptr->mPayloads.empty()) { 0416 return false; 0417 } 0418 0419 // 1. Look whether we already have one: 0420 if (d_ptr->hasMetaTypeId(mtid)) { 0421 return true; 0422 } 0423 0424 // recursion detection (shouldn't trigger, but does if the 0425 // serialiser plugins are acting funky): 0426 if (d_ptr->mConversionInProgress) { 0427 return false; 0428 } 0429 0430 // 2. Try to create one by conversion from a different representation: 0431 try { 0432 const QScopedValueRollback guard(d_ptr->mConversionInProgress, true); 0433 Item converted = ItemSerializer::convert(*this, mtid); 0434 return d_ptr->movePayloadFrom(converted.d_ptr, mtid); 0435 } catch (const std::exception &e) { 0436 qCWarning(AKONADICORE_LOG) << "Item payload conversion threw:" << e.what(); 0437 return false; 0438 } catch (...) { 0439 qCCritical(AKONADICORE_LOG, "conversion threw something not derived from std::exception: fix the program!"); 0440 return false; 0441 } 0442 } 0443 0444 static QString format_type(int spid, int mtid) 0445 { 0446 return QStringLiteral("sp(%1)<%2>").arg(spid).arg(QLatin1StringView(QMetaType(mtid).name())); 0447 } 0448 0449 static QString format_types(const PayloadContainer &container) 0450 { 0451 QStringList result; 0452 result.reserve(container.size()); 0453 for (auto it = container.begin(), end = container.end(); it != end; ++it) { 0454 result.push_back(format_type(it->sharedPointerId, it->metaTypeId)); 0455 } 0456 return result.join(QLatin1StringView(", ")); 0457 } 0458 0459 static QString format_reason(bool valid, Item::Id id) 0460 { 0461 if (valid) { 0462 return QStringLiteral("itemId: %1").arg(id); 0463 } else { 0464 return QStringLiteral("Item is not valid"); 0465 } 0466 } 0467 0468 void Item::throwPayloadException(int spid, int mtid) const 0469 { 0470 const auto reason = format_reason(isValid(), id()); 0471 0472 if (d_ptr->mPayloads.empty()) { 0473 qCDebug(AKONADICORE_LOG) << "Throwing PayloadException for Item" << id() << ": No payload set"; 0474 throw PayloadException(QStringLiteral("No Item payload set (%1)").arg(reason)); 0475 } else { 0476 const auto requestedType = format_type(spid, mtid); 0477 const auto presentType = format_types(d_ptr->mPayloads); 0478 qCDebug(AKONADICORE_LOG) << "Throwing PayloadException for Item" << id() << ": Wrong payload type (requested:" << requestedType 0479 << "; present: " << presentType << "), item mime type is" << mimeType(); 0480 throw PayloadException(QStringLiteral("Wrong Item payload type (requested: %1; present: %2, %3)").arg(requestedType, presentType, reason)); 0481 } 0482 } 0483 0484 void Item::setPayloadBaseV2(int spid, int mtid, std::unique_ptr<Internal::PayloadBase> &p) 0485 { 0486 d_ptr->setPayloadBaseImpl(spid, mtid, p, false); 0487 } 0488 0489 void Item::addPayloadBaseVariant(int spid, int mtid, std::unique_ptr<Internal::PayloadBase> &p) const 0490 { 0491 d_ptr->setPayloadBaseImpl(spid, mtid, p, true); 0492 } 0493 0494 QSet<QByteArray> Item::cachedPayloadParts() const 0495 { 0496 return d_ptr->mCachedPayloadParts; 0497 } 0498 0499 void Item::setCachedPayloadParts(const QSet<QByteArray> &cachedParts) 0500 { 0501 d_ptr->mCachedPayloadParts = cachedParts; 0502 } 0503 0504 QSet<QByteArray> Item::availablePayloadParts() const 0505 { 0506 return ItemSerializer::availableParts(*this); 0507 } 0508 0509 QList<int> Item::availablePayloadMetaTypeIds() const 0510 { 0511 QList<int> result; 0512 result.reserve(d_ptr->mPayloads.size()); 0513 // Stable Insertion Sort - N is typically _very_ low (1 or 2). 0514 for (auto it = d_ptr->mPayloads.begin(), end = d_ptr->mPayloads.end(); it != end; ++it) { 0515 result.insert(std::upper_bound(result.begin(), result.end(), it->metaTypeId), it->metaTypeId); 0516 } 0517 return result; 0518 } 0519 0520 void Item::setPayloadPath(const QString &filePath) 0521 { 0522 // Load payload from the external file, so that it's accessible via 0523 // Item::payload(). It internally calls setPayload(), which will clear 0524 // mPayloadPath, so we call it afterwards 0525 ItemSerializer::deserialize(*this, "RFC822", filePath.toUtf8(), 0, ItemSerializer::Foreign); 0526 d_ptr->mPayloadPath = filePath; 0527 } 0528 0529 QString Item::payloadPath() const 0530 { 0531 return d_ptr->mPayloadPath; 0532 } 0533 0534 void Item::apply(const Item &other) 0535 { 0536 if (mimeType() != other.mimeType() || id() != other.id()) { 0537 qCDebug(AKONADICORE_LOG) << "mimeType() = " << mimeType() << "; other.mimeType() = " << other.mimeType(); 0538 qCDebug(AKONADICORE_LOG) << "id() = " << id() << "; other.id() = " << other.id(); 0539 Q_ASSERT_X(false, "Item::apply", "mimetype or id mismatch"); 0540 } 0541 0542 setRemoteId(other.remoteId()); 0543 setRevision(other.revision()); 0544 setRemoteRevision(other.remoteRevision()); 0545 setFlags(other.flags()); 0546 setTags(other.tags()); 0547 setModificationTime(other.modificationTime()); 0548 setSize(other.size()); 0549 setParentCollection(other.parentCollection()); 0550 setStorageCollectionId(other.storageCollectionId()); 0551 0552 ItemChangeLog *changelog = ItemChangeLog::instance(); 0553 changelog->attributeStorage(d_ptr) = changelog->attributeStorage(other.d_ptr); 0554 0555 ItemSerializer::apply(*this, other); 0556 d_ptr->resetChangeLog(); 0557 0558 // Must happen after payload update 0559 d_ptr->mPayloadPath = other.payloadPath(); 0560 }