2013-06-06 04:57:06 +01:00
|
|
|
/**
|
|
|
|
* 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 "networkpackage.h"
|
2013-08-31 12:04:00 +01:00
|
|
|
|
|
|
|
#include <KSharedConfig>
|
|
|
|
#include <KConfigGroup>
|
2013-09-09 17:30:44 +01:00
|
|
|
|
2014-09-12 23:49:56 +01:00
|
|
|
#include <QMetaObject>
|
|
|
|
#include <QMetaProperty>
|
2013-08-31 12:04:00 +01:00
|
|
|
#include <QByteArray>
|
|
|
|
#include <QDataStream>
|
2013-07-23 15:11:54 +01:00
|
|
|
#include <QHostInfo>
|
2013-08-31 12:04:00 +01:00
|
|
|
#include <QSslKey>
|
|
|
|
#include <QDateTime>
|
2014-09-12 23:49:56 +01:00
|
|
|
#include <qjsondocument.h>
|
2013-09-08 18:06:13 +01:00
|
|
|
#include <QtCrypto>
|
2013-06-06 04:57:06 +01:00
|
|
|
|
2013-09-24 13:10:25 +01:00
|
|
|
#include "filetransferjob.h"
|
|
|
|
|
2013-09-03 01:13:13 +01:00
|
|
|
const QCA::EncryptionAlgorithm NetworkPackage::EncryptionAlgorithm = QCA::EME_PKCS1v15;
|
2013-09-20 14:54:30 +01:00
|
|
|
const int NetworkPackage::ProtocolVersion = 5;
|
2013-09-03 01:13:13 +01:00
|
|
|
|
2013-08-07 12:40:39 +01:00
|
|
|
NetworkPackage::NetworkPackage(const QString& type)
|
2013-07-04 00:09:49 +01:00
|
|
|
{
|
2013-09-01 21:13:03 +01:00
|
|
|
mId = QString::number(QDateTime::currentMSecsSinceEpoch());
|
2013-07-04 00:09:49 +01:00
|
|
|
mType = type;
|
2013-09-01 21:13:03 +01:00
|
|
|
mBody = QVariantMap();
|
2013-09-24 13:13:02 +01:00
|
|
|
mPayload = QSharedPointer<QIODevice>();
|
2013-09-20 14:54:30 +01:00
|
|
|
mPayloadSize = 0;
|
2013-09-09 17:30:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void NetworkPackage::createIdentityPackage(NetworkPackage* np)
|
|
|
|
{
|
|
|
|
KSharedConfigPtr config = KSharedConfig::openConfig("kdeconnectrc");
|
2014-03-04 01:34:09 +00:00
|
|
|
const QString id = config->group("myself").readEntry<QString>("id","");
|
2013-09-09 17:30:44 +01:00
|
|
|
np->mId = QString::number(QDateTime::currentMSecsSinceEpoch());
|
|
|
|
np->mType = PACKAGE_TYPE_IDENTITY;
|
2013-09-24 13:13:02 +01:00
|
|
|
np->mPayload = QSharedPointer<QIODevice>();
|
2013-09-20 14:54:30 +01:00
|
|
|
np->mPayloadSize = 0;
|
2013-09-09 17:30:44 +01:00
|
|
|
np->set("deviceId", id);
|
2014-06-21 01:05:55 +01:00
|
|
|
np->set("deviceName", qgetenv("USER") + "@" + QHostInfo::localHostName());
|
2013-11-06 17:34:51 +00:00
|
|
|
np->set("protocolType", "desktop"); //TODO: Detect laptop, tablet, phone...
|
2013-09-09 17:30:44 +01:00
|
|
|
np->set("protocolVersion", NetworkPackage::ProtocolVersion);
|
|
|
|
|
2013-11-06 21:16:55 +00:00
|
|
|
//kDebug(kdeconnect_kded()) << "createIdentityPackage" << np->serialize();
|
2013-07-03 02:52:44 +01:00
|
|
|
}
|
|
|
|
|
2014-09-12 23:49:56 +01:00
|
|
|
QVariantMap qobject2qvairant(const QObject* object)
|
|
|
|
{
|
|
|
|
QVariantMap map;
|
|
|
|
auto metaObject = object->metaObject();
|
|
|
|
for(int i = metaObject->propertyOffset(); i < metaObject->propertyCount(); ++i) {
|
|
|
|
const char *name = metaObject->property(i).name();
|
|
|
|
map.insert(QString::fromLatin1(name), object->property(name));
|
|
|
|
}
|
|
|
|
|
|
|
|
return map;
|
|
|
|
}
|
|
|
|
|
2013-07-04 00:09:49 +01:00
|
|
|
QByteArray NetworkPackage::serialize() const
|
2013-06-06 04:57:06 +01:00
|
|
|
{
|
2013-07-04 00:09:49 +01:00
|
|
|
//Object -> QVariant
|
|
|
|
//QVariantMap variant;
|
|
|
|
//variant["id"] = mId;
|
|
|
|
//variant["type"] = mType;
|
|
|
|
//variant["body"] = mBody;
|
2014-09-12 23:49:56 +01:00
|
|
|
QVariantMap variant = qobject2qvairant(this);
|
2013-07-04 00:09:49 +01:00
|
|
|
|
2013-09-09 17:30:44 +01:00
|
|
|
if (hasPayload()) {
|
2013-11-06 21:16:55 +00:00
|
|
|
//kDebug(kdeconnect_kded()) << "Serializing payloadTransferInfo";
|
2014-03-03 03:50:53 +00:00
|
|
|
variant["payloadSize"] = payloadSize();
|
2013-09-09 17:30:44 +01:00
|
|
|
variant["payloadTransferInfo"] = mPayloadTransferInfo;
|
|
|
|
}
|
|
|
|
|
2013-07-04 00:09:49 +01:00
|
|
|
//QVariant -> json
|
|
|
|
bool ok;
|
2014-09-12 23:49:56 +01:00
|
|
|
auto jsonDocument = QJsonDocument::fromVariant(variant);
|
|
|
|
QByteArray json = jsonDocument.toJson(QJsonDocument::Compact);
|
|
|
|
if (json.isEmpty()) {
|
2014-09-21 22:54:27 +01:00
|
|
|
qCDebug(KDECONNECT_CORE) << "Serialization error:";
|
2013-07-24 22:51:06 +01:00
|
|
|
} else {
|
2013-09-16 14:21:22 +01:00
|
|
|
if (!isEncrypted()) {
|
2014-07-01 00:25:12 +01:00
|
|
|
//kDebug(kDebugArea) << "Serialized package:" << json;
|
2013-09-16 14:21:22 +01:00
|
|
|
}
|
2013-07-24 22:51:06 +01:00
|
|
|
json.append('\n');
|
|
|
|
}
|
2013-07-04 00:09:49 +01:00
|
|
|
|
|
|
|
return json;
|
2013-06-06 04:57:06 +01:00
|
|
|
}
|
|
|
|
|
2014-09-12 23:49:56 +01:00
|
|
|
void qvariant2qobject(const QVariantMap& variant, QObject* object)
|
|
|
|
{
|
|
|
|
for ( QVariantMap::const_iterator iter = variant.begin(); iter != variant.end(); ++iter )
|
|
|
|
{
|
|
|
|
QVariant property = object->property( iter.key().toLatin1() );
|
|
|
|
Q_ASSERT( property.isValid() );
|
|
|
|
if ( property.isValid() )
|
|
|
|
{
|
|
|
|
QVariant value = iter.value();
|
|
|
|
if ( value.canConvert( property.type() ) )
|
|
|
|
{
|
|
|
|
value.convert( property.type() );
|
|
|
|
object->setProperty( iter.key().toLatin1(), value );
|
|
|
|
} else if ( QString( QLatin1String("QVariant") ).compare( QLatin1String( property.typeName() ) ) == 0) {
|
|
|
|
object->setProperty( iter.key().toLatin1(), value );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-02 12:26:26 +01:00
|
|
|
bool NetworkPackage::unserialize(const QByteArray& a, NetworkPackage* np)
|
2013-06-17 11:23:08 +01:00
|
|
|
{
|
2013-07-04 00:09:49 +01:00
|
|
|
//Json -> QVariant
|
2014-09-12 23:49:56 +01:00
|
|
|
QJsonParseError parseError;
|
|
|
|
auto parser = QJsonDocument::fromJson(a, &parseError);
|
|
|
|
if (parser.isNull()) {
|
2014-09-21 22:54:27 +01:00
|
|
|
qCDebug(KDECONNECT_CORE) << "Unserialization error:" << parseError.errorString();
|
2013-09-03 15:01:28 +01:00
|
|
|
return false;
|
2013-07-04 02:34:35 +01:00
|
|
|
}
|
2013-07-04 00:09:49 +01:00
|
|
|
|
2014-09-12 23:49:56 +01:00
|
|
|
auto variant = parser.toVariant().toMap();
|
|
|
|
qvariant2qobject(variant, np);
|
2013-09-09 17:28:52 +01:00
|
|
|
|
|
|
|
if (!np->isEncrypted()) {
|
2014-07-01 00:25:12 +01:00
|
|
|
//kDebug(kDebugArea) << "Unserialized: " << a;
|
2013-09-18 17:36:08 +01:00
|
|
|
}
|
|
|
|
|
2013-09-20 14:54:30 +01:00
|
|
|
np->mPayloadSize = variant["payloadSize"].toInt(); //Will return 0 if was not present, which is ok
|
2014-03-03 20:08:34 +00:00
|
|
|
if (np->mPayloadSize == -1) {
|
|
|
|
np->mPayloadSize = np->get<int>("size", -1);
|
|
|
|
}
|
2013-09-20 14:54:30 +01:00
|
|
|
np->mPayloadTransferInfo = variant["payloadTransferInfo"].toMap(); //Will return an empty qvariantmap if was not present, which is ok
|
2013-09-09 17:30:44 +01:00
|
|
|
|
2013-09-03 15:01:28 +01:00
|
|
|
return true;
|
2013-07-23 19:22:38 +01:00
|
|
|
|
2013-06-17 11:23:08 +01:00
|
|
|
}
|
|
|
|
|
2013-09-09 21:50:27 +01:00
|
|
|
void NetworkPackage::encrypt(QCA::PublicKey& key)
|
2013-09-01 21:13:03 +01:00
|
|
|
{
|
2013-09-02 02:17:23 +01:00
|
|
|
|
2013-09-01 21:13:03 +01:00
|
|
|
QByteArray serialized = serialize();
|
2013-09-02 02:17:23 +01:00
|
|
|
|
2013-09-02 12:26:26 +01:00
|
|
|
int chunkSize = key.maximumEncryptSize(NetworkPackage::EncryptionAlgorithm);
|
2013-09-02 02:17:23 +01:00
|
|
|
|
|
|
|
QStringList chunks;
|
|
|
|
while (!serialized.isEmpty()) {
|
2014-03-04 01:34:09 +00:00
|
|
|
const QByteArray chunk = serialized.left(chunkSize);
|
2013-09-02 02:17:23 +01:00
|
|
|
serialized = serialized.mid(chunkSize);
|
2014-03-04 01:34:09 +00:00
|
|
|
const QByteArray encryptedChunk = key.encrypt(chunk, NetworkPackage::EncryptionAlgorithm).toByteArray();
|
2013-09-02 02:17:23 +01:00
|
|
|
chunks.append( encryptedChunk.toBase64() );
|
|
|
|
}
|
|
|
|
|
2013-11-06 21:16:55 +00:00
|
|
|
//kDebug(kdeconnect_kded()) << chunks.size() << "chunks";
|
2013-09-01 21:13:03 +01:00
|
|
|
|
|
|
|
mId = QString::number(QDateTime::currentMSecsSinceEpoch());
|
2013-09-02 12:26:26 +01:00
|
|
|
mType = PACKAGE_TYPE_ENCRYPTED;
|
2013-09-01 21:13:03 +01:00
|
|
|
mBody = QVariantMap();
|
2013-09-02 02:17:23 +01:00
|
|
|
mBody["data"] = chunks;
|
2013-09-01 21:13:03 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-09-09 21:50:27 +01:00
|
|
|
bool NetworkPackage::decrypt(QCA::PrivateKey& key, NetworkPackage* out) const
|
2013-09-01 21:13:03 +01:00
|
|
|
{
|
2013-09-09 21:50:27 +01:00
|
|
|
|
2013-09-02 02:17:23 +01:00
|
|
|
const QStringList& chunks = mBody["data"].toStringList();
|
|
|
|
|
|
|
|
QByteArray decryptedJson;
|
|
|
|
Q_FOREACH(const QString& chunk, chunks) {
|
2014-03-04 01:34:09 +00:00
|
|
|
const QByteArray encryptedChunk = QByteArray::fromBase64(chunk.toAscii());
|
2013-09-02 02:17:23 +01:00
|
|
|
QCA::SecureArray decryptedChunk;
|
2013-09-02 12:26:26 +01:00
|
|
|
bool success = key.decrypt(encryptedChunk, &decryptedChunk, NetworkPackage::EncryptionAlgorithm);
|
2013-09-03 01:14:27 +01:00
|
|
|
if (!success) {
|
|
|
|
return false;
|
|
|
|
}
|
2013-09-02 02:17:23 +01:00
|
|
|
decryptedJson.append(decryptedChunk.toByteArray());
|
2013-09-09 21:50:27 +01:00
|
|
|
}
|
2013-09-01 21:13:03 +01:00
|
|
|
|
2013-09-20 14:54:30 +01:00
|
|
|
bool success = unserialize(decryptedJson, out);
|
|
|
|
|
2014-03-04 01:33:41 +00:00
|
|
|
if (!success) {
|
|
|
|
return false;
|
|
|
|
}
|
2013-09-20 14:54:30 +01:00
|
|
|
|
2013-09-09 21:50:27 +01:00
|
|
|
if (hasPayload()) {
|
2013-09-20 14:54:30 +01:00
|
|
|
out->mPayload = mPayload;
|
2013-09-02 02:17:23 +01:00
|
|
|
}
|
2013-09-01 21:13:03 +01:00
|
|
|
|
2013-09-20 14:54:30 +01:00
|
|
|
return true;
|
2013-09-02 12:26:26 +01:00
|
|
|
|
2013-09-01 21:13:03 +01:00
|
|
|
}
|
|
|
|
|
2014-09-21 21:22:06 +01:00
|
|
|
FileTransferJob* NetworkPackage::createPayloadTransferJob(const QUrl &destination) const
|
2013-09-24 13:10:25 +01:00
|
|
|
{
|
|
|
|
return new FileTransferJob(payload(), payloadSize(), destination);
|
|
|
|
}
|
|
|
|
|