254 lines
9.0 KiB
C++
254 lines
9.0 KiB
C++
#include "stdafx.h"
|
|
#include "ScriptManager.h"
|
|
#include "../MiscHelpers/Common/Common.h"
|
|
#include "../MiscHelpers/Common/OtherFunctions.h"
|
|
#include "../SandMan.h"
|
|
#include "../OnlineUpdater.h"
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include "SysObject.h"
|
|
|
|
#include "../MiscHelpers/Common/OtherFunctions.h"
|
|
|
|
#include "../MiscHelpers/Archive/Archive.h"
|
|
#include "../MiscHelpers/Archive/ArchiveFS.h"
|
|
|
|
#include "../Wizards/BoxAssistant.h"
|
|
|
|
QList<StrPair> ReadCommentHeader(const QString& Header)
|
|
{
|
|
QList<StrPair> HeaderFields;
|
|
QStringList HeaderLines = Header.split("\n");
|
|
foreach(QString Line, HeaderLines)
|
|
{
|
|
Line = Line.trimmed();
|
|
if(Line.left(1) == "*")
|
|
{
|
|
Line.remove(0,1);
|
|
Line = Line.trimmed();
|
|
}
|
|
|
|
StrPair Field = Split2(Line, ":");
|
|
if(Field.first.isEmpty() || Field.first.contains(" "))
|
|
continue;
|
|
|
|
HeaderFields.append(Field);
|
|
}
|
|
return HeaderFields;
|
|
}
|
|
|
|
CScriptManager::CScriptManager(QObject* parent)
|
|
:QObject(parent)
|
|
{
|
|
}
|
|
|
|
QString CScriptManager::GetScript(const QString& Name)
|
|
{
|
|
C7zFileEngineHandler IssueFS("issue");
|
|
QString Root = GetIssueDir(IssueFS);
|
|
|
|
foreach(const QString &Path, ListDir(Root, QStringList() << "*.js")) {
|
|
if (Path.right(Name.length() + 3) == Name + ".js") {
|
|
return ReadFileAsString(Root + "/" + Path);
|
|
}
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
void CScriptManager::LoadIssues()
|
|
{
|
|
//
|
|
// Load Issues, when the user has an own troubleshooting folder, don't load the 7z or online
|
|
//
|
|
|
|
C7zFileEngineHandler IssueFS("issue");
|
|
LoadIssues(GetIssueDir(IssueFS, &m_IssueDate));
|
|
if (m_IssueDate.isValid()) {
|
|
if (theConf->GetInt("Options/CheckForIssues", 2) == 1) {
|
|
|
|
QVariantMap Data = theGUI->m_pUpdater->GetUpdateData();
|
|
if (!Data.isEmpty() && Data.contains("issues") && theGUI->m_pUpdater->GetLastUpdateTime() > QDateTime::currentDateTime().addDays(-1))
|
|
OnUpdateData(Data, QVariantMap());
|
|
else
|
|
theGUI->m_pUpdater->GetUpdates(this, SLOT(OnUpdateData(const QVariantMap&, const QVariantMap&)));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CScriptManager::LoadIssues(const QString& IssueDir)
|
|
{
|
|
QVariantMap Issues = QJsonDocument::fromJson(ReadFileAsString(IssueDir + "layout.json").toUtf8()).toVariant().toMap();
|
|
|
|
QVariantList Entries = Issues.value("entries").toList();
|
|
|
|
if (Entries.isEmpty()) {
|
|
QMessageBox::critical(theGUI, "Sandboxie-Plus", tr("Fatal error, failed to load troubleshooting instructions!"));
|
|
return;
|
|
}
|
|
|
|
m_GroupedIssues.clear();
|
|
|
|
quint32 OsBuild = JSysObject::GetOSVersion()["build"].toUInt();
|
|
|
|
//QDir Dir(IssueDir);
|
|
//foreach(const QFileInfo & Info, Dir.entryInfoList(QStringList() << "*.js", QDir::Files)) {
|
|
auto List = ListDir(IssueDir, QStringList() << "*.js");
|
|
foreach(const QString & FileName, List) {
|
|
QFileInfo Info(IssueDir + FileName);
|
|
QString Script = ReadFileAsString(Info.filePath());
|
|
|
|
int HeaderBegin = Script.indexOf("/*");
|
|
int HeaderEnd = Script.indexOf("*/");
|
|
if(HeaderBegin == -1 || HeaderEnd == -1)
|
|
continue; // Header is mandatory
|
|
if(HeaderBegin != 0) {
|
|
qDebug() << "Bad Header of" << Info.fileName();
|
|
continue;
|
|
}
|
|
|
|
QVariantMap Issue;
|
|
Issue["id"] = Info.fileName().left(Info.fileName().length() - 3);
|
|
Issue["type"] = "issue";
|
|
foreach(const StrPair& KeyValue, ReadCommentHeader(Script.mid(HeaderBegin + 2, HeaderEnd - (HeaderBegin + 2))))
|
|
Issue[KeyValue.first] = KeyValue.second;
|
|
|
|
if (Issue["group"] == "system" || Issue["group"] == "library")
|
|
continue;
|
|
|
|
Issue["script"] = Script;
|
|
|
|
bool NotApplicable = false;
|
|
if (Issue.contains("versions")) {
|
|
NotApplicable = true;
|
|
foreach(const QString & V, SplitStr(Issue["versions"].toString(), ",")) {
|
|
StrPair VV = Split2(V, "-");
|
|
if ((VV.second.isEmpty() && COnlineUpdater::VersionToInt(VV.first) == COnlineUpdater::CurrentVersion()) || // exact version match
|
|
(!VV.second.isEmpty() && (COnlineUpdater::VersionToInt(VV.first) <= COnlineUpdater::CurrentVersion() && COnlineUpdater::VersionToInt(VV.second) >= COnlineUpdater::CurrentVersion()))) { // inside version range
|
|
NotApplicable = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!NotApplicable && Issue.contains("os_builds")) {
|
|
NotApplicable = true;
|
|
foreach(const QString & V, SplitStr(Issue["os_builds"].toString(), ",")) {
|
|
StrPair VV = Split2(V, "-");
|
|
if ((VV.second.isEmpty() && VV.first.toUInt() == OsBuild) || // exact version match
|
|
(!VV.second.isEmpty() && (VV.first.toUInt() <= OsBuild && VV.second.toUInt() >= OsBuild))) { // inside version range
|
|
NotApplicable = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (NotApplicable)
|
|
continue;
|
|
|
|
Entries.append(Issue);
|
|
}
|
|
|
|
foreach(const QVariant & vIssue, Entries) {
|
|
QVariantMap Issue = vIssue.toMap();
|
|
QList<QVariantMap>& Group = m_GroupedIssues[Issue["group"].toString()];
|
|
// Note: This way we can define order in the layout json and have the issue scripts loaded at the right place
|
|
QString ID = Issue["id"].toString();
|
|
auto I = std::find_if(Group.begin(), Group.end(), [ID](const QVariantMap& cur)->int { return cur["id"] == ID; });
|
|
if (I == Group.end())
|
|
Group.append(Issue);
|
|
else {
|
|
if (I->contains("script")) {
|
|
QMessageBox::warning(theGUI, "Sandboxie-Plus", tr("Error, troubleshooting instructions duplicated %1 (%2 <-> %3)!")
|
|
.arg(ID).arg(I->value("id").toString()).arg(Issue.value("id").toString()));
|
|
}
|
|
for(auto J = Issue.begin(); J != Issue.end(); ++J)
|
|
I->insert(J.key(), J.value());
|
|
}
|
|
}
|
|
|
|
//
|
|
// Load Translations
|
|
//
|
|
|
|
QString Translation = ReadFileAsString(IssueDir + "lang_" + theGUI->m_Language + ".json");
|
|
if (Translation.isEmpty()) {
|
|
QString LangAux = theGUI->m_Language; // Short version as fallback
|
|
LangAux.truncate(LangAux.lastIndexOf('_'));
|
|
Translation = ReadFileAsString(IssueDir + "lang_" + LangAux + ".json");
|
|
}
|
|
|
|
if (!Translation.isEmpty()) {
|
|
QJsonParseError error;
|
|
m_Translation = QJsonDocument::fromJson(Translation.toUtf8(), &error).toVariant().toMap();
|
|
if (m_Translation.isEmpty())
|
|
qDebug() << error.errorString() << Translation.mid(error.offset, 100);
|
|
}
|
|
}
|
|
|
|
QString CScriptManager::GetIssueDir(C7zFileEngineHandler& IssueFS, QDateTime* pDate)
|
|
{
|
|
QString IssueDir = theConf->GetConfigDir() + "/troubleshooting/";
|
|
if (!QFile::exists(IssueDir)) {
|
|
|
|
QFileInfo Installed(QApplication::applicationDirPath() + "/troubleshooting.7z");
|
|
QFileInfo Latest(theConf->GetConfigDir() + "/troubleshooting.7z");
|
|
quint64 latest = Latest.lastModified().toSecsSinceEpoch();
|
|
quint64 installed = Installed.lastModified().toSecsSinceEpoch();
|
|
if (latest >= installed && IssueFS.Open(theConf->GetConfigDir() + "/troubleshooting.7z")) {
|
|
IssueDir = IssueFS.Prefix() + "/";
|
|
if(pDate) *pDate = Latest.lastModified();
|
|
}
|
|
else if (IssueFS.Open(QApplication::applicationDirPath() + "/troubleshooting.7z")) {
|
|
IssueDir = IssueFS.Prefix() + "/";
|
|
if(pDate) *pDate = Installed.lastModified();
|
|
}
|
|
}
|
|
return IssueDir;
|
|
}
|
|
|
|
void CScriptManager::OnUpdateData(const QVariantMap& Data, const QVariantMap& Params)
|
|
{
|
|
if (Data.isEmpty() || Data["error"].toBool())
|
|
return;
|
|
|
|
QVariantMap Issues = Data["issues"].toMap();
|
|
|
|
quint64 Date = Issues["date"].toULongLong();
|
|
if (Date >= m_IssueDate.toSecsSinceEpoch()) {
|
|
|
|
QString Download = Issues["download"].toString();
|
|
|
|
QVariantMap Params;
|
|
Params["path"] = theConf->GetConfigDir() + "/troubleshooting.tmp";
|
|
Params["setDate"] = QDateTime::fromSecsSinceEpoch(Date);
|
|
Params["signature"] = Issues["signature"];
|
|
theGUI->m_pUpdater->DownloadFile(Download, this, SLOT(OnDownload(const QString&, const QVariantMap&)), Params);
|
|
}
|
|
}
|
|
|
|
extern "C" long VerifyFileSignatureImpl(const wchar_t* FilePath, void* Signature, unsigned long SignatureSize);
|
|
|
|
void CScriptManager::OnDownload(const QString& Path, const QVariantMap& Params)
|
|
{
|
|
QByteArray Signature = QByteArray::fromBase64(Params["signature"].toByteArray());
|
|
|
|
if (VerifyFileSignatureImpl(QString(Path).replace("/","\\").toStdWString().c_str(), Signature.data(), Signature.size()) < 0) { // !NT_SUCCESS
|
|
QFile::remove(Path);
|
|
return;
|
|
}
|
|
QString FinalPath = theConf->GetConfigDir() + "/troubleshooting.7z";
|
|
QFile::remove(FinalPath);
|
|
QFile::rename(Path, FinalPath);
|
|
|
|
|
|
QString IssueDir;
|
|
C7zFileEngineHandler IssueFS("issue");
|
|
if (!IssueFS.Open(FinalPath)) {
|
|
QMessageBox::critical(theGUI, "Sandboxie-Plus", tr("Downloaded troubleshooting instructions are corrupted!"));
|
|
QFile::remove(Path);
|
|
return;
|
|
}
|
|
|
|
LoadIssues(IssueFS.Prefix() + "/");
|
|
|
|
emit IssuesUpdated();
|
|
}
|