Files
logos-cpp-sdk/cpp/module_proxy.cpp
T
vedran f47b339b0e fix: redact sensitive data from debug logs
Remove logging of method arguments. It may contain private keys,
DB passwords, auth tokens, and token values. Log only method
names and argument counts for debugging.

- https://github.com/status-im/infra-logos/issues/1
2026-02-25 10:03:44 +01:00

466 lines
19 KiB
C++

#include "module_proxy.h"
#include <QDebug>
#include <QMetaObject>
#include <QMetaMethod>
#include <QMetaType>
#include <QJsonArray>
#include <QJsonObject>
#include <QStringList>
#include "../core/interface.h"
#include "logos_api.h"
#include "token_manager.h"
// Helper macro to simplify method invocation with return types
#define INVOKE_METHOD_WITH_RETURN(returnType, castType) \
do { \
castType* result = static_cast<castType*>(returnValue); \
switch (args.size()) { \
case 0: \
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, Q_RETURN_ARG(returnType, *result)); \
case 1: \
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, Q_RETURN_ARG(returnType, *result), scopedArgs[0].arg); \
case 2: \
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, Q_RETURN_ARG(returnType, *result), scopedArgs[0].arg, scopedArgs[1].arg); \
case 3: \
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, Q_RETURN_ARG(returnType, *result), scopedArgs[0].arg, scopedArgs[1].arg, scopedArgs[2].arg); \
case 4: \
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, Q_RETURN_ARG(returnType, *result), scopedArgs[0].arg, scopedArgs[1].arg, scopedArgs[2].arg, scopedArgs[3].arg); \
case 5: \
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, Q_RETURN_ARG(returnType, *result), scopedArgs[0].arg, scopedArgs[1].arg, scopedArgs[2].arg, scopedArgs[3].arg, scopedArgs[4].arg); \
default: \
qWarning() << "ModuleProxy: Currently supports 0-5 arguments. Got:" << args.size(); \
return false; \
} \
} while(0)
namespace {
class ScopedQArg {
public:
ScopedQArg(QMetaMethodArgument a, std::function<void(const void*)> d)
: arg(a), deleter(std::move(d)) {}
~ScopedQArg() {
if (deleter) {
deleter(arg.data);
}
}
ScopedQArg(ScopedQArg&& other)
: arg(std::move(other.arg)), deleter(std::move(other.deleter)) {
other.deleter = nullptr;
}
ScopedQArg& operator=(ScopedQArg&&) = delete;
ScopedQArg(const ScopedQArg&) = delete;
ScopedQArg& operator=(const ScopedQArg&) = delete;
QMetaMethodArgument arg;
private:
std::function<void(const void*)> deleter;
};
auto toScopedQArgs(const QVariantList& args)
{
auto scopedArgs = std::vector<ScopedQArg>{};
for (const auto& arg : args) {
switch (arg.typeId()) {
case QMetaType::Int: {
auto value = new int{arg.toInt()};
scopedArgs.emplace_back(
Q_ARG(int, *value),
[](const void* data) {
delete static_cast<const int*>(data);
}
);
break;
}
case QMetaType::QStringList: {
auto value = new QStringList{arg.toStringList()};
scopedArgs.emplace_back(
Q_ARG(QStringList, *value),
[](const void* data) {
delete static_cast<const QStringList*>(data);
}
);
break;
}
case QMetaType::QByteArray: {
auto value = new QByteArray{arg.toByteArray()};
scopedArgs.emplace_back(
Q_ARG(QByteArray, *value),
[](const void* data) {
delete static_cast<const QByteArray*>(data);
}
);
break;
}
case QMetaType::QUrl: {
auto value = new QUrl{arg.toUrl()};
scopedArgs.emplace_back(
Q_ARG(QUrl, *value),
[](const void* data) {
delete static_cast<const QUrl*>(data);
}
);
break;
}
case QMetaType::QString:
default: {
auto value = new QString{arg.toString()};
scopedArgs.emplace_back(
Q_ARG(QString, *value),
[](const void* data) {
delete static_cast<const QString*>(data);
}
);
break;
}
}
}
return scopedArgs;
}
// Helper method to invoke methods with different return types and argument counts
bool invokeMethodByArgCount(QObject *module, const QString& methodName, const QVariantList& args, void* returnValue, const char* returnTypeName)
{
// Store the UTF-8 data to ensure it stays in scope
QByteArray methodNameBytes = methodName.toUtf8();
const char* methodNameCStr = methodNameBytes.constData();
auto scopedArgs = toScopedQArgs(args);
if (returnValue == nullptr) {
// Void method - no return value
switch (args.size()) {
case 0:
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection);
case 1:
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, scopedArgs[0].arg);
case 2:
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, scopedArgs[0].arg, scopedArgs[1].arg);
case 3:
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, scopedArgs[0].arg, scopedArgs[1].arg, scopedArgs[2].arg);
case 4:
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, scopedArgs[0].arg, scopedArgs[1].arg, scopedArgs[2].arg, scopedArgs[3].arg);
case 5:
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, scopedArgs[0].arg, scopedArgs[1].arg, scopedArgs[2].arg, scopedArgs[3].arg, scopedArgs[4].arg);
default:
qWarning() << "ModuleProxy: Currently supports 0-5 arguments. Got:" << args.size();
return false;
}
} else if (strcmp(returnTypeName, "bool") == 0) {
qDebug() << "ModuleProxy: invokeMethodByArgCount - bool case with" << args.size() << "arguments";
INVOKE_METHOD_WITH_RETURN(bool, bool);
} else if (strcmp(returnTypeName, "int") == 0) {
INVOKE_METHOD_WITH_RETURN(int, int);
} else if (strcmp(returnTypeName, "QString") == 0) {
INVOKE_METHOD_WITH_RETURN(QString, QString);
} else if (strcmp(returnTypeName, "QVariant") == 0) {
INVOKE_METHOD_WITH_RETURN(QVariant, QVariant);
} else if (strcmp(returnTypeName, "QJsonArray") == 0) {
INVOKE_METHOD_WITH_RETURN(QJsonArray, QJsonArray);
} else if (strcmp(returnTypeName, "QStringList") == 0) {
INVOKE_METHOD_WITH_RETURN(QStringList, QStringList);
} else {
qWarning() << "ModuleProxy: Unsupported return type in invokeMethodByArgCount:" << returnTypeName;
return false;
}
}
}
ModuleProxy::ModuleProxy(QObject* module, QObject* parent)
: QObject(parent)
, m_module(module)
{
// Connect to the wrapped object's eventResponse signal to forward events
if (m_module) {
QObject::connect(m_module, SIGNAL(eventResponse(QString, QVariantList)),
this, SIGNAL(eventResponse(QString, QVariantList)));
qDebug() << "ModuleProxy: Connected to wrapped object's eventResponse signal";
}
}
ModuleProxy::~ModuleProxy()
{
qDebug() << "ModuleProxy: Destroyed for module:" << m_module;
}
bool ModuleProxy::saveToken(const QString& from_module_name, const QString& token)
{
if (from_module_name.isEmpty()) {
qWarning() << "ModuleProxy: Cannot save token with empty module name";
return false;
}
if (token.isEmpty()) {
qWarning() << "ModuleProxy: Cannot save empty token for module:" << from_module_name;
return false;
}
qDebug() << "ModuleProxy: Saving token for module:" << from_module_name;
m_tokens[from_module_name] = token;
qDebug() << "ModuleProxy: Token saved successfully. Total tokens stored:" << m_tokens.size();
return true;
}
QVariant ModuleProxy::callRemoteMethod(const QString& authToken, const QString& methodName, const QVariantList& args)
{
if (!m_module) {
qWarning() << "ModuleProxy: Cannot call method on null module:" << methodName;
return QVariant();
}
if (methodName.isEmpty()) {
qWarning() << "ModuleProxy: Method name cannot be empty";
return QVariant();
}
// TODO: review this. note this method is part of ModuleProxy
if (methodName == "getPluginMethods" && args.isEmpty()) {
qDebug() << "ModuleProxy: Handling getPluginMethods() directly";
return QVariant(getPluginMethods());
}
qDebug() << "ModuleProxy: Auth token received:" << authToken;
qDebug() << "ModuleProxy: Calling method" << methodName << "on module" << m_module << "with args:" << args;
PluginInterface* pluginInterface = qobject_cast<PluginInterface*>(m_module);
if (!pluginInterface) {
qWarning() << "ModuleProxy: Module is not a PluginInterface";
return false;
}
// now print the name
qDebug() << "ModuleProxy: PluginInterface name:" << pluginInterface->name();
// get Logos API
LogosAPI* logosAPI = pluginInterface->logosAPI;
if (!logosAPI) {
qWarning() << "ModuleProxy: LogosAPI not available";
return false;
}
// get TokenManager
TokenManager* tokenManager = logosAPI->getTokenManager();
if (!tokenManager) {
qWarning() << "ModuleProxy: TokenManager not available";
return false;
}
// print keys vand values for debug purposes
QList<QString> keys = tokenManager->getTokenKeys();
for (const QString& key : keys) {
qDebug() << "ModuleProxy: Token key:" << key;
}
// check if authToken is valid
if (authToken.isEmpty()) {
qWarning() << "ModuleProxy: Auth token is empty";
return QVariant();
}
// check if authToken is stored in tokenManager
if (!tokenManager->getToken(authToken).isEmpty()) {
qDebug() << "ERROR: ===================== getToken(authToken) is INVALID =====================";
qWarning() << "ModuleProxy: Auth token not found in stored tokens";
qDebug() << "ERROR: ===================== getToken(authToken) is INVALID =====================";
return QVariant();
} else {
qDebug() << "VALID: ===================== getToken(authToken) is VALID =====================";
}
// Each createArgument() call now generates its own unique GUID
// Find the method to get its return type
const QMetaObject* metaObject = m_module->metaObject();
int methodIndex = -1;
qDebug() << "ModuleProxy: Looking for method" << methodName << "with" << args.size() << "arguments";
qDebug() << "ModuleProxy: Available methods in" << metaObject->className() << ":";
// Debug: List all available methods
for (int i = 0; i < metaObject->methodCount(); ++i) {
QMetaMethod method = metaObject->method(i);
qDebug() << " Method" << i << ":" << method.name() << "with" << method.parameterCount() << "parameters, return type:" << method.returnMetaType().name();
}
// Find the method with matching name and argument count
for (int i = 0; i < metaObject->methodCount(); ++i) {
QMetaMethod method = metaObject->method(i);
if (method.name() == methodName && method.parameterCount() == args.size()) {
methodIndex = i;
qDebug() << "ModuleProxy: Found matching method at index" << i;
break;
}
}
if (methodIndex == -1) {
qWarning() << "ModuleProxy: Method not found:" << methodName << "with" << args.size() << "arguments";
return QVariant();
}
QMetaMethod method = metaObject->method(methodIndex);
QMetaType returnType = method.returnMetaType();
qDebug() << "ModuleProxy: Method signature:" << method.methodSignature();
qDebug() << "ModuleProxy: Parameter types:";
for (int i = 0; i < method.parameterCount(); ++i) {
qDebug() << " Param" << i << ":" << method.parameterMetaType(i).name();
}
// Handle different return types
bool success = false;
QVariant result;
if (returnType == QMetaType::fromType<void>()) {
// Void method - no return value expected
success = invokeMethodByArgCount(m_module, methodName, args, nullptr, nullptr);
if (success) {
result = QVariant(true); // Return true to indicate success
}
} else if (returnType == QMetaType::fromType<bool>()) {
// Bool return type
qDebug() << "ModuleProxy: Invoking bool method" << methodName;
bool boolResult = false;
success = invokeMethodByArgCount(m_module, methodName, args, &boolResult, "bool");
qDebug() << "ModuleProxy: Bool method invocation result:" << success << "value:" << boolResult;
if (success) {
result = QVariant(boolResult);
}
} else if (returnType == QMetaType::fromType<int>()) {
// Int return type
int intResult = 0;
success = invokeMethodByArgCount(m_module, methodName, args, &intResult, "int");
if (success) {
result = QVariant(intResult);
}
} else if (returnType == QMetaType::fromType<QString>()) {
// QString return type
QString stringResult;
success = invokeMethodByArgCount(m_module, methodName, args, &stringResult, "QString");
if (success) {
result = QVariant(stringResult);
}
} else if (returnType == QMetaType::fromType<QVariant>()) {
// QVariant return type
QVariant variantResult;
success = invokeMethodByArgCount(m_module, methodName, args, &variantResult, "QVariant");
if (success) {
result = variantResult;
}
} else if (returnType == QMetaType::fromType<QJsonArray>()) {
// QJsonArray return type
qDebug() << "ModuleProxy: Invoking QJsonArray method" << methodName;
QJsonArray jsonArrayResult;
success = invokeMethodByArgCount(m_module, methodName, args, &jsonArrayResult, "QJsonArray");
qDebug() << "ModuleProxy: QJsonArray method invocation result:" << success << "array size:" << jsonArrayResult.size();
if (success) {
result = QVariant(jsonArrayResult);
}
} else if (returnType == QMetaType::fromType<QStringList>()) {
// QStringList return type
qDebug() << "ModuleProxy: Invoking QStringList method" << methodName;
QStringList stringListResult;
success = invokeMethodByArgCount(m_module, methodName, args, &stringListResult, "QStringList");
qDebug() << "ModuleProxy: QStringList method invocation result:" << success << "list size:" << stringListResult.size();
if (success) {
result = QVariant(stringListResult);
}
} else {
qWarning() << "ModuleProxy: Unsupported return type:" << returnType.name() << "for method:" << methodName;
return QVariant();
}
if (!success) {
qWarning() << "ModuleProxy: Failed to invoke method" << methodName << "on module" << m_module;
return QVariant();
}
// Note: Argument cleanup is now handled automatically by each createArgument() call's unique GUID
qDebug() << "ModuleProxy: Successfully called method" << methodName << "on module" << m_module;
return result;
}
bool ModuleProxy::informModuleToken(const QString& authToken, const QString& moduleName, const QString& token)
{
Q_UNUSED(authToken) // Authentication token validation can be added later
// cast m_module to PluginInterface
PluginInterface* pluginInterface = qobject_cast<PluginInterface*>(m_module);
if (!pluginInterface) {
qWarning() << "ModuleProxy: Module is not a PluginInterface";
return false;
}
// now print the name
qDebug() << "ModuleProxy: PluginInterface name:" << pluginInterface->name();
// get Logos API
LogosAPI* logosAPI = pluginInterface->logosAPI;
if (!logosAPI) {
qWarning() << "ModuleProxy: LogosAPI not available";
return false;
}
// get TokenManager
TokenManager* tokenManager = logosAPI->getTokenManager();
if (!tokenManager) {
qWarning() << "ModuleProxy: TokenManager not available";
return false;
}
// save token
qDebug() << "ModuleProxy: Saving token for module:" << moduleName << "with token:" << token;
tokenManager->saveToken(moduleName, token);
qDebug() << "ModuleProxy: Token saved successfully";
return true;
}
QJsonArray ModuleProxy::getPluginMethods()
{
QJsonArray methodsArray;
const QMetaObject* metaObject = m_module->metaObject();
for (int i = 0; i < metaObject->methodCount(); ++i) {
QMetaMethod method = metaObject->method(i);
if (method.enclosingMetaObject() != metaObject) {
continue;
}
QJsonObject methodObj;
methodObj["signature"] = QString::fromUtf8(method.methodSignature());
methodObj["name"] = QString::fromUtf8(method.name());
methodObj["returnType"] = QString::fromUtf8(method.typeName());
methodObj["isInvokable"] = method.isValid() && (method.methodType() == QMetaMethod::Method || method.methodType() == QMetaMethod::Slot);
if (method.parameterCount() > 0) {
QJsonArray params;
for (int p = 0; p < method.parameterCount(); ++p) {
QJsonObject paramObj;
paramObj["type"] = QString::fromUtf8(method.parameterTypeName(p));
QByteArrayList paramNames = method.parameterNames();
if (p < paramNames.size() && !paramNames.at(p).isEmpty()) {
paramObj["name"] = QString::fromUtf8(paramNames.at(p));
} else {
paramObj["name"] = QString("param%1").arg(p);
}
params.append(paramObj);
}
methodObj["parameters"] = params;
}
methodsArray.append(methodObj);
}
return methodsArray;
}
// Include MOC for template instantiation
#include "moc_module_proxy.cpp"