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 }