parent
4727200bc9
commit
1f2e1f32de
|
@ -4,19 +4,20 @@
|
|||
|
||||
using namespace porting;
|
||||
|
||||
void SystemManifest::applyManifest(SystemManifestImportOptions options, DatabaseConnection* systemDb) {
|
||||
bool shouldMigrateConfig = options.importConfig && !configPath.isEmpty();
|
||||
bool shouldMigrateDb = options.importDb == SystemManifestImportOptions::Merge && !dbPath.isEmpty();
|
||||
void SystemManifest::applyManifest(SystemManifestImportOptions options, DatabaseConnection* systemDb)
|
||||
{
|
||||
bool shouldMigrateConfig = options.importConfig && !configPath.isEmpty();
|
||||
bool shouldMigrateDb = options.importDb == SystemManifestImportOptions::Merge && !dbPath.isEmpty();
|
||||
|
||||
if (shouldMigrateConfig) {
|
||||
Q_EMIT onStatusUpdate(tr("Importing Settings"));
|
||||
migrateConfig();
|
||||
}
|
||||
if (shouldMigrateConfig) {
|
||||
Q_EMIT onStatusUpdate(tr("Importing Settings"));
|
||||
migrateConfig();
|
||||
}
|
||||
|
||||
if (shouldMigrateDb) {
|
||||
migrateDb(systemDb);
|
||||
}
|
||||
Q_EMIT onComplete();
|
||||
if (shouldMigrateDb) {
|
||||
migrateDb(systemDb);
|
||||
}
|
||||
Q_EMIT onComplete();
|
||||
}
|
||||
|
||||
void SystemManifest::migrateConfig()
|
||||
|
@ -53,7 +54,7 @@ void SystemManifest::migrateDb(DatabaseConnection* systemDb)
|
|||
, importRecord.operationSlug
|
||||
, contentSensitiveFilename(importRecord.contentType));
|
||||
|
||||
auto fullFileExportPath = m_fileTemplate.arg(pathToManifest, item.exportPath);
|
||||
auto fullFileExportPath = m_fileTemplate.arg(m_pathToManifest, item.exportPath);
|
||||
auto parentDir = FileHelpers::getDirname(newEvidencePath);
|
||||
if (!QDir().exists(parentDir))
|
||||
QDir().mkpath(parentDir);
|
||||
|
@ -76,17 +77,16 @@ void SystemManifest::migrateDb(DatabaseConnection* systemDb)
|
|||
|
||||
QString SystemManifest::pathToFile(const QString& filename)
|
||||
{
|
||||
return m_fileTemplate.arg(pathToManifest, filename);
|
||||
return m_fileTemplate.arg(m_pathToManifest, filename);
|
||||
}
|
||||
|
||||
QString SystemManifest::contentSensitiveExtension(const QString& contentType) {
|
||||
if (contentType == Codeblock::contentType()) {
|
||||
return Codeblock::extension();
|
||||
}
|
||||
else if(contentType == Screenshot::contentType()) {
|
||||
return Screenshot::extension();
|
||||
}
|
||||
return QStringLiteral(".bin");
|
||||
QString SystemManifest::contentSensitiveExtension(const QString& contentType)
|
||||
{
|
||||
if (contentType == Codeblock::contentType())
|
||||
return Codeblock::extension();
|
||||
if(contentType == Screenshot::contentType())
|
||||
return Screenshot::extension();
|
||||
return QStringLiteral(".bin");
|
||||
}
|
||||
|
||||
QString SystemManifest::contentSensitiveFilename(const QString& contentType)
|
||||
|
@ -98,94 +98,91 @@ QString SystemManifest::contentSensitiveFilename(const QString& contentType)
|
|||
return QStringLiteral("ashirt_unknown_type_%1.bin").arg(StringHelpers::randomString());
|
||||
}
|
||||
|
||||
SystemManifest* SystemManifest::readManifest(const QString& pathToExportFile) {
|
||||
auto content = FileHelpers::readFile(pathToExportFile);
|
||||
auto manifest = parseJSONItem<SystemManifest*>(content, &SystemManifest::deserialize);
|
||||
manifest->pathToManifest = FileHelpers::getDirname(pathToExportFile);
|
||||
|
||||
return manifest;
|
||||
SystemManifest* SystemManifest::readManifest(const QString& pathToExportFile)
|
||||
{
|
||||
auto content = FileHelpers::readFile(pathToExportFile);
|
||||
auto manifest = parseJSONItem<SystemManifest*>(content, &SystemManifest::deserialize);
|
||||
manifest->m_pathToManifest = FileHelpers::getDirname(pathToExportFile);
|
||||
return manifest;
|
||||
}
|
||||
|
||||
void SystemManifest::exportManifest(DatabaseConnection* db, const QString& outputDirPath,
|
||||
const SystemManifestExportOptions& options) {
|
||||
if (!options.includesAnything()) {
|
||||
return;
|
||||
}
|
||||
const SystemManifestExportOptions& options)
|
||||
{
|
||||
if (!options.includesAnything() || outputDirPath.isEmpty())
|
||||
return;
|
||||
|
||||
bool success = QDir().mkpath(outputDirPath);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
if (!QDir().mkpath(outputDirPath))
|
||||
return;
|
||||
|
||||
os = QSysInfo::kernelType(); // may need to check possible answers, or maybe just compare to new system value?
|
||||
os = QSysInfo::kernelType(); // may need to check possible answers, or maybe just compare to new system value?
|
||||
QString basePath = QDir(outputDirPath).path();
|
||||
|
||||
QString basePath = QDir(outputDirPath).path();
|
||||
if (options.exportConfig) {
|
||||
Q_EMIT onStatusUpdate(tr("Exporting settings"));
|
||||
configPath = QStringLiteral("config.json");
|
||||
AppConfig::getInstance().writeConfig(m_fileTemplate.arg(basePath, configPath));
|
||||
}
|
||||
|
||||
if (options.exportConfig) {
|
||||
Q_EMIT onStatusUpdate(tr("Exporting settings"));
|
||||
configPath = QStringLiteral("config.json");
|
||||
AppConfig::getInstance().writeConfig(m_fileTemplate.arg(basePath, configPath));
|
||||
}
|
||||
if (options.exportDb) {
|
||||
Q_EMIT onStatusUpdate(tr("Exporting Evidence"));
|
||||
dbPath = QStringLiteral("db.sqlite");
|
||||
evidenceManifestPath = QStringLiteral("evidence.json");
|
||||
auto allEvidence = DatabaseConnection::createEvidenceExportView(m_fileTemplate.arg(basePath, dbPath), EvidenceFilters(), db);
|
||||
Q_EMIT onReady(allEvidence.size());
|
||||
porting::EvidenceManifest evidenceManifest = copyEvidence(basePath, allEvidence);
|
||||
// write evidence manifest
|
||||
FileHelpers::writeFile(m_fileTemplate.arg(basePath, evidenceManifestPath),
|
||||
QJsonDocument(EvidenceManifest::serialize(evidenceManifest)).toJson());
|
||||
}
|
||||
|
||||
if (options.exportDb) {
|
||||
Q_EMIT onStatusUpdate(tr("Exporting Evidence"));
|
||||
dbPath = QStringLiteral("db.sqlite");
|
||||
evidenceManifestPath = QStringLiteral("evidence.json");
|
||||
|
||||
auto allEvidence = DatabaseConnection::createEvidenceExportView(m_fileTemplate.arg(basePath, dbPath), EvidenceFilters(), db);
|
||||
Q_EMIT onReady(allEvidence.size());
|
||||
porting::EvidenceManifest evidenceManifest = copyEvidence(basePath, allEvidence);
|
||||
|
||||
// write evidence manifest
|
||||
FileHelpers::writeFile(m_fileTemplate.arg(basePath, evidenceManifestPath),
|
||||
QJsonDocument(EvidenceManifest::serialize(evidenceManifest)).toJson());
|
||||
}
|
||||
|
||||
QString exportPath = QStringLiteral("%1/system.json").arg(basePath);
|
||||
this->pathToManifest = exportPath;
|
||||
FileHelpers::writeFile(exportPath, QJsonDocument(serialize(*this)).toJson());
|
||||
Q_EMIT onComplete();
|
||||
m_pathToManifest = QStringLiteral("%1/system.json").arg(basePath);
|
||||
FileHelpers::writeFile(m_pathToManifest, QJsonDocument(serialize(*this)).toJson());
|
||||
Q_EMIT onComplete();
|
||||
}
|
||||
|
||||
porting::EvidenceManifest SystemManifest::copyEvidence(const QString& baseExportPath,
|
||||
QList<model::Evidence> allEvidence) {
|
||||
QString relativeEvidenceDir = QStringLiteral("evidence");
|
||||
QDir().mkpath(m_fileTemplate.arg(baseExportPath, relativeEvidenceDir));
|
||||
QList<model::Evidence> allEvidence)
|
||||
{
|
||||
QString relativeEvidenceDir = QStringLiteral("evidence");
|
||||
QDir().mkpath(m_fileTemplate.arg(baseExportPath, relativeEvidenceDir));
|
||||
|
||||
porting::EvidenceManifest evidenceManifest;
|
||||
for (size_t evidenceIndex = 0; evidenceIndex < allEvidence.size(); evidenceIndex++) {
|
||||
auto evi = allEvidence.at(evidenceIndex);
|
||||
auto newName = QStringLiteral("ashirt_evidence_%1.%2")
|
||||
.arg(StringHelpers::randomString(10), contentSensitiveExtension(evi.contentType));
|
||||
auto item = porting::EvidenceItem(evi.id, relativeEvidenceDir + "/" + newName);
|
||||
auto dstPath = m_fileTemplate.arg(baseExportPath, item.exportPath);
|
||||
QFile srcFile(evi.path);
|
||||
srcFile.copy(dstPath);
|
||||
if (!srcFile.error() != QFileDevice::NoError)
|
||||
Q_EMIT onCopyFileError(evi.path, dstPath, srcFile.errorString());
|
||||
else
|
||||
evidenceManifest.entries.append(item);
|
||||
Q_EMIT onFileProcessed(evidenceIndex + 1);
|
||||
}
|
||||
return evidenceManifest;
|
||||
porting::EvidenceManifest evidenceManifest;
|
||||
for (size_t evidenceIndex = 0; evidenceIndex < allEvidence.size(); evidenceIndex++) {
|
||||
auto evi = allEvidence.at(evidenceIndex);
|
||||
auto newName = QStringLiteral("ashirt_evidence_%1.%2")
|
||||
.arg(StringHelpers::randomString(10), contentSensitiveExtension(evi.contentType));
|
||||
auto item = porting::EvidenceItem(evi.id, m_fileTemplate.arg(relativeEvidenceDir, newName));
|
||||
auto dstPath = m_fileTemplate.arg(baseExportPath, item.exportPath);
|
||||
QFile srcFile(evi.path);
|
||||
srcFile.copy(dstPath);
|
||||
if (!srcFile.error() != QFileDevice::NoError)
|
||||
Q_EMIT onCopyFileError(evi.path, dstPath, srcFile.errorString());
|
||||
else
|
||||
evidenceManifest.entries.append(item);
|
||||
Q_EMIT onFileProcessed(evidenceIndex + 1);
|
||||
}
|
||||
return evidenceManifest;
|
||||
}
|
||||
|
||||
QJsonObject SystemManifest::serialize(const SystemManifest& src) {
|
||||
QJsonObject o;
|
||||
o.insert(QStringLiteral("operatingSystem"), src.os);
|
||||
o.insert(QStringLiteral("databasePath"), src.dbPath);
|
||||
o.insert(QStringLiteral("configPath"), src.configPath);
|
||||
o.insert(QStringLiteral("serversPath"), src.serversPath);
|
||||
o.insert(QStringLiteral("evidenceManifestPath"), src.evidenceManifestPath);
|
||||
return o;
|
||||
QJsonObject SystemManifest::serialize(const SystemManifest& src)
|
||||
{
|
||||
QJsonObject o;
|
||||
o.insert(QStringLiteral("operatingSystem"), src.os);
|
||||
o.insert(QStringLiteral("databasePath"), src.dbPath);
|
||||
o.insert(QStringLiteral("configPath"), src.configPath);
|
||||
o.insert(QStringLiteral("serversPath"), src.serversPath);
|
||||
o.insert(QStringLiteral("evidenceManifestPath"), src.evidenceManifestPath);
|
||||
return o;
|
||||
}
|
||||
|
||||
SystemManifest* SystemManifest::deserialize(const QJsonObject& o) {
|
||||
auto manifest = new SystemManifest;
|
||||
manifest->os = o.value(QStringLiteral("operatingSystem")).toString();
|
||||
manifest->dbPath = o.value(QStringLiteral("databasePath")).toString();
|
||||
manifest->configPath = o.value(QStringLiteral("configPath")).toString();
|
||||
manifest->serversPath = o.value(QStringLiteral("serversPath")).toString();
|
||||
manifest->evidenceManifestPath = o.value(QStringLiteral("evidenceManifestPath")).toString();
|
||||
return manifest;
|
||||
SystemManifest* SystemManifest::deserialize(const QJsonObject& o)
|
||||
{
|
||||
auto manifest = new SystemManifest;
|
||||
manifest->os = o.value(QStringLiteral("operatingSystem")).toString();
|
||||
manifest->dbPath = o.value(QStringLiteral("databasePath")).toString();
|
||||
manifest->configPath = o.value(QStringLiteral("configPath")).toString();
|
||||
manifest->serversPath = o.value(QStringLiteral("serversPath")).toString();
|
||||
manifest->evidenceManifestPath = o.value(QStringLiteral("evidenceManifestPath")).toString();
|
||||
return manifest;
|
||||
}
|
||||
|
|
|
@ -14,124 +14,114 @@
|
|||
#include "system_porting_options.h"
|
||||
|
||||
namespace porting {
|
||||
class SystemManifest : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
class SystemManifest : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SystemManifest() {}
|
||||
~SystemManifest(){}
|
||||
|
||||
public:
|
||||
SystemManifest() {}
|
||||
~SystemManifest(){}
|
||||
/**
|
||||
* @brief readManifest parses the the system.json (as provided by the caller) into a complete SystemManifest
|
||||
* @param pathToExportFile the location of the system.json file
|
||||
* @return the completed SystemManifest
|
||||
*/
|
||||
static SystemManifest* readManifest(const QString& pathToExportFile);
|
||||
|
||||
/**
|
||||
* @brief readManifest parses the the system.json (as provided by the caller) into a complete SystemManifest
|
||||
* @param pathToExportFile the location of the system.json file
|
||||
* @return the completed SystemManifest
|
||||
* @throws FileError if there is an issue reading the file at the indicated path
|
||||
*/
|
||||
static SystemManifest* readManifest(const QString& pathToExportFile);
|
||||
/// serialize converts the internal state of the system manifest into a json object
|
||||
static QJsonObject serialize(const SystemManifest& src);
|
||||
/// deserialize converts a json object into a system manifest instance
|
||||
static SystemManifest* deserialize(const QJsonObject& o);
|
||||
|
||||
public:
|
||||
signals:
|
||||
/// onReady fires when the breadth of the import/export is known to let the caller know that real work is starting
|
||||
void onReady(quint64 numFilesToProcess);
|
||||
/// onFileProcessed fires when an evidence file is copied during import or export
|
||||
void onFileProcessed(quint64 runningCount);
|
||||
/// onComplete fires when the entire import/export is finished
|
||||
void onComplete();
|
||||
/// onCopyFileError fires when a file cannot be copied during import or export
|
||||
void onCopyFileError(QString srcPath, QString dstPath, const QString& errStr);
|
||||
/// onStatusUpdate fires when the system moves between import/export phases
|
||||
void onStatusUpdate(QString text);
|
||||
/**
|
||||
* @brief applyManifest takes the given manifest object (and options), and begins merging that data with the running system
|
||||
* @param options switches to control what gets imported
|
||||
* @param systemDb The currently running/system database
|
||||
* @throws QSqlError If there is an issue ingesting database records from the exported database
|
||||
*/
|
||||
void applyManifest(SystemManifestImportOptions options, DatabaseConnection* systemDb);
|
||||
|
||||
public:
|
||||
/// serialize converts the internal state of the system manifest into a json object
|
||||
static QJsonObject serialize(const SystemManifest& src);
|
||||
/**
|
||||
* @brief exportManifest starts the long process of copying config and evidence into the specified directory.
|
||||
* @param db the connection to the primary database
|
||||
* @param outputDirPath the path to the expected export directory. Files will be placed under this directory
|
||||
* (not wrapped in another directory)
|
||||
* @param options exporting options (e.g. do you want to copy both evidence *and* config
|
||||
* @throws QSqlError if there is an issue accessing the system database, or the copied database
|
||||
*/
|
||||
void exportManifest(DatabaseConnection* db, const QString& outputDirPath,
|
||||
const SystemManifestExportOptions& options);
|
||||
|
||||
/// deserialize converts a json object into a system manifest instance
|
||||
static SystemManifest* deserialize(const QJsonObject& o);
|
||||
/// os is the operating system associated with the originating export
|
||||
QString os;
|
||||
/// dbPath is the (relative) path to the database file from the originating export
|
||||
QString dbPath;
|
||||
/// configPath is the (relative) path to the config file from the originating export
|
||||
QString configPath;
|
||||
/// serversPath is currently unused
|
||||
QString serversPath;
|
||||
/// evidenceManifestPath is the (relative) path to the evidence manifest file from the originating export
|
||||
QString evidenceManifestPath;
|
||||
|
||||
private:
|
||||
/// contentSensitiveFilename returns a (random) filename for the given content type. This, in
|
||||
/// turn, relies on the underlying type to provide a sensible value. If no match is found, then
|
||||
/// "ashirt_unknown_type_XXXXXX.bin" (X's will be replaced with random characters) is returned
|
||||
/// instead.
|
||||
/// @see FileHelpers::randomFilename
|
||||
static QString contentSensitiveFilename(const QString& contentType);
|
||||
signals:
|
||||
/// onReady fires when the breadth of the import/export is known to let the caller know that real work is starting
|
||||
void onReady(quint64 numFilesToProcess);
|
||||
/// onFileProcessed fires when an evidence file is copied during import or export
|
||||
void onFileProcessed(quint64 runningCount);
|
||||
/// onComplete fires when the entire import/export is finished
|
||||
void onComplete();
|
||||
/// onCopyFileError fires when a file cannot be copied during import or export
|
||||
void onCopyFileError(QString srcPath, QString dstPath, const QString& errStr);
|
||||
/// onStatusUpdate fires when the system moves between import/export phases
|
||||
void onStatusUpdate(QString text);
|
||||
|
||||
/// contentSensitiveExtension returns a file extension for the given content type. This, in turn,
|
||||
/// relies on the underlying type to provide a sensible value. If no match is found, then ".bin"
|
||||
/// is returned instead
|
||||
static QString contentSensitiveExtension(const QString& contentType);
|
||||
private:
|
||||
/// contentSensitiveFilename returns a (random) filename for the given content type. This, in
|
||||
/// turn, relies on the underlying type to provide a sensible value. If no match is found, then
|
||||
/// "ashirt_unknown_type_XXXXXX.bin" (X's will be replaced with random characters) is returned
|
||||
/// instead.
|
||||
/// @see FileHelpers::randomFilename
|
||||
static QString contentSensitiveFilename(const QString& contentType);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief applyManifest takes the given manifest object (and options), and begins merging that data with the running system
|
||||
* @param options switches to control what gets imported
|
||||
* @param systemDb The currently running/system database
|
||||
* @throws FileError is there is an issue reading the incoming files
|
||||
* @throws QSqlError If there is an issue ingesting database records from the exported database
|
||||
*/
|
||||
void applyManifest(SystemManifestImportOptions options, DatabaseConnection* systemDb);
|
||||
/// contentSensitiveExtension returns a file extension for the given content type. This, in turn,
|
||||
/// relies on the underlying type to provide a sensible value. If no match is found, then ".bin"
|
||||
/// is returned instead
|
||||
static QString contentSensitiveExtension(const QString& contentType);
|
||||
|
||||
/**
|
||||
* @brief exportManifest starts the long process of copying config and evidence into the specified directory.
|
||||
* @param db the connection to the primary database
|
||||
* @param outputDirPath the path to the expected export directory. Files will be placed under this directory
|
||||
* (not wrapped in another directory)
|
||||
* @param options exporting options (e.g. do you want to copy both evidence *and* config
|
||||
* @throws FileError if there is a problem with the given directory, or with a generated filename
|
||||
* @throws QSqlError if there is an issue accessing the system database, or the copied database
|
||||
*/
|
||||
void exportManifest(DatabaseConnection* db, const QString& outputDirPath,
|
||||
const SystemManifestExportOptions& options);
|
||||
/**
|
||||
* @brief migrateConfig imports the config file associated with the started import
|
||||
* @throws FileError if the config file cannot be copied
|
||||
*/
|
||||
void migrateConfig();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief migrateConfig imports the config file associated with the started import
|
||||
* @throws FileError if the config file cannot be copied
|
||||
*/
|
||||
void migrateConfig();
|
||||
/**
|
||||
* @brief migrateDb imports all of the database and evidence files associated with the started import
|
||||
* emits onStatusUpdate signal for periodic progress updates
|
||||
* emits onCopyFileError signal if there is an issue copying evidence files
|
||||
* emits onFileProcessed for each file processed
|
||||
* @param systemDb a pointer to the "standard" system database/running database
|
||||
* @throws QSqlError
|
||||
*/
|
||||
void migrateDb(DatabaseConnection* systemDb);
|
||||
/**
|
||||
* @brief migrateDb imports all of the database and evidence files associated with the started import
|
||||
* emits onStatusUpdate signal for periodic progress updates
|
||||
* emits onCopyFileError signal if there is an issue copying evidence files
|
||||
* emits onFileProcessed for each file processed
|
||||
* @param systemDb a pointer to the "standard" system database/running database
|
||||
* @throws QSqlError
|
||||
*/
|
||||
void migrateDb(DatabaseConnection* systemDb);
|
||||
|
||||
/// pathToFile is a small helper method to combine the absolute path to the manifest with the relative
|
||||
/// path to the given filename. The result is an absolute path to the given file
|
||||
QString pathToFile(const QString& filename);
|
||||
/// pathToFile is a small helper method to combine the absolute path to the manifest with the relative
|
||||
/// path to the given filename. The result is an absolute path to the given file
|
||||
QString pathToFile(const QString& filename);
|
||||
|
||||
/**
|
||||
* @brief copyEvidence will iteratively copy all evidence files provided to the indicated path. Files are renamed
|
||||
* to avoid any name collisions. Files are namespaced into givenPath/evidence
|
||||
* This emits a onCopyFileError signal if there is an issue copying files
|
||||
* This emits a onFileProcessed signal when the attempted copy completes (so you may get an error _and_ processed signal on the same file. Error will be first)
|
||||
* @param baseExportPath The path to the desired export directory
|
||||
* @param allEvidence a vector of evidence _data_ to export (files will be found and read from within this function)
|
||||
* @return an EvidenceManifest listing all of the files copied, and their new names.
|
||||
*/
|
||||
porting::EvidenceManifest copyEvidence(const QString& baseExportPath,
|
||||
QList<model::Evidence> allEvidence);
|
||||
/**
|
||||
* @brief copyEvidence will iteratively copy all evidence files provided to the indicated path. Files are renamed
|
||||
* to avoid any name collisions. Files are namespaced into givenPath/evidence
|
||||
* This emits a onCopyFileError signal if there is an issue copying files
|
||||
* This emits a onFileProcessed signal when the attempted copy completes (so you may get an error _and_ processed signal on the same file. Error will be first)
|
||||
* @param baseExportPath The path to the desired export directory
|
||||
* @param allEvidence a vector of evidence _data_ to export (files will be found and read from within this function)
|
||||
* @return an EvidenceManifest listing all of the files copied, and their new names.
|
||||
*/
|
||||
porting::EvidenceManifest copyEvidence(const QString& baseExportPath,
|
||||
QList<model::Evidence> allEvidence);
|
||||
|
||||
public:
|
||||
/// os is the operating system associated with the originating export
|
||||
QString os;
|
||||
/// dbPath is the (relative) path to the database file from the originating export
|
||||
QString dbPath;
|
||||
/// configPath is the (relative) path to the config file from the originating export
|
||||
QString configPath;
|
||||
/// serversPath is currently unused
|
||||
QString serversPath;
|
||||
/// evidenceManifestPath is the (relative) path to the evidence manifest file from the originating export
|
||||
QString evidenceManifestPath;
|
||||
|
||||
private:
|
||||
/// pathToManifest is the (absolute) path to the system manifest file from the originating export
|
||||
QString pathToManifest;
|
||||
inline static const QString m_fileTemplate = QStringLiteral("%1/%2");
|
||||
};
|
||||
/// pathToManifest is the (absolute) path to the system manifest file from the originating export
|
||||
QString m_pathToManifest;
|
||||
inline static const QString m_fileTemplate = QStringLiteral("%1/%2");
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue