File indexing completed on 2025-01-05 04:46:55
0001 /* 0002 SPDX-FileCopyrightText: 2009 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "collectionqueryhelper.h" 0008 0009 #include "connection.h" 0010 #include "handler.h" 0011 #include "queryhelper.h" 0012 #include "storage/querybuilder.h" 0013 #include "storage/selectquerybuilder.h" 0014 0015 #include "private/imapset_p.h" 0016 0017 using namespace Akonadi; 0018 using namespace Akonadi::Server; 0019 0020 void CollectionQueryHelper::remoteIdToQuery(const QStringList &rids, const CommandContext &context, QueryBuilder &qb) 0021 { 0022 if (rids.size() == 1) { 0023 qb.addValueCondition(Collection::remoteIdFullColumnName(), Query::Equals, rids.first()); 0024 } else { 0025 qb.addValueCondition(Collection::remoteIdFullColumnName(), Query::In, rids); 0026 } 0027 0028 if (context.resource().isValid()) { 0029 qb.addValueCondition(Collection::resourceIdFullColumnName(), Query::Equals, context.resource().id()); 0030 } 0031 } 0032 0033 void CollectionQueryHelper::scopeToQuery(const Scope &scope, const CommandContext &context, QueryBuilder &qb) 0034 { 0035 if (scope.scope() == Scope::Uid) { 0036 QueryHelper::setToQuery(scope.uidSet(), Collection::idFullColumnName(), qb); 0037 } else if (scope.scope() == Scope::Rid) { 0038 if (context.collectionId() <= 0 && !context.resource().isValid()) { 0039 throw HandlerException("Operations based on remote identifiers require a resource or collection context"); 0040 } 0041 CollectionQueryHelper::remoteIdToQuery(scope.ridSet(), context, qb); 0042 } else if (scope.scope() == Scope::HierarchicalRid) { 0043 if (!context.resource().isValid()) { 0044 throw HandlerException("Operations based on hierarchical remote identifiers require a resource or collection context"); 0045 } 0046 const Collection c = CollectionQueryHelper::resolveHierarchicalRID(scope.hridChain(), context.resource().id()); 0047 qb.addValueCondition(Collection::idFullColumnName(), Query::Equals, c.id()); 0048 } else { 0049 throw HandlerException("WTF?"); 0050 } 0051 } 0052 0053 bool CollectionQueryHelper::hasAllowedName(const Collection &collection, const QString &name, Collection::Id parent) 0054 { 0055 Q_UNUSED(collection) 0056 SelectQueryBuilder<Collection> qb; 0057 if (parent > 0) { 0058 qb.addValueCondition(Collection::parentIdColumn(), Query::Equals, parent); 0059 } else { 0060 qb.addValueCondition(Collection::parentIdColumn(), Query::Is, QVariant()); 0061 } 0062 qb.addValueCondition(Collection::nameColumn(), Query::Equals, name); 0063 if (!qb.exec()) { 0064 return false; 0065 } 0066 const QList<Collection> result = qb.result(); 0067 if (!result.isEmpty()) { 0068 return result.first().id() == collection.id(); 0069 } 0070 return true; 0071 } 0072 0073 bool CollectionQueryHelper::canBeMovedTo(const Collection &collection, const Collection &_parent) 0074 { 0075 if (_parent.isValid()) { 0076 Collection parent = _parent; 0077 for (;;) { 0078 if (parent.id() == collection.id()) { 0079 return false; // target is child of source 0080 } 0081 if (parent.parentId() == 0) { 0082 break; 0083 } 0084 parent = parent.parent(); 0085 } 0086 } 0087 return hasAllowedName(collection, collection.name(), _parent.id()); 0088 } 0089 0090 Collection CollectionQueryHelper::resolveHierarchicalRID(const QList<Scope::HRID> &ridChain, Resource::Id resId) 0091 { 0092 if (ridChain.size() < 2) { 0093 throw HandlerException("Empty or incomplete hierarchical RID chain"); 0094 } 0095 if (!ridChain.last().isEmpty()) { 0096 throw HandlerException("Hierarchical RID chain is not root-terminated"); 0097 } 0098 Collection::Id parentId = 0; 0099 Collection result; 0100 for (int i = ridChain.size() - 2; i >= 0; --i) { 0101 SelectQueryBuilder<Collection> qb; 0102 if (parentId > 0) { 0103 qb.addValueCondition(Collection::parentIdColumn(), Query::Equals, parentId); 0104 } else { 0105 qb.addValueCondition(Collection::parentIdColumn(), Query::Is, QVariant()); 0106 } 0107 qb.addValueCondition(Collection::remoteIdColumn(), Query::Equals, ridChain.at(i).remoteId); 0108 qb.addValueCondition(Collection::resourceIdColumn(), Query::Equals, resId); 0109 if (!qb.exec()) { 0110 throw HandlerException("Unable to execute query"); 0111 } 0112 const Collection::List results = qb.result(); 0113 const int resultSize = results.size(); 0114 if (resultSize == 0) { 0115 throw HandlerException("Hierarchical RID does not specify an existing collection"); 0116 } else if (resultSize > 1) { 0117 throw HandlerException("Hierarchical RID does not specify a unique collection"); 0118 } 0119 result = results.first(); 0120 parentId = result.id(); 0121 } 0122 return result; 0123 } 0124 0125 Collection CollectionQueryHelper::singleCollectionFromScope(const Scope &scope, const CommandContext &context) 0126 { 0127 // root 0128 if (scope.scope() == Scope::Uid && scope.uidSet().intervals().count() == 1) { 0129 const ImapInterval i = scope.uidSet().intervals().at(0); 0130 if (!i.size()) { // ### why do we need this hack for 0, shouldn't that be size() == 1? 0131 Collection root; 0132 root.setId(0); 0133 return root; 0134 } 0135 } 0136 SelectQueryBuilder<Collection> qb; 0137 scopeToQuery(scope, context, qb); 0138 if (!qb.exec()) { 0139 throw HandlerException("Unable to execute query"); 0140 } 0141 const Collection::List cols = qb.result(); 0142 if (cols.isEmpty()) { 0143 throw HandlerException("No collection found"); 0144 } else if (cols.size() > 1) { 0145 throw HandlerException("Collection cannot be uniquely identified"); 0146 } 0147 return cols.first(); 0148 }