File indexing completed on 2024-12-22 04:56:53

0001 /*
0002     SPDX-FileCopyrightText: 2006 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "akonadiworker.h"
0008 
0009 #include <Akonadi/Collection>
0010 #include <Akonadi/CollectionDeleteJob>
0011 #include <Akonadi/CollectionFetchJob>
0012 #include <Akonadi/EntityDisplayAttribute>
0013 #include <Akonadi/ItemDeleteJob>
0014 #include <Akonadi/ItemFetchJob>
0015 #include <Akonadi/ItemFetchScope>
0016 
0017 #include "akonadiworker_debug.h"
0018 
0019 #include <KAboutData>
0020 #include <KLocalizedString>
0021 #include <QApplication>
0022 #include <QCommandLineOption>
0023 #include <QCommandLineParser>
0024 
0025 #ifdef Q_OS_WIN
0026 // see kio/core/src/kioglobal_p.h
0027 #define S_IRUSR 0400
0028 #define S_IRGRP 0040
0029 #define S_IROTH 0004
0030 #endif
0031 
0032 // Pseudo plugin class to embed meta data
0033 class KIOPluginForMetaData : public QObject
0034 {
0035     Q_OBJECT
0036     Q_PLUGIN_METADATA(IID "org.kde.kio.worker.akonadi" FILE "akonadi.json")
0037 };
0038 
0039 extern "C" {
0040 int Q_DECL_EXPORT kdemain(int argc, char **argv);
0041 }
0042 
0043 int kdemain(int argc, char **argv)
0044 {
0045     QApplication app(argc, argv);
0046     KAboutData aboutData(QStringLiteral("kio_akonadi"), QString(), QStringLiteral("0"));
0047     QCommandLineParser parser;
0048     KAboutData::setApplicationData(aboutData);
0049     parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("+protocol"), i18n("Protocol name")));
0050     parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("+pool"), i18n("Socket name")));
0051     parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("+app"), i18n("Socket name")));
0052 
0053     aboutData.setupCommandLine(&parser);
0054     parser.process(app);
0055     aboutData.processCommandLine(&parser);
0056 
0057     AkonadiWorker worker(parser.positionalArguments().at(1).toLocal8Bit(), parser.positionalArguments().at(2).toLocal8Bit());
0058     worker.dispatchLoop();
0059 
0060     return 0;
0061 }
0062 
0063 using namespace Akonadi;
0064 
0065 AkonadiWorker::AkonadiWorker(const QByteArray &pool_socket, const QByteArray &app_socket)
0066     : KIO::WorkerBase("akonadi", pool_socket, app_socket)
0067 {
0068     qCDebug(AKONADIWORKER_LOG) << "kio_akonadi starting up";
0069 }
0070 
0071 AkonadiWorker::~AkonadiWorker()
0072 {
0073     qCDebug(AKONADIWORKER_LOG) << "kio_akonadi shutting down";
0074 }
0075 
0076 KIO::WorkerResult AkonadiWorker::get(const QUrl &url)
0077 {
0078     const Item item = Item::fromUrl(url);
0079     auto job = new ItemFetchJob(item);
0080     job->fetchScope().fetchFullPayload();
0081 
0082     if (!job->exec()) {
0083         return KIO::WorkerResult::fail(KIO::ERR_INTERNAL, job->errorString());
0084     }
0085 
0086     if (job->items().count() != 1) {
0087         return KIO::WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, i18n("No such item."));
0088     } else {
0089         const Item itemJob = job->items().at(0);
0090         const QByteArray tmp = itemJob.payloadData();
0091         data(tmp);
0092         data(QByteArray());
0093         return KIO::WorkerResult::pass();
0094     }
0095 }
0096 
0097 KIO::WorkerResult AkonadiWorker::stat(const QUrl &url)
0098 {
0099     qCDebug(AKONADIWORKER_LOG) << url;
0100 
0101     // Stats for a collection
0102     if (Collection::fromUrl(url).isValid()) {
0103         Collection collection = Collection::fromUrl(url);
0104 
0105         if (collection != Collection::root()) {
0106             // Check that the collection exists.
0107             auto job = new CollectionFetchJob(collection, CollectionFetchJob::Base);
0108             if (!job->exec()) {
0109                 return KIO::WorkerResult::fail(KIO::ERR_INTERNAL, job->errorString());
0110             }
0111 
0112             if (job->collections().count() != 1) {
0113                 return KIO::WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, i18n("No such item."));
0114             }
0115 
0116             collection = job->collections().at(0);
0117         }
0118 
0119         statEntry(entryForCollection(collection));
0120         return KIO::WorkerResult::pass();
0121     }
0122     // Stats for an item
0123     else if (Item::fromUrl(url).isValid()) {
0124         auto job = new ItemFetchJob(Item::fromUrl(url));
0125 
0126         if (!job->exec()) {
0127             return KIO::WorkerResult::fail(KIO::ERR_INTERNAL, job->errorString());
0128         }
0129 
0130         if (job->items().count() != 1) {
0131             return KIO::WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, i18n("No such item."));
0132         }
0133 
0134         const Item item = job->items().at(0);
0135         statEntry(entryForItem(item));
0136         return KIO::WorkerResult::pass();
0137     }
0138     return KIO::WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toString());
0139 }
0140 
0141 KIO::WorkerResult AkonadiWorker::del(const QUrl &url, bool isFile)
0142 {
0143     qCDebug(AKONADIWORKER_LOG) << url;
0144 
0145     if (!isFile) { // It's a directory
0146         Collection collection = Collection::fromUrl(url);
0147         auto job = new CollectionDeleteJob(collection);
0148         if (!job->exec()) {
0149             return KIO::WorkerResult::fail(KIO::ERR_INTERNAL, job->errorString());
0150         }
0151         return KIO::WorkerResult::pass();
0152     } else { // It's a file
0153         auto job = new ItemDeleteJob(Item::fromUrl(url));
0154         if (!job->exec()) {
0155             return KIO::WorkerResult::fail(KIO::ERR_INTERNAL, job->errorString());
0156         }
0157         return KIO::WorkerResult::pass();
0158     }
0159 }
0160 
0161 KIO::WorkerResult AkonadiWorker::listDir(const QUrl &url)
0162 {
0163     qCDebug(AKONADIWORKER_LOG) << url;
0164 
0165     if (!Collection::fromUrl(url).isValid()) {
0166         return KIO::WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, i18n("No such collection."));
0167     }
0168 
0169     // Fetching collections
0170     Collection collection = Collection::fromUrl(url);
0171     if (!collection.isValid()) {
0172         return KIO::WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, i18n("No such collection."));
0173     }
0174     auto job = new CollectionFetchJob(collection, CollectionFetchJob::FirstLevel);
0175     if (!job->exec()) {
0176         return KIO::WorkerResult::fail(KIO::ERR_CANNOT_ENTER_DIRECTORY, job->errorString());
0177     }
0178 
0179     const Collection::List collections = job->collections();
0180     for (const Collection &col : collections) {
0181         listEntry(entryForCollection(col));
0182     }
0183 
0184     // Fetching items
0185     if (collection != Collection::root()) {
0186         auto fjob = new ItemFetchJob(collection);
0187         if (!fjob->exec()) {
0188             return KIO::WorkerResult::fail(KIO::ERR_INTERNAL, job->errorString());
0189         }
0190         const Item::List items = fjob->items();
0191         totalSize(collections.count() + items.count());
0192         for (const Item &item : items) {
0193             listEntry(entryForItem(item));
0194         }
0195     }
0196 
0197     return KIO::WorkerResult::pass();
0198 }
0199 
0200 KIO::UDSEntry AkonadiWorker::entryForItem(const Akonadi::Item &item)
0201 {
0202     KIO::UDSEntry entry;
0203     entry.reserve(7);
0204     entry.fastInsert(KIO::UDSEntry::UDS_NAME, QString::number(item.id()));
0205     entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, item.mimeType());
0206     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG);
0207     entry.fastInsert(KIO::UDSEntry::UDS_URL, item.url().url());
0208     entry.fastInsert(KIO::UDSEntry::UDS_SIZE, item.size());
0209     entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IRGRP | S_IROTH);
0210     entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, item.modificationTime().toSecsSinceEpoch());
0211     return entry;
0212 }
0213 
0214 KIO::UDSEntry AkonadiWorker::entryForCollection(const Akonadi::Collection &collection)
0215 {
0216     KIO::UDSEntry entry;
0217     entry.reserve(7);
0218     entry.fastInsert(KIO::UDSEntry::UDS_NAME, collection.name());
0219     entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, Collection::mimeType());
0220     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
0221     entry.fastInsert(KIO::UDSEntry::UDS_URL, collection.url().url());
0222     entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IRGRP | S_IROTH);
0223     if (const auto attr = collection.attribute<EntityDisplayAttribute>()) {
0224         if (!attr->iconName().isEmpty()) {
0225             entry.fastInsert(KIO::UDSEntry::UDS_ICON_NAME, attr->iconName());
0226         }
0227         if (!attr->displayName().isEmpty()) {
0228             entry.fastInsert(KIO::UDSEntry::UDS_DISPLAY_NAME, attr->displayName());
0229         }
0230     }
0231     return entry;
0232 }
0233 
0234 #include "akonadiworker.moc"