Browse Source

WIP

pull/19/head
Aditya Kulkarni 5 years ago
parent
commit
3de363cbb5
  1. 6
      lib/src/lib.rs
  2. 7
      lib/zecwalletlitelib.h
  3. 595
      src/connection.cpp
  4. 45
      src/connection.h

6
lib/src/lib.rs

@ -18,7 +18,7 @@ lazy_static! {
// Initialize a new lightclient and store its value
#[no_mangle]
pub extern fn initialze(dangerous: bool, server: *const c_char) -> *mut c_char {
pub extern fn litelib_initialze(dangerous: bool, server: *const c_char) -> *mut c_char {
let server_str = unsafe {
assert!(!server.is_null());
@ -49,7 +49,7 @@ pub extern fn initialze(dangerous: bool, server: *const c_char) -> *mut c_char {
}
#[no_mangle]
pub extern fn execute(cmd: *const c_char) -> *mut c_char {
pub extern fn litelib_execute(cmd: *const c_char) -> *mut c_char {
let cmd_str = unsafe {
assert!(!cmd.is_null());
@ -77,7 +77,7 @@ pub extern fn execute(cmd: *const c_char) -> *mut c_char {
* back to rust, so it can be freed. Failure to call this function will result in a memory leak
*/
#[no_mangle]
pub extern fn rust_free_string(s: *mut c_char) {
pub extern fn litelib_rust_free_string(s: *mut c_char) {
unsafe {
if s.is_null() { return }
CString::from_raw(s)

7
lib/zecwalletlitelib.h

@ -1,4 +1,3 @@
#ifndef _ZEC_PAPER_RUST_H
#define _ZEC_PAPER_RUST_H
@ -6,9 +5,9 @@
extern "C"{
#endif
extern char * initialze (bool dangerous, const char* server);
extern char * execute (char* s);
extern void rust_free_string (char* s);
extern char * litelib_initialze (bool dangerous, const char* server);
extern char * litelib_execute (char* s);
extern void litelib_rust_free_string (char* s);
#ifdef __cplusplus
}

595
src/connection.cpp

@ -5,6 +5,8 @@
#include "ui_createzcashconfdialog.h"
#include "controller.h"
#include "../lib/zecwalletlitelib.h"
#include "precompiled.h"
using json = nlohmann::json;
@ -32,15 +34,24 @@ void ConnectionLoader::loadConnection() {
}
void ConnectionLoader::doAutoConnect(bool tryEzcashdStart) {
// Priority 1: Ensure all params are present.
if (!verifyParams()) {
downloadParams([=]() { this->doAutoConnect(); });
return;
}
auto config = std::shared_ptr<ConnectionConfig>(new ConnectionConfig());
config->dangerous = true;
config->server = QString("https://127.0.0.1:9067");
// Initialize the library
main->logger->write(QObject::tr("Attempting to initialize"));
litelib_initialze(config->dangerous, config->server.toStdString());
auto connection = makeConnection(config);
// After the lib is initialized, try to do get info
connection->doRPC("info", [=](auto reply) {
// If success, set the connection
d->hide();
main->logger->write("Connection is online.");
this->doRPCSetConnection(connection);
}, [=](auto err, auto errJson) {});
// Priority 2: Try to connect to detect zcash.conf and connect to it.
auto config = autoDetectZcashConf();
main->logger->write(QObject::tr("Attempting autoconnect"));
if (config.get() != nullptr) {
auto connection = makeConnection(config);
@ -106,318 +117,6 @@ void ConnectionLoader::doAutoConnect(bool tryEzcashdStart) {
}
}
QString randomPassword() {
static const char alphanum[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
const int passwordLength = 10;
char* s = new char[passwordLength + 1];
for (int i = 0; i < passwordLength; ++i) {
s[i] = alphanum[randombytes_uniform(sizeof(alphanum))];
}
s[passwordLength] = 0;
return QString::fromStdString(s);
}
/**
* This will create a new zcash.conf, download Zcash parameters.
*/
void ConnectionLoader::createZcashConf() {
main->logger->write("createZcashConf");
auto confLocation = zcashConfWritableLocation();
QFileInfo fi(confLocation);
QDialog d(main);
Ui_createZcashConf ui;
ui.setupUi(&d);
QPixmap logo(":/img/res/zcashdlogo.gif");
ui.lblTopIcon->setBasePixmap(logo.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation));
ui.btnPickDir->setEnabled(false);
ui.grpAdvanced->setVisible(false);
QObject::connect(ui.btnAdvancedConfig, &QPushButton::toggled, [=](bool isVisible) {
ui.grpAdvanced->setVisible(isVisible);
ui.btnAdvancedConfig->setText(isVisible ? QObject::tr("Hide Advanced Config") : QObject::tr("Show Advanced Config"));
});
QObject::connect(ui.chkCustomDatadir, &QCheckBox::stateChanged, [=](int chked) {
if (chked == Qt::Checked) {
ui.btnPickDir->setEnabled(true);
}
else {
ui.btnPickDir->setEnabled(false);
}
});
QObject::connect(ui.btnPickDir, &QPushButton::clicked, [=]() {
auto datadir = QFileDialog::getExistingDirectory(main, QObject::tr("Choose data directory"), ui.lblDirName->text(), QFileDialog::ShowDirsOnly);
if (!datadir.isEmpty()) {
ui.lblDirName->setText(QDir::toNativeSeparators(datadir));
}
});
// Show the dialog
QString datadir = "";
bool useTor = false;
if (d.exec() == QDialog::Accepted) {
datadir = ui.lblDirName->text();
useTor = ui.chkUseTor->isChecked();
if (!ui.chkAllowInternet->isChecked()) {
Settings::getInstance()->setAllowFetchPrices(false);
Settings::getInstance()->setCheckForUpdates(false);
}
}
main->logger->write("Creating file " + confLocation);
QDir().mkdir(fi.dir().absolutePath());
QFile file(confLocation);
if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
main->logger->write("Could not create zcash.conf, returning");
return;
}
QTextStream out(&file);
out << "server=1\n";
out << "addnode=mainnet.z.cash\n";
out << "rpcuser=zec-qt-wallet\n";
out << "rpcpassword=" % randomPassword() << "\n";
// Fast sync override
if (ui.chkFastSync->isChecked()) {
out << "ibdskiptxverification=1\n";
}
// Datadir override
if (!datadir.isEmpty()) {
out << "datadir=" % datadir % "\n";
}
// Tor override
if (useTor) {
out << "proxy=127.0.0.1:9050\n";
}
file.close();
// Now that zcash.conf exists, try to autoconnect again
this->doAutoConnect();
}
void ConnectionLoader::downloadParams(std::function<void(void)> cb) {
main->logger->write("Adding params to download queue");
// Add all the files to the download queue
downloadQueue = new QQueue<QUrl>();
client = new QNetworkAccessManager(main);
downloadQueue->enqueue(QUrl("https://z.cash/downloads/sapling-output.params"));
downloadQueue->enqueue(QUrl("https://z.cash/downloads/sapling-spend.params"));
downloadQueue->enqueue(QUrl("https://z.cash/downloads/sprout-proving.key"));
downloadQueue->enqueue(QUrl("https://z.cash/downloads/sprout-verifying.key"));
downloadQueue->enqueue(QUrl("https://z.cash/downloads/sprout-groth16.params"));
doNextDownload(cb);
}
void ConnectionLoader::doNextDownload(std::function<void(void)> cb) {
auto fnSaveFileName = [&] (QUrl url) {
QString path = url.path();
QString basename = QFileInfo(path).fileName();
return basename;
};
if (downloadQueue->isEmpty()) {
delete downloadQueue;
client->deleteLater();
main->logger->write("All Downloads done");
this->showInformation(QObject::tr("All Downloads Finished Successfully!"));
cb();
return;
}
QUrl url = downloadQueue->dequeue();
int filesRemaining = downloadQueue->size();
QString filename = fnSaveFileName(url);
QString paramsDir = zcashParamsDir();
if (QFile(QDir(paramsDir).filePath(filename)).exists()) {
main->logger->write(filename + " already exists, skipping");
doNextDownload(cb);
return;
}
// The downloaded file is written to a new name, and then renamed when the operation completes.
currentOutput = new QFile(QDir(paramsDir).filePath(filename + ".part"));
if (!currentOutput->open(QIODevice::WriteOnly)) {
main->logger->write("Couldn't open " + currentOutput->fileName() + " for writing");
this->showError(QObject::tr("Couldn't download params. Please check the help site for more info."));
}
main->logger->write("Downloading to " + filename);
qDebug() << "Downloading " << url << " to " << filename;
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
currentDownload = client->get(request);
downloadTime.start();
// Download Progress
QObject::connect(currentDownload, &QNetworkReply::downloadProgress, [=] (auto done, auto total) {
// calculate the download speed
double speed = done * 1000.0 / downloadTime.elapsed();
QString unit;
if (speed < 1024) {
unit = "bytes/sec";
} else if (speed < 1024*1024) {
speed /= 1024;
unit = "kB/s";
} else {
speed /= 1024*1024;
unit = "MB/s";
}
this->showInformation(
QObject::tr("Downloading ") % filename % (filesRemaining > 1 ? " ( +" % QString::number(filesRemaining) % QObject::tr(" more remaining )") : QString("")),
QString::number(done/1024/1024, 'f', 0) % QObject::tr("MB of ") % QString::number(total/1024/1024, 'f', 0) + QObject::tr("MB at ") % QString::number(speed, 'f', 2) % unit);
});
// Download Finished
QObject::connect(currentDownload, &QNetworkReply::finished, [=] () {
// Rename file
main->logger->write("Finished downloading " + filename);
currentOutput->rename(QDir(paramsDir).filePath(filename));
currentOutput->close();
currentDownload->deleteLater();
currentOutput->deleteLater();
if (currentDownload->error()) {
main->logger->write("Downloading " + filename + " failed");
this->showError(QObject::tr("Downloading ") + filename + QObject::tr(" failed. Please check the help site for more info"));
} else {
doNextDownload(cb);
}
});
// Download new data available.
QObject::connect(currentDownload, &QNetworkReply::readyRead, [=] () {
currentOutput->write(currentDownload->readAll());
});
}
bool ConnectionLoader::startEmbeddedZcashd() {
if (!Settings::getInstance()->useEmbedded())
return false;
main->logger->write("Trying to start embedded zcashd");
// Static because it needs to survive even after this method returns.
static QString processStdErrOutput;
if (ezcashd != nullptr) {
if (ezcashd->state() == QProcess::NotRunning) {
if (!processStdErrOutput.isEmpty()) {
QMessageBox::critical(main, QObject::tr("zcashd error"), "zcashd said: " + processStdErrOutput,
QMessageBox::Ok);
}
return false;
} else {
return true;
}
}
// Finally, start zcashd
QDir appPath(QCoreApplication::applicationDirPath());
#ifdef Q_OS_LINUX
auto zcashdProgram = appPath.absoluteFilePath("zqw-zcashd");
if (!QFile(zcashdProgram).exists()) {
zcashdProgram = appPath.absoluteFilePath("zcashd");
}
#elif defined(Q_OS_DARWIN)
auto zcashdProgram = appPath.absoluteFilePath("zcashd");
#else
auto zcashdProgram = appPath.absoluteFilePath("zcashd.exe");
#endif
if (!QFile(zcashdProgram).exists()) {
qDebug() << "Can't find zcashd at " << zcashdProgram;
main->logger->write("Can't find zcashd at " + zcashdProgram);
return false;
}
ezcashd = new QProcess(main);
QObject::connect(ezcashd, &QProcess::started, [=] () {
//qDebug() << "zcashd started";
});
QObject::connect(ezcashd, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
[=](int, QProcess::ExitStatus) {
//qDebug() << "zcashd finished with code " << exitCode << "," << exitStatus;
});
QObject::connect(ezcashd, &QProcess::errorOccurred, [&] (auto) {
//qDebug() << "Couldn't start zcashd: " << error;
});
QObject::connect(ezcashd, &QProcess::readyReadStandardError, [=]() {
auto output = ezcashd->readAllStandardError();
main->logger->write("zcashd stderr:" + output);
processStdErrOutput.append(output);
});
#ifdef Q_OS_LINUX
ezcashd->start(zcashdProgram);
#elif defined(Q_OS_DARWIN)
ezcashd->start(zcashdProgram);
#else
ezcashd->setWorkingDirectory(appPath.absolutePath());
ezcashd->start("zcashd.exe");
#endif // Q_OS_LINUX
return true;
}
void ConnectionLoader::doManualConnect() {
auto config = loadFromSettings();
if (!config) {
// Nothing configured, show an error
QString explanation = QString()
% QObject::tr("A manual connection was requested, but the settings are not configured.\n\n"
"Please set the host/port and user/password in the Edit->Settings menu.");
showError(explanation);
doRPCSetConnection(nullptr);
return;
}
auto connection = makeConnection(config);
refreshZcashdState(connection, [=] () {
QString explanation = QString()
% QObject::tr("Could not connect to zcashd configured in settings.\n\n"
"Please set the host/port and user/password in the Edit->Settings menu.");
showError(explanation);
doRPCSetConnection(nullptr);
return;
});
}
void ConnectionLoader::doRPCSetConnection(Connection* conn) {
rpc->setEZcashd(ezcashd);
rpc->setConnection(conn);
@ -428,69 +127,7 @@ void ConnectionLoader::doRPCSetConnection(Connection* conn) {
}
Connection* ConnectionLoader::makeConnection(std::shared_ptr<ConnectionConfig> config) {
QNetworkAccessManager* client = new QNetworkAccessManager(main);
QUrl myurl;
myurl.setScheme("http");
myurl.setHost(config.get()->host);
myurl.setPort(config.get()->port.toInt());
QNetworkRequest* request = new QNetworkRequest();
request->setUrl(myurl);
request->setHeader(QNetworkRequest::ContentTypeHeader, "text/plain");
QString userpass = config.get()->rpcuser % ":" % config.get()->rpcpassword;
QString headerData = "Basic " + userpass.toLocal8Bit().toBase64();
request->setRawHeader("Authorization", headerData.toLocal8Bit());
return new Connection(main, client, request, config);
}
void ConnectionLoader::refreshZcashdState(Connection* connection, std::function<void(void)> refused) {
json payload = {
{"jsonrpc", "1.0"},
{"id", "someid"},
{"method", "getinfo"}
};
connection->doRPC(payload,
[=] (auto) {
// Success, hide the dialog if it was shown.
d->hide();
main->logger->write("zcashd is online.");
this->doRPCSetConnection(connection);
},
[=] (auto reply, auto res) {
// Failed, see what it is.
auto err = reply->error();
//qDebug() << err << ":" << QString::fromStdString(res.dump());
if (err == QNetworkReply::NetworkError::ConnectionRefusedError) {
refused();
} else if (err == QNetworkReply::NetworkError::AuthenticationRequiredError) {
main->logger->write("Authentication failed");
QString explanation = QString() %
QObject::tr("Authentication failed. The username / password you specified was "
"not accepted by zcashd. Try changing it in the Edit->Settings menu");
this->showError(explanation);
} else if (err == QNetworkReply::NetworkError::InternalServerError &&
!res.is_discarded()) {
// The server is loading, so just poll until it succeeds
QString status = QString::fromStdString(res["error"]["message"]);
{
static int dots = 0;
status = status.left(status.length() - 3) + QString(".").repeated(dots);
dots++;
if (dots > 3)
dots = 0;
}
this->showInformation(QObject::tr("Your zcashd is starting up. Please wait."), status);
main->logger->write("Waiting for zcashd to come online.");
// Refresh after one second
QTimer::singleShot(1000, [=]() { this->refreshZcashdState(connection, refused); });
}
}
);
return new Connection(main, config);
}
// Update the UI with the status
@ -522,204 +159,42 @@ void ConnectionLoader::showError(QString explanation) {
d->close();
}
QString ConnectionLoader::locateZcashConfFile() {
#ifdef Q_OS_LINUX
auto confLocation = QStandardPaths::locate(QStandardPaths::HomeLocation, ".zcash/zcash.conf");
#elif defined(Q_OS_DARWIN)
auto confLocation = QStandardPaths::locate(QStandardPaths::HomeLocation, "Library/Application Support/Zcash/zcash.conf");
#else
auto confLocation = QStandardPaths::locate(QStandardPaths::AppDataLocation, "../../Zcash/zcash.conf");
#endif
main->logger->write("Found zcashconf at " + QDir::cleanPath(confLocation));
return QDir::cleanPath(confLocation);
}
QString ConnectionLoader::zcashConfWritableLocation() {
#ifdef Q_OS_LINUX
auto confLocation = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).filePath(".zcash/zcash.conf");
#elif defined(Q_OS_DARWIN)
auto confLocation = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).filePath("Library/Application Support/Zcash/zcash.conf");
#else
auto confLocation = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("../../Zcash/zcash.conf");
#endif
main->logger->write("Found zcashconf at " + QDir::cleanPath(confLocation));
return QDir::cleanPath(confLocation);
}
QString ConnectionLoader::zcashParamsDir() {
#ifdef Q_OS_LINUX
auto paramsLocation = QDir(QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).filePath(".zcash-params"));
#elif defined(Q_OS_DARWIN)
auto paramsLocation = QDir(QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).filePath("Library/Application Support/ZcashParams"));
#else
auto paramsLocation = QDir(QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("../../ZcashParams"));
#endif
if (!paramsLocation.exists()) {
main->logger->write("Creating params location at " + paramsLocation.absolutePath());
QDir().mkpath(paramsLocation.absolutePath());
}
main->logger->write("Found Zcash params directory at " + paramsLocation.absolutePath());
return paramsLocation.absolutePath();
}
bool ConnectionLoader::verifyParams() {
QDir paramsDir(zcashParamsDir());
if (!QFile(paramsDir.filePath("sapling-output.params")).exists()) return false;
if (!QFile(paramsDir.filePath("sapling-spend.params")).exists()) return false;
if (!QFile(paramsDir.filePath("sprout-proving.key")).exists()) return false;
if (!QFile(paramsDir.filePath("sprout-verifying.key")).exists()) return false;
if (!QFile(paramsDir.filePath("sprout-groth16.params")).exists()) return false;
return true;
}
/**
* Try to automatically detect a zcash.conf file in the correct location and load parameters
*/
std::shared_ptr<ConnectionConfig> ConnectionLoader::autoDetectZcashConf() {
auto confLocation = Settings::getInstance()->getZcashdConfLocation();
if (confLocation.isEmpty()) {
confLocation = locateZcashConfFile();
}
if (confLocation.isNull()) {
// No Zcash file, just return with nothing
return nullptr;
}
QFile file(confLocation);
if (!file.open(QIODevice::ReadOnly)) {
qDebug() << file.errorString();
return nullptr;
}
QTextStream in(&file);
auto zcashconf = new ConnectionConfig();
zcashconf->host = "127.0.0.1";
zcashconf->connType = ConnectionType::DetectedConfExternalZcashD;
zcashconf->usingZcashConf = true;
zcashconf->zcashDir = QFileInfo(confLocation).absoluteDir().absolutePath();
zcashconf->zcashDaemon = false;
Settings::getInstance()->setUsingZcashConf(confLocation);
while (!in.atEnd()) {
QString line = in.readLine();
auto s = line.indexOf("=");
QString name = line.left(s).trimmed().toLower();
QString value = line.right(line.length() - s - 1).trimmed();
if (name == "rpcuser") {
zcashconf->rpcuser = value;
}
if (name == "rpcpassword") {
zcashconf->rpcpassword = value;
}
if (name == "rpcport") {
zcashconf->port = value;
}
if (name == "daemon" && value == "1") {
zcashconf->zcashDaemon = true;
}
if (name == "proxy") {
zcashconf->proxy = value;
}
if (name == "testnet" &&
value == "1" &&
zcashconf->port.isEmpty()) {
zcashconf->port = "18232";
}
if (name == "ibdskiptxverification" && value == "1") {
zcashconf->skiptxverification = true;
}
}
// If rpcport is not in the file, and it was not set by the testnet=1 flag, then go to default
if (zcashconf->port.isEmpty()) zcashconf->port = "8232";
file.close();
// In addition to the zcash.conf file, also double check the params.
return std::shared_ptr<ConnectionConfig>(zcashconf);
}
/**
* Load connection settings from the UI, which indicates an unknown, external zcashd
*/
std::shared_ptr<ConnectionConfig> ConnectionLoader::loadFromSettings() {
// Load from the QT Settings.
QSettings s;
auto host = s.value("connection/host").toString();
auto port = s.value("connection/port").toString();
auto username = s.value("connection/rpcuser").toString();
auto password = s.value("connection/rpcpassword").toString();
void Executor::run() {
const char* resp = litelib_execute(this->cmd.toStdString().c_str());
QString reply = QString::fromStdString(resp);
litelib_rust_free_string(resp);
if (username.isEmpty() || password.isEmpty())
return nullptr;
auto parsed = json::parse(reply.toStdString().c_str(), nullptr, false);
auto uiConfig = new ConnectionConfig{ host, port, username, password, false, false, false, "", "", ConnectionType::UISettingsZCashD};
return std::shared_ptr<ConnectionConfig>(uiConfig);
emit responseReady(parsed);
}
/***********************************************************************************
* Connection Class
************************************************************************************/
Connection::Connection(MainWindow* m, QNetworkAccessManager* c, QNetworkRequest* r,
std::shared_ptr<ConnectionConfig> conf) {
this->restclient = c;
this->request = r;
Connection::Connection(MainWindow* m, std::shared_ptr<ConnectionConfig> conf) {
this->config = conf;
this->main = m;
}
Connection::~Connection() {
delete restclient;
delete request;
Connection::~Connection() {
}
void Connection::doRPC(const json& payload, const std::function<void(json)>& cb,
void Connection::doRPC(const QString cmd, const QString args, const std::function<void(json)>& cb,
const std::function<void(QNetworkReply*, const json&)>& ne) {
if (shutdownInProgress) {
// Ignoring RPC because shutdown in progress
return;
}
QNetworkReply *reply = restclient->post(*request, QByteArray::fromStdString(payload.dump()));
QObject::connect(reply, &QNetworkReply::finished, [=] {
reply->deleteLater();
if (shutdownInProgress) {
// Ignoring callback because shutdown in progress
return;
}
if (reply->error() != QNetworkReply::NoError) {
auto parsed = json::parse(reply->readAll(), nullptr, false);
ne(reply, parsed);
return;
}
auto parsed = json::parse(reply->readAll(), nullptr, false);
if (parsed.is_discarded()) {
ne(reply, "Unknown error");
}
cb(parsed["result"]);
});
// Create a runner.
auto runner = new Executor(cmd, args);
QObject::connect(runner, &Executor::responseReady, [=] (json resp) {
cb(resp);
})
QThreadPool::globalInstance()->start(runner);
}
void Connection::doRPCWithDefaultErrorHandling(const json& payload, const std::function<void(json)>& cb) {

45
src/connection.h

@ -9,24 +9,9 @@ using json = nlohmann::json;
class Controller;
enum ConnectionType {
DetectedConfExternalZcashD = 1,
UISettingsZCashD,
InternalZcashD
};
struct ConnectionConfig {
QString host;
QString port;
QString rpcuser;
QString rpcpassword;
bool usingZcashConf;
bool zcashDaemon;
bool skiptxverification;
QString zcashDir;
QString proxy;
ConnectionType connType;
QString server;
bool dangerous;
};
class Connection;
@ -81,17 +66,35 @@ private:
QTime downloadTime;
};
class Executor : public QRunnable {
public:
Executor(QString cmd, QString args) {
this->cmd = cmd;
this->args = args;
};
~Executor() = default;
bool autoDelete() const { return true; }
virtual void run();
signals:
void responseReady(json);
private:
QString cmd;
QString args;
}
/**
* Represents a connection to a zcashd. It may even start a new zcashd if needed.
* This is also a UI class, so it may show a dialog waiting for the connection.
*/
class Connection {
class Connection : public QObject {
public:
Connection(MainWindow* m, QNetworkAccessManager* c, QNetworkRequest* r, std::shared_ptr<ConnectionConfig> conf);
Connection(MainWindow* m, std::shared_ptr<ConnectionConfig> conf);
~Connection();
QNetworkAccessManager* restclient;
QNetworkRequest* request;
std::shared_ptr<ConnectionConfig> config;
MainWindow* main;

Loading…
Cancel
Save