File indexing completed on 2024-06-23 05:07:03
0001 /* 0002 SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "tagmodifyhandler.h" 0008 0009 #include "connection.h" 0010 #include "shared/akranges.h" 0011 #include "storage/datastore.h" 0012 #include "storage/querybuilder.h" 0013 #include "tagfetchhelper.h" 0014 0015 #include "private/imapset_p.h" 0016 0017 using namespace Akonadi; 0018 using namespace Akonadi::Server; 0019 using namespace AkRanges; 0020 0021 TagModifyHandler::TagModifyHandler(AkonadiServer &akonadi) 0022 : Handler(akonadi) 0023 { 0024 } 0025 0026 bool TagModifyHandler::parseStream() 0027 { 0028 const auto &cmd = Protocol::cmdCast<Protocol::ModifyTagCommand>(m_command); 0029 0030 Tag changedTag = Tag::retrieveById(cmd.tagId()); 0031 if (!changedTag.isValid()) { 0032 return failureResponse("No such tag"); 0033 } 0034 0035 QSet<QByteArray> changes; 0036 0037 // Retrieve all tag's attributes 0038 const TagAttribute::List attributes = TagAttribute::retrieveFiltered(TagAttribute::tagIdFullColumnName(), cmd.tagId()); 0039 const auto attributesMap = attributes | Views::transform([](const auto &attr) { 0040 return std::make_pair(attr.type(), attr); 0041 }) 0042 | Actions::toQMap; 0043 0044 if (cmd.modifiedParts() & Protocol::ModifyTagCommand::ParentId) { 0045 if (cmd.parentId() != changedTag.parentId()) { 0046 changedTag.setParentId(cmd.parentId()); 0047 changes << AKONADI_PARAM_PARENT; 0048 } 0049 } 0050 0051 if (cmd.modifiedParts() & Protocol::ModifyTagCommand::Type) { 0052 TagType type = TagType::retrieveById(changedTag.typeId()); 0053 const QString newTypeName = QString::fromUtf8(cmd.type()); 0054 if (newTypeName != type.name()) { 0055 const TagType newType = TagType::retrieveByNameOrCreate(newTypeName); 0056 if (!newType.isValid()) { 0057 return failureResponse("Failed to create new tag type"); 0058 } 0059 changedTag.setTagType(newType); 0060 changes << AKONADI_PARAM_MIMETYPE; 0061 } 0062 } 0063 0064 bool tagRemoved = false; 0065 if (cmd.modifiedParts() & Protocol::ModifyTagCommand::RemoteId) { 0066 if (!connection()->context().resource().isValid()) { 0067 return failureResponse("Only resources can change tag remote ID"); 0068 } 0069 0070 // Simply using remove() doesn't work since we need two arguments 0071 QueryBuilder qb(TagRemoteIdResourceRelation::tableName(), QueryBuilder::Delete); 0072 qb.addValueCondition(TagRemoteIdResourceRelation::tagIdColumn(), Query::Equals, cmd.tagId()); 0073 qb.addValueCondition(TagRemoteIdResourceRelation::resourceIdColumn(), Query::Equals, connection()->context().resource().id()); 0074 qb.exec(); 0075 0076 if (!cmd.remoteId().isEmpty()) { 0077 TagRemoteIdResourceRelation remoteIdRelation; 0078 remoteIdRelation.setRemoteId(QString::fromUtf8(cmd.remoteId())); 0079 remoteIdRelation.setResourceId(connection()->context().resource().id()); 0080 remoteIdRelation.setTag(changedTag); 0081 if (!remoteIdRelation.insert()) { 0082 return failureResponse("Failed to insert remotedid resource relation"); 0083 } 0084 } else { 0085 const int tagRidsCount = TagRemoteIdResourceRelation::count(TagRemoteIdResourceRelation::tagIdColumn(), changedTag.id()); 0086 // We just removed the last RID of the tag, which means that no other 0087 // resource owns this tag, so we have to remove it to simulate tag 0088 // removal 0089 if (tagRidsCount == 0) { 0090 if (!storageBackend()->removeTags(Tag::List() << changedTag)) { 0091 return failureResponse("Failed to remove tag"); 0092 } 0093 tagRemoved = true; 0094 } 0095 } 0096 // Do not notify about remoteid changes, otherwise we bounce back and forth 0097 // between resources recording it's change and updating the remote id. 0098 } 0099 0100 if (cmd.modifiedParts() & Protocol::ModifyTagCommand::RemovedAttributes) { 0101 const auto attrNames = cmd.removedAttributes(); 0102 for (const QByteArray &attrName : attrNames) { 0103 TagAttribute attribute = attributesMap.value(attrName); 0104 TagAttribute::remove(attribute.id()); 0105 changes << attrName; 0106 } 0107 } 0108 0109 if (cmd.modifiedParts() & Protocol::ModifyTagCommand::Attributes) { 0110 const QMap<QByteArray, QByteArray> attrs = cmd.attributes(); 0111 for (auto iter = attrs.cbegin(), end = attrs.cend(); iter != end; ++iter) { 0112 if (attributesMap.contains(iter.key())) { 0113 TagAttribute attribute = attributesMap.value(iter.key()); 0114 attribute.setValue(iter.value()); 0115 if (!attribute.update()) { 0116 return failureResponse("Failed to update attribute"); 0117 } 0118 } else { 0119 TagAttribute attribute; 0120 attribute.setTagId(cmd.tagId()); 0121 attribute.setType(iter.key()); 0122 attribute.setValue(iter.value()); 0123 if (!attribute.insert()) { 0124 return failureResponse("Failed to insert attribute"); 0125 } 0126 } 0127 changes << iter.key(); 0128 } 0129 } 0130 0131 if (!tagRemoved) { 0132 if (!changedTag.update()) { 0133 return failureResponse("Failed to store changes"); 0134 } 0135 if (!changes.isEmpty()) { 0136 storageBackend()->notificationCollector()->tagChanged(changedTag); 0137 } 0138 0139 ImapSet set; 0140 set.add(QList<qint64>() << cmd.tagId()); 0141 0142 Protocol::TagFetchScope fetchScope; 0143 fetchScope.setFetchRemoteID(true); 0144 fetchScope.setFetchAllAttributes(true); 0145 0146 Scope scope; 0147 scope.setUidSet(set); 0148 TagFetchHelper helper(connection(), scope, fetchScope); 0149 if (!helper.fetchTags()) { 0150 return failureResponse("Failed to fetch response"); 0151 } 0152 } else { 0153 successResponse<Protocol::DeleteTagResponse>(); 0154 } 0155 0156 return successResponse<Protocol::ModifyTagResponse>(); 0157 }