File indexing completed on 2024-06-23 05:07:00

0001 /*
0002     SPDX-FileCopyrightText: 2008 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "collectioncopyhandler.h"
0008 
0009 #include "akonadi.h"
0010 #include "cachecleaner.h"
0011 #include "connection.h"
0012 #include "handlerhelper.h"
0013 #include "protocol_p.h"
0014 #include "shared/akranges.h"
0015 #include "storage/collectionqueryhelper.h"
0016 #include "storage/datastore.h"
0017 #include "storage/itemretriever.h"
0018 #include "storage/transaction.h"
0019 
0020 using namespace Akonadi;
0021 using namespace Akonadi::Server;
0022 using namespace AkRanges;
0023 
0024 CollectionCopyHandler::CollectionCopyHandler(AkonadiServer &akonadi)
0025     : ItemCopyHandler(akonadi)
0026 {
0027 }
0028 
0029 bool CollectionCopyHandler::copyCollection(const Collection &source, const Collection &target)
0030 {
0031     if (!CollectionQueryHelper::canBeMovedTo(source, target)) {
0032         // We don't accept source==target, or source being an ancestor of target.
0033         return false;
0034     }
0035 
0036     // copy the source collection
0037     Collection col = source;
0038     col.setParentId(target.id());
0039     col.setResourceId(target.resourceId());
0040     // clear remote id and revision on inter-resource copies
0041     if (source.resourceId() != target.resourceId()) {
0042         col.setRemoteId(QString());
0043         col.setRemoteRevision(QString());
0044     }
0045 
0046     const auto mimeTypes = source.mimeTypes() | Views::transform(&MimeType::name) | Actions::toQList;
0047     const auto attributes = source.attributes() | Views::transform([](const auto &attr) {
0048                                 return std::make_pair(attr.type(), attr.value());
0049                             })
0050         | Actions::toQMap;
0051 
0052     if (!storageBackend()->appendCollection(col, mimeTypes, attributes)) {
0053         return false;
0054     }
0055 
0056     // copy sub-collections
0057     const Collection::List lstCols = source.children();
0058     for (const Collection &child : lstCols) {
0059         if (!copyCollection(child, col)) {
0060             return false;
0061         }
0062     }
0063 
0064     // copy items
0065     const auto items = source.items();
0066     for (const auto &item : items) {
0067         if (!copyItem(item, col)) {
0068             return false;
0069         }
0070     }
0071 
0072     return true;
0073 }
0074 
0075 bool CollectionCopyHandler::parseStream()
0076 {
0077     const auto &cmd = Protocol::cmdCast<Protocol::CopyCollectionCommand>(m_command);
0078 
0079     const Collection source = HandlerHelper::collectionFromScope(cmd.collection(), connection()->context());
0080     if (!source.isValid()) {
0081         return failureResponse(QStringLiteral("No valid source specified"));
0082     }
0083 
0084     const Collection target = HandlerHelper::collectionFromScope(cmd.destination(), connection()->context());
0085     if (!target.isValid()) {
0086         return failureResponse(QStringLiteral("No valid target specified"));
0087     }
0088 
0089     CacheCleanerInhibitor inhibitor(akonadi());
0090 
0091     // retrieve all not yet cached items of the source
0092     ItemRetriever retriever(akonadi().itemRetrievalManager(), connection(), connection()->context());
0093     retriever.setCollection(source, true);
0094     retriever.setRetrieveFullPayload(true);
0095     if (!retriever.exec()) {
0096         return failureResponse(retriever.lastError());
0097     }
0098 
0099     Transaction transaction(storageBackend(), QStringLiteral("CollectionCopyHandler"));
0100 
0101     if (!copyCollection(source, target)) {
0102         return failureResponse(QStringLiteral("Failed to copy collection"));
0103     }
0104 
0105     if (!transaction.commit()) {
0106         return failureResponse(QStringLiteral("Cannot commit transaction."));
0107     }
0108 
0109     return successResponse<Protocol::CopyCollectionResponse>();
0110 }