Commit e6dfaa59 authored by Albert Vaca Cintora's avatar Albert Vaca Cintora

Added BroadcastTcpLinkProvider

parent 04c59555
......@@ -3,6 +3,7 @@ set(kded_kdeconnect_SRCS
linkproviders/linkprovider.cpp
linkproviders/loopbacklinkprovider.cpp
linkproviders/avahitcplinkprovider.cpp
linkproviders/broadcasttcplinkprovider.cpp
devicelinks/devicelink.cpp
devicelinks/echodevicelink.cpp
......
......@@ -29,27 +29,25 @@
#include "packageinterfaces/batterypackageinterface.h"
#include "packageinterfaces/mpriscontrolpackageinterface.h"
#include "linkproviders/broadcasttcplinkprovider.h"
#include "linkproviders/avahitcplinkprovider.h"
#include "linkproviders/loopbacklinkprovider.h"
#include <QtNetwork/QUdpSocket>
#include <QFile>
#include <quuid.h>
#include <QUuid>
#include <QDBusConnection>
#include <QNetworkSession>
#include <QNetworkConfigurationManager>
#include <KIcon>
#include <KConfigGroup>
#include <sstream>
#include <iomanip>
#include <iostream>
K_PLUGIN_FACTORY(KdeConnectFactory, registerPlugin<Daemon>();)
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")) {
......@@ -70,7 +68,8 @@ 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
mLinkProviders.insert(new AvahiTcpLinkProvider());
mLinkProviders.insert(new BroadcastTcpLinkProvider());
//mLinkProviders.insert(new AvahiTcpLinkProvider());
//mLinkProviders.insert(new LoopbackLinkProvider());
//Read remebered paired devices
......@@ -90,8 +89,12 @@ Daemon::Daemon(QObject *parent, const QList<QVariant>&)
}
}
QNetworkSession* network = new QNetworkSession(QNetworkConfigurationManager().defaultConfiguration());
//Listen to incomming connections
Q_FOREACH (LinkProvider* a, mLinkProviders) {
connect(network, SIGNAL(stateChanged(QNetworkSession::State)),
a, SLOT(onNetworkChange(QNetworkSession::State)));
connect(a,SIGNAL(onNewDeviceLink(NetworkPackage,DeviceLink*)),
this,SLOT(onNewDeviceLink(NetworkPackage,DeviceLink*)));
}
......@@ -105,10 +108,19 @@ void Daemon::setDiscoveryEnabled(bool b)
{
//Listen to incomming connections
Q_FOREACH (LinkProvider* a, mLinkProviders) {
a->setDiscoverable(b);
if (b)
a->onStart();
else
a->onStop();
}
}
void Daemon::forceOnNetworkChange()
{
Q_FOREACH (LinkProvider* a, mLinkProviders) {
a->onNetworkChange(QNetworkSession::Connected);
}
}
QStringList Daemon::devices()
{
......
......@@ -60,6 +60,8 @@ public Q_SLOTS:
//After calling this, signal deviceDiscovered will be triggered for each device
Q_SCRIPTABLE void setDiscoveryEnabled(bool b);
Q_SCRIPTABLE void forceOnNetworkChange();
//Returns a list of ids. The respective devices can be manipulated using the dbus path: "/modules/kdeconnect/Devices/"+id
Q_SCRIPTABLE QStringList devices();
......
......@@ -19,9 +19,9 @@
*/
#include "tcpdevicelink.h"
#include "linkproviders/avahitcplinkprovider.h"
#include "linkproviders/linkprovider.h"
TcpDeviceLink::TcpDeviceLink(const QString& d, AvahiTcpLinkProvider* a, QTcpSocket* socket)
TcpDeviceLink::TcpDeviceLink(const QString& d, LinkProvider* a, QTcpSocket* socket)
: DeviceLink(d, a)
{
mSocket = socket;
......
......@@ -36,7 +36,7 @@ class TcpDeviceLink
Q_OBJECT
public:
TcpDeviceLink(const QString& d, AvahiTcpLinkProvider* a, QTcpSocket* socket);
TcpDeviceLink(const QString& d, LinkProvider* a, QTcpSocket* socket);
bool sendPackage(const NetworkPackage& np);
......
......@@ -28,7 +28,6 @@
AvahiTcpLinkProvider::AvahiTcpLinkProvider()
{
QString serviceType = "_kdeconnect._tcp";
quint16 port = 10602;
//http://api.kde.org/4.x-api/kdelibs-apidocs/dnssd/html/index.html
......@@ -36,8 +35,25 @@ AvahiTcpLinkProvider::AvahiTcpLinkProvider()
mServer = new QTcpServer(this);
connect(mServer,SIGNAL(newConnection()),this, SLOT(newConnection()));
}
void AvahiTcpLinkProvider::onStart()
{
mServer->listen(QHostAddress::Any, port);
service->publishAsync();
}
void AvahiTcpLinkProvider::onStop()
{
mServer->close();
service->stop();
}
void AvahiTcpLinkProvider::onNetworkChange(QNetworkSession::State state)
{
//Nothing to do, Avahi will handle it
}
void AvahiTcpLinkProvider::newConnection()
......@@ -104,9 +120,3 @@ AvahiTcpLinkProvider::~AvahiTcpLinkProvider()
delete service;
}
void AvahiTcpLinkProvider::setDiscoverable(bool b)
{
qDebug() << "AvahiTcpLinkProvider discoverable:" << b;
if (b) service->publishAsync();
}
......@@ -42,7 +42,10 @@ public:
QString name() { return "AvahiTcpLinkProvider"; }
int priority() { return PRIORITY_HIGH + 1; }
void setDiscoverable(bool b);
public Q_SLOTS:
virtual void onNetworkChange(QNetworkSession::State state);
virtual void onStart();
virtual void onStop();
private Q_SLOTS:
void newConnection();
......@@ -53,6 +56,7 @@ private:
DNSSD::PublicService* service;
QTcpServer* mServer;
static const quint16 port = 10602;
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 "broadcasttcplinkprovider.h"
#include "devicelinks/tcpdevicelink.h"
#include <QHostInfo>
#include <QTcpServer>
#include <QUdpSocket>
BroadcastTcpLinkProvider::BroadcastTcpLinkProvider()
{
mUdpServer = new QUdpSocket(this);
connect(mUdpServer, SIGNAL(readyRead()), this, SLOT(newUdpConnection()));
mTcpServer = new QTcpServer(this);
connect(mTcpServer,SIGNAL(newConnection()),this, SLOT(newConnection()));
}
void BroadcastTcpLinkProvider::onStart()
{
mUdpServer->bind(QHostAddress::Broadcast, port, QUdpSocket::ShareAddress);
mTcpServer->listen(QHostAddress::Any, port);
onNetworkChange(QNetworkSession::Connected);
}
void BroadcastTcpLinkProvider::onStop()
{
mUdpServer->close();
mTcpServer->close();
}
//I'm in a new network, let's be polite and introduce myself
void BroadcastTcpLinkProvider::onNetworkChange(QNetworkSession::State state) {
qDebug() << "onNetworkChange" << state;
NetworkPackage np("");
NetworkPackage::createIdentityPackage(&np);
QUdpSocket().writeDatagram(np.serialize(),QHostAddress("255.255.255.255"), port);
}
//I'm the existing device, a new device is kindly introducing itself (I will create a TcpSocket)
void BroadcastTcpLinkProvider::newUdpConnection()
{
while (mUdpServer->hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(mUdpServer->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
mUdpServer->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
NetworkPackage np("");
NetworkPackage::unserialize(datagram,&np);
if (np.version() > 0 && np.type() == PACKAGE_TYPE_IDENTITY) {
NetworkPackage np2("");
NetworkPackage::createIdentityPackage(&np2);
if (np.get<QString>("deviceId") == np2.get<QString>("deviceId")) {
qDebug() << "I can't fuck myself!";
return;
}
const QString& id = np.get<QString>("deviceId");
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.remove(id);
}
QTcpSocket* socket = new QTcpSocket(this);
qDebug() << "Received Udp presentation from" << sender << "asking for a tcp connection...";
socket->connectToHost(sender, port);
socket->waitForConnected();
qDebug() << "Connected" << socket->isWritable();
TcpDeviceLink* dl = new TcpDeviceLink(id, this, socket);
connect(dl,SIGNAL(destroyed(QObject*)),this,SLOT(deviceLinkDestroyed(QObject*)));
links[id] = dl;
bool success = dl->sendPackage(np2);
if (!success) { //FIXME: Why is this happening?
qDebug() << "Fallback, try reverse connection";
QUdpSocket().writeDatagram(np2.serialize(),sender, port);
}
qDebug() << "Handshaking done (i'm the existing device)";
emit onNewDeviceLink(np, dl);
}
}
}
//I'm the new device and this is the answer to my UDP introduction (no data received yet)
void BroadcastTcpLinkProvider::newConnection()
{
qDebug() << "BroadcastTcpLinkProvider newConnection";
QTcpSocket* socket = mTcpServer->nextPendingConnection();
socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
connect(socket,SIGNAL(readyRead()),this,SLOT(dataReceived()));
/*
NetworkPackage np(PACKAGE_TYPE_IDENTITY);
NetworkPackage::createIdentityPackage(&np);
int written = socket->write(np.serialize());
qDebug() << "BroadcastTcpLinkProvider sent package." << written << " bytes written, waiting for reply";
*/
}
//I'm the new device and this is the answer to my UDP introduction (data received)
void BroadcastTcpLinkProvider::dataReceived()
{
QTcpSocket* socket = (QTcpSocket*) QObject::sender();
QByteArray data = socket->readLine();
qDebug() << "BroadcastTcpLinkProvider 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() << "BroadcastTcpLinkProvider creating link to device" << id << "(" << socket->peerAddress() << ")";
qDebug() << "Handshaking done (i'm the new device)";
emit onNewDeviceLink(np, dl);
disconnect(socket,SIGNAL(readyRead()),this,SLOT(dataReceived()));
} else {
qDebug() << "BroadcastTcpLinkProvider/newConnection: Not an identification package (wuh?)";
}
}
void BroadcastTcpLinkProvider::deviceLinkDestroyed(QObject* deviceLink)
{
const QString& id = ((DeviceLink*)deviceLink)->deviceId();
qDebug() << "deviceLinkDestroyed";
if (links.contains(id)) {
qDebug() << "removing link from link list";
links.remove(id);
}
}
BroadcastTcpLinkProvider::~BroadcastTcpLinkProvider()
{
}
/**
* 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 BROADCASTTCPLINKPROVIDER_H
#define BROADCASTTCPLINKPROVIDER_H
#include <QObject>
#include <QTcpServer>
#include <QUdpSocket>
#include "linkprovider.h"
#include "netaddress.h"
class BroadcastTcpLinkProvider
: public LinkProvider
{
Q_OBJECT
public:
BroadcastTcpLinkProvider();
~BroadcastTcpLinkProvider();
QString name() { return "BroadcastTcpLinkProvider"; }
int priority() { return PRIORITY_HIGH + 5; }
public Q_SLOTS:
virtual void onNetworkChange(QNetworkSession::State state);
virtual void onStart();
virtual void onStop();
private Q_SLOTS:
void newUdpConnection();
void newConnection();
void dataReceived();
void deviceLinkDestroyed(QObject*);
private:
QTcpServer* mTcpServer;
QUdpSocket* mUdpServer;
const static quint16 port = 1714;
QMap<QString, DeviceLink*> links;
};
#endif
......@@ -21,8 +21,9 @@
#ifndef LINKPROVIDER_H
#define LINKPROVIDER_H
#include <qvector.h>
#include <QObject>
#include <QVector>
#include <QNetworkSession>
#include "devicelinks/devicelink.h"
#include "device.h"
......@@ -46,7 +47,10 @@ public:
virtual QString name() = 0;
virtual int priority() = 0;
virtual void setDiscoverable(bool b) = 0;
public Q_SLOTS:
virtual void onStart() = 0;
virtual void onStop() = 0;
virtual void onNetworkChange(QNetworkSession::State state) = 0;
Q_SIGNALS:
//NOTE: The provider will to destroy the DeviceLink when it's no longer accessible,
......
......@@ -23,23 +23,40 @@
#include <QDebug>
#include <kicon.h>
NotificationPackageInterface::NotificationPackageInterface(QObject* parent)
: PackageInterface(parent)
{
//TODO: Split in EventNotificationInterface and NotificationDrawerSyncInterface
trayIcon = new KStatusNotifierItem(parent);
trayIcon->setIconByName("pda");
trayIcon->setTitle("KdeConnect");
connect(trayIcon,SIGNAL(activateRequested(bool,QPoint)),this,SLOT(showPendingNotifications()));
}
KNotification* NotificationPackageInterface::createNotification(const QString& deviceName, const NetworkPackage& np)
{
QString id = QString::number(np.id());
QString npType = np.get<QString>("notificationType");
QString title, content, type, icon;
bool transient;
title = deviceName;
if (npType == "ringing") {
type = "callReceived";
icon = "call-start";
content = "Incoming call from " + np.get<QString>("phoneNumber","unknown number");
transient = false;
} else if (npType == "missedCall") {
type = "missedCall";
icon = "call-start";
content = "Missed call from " + np.get<QString>("phoneNumber","unknown number");
transient = true;
} else if (npType == "sms") {
type = "smsReceived";
icon = "mail-receive";
......@@ -47,33 +64,76 @@ KNotification* NotificationPackageInterface::createNotification(const QString& d
+ np.get<QString>("phoneNumber","unknown number")
+ ":\n"
+ np.get<QString>("messageBody","");
transient = true;
} else if (npType == "battery") {
type = "battery100";
icon = "battery-100";
content = "Battery at " + np.get<QString>("batteryLevel") + "%";
transient = false;
} else if (npType == "notification") {
type = "pingReceived";
icon = "dialog-ok";
content = np.get<QString>("notificationContent");
transient = false;
} else {
//TODO: return NULL if !debug
type = "unknownEvent";
icon = "pda";
content = "Unknown notification type: " + npType;
transient = false;
}
qDebug() << "Creating notification with type:" << type;
if (transient) {
trayIcon->setStatus(KStatusNotifierItem::Active);
KNotification* notification = new KNotification(type); //KNotification::Persistent
notification->setPixmap(KIcon(icon).pixmap(48, 48));
notification->setComponentData(KComponentData("kdeconnect", "kdeconnect"));
notification->setTitle(title);
notification->setText(content);
pendingNotifications.insert(id, notification);
}
KNotification* notification = new KNotification(type); //KNotification::Persistent
notification->setPixmap(KIcon(icon).pixmap(48, 48));
notification->setComponentData(KComponentData("kdeconnect", "kdeconnect"));
notification->setTitle(title);
notification->setText(content);
notification->setProperty("id",id);
connect(notification,SIGNAL(activated()),this,SLOT(notificationAttended()));
connect(notification,SIGNAL(closed()),this,SLOT(notificationAttended()));
return notification;
}
void NotificationPackageInterface::notificationAttended()
{
KNotification* normalNotification = (KNotification*)sender();
QString id = normalNotification->property("id").toString();
if (pendingNotifications.contains(id)) {
delete pendingNotifications[id];
pendingNotifications.remove(id);
if (pendingNotifications.isEmpty()) {
trayIcon->setStatus(KStatusNotifierItem::Passive);
}
}
}
void NotificationPackageInterface::showPendingNotifications()
{
trayIcon->setStatus(KStatusNotifierItem::Passive);
Q_FOREACH (KNotification* notification, pendingNotifications) {
notification->sendEvent();
}
pendingNotifications.clear();
}
bool NotificationPackageInterface::receivePackage(const Device& device, const NetworkPackage& np)
{
......
......@@ -25,17 +25,26 @@
#include "packageinterface.h"
#include <KStatusNotifierItem>
class NotificationPackageInterface
: public PackageInterface
{
Q_OBJECT
public:
NotificationPackageInterface(QObject* parent = 0);
virtual bool receivePackage(const Device&, const NetworkPackage& np);
private:
static KNotification* createNotification(const QString& deviceName,const NetworkPackage& np);
KNotification* createNotification(const QString& deviceName,const NetworkPackage& np);
KStatusNotifierItem* trayIcon;
QHash<QString, KNotification*> pendingNotifications;
public slots:
void showPendingNotifications();
void notificationAttended();
};