Commit 5aa55027 authored by Johannes Schwab's avatar Johannes Schwab

client: clean up the old share code

parent 5cd0d4d7
......@@ -30,43 +30,10 @@
#include "Recipe.h"
#include "SqlBackend.h"
//class RecipeTcpServer;
//class RecipeTcpClient;
class SynchronizeAsync;
class SendSyncKeyAsync;
class RecvSyncKeyAsync;
class FoundRecipe : public QObject {
Q_OBJECT
Q_PROPERTY(unsigned int id READ getId);
Q_PROPERTY(QString name READ getName CONSTANT);
private:
const unsigned int id;
const QString name;
public:
FoundRecipe()
:
id(0) //TODO is this value sane?
{}
FoundRecipe(QObject *parent, unsigned int id, const QString &name)
:
QObject(parent),
id(id),
name(name)
{}
unsigned int getId() const {
return id;
}
QString getName() const {
return name;
}
};
class Backend : public QObject {
Q_OBJECT
Q_PROPERTY(QQmlListProperty<Recipe> recipesList READ getRecipesList NOTIFY recipesListChanged)
......@@ -123,8 +90,6 @@ class Backend : public QObject {
Q_INVOKABLE void removeSyncSettings() const;
bool getSyncAvailable() const;
Q_INVOKABLE Recipe* getNewRecipe();
//Q_INVOKABLE Ingredient* getNewIngredient();
//Q_INVOKABLE bool recipeToDB(Recipe *recipe);
Q_INVOKABLE bool deleteRecipe(Recipe *recipe);
Q_INVOKABLE void synchronize();
Q_INVOKABLE void stopSynchronizing();
......@@ -148,7 +113,6 @@ class Backend : public QObject {
signals:
void recipesListChanged();
void foundRecipesListChanged();
void synchronizingDone(bool success);
void sendSyncKeyDone(bool success);
void recvSyncKeyDone(bool success);
......
/*
* OpenRecipes
* Copyright (C) 2018 Johannes Schwab
*
* 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 3 of the License, or
* (at your option) any later version.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "QREncoder.h"
#include <qrencode.h>
......
/*
* OpenRecipes
* Copyright (C) 2018 Johannes Schwab
*
* 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 3 of the License, or
* (at your option) any later version.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef QR_ENCODER_H
#define QR_ENCODER_H
......
/*
* OpenRecipes
* Copyright (C) 2018 Johannes Schwab
*
* 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 3 of the License, or
* (at your option) any later version.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "RecipeTcpClient.h"
#include <QUdpSocket>
#include <QTcpSocket>
#include <QTimer>
#include <QNetworkInterface>
#include <QNetworkDatagram>
#include <QtEndian>
RecipeTcpClient::RecipeTcpClient()
:
recipe(nullptr),
tcpSocket(nullptr),
udpSocket(new QUdpSocket(this)),
tcpData(nullptr)
{
udpSocket->bind(QHostAddress::AnyIPv6, RECIPES_UDP_MULTICAST_PORT, QUdpSocket::ShareAddress);
udpSocket->joinMulticastGroup(QHostAddress(RECIPES_UDP_MULTICAST_ADDRESS));
connect(udpSocket, &QUdpSocket::readyRead, this, &RecipeTcpClient::onUdpReadyRead);
}
void RecipeTcpClient::onUdpTimeOut() {
QTimer *timer = dynamic_cast<QTimer*>(QObject::sender());
for (auto fr = foundRecipes.begin(); fr != foundRecipes.end(); ++fr) {
if (fr->timer == timer) {
emit lostRecipe(fr->id);
foundRecipes.erase(fr);
break;
}
}
timer->deleteLater();
}
void RecipeTcpClient::onUdpReadyRead() {
qDebug("onUdpReadyRead");
const QNetworkDatagram dgram = udpSocket->receiveDatagram();
for (auto &fr : foundRecipes) {
if (dgram.senderAddress() == fr.dgram.senderAddress()
&& dgram.senderPort() == fr.dgram.senderPort()
&& dgram.data() == fr.dgram.data()) {
delete fr.timer;
fr.timer = new QTimer(this);
fr.timer->setSingleShot(true);
connect(fr.timer, &QTimer::timeout, this, &RecipeTcpClient::onUdpTimeOut);
fr.timer->start(1000);
return;
}
}
UdpData udpData(dgram.data());
if (udpData.getProtocolVersion() != NETWORK_PROTOCOL_VERSION) {
qDebug() << "Found recipe, but network protocol version " << udpData.getProtocolVersion() << " is not supported";
return;
}
foundRecipes.emplace_front(foundRecipes.front().id + 1, dgram, new QTimer(this));
foundRecipes.front().timer->setSingleShot(true);
connect(foundRecipes.front().timer, &QTimer::timeout, this, &RecipeTcpClient::onUdpTimeOut);
foundRecipes.front().timer->start(1000);
qDebug() << "Found recipe " << udpData.getRecipeName();
emit foundRecipe(foundRecipes.front().id, udpData.getRecipeName());
}
void RecipeTcpClient::getRecipeFromServer(unsigned int id) {
if (recipe || tcpSocket) throw IERROR("Allready done this");
QHostAddress hostAddress;
std::unique_ptr<UdpData> udpData(nullptr);
for (const auto &fr : foundRecipes) {
if (id == fr.id) {
hostAddress = fr.dgram.senderAddress();
udpData.reset(new UdpData(fr.dgram.data()));
break;
}
}
if (udpData.get() == nullptr) throw IERROR("Invalid id");
tcpSocket = new QTcpSocket(this);
connect(tcpSocket, &QTcpSocket::readyRead, this, &RecipeTcpClient::onTcpReadyRead);
tcpSocket->connectToHost(hostAddress, udpData->getTcpPort());
crypto_kx_keypair(client_pk, client_sk);
if (tcpSocket->write(reinterpret_cast<char*>(client_pk), sizeof(client_pk)) != sizeof(client_pk)) throw IERROR("Can't send pubkey");
delete udpSocket;
udpSocket = nullptr;
for (auto &fr : foundRecipes) delete fr.timer;
foundRecipes.clear();
}
void RecipeTcpClient::onTcpReadyRead() {
qDebug("onTcpReadyRead");
while (tcpSocket->bytesAvailable()) {
if (tcpData) {
tcpData->append(tcpSocket->readAll());
} else {
tcpData.reset(new TcpData(tcpSocket->readAll()));
}
}
if (!tcpData->isComplete()) {
qDebug("Recipe not complete, waiting for remaining data...");
return;
}
qDebug("Recipe complete, processing...");
tcpSocket->deleteLater();
tcpSocket = nullptr;
unsigned char client_rx[crypto_kx_SESSIONKEYBYTES];
if (crypto_kx_client_session_keys(client_rx, nullptr, client_pk, client_sk, tcpData->getServerPk()) != 0) throw IERROR("Received invalid pubkey");
QByteArray plaintext;
plaintext.resize(tcpData->getCiphertextSize() - crypto_secretbox_MACBYTES);
if (crypto_secretbox_open_easy(reinterpret_cast<unsigned char*>(plaintext.data()), tcpData->getCiphertext(), tcpData->getCiphertextSize(), tcpData->getNonce(), client_rx) != 0) throw IERROR("Can't decrypt received data");
tcpData.reset(nullptr);
recipe = new Recipe(plaintext, this);
emit done();
}
/*
* OpenRecipes
* Copyright (C) 2018 Johannes Schwab
*
* 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 3 of the License, or
* (at your option) any later version.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef RECIPE_TCP_CLIENT
#define RECIPE_TCP_CLIENT
#include <QObject>
#include "Recipe.h"
#include <sodium.h>
#include <list>
#include <forward_list>
#include <QNetworkDatagram>
#include <memory>
#include "RecipeTcpHelper.h"
class QTimer;
class QUdpSocket;
class QTcpSocket;
class RecipeTcpClient : public QObject {
Q_OBJECT
private:
struct foundRecipe {
unsigned int id;
QNetworkDatagram dgram;
QTimer *timer;
foundRecipe(unsigned id, const QNetworkDatagram &dgram, QTimer *timer)
:
id(id),
dgram(dgram),
timer(timer)
{}
};
std::list<foundRecipe> foundRecipes;
Recipe *recipe;
QTcpSocket *tcpSocket;
QUdpSocket *udpSocket;
std::unique_ptr<TcpData> tcpData;
unsigned char client_pk[crypto_kx_PUBLICKEYBYTES];
unsigned char client_sk[crypto_kx_SECRETKEYBYTES];
private slots:
void onTcpReadyRead();
void onUdpReadyRead();
void onUdpTimeOut();
public:
RecipeTcpClient();
void getRecipeFromServer(unsigned int id);
signals:
void done();
void failed();
void foundRecipe(unsigned int id, const QString &name);
void lostRecipe(unsigned int id);
};
#endif //RECIPE_TCP_CLIENT
/*
* OpenRecipes
* Copyright (C) 2018 Johannes Schwab
*
* 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 3 of the License, or
* (at your option) any later version.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef RECIPE_TCP_HELPER_H
#define RECIPE_TCP_HELPER_H
#include <InternalError.h>
#include <vector>
#include <QtEndian>
#include <cstring>
#include <sodium.h>
static constexpr char RECIPES_UDP_MULTICAST_ADDRESS[] = "ff02::2:3e1e";
static constexpr int RECIPES_UDP_MULTICAST_PORT = 12343;
static constexpr uint8_t NETWORK_PROTOCOL_VERSION = 1;
class UdpData {
private:
std::vector<char> data;
uint8_t *const protocolVersion;
qint16 *const tcpPort;
char *const recipeName;
public:
explicit UdpData(qint16 tcpPort, QString recipeName)
:
data(54, 0x00),
protocolVersion(reinterpret_cast<uint8_t*>(data.data())),
tcpPort(reinterpret_cast<qint16*>(data.data() + 1)),
recipeName(data.data() + 3)
{
*protocolVersion = NETWORK_PROTOCOL_VERSION;
*(this->tcpPort) = qToBigEndian<qint16>(tcpPort);
while (recipeName.toUtf8().length() > 50) recipeName.chop(1);
std::memcpy(this->recipeName, recipeName.toUtf8().constData(), recipeName.toUtf8().length());
}
explicit UdpData(const QByteArray &data)
:
data(data.constData(), data.constData() + data.size()),
protocolVersion(reinterpret_cast<uint8_t*>(this->data.data())),
tcpPort(reinterpret_cast<qint16*>(this->data.data() + 1)),
recipeName(this->data.data() + 3)
{
if (data.size() != 54) throw IERROR("Data size invalid");
if (data[53] != 0x00) throw IERROR("Data not null terminated");
}
uint8_t getProtocolVersion() const {
return *protocolVersion;
}
qint16 getTcpPort() const {
return qFromBigEndian<qint16>(*tcpPort);
}
QString getRecipeName() const {
return QString::fromUtf8(recipeName);
}
const char* getData() const {
return data.data();
}
size_t getSize() const {
return data.size();
}
};
/**
* ## Data layout ##
* Bytes: 8 | crypto_kx_PUBLICKEYBYTES | crypto_secretbox_NONCEBYTES | messageLength + crpyto_secretbox_MACBYTES
* Data: total size (big endian) | server pubkey | nonce | ciphertext
*/
class TcpData {
private:
std::vector<unsigned char> data;
public:
explicit TcpData(size_t messageLength)
:
data(8 + crypto_kx_PUBLICKEYBYTES + crypto_secretbox_NONCEBYTES + messageLength + crypto_secretbox_MACBYTES)
{
*(reinterpret_cast<qint64*>(data.data())) = qToBigEndian<qint64>(data.size()); //TODO check for overflow
}
explicit TcpData(const QByteArray &data)
:
data(data.constData(), data.constData() + data.size())
{}
void append(const QByteArray &data) {
const size_t oldSize = this->data.size();
this->data.resize(this->data.size() + data.size());
std::memcpy(this->data.data() + oldSize, data.constData(), data.size());
}
bool isComplete() const {
qint64 s = qFromBigEndian<qint64>(*(reinterpret_cast<const qint64*>(data.data())));
return (s >= 0 && static_cast<size_t>(s) == data.size());
}
unsigned char* getServerPk() {
return data.data() + 8;
}
unsigned char* getNonce() {
return data.data() + 8 + crypto_kx_PUBLICKEYBYTES;
}
unsigned char* getCiphertext() {
return data.data() + 8 + crypto_kx_PUBLICKEYBYTES + crypto_secretbox_NONCEBYTES;
}
size_t getCiphertextSize() {
return (data.size() - (getCiphertext() - data.data()));
}
const char* getData() const {
return reinterpret_cast<const char*>(data.data());
}
size_t getSize() const {
return data.size();
}
};
#endif //RECIPE_TCP_HELPER_H
/*
* OpenRecipes
* Copyright (C) 2018 Johannes Schwab
*
* 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 3 of the License, or
* (at your option) any later version.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "RecipeTcpServer.h"
#include <QTcpServer>
#include <QUdpSocket>
#include <QTcpSocket>
#include <QTimer>
#include <QtEndian>
#include <sodium.h>
RecipeTcpServer::RecipeTcpServer(const Recipe *recipe)
:
udpData(nullptr),
xml(recipe->toXML()),
timer(new QTimer(this)),
tcpServer(new QTcpServer(this)),
udpSocket(new QUdpSocket(this))
{
tcpServer->listen(QHostAddress::AnyIPv6);
connect(tcpServer, &QTcpServer::newConnection, this, &RecipeTcpServer::onNewConnection);
udpData.reset(new UdpData(tcpServer->serverPort(), recipe->getName()));
connect(timer, &QTimer::timeout, [=]() {
udpSocket->writeDatagram(udpData->getData(), udpData->getSize(), QHostAddress(RECIPES_UDP_MULTICAST_ADDRESS), RECIPES_UDP_MULTICAST_PORT);
});
timer->start(500);
}
void RecipeTcpServer::onNewConnection() {
QTcpSocket *tcpSocket = tcpServer->nextPendingConnection();
connect(tcpSocket, &QTcpSocket::readyRead, this, &RecipeTcpServer::onReadyRead);
//if (tcpSocket->hasPendingData()) ;//TODO
tcpSockets.push_front(tcpSocket);
}
void RecipeTcpServer::onReadyRead() {
QTcpSocket *tcpSocket = dynamic_cast<QTcpSocket*>(QObject::sender());
unsigned char client_pk[crypto_kx_PUBLICKEYBYTES];
if (tcpSocket->read(reinterpret_cast<char*>(client_pk), sizeof(client_pk)) != sizeof(client_pk)) {
qDebug("recieved invalid public key (1)");
return;
}
TcpData tcpData(xml.size());
unsigned char server_sk[crypto_kx_SECRETKEYBYTES];
crypto_kx_keypair(tcpData.getServerPk(), server_sk);
unsigned char server_tx[crypto_kx_SESSIONKEYBYTES];
if (crypto_kx_server_session_keys(nullptr, server_tx, tcpData.getServerPk(), server_sk, client_pk) != 0) {
qDebug("recieved invalid public key (2)");
return;
}
randombytes_buf(tcpData.getNonce(), crypto_secretbox_NONCEBYTES);
crypto_secretbox_easy(tcpData.getCiphertext(), reinterpret_cast<const unsigned char*>(xml.constData()), xml.size(), tcpData.getNonce(), server_tx);
const qint64 written = tcpSocket->write(tcpData.getData(), tcpData.getSize());
if (written < 0 && static_cast<size_t>(written) != tcpData.getSize()) {
qDebug("can't send data");
return;
}
disconnect(tcpSocket, &QTcpSocket::readyRead, this, &RecipeTcpServer::onReadyRead);
connect(tcpSocket, &QTcpSocket::bytesWritten, this, &RecipeTcpServer::onBytesWritten);
}
void RecipeTcpServer::onBytesWritten() {
QTcpSocket *tcpSocket = dynamic_cast<QTcpSocket*>(QObject::sender());
if (tcpSocket->bytesToWrite()) {
qDebug("not all data written yet...");
return;
}
qDebug("Done writing data to socket");
delete tcpSocket;
tcpSockets.remove(tcpSocket);
}
/*
* OpenRecipes
* Copyright (C) 2018 Johannes Schwab
*
* 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 3 of the License, or
* (at your option) any later version.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef RECIPE_TCP_SERVER
#define RECIPE_TCP_SERVER
#include "Recipe.h"
#include "RecipeTcpHelper.h"
#include <forward_list>
#include <QObject>
#include <QByteArray>
#include <memory>
class QTimer;
class QTcpServer;
class QUdpSocket;
class QTcpSocket;
class RecipeTcpServer : public QObject {
Q_OBJECT
private:
std::unique_ptr<UdpData> udpData;
const QByteArray xml;
QTimer *timer;
QTcpServer *tcpServer;
QUdpSocket *udpSocket;
std::forward_list<QTcpSocket*> tcpSockets;
private slots:
void onNewConnection();
void onBytesWritten();
void onReadyRead();
public:
explicit RecipeTcpServer(const Recipe *recipe);
signals:
void failed();
};
#endif //RECIPE_TCP_SERVER
......@@ -54,7 +54,6 @@ int main(int argc, char **argv) {
qmlRegisterUncreatableType<Recipe>("org.jschwab.recipes", 1, 0, "Recipe", "Can't create recipe");
qmlRegisterUncreatableType<Ingredient>("org.jschwab.recipes", 1, 0, "Ingredient", "Can't create ingredient");
qmlRegisterUncreatableType<FoundRecipe>("org.jschwab.recipes", 1, 0, "FoundRecipe", "Can't create foundRecipe");
qmlRegisterSingletonType<Globals>("org.jschwab.recipes", 1, 0, "Globals",
[](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject* {
Q_UNUSED(engine)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment