7#include "itemmodifyjob.h" 
    8#include "akonadicore_debug.h" 
    9#include "itemmodifyjob_p.h" 
   11#include "changemediator_p.h" 
   12#include "collection.h" 
   13#include "conflicthandler_p.h" 
   15#include "itemserializer_p.h" 
   18#include "gidextractor_p.h" 
   19#include "protocolhelper_p.h" 
   29ItemModifyJobPrivate::ItemModifyJobPrivate(
ItemModifyJob *parent)
 
   34void ItemModifyJobPrivate::setClean()
 
   36    mOperations.insert(Dirty);
 
   39Protocol::PartMetaData ItemModifyJobPrivate::preparePart(
const QByteArray &partName)
 
   41    ProtocolHelper::PartNamespace ns; 
 
   42    const QByteArray partLabel = ProtocolHelper::decodePartIdentifier(partName, ns);
 
   43    if (!mParts.contains(partLabel)) {
 
   45        return Protocol::PartMetaData();
 
   50    const auto item = mItems.first();
 
   51    if (mForeignParts.contains(partLabel)) {
 
   52        mPendingData = item.d_ptr->mPayloadPath.toUtf8();
 
   53        const auto size = 
QFile(item.d_ptr->mPayloadPath).
size();
 
   54        return Protocol::PartMetaData(partName, size, version, Protocol::PartMetaData::Foreign);
 
   56        ItemSerializer::serialize(mItems.first(), partLabel, mPendingData, version);
 
   57        return Protocol::PartMetaData(partName, mPendingData.size(), version);
 
   61void ItemModifyJobPrivate::conflictResolved()
 
   65    q->setError(KJob::NoError);
 
   70void ItemModifyJobPrivate::conflictResolveError(
const QString &message)
 
   74    q->setErrorText(q->errorText() + message);
 
   78void ItemModifyJobPrivate::doUpdateItemRevision(
Akonadi::Item::Id itemId, 
int oldRevision, 
int newRevision)
 
   80    auto it = std::find_if(mItems.begin(), mItems.end(), [&itemId](
const Item &item) -> 
bool {
 
   81        return item.id() == itemId;
 
   83    if (it != mItems.end() && (*it).revision() == oldRevision) {
 
   84        (*it).setRevision(newRevision);
 
   88QString ItemModifyJobPrivate::jobDebuggingString()
 const 
   91        mRemainingItems = std::span(mItems);
 
   92        return Protocol::debugString(fullCommand());
 
   98void ItemModifyJobPrivate::setSilent(
bool silent)
 
  103bool ItemModifyJobPrivate::nextBatch()
 
  107    if (mRemainingItems.empty()) {
 
  111    Protocol::ModifyItemsCommandPtr command;
 
  113        command = fullCommand();
 
  121    if (command->modifiedParts() == Protocol::ModifyItemsCommand::None) {
 
  131    : 
Job(new ItemModifyJobPrivate(this), 
parent)
 
  135    d->mItems.append(
item);
 
  136    d->mParts = 
item.loadedPayloadParts();
 
  138    d->mOperations.insert(ItemModifyJobPrivate::RemoteId);
 
  139    d->mOperations.insert(ItemModifyJobPrivate::RemoteRevision);
 
  141    if (!
item.payloadPath().isEmpty()) {
 
  142        d->mForeignParts = ItemSerializer::allowedForeignParts(
item);
 
 
  147    : 
Job(new ItemModifyJobPrivate(this), 
parent)
 
  149    Q_ASSERT(!
items.isEmpty());
 
  154    if (d->mItems.size() == 1) {
 
  155        d->mParts = 
items.first().loadedPayloadParts();
 
  156        d->mOperations.insert(ItemModifyJobPrivate::RemoteId);
 
  157        d->mOperations.insert(ItemModifyJobPrivate::RemoteRevision);
 
  159        d->mIgnorePayload = 
true;
 
  160        d->mRevCheck = 
false;
 
 
  166Protocol::ModifyItemsCommandPtr ItemModifyJobPrivate::fullCommand()
 const 
  168    auto cmd = Protocol::ModifyItemsCommandPtr::create();
 
  171    for (
int op : std::as_const(mOperations)) {
 
  173        case ItemModifyJobPrivate::RemoteId:
 
  178        case ItemModifyJobPrivate::Gid: {
 
  179            const QString gid = GidExtractor::getGid(item);
 
  185        case ItemModifyJobPrivate::RemoteRevision:
 
  190        case ItemModifyJobPrivate::Dirty:
 
  191            cmd->setDirty(
false);
 
  196    if (item.d_ptr->mClearPayload) {
 
  197        cmd->setInvalidateCache(
true);
 
  200        cmd->setNotify(
true);
 
  203    if (item.d_ptr->mFlagsOverwritten) {
 
  204        cmd->setFlags(item.
flags());
 
  206        const auto addedFlags = ItemChangeLog::instance()->addedFlags(item.d_ptr);
 
  207        if (!addedFlags.isEmpty()) {
 
  208            cmd->setAddedFlags(addedFlags);
 
  210        const auto deletedFlags = ItemChangeLog::instance()->deletedFlags(item.d_ptr);
 
  211        if (!deletedFlags.isEmpty()) {
 
  212            cmd->setRemovedFlags(deletedFlags);
 
  216    if (item.d_ptr->mTagsOverwritten) {
 
  217        const auto tags = item.tags();
 
  218        if (!tags.isEmpty()) {
 
  219            cmd->setTags(ProtocolHelper::entitySetToScope(tags));
 
  222        const auto addedTags = ItemChangeLog::instance()->addedTags(item.d_ptr);
 
  223        if (!addedTags.isEmpty()) {
 
  224            cmd->setAddedTags(ProtocolHelper::entitySetToScope(addedTags));
 
  226        const auto deletedTags = ItemChangeLog::instance()->deletedTags(item.d_ptr);
 
  227        if (!deletedTags.isEmpty()) {
 
  228            cmd->setRemovedTags(ProtocolHelper::entitySetToScope(deletedTags));
 
  232    if (!mParts.isEmpty()) {
 
  233        QSet<QByteArray> parts;
 
  235        for (
const QByteArray &part : std::as_const(mParts)) {
 
  236            parts.
insert(ProtocolHelper::encodePartIdentifier(ProtocolHelper::PartPayload, part));
 
  238        cmd->setParts(parts);
 
  241    const AttributeStorage &attributeStorage = ItemChangeLog::instance()->attributeStorage(item.d_ptr);
 
  242    const QSet<QByteArray> deletedAttributes = attributeStorage.deletedAttributes();
 
  243    if (!deletedAttributes.
isEmpty()) {
 
  244        QSet<QByteArray> removedParts;
 
  246        for (
const QByteArray &part : deletedAttributes) {
 
  247            removedParts.
insert(
"ATR:" + part);
 
  249        cmd->setRemovedParts(removedParts);
 
  251    if (attributeStorage.hasModifiedAttributes()) {
 
  252        cmd->setAttributes(ProtocolHelper::attributesToProtocol(attributeStorage.modifiedAttributes()));
 
  256    if (cmd->modifiedParts() == Protocol::ModifyItemsCommand::None && mParts.isEmpty() && !cmd->invalidateCache()) {
 
  260    const auto batchSize = qMin(MaxBatchSize, mRemainingItems.size());
 
  261    const auto batch = mRemainingItems.subspan(0, batchSize);
 
  262    mRemainingItems = mRemainingItems.subspan(batch.size());
 
  264    cmd->setItems(ProtocolHelper::entitySetToScope(QList(batch.begin(), batch.end())));
 
  265    if (mRevCheck && item.
revision() >= 0) {
 
  266        cmd->setOldRevision(item.
revision());
 
  269    if (item.d_ptr->mSizeChanged) {
 
  270        cmd->setItemSize(item.
size());
 
  280    d->mRemainingItems = std::span(d->mItems);
 
 
  288    if (!response->isResponse() && response->type() == Protocol::Command::StreamPayload) {
 
  289        const auto &streamCmd = Protocol::cmdCast<Protocol::StreamPayloadCommand>(response);
 
  290        auto streamResp = Protocol::StreamPayloadResponsePtr::create();
 
  291        if (streamCmd.request() == Protocol::StreamPayloadCommand::MetaData) {
 
  292            streamResp->setMetaData(d->preparePart(streamCmd.payloadName()));
 
  294            if (streamCmd.destination().isEmpty()) {
 
  295                streamResp->setData(d->mPendingData);
 
  298                if (!ProtocolHelper::streamPayloadToFile(streamCmd.destination(), d->mPendingData, 
error)) {
 
  303        d->sendCommand(tag, streamResp);
 
  307    if (response->isResponse() && response->type() == Protocol::Command::ModifyItems) {
 
  308        const auto &resp = Protocol::cmdCast<Protocol::ModifyItemsResponse>(response);
 
  309        if (resp.errorCode()) {
 
  316            if (d->mAutomaticConflictHandlingEnabled) {
 
  317                auto handler = 
new ConflictHandler(ConflictHandler::LocalLocalConflict, 
this);
 
  318                handler->setConflictingItems(d->mItems.first(), d->mItems.first());
 
  319                connect(handler, &ConflictHandler::conflictResolved, 
this, [d]() {
 
  320                    d->conflictResolved();
 
  322                connect(handler, &ConflictHandler::error, 
this, [d](
const QString &str) {
 
  323                    d->conflictResolveError(str);
 
  330        if (resp.modificationDateTime().isValid()) {
 
  332            item.setModificationTime(resp.modificationDateTime());
 
  333            item.d_ptr->resetChangeLog();
 
  334        } 
else if (resp.id() > -1) {
 
  335            auto it = std::find_if(d->mItems.begin(), d->mItems.end(), [&resp](
const Item &
item) -> 
bool {
 
  336                return item.id() == resp.id();
 
  338            if (it == d->mItems.end()) {
 
  339                qCDebug(AKONADICORE_LOG) << 
"Received STORE response for an item we did not modify: " << tag << Protocol::debugString(response);
 
  343            const int newRev = resp.newRevision();
 
  344            const int oldRev = (*it).revision();
 
  345            if (newRev >= oldRev && newRev >= 0) {
 
  346                d->itemRevisionChanged((*it).id(), oldRev, newRev);
 
  347                (*it).setRevision(newRev);
 
  354        if (!d->mRemainingItems.empty()) {
 
  355            if (!d->nextBatch()) {
 
  360        for (
const Item &
item : std::as_const(d->mItems)) {
 
  361            ChangeMediator::invalidateItem(
item);
 
 
  374    if (d->mIgnorePayload == ignore) {
 
  378    d->mIgnorePayload = ignore;
 
  379    if (d->mIgnorePayload) {
 
  382        Q_ASSERT(!d->mItems.first().mimeType().isEmpty());
 
  383        d->mParts = d->mItems.first().loadedPayloadParts();
 
 
  391    return d->mIgnorePayload;
 
 
  398        d->mOperations.insert(ItemModifyJobPrivate::Gid);
 
  400        d->mOperations.remove(ItemModifyJobPrivate::Gid);
 
 
  407    return d->mOperations.contains(ItemModifyJobPrivate::Gid);
 
 
  414    d->mRevCheck = 
false;
 
 
  421    d->mAutomaticConflictHandlingEnabled = 
false;
 
 
  427    Q_ASSERT(d->mItems.size() == 1);
 
  429    return d->mItems.first();
 
 
  438#include "moc_itemmodifyjob.cpp" 
Base class for exceptions used by the Akonadi library.
 
const char * what() const noexcept override
Returns the error message associated with this exception.
 
Job that modifies an existing item in the Akonadi storage.
 
void setIgnorePayload(bool ignore)
Sets whether the payload of the modified item shall be omitted from transmission to the Akonadi stora...
 
void disableRevisionCheck()
Disables the check of the revision number.
 
bool updateGid() const
Returns whether the GID should be updated.
 
~ItemModifyJob() override
Destroys the item modify job.
 
ItemModifyJob(const Item &item, QObject *parent=nullptr)
Creates a new item modify job.
 
bool ignorePayload() const
Returns whether the payload of the modified item shall be omitted from transmission to the Akonadi st...
 
void disableAutomaticConflictHandling()
Disables the automatic handling of conflicts.
 
Item item() const
Returns the modified and stored item including the changed revision number.
 
void setUpdateGid(bool update)
Sets whether the GID shall be updated either from the gid parameter or by extracting it from the payl...
 
void doStart() override
This method must be reimplemented in the concrete jobs.
 
Item::List items() const
Returns the modified and stored items including the changed revision number.
 
bool doHandleResponse(qint64 tag, const Protocol::CommandPtr &response) override
This method should be reimplemented in the concrete jobs in case you want to handle incoming data.
 
Represents a PIM item stored in Akonadi storage.
 
QString remoteRevision() const
Returns the remote revision of the item.
 
qint64 Id
Describes the unique id type.
 
qint64 size() const
Returns the size of the items in bytes.
 
Flags flags() const
Returns all flags of this item.
 
int revision() const
Returns the revision number of the item.
 
QString remoteId() const
Returns the remote id of the item.
 
QList< Item > List
Describes a list of items.
 
virtual bool doHandleResponse(qint64 tag, const Protocol::CommandPtr &response)
This method should be reimplemented in the concrete jobs in case you want to handle incoming data.
 
Job(QObject *parent=nullptr)
Creates a new job.
 
void setErrorText(const QString &errorText)
 
void setError(int errorCode)
 
Helper integration between Akonadi and Qt.
 
KDB_EXPORT KDbVersionInfo version()
 
KLEO_EXPORT std::unique_ptr< GpgME::DefaultAssuanTransaction > sendCommand(std::shared_ptr< GpgME::Context > &assuanContext, const std::string &command, GpgME::Error &err)
 
virtual qint64 size() const const override
 
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
 
QObject * parent() const const
 
iterator insert(const T &value)
 
bool isEmpty() const const
 
void reserve(qsizetype size)
 
qsizetype size() const const
 
QString fromUtf8(QByteArrayView str)
 
bool isNull() const const