Code owners
Assign users and groups as approvers for specific file changes. Learn more.
PluginLoader.cpp 9.86 KiB
#include "PluginLoader.h"
#include <QDebug>
#include <vector>
#include <sstream>
#include <filesystem>
#ifdef _WIN32
#include <stdio.h>
#include <tchar.h>
#include <string.h>
#include <atlbase.h>
#include <QDebug>
#include <QFileInfo>
#define MAX_KEY_LENGTH 255
#define MAX_VALUE_NAME 16383
std::vector<std::string> QueryKey(HKEY hKey, std::string path)
{
//See https://docs.microsoft.com/en-us/windows/desktop/sysinfo/enumerating-registry-subkeys
std::vector<std::string> list;
TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name
DWORD cchClassName = MAX_PATH; // size of class string
DWORD cSubKeys=0; // number of subkeys
DWORD cbMaxSubKey; // longest subkey size
DWORD cchMaxClass; // longest class string
DWORD cValues; // number of values for key
DWORD cchMaxValue; // longest value name
DWORD cbMaxValueData; // longest value data
DWORD cbSecurityDescriptor; // size of security descriptor
FILETIME ftLastWriteTime; // last write time
DWORD i, retCode;
TCHAR achValue[MAX_VALUE_NAME];
DWORD cchValue = MAX_VALUE_NAME;
// Get the class name and the value count.
retCode = RegQueryInfoKey(
hKey, // key handle
achClass, // buffer for class name
&cchClassName, // size of class string
NULL, // reserved
&cSubKeys, // number of subkeys
&cbMaxSubKey, // longest subkey size
&cchMaxClass, // longest class string
&cValues, // number of values for this key
&cchMaxValue, // longest value name
&cbMaxValueData, // longest value data
&cbSecurityDescriptor, // security descriptor
&ftLastWriteTime); // last write time
// Enumerate the key values.
if (cValues)
{
//printf( "\nNumber of values: %d\n", cValues);
for (i=0, retCode=ERROR_SUCCESS; i<cValues; i++)
{
cchValue = MAX_VALUE_NAME;
achValue[0] = '\0';
retCode = RegEnumValue(hKey, i,
achValue,
&cchValue,
NULL,
NULL,
NULL,
NULL);
if (retCode == ERROR_SUCCESS )
{
CRegKey regKey;
CHAR szBuffer[512];
ULONG dwBufferSize = sizeof(szBuffer);
if(auto error_code = regKey.Open(HKEY_LOCAL_MACHINE, path.c_str()); error_code != ERROR_SUCCESS)
{
qWarning() << "Error opening windows registry path " << path.c_str() << ": " << error_code;
regKey.Close();
continue;
}
if(auto error_code = regKey.QueryStringValue(achValue,szBuffer,&dwBufferSize); error_code != ERROR_SUCCESS)
{
qWarning() << "Error opening windows registry value " << achValue << ": " << error_code;
regKey.Close();
continue;
}
std::string fp = szBuffer;
std::replace( fp.begin(), fp.end(), '\\', '/');
list.push_back(fp);
}
}
}
return list;
}
#endif
std::vector<std::string> PluginLoader::queryRegistryBehaviors(std::string path)
{
std::vector<std::string> list;
#ifdef _WIN32
HKEY hTestKey;
if( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
TEXT(path.c_str()),
0,
KEY_READ,
&hTestKey) == ERROR_SUCCESS)
{
list = QueryKey(hTestKey, path);
}
RegCloseKey(hTestKey);
#endif
return list;
}
#ifdef _WIN32
const char * WinGetEnv(const char * name)
{
const DWORD buffSize = 65535;
static char buffer[buffSize];
if (GetEnvironmentVariableA(name, buffer, buffSize))
{
return buffer;
}
else
{
return 0;
}
}
bool WinSetEnv(const char* name, const char* toWhat){
return SetEnvironmentVariableA(name, toWhat);
}
#endif
bool is_shared_library(std::filesystem::path const& p)
{
auto extension = p.extension();
return extension == ".so" || extension == ".dylib" || extension == ".dll";
}
const char* PluginLoader::addDllPath(std::string f)
{
//Get the directory of the DLL/*.so and add it to the PATH env variable.
//This way dependencies can be shipped in the same directory
#ifdef _WIN32
QFileInfo finf(f.c_str());
//rather than the buggy _getenv: https://docs.microsoft.com/de-de/windows/desktop/api/winbase/nf-winbase-getenvironmentvariable
auto old_path = WinGetEnv("PATH");
auto path = std::ostringstream();
if(old_path){
path << old_path << ";" << finf.absolutePath().toStdString().c_str();
WinSetEnv("PATH", path.str().c_str());
}else{
qWarning() << "Failed to get and modify PATH enviromental variable.";
}
return old_path;
#endif
return "";
}
void PluginLoader::delDllPath(const char* oldPath){
//reset path. We don't want some weird cross-effects
#ifdef _WIN32
if(oldPath){
WinSetEnv("PATH", oldPath);
}
#endif
}
bool endsWith(std::string value, std::string ending)
{
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
std::transform(ending.begin(), ending.end(), ending.begin(), ::tolower);
if (ending.size() > value.size()) return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}
bool validSuffix(std::string f, std::string suffix){
return (endsWith(f,suffix+".dll") || endsWith(f,suffix+".so"));
}
std::vector<std::string> PluginLoader::searchDirectoriesForPlugins(std::vector<std::string> list, std::string suffix){
//Search directories
std::vector<std::string> filesFromFolders;
for (auto f: list)
{
auto path = std::filesystem::path(f);
if (path.empty())
{
continue;
}
if (std::filesystem::is_directory(path))
{
try
{
for (auto& e : std::filesystem::directory_iterator(path))
{
auto p = e.path();
if (is_shared_library(p) && p.replace_extension().extension() == suffix)
{
filesFromFolders.push_back(e.path().string());
}
}
}
catch (std::filesystem::filesystem_error const& e)
{
qWarning() << e.what();
}
}
else if (is_shared_library(path))
{
filesFromFolders.push_back(f);
}
else
{
qWarning() << "Neither a directory, nor a shared library:" << f.data();
}
}
return filesFromFolders;
}
PluginLoader::PluginLoader(QObject *parent)
{
m_MetaData = nullptr;
m_PluginLoader = new QPluginLoader(this);
m_PluginListModel = new QStringListModel();
}
PluginLoader::~PluginLoader()
{
delete m_PluginLoader;
delete m_PluginListModel;
}
bool PluginLoader::loadPluginFromFilename(QString const& filename)
{
bool retval = false;
if (m_PluginLoader->isLoaded()) {
m_PluginLoader->unload();
}
bool isLib = QLibrary::isLibrary(filename);
if (isLib) {
auto oldPath = PluginLoader::addDllPath(filename.toStdString());
m_PluginLoader->setFileName(filename);
readMetaDataFromPlugin();
retval = m_PluginLoader->load();
QString s = m_PluginLoader->errorString();
std::string ss = s.toStdString();
addPluginnameToLists(getCurrentPluginName(), filename);
if (!m_PluginLoader->isLoaded())
{
qWarning() << ss.c_str();
retval = false;
}
PluginLoader::delDllPath(oldPath);
}
else {
retval = false;
}
return retval;
}
void PluginLoader::addPluginnameToLists(QString mstring, QString filename)
{
if (!m_PluginList.contains(mstring))
m_PluginList.append(mstring);
m_PluginListModel->setStringList(m_PluginList);
m_PluginMap.insert(std::pair<QString, QString>(mstring, filename));
}
bool PluginLoader::loadPluginFromName(QString name) {
QString filename = m_PluginMap.find(name)->second;
return loadPluginFromFilename(filename);
}
int PluginLoader::addToPluginList(QString filename, QString suffix) {
if (!validSuffix(filename.toStdString(), suffix.toStdString()))
return 1;
bool isLib = QLibrary::isLibrary(filename);
if (isLib) {
QPluginLoader loader;
loader.setFileName(filename);
QJsonValue pluginMeda(loader.metaData().value("MetaData"));
QJsonObject metaObj = pluginMeda.toObject();
QString mstring = metaObj.value("name").toString();
addPluginnameToLists(mstring, filename);
}
else {
return 2;
qWarning() << "Error reading plugin: " << filename;
}
return 0;
}
QStringListModel* PluginLoader::getPluginList() {
return m_PluginListModel;
}
QObject* PluginLoader::getPluginInstance() {
return (m_PluginLoader->instance());
}
QJsonObject PluginLoader::getPluginMetaData() const
{
if (m_MetaData == nullptr)
qFatal("(getPluginMetaData) No plugin loaded");
return *m_MetaData;
}
void PluginLoader::readMetaDataFromPlugin()
{
m_MetaData = std::make_shared<QJsonObject>(m_PluginLoader->metaData().value("MetaData").toObject());
}
bool PluginLoader::getIsPluginLoaded() {
return m_isPluginLoaded;
}
QString PluginLoader::getCurrentPluginName() {
if (m_MetaData == nullptr)
return "Error name";
return m_MetaData->value("name").toString();
}
const std::map<QString, QString> &PluginLoader::getPluginMap() const
{
return m_PluginMap;
}