...
 
Commits (179)
......@@ -7,3 +7,4 @@ insert_final_newline = true
indent_style = space
indent_size = 4
charset = utf-8
trim_trailing_whitespace = true
......@@ -16,3 +16,9 @@ KDEConnect.includes
*.qmlc
*.rej
.vscode
# Eclipse settings
.cproject
.project
.settings/*
......@@ -2,15 +2,28 @@ cmake_minimum_required(VERSION 3.0)
project(kdeconnect)
set(KDECONNECT_VERSION_MAJOR 1)
set(KDECONNECT_VERSION_MINOR 3)
set(KDECONNECT_VERSION_PATCH 3)
find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
set(KDECONNECT_VERSION "${KDECONNECT_VERSION_MAJOR}.${KDECONNECT_VERSION_MINOR}.${KDECONNECT_VERSION_PATCH}")
include(KDEInstallDirs)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(KDECMakeSettings)
include(ECMAddTests)
include(ECMSetupVersion)
include(ECMInstallIcons)
include(FeatureSummary)
include(ECMQtDeclareLoggingCategory)
include(GenerateExportHeader)
find_package(PkgConfig)
include(KDEConnectMacros.cmake)
ecm_setup_version(1.3.3
VARIABLE_PREFIX KDECONNECT
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/kdeconnect-version.h
)
if (SAILFISHOS)
find_package(PkgConfig)
set(KF5_MIN_VERSION "5.31.0")
set(QT_MIN_VERSION "5.6.0")
set(KF5_REQUIRED_COMPONENTS I18n DBusAddons CoreAddons IconThemes Config)
......@@ -22,22 +35,21 @@ if (SAILFISHOS)
include_directories(${QCA2_INCLUDEDIR})
add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
else()
set(KF5_MIN_VERSION "5.42.0")
set(QT_MIN_VERSION "5.7.0")
set(KF5_REQUIRED_COMPONENTS I18n ConfigWidgets DBusAddons IconThemes Notifications KIO KCMUtils Service)
set(KF5_MIN_VERSION "5.48.0")
set(QT_MIN_VERSION "5.10.0")
set(KF5_REQUIRED_COMPONENTS I18n ConfigWidgets DBusAddons IconThemes Notifications KIO KCMUtils Service Kirigami2)
set(KF5_OPTIONAL_COMPONENTS DocTools)
if(UNIX)
set(KF5_OPTIONAL_COMPONENTS ${KF5_OPTIONAL_COMPONENTS} Runner)
endif()
set(QCA_MIN_VERSION "2.1.0")
find_package(Qca-qt5 ${QCA_MIN_VERSION} REQUIRED)
find_package(KF5PulseAudioQt)
add_definitions(-DQT_NO_URL_CAST_FROM_STRING -DQT_NO_KEYWORDS)
if(NOT WIN32 AND NOT APPLE)
find_package(KF5PulseAudioQt REQUIRED)
endif()
add_definitions(-DQT_NO_URL_CAST_FROM_STRING -DQT_NO_KEYWORDS -DQT_NO_CAST_FROM_ASCII)
endif()
find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_SOURCE_DIR}/cmake)
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS Quick Network)
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS ${KF5_REQUIRED_COMPONENTS})
if (KF5_OPTIONAL_COMPONENTS)
......@@ -57,20 +69,10 @@ set_package_properties(KF5Kirigami2 PROPERTIES
TYPE RUNTIME
)
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)
include(GenerateExportHeader)
option(PRIVATE_DBUS_ENABLED "Use private dbus session for kdeconnect" OFF)
if(PRIVATE_DBUS_ENABLED OR APPLE)
add_compile_definitions(USE_PRIVATE_DBUS)
endif()
add_subdirectory(core)
if(NOT SAILFISHOS)
add_subdirectory(kcm)
......@@ -86,12 +88,6 @@ endif()
add_subdirectory(icon)
add_subdirectory(interfaces)
add_subdirectory(data)
option(EXPERIMENTALAPP_ENABLED OFF)
if(EXPERIMENTALAPP_ENABLED)
find_package(KF5Kirigami2)
add_subdirectory(app)
endif()
add_subdirectory(plugins)
add_subdirectory(cli)
add_subdirectory(declarativeplugin)
......@@ -101,6 +97,7 @@ if(KF5Runner_FOUND)
endif()
if (NOT SAILFISHOS)
add_subdirectory(app)
add_subdirectory(indicator)
add_subdirectory(urlhandler)
add_subdirectory(nautilus-extension)
......@@ -109,19 +106,16 @@ else()
add_subdirectory(sfos)
endif()
option(SMSAPP_ENABLED OFF)
if(SMSAPP_ENABLED)
find_package(KF5Kirigami2)
find_package(KF5People REQUIRED)
find_package(KF5PeopleVCard)
set_package_properties(KF5PeopleVCard PROPERTIES
PURPOSE "Read vcards from the file system"
URL "https://phabricator.kde.org/source/kpeoplevcard/"
TYPE RUNTIME
)
add_subdirectory(smsapp)
endif()
find_package(KF5Kirigami2)
find_package(KF5People REQUIRED)
find_package(KF5PeopleVCard)
set_package_properties(KF5PeopleVCard PROPERTIES
PURPOSE "Read vcards from the file system"
URL "https://phabricator.kde.org/source/kpeoplevcard/"
TYPE RUNTIME
)
add_subdirectory(smsapp)
if(KF5DocTools_FOUND)
add_subdirectory(doc)
......
......@@ -56,7 +56,7 @@ NetworkPackets can also have binary data attached that can't be serialized to JS
## Contributing
To contribute patches, use [KDE Connect's Phabricator](https://phabricator.kde.org/project/profile/159/). There you can also find a task list with stuff to do, and links to other relevant resources. It is a good idea to also subscribe to the [KDE Connect mailing list](https://mail.kde.org/mailman/listinfo/kdeconnect). We can also be reached on IRC at #kdeconnect on freenode or on [telegram](https://t.me/joinchat/AOS6gA37orb2dZCLhqbZjg), contributions and feedback are warmly welcomed.
To contribute patches, use [KDE Connect's GitLab](https://invent.kde.org/kde/kdeconnect-kde). There you can also find a task list with stuff to do, and links to other relevant resources. It is a good idea to also subscribe to the [KDE Connect mailing list](https://mail.kde.org/mailman/listinfo/kdeconnect). We can also be reached on IRC at #kdeconnect on freenode or on [telegram](https://t.me/joinchat/AOS6gA37orb2dZCLhqbZjg), contributions and feedback are warmly welcomed.
## License
[GNU GPL v2](https://www.gnu.org/licenses/gpl-2.0.html) and [GNU GPL v3](https://www.gnu.org/licenses/gpl-3.0.html)
......
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Declarative)
qt5_add_resources(kdeconnect_SRCS resources.qrc)
qt5_add_resources(kcapp_SRCS resources.qrc)
add_executable(kdeconnect main.cpp ${kdeconnect_SRCS})
target_link_libraries(kdeconnect Qt5::Quick Qt5::Widgets KF5::CoreAddons KF5::I18n)
add_executable(kcapp main.cpp ${kcapp_SRCS})
target_link_libraries(kcapp Qt5::Quick Qt5::Widgets KF5::Declarative KF5::CoreAddons KF5::I18n)
install(TARGETS kcapp ${INSTALL_TARGETS_DEFAULT_ARGS})
install(TARGETS kdeconnect ${INSTALL_TARGETS_DEFAULT_ARGS})
install(PROGRAMS org.kde.kdeconnect.app.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
......@@ -30,7 +30,7 @@
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
KAboutData aboutData(QStringLiteral("kdeconnect.app"), i18n("KDE Connect App"), QStringLiteral("1.0"), i18n("KDE Connect App"), KAboutLicense::GPL, i18n("(c) 2015, Aleix Pol Gonzalez"));
KAboutData aboutData(QStringLiteral("kdeconnect.app"), i18n("KDE Connect"), QStringLiteral("1.0"), i18n("KDE Connect"), KAboutLicense::GPL, i18n("(c) 2015, Aleix Pol Gonzalez"));
aboutData.addAuthor(i18n("Aleix Pol Gonzalez"), i18n("Maintainer"), QStringLiteral("aleixpol@kde.org"));
KAboutData::setApplicationData(aboutData);
......
[Desktop Entry]
Name=KDE Connect Application
Name=KDE Connect
Name[ar]=تطبيق كدي المتّصل
Name[ca]=Aplicació KDE Connect
Name[ca@valencia]=Aplicació KDE Connect
......@@ -19,6 +19,7 @@ Name[hu]=KDE Connect alkalmazás
Name[id]=Aplikasi KDE Connect
Name[it]=Applicazione KDE Connect
Name[ko]=KDE Connect 프로그램
Name[lt]=KDE Connect programa
Name[nl]=KDE Connect-toepassing
Name[nn]=KDE Connect-program
Name[pl]=Program KDE Connect
......@@ -94,6 +95,7 @@ Comment[hu]=Egyesítse eszközeit
Comment[id]=Membuat semua perangkatmu menyatu
Comment[it]=Unisce tutti i tuoi dispositivi
Comment[ko]=모든 장치를 하나로 동기화
Comment[lt]=Paverskite visus savo įrenginius vienu
Comment[nl]=Al uw apparaten een maken
Comment[nn]=Slå alle einingane dine saman
Comment[pl]=Uczyń wszystkie swoje urządzenia jednym
......@@ -111,7 +113,7 @@ Comment[uk]=Поєднайте усі ваші пристрої
Comment[x-test]=xxMake all your devices onexx
Comment[zh_CN]=连接您所有设备
Comment[zh_TW]=讓您的設備內容全面同步
Exec=kcapp
Exec=kdeconnect
Icon=kdeconnect
Type=Application
Terminal=false
......
import QtQuick 2.2
import QtQuick.Controls 2.2
Loader {
property var plugin
property var lastPosition: plugin.position
property date lastPositionTime: new Date()
property bool updatePositionSlider: true
sourceComponent: plugin.canSeek ? seekBar : progressBar
onLastPositionChanged: {
lastPositionTime = new Date();
}
Component {
id: seekBar
Slider {
from: 0
to: plugin.length
onPressedChanged: {
if (pressed) {
updatePositionSlider = false
} else {
updatePositionSlider = true
plugin.position = value
}
}
}
}
Component {
id: progressBar
ProgressBar {
from: 0
to: plugin.length
}
}
Timer {
id: positionUpdateTimer
interval: 1000
repeat: true
running: updatePositionSlider && plugin.isPlaying
onTriggered: item.value = lastPosition + (new Date().getTime() - lastPositionTime.getTime())
}
Connections {
target: plugin
onNowPlayingChanged: {
item.value = lastPosition
}
}
}
......@@ -59,6 +59,9 @@ Kirigami.ApplicationWindow
titleIcon: "kdeconnect"
// bannerImageSource: "/home/apol/devel/kde5/share/wallpapers/Next/contents/images/1024x768.png"
// FIXME: Dirty workaround for the sidebar being too wide
width: Kirigami.Units.gridUnit * 14
modal: !root.wideScreen
handleVisible: !root.wideScreen
......
......@@ -28,17 +28,10 @@ Kirigami.Page
id: root
property QtObject pluginInterface
property bool muted: false
property bool updatePositionSlider: true
property int volumeUnmuted
property var volume: pluginInterface.volume
property var lastPosition: pluginInterface.position
property date lastPositionTime: new Date()
title: i18n("Multimedia Controls")
onLastPositionChanged: {
lastPositionTime = new Date();
}
onVolumeChanged: {
if (muted && volume != 0) {
toggleMute()
......@@ -78,11 +71,6 @@ Kirigami.Page
}
}
Connections {
target: root.pluginInterface
onNowPlayingChanged: positionSlider.value = lastPosition
}
Label {
id: noPlayersText
text: i18n("No players available")
......@@ -158,29 +146,15 @@ Kirigami.Page
RowLayout {
Layout.fillWidth: true
Label {
text: msToTime(new Date(positionSlider.value), new Date(root.pluginInterface.length))
text: msToTime(new Date(positionIndicator.item.value), new Date(root.pluginInterface.length))
}
Slider {
id: positionSlider
to: root.pluginInterface.length
Layout.fillWidth: true
Timer {
id: positionUpdateTimer
interval: 1000
repeat: true
running: updatePositionSlider && root.pluginInterface.isPlaying
onTriggered: positionSlider.value = lastPosition + (new Date().getTime() - lastPositionTime.getTime())
}
onPressedChanged: {
if (pressed) {
updatePositionSlider = false
} else {
updatePositionSlider = true
root.pluginInterface.position = value
}
}
MprisSlider {
id: positionIndicator
plugin: root.pluginInterface
Layout.fillWidth: true
}
Label {
text: msToTime(new Date(root.pluginInterface.length), new Date(root.pluginInterface.length))
}
......
......@@ -10,5 +10,6 @@
<file>qml/FindDevicesPage.qml</file>
<file>qml/runcommand.qml</file>
<file>qml/volume.qml</file>
<file>qml/MprisSlider.qml</file>
</qresource>
</RCC>
......@@ -7,6 +7,7 @@ target_include_directories(kdeconnect-cli PUBLIC ${CMAKE_BINARY_DIR})
target_link_libraries(kdeconnect-cli
kdeconnectinterfaces
kdeconnectcore
KF5::CoreAddons
KF5::I18n
)
......
This diff is collapsed.
project(KDEConnectCore)
add_definitions(-DTRANSLATION_DOMAIN=\"kdeconnect-core\")
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
)
set(KDECONNECT_PRIVATE_DBUS_ADDR unix:tmpdir=/tmp)
if(WIN32)
set(KDECONNECT_PRIVATE_DBUS_ADDR tcp:host=localhost,port=0)
endif()
set(KDECONNECT_PRIVATE_DBUS_NAME DBusKDEConnectOnly)
configure_file(dbushelper.h.in ${CMAKE_CURRENT_BINARY_DIR}/dbushelper.h)
add_subdirectory(backends/lan)
add_subdirectory(backends/loopback)
......@@ -33,9 +34,17 @@ set(kdeconnectcore_SRCS
dbushelper.cpp
networkpacket.cpp
filetransferjob.cpp
compositefiletransferjob.cpp
daemon.cpp
device.cpp
core_debug.cpp
notificationserverinfo.cpp
)
ecm_qt_declare_logging_category(
kdeconnectcore_SRCS
HEADER core_debug.h
IDENTIFIER KDECONNECT_CORE
CATEGORY_NAME kdeconnect.core
)
add_library(kdeconnectcore ${kdeconnectcore_SRCS})
......
......@@ -2,6 +2,9 @@
set(backends_kdeconnect_SRCS
${backends_kdeconnect_SRCS}
backends/bluetooth/multiplexchannel.cpp
backends/bluetooth/multiplexchannelstate.cpp
backends/bluetooth/connectionmultiplexer.cpp
backends/bluetooth/bluetoothlinkprovider.cpp
backends/bluetooth/bluetoothdevicelink.cpp
backends/bluetooth/bluetoothpairinghandler.cpp
......
......@@ -43,11 +43,11 @@ Where the message type can be one of the following.
This message should be the first message send, and never at a later time. Its format is as follows:
```
| MESSAGE_PROTOCOL_VERSION header | Lowest version supported | Highest version supported |
| 19 bytes (UUID ignored) | 2 bytes (Big-Endian) | 2 bytes (Big-Endian) |
| MESSAGE_PROTOCOL_VERSION header | Lowest version supported | Highest version supported | Other data |
| 19 bytes (UUID ignored) | 2 bytes (Big-Endian) | 2 bytes (Big-Endian) | Remaining data bytes |
```
This message should be the first message to send. Use the maximum version supported by both endpoints (if any), or otherwise close the connection.
This message should be the first message to send. Use the maximum version supported by both endpoints (if any), or otherwise close the connection. The other data field is not used (and should be empty for protocol version 1), but it implies that message lengths of more than 4 need to be supported for future compatability.
Currently, no client will send this message with a version other than 1, but you *must* accept and check it, for forward compatibility.
......
......@@ -25,36 +25,39 @@
#include "bluetoothuploadjob.h"
#include "bluetoothdownloadjob.h"
#include "core_debug.h"
#include "connectionmultiplexer.h"
#include "multiplexchannel.h"
BluetoothDeviceLink::BluetoothDeviceLink(const QString& deviceId, LinkProvider* parent, QBluetoothSocket* socket)
BluetoothDeviceLink::BluetoothDeviceLink(const QString& deviceId, LinkProvider* parent, ConnectionMultiplexer* connection, QSharedPointer<MultiplexChannel> socket)
: DeviceLink(deviceId, parent)
, mSocketReader(new DeviceLineReader(socket, this))
, mBluetoothSocket(socket)
, mSocketReader(new DeviceLineReader(socket.data(), this))
, mConnection(connection)
, mChannel(socket)
, mPairingHandler(new BluetoothPairingHandler(this))
{
connect(mSocketReader, SIGNAL(readyRead()),
this, SLOT(dataReceived()));
connect(mSocketReader, &DeviceLineReader::readyRead, this, &BluetoothDeviceLink::dataReceived);
//We take ownership of the socket.
//We take ownership of the connection.
//When the link provider destroys us,
//the socket (and the reader) will be
//destroyed as well
mBluetoothSocket->setParent(this);
connect(mBluetoothSocket, SIGNAL(disconnected()), this, SLOT(deleteLater()));
mConnection->setParent(this);
connect(socket.data(), &MultiplexChannel::aboutToClose, this, &QObject::deleteLater);
}
QString BluetoothDeviceLink::name()
{
return "BluetoothLink"; // Should be same in both android and kde version
return QStringLiteral("BluetoothLink"); // Should be same in both android and kde version
}
bool BluetoothDeviceLink::sendPacket(NetworkPacket& np)
{
if (np.hasPayload()) {
BluetoothUploadJob* uploadJob = new BluetoothUploadJob(np.payload(), mBluetoothSocket->peerAddress(), this);
BluetoothUploadJob* uploadJob = new BluetoothUploadJob(np.payload(), mConnection, this);
np.setPayloadTransferInfo(uploadJob->transferInfo());
uploadJob->start();
}
//TODO: handle too-big packets
int written = mSocketReader->write(np.serialize());
return (written != -1);
}
......@@ -89,8 +92,7 @@ void BluetoothDeviceLink::dataReceived()
}
if (packet.hasPayloadTransferInfo()) {
BluetoothDownloadJob* downloadJob = new BluetoothDownloadJob(mBluetoothSocket->peerAddress(),
packet.payloadTransferInfo(), this);
BluetoothDownloadJob* downloadJob = new BluetoothDownloadJob(mConnection, packet.payloadTransferInfo(), this);
downloadJob->start();
packet.setPayload(downloadJob->payload(), packet.payloadSize());
}
......@@ -98,6 +100,6 @@ void BluetoothDeviceLink::dataReceived()
Q_EMIT receivedPacket(packet);
if (mSocketReader->bytesAvailable() > 0) {
QMetaObject::invokeMethod(this, "dataReceived", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, &BluetoothDeviceLink::dataReceived, Qt::QueuedConnection);
}
}
......@@ -29,13 +29,16 @@
#include "../devicelinereader.h"
#include "bluetoothpairinghandler.h"
class ConnectionMultiplexer;
class MultiplexChannel;
class KDECONNECTCORE_EXPORT BluetoothDeviceLink
: public DeviceLink
{
Q_OBJECT
public:
BluetoothDeviceLink(const QString& deviceId, LinkProvider* parent, QBluetoothSocket* socket);
BluetoothDeviceLink(const QString& deviceId, LinkProvider* parent, ConnectionMultiplexer* connection, QSharedPointer<MultiplexChannel> socket);
virtual QString name() override;
bool sendPacket(NetworkPacket& np) override;
......@@ -50,7 +53,8 @@ private Q_SLOTS:
private:
DeviceLineReader* mSocketReader;
QBluetoothSocket* mBluetoothSocket;
ConnectionMultiplexer* mConnection;
QSharedPointer<MultiplexChannel> mChannel;
BluetoothPairingHandler* mPairingHandler;
void sendMessage(const QString mMessage);
......
......@@ -19,13 +19,14 @@
*/
#include "bluetoothdownloadjob.h"
#include "connectionmultiplexer.h"
#include "multiplexchannel.h"
BluetoothDownloadJob::BluetoothDownloadJob(const QBluetoothAddress& remoteAddress, const QVariantMap& transferInfo, QObject* parent)
BluetoothDownloadJob::BluetoothDownloadJob(ConnectionMultiplexer *connection, const QVariantMap& transferInfo, QObject* parent)
: QObject(parent)
, mRemoteAddress(remoteAddress)
, mTransferUuid(QBluetoothUuid(transferInfo.value("uuid").toString()))
, mSocket(new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol))
{
QBluetoothUuid id{transferInfo.value(QStringLiteral("uuid")).toString()};
mSocket = QSharedPointer<MultiplexChannel>{connection->getChannel(id).release()};
}
QSharedPointer<QIODevice> BluetoothDownloadJob::payload() const
......@@ -35,7 +36,4 @@ QSharedPointer<QIODevice> BluetoothDownloadJob::payload() const
void BluetoothDownloadJob::start()
{
connect(mSocket.data(), &QBluetoothSocket::disconnected, mSocket.data(), &QBluetoothSocket::readyRead);
connect(mSocket.data(), &QBluetoothSocket::disconnected, mSocket.data(), &QBluetoothSocket::readChannelFinished);
mSocket->connectToService(mRemoteAddress, mTransferUuid, QIODevice::ReadOnly);
}
......@@ -29,19 +29,20 @@
#include <QBluetoothUuid>
#include <QBluetoothSocket>
class ConnectionMultiplexer;
class MultiplexChannel;
class BluetoothDownloadJob
: public QObject
{
Q_OBJECT
public:
explicit BluetoothDownloadJob(const QBluetoothAddress& remoteAddress, const QVariantMap& transferInfo, QObject* parent = 0);
explicit BluetoothDownloadJob(ConnectionMultiplexer *connection, const QVariantMap& transferInfo, QObject* parent = 0);
QSharedPointer<QIODevice> payload() const;
void start();
private:
QBluetoothAddress mRemoteAddress;
QBluetoothUuid mTransferUuid;
QSharedPointer<QBluetoothSocket> mSocket;
QSharedPointer<MultiplexChannel> mSocket;
};
#endif // BLUETOOTHDOWNLOADJOB_H
......@@ -35,6 +35,8 @@
#include "../linkprovider.h"
class BluetoothDeviceLink;
class ConnectionMultiplexer;
class MultiplexChannel;
class KDECONNECTCORE_EXPORT BluetoothLinkProvider
: public LinkProvider
......@@ -45,7 +47,7 @@ public:
BluetoothLinkProvider();
virtual ~BluetoothLinkProvider();
QString name() override { return "BluetoothLinkProvider"; }
QString name() override { return QStringLiteral("BluetoothLinkProvider"); }
int priority() override { return PRIORITY_MEDIUM; }
public Q_SLOTS:
......@@ -53,16 +55,17 @@ public Q_SLOTS:
virtual void onStart() override;
virtual void onStop() override;
void connectError();
void serviceDiscoveryFinished();
private Q_SLOTS:
void deviceLinkDestroyed(QObject* destroyedDeviceLink);
void socketDisconnected();
void socketDisconnected(const QBluetoothAddress &peerAddress, MultiplexChannel *socket);
void serverNewConnection();
void serverDataReceived();
void clientConnected();
void clientIdentityReceived();
void serverDataReceived(const QBluetoothAddress &peerAddress, QSharedPointer<MultiplexChannel> socket);
void clientConnected(QPointer<QBluetoothSocket> socket);
void clientIdentityReceived(const QBluetoothAddress &peerAddress, QSharedPointer<MultiplexChannel> socket);
void serviceDiscovered(const QBluetoothServiceInfo &info);
private:
void addLink(BluetoothDeviceLink* deviceLink, const QString& deviceId);
......@@ -76,7 +79,7 @@ private:
QMap<QString, DeviceLink*> mLinks;
QMap<QBluetoothAddress, QBluetoothSocket*> mSockets;
QMap<QBluetoothAddress, ConnectionMultiplexer*> mSockets;
};
......
......@@ -42,7 +42,7 @@ void BluetoothPairingHandler::packetReceived(const NetworkPacket& np)
m_pairingTimeout.stop();
bool wantsPair = np.get<bool>("pair");
bool wantsPair = np.get<bool>(QStringLiteral("pair"));
if (wantsPair) {
......@@ -91,7 +91,7 @@ bool BluetoothPairingHandler::requestPairing()
}
NetworkPacket np(PACKET_TYPE_PAIR);
np.set("pair", true);
np.set(QStringLiteral("pair"), true);
bool success;
success = deviceLink()->sendPacket(np);
if (success) {
......@@ -106,7 +106,7 @@ bool BluetoothPairingHandler::acceptPairing()
qCDebug(KDECONNECT_CORE) << "User accepts pairing";
m_pairingTimeout.stop(); // Just in case it is started
NetworkPacket np(PACKET_TYPE_PAIR);
np.set("pair", true);
np.set(QStringLiteral("pair"), true);
bool success = deviceLink()->sendPacket(np);
if (success) {
setInternalPairStatus(Paired);
......@@ -118,14 +118,14 @@ void BluetoothPairingHandler::rejectPairing()
{
qCDebug(KDECONNECT_CORE) << "User rejects pairing";
NetworkPacket np(PACKET_TYPE_PAIR);
np.set("pair", false);
np.set(QStringLiteral("pair"), false);
deviceLink()->sendPacket(np);
setInternalPairStatus(NotPaired);
}
void BluetoothPairingHandler::unpair() {
NetworkPacket np(PACKET_TYPE_PAIR);
np.set("pair", false);
np.set(QStringLiteral("pair"), false);
deviceLink()->sendPacket(np);
setInternalPairStatus(NotPaired);
}
......@@ -133,7 +133,7 @@ void BluetoothPairingHandler::unpair() {
void BluetoothPairingHandler::pairingTimeout()
{
NetworkPacket np(PACKET_TYPE_PAIR);
np.set("pair", false);
np.set(QStringLiteral("pair"), false);
deviceLink()->sendPacket(np);
setInternalPairStatus(NotPaired); //Will emit the change as well
Q_EMIT pairingError(i18n("Timed out"));
......
......@@ -43,11 +43,11 @@ public:
BluetoothPairingHandler(DeviceLink* deviceLink);
virtual ~BluetoothPairingHandler() { }
virtual void packetReceived(const NetworkPacket& 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;
void packetReceived(const NetworkPacket& np) override;
bool requestPairing() override;
bool acceptPairing() override;
void rejectPairing() override;
void unpair() override;
bool isPairRequested() const { return m_status == Requested; }
bool isPaired() const { return m_status == Paired; }
......
......@@ -20,69 +20,44 @@
*/
#include "bluetoothuploadjob.h"
#include "connectionmultiplexer.h"
#include "multiplexchannel.h"
#include <QBluetoothSocket>
#include "core_debug.h"
#include <QCoreApplication>
BluetoothUploadJob::BluetoothUploadJob(const QSharedPointer<QIODevice>& data, const QBluetoothAddress& remoteAddress, QObject* parent)
BluetoothUploadJob::BluetoothUploadJob(const QSharedPointer<QIODevice>& data, ConnectionMultiplexer *connection, QObject* parent)
: QObject(parent)
, mData(data)
, mRemoteAddress(remoteAddress)
, mTransferUuid(QBluetoothUuid::createUuid())
, mServer(new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this))
, mTransferUuid(connection->newChannel())
{
mServer->setSecurityFlags(QBluetooth::Encryption | QBluetooth::Secure);
mSocket = QSharedPointer<MultiplexChannel>{connection->getChannel(mTransferUuid).release()};
}
QVariantMap BluetoothUploadJob::transferInfo() const
{
QVariantMap ret;
ret["uuid"] = mTransferUuid.toString().mid(1, 36);
ret[QStringLiteral("uuid")] = mTransferUuid.toString().mid(1, 36);
return ret;
}
void BluetoothUploadJob::start()
{
connect(mServer, &QBluetoothServer::newConnection, this, &BluetoothUploadJob::newConnection);
mServiceInfo = mServer->listen(mTransferUuid, "KDE Connect Transfer Job");
Q_ASSERT(mServiceInfo.isValid());
}
void BluetoothUploadJob::newConnection()
{
m_socket = mServer->nextPendingConnection();
Q_ASSERT(m_socket);
m_socket->setParent(this);
connect(m_socket, &QBluetoothSocket::disconnected, this, &BluetoothUploadJob::deleteLater);
if (m_socket->peerAddress() != mRemoteAddress) {
m_socket->close();
} else {
mServer->close();
disconnect(mServer, &QBluetoothServer::newConnection, this, &BluetoothUploadJob::newConnection);
mServiceInfo.unregisterService();
if (!mData->open(QIODevice::ReadOnly)) {
qCWarning(KDECONNECT_CORE) << "error when opening the input to upload";
m_socket->close();
deleteLater();
return;
}
if (!mData->open(QIODevice::ReadOnly)) {
qCWarning(KDECONNECT_CORE) << "error when opening the input to upload";
return; //TODO: Handle error, clean up...
}
connect(m_socket, &QBluetoothSocket::bytesWritten, this, &BluetoothUploadJob::writeSome);
connect(m_socket, &QBluetoothSocket::disconnected, this, &BluetoothUploadJob::closeConnection);
connect(mSocket.data(), &MultiplexChannel::bytesWritten, this, &BluetoothUploadJob::writeSome);
connect(mSocket.data(), &MultiplexChannel::aboutToClose, this, &BluetoothUploadJob::closeConnection);
writeSome();
}
void BluetoothUploadJob::writeSome() {
Q_ASSERT(m_socket);
bool errorOccurred = false;
while (m_socket->bytesToWrite() == 0 && mData->bytesAvailable() && m_socket->isWritable()) {
while (mSocket->bytesToWrite() == 0 && mData->bytesAvailable() && mSocket->isWritable()) {
qint64 bytes = qMin<qint64>(mData->bytesAvailable(), 4096);
int bytesWritten = m_socket->write(mData->read(bytes));
int bytesWritten = mSocket->write(mData->read(bytes));
if (bytesWritten < 0) {
qCWarning(KDECONNECT_CORE()) << "error when writing data to bluetooth upload" << bytes << mData->bytesAvailable();
......@@ -92,18 +67,8 @@ void BluetoothUploadJob::writeSome() {
}
if (mData->atEnd() || errorOccurred) {
disconnect(m_socket, &QBluetoothSocket::bytesWritten, this, &BluetoothUploadJob::writeSome);
mData->close();
connect(m_socket, &QBluetoothSocket::bytesWritten, this, &BluetoothUploadJob::finishWrites);
finishWrites();
}
}
void BluetoothUploadJob::finishWrites() {
Q_ASSERT(m_socket);
if (m_socket->bytesToWrite() == 0) {
closeConnection();
mSocket->close();
}
}
......
......@@ -30,30 +30,28 @@
#include <QBluetoothUuid>
#include <QBluetoothServer>
class ConnectionMultiplexer;
class MultiplexChannel;
class BluetoothUploadJob
: public QObject
{
Q_OBJECT
public:
explicit BluetoothUploadJob(const QSharedPointer<QIODevice>& data, const QBluetoothAddress& remoteAddress, QObject* parent = 0);
explicit BluetoothUploadJob(const QSharedPointer<QIODevice>& data, ConnectionMultiplexer *connection, QObject* parent = 0);
QVariantMap transferInfo() const;
void start();
private:
QSharedPointer<QIODevice> mData;
QBluetoothAddress mRemoteAddress;
QBluetoothUuid mTransferUuid;
QBluetoothServer* mServer;
QBluetoothServiceInfo mServiceInfo;
QBluetoothSocket* m_socket;
QSharedPointer<MultiplexChannel> mSocket;
void closeConnection();
private Q_SLOTS:
void newConnection();
void writeSome();
void finishWrites();
};
#endif // BLUETOOTHUPLOADJOB_H
This diff is collapsed.
/**
* Copyright 2019 Matthijs Tijink <matthijstijink@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef CONNECTIONMULTIPLEXER_H
#define CONNECTIONMULTIPLEXER_H
#include <QObject>
#include <QByteArray>
#include <QHash>
#include <QSharedPointer>
#include <memory>
class QBluetoothUuid;
class MultiplexChannel;
class MultiplexChannelState;
class QBluetoothSocket;
/**
* An utility class to split a single (bluetooth) connection to multiple independent channels.
* By default (and without needing any communication with the other endpoint), a single default channel is open.
*
* Destroying/closing this object will automatically close all channels.
*/
class ConnectionMultiplexer : public QObject {
Q_OBJECT
public:
ConnectionMultiplexer(QBluetoothSocket *socket, QObject *parent = nullptr);
~ConnectionMultiplexer();
/**
* Open a new channel within this connection.
*
* @return The uuid to refer to this channel.
* @see getChannel()
*/
QBluetoothUuid newChannel();
/**
* Get the channel device for the specified channel uuid.
* If the channel does not exist, this will return a null pointer.
*
* A channel is guaranteed to exist until the first call to get it.
* @param channelId The channel uuid
* @return A shared pointer to the channel object
* @see getDefaultChannel()
*/
std::unique_ptr<MultiplexChannel> getChannel(QBluetoothUuid channelId);
/**
* Get the default channel.
*
* @see getChannel()
*/
std::unique_ptr<MultiplexChannel> getDefaultChannel();
/**
* Close all channels and the underlying connection.
*/
void close();
/**
* Check if the underlying connection is still open.
* @return True if the connection is open
* @see close()
*/
bool isOpen() const;
private:
/**
* The underlying connection
*/
QBluetoothSocket *mSocket;
/**
* The buffer of to-be-written bytes
*/
QByteArray to_write_bytes;
/**
* The channels not requested by the user yet
*/
QHash<QBluetoothUuid, MultiplexChannel*> unrequested_channels;
/**
* All channels currently open
*/
QHash<QBluetoothUuid, QSharedPointer<MultiplexChannelState>> channels;
/**
* True once the other side has sent its protocol version
*/
bool receivedProtocolVersion;
/**
* Slot for connection reading
*/
void readyRead();
/**
* Slot for disconnection
*/
void disconnected();
/**
* Slot for progress in writing data/new data available to be written
*/
void bytesWritten();
/**
* Tries to parse a single connection message.
*
* @return True if a message was parsed succesfully.
*/
bool tryParseMessage();
/**
* Add a new channel. Assumes that the communication about this channel is done
* (i.e. the other endpoint also knows this channel exists).
*
* @param new_id The channel uuid
*/
void addChannel(QBluetoothUuid new_id);
/**
* Slot for closing a channel
*/
void closeChannel(QBluetoothUuid channelId);
/**
* Slot for writing a channel's data to the other endpoint
*/
void channelCanWrite(QBluetoothUuid channelId);
/**
* Slot for indicating that a channel can receive more data
*/
void channelCanRead(QBluetoothUuid channelId);
/**
* Slot for removing a channel from tracking
*/
void removeChannel(QBluetoothUuid channelId);
};
#endif
/**
* Copyright 2019 Matthijs Tijink <matthijstijink@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "multiplexchannel.h"
#include "multiplexchannelstate.h"
#include "core_debug.h"
MultiplexChannel::MultiplexChannel(QSharedPointer<MultiplexChannelState> state) : state{state} {
QIODevice::open(QIODevice::ReadWrite);
connect(this, &QIODevice::aboutToClose, state.data(), &MultiplexChannelState::requestClose);
connect(state.data(), &MultiplexChannelState::readyRead, this, &QIODevice::readyRead);
connect(state.data(), &MultiplexChannelState::bytesWritten, this, &QIODevice::bytesWritten);
connect(state.data(), &MultiplexChannelState::disconnected, this, &MultiplexChannel::disconnect);
}
MultiplexChannel::~MultiplexChannel() {}
bool MultiplexChannel::atEnd() const {
return !isOpen() || (!state->connected && state->read_buffer.isEmpty());
}
void MultiplexChannel::disconnect() {
state->connected = false;
setOpenMode(QIODevice::ReadOnly);
Q_EMIT state->readyRead();
Q_EMIT state->requestClose();
if (state->read_buffer.isEmpty()) {
close();
}
}
qint64 MultiplexChannel::bytesAvailable() const {
return state->read_buffer.size() + QIODevice::bytesAvailable();
}
qint64 MultiplexChannel::bytesToWrite() const {
return state->write_buffer.size() + QIODevice::bytesToWrite();
}
qint64 MultiplexChannel::readData(char* data, qint64 maxlen) {
if (maxlen <= state->read_buffer.size()) {
for (int i = 0; i < maxlen; ++i) {
data[i] = state->read_buffer[i];
}
state->read_buffer.remove(0, maxlen);
Q_EMIT state->readAvailable();
if (!state->connected && state->read_buffer.isEmpty()) {
close();
}
return maxlen;
} else if (state->read_buffer.size() > 0) {
auto num_to_read = state->read_buffer.size();
for (int i = 0; i < num_to_read; ++i) {
data[i] = state->read_buffer[i];
}
state->read_buffer.remove(0, num_to_read);
Q_EMIT state->readAvailable();
if (!state->connected && state->read_buffer.isEmpty()) {
close();
}
return num_to_read;
} else if (isOpen() && state->connected) {
if (state->requestedReadAmount < BUFFER_SIZE) {
Q_EMIT state->readAvailable();
}
return 0;
} else {
close();
return -1;
}
}
qint64 MultiplexChannel::writeData(const char* data, qint64 len) {
state->write_buffer.append(data, len);
Q_EMIT state->writeAvailable();
return len;
}
bool MultiplexChannel::canReadLine() const {
return isReadable() && (QIODevice::canReadLine() || state->read_buffer.contains('\n'));
}
bool MultiplexChannel::isSequential() const {
return true;
}
/**
* Copyright 2019 Matthijs Tijink <matthijstijink@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef MULTIPLEXCHANNEL_H
#define MULTIPLEXCHANNEL_H
#include <QIODevice>
#include <QSharedPointer>
class ConnectionMultiplexer;
class MultiplexChannelState;
/**
* Represents a single channel in a multiplexed connection
*
* @see ConnectionMultiplexer
* @see ConnectionMultiplexer::getChannel
*/
class MultiplexChannel : public QIODevice {
Q_OBJECT
private:
/**
* You cannot construct a MultiplexChannel yourself, use the ConnectionMultiplexer
*/
MultiplexChannel(QSharedPointer<MultiplexChannelState> state);
public:
~MultiplexChannel();
constexpr static int BUFFER_SIZE = 4096;
bool canReadLine() const override;
bool atEnd() const override;
qint64 bytesAvailable() const override;
qint64 bytesToWrite() const override;
bool isSequential() const override;
protected:
qint64 readData(char * data, qint64 maxlen) override;
qint64 writeData(const char * data, qint64 len) override;
private:
QSharedPointer<MultiplexChannelState> state;
/**
* Disconnects the channel
*/
void disconnect();
friend class ConnectionMultiplexer;
};
#endif
/**
* Copyright 2014 Alejandro Fiestas Olivares <afiestas@kde.org>
* Copyright 2019 Matthijs Tijink <matthijstijink@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
......@@ -15,14 +15,9 @@
* 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 <https://www.gnu.org/licenses/>.
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KDECONNECT_INTERFACE_DEBUG_H
#define KDECONNECT_INTERFACE_DEBUG_H
#include "multiplexchannelstate.h"
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(KDECONNECT_INTERFACES)
#endif //KDECONNECT_INTERFACE_DEBUG_H
MultiplexChannelState::MultiplexChannelState() : requestedReadAmount{0}, freeWriteAmount{0}, connected{true}, close_after_write{false} {}
/**
* Copyright 2019 Matthijs Tijink <matthijstijink@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef MULTIPLEXCHANNELSTATE_H
#define MULTIPLEXCHANNELSTATE_H
#include <QObject>
#include <QByteArray>
class ConnectionMultiplexer;
class MultiplexChannel;
/**
* Represents a single channel in a multiplexed connection
*
* @internal
* @see ConnectionMultiplexer
*/
class MultiplexChannelState : public QObject {
Q_OBJECT
private:
MultiplexChannelState();
public:
~MultiplexChannelState() = default;
constexpr static int BUFFER_SIZE = 4096;
private:
/**
* The read buffer (already read from underlying connection but not read by the user of the channel)
*/
QByteArray read_buffer;
/**