/** \file PluginsManager.cpp \brief Define the class to manage and load the plugins \author alpha_one_x86 \licence GPL3, see the file COPYING */ #include #include #include #include #include #include #include "PluginsManager.h" #include "cpp11addition.h" #include "FacilityEngine.h" /// \brief Create the manager and load the defaults variables PluginsManager::PluginsManager() { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"start"); //load the overall instance pluginLoaded = false; language = "en"; stopIt = false; pluginInformation = NULL; #ifndef ULTRACOPIER_PLUGIN_ALL_IN_ONE importingPlugin = false; #endif editionSemList.release(); englishPluginType.push_back("CopyEngine");englishPluginType.push_back("Languages");englishPluginType.push_back("Listener");englishPluginType.push_back("PluginLoader");englishPluginType.push_back("SessionLoader");englishPluginType.push_back("Themes"); //catPlugin << tr("CopyEngine") << tr("Languages") << tr("Listener") << tr("PluginLoader") << tr("SessionLoader") << tr("Themes"); #ifdef ULTRACOPIER_PLUGIN_IMPORT_SUPPORT connect(&decodeThread, &QXzDecodeThread::decodedIsFinish, this, &PluginsManager::decodingFinished,Qt::QueuedConnection); #endif connect(this, &PluginsManager::finished, this, &PluginsManager::post_operation,Qt::QueuedConnection); // connect(this, &PluginsManager::pluginListingIsfinish, options,&OptionEngine::setInterfaceValue); //load the plugins list /// \bug bug when I put here: moveToThread(this);, due to the direction connection to remove the plugin start(); } /// \brief Destroy the manager PluginsManager::~PluginsManager() { stopIt=true; if(pluginInformation!=NULL) delete pluginInformation; if(this->isRunning()) this->wait(0); } /// \brief set current language void PluginsManager::setLanguage(const std::string &language) { this->language=language; } void PluginsManager::post_operation() { pluginLoaded=true; emit pluginListingIsfinish(); } bool PluginsManager::allPluginHaveBeenLoaded() const { return pluginLoaded; } void PluginsManager::lockPluginListEdition() { editionSemList.acquire(); } void PluginsManager::unlockPluginListEdition() { editionSemList.release(); } void PluginsManager::run() { regexp_to_clean_1=std::regex("[\n\r]+"); regexp_to_clean_2=std::regex("[ \t]+"); regexp_to_clean_3=std::regex("(&&)+"); regexp_to_clean_4=std::regex("^&&"); regexp_to_clean_5=std::regex("&&$"); regexp_to_dep_1=std::regex("(&&|\\|\\||\\(|\\))"); regexp_to_dep_2=std::regex("^(<=|<|=|>|>=)[a-zA-Z0-9\\-]+-([0-9]+\\.)*[0-9]+$"); regexp_to_dep_3=std::regex("(<=|<|=|>|>=)"); regexp_to_dep_4=std::regex("-([0-9]+\\.)*[0-9]+"); regexp_to_dep_5=std::regex("[a-zA-Z0-9\\-]+-"); regexp_to_dep_6=std::regex("[a-zA-Z0-9\\-]+-([0-9]+\\.)*[0-9]+"); //load the path and plugins into the path const std::string &separator=FacilityEngine::separator(); std::vector readPath; readPath=ResourcesManager::resourcesManager->getReadPath(); pluginsList.clear(); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"pluginsList.size(): "+std::to_string(pluginsList.size())); foreach(std::string basePath,readPath) { foreach(std::string dirSub,englishPluginType) { std::string pluginComposed=basePath+dirSub+separator; QDir dir(QString::fromStdString(pluginComposed)); if(stopIt) return; ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"search plugin into: "+pluginComposed); if(dir.exists()) { foreach(QString dirName, dir.entryList(QDir::Dirs|QDir::NoDotAndDotDot)) { if(stopIt) return; loadPluginInformation(pluginComposed+dirName.toStdString()+separator); } } } } #ifdef ULTRACOPIER_DEBUG unsigned int index_debug=0; while(index_debug list; unsigned int index=0; while(indexcategoryToTranslation(category); } bool PluginsManager::isSamePlugin(const PluginsAvailable &pluginA,const PluginsAvailable &pluginB) { /*if(pluginA.category!=pluginB.category) return false;*/ //only this test should be suffisent if(pluginA.path!=pluginB.path) return false; /*if(pluginA.name!=pluginB.name) return false; if(pluginA.writablePath!=pluginB.writablePath) return false; if(pluginA.categorySpecific!=pluginB.categorySpecific) return false; if(pluginA.version!=pluginB.version) return false; if(pluginA.informations!=pluginB.informations) return false;*/ return true; } bool PluginsManager::loadPluginInformation(const std::string &path) { PluginsAvailable tempPlugin; tempPlugin.isAuth = false; tempPlugin.path = path; tempPlugin.category = PluginType_Unknow; QDir pluginPath(QString::fromStdString(path)); if(pluginPath.cdUp() && pluginPath.cdUp() && !ResourcesManager::resourcesManager->getWritablePath().empty() && pluginPath==QDir(QString::fromStdString(ResourcesManager::resourcesManager->getWritablePath()))) tempPlugin.isWritable=true; else tempPlugin.isWritable=false; QFile xmlMetaData(QString::fromStdString(path)+"informations.xml"); if(xmlMetaData.exists()) { if(xmlMetaData.open(QIODevice::ReadOnly)) { loadPluginXml(&tempPlugin,xmlMetaData.readAll()); xmlMetaData.close(); } else { tempPlugin.errorString=tr("informations.xml is not accessible").toStdString(); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"informations.xml is not accessible into the plugin: "+path); } } else { tempPlugin.errorString=tr("informations.xml not found for the plugin").toStdString(); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"informations.xml not found for the plugin: "+path); } editionSemList.acquire(); pluginsList.push_back(tempPlugin); if(tempPlugin.errorString.empty()) pluginsListIndexed[tempPlugin.category].push_back(tempPlugin); editionSemList.release(); if(tempPlugin.errorString.empty()) return true; else { emit onePluginInErrorAdded(tempPlugin); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"Error detected, the not loaded: "+tempPlugin.errorString+", for path: "+tempPlugin.path); return false; } } void PluginsManager::loadPluginXml(PluginsAvailable * thePlugin,const QByteArray &xml) { QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(xml, false, &errorStr,&errorLine,&errorColumn)) { thePlugin->errorString=tr("%1, parse error at line %2, column %3: %4").arg("informations.xml").arg(errorLine).arg(errorColumn).arg(errorStr).toStdString(); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"informations.xml, Parse error at line "+std::to_string(errorLine)+", column "+std::to_string(errorColumn)+": "+errorStr.toStdString()); } else { QDomElement root = domDocument.documentElement(); if (root.tagName() != QStringLiteral("package")) { thePlugin->errorString=tr("\"package\" root tag not found for the xml file").toStdString(); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"\"package\" root balise not found for the xml file"); } //load the variable if(thePlugin->errorString.empty()) loadBalise(root,"title",&(thePlugin->informations),&(thePlugin->errorString),true,true,true); if(thePlugin->errorString.empty()) loadBalise(root,"website",&(thePlugin->informations),&(thePlugin->errorString),false,true); if(thePlugin->errorString.empty()) loadBalise(root,"description",&(thePlugin->informations),&(thePlugin->errorString),true,true,true); if(thePlugin->errorString.empty()) loadBalise(root,"author",&(thePlugin->informations),&(thePlugin->errorString),true,false); if(thePlugin->errorString.empty()) loadBalise(root,"pubDate",&(thePlugin->informations),&(thePlugin->errorString),true,false); if(thePlugin->errorString.empty()) { loadBalise(root,"version",&(thePlugin->informations),&(thePlugin->errorString),true,false); if(thePlugin->errorString.empty()) thePlugin->version=thePlugin->informations.back().back(); } if(thePlugin->errorString.empty()) { loadBalise(root,"category",&(thePlugin->informations),&(thePlugin->errorString),true,false); if(thePlugin->errorString.empty()) { std::string tempCat=thePlugin->informations.back().back(); if(tempCat=="Languages") thePlugin->category=PluginType_Languages; else if(tempCat=="CopyEngine") thePlugin->category=PluginType_CopyEngine; else if(tempCat=="Listener") thePlugin->category=PluginType_Listener; else if(tempCat=="PluginLoader") thePlugin->category=PluginType_PluginLoader; else if(tempCat=="SessionLoader") thePlugin->category=PluginType_SessionLoader; else if(tempCat=="Themes") thePlugin->category=PluginType_Themes; else thePlugin->errorString="Unknow category: "+std::to_string((int)thePlugin->category); if(thePlugin->errorString.empty()) { if(thePlugin->category!=PluginType_Languages) { #ifndef ULTRACOPIER_PLUGIN_ALL_IN_ONE loadBalise(root,"architecture",&(thePlugin->informations),&(thePlugin->errorString),true,false); if(thePlugin->errorString.empty()) { const std::string &platform=thePlugin->informations.back().back(); if(platform!=ULTRACOPIER_PLATFORM_CODE) thePlugin->errorString="Wrong platform code: "+platform+std::string(" should be ")+ULTRACOPIER_PLATFORM_CODE; } #endif } } } } if(thePlugin->errorString.empty()) { loadBalise(root,"name",&(thePlugin->informations),&(thePlugin->errorString),true,false); if(thePlugin->errorString.empty()) { thePlugin->name=thePlugin->informations.back().back(); size_t index=0; while(indexname && pluginsList.at(index).category==thePlugin->category) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"Plugin duplicate found ("+std::to_string((int)thePlugin->category)+"/"+pluginsList.at(index).informations.at(sub_index).back()+"), already loaded, actual version skipped: "+thePlugin->version); thePlugin->errorString=tr("Duplicated plugin found, already loaded!").toStdString(); break; break; } sub_index++; } index++; } } } if(thePlugin->errorString.empty()) loadBalise(root,"dependencies",&(thePlugin->informations),&(thePlugin->errorString),true,false); if(thePlugin->errorString.empty()) { QDomElement child = root.firstChildElement("categorySpecific"); if(!child.isNull() && child.isElement()) thePlugin->categorySpecific=child; } } } /// \brief to load the multi-language balise void PluginsManager::loadBalise(const QDomElement &root,const std::string &name,std::vector > *informations,std::string *errorString,bool needHaveOneEntryMinimum,bool multiLanguage,bool englishNeedBeFound) { int foundElement=0; bool englishTextIsFoundForThisChild=false; QDomElement child = root.firstChildElement(QString::fromStdString(name)); while(!child.isNull()) { if(child.isElement()) { std::vector newInformations; if(multiLanguage) { if(child.hasAttribute(QStringLiteral("xml:lang"))) { if(child.attribute(QStringLiteral("xml:lang"))==QStringLiteral("en")) englishTextIsFoundForThisChild=true; foundElement++; newInformations.push_back(child.tagName().toStdString()); newInformations.push_back(child.attribute(QStringLiteral("xml:lang")).toStdString()); newInformations.push_back(child.text().toStdString()); } else ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"Have not the attribute xml:lang: child.tagName(): "+child.tagName().toStdString()+", child.text(): "+child.text().toStdString()); } else { foundElement++; newInformations.push_back(child.tagName().toStdString()); newInformations.push_back(child.text().toStdString()); } informations->push_back(newInformations); } else ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"Is not Element: child.tagName(): "+child.tagName().toStdString()); child = child.nextSiblingElement(QString::fromStdString(name)); } if(multiLanguage && englishTextIsFoundForThisChild==false && englishNeedBeFound) { informations->clear(); *errorString=tr("English text missing in the informations.xml for the tag: %1").arg(QString::fromStdString(name)).toStdString(); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"English text missing into the informations.xml for the tag: "+name); return; } if(needHaveOneEntryMinimum && foundElement==0) { informations->clear(); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"Tag not found: "+name); *errorString=tr("Tag not found: %1").arg(QString::fromStdString(name)).toStdString(); } } /// \brief to load the get dom specific std::string PluginsManager::getDomSpecific(const QDomElement &root,const std::string &name,const std::vector > &listChildAttribute) const { QDomElement child = root.firstChildElement(QString::fromStdString(name)); bool allIsFound; while(!child.isNull()) { if(child.isElement()) { allIsFound=true; size_t index=0; while(index &entry=listChildAttribute.at(index); if(child.attribute(QString::fromStdString(entry.first))!=QString::fromStdString(entry.second)) { allIsFound=false; break; } index++; } if(allIsFound) return child.text().toStdString(); } else ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"Is not Element: child.tagName(): "+child.tagName().toStdString()); child = child.nextSiblingElement(QString::fromStdString(name)); } return std::string(); } /// \brief to load the get dom specific std::string PluginsManager::getDomSpecific(const QDomElement &root,const std::string &name) const { QDomElement child = root.firstChildElement(QString::fromStdString(name)); while(!child.isNull()) { if(child.isElement()) return child.text().toStdString(); else ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"Is not Element: child.tagName(): "+child.tagName().toStdString()); child = child.nextSiblingElement(QString::fromStdString(name)); } return std::string(); } #ifndef ULTRACOPIER_PLUGIN_ALL_IN_ONE /// \brief check the dependencies uint32_t PluginsManager::checkDependencies() { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"start"); uint32_t errors=0; unsigned int index=0; bool depCheck; while(index versionANumber=stringsplit(versionA,'.'); std::vector versionBNumber=stringsplit(versionB,'.'); unsigned int index=0; unsigned int defaultReturnValue=true; if(sign=="<") defaultReturnValue=false; if(sign==">") defaultReturnValue=false; bool ok; while(indexreaNumberB) return false; if(reaNumberA") { if(reaNumberAreaNumberB) return true; } if(sign=="<=") { if(reaNumberA>reaNumberB) return false; if(reaNumberA=") { if(reaNumberAreaNumberB) return true; } index++; } return defaultReturnValue; } std::vector PluginsManager::getPluginsByCategory(const PluginType &category) const { if(pluginsListIndexed.find(category)==pluginsListIndexed.cend()) return std::vector(); return pluginsListIndexed.at(category); } std::vector PluginsManager::getPlugins(bool withError) const { std::vector list; unsigned int index=0; while(indexsetLanguage(mainShortName); pluginInformation->setPlugin(pluginsList.at(index)); pluginInformation->show(); return; } index++; } ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"item not selected"); } void PluginsManager::showInformationDoubleClick() { // showInformation(false); } #ifdef ULTRACOPIER_PLUGIN_IMPORT_SUPPORT void PluginsManager::removeThePluginSelected(const std::string &path) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"start"); unsigned int index=0; while(index cppdata; cppdata.resize(data.size()); memcpy(cppdata.data(),data.data(),data.size()); if(!tarFile.decodeData(cppdata)) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"tarFile.errorString(): "+tarFile.errorString()); QMessageBox::critical(NULL,tr("Plugin loader"),tr("Unable to load the plugin content, please check it: %1").arg(QString::fromStdString(tarFile.errorString()))); } else { std::vector fileList = tarFile.getFileList(); std::vector > dataList = tarFile.getDataList(); if(fileList.size()>1) { std::string basePluginArchive=""; /* block use less for tar? if(fileList.at(0).contains(QRegularExpression("[\\/]"))) { bool folderFoundEveryWhere=true; basePluginArchive=fileList.at(0); basePluginArchive=std::regex_replace(basePluginArchive, std::regex("[\\/].*$"), ""); for (int i = 0; i < list.size(); ++i) { if(!stringStartWith(fileList.at(i),basePluginArchive)) { folderFoundEveryWhere=false; break; } } if(folderFoundEveryWhere) { for (int i = 0; i < fileList.size(); ++i) fileList[i].substr(basePluginArchive.size()); } else basePluginArchive=""; }*/ PluginsAvailable tempPlugin; std::string categoryFinal=""; for (unsigned int i = 0; i < fileList.size(); ++i) if(fileList.at(i)=="informations.xml") { loadPluginXml(&tempPlugin,QByteArray(dataList.at(i).data(),static_cast(dataList.at(i).size()))); break; } if(tempPlugin.errorString=="") { categoryFinal=categoryToString(tempPlugin.category); if(categoryFinal!="") { std::string writablePath=ResourcesManager::resourcesManager->getWritablePath(); if(writablePath!="") { QDir dir; std::string finalPluginPath=writablePath+categoryFinal+FacilityEngine::separator()+tempPlugin.name+FacilityEngine::separator(); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"writablePath: \""+writablePath+"\""); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"basePluginArchive: \""+basePluginArchive+"\""); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"categoryFinal: \""+categoryFinal+"\""); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Information,"finalPluginPath: \""+finalPluginPath+"\""); if(!dir.exists(QString::fromStdString(finalPluginPath))) { bool errorFound=false; for (unsigned int i = 0; i < fileList.size(); ++i) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Information,"file "+std::to_string(i)+": "+finalPluginPath+fileList.at(i)); std::string fileListEntry=fileList[i]; fileListEntry=std::regex_replace(fileListEntry, std::regex("^(..?[\\/])+"), ""); QFile currentFile(QString::fromStdString(finalPluginPath+fileListEntry)); QFileInfo info(currentFile); if(!dir.exists(info.absolutePath())) if(!dir.mkpath(info.absolutePath())) { ResourcesManager::resourcesManager->disableWritablePath(); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Critical,"Unable to make the path: "+info.absolutePath().toStdString()); QMessageBox::critical(NULL,tr("Plugin loader"),tr("Unable to create a folder to install the plugin:\n%1").arg(info.absolutePath())); errorFound=true; break; } if(currentFile.open(QIODevice::ReadWrite)) { currentFile.write(QByteArray(dataList.at(i).data(),static_cast(dataList.at(i).size()))); currentFile.close(); } else { ResourcesManager::resourcesManager->disableWritablePath(); ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Critical,"Unable to make the file: "+info.absolutePath().toStdString()+", error:"+currentFile.errorString().toStdString()); QMessageBox::critical(NULL,tr("Plugin loader"),tr("Unable to create a file to install the plugin:\n%1\nError:%2").arg(info.absolutePath()).arg(currentFile.errorString())); errorFound=true; break; } } if(!errorFound) { if(loadPluginInformation(finalPluginPath)) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Information,"push the new plugin into the real list"); while(checkDependencies()!=0){}; emit needLangToRefreshPluginList(); emit manuallyAdded(tempPlugin); } } } else { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Critical,"Folder with same name is present, skip the plugin installation: "+finalPluginPath); QMessageBox::critical(NULL,tr("Plugin loader"),tr("Folder with same name is present, skip the plugin installation:\n%1").arg(QString::fromStdString(finalPluginPath))); } } else { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Critical,"Have not writable path, then how add you plugin?"); QMessageBox::critical(NULL,tr("Plugin loader"),tr("Unable to load the plugin content, please check it")); } } else { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"category into informations.xml not found!"); QMessageBox::critical(NULL,tr("Plugin loader"),tr("Unable to load the plugin content, please check it")); } } else { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"Error in the xml: "+tempPlugin.errorString); QMessageBox::critical(NULL,tr("Plugin loader"),tr("Unable to load the plugin content, please check it: %1").arg(QString::fromStdString(tempPlugin.errorString))); } } else { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"No file found into the plugin"); QMessageBox::critical(NULL,tr("Plugin loader"),tr("Unable to load the plugin content, please check it")); } } } else { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Warning,"decodeThread.errorFound(), error: "+decodeThread.errorString().toStdString()); QMessageBox::critical(NULL,tr("Plugin loader"),tr("Unable to load the plugin content, please check it: %1").arg(decodeThread.errorString())); } importingPlugin=false; } #endif #ifndef ULTRACOPIER_PLUGIN_ALL_IN_ONE void PluginsManager::newAuthPath(const std::string &path) { ULTRACOPIER_DEBUGCONSOLE(Ultracopier::DebugLevel_Notice,"start"); unsigned int index=0; while(index