#include "module_proxy.h" #include #include #include #include #include #include #include #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(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 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 deleter; }; auto toScopedQArgs(const QVariantList& args) { auto scopedArgs = std::vector{}; 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(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(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(); } qDebug() << "ModuleProxy: Auth token received:" << authToken; qDebug() << "ModuleProxy: Calling method" << methodName << "on module" << m_module << "with args:" << args; PluginInterface* pluginInterface = qobject_cast(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 keys = tokenManager->getTokenKeys(); for (const QString& key : keys) { qDebug() << "ModuleProxy: Token key:" << key << "value:" << tokenManager->getToken(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 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 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 return type int intResult = 0; success = invokeMethodByArgCount(m_module, methodName, args, &intResult, "int"); if (success) { result = QVariant(intResult); } } else if (returnType == QMetaType::fromType()) { // QString return type QString stringResult; success = invokeMethodByArgCount(m_module, methodName, args, &stringResult, "QString"); if (success) { result = QVariant(stringResult); } } else if (returnType == QMetaType::fromType()) { // QVariant return type QVariant variantResult; success = invokeMethodByArgCount(m_module, methodName, args, &variantResult, "QVariant"); if (success) { result = variantResult; } } else if (returnType == QMetaType::fromType()) { // 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 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(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"