diff --git a/CMakeLists.txt b/CMakeLists.txt index 80196d5f5..638cfd9dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,9 +10,11 @@ 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) +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}) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 0b1609634..e73155a3e 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -8,6 +8,7 @@ include_directories( add_subdirectory(backends/lan) add_subdirectory(backends/loopback) +add_subdirectory(backends/bluetooth) find_package(KF5Notifications 5.9 REQUIRED) @@ -17,6 +18,7 @@ set(kdeconnectcore_SRCS backends/linkprovider.cpp backends/devicelink.cpp backends/pairinghandler.cpp + backends/devicelinereader.cpp kdeconnectplugin.cpp kdeconnectpluginconfig.cpp @@ -37,6 +39,7 @@ PUBLIC Qt5::Network KF5::CoreAddons qca-qt5 + Qt5::Bluetooth PRIVATE Qt5::DBus Qt5::Gui diff --git a/core/backends/bluetooth/CMakeLists.txt b/core/backends/bluetooth/CMakeLists.txt new file mode 100644 index 000000000..125fa87a8 --- /dev/null +++ b/core/backends/bluetooth/CMakeLists.txt @@ -0,0 +1,9 @@ + +set(backends_kdeconnect_SRCS + ${backends_kdeconnect_SRCS} + + backends/bluetooth/bluetoothlinkprovider.cpp + backends/bluetooth/bluetoothdevicelink.cpp + + PARENT_SCOPE +) diff --git a/core/backends/bluetooth/bluetoothdevicelink.cpp b/core/backends/bluetooth/bluetoothdevicelink.cpp new file mode 100644 index 000000000..e3c1e3335 --- /dev/null +++ b/core/backends/bluetooth/bluetoothdevicelink.cpp @@ -0,0 +1,110 @@ +/** + * Copyright 2015 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" + +BluetoothDeviceLink::BluetoothDeviceLink(const QString& deviceId, LinkProvider* parent, QBluetoothSocket* socket) + : DeviceLink(deviceId, parent) + , mBluetoothSocket(new DeviceLineReader(socket)) +{ + connect(mBluetoothSocket, SIGNAL(readyRead()), + this, SLOT(dataReceived())); + + //We take ownership of the socket. + //When the link provider distroys us, + //the socket (and the reader) will be + //destroyed as well + connect(mBluetoothSocket, SIGNAL(disconnected()), + this, SLOT(deleteLater())); + mBluetoothSocket->setParent(this); +} + +/*void BluetoothDeviceLink::sendPayload(const QSharedPointer& mInput) +{ + 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); +} + +bool BluetoothDeviceLink::sendPackage(NetworkPackage& np) +{ + int written = mBluetoothSocket->write(np.serialize()); + if (np.hasPayload()) { + qWarning() << "Bluetooth backend does not support payloads."; + } + return (written != -1); +} + +void BluetoothDeviceLink::dataReceived() +{ + + if (mBluetoothSocket->bytesAvailable() == 0) return; + + const QByteArray package = mBluetoothSocket->readLine(); + + //kDebug(debugArea()) << "BluetoothDeviceLink dataReceived" << package; + + 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); + + if (decrypted.hasPayloadTransferInfo()) { + qWarning() << "Bluetooth backend does not support payloads."; + } + + Q_EMIT receivedPackage(decrypted); + + } else { + if (unserialized.hasPayloadTransferInfo()) { + qWarning() << "Ignoring unencrypted payload"; + } + + Q_EMIT receivedPackage(unserialized); + + } + + if (mBluetoothSocket->bytesAvailable() > 0) { + QMetaObject::invokeMethod(this, "dataReceived", Qt::QueuedConnection); + } + +} diff --git a/core/backends/bluetooth/bluetoothdevicelink.h b/core/backends/bluetooth/bluetoothdevicelink.h new file mode 100644 index 000000000..199d9ee4c --- /dev/null +++ b/core/backends/bluetooth/bluetoothdevicelink.h @@ -0,0 +1,53 @@ +/** + * Copyright 2015 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" + +class 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); + +private Q_SLOTS: + void dataReceived(); + +private: + DeviceLineReader* mBluetoothSocket; + + //void sendPayload(const QSharedPointer& mInput); + void sendMessage(const QString mMessage); + +}; + +#endif diff --git a/core/backends/bluetooth/bluetoothlinkprovider.cpp b/core/backends/bluetooth/bluetoothlinkprovider.cpp new file mode 100644 index 000000000..2b78171bb --- /dev/null +++ b/core/backends/bluetooth/bluetoothlinkprovider.cpp @@ -0,0 +1,367 @@ +/** + * Copyright 2015 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 "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")); + + 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) { + return; + } + +#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(); +} + +//I'm in a new network, let's be polite and introduce myself +void BluetoothLinkProvider::onNetworkChange(QNetworkSession::State state) +{ + Q_UNUSED(state); +} + +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); + } +} + +void BluetoothLinkProvider::connectError() +{ + QBluetoothSocket* socket = qobject_cast(sender()); + if (!socket) return; + + qCWarning(KDECONNECT_CORE) << "connectError: 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) { + 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(); + + if (socket->error() != QBluetoothSocket::NoSocketError) { + qCWarning(KDECONNECT_CORE) << "BluetoothLinkProvider/serviceDiscoveryFinished: 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(); + + if (mSockets.contains(socket->peerAddress())) { + qCDebug(KDECONNECT_CORE()) << "BluetoothLinkProvider/clientConnected: Duplicate connection to " << socket->peerAddress().toString(); + 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?)"; + mSockets.remove(socket->peerAddress()); + socket->close(); + socket->deleteLater(); + return; + } + + qCDebug(KDECONNECT_CORE()) << "BluetoothLinkProvider/clientIdentityReceived: Received identity package from " << socket->peerAddress().toString(); + + 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(); + + if (mSockets.contains(socket->peerAddress())) { + qCDebug(KDECONNECT_CORE()) << "BluetoothLinkProvider/serverNewConnection: Duplicate connection from " << socket->peerAddress().toString(); + 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(); + + 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."; + mSockets.remove(socket->peerAddress()); + socket->close(); + socket->deleteLater(); + return; + } + + qCDebug(KDECONNECT_CORE()) << "BluetoothLinkProvider/serverDataReceived: Received identity package from " << socket->peerAddress().toString(); + + 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 new file mode 100644 index 000000000..ee4188861 --- /dev/null +++ b/core/backends/bluetooth/bluetoothlinkprovider.h @@ -0,0 +1,82 @@ +/** + * Copyright 2015 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 "../linkprovider.h" + +class BluetoothDeviceLink; + +class BluetoothLinkProvider + : public LinkProvider +{ + Q_OBJECT + +public: + BluetoothLinkProvider(); + ~BluetoothLinkProvider(); + + QString name() { return "BluetoothLinkProvider"; } + int priority() { return PRIORITY_MEDIUM; } +public Q_SLOTS: + virtual void onNetworkChange(QNetworkSession::State state); + virtual void onStart(); + virtual void onStop(); + void connectError(); + void serviceDiscoveryFinished(); + +private Q_SLOTS: + void deviceLinkDestroyed(QObject* destroyedDeviceLink); + void socketDisconnected(); + void connectToPairedDevices(); + void serverNewConnection(); + void serverDataReceived(); + void clientConnected(); + void clientIdentityReceived(); + +private: + void addLink(BluetoothDeviceLink* deviceLink, const QString& deviceId); + QList getPairedDevices(); + + QBluetoothLocalDevice mDevice; + QBluetoothUuid mServiceUuid; + QBluetoothServer* mBluetoothServer; + QBluetoothServiceInfo mKdeconnectService; + QBluetoothServiceDiscoveryAgent* mServiceDiscoveryAgent; + QTimer* connectTimer; + + QMap mLinks; + + QMap mSockets; + +}; + +#endif diff --git a/core/backends/devicelinereader.cpp b/core/backends/devicelinereader.cpp new file mode 100644 index 000000000..bba0bdaae --- /dev/null +++ b/core/backends/devicelinereader.cpp @@ -0,0 +1,55 @@ +/** + * Copyright 2013 Albert Vaca + * Copyright 2014 Alejandro Fiestas Olivares + * + * 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 "devicelinereader.h" + +DeviceLineReader::DeviceLineReader(QIODevice* device, QObject* parent) + : QObject(parent) + , mDevice(device) +{ + connect(mDevice, SIGNAL(readyRead()), + this, SLOT(dataReceived())); + connect(mDevice, SIGNAL(disconnected()), + this, SIGNAL(disconnected())); +} + +void DeviceLineReader::dataReceived() +{ + while(mDevice->canReadLine()) { + const QByteArray line = mDevice->readLine(); + if (line.length() > 1) { + mPackages.enqueue(line);//we don't want single \n + } + } + + //If we still have things to read from the device, call dataReceived again + //We do this manually because we do not trust readyRead to be emitted again + //So we call this method again just in case. + if (mDevice->bytesAvailable() > 0) { + QMetaObject::invokeMethod(this, "dataReceived", Qt::QueuedConnection); + return; + } + + //If we have any packages, tell it to the world. + if (!mPackages.isEmpty()) { + Q_EMIT readyRead(); + } +} diff --git a/core/backends/devicelinereader.h b/core/backends/devicelinereader.h new file mode 100644 index 000000000..a5255c77d --- /dev/null +++ b/core/backends/devicelinereader.h @@ -0,0 +1,59 @@ +/** + * 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. + */ +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(); } + +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 3f4028cc7..aab240e3a 100644 --- a/core/daemon.cpp +++ b/core/daemon.cpp @@ -28,6 +28,7 @@ #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" @@ -104,6 +105,11 @@ void Daemon::releaseDiscoveryMode(const QString &key) 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(); } diff --git a/core/kdeconnectplugin_config.cpp b/core/kdeconnectplugin_config.cpp new file mode 100644 index 000000000..c59cd553a --- /dev/null +++ b/core/kdeconnectplugin_config.cpp @@ -0,0 +1,49 @@ +/** + * 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 "kdeconnectplugin_config.h" +#include + +KdeConnectPluginConfig::KdeConnectPluginConfig(QObject* parent, const QVariantList& args) + : KCModule(SftpConfigFactory::componentData(), parent) + , mGlobalConfig(KSharedConfig::openConfig("kdeconnect/plugins/pausemusic")) +{ + 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() +{ +} + + + diff --git a/core/kdeconnectplugin_config.h b/core/kdeconnectplugin_config.h new file mode 100644 index 000000000..e96a800cc --- /dev/null +++ b/core/kdeconnectplugin_config.h @@ -0,0 +1,49 @@ +/** + * 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 KDECONNECTPLUGINCONFIG_H +#define KDECONNECTPLUGINCONFIG_H + +#include "kdeconnectcore_export.h" +#include + +struct DeviceDbusInterface; + +class KDECONNECTCORE_EXPORT KdeConnectPluginConfig + : public KCModule +{ + 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; } + +private: + QString mDeviceId; + KSharedConfigPtr mGlobalConfig; + KSharedConfigPtr mDeviceConfig; +}; + +#endif