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

Merge branch 'filetransfer-debug'

parents 2054288c 2d065ab8
......@@ -15,11 +15,11 @@ Class diagram
Daemon instantiates Backends
Backends manage to create DeviceLinks with the devices they can reach, and Q_EMIT them to Daemon.
Backends manage to create DeviceLinks with the devices they can reach, and Q_EMIT them to the Daemon.
When Daemon receives a DeviceLink from a backend it:
- If he already knows the Device, adds the DeviceLink to the Device
- If not, it creates a new Device.
When the Daemon receives a DeviceLink from a backend:
- If it already knows the Device, adds the new DeviceLink to the existing Device, as a new way to reach it.
- If not, it creates a new untrusted (yet to be paired) Device.
Devices contain a list of DeviceLinks, plus a list of Plugins (instantiated automatically)
......@@ -40,15 +40,37 @@ are sent from one device to another (via a DeviceLink) serialized in json.
The basic structure of a NetworkPackage is the following:
{
"id": 123456789,
"type": "org.kde.whatever",
"body": {
"id": 123456789,
"type": "org.kde.whatever",
"body": {
},
"version": 1
},
"version": 5
}
Each type of package defines what it should contain inside its "body", so only
the emisor Plugin and receiver Plugin of this type of package need agree about
its content.
the emisor plugin and receiver plugin of this type of package need agree about
its content. Each plugin should provide a README explaining what does it write
in the "body" section, to ease porting to other platforms.
If the package has a payload attached, it will also contain two more fields:
"payloadSize": The size of the file, or -1 if it is a stream without known end
"payloadTransferInfo": Another JSON Object containing the information that the
backend wants to send to the peer backend, to actually
transfer the payload.
Encrypted network packages have the following format:
"type": is always set to "kdeconnect.encrypted".
"id": contains a new valid id (ie: not the same id as the unencrypted package).
"version": contains the package version.
"body": contains a "data" array that carries the original package encrypted.
The "data" array is filled the following way:
1. The original package is serialized.
2. The serialized string is divided in chunks small enough to be encrypted with
the other device's public key.
3. Each chunk is encrypted and appended to the array in order.
......@@ -14,7 +14,6 @@ X-KDE-System-Settings-Parent-Category=hardware
Name=Devices
Name[cs]=Zařízení
Name[de]=Geräte
Name[es]=Dispositivos
Name[nl]=Apparaten
Name[pt]=Dispositivos
Name[pt_BR]=Dispositivos
......@@ -25,7 +24,6 @@ Name[x-test]=xxDevicesxx
Comment=Connect and sync your devices with KDE
Comment[de]=Verbinden und Abgleichen Ihrer Geräte mit KDE
Comment[es]=Conectar y sincronizar dispositivos con KDE
Comment[nl]=Uw apparaten met KDE verbinden en synchroniseren
Comment[pt]=Ligue e sincronize os seus dispositivos com o KDE
Comment[pt_BR]=Conecta e sincroniza seus dispositivos com o KDE
......@@ -35,7 +33,6 @@ Comment[x-test]=xxConnect and sync your devices with KDExx
X-KDE-Keywords=Network,Android,Devices
X-KDE-Keywords[de]=Netzwerk,Android,Geräte
X-KDE-Keywords[es]=Red,Android,Dispositivos
X-KDE-Keywords[nl]=Netwerk,Android,Apparaten
X-KDE-Keywords[pt]=Rede,Android,Dispositivos
X-KDE-Keywords[pt_BR]=Rede,Android,Dispositivos
......
......@@ -4,24 +4,25 @@ find_package (QJSON REQUIRED)
find_package (QCA2 REQUIRED)
include_directories(
${KDE4_KIO_LIBS}
${QJSON_INCLUDE_DIR}
${QCA2_INCLUDE_DIR}
)
set(kded_kdeconnect_SRCS
add_subdirectory(backends/lan)
add_subdirectory(backends/loopback)
linkproviders/linkprovider.cpp
linkproviders/loopbacklinkprovider.cpp
linkproviders/lanlinkprovider.cpp
set(kded_kdeconnect_SRCS
${kded_kdeconnect_SRCS}
devicelinks/devicelink.cpp
devicelinks/loopbackdevicelink.cpp
devicelinks/landevicelink.cpp
backends/linkprovider.cpp
backends/devicelink.cpp
plugins/kdeconnectplugin.cpp
plugins/pluginloader.cpp
networkpackage.cpp
filetransferjob.cpp
daemon.cpp
device.cpp
)
......@@ -31,6 +32,7 @@ kde4_add_plugin(kded_kdeconnect ${kded_kdeconnect_SRCS})
target_link_libraries(kded_kdeconnect
${KDE4_KDECORE_LIBS}
${KDE4_KDEUI_LIBS}
${KDE4_KIO_LIBS}
${QT_QTNETWORK_LIBRARY}
${QJSON_LIBRARIES}
${QCA2_LIBRARIES}
......
......@@ -19,7 +19,7 @@
*/
#include "devicelink.h"
#include "linkproviders/linkprovider.h"
#include "linkprovider.h"
DeviceLink::DeviceLink(const QString& deviceId, LinkProvider* parent)
: QObject(parent)
......@@ -27,4 +27,5 @@ DeviceLink::DeviceLink(const QString& deviceId, LinkProvider* parent)
, mLinkProvider(parent)
{
//gcc complains if we don't add something to compile on a class with virtual functions
}
\ No newline at end of file
}
......@@ -22,10 +22,11 @@
#define DEVICELINK_H
#include <QObject>
#include <QtCrypto>
#include "../networkpackage.h"
class NetworkPackage;
class Device;
class LinkProvider;
class DeviceLink
......@@ -38,16 +39,22 @@ public:
const QString& deviceId() { return mDeviceId; }
LinkProvider* provider() { return mLinkProvider; }
virtual bool sendPackage(const NetworkPackage& np) = 0;
virtual bool sendPackage(NetworkPackage& np) = 0;
virtual bool sendPackageEncrypted(QCA::PublicKey& publicKey, NetworkPackage& np) = 0;
void setPrivateKey(const QCA::PrivateKey& privateKey) { mPrivateKey = privateKey; }
Q_SIGNALS:
void receivedPackage(const NetworkPackage& np);
protected:
QCA::PrivateKey mPrivateKey;
private:
QString mDeviceId;
LinkProvider* mLinkProvider;
};
#endif // DEVICELINK_H
#endif
set(kded_kdeconnect_SRCS
${kded_kdeconnect_SRCS}
backends/lan/lanlinkprovider.cpp
backends/lan/landevicelink.cpp
backends/lan/uploadjob.cpp
backends/lan/downloadjob.cpp
PARENT_SCOPE
)
/**
/*
* Copyright 2013 Albert Vaca <albertvaka@gmail.com>
*
* This program is free software; you can redistribute it and/or
......@@ -18,22 +18,32 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "loopbackdevicelink.h"
#include "downloadjob.h"
#include "linkproviders/loopbacklinkprovider.h"
LoopbackDeviceLink::LoopbackDeviceLink(const QString& deviceId, LoopbackLinkProvider* provider)
: DeviceLink(deviceId, provider)
DownloadJob::DownloadJob(QHostAddress address, QVariantMap transferInfo): KJob()
{
mAddress = address;
mPort = transferInfo["port"].toInt();
mSocket = QSharedPointer<QTcpSocket>(new QTcpSocket);
}
bool LoopbackDeviceLink::sendPackage(const NetworkPackage& toSend)
void DownloadJob::start()
{
NetworkPackage toReceive("");
NetworkPackage::unserialize(toSend.serialize(), &toReceive);
//qDebug() << "DownloadJob Start";
mSocket->connectToHost(mAddress, mPort, QIODevice::ReadOnly);
connect(mSocket.data(), SIGNAL(disconnected()),
this, SLOT(disconnected()));
//TODO: Implement payload encryption somehow (create an intermediate iodevice to encrypt the payload here?)
}
Q_EMIT receivedPackage(toReceive);
void DownloadJob::disconnected()
{
//qDebug() << "DownloadJob End";
emitResult();
}
return true;
QSharedPointer<QIODevice> DownloadJob::getPayload()
{
//qDebug() << "getPayload";
return mSocket.staticCast<QIODevice>();
}
/*
* 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 DOWNLOADJOB_H
#define DOWNLOADJOB_H
#include <KJob>
#include <QIODevice>
#include <QVariantMap>
#include <QHostAddress>
#include <QTcpSocket>
#include <QSharedPointer>
class DownloadJob
: public KJob
{
Q_OBJECT
public:
DownloadJob(QHostAddress address, QVariantMap transferInfo);
virtual void start();
QSharedPointer<QIODevice> getPayload();
private:
QHostAddress mAddress;
qint16 mPort;
QSharedPointer<QTcpSocket> mSocket;
private Q_SLOTS:
void disconnected();
};
#endif // UPLOADJOB_H
......@@ -25,8 +25,9 @@
#include <netinet/tcp.h>
#include <netdb.h>
#include "linkproviders/linkprovider.h"
#include "networkpackage.h"
#include "../linkprovider.h"
#include "uploadjob.h"
#include "downloadjob.h"
LanDeviceLink::LanDeviceLink(const QString& d, LinkProvider* a, QTcpSocket* socket)
: DeviceLink(d, a)
......@@ -52,26 +53,84 @@ LanDeviceLink::LanDeviceLink(const QString& d, LinkProvider* a, QTcpSocket* sock
this, SLOT(dataReceived()));
}
bool LanDeviceLink::sendPackage(const NetworkPackage& np)
bool LanDeviceLink::sendPackageEncrypted(QCA::PublicKey& key, NetworkPackage& np)
{
if (np.hasPayload()) {
UploadJob* job = new UploadJob(np.payload());
job->start();
np.setPayloadTransferInfo(job->getTransferInfo());
}
np.encrypt(key);
int written = mSocket->write(np.serialize());
return written != -1;
return (written != -1);
}
void LanDeviceLink::dataReceived()
bool LanDeviceLink::sendPackage(NetworkPackage& np)
{
qDebug() << "LanDeviceLink dataReceived";
if (np.hasPayload()) {
UploadJob* job = new UploadJob(np.payload());
job->start();
np.setPayloadTransferInfo(job->getTransferInfo());
}
int written = mSocket->write(np.serialize());
return (written != -1);
}
void LanDeviceLink::dataReceived()
{
QByteArray data = mSocket->readAll();
//qDebug() << "LanDeviceLink dataReceived" << data;
QList<QByteArray> packages = data.split('\n');
Q_FOREACH(const QByteArray& package, packages) {
if (package.length() < 3) continue;
if (package.length() < 3) {
continue;
}
NetworkPackage unserialized(QString::null);
NetworkPackage::unserialize(package, &unserialized);
if (unserialized.isEncrypted()) {
NetworkPackage np("");
NetworkPackage::unserialize(package, &np);
//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);
Q_EMIT receivedPackage(np);
if (decrypted.hasPayloadTransferInfo()) {
qDebug() << "HasPayloadTransferInfo";
DownloadJob* job = new DownloadJob(mSocket->peerAddress(), decrypted.payloadTransferInfo());
job->start();
decrypted.setPayload(job->getPayload(), decrypted.payloadSize());
}
Q_EMIT receivedPackage(decrypted);
} else {
if (unserialized.hasPayloadTransferInfo()) {
qWarning() << "Ignoring unencrypted payload";
continue;
}
Q_EMIT receivedPackage(unserialized);
}
}
//qDebug() << "MOAR BYTES" << mSocket->bytesAvailable() << packages.length();
if (mSocket->bytesAvailable() > 0) {
QMetaObject::invokeMethod(this, "dataReceived", Qt::QueuedConnection);
}
}
void LanDeviceLink::readyRead()
{
}
......@@ -23,11 +23,10 @@
#include <QObject>
#include <QString>
#include <qthread.h>
#include "devicelink.h"
#include <QTcpSocket>
#include "../devicelink.h"
class AvahiTcpLinkProvider;
class LanDeviceLink
......@@ -38,10 +37,12 @@ class LanDeviceLink
public:
LanDeviceLink(const QString& d, LinkProvider* a, QTcpSocket* socket);
bool sendPackage(const NetworkPackage& np);
bool sendPackage(NetworkPackage& np);
bool sendPackageEncrypted(QCA::PublicKey& key, NetworkPackage& np);
private Q_SLOTS:
void dataReceived();
void readyRead();
private:
QTcpSocket* mSocket;
......
......@@ -24,8 +24,7 @@
#include <QTcpServer>
#include <QUdpSocket>
#include "devicelinks/landevicelink.h"
#include "networkpackage.h"
#include "landevicelink.h"
LanLinkProvider::LanLinkProvider()
{
......@@ -57,7 +56,8 @@ void LanLinkProvider::onStop()
//I'm in a new network, let's be polite and introduce myself
void LanLinkProvider::onNetworkChange(QNetworkSession::State state)
{
qDebug() << "onNetworkChange" << state;
Q_UNUSED(state);
//qDebug() << "onNetworkChange" << state;
NetworkPackage np("");
NetworkPackage::createIdentityPackage(&np);
np.set("tcpPort",tcpPort);
......@@ -84,13 +84,13 @@ void LanLinkProvider::newUdpConnection()
NetworkPackage::createIdentityPackage(&np2);
if (np->get<QString>("deviceId") == np2.get<QString>("deviceId")) {
qDebug() << "Ignoring my own broadcast";
//qDebug() << "Ignoring my own broadcast";
return;
}
int tcpPort = np->get<int>("tcpPort",port);
qDebug() << "Received Udp presentation from" << sender << "asking for a tcp connection on port " << tcpPort;
//qDebug() << "Received Udp presentation from" << sender << "asking for a tcp connection on port " << tcpPort;
QTcpSocket* socket = new QTcpSocket(this);
receivedIdentityPackages[socket].np = np;
......@@ -146,7 +146,7 @@ void LanLinkProvider::connected()
//TODO: Use reverse connection too to preffer connecting a unstable device (a phone) to a stable device (a computer)
if (success) {
qDebug() << "Handshaking done (i'm the existing device)";
//qDebug() << "Handshaking done (i'm the existing device)";
connect(dl, SIGNAL(destroyed(QObject*)),
this, SLOT(deviceLinkDestroyed(QObject*)));
......@@ -180,7 +180,7 @@ void LanLinkProvider::connected()
//I'm the new device and this is the answer to my UDP introduction (no data received yet)
void LanLinkProvider::newConnection()
{
qDebug() << "LanLinkProvider newConnection";
//qDebug() << "LanLinkProvider newConnection";
QTcpSocket* socket = mTcpServer->nextPendingConnection();
socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
......@@ -203,7 +203,7 @@ void LanLinkProvider::dataReceived()
QByteArray data = socket->readLine();
qDebug() << "LanLinkProvider received reply:" << data;
//qDebug() << "LanLinkProvider received reply:" << data;
NetworkPackage np("");
bool success = NetworkPackage::unserialize(data,&np);
......@@ -213,7 +213,7 @@ void LanLinkProvider::dataReceived()
const QString& id = np.get<QString>("deviceId");
LanDeviceLink* dl = new LanDeviceLink(id, this, socket);
qDebug() << "Handshaking done (i'm the new device)";
//qDebug() << "Handshaking done (i'm the new device)";
connect(dl, SIGNAL(destroyed(QObject*)),
this, SLOT(deviceLinkDestroyed(QObject*)));
......@@ -241,7 +241,7 @@ void LanLinkProvider::dataReceived()
void LanLinkProvider::deviceLinkDestroyed(QObject* uncastedDeviceLink)
{
qDebug() << "deviceLinkDestroyed";
//qDebug() << "deviceLinkDestroyed";
DeviceLink* deviceLink = (DeviceLink*)uncastedDeviceLink;
const QString& id = deviceLink->deviceId();
......
......@@ -25,7 +25,7 @@
#include <QTcpServer>
#include <QUdpSocket>
#include "linkprovider.h"
#include "../linkprovider.h"
#include "netaddress.h"
class LanLinkProvider
......
/*
* 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 <qalgorithms.h>
#include <QDebug>
#include "uploadjob.h"
UploadJob::UploadJob(const QSharedPointer<QIODevice>& source): KJob()
{
mInput = source;
mServer = new QTcpServer(this);
mSocket = 0;
}
void UploadJob::start()
{
mPort = 1739;
while(!mServer->listen(QHostAddress::Any, mPort)) {
mPort++;
if (mPort > 1764) { //No ports available?
qDebug() << "Error opening a port in range 1739-1764 for file transfer";
mPort = 0;
return;
}
}
connect(mServer, SIGNAL(newConnection()), this, SLOT(newConnection()));
}
void UploadJob::newConnection()
{
if (mSocket || !mServer->hasPendingConnections()) return;
mSocket = mServer->nextPendingConnection();
connect(mInput.data(), SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(mInput.data(), SIGNAL(aboutToClose()), this, SLOT(aboutToClose()));
if (!mInput->open(QIODevice::ReadOnly)) {
return; //TODO: Handle error, clean up...
}
}
void UploadJob::readyRead()
{
//TODO: Implement payload encryption
qint64 bytes = qMax(mInput->bytesAvailable(), (qint64)4096);
mSocket->write(mInput->read(bytes));
}
void UploadJob::aboutToClose()
{
mSocket->close();
mSocket->disconnectFromHost();
emitResult();
}
QVariantMap UploadJob::getTransferInfo()
{
QVariantMap ret;
ret["port"] = mPort;
return ret;
}
/*
* 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/>.