Commit 7ef29fdd authored by Albert Vaca Cintora's avatar Albert Vaca Cintora

Implemented bi-directional communication using tcp.

parent acadeef7
......@@ -3,10 +3,12 @@ set(kded_kdeconnect_SRCS
announcers/announcer.cpp
announcers/fakeannouncer.cpp
announcers/avahiannouncer.cpp
announcers/avahitcpannouncer.cpp
devicelinks/devicelink.cpp
devicelinks/echodevicelink.cpp
devicelinks/udpdevicelink.cpp
devicelinks/devicelink.cpp
devicelinks/tcpdevicelink.cpp
packagereceivers/packagereceiver.cpp
packagereceivers/pingpackagereceiver.cpp
......
......@@ -34,24 +34,23 @@ class Announcer : public QObject
Q_OBJECT
public:
const int PRIORITY_LOW = 0; //ie: 3g
const int PRIORITY_MEDIUM = 50; //ie: internet
const int PRIORITY_HIGH = 100; //ie: lan
Announcer();
virtual ~Announcer() { }
enum Priority {
PRIORITY_LOW = 0, //ie: 3g
PRIORITY_MEDIUM = 50, //ie: internet
PRIORITY_HIGH = 100 //ie: lan
};
virtual QString name() = 0;
virtual Priority priority() = 0;
virtual int priority() = 0;
virtual void setDiscoverable(bool b) = 0;
signals:
//NOTE: The announcer has to destroy the DeviceLink when it's no longer accessible,
// and every user should listen to the destroy signal to remove its references.
void onNewDeviceLink(const QString& id, const QString& name, DeviceLink*);
//NOTE: The announcer will to destroy the DeviceLink when it's no longer accessible,
// and every user should listen to the destroyed signal to remove its references.
void onNewDeviceLink(const NetworkPackage& identityPackage, DeviceLink*);
signals:
......
......@@ -36,23 +36,23 @@ AvahiAnnouncer::AvahiAnnouncer()
mUdpSocket = new QUdpSocket();
mUdpSocket->bind(port);
connect(mUdpSocket, SIGNAL(readyRead()), this, SLOT(readPendingNotifications()));
connect(mUdpSocket, SIGNAL(readyRead()), this, SLOT(newConnection()));
qDebug() << "Listening to udp messages";
}
void AvahiAnnouncer::readPendingNotifications()
void AvahiAnnouncer::newConnection()
{
qDebug() << "AvahiAnnouncer readPendingNotifications";
qDebug() << "AvahiAnnouncer newConnection";
while (mUdpSocket->hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(mUdpSocket->pendingDatagramSize());
NetAddress sender;
mUdpSocket->readDatagram(datagram.data(), datagram.size(), &(sender.ip), &(sender.port));
NetAddress client;
mUdpSocket->readDatagram(datagram.data(), datagram.size(), &(client.ip), &(client.port));
//log.write(datagram);
qDebug() << "AvahiAnnouncer incomming udp datagram: " << datagram;
......@@ -62,18 +62,27 @@ void AvahiAnnouncer::readPendingNotifications()
if (np.type() == "kdeconnect.identity") {
QString id = np.get<QString>("deviceId");
QString name = np.get<QString>("deviceName");
const QString& id = np.get<QString>("deviceId");
//const QString& name = np.get<QString>("deviceName");
qDebug() << "AvahiAnnouncer creating link to device" << id;
qDebug() << "AvahiAnnouncer creating link to device" << id << "(" << client.ip << "," << client.port << ")";
DeviceLink* dl = new UdpDeviceLink(id, this, sender.ip, 10600);
DeviceLink* dl = new UdpDeviceLink(id, this, client.ip);
connect(dl,SIGNAL(destroyed(QObject*)),this,SLOT(deviceLinkDestroyed(QObject*)));
if (links.contains(sender)) delete links[sender]; //Delete old link if we already know it, probably it is down if this happens.
emit onNewDeviceLink(np, dl);
links[sender] = dl;
NetworkPackage::createIdentityPackage(&np);
dl->sendPackage(np);
if (links.contains(id)) {
//Delete old link if we already know it, probably it is down if this happens.
qDebug() << "Destroying old link";
delete links[id];
}
links[id] = dl;
emit onNewDeviceLink(id, name, dl);
} else {
qDebug() << "Not an identification package (wuh?)";
}
......@@ -81,6 +90,12 @@ void AvahiAnnouncer::readPendingNotifications()
}
void AvahiAnnouncer::deviceLinkDestroyed(QObject* deviceLink)
{
const QString& id = ((DeviceLink*)deviceLink)->deviceId();
if (links.contains(id)) links.remove(id);
}
AvahiAnnouncer::~AvahiAnnouncer()
{
delete service;
......@@ -88,7 +103,7 @@ AvahiAnnouncer::~AvahiAnnouncer()
void AvahiAnnouncer::setDiscoverable(bool b)
{
qDebug() << "Avahi scanning";
qDebug() << "Avahi announcing";
if (b) service->publishAsync();
}
......@@ -40,18 +40,19 @@ public:
~AvahiAnnouncer();
QString name() { return "Avahi"; }
Priority priority() { return PRIORITY_HIGH; }
int priority() { return PRIORITY_HIGH; }
void setDiscoverable(bool b);
private Q_SLOTS:
void readPendingNotifications();
void newConnection();
void deviceLinkDestroyed(QObject*);
private:
DNSSD::PublicService* service;
QUdpSocket* mUdpSocket;
QMap<NetAddress, DeviceLink*> links;
QMap<QString, DeviceLink*> links;
};
......
/**
* Copyright 2013 Albert Vaca <albertvaka@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 "avahitcpannouncer.h"
#include "devicelinks/tcpdevicelink.h"
#include <QHostInfo>
#include <QTcpServer>
AvahiTcpAnnouncer::AvahiTcpAnnouncer()
{
QString serviceType = "_kdeconnect._tcp";
quint16 port = 10602;
//http://api.kde.org/4.x-api/kdelibs-apidocs/dnssd/html/index.html
service = new DNSSD::PublicService(QHostInfo::localHostName(), serviceType, port);
mServer = new QTcpServer();
connect(mServer,SIGNAL(newConnection()),this, SLOT(newConnection()));
mServer->listen(QHostAddress::Any, port);
}
void AvahiTcpAnnouncer::newConnection()
{
qDebug() << "AvahiTcpAnnouncer newConnection";
QTcpSocket* socket = mServer->nextPendingConnection();
connect(socket,SIGNAL(readyRead()),this,SLOT(dataReceived()));
NetworkPackage np;
NetworkPackage::createIdentityPackage(&np);
qDebug() << socket->isWritable();
qDebug() << socket->isOpen();
int written = socket->write(np.serialize());
qDebug() << np.serialize();
qDebug() << "AvahiTcpAnnouncer sent tcp package" << written << " bytes written, waiting for reply";
}
void AvahiTcpAnnouncer::dataReceived()
{
QTcpSocket* socket = (QTcpSocket*) QObject::sender();
QByteArray data = socket->readLine();
qDebug() << "AvahiTcpAnnouncer received reply:" << data;
NetworkPackage np;
NetworkPackage::unserialize(data,&np);
if (np.version() > 0 && np.type() == PACKAGE_TYPE_IDENTITY) {
const QString& id = np.get<QString>("deviceId");
TcpDeviceLink* dl = new TcpDeviceLink(id, this, socket);
connect(dl,SIGNAL(destroyed(QObject*)),this,SLOT(deviceLinkDestroyed(QObject*)));
if (links.contains(id)) {
//Delete old link if we already know it, probably it is down if this happens.
qDebug() << "Destroying old link";
delete links[id];
}
links[id] = dl;
qDebug() << "AvahiAnnouncer creating link to device" << id << "(" << socket->peerAddress() << ")";
emit onNewDeviceLink(np, dl);
disconnect(socket,SIGNAL(readyRead()),this,SLOT(dataReceived()));
} else {
qDebug() << "AvahiTcpAnnouncer/newConnection: Not an identification package (wuh?)";
}
}
void AvahiTcpAnnouncer::deviceLinkDestroyed(QObject* deviceLink)
{
const QString& id = ((DeviceLink*)deviceLink)->deviceId();
if (links.contains(id)) links.remove(id);
}
AvahiTcpAnnouncer::~AvahiTcpAnnouncer()
{
delete service;
}
void AvahiTcpAnnouncer::setDiscoverable(bool b)
{
qDebug() << "AvahiTcp announcing";
if (b) service->publishAsync();
}
/**
* Copyright 2013 Albert Vaca <albertvaka@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 AVAHITCPANNOUNCER_H
#define AVAHITCPANNOUNCER_H
#include <QObject>
#include <QTcpServer>
#include <KDE/DNSSD/PublicService>
#include "announcer.h"
#include "netaddress.h"
class AvahiTcpAnnouncer
: public Announcer
{
Q_OBJECT
public:
AvahiTcpAnnouncer();
~AvahiTcpAnnouncer();
QString name() { return "AvahiTcpAnnouncer"; }
int priority() { return PRIORITY_HIGH + 1; }
void setDiscoverable(bool b);
private Q_SLOTS:
void newConnection();
void deviceLinkDestroyed(QObject*);
void dataReceived();
private:
DNSSD::PublicService* service;
QTcpServer* mServer;
QMap<QString, DeviceLink*> links;
};
#endif
......@@ -21,18 +21,22 @@
#include "fakeannouncer.h"
#include "devicelinks/echodevicelink.h"
FakeAnnouncer::FakeAnnouncer()
#include <QDebug>
LoopbackAnnouncer::LoopbackAnnouncer()
{
echoDeviceLink = new EchoDeviceLink("fake", this);
NetworkPackage::createIdentityPackage(&identityPackage);
}
FakeAnnouncer::~FakeAnnouncer()
LoopbackAnnouncer::~LoopbackAnnouncer()
{
//delete echoDeviceLink;
}
void FakeAnnouncer::setDiscoverable(bool b)
void LoopbackAnnouncer::setDiscoverable(bool b)
{
if (b) emit onNewDeviceLink(echoDeviceLink->deviceId(),"Echo device", echoDeviceLink);
qDebug() << "Echo Device discovery emitted";
if (b) emit onNewDeviceLink(identityPackage, echoDeviceLink);
}
......@@ -18,25 +18,27 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FAKEANNOUNCER_H
#define FAKEANNOUNCER_H
#ifndef LOOPBACKANNOUNCER_H
#define LOOPBACKANNOUNCER_H
#include "announcer.h"
#include "networkpackage.h"
class FakeAnnouncer : public Announcer
class LoopbackAnnouncer : public Announcer
{
Q_OBJECT
public:
FakeAnnouncer();
~FakeAnnouncer();
LoopbackAnnouncer();
~LoopbackAnnouncer();
QString name() { return "FakeAnnouncer"; }
Priority priority() { return PRIORITY_LOW; }
QString name() { return "LoopbackAnnouncer"; }
int priority() { return PRIORITY_LOW; }
void setDiscoverable(bool b);
private:
DeviceLink* echoDeviceLink;
NetworkPackage identityPackage;
};
......
......@@ -24,11 +24,13 @@
#include "packagereceivers/notificationpackagereceiver.h"
#include "packagereceivers/pausemusicpackagereceiver.h"
#include "announcers/avahiannouncer.h"
#include "announcers/avahitcpannouncer.h"
#include "announcers/fakeannouncer.h"
#include "devicelinks/echodevicelink.h"
#include <QtNetwork/QUdpSocket>
#include <QFile>
#include <quuid.h>
#include <QDBusConnection>
#include <KIcon>
......@@ -44,9 +46,12 @@ K_EXPORT_PLUGIN(KdeConnectFactory("kdeconnect", "kdeconnect"))
Daemon::Daemon(QObject *parent, const QList<QVariant>&)
: KDEDModule(parent)
{
KSharedConfigPtr config = KSharedConfig::openConfig("kdeconnectrc");
if (!config->group("myself").hasKey("id")) {
config->group("myself").writeEntry("id",QUuid::createUuid().toString());
}
//Debugging
qDebug() << "GO GO GO!";
config->group("devices").group("paired").group("fake_unreachable").writeEntry("name","Fake device");
......@@ -59,8 +64,9 @@ Daemon::Daemon(QObject *parent, const QList<QVariant>&)
//TODO: Do not hardcode the load of the device locators
//use: https://techbase.kde.org/Development/Tutorials/Services/Plugins
announcers.insert(new AvahiAnnouncer());
announcers.insert(new FakeAnnouncer());
// announcers.insert(new AvahiAnnouncer());
announcers.insert(new AvahiTcpAnnouncer());
//announcers.insert(new LoopbackAnnouncer());
//TODO: Add package emitters
......@@ -81,8 +87,8 @@ Daemon::Daemon(QObject *parent, const QList<QVariant>&)
//Listen to incomming connections
Q_FOREACH (Announcer* a, announcers) {
connect(a,SIGNAL(onNewDeviceLink(QString,QString,DeviceLink*)),
this,SLOT(onNewDeviceLink(QString,QString,DeviceLink*)));
connect(a,SIGNAL(onNewDeviceLink(NetworkPackage,DeviceLink*)),
this,SLOT(onNewDeviceLink(NetworkPackage,DeviceLink*)));
}
QDBusConnection::sessionBus().registerService("org.kde.kdeconnect");
......@@ -106,14 +112,16 @@ QStringList Daemon::devices()
}
void Daemon::onNewDeviceLink(const QString& id, const QString& name, DeviceLink* dl)
void Daemon::onNewDeviceLink(const NetworkPackage& identityPackage, DeviceLink* dl)
{
qDebug() << "Device discovered" << dl->deviceId();
const QString& id = identityPackage.get<QString>("deviceId");
if (m_devices.contains(dl->deviceId())) {
qDebug() << "Device discovered" << id << "via" << dl->announcer()->name();
if (m_devices.contains(id)) {
qDebug() << "It is a known device";
Device* device = m_devices[dl->deviceId()];
Device* device = m_devices[id];
device->addLink(dl);
KNotification* notification = new KNotification("pingReceived"); //KNotification::Persistent
......@@ -127,8 +135,10 @@ void Daemon::onNewDeviceLink(const QString& id, const QString& name, DeviceLink*
} else {
qDebug() << "It is a new device";
const QString& name = identityPackage.get<QString>("deviceName");
Device* device = new Device(id,name,dl);
m_devices[dl->deviceId()] = device;
m_devices[id] = device;
Q_FOREACH (PackageReceiver* pr, packageReceivers) {
connect(device,SIGNAL(receivedPackage(const Device&, const NetworkPackage&)),
pr,SLOT(receivePackage(const Device&, const NetworkPackage&)));
......
......@@ -69,7 +69,7 @@ Q_SIGNALS:
private Q_SLOTS:
void onNewDeviceLink(const QString& id, const QString& name, DeviceLink* dl);
void onNewDeviceLink(const NetworkPackage& identityPackage, DeviceLink* dl);
private:
......
......@@ -2,6 +2,7 @@
#include <ksharedptr.h>
#include <ksharedconfig.h>
#include "devicelinks/devicelink.h"
#include "announcers/announcer.h"
#include <KConfigGroup>
#include <QDebug>
......@@ -63,7 +64,8 @@ static bool lessThan(DeviceLink* p1, DeviceLink* p2)
void Device::addLink(DeviceLink* link)
{
qDebug() << "AddLink";
qDebug() << "Adding link to " << id() << "via" << link->announcer();
connect(link,SIGNAL(destroyed(QObject*)),this,SLOT(linkDestroyed(QObject*)));
m_deviceLinks.append(link);
......@@ -74,20 +76,23 @@ void Device::addLink(DeviceLink* link)
void Device::linkDestroyed(QObject* o)
{
qDebug() << "Link destroyed";
removeLink(static_cast<DeviceLink*>(o));
}
void Device::removeLink(DeviceLink* link)
{
qDebug() << "RemoveLink";
disconnect(link, SIGNAL(receivedPackage(NetworkPackage)), this, SLOT(privateReceivedPackage(NetworkPackage)));
//disconnect(link, SIGNAL(receivedPackage(NetworkPackage)), this, SLOT(privateReceivedPackage(NetworkPackage)));
m_deviceLinks.removeOne(link);
}
bool Device::sendPackage(const NetworkPackage& np)
{
if (m_deviceLinks.empty()) return false;
return m_deviceLinks.first()->sendPackage(np);
Q_FOREACH(DeviceLink* dl, m_deviceLinks) {
if (dl->sendPackage(np)) return true;
}
return false;
}
void Device::privateReceivedPackage(const NetworkPackage& np)
......@@ -102,6 +107,15 @@ void Device::privateReceivedPackage(const NetworkPackage& np)
}
}
QStringList Device::availableLinks() const
{
QStringList sl;
Q_FOREACH(DeviceLink* dl, m_deviceLinks) {
sl.append(dl->announcer()->name());
}
return sl;
}
void Device::sendPing()
{
NetworkPackage np("kdeconnect.ping");
......
......@@ -59,6 +59,7 @@ public Q_SLOTS:
//Public dbus interface
Q_SCRIPTABLE QString id() const{ return m_deviceId; }
Q_SCRIPTABLE QString name() const { return m_deviceName; }
Q_SCRIPTABLE QStringList availableLinks() const;
Q_SCRIPTABLE bool paired() const { return m_paired; }
Q_SCRIPTABLE bool reachable() const { return !m_deviceLinks.empty(); }
Q_SCRIPTABLE void setPair(bool b);
......
......@@ -22,7 +22,7 @@
#include "announcers/fakeannouncer.h"
EchoDeviceLink::EchoDeviceLink(const QString& d, FakeAnnouncer* a)
EchoDeviceLink::EchoDeviceLink(const QString& d, LoopbackAnnouncer* a)
: DeviceLink(d, a)
{
......
......@@ -22,13 +22,13 @@
#define ECHODEVICELINK_H
#include "devicelink.h"
class FakeAnnouncer;
class LoopbackAnnouncer;
class EchoDeviceLink : public DeviceLink
{
Q_OBJECT
public:
EchoDeviceLink(const QString& d, FakeAnnouncer* a);
EchoDeviceLink(const QString& d, LoopbackAnnouncer* a);
bool sendPackage(const NetworkPackage& np) {
emit receivedPackage(np);
......
/**
* Copyright 2013 Albert Vaca <albertvaka@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 "tcpdevicelink.h"
#include "announcers/avahitcpannouncer.h"
TcpDeviceLink::TcpDeviceLink(const QString& d, AvahiTcpAnnouncer* a, QTcpSocket* socket)
: DeviceLink(d, a)