File indexing completed on 2024-04-14 04:51:45

0001 /**
0002  * SPDX-FileCopyrightText: 2013 Albert Vaca <albertvaka@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include "kiokdeconnect.h"
0008 
0009 #include <QDBusMetaType>
0010 #include <QThread>
0011 
0012 #include <KLocalizedString>
0013 
0014 #include <QDebug>
0015 #include <QtPlugin>
0016 
0017 #include "kdeconnectkio_debug.h"
0018 
0019 class KIOPluginForMetaData : public QObject
0020 {
0021     Q_OBJECT
0022     Q_PLUGIN_METADATA(IID "org.kde.kio.slave.kdeconnect" FILE "kdeconnect.json")
0023 };
0024 
0025 extern "C" int Q_DECL_EXPORT kdemain(int argc, char **argv)
0026 {
0027     QCoreApplication app(argc, argv);
0028     app.setApplicationName(QStringLiteral("kio_kdeconnect"));
0029 
0030     if (argc != 4) {
0031         fprintf(stderr, "Usage: kio_kdeconnect protocol pool app\n");
0032         exit(-1);
0033     }
0034 
0035     KioKdeconnect worker(argv[2], argv[3]);
0036     worker.dispatchLoop();
0037     return 0;
0038 }
0039 
0040 // Some useful error mapping
0041 KIO::Error toKioError(const QDBusError::ErrorType type)
0042 {
0043     switch (type) {
0044     case QDBusError::NoError:
0045         return KIO::Error(KJob::NoError);
0046     case QDBusError::NoMemory:
0047         return KIO::ERR_OUT_OF_MEMORY;
0048     case QDBusError::Timeout:
0049         return KIO::ERR_SERVER_TIMEOUT;
0050     case QDBusError::TimedOut:
0051         return KIO::ERR_SERVER_TIMEOUT;
0052     default:
0053         return KIO::ERR_WORKER_DEFINED;
0054     };
0055 };
0056 
0057 template<typename T>
0058 KIO::WorkerResult handleDBusError(QDBusReply<T> &reply)
0059 {
0060     if (!reply.isValid()) {
0061         qCDebug(KDECONNECT_KIO) << "Error in DBus request:" << reply.error();
0062         return KIO::WorkerResult::fail(toKioError(reply.error().type()), reply.error().message());
0063     }
0064     return KIO::WorkerResult::pass();
0065 }
0066 
0067 KioKdeconnect::KioKdeconnect(const QByteArray &pool, const QByteArray &app)
0068     : WorkerBase("kdeconnect", pool, app)
0069     , m_dbusInterface(new DaemonDbusInterface(this))
0070 {
0071 }
0072 
0073 KIO::WorkerResult KioKdeconnect::listAllDevices()
0074 {
0075     infoMessage(i18n("Listing devices..."));
0076 
0077     // TODO: Change to all devices and show different icons for connected and disconnected?
0078     const QStringList devices = m_dbusInterface->devices(true, true);
0079 
0080     for (const QString &deviceId : devices) {
0081         DeviceDbusInterface interface(deviceId);
0082 
0083         if (!interface.hasPlugin(QStringLiteral("kdeconnect_sftp")))
0084             continue;
0085 
0086         const QString path = QStringLiteral("kdeconnect://").append(deviceId).append(QStringLiteral("/"));
0087         const QString name = interface.name();
0088         const QString icon = QStringLiteral("kdeconnect");
0089 
0090         KIO::UDSEntry entry;
0091         entry.reserve(6);
0092         entry.fastInsert(KIO::UDSEntry::UDS_NAME, name);
0093         entry.fastInsert(KIO::UDSEntry::UDS_ICON_NAME, icon);
0094         entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, QT_STAT_DIR);
0095         entry.fastInsert(KIO::UDSEntry::UDS_ACCESS,
0096                          QFileDevice::ReadOwner | QFileDevice::ExeOwner | QFileDevice::ReadGroup | QFileDevice::ExeGroup | QFileDevice::ReadOther
0097                              | QFileDevice::ExeOther);
0098         entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QLatin1String(""));
0099         entry.fastInsert(KIO::UDSEntry::UDS_URL, path);
0100         listEntry(entry);
0101     }
0102 
0103     // We also need a non-null and writable UDSentry for "."
0104     KIO::UDSEntry entry;
0105     entry.reserve(4);
0106     entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral("."));
0107     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, QT_STAT_DIR);
0108     entry.fastInsert(KIO::UDSEntry::UDS_SIZE, 0);
0109     entry.fastInsert(KIO::UDSEntry::UDS_ACCESS,
0110                      QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner | QFileDevice::ReadGroup | QFileDevice::WriteGroup
0111                          | QFileDevice::ExeGroup | QFileDevice::ReadOther | QFileDevice::ExeOther);
0112     listEntry(entry);
0113 
0114     infoMessage(QLatin1String(""));
0115     return KIO::WorkerResult::pass();
0116 }
0117 
0118 KIO::WorkerResult KioKdeconnect::listDevice(const QString &device)
0119 {
0120     infoMessage(i18n("Accessing device..."));
0121 
0122     qCDebug(KDECONNECT_KIO) << "ListDevice" << device;
0123 
0124     SftpDbusInterface interface(device);
0125 
0126     QDBusReply<bool> mountreply = interface.mountAndWait();
0127 
0128     if (mountreply.error().type() == QDBusError::UnknownObject) {
0129         DaemonDbusInterface daemon;
0130 
0131         auto devsRepl = daemon.devices(false, false);
0132         devsRepl.waitForFinished();
0133 
0134         if (!devsRepl.value().contains(device)) {
0135             return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("No such device: %0").arg(device));
0136         }
0137 
0138         DeviceDbusInterface dev(device);
0139 
0140         if (!dev.isPaired()) {
0141             return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("%0 is not paired").arg(dev.name()));
0142         }
0143 
0144         if (!dev.isReachable()) {
0145             return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("%0 is not connected").arg(dev.name()));
0146         }
0147 
0148         if (!dev.hasPlugin(QStringLiteral("kdeconnect_sftp"))) {
0149             return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("%0 has no Remote Filesystem plugin").arg(dev.name()));
0150         }
0151     }
0152 
0153     if (auto result = handleDBusError(mountreply); !result.success()) {
0154         return result;
0155     }
0156 
0157     if (!mountreply.value()) {
0158         return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, interface.getMountError());
0159     }
0160 
0161     QDBusReply<QVariantMap> urlreply = interface.getDirectories();
0162 
0163     if (auto result = handleDBusError(urlreply); !result.success()) {
0164         return result;
0165     }
0166 
0167     QVariantMap urls = urlreply.value();
0168 
0169     for (QVariantMap::iterator it = urls.begin(); it != urls.end(); ++it) {
0170         const QString path = it.key();
0171         const QString name = it.value().toString();
0172         const QString icon = QStringLiteral("folder");
0173 
0174         KIO::UDSEntry entry;
0175         entry.reserve(6);
0176         entry.fastInsert(KIO::UDSEntry::UDS_NAME, name);
0177         entry.fastInsert(KIO::UDSEntry::UDS_ICON_NAME, icon);
0178         entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, QT_STAT_DIR);
0179         entry.fastInsert(KIO::UDSEntry::UDS_ACCESS,
0180                          QFileDevice::ReadOwner | QFileDevice::ExeOwner | QFileDevice::ReadGroup | QFileDevice::ExeGroup | QFileDevice::ReadOther
0181                              | QFileDevice::ExeOther);
0182         entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QLatin1String(""));
0183         entry.fastInsert(KIO::UDSEntry::UDS_URL, QUrl::fromLocalFile(path).toString());
0184         listEntry(entry);
0185     }
0186 
0187     // We also need a non-null and writable UDSentry for "."
0188     KIO::UDSEntry entry;
0189     entry.reserve(4);
0190     entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral("."));
0191     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, QT_STAT_DIR);
0192     entry.fastInsert(KIO::UDSEntry::UDS_SIZE, 0);
0193     entry.fastInsert(KIO::UDSEntry::UDS_ACCESS,
0194                      QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner | QFileDevice::ReadGroup | QFileDevice::WriteGroup
0195                          | QFileDevice::ExeGroup | QFileDevice::ReadOther | QFileDevice::ExeOther);
0196 
0197     listEntry(entry);
0198 
0199     infoMessage(QLatin1String(""));
0200     return KIO::WorkerResult::pass();
0201 }
0202 
0203 KIO::WorkerResult KioKdeconnect::listDir(const QUrl &url)
0204 {
0205     qCDebug(KDECONNECT_KIO) << "Listing..." << url;
0206 
0207     if (!m_dbusInterface->isValid()) {
0208         return KIO::WorkerResult::fail(KIO::Error::ERR_WORKER_DEFINED, i18n("Could not contact background service."));
0209     }
0210 
0211     QString currentDevice = url.host();
0212 
0213     if (currentDevice.isEmpty()) {
0214         return listAllDevices();
0215     } else {
0216         return listDevice(currentDevice);
0217     }
0218 }
0219 
0220 KIO::WorkerResult KioKdeconnect::stat(const QUrl &url)
0221 {
0222     qCDebug(KDECONNECT_KIO) << "Stat: " << url;
0223 
0224     KIO::UDSEntry entry;
0225     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, QT_STAT_DIR);
0226 
0227     QString currentDevice = url.host();
0228     if (!currentDevice.isEmpty()) {
0229         SftpDbusInterface interface(currentDevice);
0230 
0231         if (interface.isValid()) {
0232             entry.fastInsert(KIO::UDSEntry::UDS_LOCAL_PATH, interface.mountPoint());
0233 
0234             if (!interface.isMounted()) {
0235                 interface.mount();
0236             }
0237         }
0238     }
0239 
0240     statEntry(entry);
0241 
0242     return KIO::WorkerResult::pass();
0243 }
0244 
0245 KIO::WorkerResult KioKdeconnect::get(const QUrl &url)
0246 {
0247     qCDebug(KDECONNECT_KIO) << "Get: " << url;
0248     mimeType(QLatin1String(""));
0249     return KIO::WorkerResult::pass();
0250 }
0251 
0252 #include "kiokdeconnect.moc"
0253 #include "moc_kiokdeconnect.cpp"