493 lines
19 KiB
C++
493 lines
19 KiB
C++
/**
|
|
* Copyright (c) 2017-present, Status Research and Development GmbH.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
*/
|
|
|
|
#include "rctstatus.h"
|
|
#include "bridge.h"
|
|
#include "eventdispatcher.h"
|
|
|
|
#include <QDebug>
|
|
#include <QMessageBox>
|
|
#include <QStorageInfo>
|
|
#include <QJsonDocument>
|
|
#include <QByteArray>
|
|
#include <QVariantMap>
|
|
#include <QDir>
|
|
#include <QStandardPaths>
|
|
#include <QtConcurrent>
|
|
|
|
#include "libstatus.h"
|
|
|
|
extern QString getDataStoragePath();
|
|
extern QString getLogFilePath();
|
|
|
|
namespace {
|
|
struct RegisterQMLMetaType {
|
|
RegisterQMLMetaType() {
|
|
qRegisterMetaType<RCTStatus*>();
|
|
}
|
|
} registerMetaType;
|
|
} // namespace
|
|
|
|
class RCTStatusPrivate {
|
|
public:
|
|
static Bridge* bridge;
|
|
static RCTStatus* rctStatus;
|
|
};
|
|
|
|
Bridge* RCTStatusPrivate::bridge = nullptr;
|
|
RCTStatus* RCTStatusPrivate::rctStatus = nullptr;
|
|
|
|
Q_LOGGING_CATEGORY(RCTSTATUS, "RCTStatus")
|
|
|
|
RCTStatus::RCTStatus(QObject* parent) : QObject(parent), d_ptr(new RCTStatusPrivate) {
|
|
RCTStatusPrivate::rctStatus = this;
|
|
SetSignalEventCallback((void*)&RCTStatus::statusGoEventCallback);
|
|
connect(this, &RCTStatus::statusGoEvent, this, &RCTStatus::onStatusGoEvent);
|
|
}
|
|
|
|
RCTStatus::~RCTStatus() {}
|
|
|
|
void RCTStatus::setBridge(Bridge* bridge) {
|
|
Q_D(RCTStatus);
|
|
d->bridge = bridge;
|
|
}
|
|
|
|
QString RCTStatus::moduleName() {
|
|
return "Status";
|
|
}
|
|
|
|
QList<ModuleMethod*> RCTStatus::methodsToExport() {
|
|
return QList<ModuleMethod*>{};
|
|
}
|
|
|
|
QVariantMap RCTStatus::constantsToExport() {
|
|
return QVariantMap();
|
|
}
|
|
|
|
void RCTStatus::getDeviceUUID(double callbackId) {
|
|
Q_D(RCTStatus);
|
|
qCDebug(RCTSTATUS) << "::getDeviceUUID call";
|
|
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{"com.status.StatusIm"});
|
|
}
|
|
|
|
QString RCTStatus::prepareDirAndUpdateConfig(QString configString) {
|
|
Q_D(RCTStatus);
|
|
qCDebug(RCTSTATUS) << "::prepareDirAndUpdateConfig call - configString:" << configString;
|
|
|
|
QJsonParseError jsonError;
|
|
const QJsonDocument& jsonDoc = QJsonDocument::fromJson(configString.toUtf8(), &jsonError);
|
|
if (jsonError.error != QJsonParseError::NoError){
|
|
qCWarning(RCTSTATUS) << jsonError.errorString();
|
|
}
|
|
|
|
QVariantMap configJSON = jsonDoc.toVariant().toMap();
|
|
QVariantMap shhextConfig = configJSON["ShhextConfig"].toMap();
|
|
qCDebug(RCTSTATUS) << "::startNode configString: " << configJSON;
|
|
|
|
int networkId = configJSON["NetworkId"].toInt();
|
|
QString relativeDataDirPath = configJSON["DataDir"].toString();
|
|
if (!relativeDataDirPath.startsWith("/"))
|
|
relativeDataDirPath.prepend("/");
|
|
|
|
QString rootDirPath = getDataStoragePath();
|
|
QDir rootDir(rootDirPath);
|
|
QString absDataDirPath = rootDirPath + relativeDataDirPath;
|
|
QDir dataDir(absDataDirPath);
|
|
if (!dataDir.exists()) {
|
|
dataDir.mkpath(".");
|
|
}
|
|
|
|
d_gethLogFilePath = dataDir.absoluteFilePath("geth.log");
|
|
configJSON["DataDir"] = absDataDirPath;
|
|
configJSON["KeyStoreDir"] = rootDir.absoluteFilePath("keystore");
|
|
configJSON["LogFile"] = d_gethLogFilePath;
|
|
|
|
shhextConfig["BackupDisabledDataDir"] = rootDirPath;
|
|
|
|
configJSON["ShhExtConfig"] = shhextConfig;
|
|
|
|
const QJsonDocument& updatedJsonDoc = QJsonDocument::fromVariant(configJSON);
|
|
qCInfo(RCTSTATUS) << "::startNode updated configString: " << updatedJsonDoc.toVariant().toMap();
|
|
return QString(updatedJsonDoc.toJson(QJsonDocument::Compact));
|
|
}
|
|
|
|
void RCTStatus::prepareDirAndUpdateConfig(QString configString, double callbackId) {
|
|
Q_D(RCTStatus);
|
|
qCInfo(RCTSTATUS) << "::prepareDirAndUpdateConfig call - callbackId:" << callbackId;
|
|
QtConcurrent::run([&](QString configString, double callbackId) {
|
|
QString updatedConfig = prepareDirAndUpdateConfig(configString);
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{updatedConfig.toUtf8().data()});
|
|
}, configString, callbackId);
|
|
}
|
|
|
|
void RCTStatus::initKeystore() {
|
|
qCInfo(RCTSTATUS) << "::initKeystore call";
|
|
QString rootDir = getDataStoragePath();
|
|
const char* result = InitKeystore(rootDir.toUtf8().data());
|
|
logStatusGoResult("::initKeystore InitKeystore", result);
|
|
}
|
|
|
|
void RCTStatus::sendDataNotification(QString dataPayloadJSON, QString tokensJSON, double callbackId) {
|
|
Q_D(RCTStatus);
|
|
qCDebug(RCTSTATUS) << "::sendDataNotification call - callbackId:" << callbackId;
|
|
QtConcurrent::run([&](QString dataPayloadJSON, QString tokensJSON, double callbackId) {
|
|
const char* result = SendDataNotification(dataPayloadJSON.toUtf8().data(), tokensJSON.toUtf8().data());
|
|
logStatusGoResult("::sendDataNotification SendDataNotification", result);
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
|
|
}, dataPayloadJSON, tokensJSON, callbackId);
|
|
}
|
|
|
|
|
|
#include <QApplication>
|
|
#include <QProcess>
|
|
#include <QStandardPaths>
|
|
#include <QMessageBox>
|
|
#include <QtGui/private/qzipwriter_p.h>
|
|
|
|
void showFileInGraphicalShell(QWidget *parent, const QFileInfo &fileInfo)
|
|
{
|
|
// Mac, Windows support folder or file.
|
|
#ifdef Q_OS_WIN
|
|
const QString explorer = QStandardPaths::findExecutable(QLatin1String("explorer.exe"));
|
|
if (explorer.isEmpty()) {
|
|
QMessageBox::warning(parent,
|
|
QApplication::translate("Core::Internal",
|
|
"Launching Windows Explorer Failed"),
|
|
QApplication::translate("Core::Internal",
|
|
"Could not find explorer.exe in path to launch Windows Explorer."));
|
|
return;
|
|
}
|
|
QStringList param;
|
|
if (!fileInfo.isDir())
|
|
param += QLatin1String("/select,");
|
|
param += QDir::toNativeSeparators(fileInfo.canonicalFilePath());
|
|
QProcess::startDetached(explorer, param);
|
|
#elif defined(Q_OS_MAC)
|
|
QStringList scriptArgs;
|
|
scriptArgs << QLatin1String("-e")
|
|
<< QString::fromLatin1("tell application \"Finder\" to reveal POSIX file \"%1\"")
|
|
.arg(fileInfo.canonicalFilePath());
|
|
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
|
|
scriptArgs.clear();
|
|
scriptArgs << QLatin1String("-e")
|
|
<< QLatin1String("tell application \"Finder\" to activate");
|
|
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
|
|
#else
|
|
// we cannot select a file here, because no file browser really supports it...
|
|
const QString folder = fileInfo.isDir() ? fileInfo.absoluteFilePath() : fileInfo.dir().absolutePath();
|
|
QProcess browserProc;
|
|
browserProc.setProgram("xdg-open");
|
|
browserProc.setArguments(QStringList(folder));
|
|
bool success = browserProc.startDetached();
|
|
const QString error = QString::fromLocal8Bit(browserProc.readAllStandardError());
|
|
success = success && error.isEmpty();
|
|
if (!success) {
|
|
QMessageBox::warning(parent, "Launching Explorer Failed", error);
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void RCTStatus::sendLogs(QString dbJSON, QString jsLogs, double callbackId) {
|
|
Q_D(RCTStatus);
|
|
|
|
qCDebug(RCTSTATUS) << "::sendLogs call - logFilePath:" << getLogFilePath()
|
|
<< "d_gethLogFilePath:" << d_gethLogFilePath
|
|
<< "dbJSON:" << dbJSON;
|
|
|
|
QString tmpDirName("Status.im");
|
|
QDir tmpDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
|
|
if (!tmpDir.mkpath(tmpDirName)) {
|
|
qCWarning(RCTSTATUS) << "::sendLogs could not create temp dir:" << tmpDirName;
|
|
return;
|
|
}
|
|
|
|
// Check that at least 20MB are available for log generation
|
|
QStorageInfo storage(tmpDir);
|
|
if (storage.bytesAvailable() < 20 * 1024 * 1024) {
|
|
QMessageBox dlg;
|
|
dlg.warning(QApplication::activeWindow(),
|
|
"Error", QString("Insufficient storage space available in %1 for log generation. Please free up some space.").arg(storage.rootPath()),
|
|
QMessageBox::Close);
|
|
return;
|
|
}
|
|
|
|
QFile zipFile(tmpDir.absoluteFilePath(tmpDirName + QDir::separator() + "Status-debug-logs.zip"));
|
|
QZipWriter zipWriter(&zipFile);
|
|
QFile gethLogFile(d_gethLogFilePath);
|
|
QFile logFile(getLogFilePath());
|
|
zipWriter.addFile("db.json", dbJSON.toUtf8());
|
|
zipWriter.addFile("js_logs.log", jsLogs.toUtf8());
|
|
if (gethLogFile.exists()) {
|
|
zipWriter.addFile(QFileInfo(gethLogFile).fileName(), &gethLogFile);
|
|
}
|
|
if (logFile.exists()) {
|
|
zipWriter.addFile(QFileInfo(logFile).fileName(), &logFile);
|
|
}
|
|
zipWriter.close();
|
|
|
|
showFileInGraphicalShell(QApplication::activeWindow(), QFileInfo(zipFile));
|
|
}
|
|
|
|
|
|
void RCTStatus::addPeer(QString enode, double callbackId) {
|
|
Q_D(RCTStatus);
|
|
qCDebug(RCTSTATUS) << "::addPeer call - callbackId:" << callbackId;
|
|
QtConcurrent::run([&](QString enode, double callbackId) {
|
|
const char* result = AddPeer(enode.toUtf8().data());
|
|
logStatusGoResult("::addPeer AddPeer", result);
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
|
|
}, enode, callbackId);
|
|
}
|
|
|
|
void RCTStatus::saveAccountAndLogin(QString accountData, QString password, QString config, QString subAccountsData) {
|
|
|
|
Q_D(RCTStatus);
|
|
QString finalConfig = prepareDirAndUpdateConfig(config);
|
|
QtConcurrent::run([&](QString accountData, QString password, QString finalConfig, QString subAccountsData) {
|
|
const char* result = SaveAccountAndLogin(accountData.toUtf8().data(), password.toUtf8().data(), finalConfig.toUtf8().data(), subAccountsData.toUtf8().data());
|
|
logStatusGoResult("::saveAccountAndLogin", result);
|
|
}, accountData, password, finalConfig, subAccountsData);
|
|
}
|
|
|
|
void RCTStatus::login(QString accountData, QString password) {
|
|
|
|
Q_D(RCTStatus);
|
|
QtConcurrent::run([&](QString accountData, QString password) {
|
|
const char* result = Login(accountData.toUtf8().data(), password.toUtf8().data());
|
|
logStatusGoResult("::login", result);
|
|
}, accountData, password);
|
|
|
|
}
|
|
|
|
void RCTStatus::logout() {
|
|
Q_D(RCTStatus);
|
|
QtConcurrent::run([&]() {
|
|
const char* result = Logout();
|
|
logStatusGoResult("::logout", result);
|
|
});
|
|
|
|
}
|
|
|
|
void RCTStatus::openAccounts(double callbackId) {
|
|
Q_D(RCTStatus);
|
|
QtConcurrent::run([&](double callbackId) {
|
|
QString rootDir = getDataStoragePath();
|
|
const char* result = OpenAccounts(rootDir.toUtf8().data());
|
|
logStatusGoResult("::openAccounts", result);
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
|
|
}, callbackId);
|
|
|
|
}
|
|
|
|
void RCTStatus::multiAccountStoreAccount(QString json, double callbackId) {
|
|
Q_D(RCTStatus);
|
|
QtConcurrent::run([&](QString json, double callbackId) {
|
|
const char* result = MultiAccountStoreAccount(json.toUtf8().data());
|
|
logStatusGoResult("::multiAccountStoreAccount", result);
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
|
|
}, json, callbackId);
|
|
}
|
|
|
|
void RCTStatus::multiAccountLoadAccount(QString json, double callbackId) {
|
|
Q_D(RCTStatus);
|
|
QtConcurrent::run([&](QString json, double callbackId) {
|
|
const char* result = MultiAccountLoadAccount(json.toUtf8().data());
|
|
logStatusGoResult("::multiAccountLoadAccount", result);
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
|
|
}, json, callbackId);
|
|
}
|
|
|
|
void RCTStatus::multiAccountReset(double callbackId) {
|
|
Q_D(RCTStatus);
|
|
QtConcurrent::run([&](double callbackId) {
|
|
const char* result = MultiAccountReset();
|
|
logStatusGoResult("::multiAccountReset", result);
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
|
|
}, callbackId);
|
|
}
|
|
|
|
void RCTStatus::multiAccountDeriveAddresses(QString json, double callbackId) {
|
|
Q_D(RCTStatus);
|
|
QtConcurrent::run([&](QString json, double callbackId) {
|
|
const char* result = MultiAccountDeriveAddresses(json.toUtf8().data());
|
|
logStatusGoResult("::multiAccountDeriveAddresses", result);
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
|
|
}, json, callbackId);
|
|
}
|
|
|
|
|
|
void RCTStatus::verify(QString address, QString password, double callbackId) {
|
|
Q_D(RCTStatus);
|
|
qCInfo(RCTSTATUS) << "::verify call - callbackId:" << callbackId;
|
|
QtConcurrent::run([&](QString address, QString password, double callbackId) {
|
|
QDir rootDir(getDataStoragePath());
|
|
QString keystorePath = rootDir.absoluteFilePath("keystore");
|
|
const char* result = VerifyAccountPassword(keystorePath.toUtf8().data(), address.toUtf8().data(), password.toUtf8().data());
|
|
logStatusGoResult("::verify VerifyAccountPassword", result);
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
|
|
}, address, password, callbackId);
|
|
}
|
|
|
|
|
|
|
|
void RCTStatus::sendTransaction(QString txArgsJSON, QString password, double callbackId) {
|
|
Q_D(RCTStatus);
|
|
qCDebug(RCTSTATUS) << "::sendTransaction call - callbackId:" << callbackId;
|
|
QtConcurrent::run([&](QString txArgsJSON, QString password, double callbackId) {
|
|
const char* result = SendTransaction(txArgsJSON.toUtf8().data(), password.toUtf8().data());
|
|
logStatusGoResult("::sendTransaction SendTransaction", result);
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
|
|
}, txArgsJSON, password, callbackId);
|
|
}
|
|
|
|
|
|
void RCTStatus::signMessage(QString rpcParams, double callbackId) {
|
|
Q_D(RCTStatus);
|
|
qCDebug(RCTSTATUS) << "::signMessage call - callbackId:" << callbackId;
|
|
QtConcurrent::run([&](QString rpcParams, double callbackId) {
|
|
const char* result = SignMessage(rpcParams.toUtf8().data());
|
|
logStatusGoResult("::signMessage SignMessage", result);
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
|
|
}, rpcParams, callbackId);
|
|
}
|
|
|
|
void RCTStatus::signGroupMembership(QString content, double callbackId) {
|
|
Q_D(RCTStatus);
|
|
qCDebug(RCTSTATUS) << "::signGroupMembership - callbackId:" << callbackId;
|
|
QtConcurrent::run([&](QString content, double callbackId) {
|
|
const char* result = SignGroupMembership(content.toUtf8().data());
|
|
logStatusGoResult("::signGroupMembership SignGroupMembership", result);
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
|
|
}, content, callbackId);
|
|
}
|
|
|
|
void RCTStatus::extractGroupMembershipSignatures(QString signatures, double callbackId) {
|
|
Q_D(RCTStatus);
|
|
qCDebug(RCTSTATUS) << "::extractGroupMembershipSignatures - callbackId:" << callbackId;
|
|
QtConcurrent::run([&](QString signatures, double callbackId) {
|
|
const char* result = ExtractGroupMembershipSignatures(signatures.toUtf8().data());
|
|
logStatusGoResult("::extractGroupMembershipSignatures ExtractGroupMembershipSignatures", result);
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
|
|
}, signatures, callbackId);
|
|
}
|
|
|
|
void RCTStatus::setAdjustResize() {
|
|
}
|
|
|
|
|
|
void RCTStatus::setAdjustPan() {
|
|
}
|
|
|
|
|
|
void RCTStatus::setSoftInputMode(int i) {
|
|
}
|
|
|
|
|
|
|
|
void RCTStatus::clearCookies() {
|
|
}
|
|
|
|
|
|
void RCTStatus::clearStorageAPIs() {
|
|
}
|
|
|
|
|
|
void RCTStatus::callRPC(QString payload, double callbackId) {
|
|
Q_D(RCTStatus);
|
|
qCDebug(RCTSTATUS) << "::callRPC call - payload:" << payload.left(128) << "callbackId:" << callbackId;
|
|
QtConcurrent::run([&](QString payload, double callbackId) {
|
|
const char* result = CallRPC(payload.toUtf8().data());
|
|
logStatusGoResult("::callRPC CallRPC", result);
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
|
|
}, payload, callbackId);
|
|
}
|
|
|
|
void RCTStatus::callPrivateRPC(QString payload, double callbackId) {
|
|
Q_D(RCTStatus);
|
|
qCDebug(RCTSTATUS) << "::callPrivateRPC call - payload:" << payload.left(128) << "callbackId:" << callbackId;
|
|
QtConcurrent::run([&](QString payload, double callbackId) {
|
|
const char* result = CallPrivateRPC(payload.toUtf8().data());
|
|
logStatusGoResult("::callPrivateRPC CallPrivateRPC", result);
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
|
|
}, payload, callbackId);
|
|
}
|
|
|
|
void RCTStatus::closeApplication() {
|
|
}
|
|
|
|
bool RCTStatus::JSCEnabled() {
|
|
qCDebug(RCTSTATUS) << "::JSCEnabled call";
|
|
return false;
|
|
}
|
|
|
|
void RCTStatus::statusGoEventCallback(const char* event) {
|
|
qCDebug(RCTSTATUS) << "::statusGoEventCallback call - event: " << event;
|
|
RCTStatusPrivate::rctStatus->emitStatusGoEvent(event);
|
|
}
|
|
|
|
void RCTStatus::emitStatusGoEvent(QString event) {
|
|
qCDebug(RCTSTATUS) << "::emitStatusGoEvent call - event: " << event;
|
|
Q_EMIT statusGoEvent(event);
|
|
}
|
|
|
|
void RCTStatus::onStatusGoEvent(QString event) {
|
|
qCDebug(RCTSTATUS) << "::onStatusGoEvent call - event: " << event.toUtf8().data();
|
|
RCTStatusPrivate::bridge->eventDispatcher()->sendDeviceEvent("gethEvent", QVariantMap{{"jsonEvent", event.toUtf8().data()}});
|
|
}
|
|
|
|
void RCTStatus::logStatusGoResult(const char* methodName, const char* result)
|
|
{
|
|
QJsonParseError jsonError;
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(QString(result).toUtf8(), &jsonError);
|
|
if (jsonError.error != QJsonParseError::NoError) {
|
|
qCWarning(RCTSTATUS) << qUtf8Printable(jsonError.errorString());
|
|
return;
|
|
}
|
|
|
|
QString error = jsonDoc.toVariant().toMap().value("error").toString();
|
|
if (error.isEmpty()) {
|
|
qCDebug(RCTSTATUS) << methodName << "succeeded";
|
|
} else {
|
|
qCWarning(RCTSTATUS) << methodName << "- error:" << qUtf8Printable(error);
|
|
}
|
|
}
|
|
|
|
void RCTStatus::updateMailservers(QString enodes, double callbackId) {
|
|
Q_D(RCTStatus);
|
|
qCDebug(RCTSTATUS) << "::updateMailservers call - callbackId:" << callbackId;
|
|
QtConcurrent::run([&](QString enodes, double callbackId) {
|
|
const char* result = UpdateMailservers(enodes.toUtf8().data());
|
|
logStatusGoResult("::updateMailservers UpdateMailservers", result);
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
|
|
}, enodes, callbackId);
|
|
}
|
|
|
|
void RCTStatus::getNodesFromContract(QString url, QString address, double callbackId) {
|
|
Q_D(RCTStatus);
|
|
qCDebug(RCTSTATUS) << "::getNodesFromContract call - callbackId:" << callbackId;
|
|
QtConcurrent::run([&](QString url, QString address, double callbackId) {
|
|
const char* result = GetNodesFromContract(url.toUtf8().data(), address.toUtf8().data());
|
|
logStatusGoResult("::getNodesFromContract GetNodesFromContract", result);
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
|
|
}, url, address, callbackId);
|
|
}
|
|
|
|
void RCTStatus::chaosModeUpdate(bool on, double callbackId) {
|
|
Q_D(RCTStatus);
|
|
qCDebug(RCTSTATUS) << "::chaosModeUpdate call - callbackId:" << callbackId;
|
|
QtConcurrent::run([&](bool on, double callbackId) {
|
|
const char* result = ChaosModeUpdate(on);
|
|
logStatusGoResult("::chaosModeUpdate ChaosModeUpdate", result);
|
|
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
|
|
}, on, callbackId);
|
|
}
|