Compare commits

...

8 Commits

@ -505,7 +505,7 @@ if(NEROSHOP_BUILD_GUI)
set(neroshop_executable "neroshop")
add_executable(${neroshop_executable})
install(TARGETS ${neroshop_executable} DESTINATION bin)
target_sources(${neroshop_executable} PRIVATE ${neroshop_res} src/gui/main.cpp src/gui/script_manager.cpp src/gui/wallet_controller.cpp src/gui/user_controller.cpp src/gui/image_loader.cpp src/gui/image_provider.cpp src/gui/wallet_qr_provider.cpp src/gui/currency_rate_provider.cpp #[[src/gui/table_model.cpp]] src/gui/daemon_manager.cpp src/gui/enum_wrapper.cpp src/gui/backend.cpp ${neroshop_core_src})
target_sources(${neroshop_executable} PRIVATE ${neroshop_res} src/gui/main.cpp src/gui/script_manager.cpp src/gui/wallet_controller.cpp src/gui/user_controller.cpp src/gui/image_loader.cpp src/gui/image_provider.cpp src/gui/wallet_qr_provider.cpp src/gui/currency_rate_provider.cpp #[[src/gui/table_model.cpp]] src/gui/daemon_manager.cpp src/gui/enum_wrapper.cpp src/gui/proxy_manager.cpp src/gui/backend.cpp ${neroshop_core_src})
target_compile_definitions(${neroshop_executable} PRIVATE NEROSHOP_DEBUG)#set_target_properties(${neroshop_executable} PROPERTIES COMPILE_DEFINITIONS "NEROSHOP_DEBUG")
target_include_directories(${neroshop_executable} PRIVATE #[[${CMAKE_CURRENT_SOURCE_DIR}/src/]] ${monero_include_dirs} ${monero_cpp_include_dir} ${sqlite_include_dir} ${json_include_dir} ${zlib_include_dir} ${i2pd_include_dirs} ${lua_include_dir} ${qrcodegen_include_dir})
target_link_libraries(${neroshop_executable} ${i2pd_src} ${monero_cpp_src} ${sqlite_src} ${qrcodegen_src} ${monero_src} ${zlib_src} ${lua_src})

@ -1 +1 @@
Subproject commit e4c24d84dc9733ec2a53913270226524495dcb6d
Subproject commit 91189c5d013622ced87d89c3eea0d8cad00629a9

@ -409,7 +409,7 @@ Popup {
//Layout.row: 1
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: settingsStack.contentBoxWidth
title: qsTr("Application")
title: qsTr("Appearance")
//width: scrollView.width//contentWidth // does nothing
background: Rectangle {
y: parent.topPadding - parent.bottomPadding
@ -1576,6 +1576,93 @@ Item {
}
}
}
GroupBox {
title: qsTr("Proxy")
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: settingsStack.contentBoxWidth
background: Rectangle {
y: parent.topPadding - parent.bottomPadding
width: parent.width
height: parent.height - parent.topPadding + parent.bottomPadding
color: settingsStack.contentBoxColor
border.color: settingsStack.contentBoxBorderColor
radius: 2
}
label: Label {
x: parent.leftPadding
width: parent.availableWidth
text: parent.title
color: parent.background.border.color
elide: Text.ElideRight
}
// Proxy settings content
ColumnLayout {
id: proxySetColumn
width: parent.width; height: parent.height//childrenRect.height
Item {
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
Text {
anchors.verticalCenter: proxyBox.verticalCenter
text: qsTr("Proxy:")
color: NeroshopComponents.Style.darkTheme ? "#ffffff" : "#000000"
}
NeroshopComponents.ComboBox {
id: proxyBox
anchors.right: parent.right
width: settingsStack.comboBoxWidth; indicatorWidth: settingsStack.comboBoxButtonWidth
model: ["None", "Tor", "i2p"]
currentIndex: model.indexOf("None")//model.indexOf(Script.getJsonRootObject()["proxy"]["type"])
onActivated: {
if(currentText == "None") {
ProxyManager.stopTorDaemon()
ProxyManager.useDefaultProxy()
}
if(currentText == "Tor") {
ProxyManager.startTorDaemon() // calls useTorProxy()
}
if(currentText == "i2p") {
ProxyManager.stopTorDaemon()
ProxyManager.useI2PProxy()
}
}
onCurrentTextChanged: settingsDialog.save()
color: "#f2f2f2"
indicatorDoNotPassBorder: settingsStack.comboBoxNestedButton
}
}
Item {
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
visible: (proxyBox.currentText == "Tor")
ScrollView {
id: processOutputScroller
width: parent.width; height: 125//250
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
// TODO: Scroll to the bottom when new text is added
clip: true
TextArea {
id: processOutputArea
text: ProxyManager.torOutput
wrapMode: Text.Wrap
selectByMouse: true
readOnly: true
color: "#000000"
onTextChanged: {}
background: Rectangle {
color: "#ffffff"
border.color: "#404040"
border.width: parent.activeFocus ? 2 : 1
radius: 3
}
}
}
}
}
}
}
} // StackLayout (settings)
} // ScrollView

@ -37,12 +37,7 @@ void neroshop::KeyMapper::add(const std::string& key, const std::string& value)
std::cerr << "JSON parsing error: " << e.what() << std::endl;
return;
}
//-----------------------------------------------
if (!json.contains("metadata")) {
std::cerr << "No metadata found\n";
return;
}
assert(json["metadata"].is_string());
std::string metadata = json["metadata"].get<std::string>();
//-----------------------------------------------
@ -62,38 +57,42 @@ void neroshop::KeyMapper::add(const std::string& key, const std::string& value)
if(metadata == "listing") {
// Note: As long as we have the product id, we can find the product_ratings
// Map a listing's key to product_ids, product_names, product_categories, product_tags, and product_codes
assert(json.contains("product") && json["product"].is_object());
nlohmann::json product_obj = json["product"];
if (product_obj.contains("id") && product_obj["id"].is_string()) {
std::string product_id = product_obj["id"].get<std::string>();
product_ids[product_id].push_back(key);
}
if (product_obj.contains("name") && product_obj["name"].is_string()) {
std::string product_name = product_obj["name"].get<std::string>();
product_names[product_name].push_back(key);
}
if (product_obj.contains("category") && product_obj["category"].is_string()) {
std::string product_category = product_obj["category"].get<std::string>();
product_categories[product_category].push_back(key);
}
if (product_obj.contains("subcategories") && product_obj["subcategories"].is_array()) {
std::vector<std::string> subcategories = product_obj["subcategories"].get<std::vector<std::string>>();
for (const auto& product_subcategory : subcategories) {
product_categories[product_subcategory].push_back(key);
if(json.contains("product") && json["product"].is_object()) {
nlohmann::json product_obj = json["product"];
if (product_obj.contains("id") && product_obj["id"].is_string()) {
std::string product_id = product_obj["id"].get<std::string>();
product_ids[product_id].push_back(key);
}
if (product_obj.contains("name") && product_obj["name"].is_string()) {
std::string product_name = product_obj["name"].get<std::string>();
product_names[product_name].push_back(key);
}
}
if (product_obj.contains("tags") && product_obj["tags"].is_array()) {
const auto& tags = product_obj["tags"];
for (const auto& tag : tags) {
if (tag.is_string()) {
std::string product_tag = tag.get<std::string>();
product_tags[product_tag].push_back(key);
if (product_obj.contains("category") && product_obj["category"].is_string()) {
std::string product_category = product_obj["category"].get<std::string>();
product_categories[product_category].push_back(key);
}
if (product_obj.contains("subcategories") && product_obj["subcategories"].is_array()) {
const auto& subcategories = product_obj["subcategories"];
for (const auto& subcategory : subcategories) {
if (subcategory.is_string()) {
std::string product_subcategory = subcategory.get<std::string>();
product_categories[product_subcategory].push_back(key);
}
}
}
if (product_obj.contains("tags") && product_obj["tags"].is_array()) {
const auto& tags = product_obj["tags"];
for (const auto& tag : tags) {
if (tag.is_string()) {
std::string product_tag = tag.get<std::string>();
product_tags[product_tag].push_back(key);
}
}
}
}
if (product_obj.contains("code") && product_obj["code"].is_string()) {
std::string product_code = product_obj["code"].get<std::string>();
product_codes[product_code].push_back(key);
if (product_obj.contains("code") && product_obj["code"].is_string()) {
std::string product_code = product_obj["code"].get<std::string>();
product_codes[product_code].push_back(key);
}
}
// Map a listing's key to listing_id
if (json.contains("id") && json["id"].is_string()) {
@ -123,11 +122,12 @@ void neroshop::KeyMapper::add(const std::string& key, const std::string& value)
const auto& order_items = json["items"];
for (const auto& item : order_items) {
assert(item.is_object());
if (item.contains("seller_id") && item["seller_id"].is_string()) {
std::string seller_id = item["seller_id"].get<std::string>();
order_recipients[seller_id].push_back(key);
std::cout << "seller_id (" << seller_id << ") has been mapped to order key (" << key << ")\n";
if(item.is_object()) {
if (item.contains("seller_id") && item["seller_id"].is_string()) {
std::string seller_id = item["seller_id"].get<std::string>();
order_recipients[seller_id].push_back(key);
std::cout << "seller_id (" << seller_id << ") has been mapped to order key (" << key << ")\n";
}
}
}
}*/

@ -317,42 +317,42 @@ int neroshop::Node::set(const std::string& key, const std::string& value) {
std::string metadata = json["metadata"].get<std::string>();
if(metadata != current_json["metadata"].get<std::string>()) { std::cerr << "\033[91mMetadata mismatch\033[0m\n"; return false; } // metadata is immutable
if(metadata == "user") {
if(!json["monero_address"].is_string()) { return false; }
std::string user_id = json["monero_address"].get<std::string>();
if(user_id != current_json["monero_address"].get<std::string>()) { std::cerr << "\033[91mUser ID (Monero Primary Address) mismatch\033[0m\n"; return false; } // monero_address is immutable
}
if(metadata == "listing") {
if(!json["id"].is_string()) { return false; }
if(!json["seller_id"].is_string()) { return false; }
std::string listing_uuid = json["id"].get<std::string>();
std::string seller_id = json["seller_id"].get<std::string>(); // seller_id (monero primary address)
if(listing_uuid != current_json["id"].get<std::string>()) { std::cerr << "\033[91mListing UUID mismatch\033[0m\n"; return false; } // id is immutable
if(seller_id != current_json["seller_id"].get<std::string>()) { std::cerr << "\033[91mSeller ID mismatch\033[0m\n"; return false; } // seller_id is immutable
}
if(metadata == "product_rating" || metadata == "seller_rating") {
if(!json["rater_id"].is_string()) { return false; }
std::string rater_id = json["rater_id"].get<std::string>(); // rater_id (monero primary address)
if(rater_id != current_json["rater_id"].get<std::string>()) { std::cerr << "\033[91mRater ID mismatch\033[0m\n"; return false; } // rater_id is immutable
}
// Make sure the signature has been updated (re-signed)
if (json.contains("signature")) {
assert(json["signature"].is_string());
if (json.contains("signature") && json["signature"].is_string()) {
std::string signature = json["signature"].get<std::string>();
if(signature == current_json["signature"].get<std::string>()) { std::cerr << "\033[91mSignature is outdated\033[0m\n"; return false; }
}
// Note: All messages are unique and cannot be modified once created, so they should not ever be able to pass through this function
// No "last_updated" field found in the modified value, only the current value, discard the new value (its likely outdated) - untested
if(metadata != "message") { // messages cannot be updated
if(!json.contains("last_updated") && current_json.contains("last_updated")) {
std::cout << "Value for key (" << key << ") is already up to date" << std::endl;
return true;
}
if(!json.contains("last_updated") && current_json.contains("last_updated")) {
std::cout << "Value for key (" << key << ") is already up to date" << std::endl;
return true;
}
// Compare "last_updated" field of modified value and current value - untested
if(json.contains("last_updated")) {
assert(json["last_updated"].is_string());
if(json.contains("last_updated") && json["last_updated"].is_string()) {
std::string last_updated = json["last_updated"].get<std::string>();
// Check if current value has a last_updated field too
if(current_json.contains("last_updated")) {
assert(current_json["last_updated"].is_string());
if(current_json.contains("last_updated") && current_json["last_updated"].is_string()) {
std::string current_last_updated = current_json["last_updated"].get<std::string>();
// Compare the new json's last_updated timestamp with the current json's own
// And choose whichever has the most recent timestamp then exit the function
@ -972,8 +972,14 @@ void neroshop::Node::republish() {
//-----------------------------------------------------------------------------
bool neroshop::Node::validate(const std::string& key, const std::string& value) {
assert(key.length() == 64 && "Key length is not 64 characters");
assert(!value.empty() && "Value is empty");
if(key.length() != 64) {
std::cerr << "Key length is not 64 characters\n";
return false;
}
if(value.empty()) {
std::cerr << "Value is empty\n";
return false;
}
// Ensure that the value is valid JSON
nlohmann::json json;
@ -989,7 +995,7 @@ bool neroshop::Node::validate(const std::string& key, const std::string& value)
std::cerr << "No metadata found\n";
return false;
}
assert(json["metadata"].is_string());
if(!json["metadata"].is_string()) { return false; }
std::string metadata = json["metadata"].get<std::string>();
std::vector<std::string> valid_metadatas = { "user","listing","product_rating","seller_rating","order","message", };
if (std::find(valid_metadatas.begin(), valid_metadatas.end(), metadata) == valid_metadatas.end()) {
@ -1006,7 +1012,7 @@ bool neroshop::Node::validate(const std::string& key, const std::string& value)
// Check whether data is expired so we don't store it
db::Sqlite3 * database = neroshop::get_database();
if(json.contains("expiration_date")) {
assert(json["expiration_date"].is_string());
if(!json["expiration_date"].is_string()) { return false; }
std::string expiration_date = json["expiration_date"].get<std::string>();
if(neroshop_timestamp::is_expired(expiration_date)) {
// Remove the data from hash table if it was previously stored
@ -1030,25 +1036,31 @@ bool neroshop::Node::verify(const std::string& value) const {
// Get required fields from the value
std::string metadata = json["metadata"].get<std::string>();
std::string signed_message, signing_address;
std::string signed_message, signing_address, signature;
// Messages and orders don't need to be verified by peers since they will be encrypted then decrypted and verified by the intended recipient instead
if(metadata == "order") { return true; }
if(metadata == "message") { return true; }
if(metadata == "user") {
if(!json["monero_address"].is_string()) { return false; }
signed_message = json["monero_address"].get<std::string>(); // user_id
signing_address = signed_message; // the monero_address (monero primary address) is both the signed message and the signing address
}
if(metadata == "listing") {
if(!json["id"].is_string()) { return false; }
signed_message = json["id"].get<std::string>(); // the id (uuid) is the signed message
if(!json["seller_id"].is_string()) { return false; }
signing_address = json["seller_id"].get<std::string>(); // the seller_id (monero primary address) is the signing address
}
if(metadata == "product_rating" || metadata == "seller_rating") {
if(!json["comments"].is_string()) { return false; }
signed_message = json["comments"].get<std::string>(); // the comments is the signed message
if(!json["rater_id"].is_string()) { return false; }
signing_address = json["rater_id"].get<std::string>(); // the rater_id (monero primary address) is the signing address
}
// Messages and orders don't need to be signed since they will be encrypted??
if(metadata == "order") { return true; }
if(metadata == "message") { return true; }
std::string signature;
// Get signature field
if (json.contains("signature")) {
assert(json["signature"].is_string());
if(!json["signature"].is_string()) { return false; }
signature = json["signature"].get<std::string>(); // the signature may have been updated
}
@ -1160,7 +1172,7 @@ void neroshop::Node::periodic_check() {
/*bool neroshop::Node::on_keyword_blocked(const nlohmann::json& value) {
// Note: This code is in the testing stage
// Block certain keywords/search terms from listings
assert(json.contains("metadata"));
if(!json.contains("metadata")) { return false; }
if(json["metadata"] == "listing") {
//--------------------------------------------
// Block categories marked as "Illegal"
@ -1174,7 +1186,7 @@ void neroshop::Node::periodic_check() {
std::vector<std::string> blocked_tags = { "heroin", "meth", "cp", "child porn" };
if(json["product"].contains("tags")) {
assert(json["product"]["tags"].is_array());
if(!json["product"]["tags"].is_array()) { return false; }
std::vector<std::string> product_tags = json["product"]["tags"].get<std::vector<std::string>>();
// Check if any of the product tags match the blocked tags
bool has_blocked_tag = std::any_of(product_tags.begin(), product_tags.end(), [&](const std::string& tag) {

@ -639,6 +639,7 @@ void neroshop::User::send_message(const std::string& recipient_id, const std::st
data["expiration_date"] = neroshop::timestamp::get_utc_timestamp_after_duration(number, time_unit);
}
}
data["signature"] = wallet->sign_message(content, monero_message_signature_type::SIGN_WITH_SPEND_KEY);
std::string value = data.dump();
std::string key = neroshop::crypto::sha3_256(value);//std::cout << "key: " << key << "\nvalue: " << value << "\n";

@ -44,7 +44,6 @@ void neroshop::DaemonManager::startDaemonProcess()
}
// Start the daemon process
QProcess daemonProcess;
daemonProcess.start(program, arguments, QIODevice::ReadWrite);
if (daemonProcess.waitForStarted()) {
// Daemon process started successfully

@ -120,6 +120,8 @@ int main(int argc, char *argv[])
DaemonManager * daemonManager = new DaemonManager(&engine);
daemonManager->startDaemonProcessDetached();
engine.rootContext()->setContextProperty("DaemonManager", daemonManager);
ProxyManager * proxyManager = new ProxyManager(&engine);
engine.rootContext()->setContextProperty("ProxyManager", proxyManager);
// we can also register an instance of a class instead of the class itself
WalletController *wallet = new WalletController(&engine);
engine.rootContext()->setContextProperty("Wallet", wallet);//new WalletController());//qmlRegisterUncreatableType<WalletProxy>("neroshop.Wallet", 1, 0, "Wallet", "Wallet cannot be instantiated directly.");//qmlRegisterType<WalletProxy>("neroshop.Wallet", 1, 0, "Wallet"); // Usage: import neroshop.Wallet ... Wallet { id: wallet }

@ -0,0 +1,337 @@
#include "proxy_manager.hpp"
#include <QFile>
#include <QNetworkProxy>
#include <QNetworkProxyFactory>
#include <QNetworkProxyQuery>
#include <QNetworkRequest>
#include <QString>
#include <QUrl>
#include "../neroshop_config.hpp"
#include "../core/tools/filesystem.hpp"
#include "../core/tools/device.hpp"
neroshop::ProxyManager::ProxyManager(QObject* parent) : QObject(parent), m_torEnabled(false) {
torManager = new QNetworkAccessManager(this);
i2pManager = new QNetworkAccessManager(this);
clearnetManager = new QNetworkAccessManager(this);
// Connect finished signal to onReplyFinished slot
connect(torManager, &QNetworkAccessManager::finished, this, &ProxyManager::onReplyFinished);
connect(i2pManager, &QNetworkAccessManager::finished, this, &ProxyManager::onReplyFinished);
connect(clearnetManager, &QNetworkAccessManager::finished, this, &ProxyManager::onReplyFinished);
QNetworkProxy torProxy;
torProxy.setType(QNetworkProxy::Socks5Proxy);
torProxy.setHostName("127.0.0.1"); // Tor SOCKS5 proxy address
torProxy.setPort(9050); // Tor SOCKS5 proxy port
torManager->setProxy(torProxy);
QNetworkProxy i2pProxy;
i2pProxy.setType(QNetworkProxy::Socks5Proxy);
i2pProxy.setHostName("127.0.0.1");
i2pProxy.setPort(4447); // 4448?
i2pManager->setProxy(i2pProxy);
}
neroshop::ProxyManager::~ProxyManager() {}
void neroshop::ProxyManager::useDefaultProxy() {
QNetworkProxy::setApplicationProxy(QNetworkProxy());
}
void neroshop::ProxyManager::useTorProxy() {
QNetworkProxy torProxy;
torProxy.setType(QNetworkProxy::Socks5Proxy);
torProxy.setHostName("127.0.0.1");
torProxy.setPort(9050);
QNetworkProxy::setApplicationProxy(torProxy);
}
void neroshop::ProxyManager::useI2PProxy() {
QNetworkProxy i2pProxy;
i2pProxy.setType(QNetworkProxy::Socks5Proxy);
i2pProxy.setHostName("127.0.0.1");
i2pProxy.setPort(4447);
QNetworkProxy::setApplicationProxy(i2pProxy);
}
QNetworkAccessManager * neroshop::ProxyManager::getNetworkClearnet() const {
return clearnetManager;
}
QNetworkAccessManager * neroshop::ProxyManager::getNetworkTor() const {
return torManager;
}
QNetworkAccessManager * neroshop::ProxyManager::getNetworkI2P() const {
return i2pManager;
}
QNetworkAccessManager * neroshop::ProxyManager::getNetwork() const {
QNetworkProxyQuery query(QUrl("http://example.com"));
QList<QNetworkProxy> proxies = QNetworkProxyFactory::proxyForQuery(query);
if (proxies.isEmpty()) {
return clearnetManager;
}
foreach(const QNetworkProxy &proxy, proxies) {
auto proxyType = proxy.type(); // https://doc.qt.io/qt-6/qnetworkproxy.html#ProxyType-enum
auto proxyPort = proxy.port();
//auto proxyHostName = proxy.hostName();
qDebug() << "Proxy type:" << proxy.type();
switch(proxyType) {
case QNetworkProxy::DefaultProxy: return clearnetManager;
case QNetworkProxy::Socks5Proxy:
if(proxyPort == 9050) {
return torManager;
}
if(proxyPort == 4447) {
return i2pManager;
}
return torManager; // default for Socks5
case QNetworkProxy::NoProxy: return clearnetManager;
case QNetworkProxy::HttpProxy: return nullptr;
case QNetworkProxy::HttpCachingProxy: return nullptr;
case QNetworkProxy::FtpCachingProxy: return nullptr;
default: return clearnetManager;
}
}
return nullptr;
}
namespace {
const std::string tor_archive_url { "https://archive.torproject.org/tor-package-archive/torbrowser" };
const std::string tor_browser_version { "13.0.14" }; // tor 0.4.8.11
const std::string tor_expert_bundle { "tor-expert-bundle-" +
neroshop::device::get_os() + "-" +
neroshop::device::get_architecture() + "-" + tor_browser_version + ".tar.gz" };
const std::string tor { tor_archive_url + "/" + tor_browser_version + "/" + tor_expert_bundle };
}
void neroshop::ProxyManager::downloadTor() {
if(hasTor()) {
std::string torDirPath = NEROSHOP_DEFAULT_CONFIGURATION_PATH + "/tor";
std::cout << "Tor found in " << torDirPath << std::endl;
return;
}
std::string save_as { NEROSHOP_DEFAULT_CONFIGURATION_PATH + "/" + tor_expert_bundle };//"tor.tar.gz" };
std::cout << "Downloading " << tor << " to " << NEROSHOP_DEFAULT_CONFIGURATION_PATH << "...\n\n";
QNetworkAccessManager * manager = new QNetworkAccessManager(nullptr);
QNetworkReply * reply = manager->get(QNetworkRequest(QUrl(QString::fromStdString(tor))));
QObject::connect(reply, &QNetworkReply::downloadProgress, [=](qint64 bytesReceived, qint64 bytesTotal) {
qInfo() << "Download progress:" << bytesReceived << "/" << bytesTotal;
});
// Declare file outside the lambda function's scope
QFile* file = new QFile(QString::fromStdString(save_as));
// Connect to the finished signal to write the file
QObject::connect(reply, &QNetworkReply::finished, [=]() {
if (reply->error() == QNetworkReply::NoError) {
// If download succeeds, write the file
if (!file->open(QIODevice::WriteOnly | QIODevice::Truncate)) {
qDebug() << "Failed to open file for writing:" << file->errorString();
delete file;
delete reply;
delete manager;
return;
}
file->write(reply->readAll());
qInfo() << "Download succeeded";
file->flush();
file->close();
delete file;
// Extract the file
if(neroshop::filesystem::is_file(save_as)) {
extractTar(QString::fromStdString(save_as));
} else {
qWarning() << "Error: Downloaded file not found\n";
}
} else {
qWarning() << "Download failed:" << reply->errorString();
delete file;
delete reply;
delete manager;
return;
}
// Clean up resources
reply->deleteLater();
manager->deleteLater();// or delete manager;
});
}
void neroshop::ProxyManager::extractTar(const QString& fileName) {
auto fileNameStdString = fileName.toStdString();
std::cout << "Extracting " << "tor/ from " << fileNameStdString << "...\n";
std::string folder = fileNameStdString.substr(0, fileNameStdString.find_last_of("\\/")); // get path from filename
assert(neroshop::filesystem::is_directory(folder));
//--------------------------------------------------
QString extractDir = QString::fromStdString(folder);
QString wildcards = "tor/*";
QProcess process;
process.setProgram("tar");
QStringList arguments;
arguments << "-xzf" << fileName << "-C" << extractDir << "--wildcards" << wildcards;
process.setArguments(arguments);
process.start();
process.waitForFinished();
if(process.exitCode() != 0) {
qDebug() << process.readAllStandardError();
}
//--------------------------------------------------
// Check if tor/tor file was extracted or not
std::string out_file { folder + "/" + "tor/tor" };
if(!neroshop::filesystem::is_file(out_file)) {
std::cout << "Error extracting tar\n";
return;// or try a different method?
}
std::cout << "Extraction completed!\n";
std::remove(fileNameStdString.c_str()); // Delete the tar.gz file after we're done extracting
}
void neroshop::ProxyManager::startTorDaemon() {
std::string torDirPath = NEROSHOP_DEFAULT_CONFIGURATION_PATH + "/tor";
#ifdef Q_OS_WIN
QString program = QString::fromStdString(torDirPath + "/" + "tor.exe");
#else
QString program = QString::fromStdString(torDirPath + "/" + "./tor");
#endif
if(isTorRunning()) {
std::cout << "\033[90mtor was already running in the background\033[0m\n";
useTorProxy();
setTorEnabled(true);
std::cout << "Tor is now enabled.\n";
return;
}
// Start Tor
if(!torProcess) {
torProcess = new QProcess(this);
QObject::connect(torProcess, &QProcess::started, [&]() { // only works when placed before calling torProcess->start()
emit processStarted();
});
}
torProcess->start(program, QStringList());
if (torProcess->waitForStarted()) {
qint64 pid = torProcess->processId();
std::cout << "\033[90;1mtor started (pid: " << pid << ")\033[0m\n";
useTorProxy();
setTorEnabled(true);
std::cout << "Tor is now enabled.\n";
} else {
torOutput.append(QString("%1 is missing so Tor could not be started\n").arg(program));
emit torOutputChanged(torOutput);
return;
}
// Connect the readyReadStandardOutput signal to a slot
QObject::connect(torProcess, &QProcess::readyReadStandardOutput, [&]() {
// Read the output of the process
QByteArray output = torProcess->readAllStandardOutput();
// Convert the output to QString and print it
////qDebug() << "Process output:" << QString::fromUtf8(output);
// Convert the output to QString and store it in a member variable
torOutput.append(QString::fromUtf8(output));
emit torOutputChanged(torOutput); // Emit signal when the output changes
});
// Connect the finished signal to a slot
QObject::connect(torProcess, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
[&](int exitCode, QProcess::ExitStatus exitStatus) {
// Handle process finished event
qDebug() << "Process finished with exit code:" << exitCode;
setTorEnabled(false);
emit processFinished(exitCode, exitStatus);
});
}
void neroshop::ProxyManager::stopTorDaemon() {
if(torProcess) {
torProcess->kill(); // Terminate the process
torProcess->deleteLater(); // Delete the QProcess instance
torProcess = nullptr;
}
}
void neroshop::ProxyManager::setTorEnabled(bool torEnabled) {
if(m_torEnabled != torEnabled) {
m_torEnabled = torEnabled;
emit networkProxyChanged();
}
}
QString neroshop::ProxyManager::getTorOutput() const {
return torOutput;
}
QNetworkReply * neroshop::ProxyManager::getUrl(const QString& url) {
return torManager->get(QNetworkRequest(QUrl(url)));
//clearnetManager->get(QNetworkRequest(QUrl(url)));
}
bool neroshop::ProxyManager::hasTor() {
std::string torDirPath = NEROSHOP_DEFAULT_CONFIGURATION_PATH + "/tor";
#ifdef Q_OS_WIN
std::string torExecutable = torDirPath + "/" + "tor.exe";
#else
std::string torExecutable = torDirPath + "/" + "tor";
#endif
return neroshop::filesystem::is_file(torExecutable);
}
bool neroshop::ProxyManager::isTorRunning() {
QTcpSocket socket;
socket.connectToHost("127.0.0.1", 9050); // Connect to Tor's SOCKS proxy port
if (!socket.waitForConnected()) {
QString errorMessage = socket.errorString();
if (errorMessage.contains("Address already in use")) {
return true;
} else {
//qDebug() << "Failed to connect to Tor's SOCKS proxy port:" << errorMessage;
return false;
}
} else {
// Close the socket
socket.close();
return true;
}
}
bool neroshop::ProxyManager::isTorEnabled() const {
return m_torEnabled;
}
void neroshop::ProxyManager::onReplyFinished(QNetworkReply * reply) {
if(reply->error() == QNetworkReply::NoError) {
// Reply received successfully
QByteArray data = reply->readAll();
////qDebug() << "Data received:" << data;
} else {
// Error occurred
qDebug() << "Network error: " << reply->errorString();
}
// Clean up the reply
reply->deleteLater();
}

@ -0,0 +1,60 @@
#pragma once
#ifndef PROXY_MANAGER_HPP_NEROSHOP
#define PROXY_MANAGER_HPP_NEROSHOP
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QProcess>
namespace neroshop {
class ProxyManager : public QObject {
Q_OBJECT
Q_PROPERTY(bool torEnabled READ isTorEnabled NOTIFY networkProxyChanged)
Q_PROPERTY(QString torOutput READ getTorOutput NOTIFY torOutputChanged)
public:
ProxyManager(QObject *parent = nullptr);
~ProxyManager();
Q_INVOKABLE static void useDefaultProxy();
Q_INVOKABLE static void useTorProxy();
Q_INVOKABLE static void useI2PProxy();
Q_INVOKABLE static void downloadTor();
Q_INVOKABLE void startTorDaemon();
Q_INVOKABLE void stopTorDaemon();
void setTorEnabled(bool torEnabled);
QNetworkAccessManager * getNetworkClearnet() const;
QNetworkAccessManager * getNetworkTor() const;
QNetworkAccessManager * getNetworkI2P() const;
QNetworkAccessManager * getNetwork() const;
QString getTorOutput() const;
Q_INVOKABLE static bool hasTor();
Q_INVOKABLE static bool isTorRunning();
Q_INVOKABLE bool isTorEnabled() const;
public slots:
QNetworkReply * getUrl(const QString& url);
void onReplyFinished(QNetworkReply * reply);
signals:
void networkProxyChanged();
void processStarted();
void processFinished(int exitCode, QProcess::ExitStatus exitStatus);
void torOutputChanged(const QString &output);
private:
static void extractTar(const QString& fileName);
QNetworkAccessManager * clearnetManager;
QNetworkAccessManager * torManager;
QNetworkAccessManager * i2pManager;
bool m_torEnabled; // tor status
QProcess *torProcess = nullptr;
QString torOutput;
};
}
#endif

@ -529,6 +529,16 @@ QVariantList neroshop::UserController::getMessages() const {
message_object.insert("sender_id", QString::fromStdString(message_decrypted.second));
message_object.insert("recipient_id", QString::fromStdString(value_obj["recipient_id"].get<std::string>()));
message_object.insert("timestamp", QString::fromStdString(value_obj["timestamp"].get<std::string>()));
if(value_obj.contains("signature") && value_obj["signature"].is_string()) {
std::string signature = value_obj["signature"].get<std::string>();
auto verified = _user->get_wallet()->get_monero_wallet()->verify_message(message_decrypted.first, message_decrypted.second, signature).m_is_good;
#ifdef NEROSHOP_DEBUG
std::cout << "\033[1mverified: " << (verified == 1 ? "\033[32mpass" : "\033[91mfail") << "\033[0m\n";
#endif
message_object.insert("verified", verified);
} else {
message_object.insert("verified", false);
}
}
messages_array.append(message_object);
}

@ -65,6 +65,7 @@
#include "gui/enum_wrapper.hpp"
#include "gui/image_loader.hpp"
#include "gui/image_provider.hpp"
#include "gui/proxy_manager.hpp"
#include "gui/script_manager.hpp"
//#include "gui/table_model.hpp"
#include "gui/user_controller.hpp"

Loading…
Cancel
Save