File indexing completed on 2024-11-10 04:40:29
0001 /* 0002 SPDX-FileCopyrightText: 2006-2007 Volker Krause <vkrause@kde.org> 0003 SPDX-FileCopyrightText: 2007 Robert Zwerus <arzie@dds.nl> 0004 SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "itemcreatejob.h" 0010 0011 #include "collection.h" 0012 #include "gidextractor_p.h" 0013 #include "item.h" 0014 #include "item_p.h" 0015 #include "itemserializer_p.h" 0016 #include "job_p.h" 0017 #include "private/protocol_p.h" 0018 #include "protocolhelper_p.h" 0019 0020 #include <QFile> 0021 0022 #include <KLocalizedString> 0023 0024 using namespace Akonadi; 0025 0026 class Akonadi::ItemCreateJobPrivate : public JobPrivate 0027 { 0028 public: 0029 explicit ItemCreateJobPrivate(ItemCreateJob *parent) 0030 : JobPrivate(parent) 0031 { 0032 } 0033 0034 Protocol::PartMetaData preparePart(const QByteArray &part); 0035 0036 QString jobDebuggingString() const override; 0037 Collection mCollection; 0038 Item mItem; 0039 QSet<QByteArray> mParts; 0040 QSet<QByteArray> mForeignParts; 0041 struct PendingPart { 0042 void clear() 0043 { 0044 name.clear(); 0045 data.clear(); 0046 } 0047 0048 QByteArray name; 0049 QByteArray data; 0050 } mPendingPart; 0051 ItemCreateJob::MergeOptions mMergeOptions = ItemCreateJob::NoMerge; 0052 bool mItemReceived = false; 0053 }; 0054 0055 QString Akonadi::ItemCreateJobPrivate::jobDebuggingString() const 0056 { 0057 const QString collectionName = mCollection.name(); 0058 QString str = QStringLiteral("%1 Item %2 from col %3") 0059 .arg(mMergeOptions == ItemCreateJob::NoMerge ? QStringLiteral("Create") : QStringLiteral("Merge")) 0060 .arg(mItem.id()) 0061 .arg(mCollection.id()); 0062 if (!collectionName.isEmpty()) { 0063 str += QStringLiteral(" (%1)").arg(collectionName); 0064 } 0065 return str; 0066 } 0067 0068 Protocol::PartMetaData ItemCreateJobPrivate::preparePart(const QByteArray &partName) 0069 { 0070 ProtocolHelper::PartNamespace ns; // dummy 0071 const QByteArray partLabel = ProtocolHelper::decodePartIdentifier(partName, ns); 0072 if (!mParts.remove(partLabel)) { 0073 // ERROR? 0074 return Protocol::PartMetaData(); 0075 } 0076 0077 int version = 0; 0078 if (mForeignParts.contains(partLabel)) { 0079 mPendingPart = PendingPart{.name = partName, .data = mItem.d_ptr->mPayloadPath.toUtf8()}; 0080 const auto size = QFile(mItem.d_ptr->mPayloadPath).size(); 0081 return Protocol::PartMetaData(partName, size, version, Protocol::PartMetaData::Foreign); 0082 } else { 0083 mPendingPart.clear(); 0084 mPendingPart.name = partName; 0085 ItemSerializer::serialize(mItem, partLabel, mPendingPart.data, version); 0086 return Protocol::PartMetaData(partName, mPendingPart.data.size(), version); 0087 } 0088 } 0089 0090 ItemCreateJob::ItemCreateJob(const Item &item, const Collection &collection, QObject *parent) 0091 : Job(new ItemCreateJobPrivate(this), parent) 0092 { 0093 Q_D(ItemCreateJob); 0094 0095 Q_ASSERT(!item.mimeType().isEmpty()); 0096 d->mItem = item; 0097 d->mParts = d->mItem.loadedPayloadParts(); 0098 d->mCollection = collection; 0099 0100 if (!d->mItem.payloadPath().isEmpty()) { 0101 d->mForeignParts = ItemSerializer::allowedForeignParts(d->mItem); 0102 } 0103 } 0104 0105 ItemCreateJob::~ItemCreateJob() 0106 { 0107 } 0108 0109 void ItemCreateJob::doStart() 0110 { 0111 Q_D(ItemCreateJob); 0112 0113 if (!d->mCollection.isValid()) { 0114 setError(Unknown); 0115 setErrorText(i18n("Invalid parent collection")); 0116 emitResult(); 0117 return; 0118 } 0119 0120 auto cmd = Protocol::CreateItemCommandPtr::create(); 0121 cmd->setMimeType(d->mItem.mimeType()); 0122 cmd->setGid(d->mItem.gid()); 0123 cmd->setRemoteId(d->mItem.remoteId()); 0124 cmd->setRemoteRevision(d->mItem.remoteRevision()); 0125 cmd->setModificationTime(d->mItem.modificationTime()); 0126 0127 Protocol::CreateItemCommand::MergeModes mergeModes = Protocol::CreateItemCommand::None; 0128 if ((d->mMergeOptions & GID) && !d->mItem.gid().isEmpty()) { 0129 mergeModes |= Protocol::CreateItemCommand::GID; 0130 } 0131 if ((d->mMergeOptions & RID) && !d->mItem.remoteId().isEmpty()) { 0132 mergeModes |= Protocol::CreateItemCommand::RemoteID; 0133 } 0134 if ((d->mMergeOptions & Silent)) { 0135 mergeModes |= Protocol::CreateItemCommand::Silent; 0136 } 0137 const bool merge = (mergeModes & Protocol::CreateItemCommand::GID) || (mergeModes & Protocol::CreateItemCommand::RemoteID); 0138 cmd->setMergeModes(mergeModes); 0139 0140 if (d->mItem.d_ptr->mFlagsOverwritten || !merge) { 0141 cmd->setFlags(d->mItem.flags()); 0142 cmd->setFlagsOverwritten(d->mItem.d_ptr->mFlagsOverwritten); 0143 } else { 0144 const auto addedFlags = ItemChangeLog::instance()->addedFlags(d->mItem.d_ptr); 0145 const auto deletedFlags = ItemChangeLog::instance()->deletedFlags(d->mItem.d_ptr); 0146 cmd->setAddedFlags(addedFlags); 0147 cmd->setRemovedFlags(deletedFlags); 0148 } 0149 0150 if (d->mItem.d_ptr->mTagsOverwritten || !merge) { 0151 const auto tags = d->mItem.tags(); 0152 if (!tags.isEmpty()) { 0153 cmd->setTags(ProtocolHelper::entitySetToScope(tags)); 0154 } 0155 } else { 0156 const auto addedTags = ItemChangeLog::instance()->addedTags(d->mItem.d_ptr); 0157 if (!addedTags.isEmpty()) { 0158 cmd->setAddedTags(ProtocolHelper::entitySetToScope(addedTags)); 0159 } 0160 const auto deletedTags = ItemChangeLog::instance()->deletedTags(d->mItem.d_ptr); 0161 if (!deletedTags.isEmpty()) { 0162 cmd->setRemovedTags(ProtocolHelper::entitySetToScope(deletedTags)); 0163 } 0164 } 0165 0166 cmd->setCollection(ProtocolHelper::entityToScope(d->mCollection)); 0167 cmd->setItemSize(d->mItem.size()); 0168 0169 cmd->setAttributes(ProtocolHelper::attributesToProtocol(d->mItem)); 0170 QSet<QByteArray> parts; 0171 parts.reserve(d->mParts.size()); 0172 for (const QByteArray &part : std::as_const(d->mParts)) { 0173 parts.insert(ProtocolHelper::encodePartIdentifier(ProtocolHelper::PartPayload, part)); 0174 } 0175 cmd->setParts(parts); 0176 0177 d->sendCommand(cmd); 0178 } 0179 0180 bool ItemCreateJob::doHandleResponse(qint64 tag, const Protocol::CommandPtr &response) 0181 { 0182 Q_D(ItemCreateJob); 0183 0184 if (!response->isResponse() && response->type() == Protocol::Command::StreamPayload) { 0185 const auto &streamCmd = Protocol::cmdCast<Protocol::StreamPayloadCommand>(response); 0186 auto streamResp = Protocol::StreamPayloadResponsePtr::create(); 0187 streamResp->setPayloadName(streamCmd.payloadName()); 0188 if (streamCmd.request() == Protocol::StreamPayloadCommand::MetaData) { 0189 streamResp->setMetaData(d->preparePart(streamCmd.payloadName())); 0190 } else if (streamCmd.request() == Protocol::StreamPayloadCommand::Data) { 0191 if (streamCmd.payloadName() != d->mPendingPart.name) { 0192 streamResp->setError(1, QStringLiteral("Unexpected payload name")); 0193 } else if (streamCmd.destination().isEmpty()) { 0194 streamResp->setData(d->mPendingPart.data); 0195 } else { 0196 QByteArray error; 0197 if (!ProtocolHelper::streamPayloadToFile(streamCmd.destination(), d->mPendingPart.data, error)) { 0198 streamResp->setError(1, QStringLiteral("Failed to stream payload to file: %1").arg(QString::fromUtf8(error))); 0199 } 0200 } 0201 } else { 0202 streamResp->setError(1, QStringLiteral("Unknown stream payload request")); 0203 } 0204 d->sendCommand(tag, streamResp); 0205 return false; 0206 } 0207 0208 if (response->isResponse() && response->type() == Protocol::Command::FetchItems) { 0209 const auto &fetchResp = Protocol::cmdCast<Protocol::FetchItemsResponse>(response); 0210 Item item = ProtocolHelper::parseItemFetchResult(fetchResp); 0211 if (!item.isValid()) { 0212 // Error, maybe? 0213 return false; 0214 } 0215 d->mItem = item; 0216 return false; 0217 } 0218 0219 if (response->isResponse() && response->type() == Protocol::Command::CreateItem) { 0220 return true; 0221 } 0222 0223 return Job::doHandleResponse(tag, response); 0224 } 0225 0226 void ItemCreateJob::setMerge(ItemCreateJob::MergeOptions options) 0227 { 0228 Q_D(ItemCreateJob); 0229 0230 d->mMergeOptions = options; 0231 } 0232 0233 Item ItemCreateJob::item() const 0234 { 0235 Q_D(const ItemCreateJob); 0236 0237 // Parent collection is available only with non-silent merge/create 0238 if (d->mItem.parentCollection().isValid()) { 0239 return d->mItem; 0240 } 0241 0242 Item item(d->mItem); 0243 item.setRevision(0); 0244 item.setParentCollection(d->mCollection); 0245 item.setStorageCollectionId(d->mCollection.id()); 0246 0247 return item; 0248 } 0249 0250 #include "moc_itemcreatejob.cpp"