diff --git a/CMakeLists.txt b/CMakeLists.txt index 638cfd9d..12010260 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,67 +1,66 @@ project(kdeconnect) set(KDECONNECT_VERSION_MAJOR 1) set(KDECONNECT_VERSION_MINOR 1) set(KDECONNECT_VERSION_PATCH 0) set(KDECONNECT_VERSION "${KDECONNECT_VERSION_MAJOR}.${KDECONNECT_VERSION_MINOR}.${KDECONNECT_VERSION_PATCH}") cmake_minimum_required(VERSION 2.8.12) find_package(ECM 0.0.9 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_SOURCE_DIR}/cmake) find_package(Qt5 5.2 REQUIRED COMPONENTS Quick Bluetooth) find_package(KF5 REQUIRED COMPONENTS I18n ConfigWidgets DBusAddons) find_package(KF5DocTools) - find_package(Qca-qt5 2.1.0 REQUIRED) include_directories(${CMAKE_SOURCE_DIR}) configure_file(kdeconnect-version.h.in ${CMAKE_CURRENT_BINARY_DIR}/kdeconnect-version.h) include(KDEInstallDirs) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMAddTests) include(ECMSetupVersion) include(ECMInstallIcons) include(FeatureSummary) include(KDEConnectMacros.cmake) add_definitions(-DQT_NO_URL_CAST_FROM_STRING -DQT_NO_KEYWORDS) include(GenerateExportHeader) include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_subdirectory(core) add_subdirectory(kcm) add_subdirectory(kcmplugin) if(NOT WIN32) add_subdirectory(kio) add_subdirectory(plasmoid) endif() add_subdirectory(icon) add_subdirectory(interfaces) option(EXPERIMENTALAPP_ENABLED OFF) if(EXPERIMENTALAPP_ENABLED) add_subdirectory(app) endif() add_subdirectory(daemon) add_subdirectory(plugins) add_subdirectory(cli) add_subdirectory(indicator) add_subdirectory(fileitemactionplugin) if(KF5DocTools_FOUND) add_subdirectory(doc) endif() if(BUILD_TESTING) add_subdirectory(tests) endif() install(FILES org.kde.kdeconnect.kcm.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/core/backends/bluetooth/CMakeLists.txt b/core/backends/bluetooth/CMakeLists.txt index 125fa87a..9933f332 100644 --- a/core/backends/bluetooth/CMakeLists.txt +++ b/core/backends/bluetooth/CMakeLists.txt @@ -1,9 +1,12 @@ set(backends_kdeconnect_SRCS ${backends_kdeconnect_SRCS} backends/bluetooth/bluetoothlinkprovider.cpp backends/bluetooth/bluetoothdevicelink.cpp + backends/bluetooth/bluetoothpairinghandler.cpp + backends/bluetooth/bluetoothdownloadjob.cpp + backends/bluetooth/bluetoothuploadjob.cpp PARENT_SCOPE ) diff --git a/core/backends/bluetooth/bluetoothdevicelink.cpp b/core/backends/bluetooth/bluetoothdevicelink.cpp index e3c1e333..43a14dcd 100644 --- a/core/backends/bluetooth/bluetoothdevicelink.cpp +++ b/core/backends/bluetooth/bluetoothdevicelink.cpp @@ -1,110 +1,106 @@ /** - * Copyright 2015 Saikrishna Arcot + * Copyright 2016 Saikrishna Arcot * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "bluetoothdevicelink.h" #include "../linkprovider.h" +#include "bluetoothlinkprovider.h" +#include "bluetoothuploadjob.h" +#include "bluetoothdownloadjob.h" +#include "core_debug.h" BluetoothDeviceLink::BluetoothDeviceLink(const QString& deviceId, LinkProvider* parent, QBluetoothSocket* socket) : DeviceLink(deviceId, parent) - , mBluetoothSocket(new DeviceLineReader(socket)) + , mSocketReader(new DeviceLineReader(socket, this)) + , mBluetoothSocket(socket) + , mPairingHandler(new BluetoothPairingHandler(this)) { - connect(mBluetoothSocket, SIGNAL(readyRead()), + connect(mSocketReader, SIGNAL(readyRead()), this, SLOT(dataReceived())); //We take ownership of the socket. - //When the link provider distroys us, + //When the link provider destroys us, //the socket (and the reader) will be //destroyed as well - connect(mBluetoothSocket, SIGNAL(disconnected()), - this, SLOT(deleteLater())); mBluetoothSocket->setParent(this); + connect(mBluetoothSocket, SIGNAL(disconnected()), this, SLOT(deleteLater())); } -/*void BluetoothDeviceLink::sendPayload(const QSharedPointer& mInput) +QString BluetoothDeviceLink::name() { - while ( mInput->bytesAvailable() > 0 ) - { - qint64 bytes = qMin(mInput->bytesAvailable(), (qint64)4096); - int w = mBluetoothSocket->write(mInput->read(bytes)); - if (w<0) { - qWarning() << "error when writing data to upload" << bytes << mInput->bytesAvailable(); - break; - } - else - { - while ( mBluetoothSocket->flush() ); - } - } - - mInput->close(); -}*/ - -bool BluetoothDeviceLink::sendPackageEncrypted(QCA::PublicKey& key, NetworkPackage& np) -{ - np.encrypt(key); - return sendPackage(np); + return "BluetoothLink"; // Should be same in both android and kde version } bool BluetoothDeviceLink::sendPackage(NetworkPackage& np) { - int written = mBluetoothSocket->write(np.serialize()); if (np.hasPayload()) { - qWarning() << "Bluetooth backend does not support payloads."; + qCWarning(KDECONNECT_CORE) << "Sending packages with payload over bluetooth not yet supported"; + /* + BluetoothUploadJob* uploadJob = new BluetoothUploadJob(np.payload(), mBluetoothSocket->peerAddress(), this); + np.setPayloadTransferInfo(uploadJob->transferInfo()); + uploadJob->start(); + */ } + int written = mSocketReader->write(np.serialize()); return (written != -1); } -void BluetoothDeviceLink::dataReceived() -{ - - if (mBluetoothSocket->bytesAvailable() == 0) return; +void BluetoothDeviceLink::userRequestsPair() { + mPairingHandler->requestPairing(); +} - const QByteArray package = mBluetoothSocket->readLine(); +void BluetoothDeviceLink::userRequestsUnpair() { + mPairingHandler->unpair(); +} - //kDebug(debugArea()) << "BluetoothDeviceLink dataReceived" << package; +bool BluetoothDeviceLink::linkShouldBeKeptAlive() { + return pairStatus() == Paired; +} - NetworkPackage unserialized(QString::null); - NetworkPackage::unserialize(package, &unserialized); - if (unserialized.isEncrypted()) { - //mPrivateKey should always be set when device link is added to device, no null-checking done here - NetworkPackage decrypted(QString::null); - unserialized.decrypt(mPrivateKey, &decrypted); +void BluetoothDeviceLink::dataReceived() +{ + if (mSocketReader->bytesAvailable() == 0) return; - if (decrypted.hasPayloadTransferInfo()) { - qWarning() << "Bluetooth backend does not support payloads."; - } + const QByteArray serializedPackage = mSocketReader->readLine(); - Q_EMIT receivedPackage(decrypted); + //qCDebug(KDECONNECT_CORE) << "BluetoothDeviceLink dataReceived" << package; - } else { - if (unserialized.hasPayloadTransferInfo()) { - qWarning() << "Ignoring unencrypted payload"; - } + NetworkPackage package(QString::null); + NetworkPackage::unserialize(serializedPackage, &package); - Q_EMIT receivedPackage(unserialized); + if (package.type() == PACKAGE_TYPE_PAIR) { + //TODO: Handle pair/unpair requests and forward them (to the pairing handler?) + mPairingHandler->packageReceived(package); + return; + } + if (package.hasPayloadTransferInfo()) { + BluetoothDownloadJob* downloadJob = new BluetoothDownloadJob(mBluetoothSocket->peerAddress(), + package.payloadTransferInfo(), this); + downloadJob->start(); + package.setPayload(downloadJob->payload(), package.payloadSize()); } - if (mBluetoothSocket->bytesAvailable() > 0) { + Q_EMIT receivedPackage(package); + + if (mSocketReader->bytesAvailable() > 0) { QMetaObject::invokeMethod(this, "dataReceived", Qt::QueuedConnection); } - } diff --git a/core/backends/bluetooth/bluetoothdevicelink.h b/core/backends/bluetooth/bluetoothdevicelink.h index 199d9ee4..64e851e2 100644 --- a/core/backends/bluetooth/bluetoothdevicelink.h +++ b/core/backends/bluetooth/bluetoothdevicelink.h @@ -1,53 +1,60 @@ /** - * Copyright 2015 Saikrishna Arcot + * Copyright 2016 Saikrishna Arcot * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BLUETOOTHDEVICELINK_H #define BLUETOOTHDEVICELINK_H #include #include #include #include "../devicelink.h" #include "../devicelinereader.h" +#include "bluetoothpairinghandler.h" -class BluetoothDeviceLink +class KDECONNECTCORE_EXPORT BluetoothDeviceLink : public DeviceLink { Q_OBJECT public: BluetoothDeviceLink(const QString& deviceId, LinkProvider* parent, QBluetoothSocket* socket); - bool sendPackage(NetworkPackage& np); - bool sendPackageEncrypted(QCA::PublicKey& key, NetworkPackage& np); + virtual QString name() override; + bool sendPackage(NetworkPackage& np) override; + + virtual void userRequestsPair() override; + virtual void userRequestsUnpair() override; + + virtual bool linkShouldBeKeptAlive() override; private Q_SLOTS: void dataReceived(); private: - DeviceLineReader* mBluetoothSocket; + DeviceLineReader* mSocketReader; + QBluetoothSocket* mBluetoothSocket; + BluetoothPairingHandler* mPairingHandler; - //void sendPayload(const QSharedPointer& mInput); void sendMessage(const QString mMessage); }; #endif diff --git a/core/kdeconnectplugin_config.cpp b/core/backends/bluetooth/bluetoothdownloadjob.cpp similarity index 51% rename from core/kdeconnectplugin_config.cpp rename to core/backends/bluetooth/bluetoothdownloadjob.cpp index c59cd553..158abd2c 100644 --- a/core/kdeconnectplugin_config.cpp +++ b/core/backends/bluetooth/bluetoothdownloadjob.cpp @@ -1,49 +1,40 @@ -/** - * Copyright 2013 Albert Vaca +/* + * Copyright 2016 Saikrishna Arcot * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "kdeconnectplugin_config.h" -#include +#include "bluetoothdownloadjob.h" -KdeConnectPluginConfig::KdeConnectPluginConfig(QObject* parent, const QVariantList& args) - : KCModule(SftpConfigFactory::componentData(), parent) - , mGlobalConfig(KSharedConfig::openConfig("kdeconnect/plugins/pausemusic")) +BluetoothDownloadJob::BluetoothDownloadJob(const QBluetoothAddress &remoteAddress, const QVariantMap &transferInfo, QObject *parent) + : QObject(parent) + , mRemoteAddress(remoteAddress) + , mTransferUuid(QBluetoothUuid(transferInfo.value("uuid").toString())) + , mSocket(new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol)) { - KdeConnectKcm* kcm = 0; - QObject* kcmCandidate = parent->parentWidget(); - while (kcmCandidate) { - //qDebug() << kcmCandidate; - if (kcmCandidate->objectName() == "KdeConnectKcm") { - kcm = qobject_cast(kcmCandidate); - break; - } - kcmCandidate = kcmCandidate->parent(); - } - if (kcm) { - kcm->selectedDevice()->id(); - } - } -KdeConnectPluginConfig::~KdeConnectPluginConfig() +QSharedPointer BluetoothDownloadJob::payload() const { + return mSocket.staticCast(); } - - +void BluetoothDownloadJob::start() +{ + connect(mSocket.data(), SIGNAL(disconnected()), this, SLOT(deleteLater())); + mSocket->connectToService(mRemoteAddress, mTransferUuid, QIODevice::ReadOnly); +} diff --git a/core/kdeconnectplugin_config.h b/core/backends/bluetooth/bluetoothdownloadjob.h similarity index 55% rename from core/kdeconnectplugin_config.h rename to core/backends/bluetooth/bluetoothdownloadjob.h index e96a800c..69754702 100644 --- a/core/kdeconnectplugin_config.h +++ b/core/backends/bluetooth/bluetoothdownloadjob.h @@ -1,49 +1,47 @@ -/** - * Copyright 2013 Albert Vaca +/* + * Copyright 2016 Saikrishna Arcot * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef KDECONNECTPLUGINCONFIG_H -#define KDECONNECTPLUGINCONFIG_H +#ifndef BLUETOOTHDOWNLOADJOB_H +#define BLUETOOTHDOWNLOADJOB_H -#include "kdeconnectcore_export.h" -#include +#include +#include +#include +#include +#include +#include +#include -struct DeviceDbusInterface; - -class KDECONNECTCORE_EXPORT KdeConnectPluginConfig - : public KCModule +class BluetoothDownloadJob + : public QObject { Q_OBJECT - public: - KdeConnectPluginConfig(QObject* parent, const QVariantList& args); - virtual ~KdeConnectPluginConfig(); - QString deviceId() { return mDeviceId; } - - //TODO: Add these two to the Plugin as well - KSharedConfigPtr deviceConfig() { return mDeviceConfig; } - KSharedConfigPtr globalConfig() { return mGlobalConfig; } + explicit BluetoothDownloadJob(const QBluetoothAddress &remoteAddress, const QVariantMap &transferInfo, QObject* parent = 0); + QSharedPointer payload() const; + void start(); private: - QString mDeviceId; - KSharedConfigPtr mGlobalConfig; - KSharedConfigPtr mDeviceConfig; + QBluetoothAddress mRemoteAddress; + QBluetoothUuid mTransferUuid; + QSharedPointer mSocket; }; -#endif +#endif // BLUETOOTHDOWNLOADJOB_H diff --git a/core/backends/bluetooth/bluetoothlinkprovider.cpp b/core/backends/bluetooth/bluetoothlinkprovider.cpp index 2b78171b..fca2032d 100644 --- a/core/backends/bluetooth/bluetoothlinkprovider.cpp +++ b/core/backends/bluetooth/bluetoothlinkprovider.cpp @@ -1,367 +1,363 @@ /** - * Copyright 2015 Saikrishna Arcot + * Copyright 2016 Saikrishna Arcot * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "bluetoothlinkprovider.h" #include "core_debug.h" #include #include #include #include #include #include +#include #include "bluetoothdevicelink.h" BluetoothLinkProvider::BluetoothLinkProvider() { - if (!mDevice.isValid()) { - qCWarning(KDECONNECT_CORE) << "No local device found"; - mBluetoothServer = NULL; - return; - } - - mBluetoothServer = new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this); - mBluetoothServer->setSecurityFlags(QBluetooth::Encryption | QBluetooth::Secure); - connect(mBluetoothServer, SIGNAL(newConnection()), this, SLOT(serverNewConnection())); - - mServiceUuid = QBluetoothUuid(QString("576bf9a0-98c9-11e4-bc89-0002a5d5c51b")); + mServiceUuid = QBluetoothUuid(QString("185f3df4-3268-4e3f-9fca-d4d5059915bd")); connectTimer = new QTimer(this); connectTimer->setInterval(30000); connectTimer->setSingleShot(false); #if QT_VERSION < QT_VERSION_CHECK(5, 4, 0) connect(connectTimer, SIGNAL(timeout()), this, SLOT(connectToPairedDevices())); #else mServiceDiscoveryAgent = new QBluetoothServiceDiscoveryAgent(this); mServiceDiscoveryAgent->setUuidFilter(mServiceUuid); connect(mServiceDiscoveryAgent, SIGNAL(finished()), this, SLOT(serviceDiscoveryFinished())); connect(connectTimer, SIGNAL(timeout()), mServiceDiscoveryAgent, SLOT(start())); #endif } void BluetoothLinkProvider::onStart() { - if (!mBluetoothServer) { + QBluetoothLocalDevice localDevice; + if (!localDevice.isValid()) { + qCWarning(KDECONNECT_CORE) << "No local bluetooth adapter found"; return; } + mBluetoothServer = new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this); + mBluetoothServer->setSecurityFlags(QBluetooth::Encryption | QBluetooth::Secure); + connect(mBluetoothServer, SIGNAL(newConnection()), this, SLOT(serverNewConnection())); + #if QT_VERSION < QT_VERSION_CHECK(5, 4, 0) connectToPairedDevices(); #else mServiceDiscoveryAgent->start(); #endif connectTimer->start(); mKdeconnectService = mBluetoothServer->listen(mServiceUuid, "KDE Connect"); - - onNetworkChange(QNetworkSession::Connected); } void BluetoothLinkProvider::onStop() { if (!mBluetoothServer) { return; } connectTimer->stop(); mKdeconnectService.unregisterService(); mBluetoothServer->close(); + mBluetoothServer->deleteLater(); } //I'm in a new network, let's be polite and introduce myself -void BluetoothLinkProvider::onNetworkChange(QNetworkSession::State state) +void BluetoothLinkProvider::onNetworkChange() { - Q_UNUSED(state); } +#if QT_VERSION < QT_VERSION_CHECK(5, 4, 0) QList BluetoothLinkProvider::getPairedDevices() { QDBusConnection bus = QDBusConnection::systemBus(); QDBusInterface manager_iface("org.bluez", "/","org.bluez.Manager", bus); QDBusReply devices = manager_iface.call("DefaultAdapter"); if (!devices.isValid()) { qCWarning(KDECONNECT_CORE) << "Couldn't get default adapter:" << devices.error(); return QList(); } QDBusObjectPath defaultAdapter = devices.value(); QString defaultAdapterPath = defaultAdapter.path(); QDBusInterface devices_iface("org.bluez", defaultAdapterPath, "org.bluez.Adapter", bus); QDBusMessage pairedDevices = devices_iface.call("ListDevices"); QDBusArgument pairedDevicesArg = pairedDevices.arguments().at(0).value(); pairedDevicesArg.beginArray(); QList pairedDevicesList; while (!pairedDevicesArg.atEnd()) { QVariant variant = pairedDevicesArg.asVariant(); QDBusObjectPath pairedDevice = qvariant_cast(variant); QString pairedDevicePath = pairedDevice.path(); QString pairedDeviceMac = pairedDevicePath.split(QChar('/')).last().remove("dev_").replace("_", ":"); pairedDevicesList.append(QBluetoothAddress(pairedDeviceMac)); } return pairedDevicesList; } void BluetoothLinkProvider::connectToPairedDevices() { QList pairedDevices = getPairedDevices(); for (int i = 0; i < pairedDevices.size(); i++) { QBluetoothAddress pairedDevice = pairedDevices.at(i); if (mSockets.contains(pairedDevice)) { continue; } QBluetoothSocket* socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, this); connect(socket, SIGNAL(connected()), this, SLOT(clientConnected())); connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); qCDebug(KDECONNECT_CORE()) << "BluetoothLinkProvider/onStart: Connecting to " << pairedDevice.toString(); socket->connectToService(pairedDevice, mServiceUuid); } } +#endif void BluetoothLinkProvider::connectError() { QBluetoothSocket* socket = qobject_cast(sender()); if (!socket) return; - qCWarning(KDECONNECT_CORE) << "connectError: Couldn't connect to socket:" << socket->errorString(); + qCWarning(KDECONNECT_CORE) << "Couldn't connect to socket:" << socket->errorString(); disconnect(socket, SIGNAL(connected()), this, SLOT(clientConnected())); disconnect(socket, SIGNAL(readyRead()), this, SLOT(serverDataReceived())); disconnect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); mSockets.remove(socket->peerAddress()); socket->deleteLater(); } void BluetoothLinkProvider::addLink(BluetoothDeviceLink* deviceLink, const QString& deviceId) { QMap< QString, DeviceLink* >::iterator oldLinkIterator = mLinks.find(deviceId); if (oldLinkIterator != mLinks.end()) { DeviceLink* oldLink = oldLinkIterator.value(); disconnect(oldLink, SIGNAL(destroyed(QObject*)), this, SLOT(deviceLinkDestroyed(QObject*))); oldLink->deleteLater(); mLinks.erase(oldLinkIterator); } mLinks[deviceId] = deviceLink; } //I'm the new device and I found an existing device void BluetoothLinkProvider::serviceDiscoveryFinished() { QList services = mServiceDiscoveryAgent->discoveredServices(); - foreach (QBluetoothServiceInfo info, services) { + Q_FOREACH (QBluetoothServiceInfo info, services) { if (mSockets.contains(info.device().address())) { continue; } QBluetoothSocket* socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol); connect(socket, SIGNAL(connected()), this, SLOT(clientConnected())); connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); socket->connectToService(info); - qCDebug(KDECONNECT_CORE()) << "BluetoothLinkProvider/serviceDiscoveryFinished: Connecting to " << socket->peerAddress().toString(); + qCDebug(KDECONNECT_CORE()) << "Connecting to" << info.device().address(); if (socket->error() != QBluetoothSocket::NoSocketError) { - qCWarning(KDECONNECT_CORE) << "BluetoothLinkProvider/serviceDiscoveryFinished: Socket connection error: " << socket->errorString(); + qCWarning(KDECONNECT_CORE) << "Socket connection error:" << socket->errorString(); } } } //I'm the new device and I'm connected to the existing device. Time to get data. void BluetoothLinkProvider::clientConnected() { QBluetoothSocket* socket = qobject_cast(sender()); if (!socket) return; - qCDebug(KDECONNECT_CORE()) << "BluetoothLinkProvider/clientConnected: Connected to " << socket->peerAddress().toString(); + qCDebug(KDECONNECT_CORE()) << "Connected to" << socket->peerAddress(); if (mSockets.contains(socket->peerAddress())) { - qCDebug(KDECONNECT_CORE()) << "BluetoothLinkProvider/clientConnected: Duplicate connection to " << socket->peerAddress().toString(); + qCWarning(KDECONNECT_CORE()) << "Duplicate connection to" << socket->peerAddress(); socket->close(); socket->deleteLater(); return; } mSockets.insert(socket->peerAddress(), socket); disconnect(socket, SIGNAL(connected()), this, SLOT(clientConnected())); connect(socket, SIGNAL(readyRead()), this, SLOT(clientIdentityReceived())); connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); } //I'm the new device and the existing device sent me data. void BluetoothLinkProvider::clientIdentityReceived() { QBluetoothSocket* socket = qobject_cast(sender()); if (!socket) return; QByteArray identityArray; while (socket->bytesAvailable() > 0 || !identityArray.contains('\n')) { identityArray += socket->readAll(); } disconnect(socket, SIGNAL(readyRead()), this, SLOT(clientIdentityReceived())); NetworkPackage receivedPackage(""); bool success = NetworkPackage::unserialize(identityArray, &receivedPackage); if (!success || receivedPackage.type() != PACKAGE_TYPE_IDENTITY) { - qCDebug(KDECONNECT_CORE) << "BluetoothLinkProvider/clientIdentityReceived: Not an identification package (wuh?)"; + qCWarning(KDECONNECT_CORE) << "Not an identity package"; mSockets.remove(socket->peerAddress()); socket->close(); socket->deleteLater(); return; } - qCDebug(KDECONNECT_CORE()) << "BluetoothLinkProvider/clientIdentityReceived: Received identity package from " << socket->peerAddress().toString(); + qCDebug(KDECONNECT_CORE()) << "Received identity package from" << socket->peerAddress(); disconnect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); const QString& deviceId = receivedPackage.get("deviceId"); BluetoothDeviceLink* deviceLink = new BluetoothDeviceLink(deviceId, this, socket); NetworkPackage np2(""); NetworkPackage::createIdentityPackage(&np2); success = deviceLink->sendPackage(np2); if (success) { qCDebug(KDECONNECT_CORE) << "Handshaking done (I'm the new device)"; connect(deviceLink, SIGNAL(destroyed(QObject*)), this, SLOT(deviceLinkDestroyed(QObject*))); Q_EMIT onConnectionReceived(receivedPackage, deviceLink); //We kill any possible link from this same device addLink(deviceLink, deviceId); } else { // Connection might be lost. Delete it. delete deviceLink; } //We don't delete the socket because now it's owned by the BluetoothDeviceLink } //I'm the existing device, a new device is kindly introducing itself. void BluetoothLinkProvider::serverNewConnection() { QBluetoothSocket* socket = mBluetoothServer->nextPendingConnection(); - qCDebug(KDECONNECT_CORE()) << "BluetoothLinkProvider/serverNewConnection:" << "Received connection from " << socket->peerAddress().toString(); + qCDebug(KDECONNECT_CORE()) << "Received connection from" << socket->peerAddress(); if (mSockets.contains(socket->peerAddress())) { - qCDebug(KDECONNECT_CORE()) << "BluetoothLinkProvider/serverNewConnection: Duplicate connection from " << socket->peerAddress().toString(); + qCDebug(KDECONNECT_CORE()) << "Duplicate connection from" << socket->peerAddress(); socket->close(); socket->deleteLater(); return; } connect(socket, SIGNAL(readyRead()), this, SLOT(serverDataReceived())); connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); NetworkPackage np2(""); NetworkPackage::createIdentityPackage(&np2); socket->write(np2.serialize()); - qCDebug(KDECONNECT_CORE()) << "BluetoothLinkProvider/serverNewConnection: Sent identity package to " << socket->peerAddress().toString(); + qCDebug(KDECONNECT_CORE()) << "Sent identity package to" << socket->peerAddress(); mSockets.insert(socket->peerAddress(), socket); } //I'm the existing device and this is the answer to my identity package (data received) void BluetoothLinkProvider::serverDataReceived() { QBluetoothSocket* socket = qobject_cast(sender()); if (!socket) return; QByteArray identityArray; while (socket->bytesAvailable() > 0 || !identityArray.contains('\n')) { identityArray += socket->readAll(); } disconnect(socket, SIGNAL(readyRead()), this, SLOT(serverDataReceived())); disconnect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); NetworkPackage receivedPackage(""); bool success = NetworkPackage::unserialize(identityArray, &receivedPackage); if (!success || receivedPackage.type() != PACKAGE_TYPE_IDENTITY) { - qCDebug(KDECONNECT_CORE) << "BluetoothLinkProvider/serverDataReceived: Not an identity package."; + qCWarning(KDECONNECT_CORE) << "Not an identity package."; mSockets.remove(socket->peerAddress()); socket->close(); socket->deleteLater(); return; } - qCDebug(KDECONNECT_CORE()) << "BluetoothLinkProvider/serverDataReceived: Received identity package from " << socket->peerAddress().toString(); + qCDebug(KDECONNECT_CORE()) << "Received identity package from" << socket->peerAddress(); const QString& deviceId = receivedPackage.get("deviceId"); BluetoothDeviceLink* deviceLink = new BluetoothDeviceLink(deviceId, this, socket); connect(deviceLink, SIGNAL(destroyed(QObject*)), this, SLOT(deviceLinkDestroyed(QObject*))); Q_EMIT onConnectionReceived(receivedPackage, deviceLink); addLink(deviceLink, deviceId); } void BluetoothLinkProvider::deviceLinkDestroyed(QObject* destroyedDeviceLink) { - //kDebug(debugArea()) << "deviceLinkDestroyed"; const QString id = destroyedDeviceLink->property("deviceId").toString(); qCDebug(KDECONNECT_CORE()) << "Device disconnected:" << id; QMap< QString, DeviceLink* >::iterator oldLinkIterator = mLinks.find(id); if (oldLinkIterator != mLinks.end() && oldLinkIterator.value() == destroyedDeviceLink) { mLinks.erase(oldLinkIterator); } } void BluetoothLinkProvider::socketDisconnected() { QBluetoothSocket* socket = qobject_cast(sender()); if (!socket) return; disconnect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); mSockets.remove(mSockets.key(socket)); } BluetoothLinkProvider::~BluetoothLinkProvider() { } diff --git a/core/backends/bluetooth/bluetoothlinkprovider.h b/core/backends/bluetooth/bluetoothlinkprovider.h index ee418886..bced5917 100644 --- a/core/backends/bluetooth/bluetoothlinkprovider.h +++ b/core/backends/bluetooth/bluetoothlinkprovider.h @@ -1,82 +1,85 @@ /** - * Copyright 2015 Saikrishna Arcot + * Copyright 2016 Saikrishna Arcot * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BLUETOOTHLINKPROVIDER_H #define BLUETOOTHLINKPROVIDER_H #include +#include #include #include #include #include #include #include #include #include #include "../linkprovider.h" class BluetoothDeviceLink; -class BluetoothLinkProvider +class KDECONNECTCORE_EXPORT BluetoothLinkProvider : public LinkProvider { Q_OBJECT public: BluetoothLinkProvider(); - ~BluetoothLinkProvider(); + virtual ~BluetoothLinkProvider(); + + QString name() override { return "BluetoothLinkProvider"; } + int priority() override { return PRIORITY_MEDIUM; } - QString name() { return "BluetoothLinkProvider"; } - int priority() { return PRIORITY_MEDIUM; } public Q_SLOTS: - virtual void onNetworkChange(QNetworkSession::State state); - virtual void onStart(); - virtual void onStop(); + virtual void onNetworkChange() override; + virtual void onStart() override; + virtual void onStop() override; void connectError(); void serviceDiscoveryFinished(); private Q_SLOTS: void deviceLinkDestroyed(QObject* destroyedDeviceLink); void socketDisconnected(); +#if QT_VERSION < QT_VERSION_CHECK(5, 4, 0) void connectToPairedDevices(); +#endif void serverNewConnection(); void serverDataReceived(); void clientConnected(); void clientIdentityReceived(); private: void addLink(BluetoothDeviceLink* deviceLink, const QString& deviceId); QList getPairedDevices(); - QBluetoothLocalDevice mDevice; QBluetoothUuid mServiceUuid; - QBluetoothServer* mBluetoothServer; + QPointer mBluetoothServer; QBluetoothServiceInfo mKdeconnectService; QBluetoothServiceDiscoveryAgent* mServiceDiscoveryAgent; QTimer* connectTimer; QMap mLinks; QMap mSockets; }; #endif diff --git a/core/backends/bluetooth/bluetoothpairinghandler.cpp b/core/backends/bluetooth/bluetoothpairinghandler.cpp new file mode 100644 index 00000000..fa6ee7a8 --- /dev/null +++ b/core/backends/bluetooth/bluetoothpairinghandler.cpp @@ -0,0 +1,150 @@ +/** + * Copyright 2015 Vineet Garg + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "core_debug.h" +#include "daemon.h" +#include "kdeconnectconfig.h" +#include "bluetoothpairinghandler.h" +#include "networkpackagetypes.h" + +BluetoothPairingHandler::BluetoothPairingHandler(DeviceLink* deviceLink) + : PairingHandler(deviceLink) + , m_status(NotPaired) +{ + m_pairingTimeout.setSingleShot(true); + m_pairingTimeout.setInterval(30 * 1000); //30 seconds of timeout + connect(&m_pairingTimeout, &QTimer::timeout, this, &BluetoothPairingHandler::pairingTimeout); +} + +void BluetoothPairingHandler::packageReceived(const NetworkPackage& np) +{ + qCDebug(KDECONNECT_CORE) << "Pairing package received!" << np.serialize(); + + m_pairingTimeout.stop(); + + bool wantsPair = np.get("pair"); + + if (wantsPair) { + + if (isPairRequested()) { //We started pairing + + qCDebug(KDECONNECT_CORE) << "Pair answer"; + setInternalPairStatus(Paired); + + } else { + qCDebug(KDECONNECT_CORE) << "Pair request"; + + if (isPaired()) { //I'm already paired, but they think I'm not + acceptPairing(); + return; + } + + Daemon::instance()->askPairingConfirmation(this); + setInternalPairStatus(RequestedByPeer); + } + + } else { //wantsPair == false + + qCDebug(KDECONNECT_CORE) << "Unpair request"; + + setInternalPairStatus(NotPaired); + if (isPairRequested()) { + Q_EMIT pairingError(i18n("Canceled by other peer")); + } + } +} + +bool BluetoothPairingHandler::requestPairing() +{ + switch (m_status) { + case Paired: + Q_EMIT pairingError(i18n("%1: Already paired", deviceLink()->name())); + return false; + case Requested: + Q_EMIT pairingError(i18n("%1: Pairing already requested for this device", deviceLink()->name())); + return false; + case RequestedByPeer: + qCDebug(KDECONNECT_CORE) << deviceLink()->name() << " : Pairing already started by the other end, accepting their request."; + acceptPairing(); + return false; + case NotPaired: + ; + } + + NetworkPackage np(PACKAGE_TYPE_PAIR); + np.set("pair", true); + bool success; + success = deviceLink()->sendPackage(np); + if (success) { + setInternalPairStatus(Requested); + m_pairingTimeout.start(); + } + return success; +} + +bool BluetoothPairingHandler::acceptPairing() +{ + qCDebug(KDECONNECT_CORE) << "User accepts pairing"; + m_pairingTimeout.stop(); // Just in case it is started + NetworkPackage np(PACKAGE_TYPE_PAIR); + np.set("pair", true); + bool success = deviceLink()->sendPackage(np); + if (success) { + setInternalPairStatus(Paired); + } + return success; +} + +void BluetoothPairingHandler::rejectPairing() +{ + qCDebug(KDECONNECT_CORE) << "User rejects pairing"; + NetworkPackage np(PACKAGE_TYPE_PAIR); + np.set("pair", false); + deviceLink()->sendPackage(np); + setInternalPairStatus(NotPaired); +} + +void BluetoothPairingHandler::unpair() { + NetworkPackage np(PACKAGE_TYPE_PAIR); + np.set("pair", false); + deviceLink()->sendPackage(np); + setInternalPairStatus(NotPaired); +} + +void BluetoothPairingHandler::pairingTimeout() +{ + NetworkPackage np(PACKAGE_TYPE_PAIR); + np.set("pair", false); + deviceLink()->sendPackage(np); + setInternalPairStatus(NotPaired); //Will emit the change as well + Q_EMIT pairingError(i18n("Timed out")); +} + +void BluetoothPairingHandler::setInternalPairStatus(BluetoothPairingHandler::InternalPairStatus status) +{ + m_status = status; + if (status == Paired) { + deviceLink()->setPairStatus(DeviceLink::Paired); + } else { + deviceLink()->setPairStatus(DeviceLink::NotPaired); + } +} diff --git a/core/backends/bluetooth/bluetoothpairinghandler.h b/core/backends/bluetooth/bluetoothpairinghandler.h new file mode 100644 index 00000000..f716952e --- /dev/null +++ b/core/backends/bluetooth/bluetoothpairinghandler.h @@ -0,0 +1,67 @@ +/** + * Copyright 2015 Vineet Garg + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KDECONNECT_LANPAIRINGHANDLER_H +#define KDECONNECT_LANPAIRINGHANDLER_H + +#include "device.h" +#include "../devicelink.h" +#include "../pairinghandler.h" + +#include + +// This class is used pairing related stuff. It has direct access to links and can directly send packages +class BluetoothPairingHandler + : public PairingHandler +{ +public: + + enum InternalPairStatus { + NotPaired, + Requested, + RequestedByPeer, + Paired, + }; + + BluetoothPairingHandler(DeviceLink* deviceLink); + virtual ~BluetoothPairingHandler() { } + + virtual void packageReceived(const NetworkPackage& np) Q_DECL_OVERRIDE; + virtual bool requestPairing() Q_DECL_OVERRIDE; + virtual bool acceptPairing() Q_DECL_OVERRIDE; + virtual void rejectPairing() Q_DECL_OVERRIDE; + virtual void unpair() Q_DECL_OVERRIDE; + + bool isPairRequested() const { return m_status == Requested; } + bool isPaired() const { return m_status == Paired; } + +private Q_SLOTS: + void pairingTimeout(); + +protected: + void setInternalPairStatus(InternalPairStatus status); + + QTimer m_pairingTimeout; + + InternalPairStatus m_status; +}; + + +#endif //KDECONNECT_LANPAIRINGHANDLER_H diff --git a/core/backends/bluetooth/bluetoothuploadjob.cpp b/core/backends/bluetooth/bluetoothuploadjob.cpp new file mode 100644 index 00000000..17c7f5f4 --- /dev/null +++ b/core/backends/bluetooth/bluetoothuploadjob.cpp @@ -0,0 +1,85 @@ +/* + * Copyright 2016 Saikrishna Arcot + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "bluetoothuploadjob.h" + +#include +#include "core_debug.h" +#include + +BluetoothUploadJob::BluetoothUploadJob(const QSharedPointer &data, const QBluetoothAddress &remoteAddress, QObject *parent) + : QObject(parent) + , mData(data) + , mRemoteAddress(remoteAddress) + , mTransferUuid(QBluetoothUuid::createUuid()) + , mServer(new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this)) +{ + mServer->setSecurityFlags(QBluetooth::Encryption | QBluetooth::Secure); +} + +QVariantMap BluetoothUploadJob::transferInfo() const +{ + QVariantMap ret; + ret["uuid"] = mTransferUuid.toString().mid(1, 36); + return ret; +} + +void BluetoothUploadJob::start() +{ + connect(mServer, SIGNAL(newConnection()), this, SLOT(newConnection())); + mServiceInfo = mServer->listen(mTransferUuid, "KDE Connect Transfer Job"); + Q_ASSERT(mServiceInfo.isValid()); +} + +void BluetoothUploadJob::newConnection() +{ + QBluetoothSocket* socket = mServer->nextPendingConnection(); + connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater())); + + if (socket->peerAddress() != mRemoteAddress) { + socket->close(); + } else { + mServer->close(); + disconnect(mServer, SIGNAL(newConnection()), this, SLOT(newConnection())); + mServiceInfo.unregisterService(); + + if (!mData->open(QIODevice::ReadOnly)) { + qCWarning(KDECONNECT_CORE) << "error when opening the input to upload"; + socket->close(); + deleteLater(); + return; + } + } + + while (mData->bytesAvailable() > 0 && socket->isWritable()) { + qint64 bytes = qMin(mData->bytesAvailable(), (qint64)4096); + int w = socket->write(mData->read(bytes)); + if (w < 0) { + qCWarning(KDECONNECT_CORE()) << "error when writing data to upload" << bytes << mData->bytesAvailable(); + break; + } else { + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 2000); + } + } + + mData->close(); + socket->close(); + deleteLater(); +} diff --git a/core/backends/devicelinereader.h b/core/backends/bluetooth/bluetoothuploadjob.h similarity index 54% copy from core/backends/devicelinereader.h copy to core/backends/bluetooth/bluetoothuploadjob.h index a5255c77..7621f6b5 100644 --- a/core/backends/devicelinereader.h +++ b/core/backends/bluetooth/bluetoothuploadjob.h @@ -1,59 +1,53 @@ -/** - * Copyright 2013 Albert Vaca +/* + * Copyright 2016 Saikrishna Arcot * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef DEVICELINEREADER_H -#define DEVICELINEREADER_H +#ifndef BLUETOOTHUPLOADJOB_H +#define BLUETOOTHUPLOADJOB_H -#include -#include -#include #include - -/* - * Encapsulates a QIODevice and implements the same methods of its API that are - * used by LanDeviceLink, but readyRead is emitted only when a newline is found. - */ -class DeviceLineReader +#include +#include +#include +#include +#include +#include + +class BluetoothUploadJob : public QObject { Q_OBJECT - public: - DeviceLineReader(QIODevice* device, QObject* parent = 0); + explicit BluetoothUploadJob(const QSharedPointer &data, const QBluetoothAddress &remoteAddress, QObject* parent = 0); - QByteArray readLine() { return mPackages.dequeue(); } - qint64 write(const QByteArray& data) { return mDevice->write(data); } - qint64 bytesAvailable() { return mPackages.size(); } - -Q_SIGNALS: - void readyRead(); - void disconnected(); - -private Q_SLOTS: - void dataReceived(); + QVariantMap transferInfo() const; + void start(); private: - QByteArray lastChunk; - QIODevice* mDevice; - QQueue mPackages; + QSharedPointer mData; + QBluetoothAddress mRemoteAddress; + QBluetoothUuid mTransferUuid; + QBluetoothServer* mServer; + QBluetoothServiceInfo mServiceInfo; +private Q_SLOTS: + void newConnection(); }; -#endif +#endif // BLUETOOTHUPLOADJOB_H diff --git a/core/backends/devicelinereader.h b/core/backends/devicelinereader.h index a5255c77..8fe66296 100644 --- a/core/backends/devicelinereader.h +++ b/core/backends/devicelinereader.h @@ -1,59 +1,60 @@ /** * Copyright 2013 Albert Vaca * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DEVICELINEREADER_H #define DEVICELINEREADER_H #include #include #include #include /* * Encapsulates a QIODevice and implements the same methods of its API that are - * used by LanDeviceLink, but readyRead is emitted only when a newline is found. + * used by LanDeviceLink and BluetoothDeviceLink, but readyRead is emitted only + * when a newline is found. */ class DeviceLineReader : public QObject { Q_OBJECT public: DeviceLineReader(QIODevice* device, QObject* parent = 0); QByteArray readLine() { return mPackages.dequeue(); } qint64 write(const QByteArray& data) { return mDevice->write(data); } - qint64 bytesAvailable() { return mPackages.size(); } + qint64 bytesAvailable() const { return mPackages.size(); } Q_SIGNALS: void readyRead(); void disconnected(); private Q_SLOTS: void dataReceived(); private: QByteArray lastChunk; QIODevice* mDevice; QQueue mPackages; }; #endif diff --git a/core/daemon.cpp b/core/daemon.cpp index aab240e3..c4536237 100644 --- a/core/daemon.cpp +++ b/core/daemon.cpp @@ -1,284 +1,281 @@ /** * Copyright 2013 Albert Vaca * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "daemon.h" #include #include #include #include #include "core_debug.h" #include "kdeconnectconfig.h" #include "networkpackage.h" #include "backends/bluetooth/bluetoothlinkprovider.h" #include "backends/lan/lanlinkprovider.h" #include "backends/loopback/loopbacklinkprovider.h" #include "device.h" #include "backends/devicelink.h" #include "backends/linkprovider.h" static Daemon* s_instance = nullptr; struct DaemonPrivate { //Different ways to find devices and connect to them QSet mLinkProviders; //Every known device QMap mDevices; QSet mDiscoveryModeAcquisitions; }; Daemon* Daemon::instance() { Q_ASSERT(s_instance != nullptr); return s_instance; } Daemon::Daemon(QObject *parent, bool testMode) : QObject(parent) , d(new DaemonPrivate) { Q_ASSERT(!s_instance); s_instance = this; qCDebug(KDECONNECT_CORE) << "KdeConnect daemon starting"; //Load backends if (testMode) d->mLinkProviders.insert(new LoopbackLinkProvider()); - else + else { d->mLinkProviders.insert(new LanLinkProvider()); + d->mLinkProviders.insert(new BluetoothLinkProvider()); + } //Read remebered paired devices const QStringList& list = KdeConnectConfig::instance()->trustedDevices(); Q_FOREACH (const QString& id, list) { addDevice(new Device(this, id)); } //Listen to new devices Q_FOREACH (LinkProvider* a, d->mLinkProviders) { connect(a, &LinkProvider::onConnectionReceived, this, &Daemon::onNewDeviceLink); a->onStart(); } //Register on DBus QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.kdeconnect")); QDBusConnection::sessionBus().registerObject(QStringLiteral("/modules/kdeconnect"), this, QDBusConnection::ExportScriptableContents); qCDebug(KDECONNECT_CORE) << "KdeConnect daemon started"; } void Daemon::acquireDiscoveryMode(const QString &key) { bool oldState = d->mDiscoveryModeAcquisitions.isEmpty(); d->mDiscoveryModeAcquisitions.insert(key); if (oldState != d->mDiscoveryModeAcquisitions.isEmpty()) { forceOnNetworkChange(); } } void Daemon::releaseDiscoveryMode(const QString &key) { bool oldState = d->mDiscoveryModeAcquisitions.isEmpty(); d->mDiscoveryModeAcquisitions.remove(key); - //Load backends - //d->mLinkProviders.insert(new LoopbackLinkProvider()); - d->mLinkProviders.insert(new LanLinkProvider()); - d->mLinkProviders.insert(new BluetoothLinkProvider()); - if (oldState != d->mDiscoveryModeAcquisitions.isEmpty()) { cleanDevices(); } } void Daemon::removeDevice(Device* device) { d->mDevices.remove(device->id()); device->deleteLater(); Q_EMIT deviceRemoved(device->id()); } void Daemon::cleanDevices() { Q_FOREACH (Device* device, d->mDevices) { if (device->isTrusted()) { continue; } device->cleanUnneededLinks(); //If there are no links remaining if (!device->isReachable()) { removeDevice(device); } } } void Daemon::forceOnNetworkChange() { qCDebug(KDECONNECT_CORE) << "Sending onNetworkChange to " << d->mLinkProviders.size() << " LinkProviders"; Q_FOREACH (LinkProvider* a, d->mLinkProviders) { a->onNetworkChange(); } } Device*Daemon::getDevice(const QString& deviceId) { Q_FOREACH (Device* device, d->mDevices) { if (device->id() == deviceId) { return device; } } return Q_NULLPTR; } QStringList Daemon::devices(bool onlyReachable, bool onlyTrusted) const { QStringList ret; Q_FOREACH (Device* device, d->mDevices) { if (onlyReachable && !device->isReachable()) continue; if (onlyTrusted && !device->isTrusted()) continue; ret.append(device->id()); } return ret; } void Daemon::onNewDeviceLink(const NetworkPackage& identityPackage, DeviceLink* dl) { const QString& id = identityPackage.get(QStringLiteral("deviceId")); //qCDebug(KDECONNECT_CORE) << "Device discovered" << id << "via" << dl->provider()->name(); if (d->mDevices.contains(id)) { qCDebug(KDECONNECT_CORE) << "It is a known device" << identityPackage.get(QStringLiteral("deviceName")); Device* device = d->mDevices[id]; bool wasReachable = device->isReachable(); device->addLink(identityPackage, dl); if (!wasReachable) { Q_EMIT deviceVisibilityChanged(id, true); } } else { qCDebug(KDECONNECT_CORE) << "It is a new device" << identityPackage.get(QStringLiteral("deviceName")); Device* device = new Device(this, identityPackage, dl); //we discard the connections that we created but it's not paired. if (!isDiscoveringDevices() && !device->isTrusted() && !dl->linkShouldBeKeptAlive()) { device->deleteLater(); } else { addDevice(device); } } } void Daemon::onDeviceStatusChanged() { Device* device = (Device*)sender(); //qCDebug(KDECONNECT_CORE) << "Device" << device->name() << "status changed. Reachable:" << device->isReachable() << ". Paired: " << device->isPaired(); if (!device->isReachable() && !device->isTrusted()) { //qCDebug(KDECONNECT_CORE) << "Destroying device" << device->name(); removeDevice(device); } else { Q_EMIT deviceVisibilityChanged(device->id(), device->isReachable()); } } void Daemon::setAnnouncedName(const QString &name) { qCDebug(KDECONNECT_CORE()) << "Announcing name"; KdeConnectConfig::instance()->setName(name); forceOnNetworkChange(); Q_EMIT announcedNameChanged(name); } QString Daemon::announcedName() { return KdeConnectConfig::instance()->name(); } QNetworkAccessManager* Daemon::networkAccessManager() { static QPointer manager; if (!manager) { manager = new QNetworkAccessManager(this); } return manager; } QList Daemon::devicesList() const { return d->mDevices.values(); } bool Daemon::isDiscoveringDevices() const { return !d->mDiscoveryModeAcquisitions.isEmpty(); } QString Daemon::deviceIdByName(const QString &name) const { Q_FOREACH (Device* d, d->mDevices) { if (d->name() == name && d->isTrusted()) return d->id(); } return {}; } void Daemon::addDevice(Device* device) { const QString id = device->id(); connect(device, &Device::reachableChanged, this, &Daemon::onDeviceStatusChanged); connect(device, &Device::trustedChanged, this, &Daemon::onDeviceStatusChanged); connect(device, &Device::hasPairingRequestsChanged, this, &Daemon::pairingRequestsChanged); connect(device, &Device::hasPairingRequestsChanged, this, [this, device](bool hasPairingRequests) { if (hasPairingRequests) askPairingConfirmation(device); } ); d->mDevices[id] = device; Q_EMIT deviceAdded(id); } QStringList Daemon::pairingRequests() const { QStringList ret; for(Device* dev: d->mDevices) { if (dev->hasPairingRequests()) ret += dev->id(); } return ret; } Daemon::~Daemon() { } QString Daemon::selfId() const { return KdeConnectConfig::instance()->deviceId(); }