File indexing completed on 2024-04-14 15:32:03

0001 /*
0002  *  SPDX-FileCopyrightText: 2013 Alejandro Fiestas Fiestas <afiestas@kde.org>
0003  *  SPDX-FileCopyrightText: 2014-2015 David Rosca <nowrep@gmail.com>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "obexftp.h"
0009 #include "bluedevil_kded.h"
0010 #include "bluedevildaemon.h"
0011 
0012 #include <QDBusConnection>
0013 #include <QDBusObjectPath>
0014 #include <QDBusPendingCallWatcher>
0015 #include <QDBusPendingReply>
0016 
0017 #include <KLocalizedString>
0018 
0019 #include <BluezQt/Device>
0020 #include <BluezQt/InitObexManagerJob>
0021 #include <BluezQt/ObexFileTransfer>
0022 #include <BluezQt/ObexManager>
0023 #include <BluezQt/ObexSession>
0024 #include <BluezQt/PendingCall>
0025 
0026 ObexFtp::ObexFtp(BlueDevilDaemon *daemon)
0027     : QDBusAbstractAdaptor(daemon)
0028     , m_daemon(daemon)
0029 {
0030     connect(m_daemon->obexManager(), &BluezQt::ObexManager::sessionRemoved, this, &ObexFtp::sessionRemoved);
0031 }
0032 
0033 bool ObexFtp::isOnline()
0034 {
0035     return m_daemon->obexManager()->isOperational();
0036 }
0037 
0038 QString ObexFtp::preferredTarget(const QString &address)
0039 {
0040     BluezQt::DevicePtr device = m_daemon->manager()->deviceForAddress(address);
0041 
0042     // Prefer pcsuite target on S60 devices
0043     if (device && device->uuids().contains(QStringLiteral("00005005-0000-1000-8000-0002EE000001"))) {
0044         return QStringLiteral("pcsuite");
0045     }
0046     return QStringLiteral("ftp");
0047 }
0048 
0049 QString ObexFtp::session(const QString &address, const QString &target, const QDBusMessage &msg)
0050 {
0051     if (!m_daemon->obexManager()->isOperational()) {
0052         return QString();
0053     }
0054 
0055     if (m_sessionMap.contains(address)) {
0056         return m_sessionMap[address];
0057     }
0058 
0059     qCDebug(BLUEDEVIL_KDED_LOG) << "Creating obexftp session for" << address;
0060 
0061     // At this point we always want delayed reply
0062     msg.setDelayedReply(true);
0063 
0064     if (m_pendingSessions.contains(address)) {
0065         m_pendingSessions[address].append(msg);
0066         return QString();
0067     }
0068 
0069     m_pendingSessions.insert(address, {msg});
0070 
0071     QVariantMap args;
0072     args[QStringLiteral("Target")] = target;
0073 
0074     BluezQt::PendingCall *call = m_daemon->obexManager()->createSession(address, args);
0075     call->setUserData(address);
0076     connect(call, &BluezQt::PendingCall::finished, this, &ObexFtp::createSessionFinished);
0077 
0078     return QString();
0079 }
0080 
0081 bool ObexFtp::cancelTransfer(const QString &transfer, const QDBusMessage &msg)
0082 {
0083     // We need this function because kio_obexftp is not owner of the transfer,
0084     // and thus cannot cancel it.
0085 
0086     msg.setDelayedReply(true);
0087 
0088     QDBusMessage call = QDBusMessage::createMethodCall(QStringLiteral("org.bluez.obex"), //
0089                                                        transfer,
0090                                                        QStringLiteral("org.bluez.obex.Transfer1"),
0091                                                        QStringLiteral("Cancel"));
0092 
0093     QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(QDBusConnection::sessionBus().asyncCall(call));
0094     watcher->setProperty("ObexFtpDaemon-msg", QVariant::fromValue(msg));
0095     connect(watcher, &QDBusPendingCallWatcher::finished, this, &ObexFtp::cancelTransferFinished);
0096 
0097     return false;
0098 }
0099 
0100 void ObexFtp::createSessionFinished(BluezQt::PendingCall *call)
0101 {
0102     QString path;
0103 
0104     if (call->error() == BluezQt::PendingCall::AlreadyExists) {
0105         // It may happen when kded crashes, or the session was created by different app
0106         // What to do here? We are not owners of the session...
0107         qCWarning(BLUEDEVIL_KDED_LOG) << "Obex session already exists but it was created by different process!";
0108     } else if (call->error()) {
0109         qCWarning(BLUEDEVIL_KDED_LOG) << "Error creating Obex session" << call->errorText();
0110     } else {
0111         path = call->value().value<QDBusObjectPath>().path();
0112         qCDebug(BLUEDEVIL_KDED_LOG) << "Created Obex session" << path;
0113     }
0114 
0115     const QString &address = call->userData().toString();
0116 
0117     // Send reply (empty session path in case of error)
0118     Q_FOREACH (const QDBusMessage &msg, m_pendingSessions[address]) {
0119         QDBusMessage reply = msg.createReply(path);
0120         QDBusConnection::sessionBus().send(reply);
0121     }
0122 
0123     m_pendingSessions.remove(address);
0124 
0125     if (!call->error()) {
0126         m_sessionMap.insert(address, path);
0127     }
0128 }
0129 
0130 void ObexFtp::cancelTransferFinished(QDBusPendingCallWatcher *watcher)
0131 {
0132     const QDBusPendingReply<> &reply = *watcher;
0133     QDBusMessage msg = watcher->property("ObexFtpDaemon-msg").value<QDBusMessage>();
0134 
0135     bool success = !reply.isError();
0136     QDBusConnection::sessionBus().send(msg.createReply(QVariant(success)));
0137 }
0138 
0139 void ObexFtp::sessionRemoved(BluezQt::ObexSessionPtr session)
0140 {
0141     const QString &path = session->objectPath().path();
0142     const QString &key = m_sessionMap.key(path);
0143 
0144     if (!m_sessionMap.contains(key)) {
0145         qCDebug(BLUEDEVIL_KDED_LOG) << "Removed Obex session is not ours" << path;
0146         return;
0147     }
0148 
0149     qCDebug(BLUEDEVIL_KDED_LOG) << "Removed Obex session" << path;
0150     m_sessionMap.remove(key);
0151 }