Database round2 (#193)

* Dont throw as many errors

* hold a QSqlDatabase in the connection

* only Make Dir if it doesn't exist

* more database cleanup

* Replace remaining FileError use with other methods

* Try not do

* allow database to report errors

Co-authored-by: Chris Rizzitello <crizzitello@ics.com>
main
crizzitello 2022-07-18 18:16:42 -04:00 committed by GitHub
parent 45ee47b145
commit 5928942399
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 465 additions and 618 deletions

View File

@ -3,7 +3,6 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_subdirectory(components)
add_subdirectory(db)
add_subdirectory(dtos)
add_subdirectory(exceptions)
add_subdirectory(forms)
add_subdirectory(helpers)
add_subdirectory(models)
@ -54,7 +53,6 @@ target_link_libraries ( ashirt
ASHIRT::COMPONENTS
ASHIRT::HELPERS
ASHIRT::DTOS
ASHIRT::EXCEPTIONS
ASHIRT::FORMS
ASHIRT::PORTING
)

View File

@ -44,20 +44,14 @@ void CodeBlockView::buildUi() {
gridLayout->addWidget(codeEditor, 1, 0, 1, gridLayout->columnCount());
}
void CodeBlockView::loadFromFile(QString filepath) {
try {
void CodeBlockView::loadFromFile(QString filepath)
{
codeEditor->setPlainText(tr("No Codeblock Loaded"));
loadedCodeblock = Codeblock::readCodeblock(filepath);
codeEditor->setPlainText(loadedCodeblock.content);
sourceTextBox->setText(loadedCodeblock.source);
UIHelpers::setComboBoxValue(languageComboBox, loadedCodeblock.subtype);
}
catch (std::exception& e) {
QString msg = tr("Unable to load codeblock. Error: %1").arg(e.what());
codeEditor->setPlainText(msg);
setReadonly(true);
}
}
bool CodeBlockView::saveEvidence() {
loadedCodeblock.source = sourceTextBox->text();

View File

@ -95,35 +95,31 @@ void EvidenceEditor::setEnabled(bool enable) {
}
}
void EvidenceEditor::loadData() {
void EvidenceEditor::loadData()
{
// get local db evidence data
clearEditor();
try {
originalEvidenceData = db->getEvidenceDetails(evidenceID);
if(originalEvidenceData.id == -1) {
loadedPreview = new ErrorView(tr("Unable to load evidence: %1").arg(db->errorString()), this);
return;
}
descriptionTextBox->setText(originalEvidenceData.description);
operationSlug = originalEvidenceData.operationSlug;
if (originalEvidenceData.contentType == QStringLiteral("image")) {
loadedPreview = new ImageView(this);
loadedPreview->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}
else if (originalEvidenceData.contentType == QStringLiteral("codeblock")) {
} else if (originalEvidenceData.contentType == QStringLiteral("codeblock")) {
loadedPreview = new CodeBlockView(this);
loadedPreview->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}
else {
loadedPreview =
new ErrorView(tr("Unsupported evidence type: %1").arg(originalEvidenceData.contentType), this);
} else {
loadedPreview = new ErrorView(tr("Unsupported evidence type: %1").arg(originalEvidenceData.contentType), this);
}
loadedPreview->loadFromFile(originalEvidenceData.path);
loadedPreview->setReadonly(readonly);
// get all remote tags (for op)
tagEditor->loadTags(operationSlug, originalEvidenceData.tags);
}
catch (QSqlError &e) {
loadedPreview = new ErrorView(tr("Unable to load evidence: %1").arg(e.text()), this);
}
splitter->insertWidget(0, loadedPreview);
}
@ -161,21 +157,27 @@ void EvidenceEditor::onTagsLoaded(bool success) {
// saveEvidence is a helper method to save (to the database) the currently
// loaded evidence, using the editor changes.
SaveEvidenceResponse EvidenceEditor::saveEvidence() {
SaveEvidenceResponse EvidenceEditor::saveEvidence()
{
if (loadedPreview != nullptr) {
loadedPreview->saveEvidence();
}
auto evi = encodeEvidence();
auto resp = SaveEvidenceResponse(evi);
try {
db->updateEvidenceDescription(evi.description, evi.id);
db->setEvidenceTags(evi.tags, evi.id);
resp.actionSucceeded = true;
}
catch (QSqlError &e) {
if(!db->errorString().isEmpty()) {
resp.actionSucceeded = false;
resp.errorText = e.text();
resp.errorText = db->errorString();
return resp;
}
db->setEvidenceTags(evi.tags, evi.id);
if(!db->errorString().isEmpty()) {
resp.actionSucceeded = false;
resp.errorText = db->errorString();
return resp;
}
resp.actionSucceeded = true;
return resp;
}
@ -185,12 +187,9 @@ QList<DeleteEvidenceResponse> EvidenceEditor::deleteEvidence(QList<qint64> evide
for (qint64 id : evidenceIDs) {
model::Evidence evi = db->getEvidenceDetails(id);
DeleteEvidenceResponse resp(evi);
try{
resp.dbDeleteSuccess = db->deleteEvidence(evi.id);
} catch(QSqlError &e) {
resp.errorText = e.text();
}
if(!resp.dbDeleteSuccess)
resp.errorText = db->errorString();
auto localFile = QFile(evi.path);
if (!localFile.remove()) {

View File

@ -6,68 +6,61 @@
#include <QDir>
#include <QVariant>
#include "exceptions/fileerror.h"
#include "helpers/file_helpers.h"
DatabaseConnection::DatabaseConnection(const QString& dbPath, const QString& databaseName)
: _dbName(databaseName)
, _dbPath(dbPath)
, _db(QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), databaseName))
{
auto db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), databaseName);
QDir().mkpath(FileHelpers::getDirname(dbPath));
db.setDatabaseName(dbPath);
const auto dbDir = FileHelpers::getDirname(_dbPath);
if(!QDir().exists(dbDir))
QDir().mkpath(dbDir);
_db.setDatabaseName(_dbPath);
}
void DatabaseConnection::withConnection(const QString& dbPath, const QString &dbName,
const std::function<void(DatabaseConnection)> &actions) {
bool DatabaseConnection::withConnection(const QString& dbPath, const QString &dbName,
const std::function<void(DatabaseConnection)> &actions)
{
DatabaseConnection conn(dbPath, dbName);
conn.connect();
try {
if(!conn.connect())
return false;
actions(conn);
} catch(const std::runtime_error& e) {
qWarning() << "Error running action: " << e.what();
}
bool rtn = true;
if( conn._db.lastError().type() != QSqlError::NoError)
rtn = false;
conn.close();
QSqlDatabase::removeDatabase(dbName);
QSqlDatabase::removeDatabase(dbPath);
return rtn;
}
bool DatabaseConnection::connect()
{
auto db = getDB();
if (!db.open()) {
if (!_db.open())
return false;
throw db.lastError();
}
return migrateDB();
}
void DatabaseConnection::close() noexcept { getDB().close(); }
qint64 DatabaseConnection::createEvidence(const QString &filepath, const QString &operationSlug,
const QString &contentType) {
return doInsert(getDB(),
"INSERT INTO evidence"
" (path, operation_slug, content_type, recorded_date)"
" VALUES"
" (?, ?, ?, datetime('now'))",
{filepath, operationSlug, contentType});
qint64 DatabaseConnection::createEvidence(const QString &filepath, const QString &operationSlug, const QString &contentType)
{
auto qKeys = QStringLiteral("path, operation_slug, content_type, recorded_date");
auto qValues = QStringLiteral("?, ?, ?, datetime('now')");
auto qStr = _sqlBasicInsert.arg(_tblEvidence, qKeys, qValues);
return doInsert(_db, qStr, {filepath, operationSlug, contentType});
}
qint64 DatabaseConnection::createFullEvidence(const model::Evidence &evidence) {
return doInsert(getDB(),
"INSERT INTO evidence"
" (path, operation_slug, content_type, description, error, recorded_date, upload_date)"
" VALUES"
" (?, ?, ?, ?, ?, ?, ?)",
auto qKeys = QStringLiteral("path, operation_slug, content_type, description, error, recorded_date, upload_date");
auto qValues = QStringLiteral("?, ?, ?, ?, ?, ?, ?");
auto qStr = _sqlBasicInsert.arg(_tblEvidence, qKeys, qValues);
return doInsert(_db, qStr,
{evidence.path, evidence.operationSlug, evidence.contentType, evidence.description,
evidence.errorText, evidence.recordedDate, evidence.uploadDate});
}
void DatabaseConnection::batchCopyFullEvidence(const QList<model::Evidence> &evidence) {
QString baseQuery = "INSERT INTO evidence"
" (id, path, operation_slug, content_type, description, error, recorded_date, upload_date)"
" VALUES %1";
auto baseQuery = QStringLiteral("INSERT INTO evidence (%1) VALUES %2").arg(_evidenceAllKeys, QStringLiteral("%1"));
int varsPerRow = 8; // count number of "?"
std::function<QVariantList(int)> getItemValues = [evidence](int i){
auto item = evidence.at(i);
@ -79,28 +72,13 @@ void DatabaseConnection::batchCopyFullEvidence(const QList<model::Evidence> &evi
batchInsert(baseQuery, varsPerRow, evidence.size(), getItemValues);
}
qint64 DatabaseConnection::copyFullEvidence(const model::Evidence &evidence) {
return doInsert(getDB(),
"INSERT INTO evidence"
" (id, path, operation_slug, content_type, description, error, recorded_date, upload_date)"
" VALUES"
" (?, ?, ?, ?, ?, ?, ?)",
{evidence.id, evidence.path, evidence.operationSlug, evidence.contentType,
evidence.description, evidence.errorText, evidence.recordedDate,
evidence.uploadDate});
}
model::Evidence DatabaseConnection::getEvidenceDetails(qint64 evidenceID) {
model::Evidence DatabaseConnection::getEvidenceDetails(qint64 evidenceID)
{
model::Evidence rtn;
auto query = executeQuery(getDB(),
"SELECT"
" id, path, operation_slug, content_type, description, error, recorded_date, upload_date"
" FROM evidence"
" WHERE id=? LIMIT 1",
{evidenceID});
if (query.first()) {
auto qStr = QStringLiteral("%1 WHERE id=? LIMIT 1").arg(_sqlSelectTemplate.arg(_evidenceAllKeys, _tblEvidence));
auto query = executeQuery(_db, qStr, {evidenceID});
if (_db.lastError().type() == QSqlError::NoError && query.first()) {
rtn.id = query.value(QStringLiteral("id")).toLongLong();
rtn.path = query.value(QStringLiteral("path")).toString();
rtn.operationSlug = query.value(QStringLiteral("operation_slug")).toString();
@ -109,43 +87,39 @@ model::Evidence DatabaseConnection::getEvidenceDetails(qint64 evidenceID) {
rtn.errorText = query.value(QStringLiteral("error")).toString();
rtn.recordedDate = query.value(QStringLiteral("recorded_date")).toDateTime();
rtn.uploadDate = query.value(QStringLiteral("upload_date")).toDateTime();
rtn.recordedDate.setTimeSpec(Qt::UTC);
rtn.uploadDate.setTimeSpec(Qt::UTC);
rtn.tags = getTagsForEvidenceID(evidenceID);
}
else {
qWarning() << "Could not find evidence with id: " << evidenceID;
} else {
rtn.id = -1;
}
return rtn;
}
void DatabaseConnection::updateEvidenceDescription(const QString &newDescription,
qint64 evidenceID) {
executeQuery(getDB(), "UPDATE evidence SET description=? WHERE id=?", {newDescription, evidenceID});
bool DatabaseConnection::updateEvidenceDescription(const QString &newDescription, qint64 evidenceID)
{
auto q = executeQuery(_db, QStringLiteral("UPDATE evidence SET description=? WHERE id=?"), {newDescription, evidenceID});
return (q.lastError().type() == QSqlError::NoError);
}
bool DatabaseConnection::deleteEvidence(qint64 evidenceID)
{
auto q = executeQuery(getDB(), "DELETE FROM evidence WHERE id=?", {evidenceID});
if (q.lastError().type() == QSqlError::NoError)
return true;
qWarning() << "Unable to Delete " << evidenceID << " " << q.lastError().text();
return false;
auto q = executeQuery(_db, QStringLiteral("DELETE FROM evidence WHERE id=?"), {evidenceID});
return (q.lastError().type() == QSqlError::NoError);
}
void DatabaseConnection::updateEvidenceError(const QString &errorText, qint64 evidenceID) {
executeQuery(getDB(), "UPDATE evidence SET error=? WHERE id=?", {errorText, evidenceID});
bool DatabaseConnection::updateEvidenceError(const QString &errorText, qint64 evidenceID) {
auto q = executeQuery(_db, QStringLiteral("UPDATE evidence SET error=? WHERE id=?"), {errorText, evidenceID});
return (q.lastError().type() == QSqlError::NoError);
}
void DatabaseConnection::updateEvidenceSubmitted(qint64 evidenceID) {
executeQuery(getDB(), "UPDATE evidence SET upload_date=datetime('now') WHERE id=?", {evidenceID});
executeQuery(_db, QStringLiteral("UPDATE evidence SET upload_date=datetime('now') WHERE id=?"), {evidenceID});
}
QList<model::Tag> DatabaseConnection::getTagsForEvidenceID(qint64 evidenceID) {
QList<model::Tag> tags;
auto getTagQuery = executeQuery(getDB(), "SELECT id, tag_id, name FROM tags WHERE evidence_id=?",
auto getTagQuery = executeQuery(_db, QStringLiteral("SELECT id, tag_id, name FROM tags WHERE evidence_id=?"),
{evidenceID});
while (getTagQuery.next()) {
auto tag = model::Tag(getTagQuery.value(QStringLiteral("id")).toLongLong(),
@ -160,7 +134,7 @@ QList<model::Tag> DatabaseConnection::getFullTagsForEvidenceIDs(
const QList<qint64>& evidenceIDs) {
QList<model::Tag> tags;
batchQuery("SELECT id, evidence_id, tag_id, name FROM tags WHERE evidence_id IN (%1)", 1, evidenceIDs.size(),
batchQuery(QStringLiteral("SELECT id, evidence_id, tag_id, name FROM tags WHERE evidence_id IN (%1)"), 1, evidenceIDs.size(),
[evidenceIDs](unsigned int index){
return QVariantList{evidenceIDs[index]};
},
@ -175,33 +149,37 @@ QList<model::Tag> DatabaseConnection::getFullTagsForEvidenceIDs(
return tags;
}
void DatabaseConnection::setEvidenceTags(const QList<model::Tag> &newTags, qint64 evidenceID)
bool DatabaseConnection::setEvidenceTags(const QList<model::Tag> &newTags, qint64 evidenceID)
{
// todo: this this actually work?
if(newTags.isEmpty())
return;
return false;
auto db = getDB();
QVariantList newTagIds;
for (const auto &tag : newTags) {
for (const auto &tag : newTags)
newTagIds.append(tag.serverTagId);
}
executeQuery(db, "DELETE FROM tags WHERE tag_id NOT IN (?) AND evidence_id = ?",
{newTagIds, evidenceID});
auto currentTagsResult =
executeQuery(db, "SELECT tag_id FROM tags WHERE evidence_id = ?", {evidenceID});
auto qDelStr = QStringLiteral("DELETE FROM tags WHERE tag_id NOT IN (?) AND evidence_id = ?");
auto a = executeQuery(_db, qDelStr, {newTagIds, evidenceID});
if(a.lastError().type() != QSqlError::NoError)
return false;
auto qSelStr = QStringLiteral("SELECT tag_id FROM tags WHERE evidence_id = ?");
auto currentTagsResult = executeQuery(_db, qSelStr, {evidenceID});
if (currentTagsResult.lastError().type() != QSqlError::NoError)
return false;
QList<qint64> currentTags;
while (currentTagsResult.next()) {
while (currentTagsResult.next())
currentTags.append(currentTagsResult.value(QStringLiteral("tag_id")).toLongLong());
}
struct dataset {
qint64 evidenceID = 0;
qint64 tagID = 0;
QString name;
};
QList<dataset> tagDataToInsert;
QString baseQuery = "INSERT INTO tags (evidence_id, tag_id, name) VALUES ";
QString baseQuery = QStringLiteral("INSERT INTO tags (evidence_id, tag_id, name) VALUES ");
for (const auto &newTag : newTags) {
if (currentTags.count(newTag.serverTagId) == 0) {
dataset item;
@ -216,19 +194,23 @@ void DatabaseConnection::setEvidenceTags(const QList<model::Tag> &newTags, qint6
// sqlite indicates it's default is 100 passed parameter, but it can "handle thousands"
if (!tagDataToInsert.empty()) {
QVariantList args;
baseQuery += "(?,?,?)";
baseQuery += QString(", (?,?,?)").repeated(int(tagDataToInsert.size() - 1));
baseQuery.append(QStringLiteral("(?, ?, ?"));
baseQuery.append(QString(", (?,?,?)").repeated(int(tagDataToInsert.size() - 1)));
baseQuery.append(QStringLiteral(")"));
for (const auto &item : tagDataToInsert) {
args.append(item.evidenceID);
args.append(item.tagID);
args.append(item.name);
}
executeQuery(db, baseQuery, args);
auto q = executeQuery(_db, baseQuery, args);
if (q.lastError().type() != QSqlError::NoError)
return false;
}
return true;
}
void DatabaseConnection::batchCopyTags(const QList<model::Tag> &allTags) {
QString baseQuery = "INSERT INTO tags (id, evidence_id, tag_id, name) VALUES %1";
QString baseQuery = QStringLiteral("INSERT INTO tags (id, evidence_id, tag_id, name) VALUES %1");
int varsPerRow = 4;
std::function<QVariantList(int)> getItemValues = [allTags](int i){
model::Tag item = allTags.at(i);
@ -237,23 +219,26 @@ void DatabaseConnection::batchCopyTags(const QList<model::Tag> &allTags) {
batchInsert(baseQuery, varsPerRow, allTags.size(), getItemValues);
}
DBQuery DatabaseConnection::buildGetEvidenceWithFiltersQuery(const EvidenceFilters &filters) {
QString query =
"SELECT"
" id, path, operation_slug, content_type, description, error, recorded_date, upload_date"
" FROM evidence";
DBQuery DatabaseConnection::buildGetEvidenceWithFiltersQuery(const EvidenceFilters &filters)
{
QString query = _sqlSelectTemplate.arg(_evidenceAllKeys, _tblEvidence);
QVariantList values;
QStringList parts;
if (filters.hasError != Tri::Any) {
parts.append(" error LIKE ? ");
parts.append(QStringLiteral(" error LIKE ? "));
// _% will ensure at least one character exists in the error column, ensuring it's populated
values.append(filters.hasError == Tri::Yes ? "_%" : "");
values.append(filters.hasError == Tri::Yes ? QStringLiteral("_%") : QString());
}
if (filters.submitted != Tri::Any) {
parts.append((filters.submitted == Tri::Yes) ? " upload_date IS NOT NULL "
: " upload_date IS NULL ");
auto sub = QStringLiteral(" upload_data IS%1NULL");
if(filters.submitted == Tri::Yes)
parts.append(sub.arg(QStringLiteral(" NOT ")));
else
parts.append(sub.arg(QStringLiteral(" ")));
}
if (!filters.operationSlug.isEmpty()) {
parts.append(" operation_slug = ? ");
values.append(filters.operationSlug);
@ -273,24 +258,24 @@ DBQuery DatabaseConnection::buildGetEvidenceWithFiltersQuery(const EvidenceFilte
}
if (!parts.empty()) {
query += " WHERE " + parts.at(0);
for (size_t i = 1; i < parts.size(); i++) {
query += " AND " + parts.at(i);
}
query.append(QStringLiteral(" WHERE %1").arg(parts.at(0)));
for (size_t i = 1; i < parts.size(); i++)
query.append(QStringLiteral(" AND %1").arg(parts.at(i)));
}
return DBQuery(query, values);
}
void DatabaseConnection::updateEvidencePath(const QString& newPath, qint64 evidenceID) {
executeQuery(getDB(), "UPDATE evidence SET path=? WHERE id=?", {newPath, evidenceID});
void DatabaseConnection::updateEvidencePath(const QString& newPath, qint64 evidenceID)
{
executeQuery(_db, QStringLiteral("UPDATE evidence SET path=? WHERE id=?"), {newPath, evidenceID});
}
QList<model::Evidence> DatabaseConnection::getEvidenceWithFilters(
const EvidenceFilters &filters) {
QList<model::Evidence> DatabaseConnection::getEvidenceWithFilters(const EvidenceFilters &filters)
{
auto dbQuery = buildGetEvidenceWithFiltersQuery(filters);
auto resultSet = executeQuery(getDB(), dbQuery.query(), dbQuery.values());
auto resultSet = executeQuery(_db, dbQuery.query(), dbQuery.values());
QList<model::Evidence> allEvidence;
while (resultSet.next()) {
model::Evidence evi;
evi.id = resultSet.value(QStringLiteral("id")).toLongLong();
@ -301,10 +286,8 @@ QList<model::Evidence> DatabaseConnection::getEvidenceWithFilters(
evi.errorText = resultSet.value(QStringLiteral("error")).toString();
evi.recordedDate = resultSet.value(QStringLiteral("recorded_date")).toDateTime();
evi.uploadDate = resultSet.value(QStringLiteral("upload_date")).toDateTime();
evi.recordedDate.setTimeSpec(Qt::UTC);
evi.uploadDate.setTimeSpec(Qt::UTC);
allEvidence.append(evi);
}
@ -312,12 +295,11 @@ QList<model::Evidence> DatabaseConnection::getEvidenceWithFilters(
}
QList<model::Evidence> DatabaseConnection::createEvidenceExportView(
const QString& pathToExport, const EvidenceFilters& filters, DatabaseConnection *runningDB) {
const QString& pathToExport, const EvidenceFilters& filters, DatabaseConnection *runningDB)
{
QList<model::Evidence> exportEvidence;
auto exportViewAction = [runningDB, filters, &exportEvidence](DatabaseConnection exportDB) {
exportEvidence = runningDB->getEvidenceWithFilters(filters);
exportDB.batchCopyFullEvidence(exportEvidence);
QList<qint64> evidenceIds;
evidenceIds.resize(exportEvidence.size());
@ -326,65 +308,46 @@ QList<model::Evidence> DatabaseConnection::createEvidenceExportView(
QList<model::Tag> tags = runningDB->getFullTagsForEvidenceIDs(evidenceIds);
exportDB.batchCopyTags(tags);
};
withConnection(pathToExport, QStringLiteral("exportDB"), exportViewAction);
return exportEvidence;
}
// migrateDB checks the migration status and then performs the full migration for any
// lacking update.
//
// Throws exceptions/FileError if a migration file cannot be found.
bool DatabaseConnection::migrateDB() {
auto db = getDB();
bool DatabaseConnection::migrateDB()
{
qInfo() << "Checking database state";
auto migrationsToApply = DatabaseConnection::getUnappliedMigrations(db);
auto migrationsToApply = DatabaseConnection::getUnappliedMigrations();
for (const QString &newMigration : migrationsToApply) {
QFile migrationFile(QStringLiteral(":/migrations/%1").arg(newMigration));
auto ok = migrationFile.open(QFile::ReadOnly);
if (!ok)
for (const auto &newMigration : migrationsToApply) {
QFile migrationFile(QStringLiteral("%1/%2").arg(_migrationPath, newMigration));
if (!migrationFile.open(QFile::ReadOnly))
return false;
auto content = QString(migrationFile.readAll());
migrationFile.close();
qInfo() << "Applying Migration: " << newMigration;
auto upScript = extractMigrateUpContent(content);
executeQuery(db, upScript);
executeQuery(db,
"INSERT INTO migrations (migration_name, applied_at) VALUES (?, datetime('now'))",
{newMigration});
executeQuery(_db, upScript);
executeQuery(_db, _sqlAddAppliedMigration, {newMigration});
}
qInfo() << "All migrations applied";
return true;
}
// getUnappliedMigrations retrieves a list of all of the migrations that have not been applied
// to the local database.
//
// Note: All sql files must end in ".sql" to be picked up
//
// Throws:
// * BadDatabaseStateError if some migrations have been applied that are not known
// * QSqlError if database queries fail
QStringList DatabaseConnection::getUnappliedMigrations(const QSqlDatabase &db)
QStringList DatabaseConnection::getUnappliedMigrations()
{
QDir migrationsDir(QStringLiteral(":/migrations"));
QDir migrationsDir(_migrationPath);
const auto allMigrations = migrationsDir.entryList(QDir::Files, QDir::Name);
QStringList appliedMigrations;
QStringList migrationsToApply;
auto queryResult = executeQueryNoThrow(db, "SELECT migration_name FROM migrations");
auto queryResult = executeQueryNoThrow(_db, _sqlSelectTemplate.arg(_migration_name, _tblMigrations));
QSqlQuery* dbMigrations = &queryResult.query;
while (queryResult.success && queryResult.query.next()) {
appliedMigrations << dbMigrations->value(QStringLiteral("migration_name")).toString();
}
while (queryResult.success && queryResult.query.next())
appliedMigrations << dbMigrations->value(_migration_name).toString();
// compare the two list to find gaps
for (const QString &possibleMigration : allMigrations) {
for (const auto &possibleMigration : allMigrations) {
if (!possibleMigration.endsWith(QStringLiteral(".sql")))
continue; // assume non-sql files aren't actual migrations.
continue;
auto foundIndex = appliedMigrations.indexOf(possibleMigration);
if (foundIndex == -1)
migrationsToApply << possibleMigration;
@ -399,7 +362,8 @@ QStringList DatabaseConnection::getUnappliedMigrations(const QSqlDatabase &db)
// extractMigrateUpContent parses the given migration content and retrieves only
// the portion that applies to the "up" / apply logic. The "down" section is ignored.
QString DatabaseConnection::extractMigrateUpContent(const QString &allContent) noexcept {
QString DatabaseConnection::extractMigrateUpContent(const QString &allContent) noexcept
{
QString upContent;
const QStringList lines = allContent.split(_newLine);
for (const QString &line : lines) {
@ -415,52 +379,35 @@ QString DatabaseConnection::extractMigrateUpContent(const QString &allContent) n
// executeQuery simply attempts to execute the given stmt with the passed args. The statement is
// first prepared, and arg placements can be specified with "?"
//
// Throws: QSqlError when a query error occurs
QSqlQuery DatabaseConnection::executeQuery(const QSqlDatabase& db, const QString &stmt,
const QVariantList &args) {
auto result = executeQueryNoThrow(db, stmt, args);
if (!result.success) {
throw result.err;
}
if (!result.success)
qWarning() << "Error executing Query: " << result.err.text();
return std::move(result.query);
}
QueryResult DatabaseConnection::executeQueryNoThrow(const QSqlDatabase& db, const QString &stmt,
const QVariantList &args) noexcept {
const QVariantList &args) noexcept
{
QSqlQuery query(db);
bool prepared = query.prepare(stmt);
if (!prepared) {
if (!query.prepare(stmt))
return QueryResult(std::move(query));
}
for (const auto &arg : args) {
for (const auto &arg : args)
query.addBindValue(arg);
}
query.exec();
return QueryResult(std::move(query));
}
// doInsert is a version of executeQuery that returns the last inserted id, rather than the
// underlying query/response
//
// Throws: QSqlError when a query error occurs
qint64 DatabaseConnection::doInsert(const QSqlDatabase& db, const QString &stmt,
const QVariantList &args) {
// Logs then returns -1
qint64 DatabaseConnection::doInsert(const QSqlDatabase& db, const QString &stmt, const QVariantList &args)
{
auto query = executeQuery(db, stmt, args);
if(query.lastInsertId() != QVariant())
return query.lastInsertId().toLongLong();
}
QSqlDatabase DatabaseConnection::getDB()
{
return QSqlDatabase::database(_dbName);
}
QString DatabaseConnection::getDatabasePath()
{
return _dbPath;
return -1;
}
void DatabaseConnection::batchInsert(const QString& baseQuery, unsigned int varsPerRow, unsigned int numRows,
@ -484,7 +431,6 @@ void DatabaseConnection::batchQuery(const QString &baseQuery, unsigned int varsP
variableTemplate = QString("?,").repeated(int(varsPerRow));
}
int runningRowIndex = 0; // tracks what row is next to be encoded/"inserted"
auto db = getDB();
/// prepArgString generates a string that looks like ?,?,?, with as many ? as rowInsertTemplate * numRows
auto prepArgString = [variableTemplate](unsigned int numRows){
auto inst = variableTemplate.repeated(int(numRows));
@ -501,8 +447,8 @@ void DatabaseConnection::batchQuery(const QString &baseQuery, unsigned int varsP
return values;
};
/// runQuery executes the given query, and iterates over the result set
auto runQuery = [db, decodeRows](const QString &query, const QVariantList& values) {
auto completedQuery = executeQuery(db, query, values);
auto runQuery = [this, decodeRows](const QString &query, const QVariantList& values) {
auto completedQuery = executeQuery(_db, query, values);
while (completedQuery.next()) {
decodeRows(completedQuery);
}

View File

@ -28,9 +28,15 @@ class DBQuery {
inline QString query() { return _query; }
inline QVariantList values() { return _values; }
};
/**
* @brief The DatabaseConnection class Interface to the local database
* All Changes / reads to db should return true on success
* any failed actions can have erorrs checked with DatabaseConnection::errorString()
*/
class DatabaseConnection {
public:
const unsigned long SQLITE_MAX_VARS = 999;
QString getDatabasePath() { return _dbPath; }
/**
* @brief DatabaseConnection construct a connect to the database,
* Uses QSQLite Driver if the driver is missing the app to exit with code 255.
@ -48,29 +54,39 @@ class DatabaseConnection {
* Specifically, it should NOT be the value from Constants::databaseName)
* @param actions A function that will execute after a connection is established. This is where
* all db interactions should occur.
* Returns True is successful
*/
static void withConnection(const QString& dbPath, const QString &dbName,
static bool withConnection(const QString& dbPath, const QString &dbName,
const std::function<void(DatabaseConnection)> &actions);
///Return the last Error
QString errorString() {return _db.lastError().text();}
bool connect();
void close() noexcept;
void close() noexcept {_db.close();}
static DBQuery buildGetEvidenceWithFiltersQuery(const EvidenceFilters &filters);
model::Evidence getEvidenceDetails(qint64 evidenceID);
QList<model::Evidence> getEvidenceWithFilters(const EvidenceFilters &filters);
/// Return -1 if Failed
qint64 createEvidence(const QString &filepath, const QString &operationSlug,
const QString &contentType);
qint64 createFullEvidence(const model::Evidence &evidence);
void batchCopyFullEvidence(const QList<model::Evidence> &evidence);
qint64 copyFullEvidence(const model::Evidence &evidence);
void updateEvidenceDescription(const QString &newDescription, qint64 evidenceID);
void updateEvidenceError(const QString &errorText, qint64 evidenceID);
/**
* @brief updateEvidenceDescription
* @param newDescription
* @param evidenceID
* @return True if successful
*/
bool updateEvidenceDescription(const QString &newDescription, qint64 evidenceID);
bool updateEvidenceError(const QString &errorText, qint64 evidenceID);
void updateEvidenceSubmitted(qint64 evidenceID);
void updateEvidencePath(const QString& newPath, qint64 evidenceID);
void setEvidenceTags(const QList<model::Tag> &newTags, qint64 evidenceID);
bool setEvidenceTags(const QList<model::Tag> &newTags, qint64 evidenceID);
void batchCopyTags(const QList<model::Tag> &allTags);
QList<model::Tag> getFullTagsForEvidenceIDs(const QList<qint64>& evidenceIDs);
@ -91,25 +107,38 @@ class DatabaseConnection {
DatabaseConnection *runningDB);
QList<model::Tag> getTagsForEvidenceID(qint64 evidenceID);
/// getDatabasePath returns the filepath associated with the loaded database
QString getDatabasePath();
public:
const unsigned long SQLITE_MAX_VARS = 999;
QSqlError lastError() {return _db.lastError();}
private:
QString _dbName;
QString _dbPath;
QSqlDatabase _db = QSqlDatabase();
inline static const auto _migrateUp = QStringLiteral("-- +migrate up");
inline static const auto _migrateDown = QStringLiteral("-- +migrate down");
inline static const auto _newLine = QStringLiteral("\n");
inline static const auto _lineTemplate = QStringLiteral("%1").append(_newLine);
inline static const auto _migrationPath = QStringLiteral(":/migrations");
inline static const auto _sqlSelectTemplate = QStringLiteral("SELECT %1 FROM %2");
inline static const auto _sqlBasicInsert = QStringLiteral("INSERT INTO %1 (%2) VALUES (%3)");
inline static const auto _sqlAddAppliedMigration = QStringLiteral("INSERT INTO migrations (migration_name, applied_at) VALUES (?, datetime('now'))");
inline static const auto _migration_name = QStringLiteral("migration_name");
inline static const auto _tblEvidence = QStringLiteral("evidence");
inline static const auto _tblMigrations = QStringLiteral("migrations");
inline static const auto _evidenceAllKeys = QStringLiteral("id, path, operation_slug, content_type, description, error, recorded_date, upload_date");
/**
* @brief migrateDB - Check migration status and apply any outstanding ones
* @return true if successful
*/
bool migrateDB();
QSqlDatabase getDB();
static QStringList getUnappliedMigrations(const QSqlDatabase &db);
static QString extractMigrateUpContent(const QString &allContent) noexcept;
/**
* @brief getUnappliedMigrations retrieves a list of all of the migrations that have not been applied to the database db
* Note: only files ending in ".sql" are checked
* @return List of migrations that have not be applied
*/
QStringList getUnappliedMigrations();
QString extractMigrateUpContent(const QString &allContent) noexcept;
static QSqlQuery executeQuery(const QSqlDatabase& db, const QString &stmt,
const QVariantList &args = {});
@ -118,6 +147,14 @@ class DatabaseConnection {
/// QueryResult.sucess/QueryResult.err fields to determine the actual result.
static QueryResult executeQueryNoThrow(const QSqlDatabase& db, const QString &stmt,
const QVariantList &args = {}) noexcept;
/**
* @brief doInsert is a version of executeQuery that returns the last inserted id, rather than the underlying query/response
* @param db database to act upon
* @param stmt sql to run
* @param args args
* @return Inserted ID or -1 if failed.
*/
static qint64 doInsert(const QSqlDatabase &db, const QString &stmt, const QVariantList &args);
/**

View File

@ -1,12 +0,0 @@
add_library (EXCEPTIONS STATIC fileerror.h)
add_library(ASHIRT::EXCEPTIONS ALIAS EXCEPTIONS)
target_include_directories (EXCEPTIONS
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
)
target_link_libraries ( EXCEPTIONS PUBLIC Qt::Core)

View File

@ -1,76 +0,0 @@
// Copyright 2020, Verizon Media
// Licensed under the terms of MIT. See LICENSE file in project root for terms.
#pragma once
#include <QFileDevice>
#include <stdexcept>
#include <string>
class FileError : public std::runtime_error {
public:
/// mkError constructs an std::runtime_error with the given details.
static FileError mkError(QString msg, QString path, QFileDevice::FileError err) {
return FileError::mkError(msg.toStdString(), path.toStdString(), err);
}
/// mkError constructs an std::runtime_error with the given details.
static FileError mkError(std::string msg, std::string path, QFileDevice::FileError err) {
std::string suberror;
switch (err) {
case QFileDevice::ReadError:
suberror = "Error reading file";
break;
case QFileDevice::WriteError:
suberror = "Error writing file";
break;
case QFileDevice::FatalError:
suberror = "Fatal error occurred";
break;
case QFileDevice::ResourceError:
suberror = "Insufficient resources available";
break;
case QFileDevice::OpenError:
suberror = "Could not open file";
break;
case QFileDevice::AbortError:
suberror = "Operation was aborted";
break;
case QFileDevice::TimeOutError:
suberror = "Operation timed out";
break;
case QFileDevice::UnspecifiedError:
suberror = "Unknown Error";
break;
case QFileDevice::RemoveError:
suberror = "Unable to remove file";
break;
case QFileDevice::RenameError:
suberror = "Unable to rename/move file";
break;
case QFileDevice::PositionError:
suberror = "Position error"; // I don't think we'll ever enounter this error
break;
case QFileDevice::ResizeError:
suberror = "Unable to resize file";
break;
case QFileDevice::PermissionsError:
suberror = "Unable to access file";
break;
case QFileDevice::CopyError:
suberror = "Unable to copy file";
break;
case QFileDevice::NoError:
suberror = "Actually, no error occurred -- just bad programming.";
break;
}
FileError wrappedErr(msg + " (path: " + path + "): " + suberror);
wrappedErr.fileDeviceError = err;
return wrappedErr;
}
public:
QFileDevice::FileError fileDeviceError;
private:
FileError(std::string msg) : std::runtime_error(msg) {}
};

View File

@ -15,7 +15,6 @@
#include "appconfig.h"
#include "dtos/tag.h"
#include "exceptions/fileerror.h"
#include "forms/evidence_filter/evidencefilter.h"
#include "forms/evidence_filter/evidencefilterform.h"
#include "helpers/netman.h"
@ -194,23 +193,23 @@ void EvidenceManager::showEvent(QShowEvent* evt) {
resetFilterButtonClicked();
}
void EvidenceManager::submitEvidenceTriggered() {
void EvidenceManager::submitEvidenceTriggered()
{
loadingAnimation->startAnimation();
evidenceTable->setEnabled(false); // prevent switching evidence while one is being submitted.
if (saveData()) {
if (!saveData())
return;
evidenceIDForRequest = selectedRowEvidenceID();
try {
model::Evidence evi = db->getEvidenceDetails(evidenceIDForRequest);
uploadAssetReply = NetMan::uploadAsset(evi);
connect(uploadAssetReply, &QNetworkReply::finished, this, &EvidenceManager::onUploadComplete);
}
catch (QSqlError& e) {
if(evi.id == -1) {
evidenceTable->setEnabled(true);
loadingAnimation->stopAnimation();
QMessageBox::warning(this, tr("Cannot submit evidence"),
tr("Could not retrieve data. Please try again."));
return;
}
}
uploadAssetReply = NetMan::uploadAsset(evi);
connect(uploadAssetReply, &QNetworkReply::finished, this, &EvidenceManager::onUploadComplete);
}
void EvidenceManager::deleteEvidenceTriggered() {
@ -277,19 +276,15 @@ void EvidenceManager::deleteSet(QList<qint64> ids) {
auto errLogPath = QStringLiteral("%1/%2.log")
.arg(AppConfig::value(CONFIG::EVIDENCEREPO)
, QDateTime::currentDateTime().toMSecsSinceEpoch());
try {
QByteArray dataToWrite = tr("Paths to files that could not be deleted: \n\n %1")
.arg(undeletedFiles.join(QStringLiteral("\n"))).toUtf8();
FileHelpers::writeFile(errLogPath, dataToWrite);
}
catch(FileError &e) {
logWritten = false;
}
QString msg = tr("Some files could not be deleted.");
logWritten = FileHelpers::writeFile(errLogPath, dataToWrite);
if (logWritten) {
QString msg = tr("Some files could not be deleted.");
if (logWritten)
msg.append(tr(" A list of the excluded files can be found here: \n").arg(errLogPath));
}
QMessageBox::warning(this, tr("Could not complete evidence deletion"), msg);
}
@ -332,7 +327,8 @@ void EvidenceManager::applyFilterForm(const EvidenceFilters& filter) {
loadEvidence();
}
void EvidenceManager::loadEvidence() {
void EvidenceManager::loadEvidence()
{
qint64 reselectId = -1;
if (evidenceTable->selectedItems().size() > 0) {
reselectId = selectedRowEvidenceID();
@ -340,9 +336,11 @@ void EvidenceManager::loadEvidence() {
evidenceTable->clearContents();
try {
auto filter = EvidenceFilters::parseFilter(filterTextBox->text());
QList<model::Evidence> operationEvidence = db->getEvidenceWithFilters(filter);
if(db->lastError().type() != QSqlError::NoError){
qWarning() << "Could not retrieve evidence for operation. Error: " << db->lastError().text();
}
evidenceTable->setRowCount(operationEvidence.size());
// removing sorting temporarily to solve a bug (per qt: not a bug)
@ -380,10 +378,6 @@ void EvidenceManager::loadEvidence() {
evidenceTable->setCurrentCell(selectRow, 0);
}
}
catch (QSqlError& e) {
qWarning() << "Could not retrieve evidence for operation. Error: " << e.text();
}
}
// buildBaseEvidenceRow constructs a container for a row of data.
// Note: the row (container) is on the stack, but items in the container
@ -432,15 +426,15 @@ void EvidenceManager::setRowText(int row, const model::Evidence& model) {
setColText(COL_DATE_SUBMITTED, uploadDateText);
}
void EvidenceManager::refreshRow(int row) {
void EvidenceManager::refreshRow(int row)
{
auto evidenceID = selectedRowEvidenceID();
try {
auto updatedData = db->getEvidenceDetails(evidenceID);
if (updatedData.id != -1) {
setRowText(row, updatedData);
return;
}
catch (QSqlError& e) {
qWarning() << "Could not refresh table row: " << e.text();
}
qWarning() << "Could not refresh table row: " << db->errorString();
}
bool EvidenceManager::saveData() {
@ -500,25 +494,13 @@ void EvidenceManager::onUploadComplete() {
NetMan::extractResponse(uploadAssetReply, isValid);
if (!isValid) {
auto errMessage =
tr("Unable to upload evidence: Network error (%1)").arg(uploadAssetReply->errorString());
try {
auto errMessage = tr("Unable to upload evidence: Network error (%1)").arg(uploadAssetReply->errorString());
db->updateEvidenceError(errMessage, evidenceIDForRequest);
}
catch (QSqlError& e) {
qWarning() << "Upload failed. Could not update internal database. Error: " << e.text();
}
QMessageBox::warning(this, tr("Cannot Submit Evidence"),
tr("Upload failed: Network error. Check your connection and try again.\n"
"(Error: %1)").arg(uploadAssetReply->errorString()));
}
else {
try {
} else {
db->updateEvidenceSubmitted(evidenceIDForRequest);
}
catch (QSqlError& e) {
qWarning() << "Upload successful. Could not update internal database. Error: " << e.text();
}
Q_EMIT evidenceChanged(evidenceIDForRequest, true); // lock the editing form
}
refreshRow(evidenceTable->currentRow());

View File

@ -84,20 +84,20 @@ bool GetInfo::saveData() {
return saveResponse.actionSucceeded;
}
void GetInfo::submitButtonClicked() {
void GetInfo::submitButtonClicked()
{
submitButton->startAnimation();
Q_EMIT setActionButtonsEnabled(false);
if (saveData()) {
try {
if (!saveData())
return;
model::Evidence evi = db->getEvidenceDetails(evidenceID);
uploadAssetReply = NetMan::uploadAsset(evi);
connect(uploadAssetReply, &QNetworkReply::finished, this, &GetInfo::onUploadComplete);
}
catch (QSqlError& e) {
if(evi.id == -1) {
QMessageBox::warning(this, tr("Cannot submit evidence"),
tr("Could not retrieve data. Please try again."));
return;
}
}
uploadAssetReply = NetMan::uploadAsset(evi);
connect(uploadAssetReply, &QNetworkReply::finished, this, &GetInfo::onUploadComplete);
}
void GetInfo::deleteButtonClicked() {
@ -127,32 +127,25 @@ void GetInfo::deleteButtonClicked() {
}
}
void GetInfo::onUploadComplete() {
void GetInfo::onUploadComplete()
{
if (uploadAssetReply->error() != QNetworkReply::NoError) {
auto errMessage =
tr("Unable to upload evidence: Network error (%1)").arg(uploadAssetReply->errorString());
try {
auto errMessage = tr("Unable to upload evidence: Network error (%1)").arg(uploadAssetReply->errorString());
db->updateEvidenceError(errMessage, evidenceID);
}
catch (QSqlError& e) {
qWarning() << "Upload failed. Could not update internal database. Error: " << e.text();
}
if(!db->errorString().isEmpty())
qWarning() << "Upload failed. Could not update internal database. Error: " << db->errorString();
QMessageBox::warning(this, tr("Cannot submit evidence"),
tr("Upload failed: Network error. Check your connection and try again.\n"
"Note: This evidence has been saved. You can close this window and "
"re-submit from the evidence manager."
"\n(Error: %1)").arg(uploadAssetReply->errorString()));
}
else {
try {
} else {
db->updateEvidenceSubmitted(evidenceID);
if(!db->errorString().isEmpty())
qWarning() << "Upload successful. Could not update internal database. Error: " << db->errorString();
Q_EMIT evidenceSubmitted(db->getEvidenceDetails(evidenceID));
close();
}
catch (QSqlError& e) {
qWarning() << "Upload successful. Could not update internal database. Error: " << e.text();
}
}
// we don't actually need anything from the uploadAssets reply, so just clean it up.
// one thing we might want to record: evidence uuid... not sure why we'd need it though.
submitButton->stopAnimation();

View File

@ -12,7 +12,6 @@
#include <QtConcurrent/QtConcurrent>
#include "db/databaseconnection.h"
#include "exceptions/fileerror.h"
PortingDialog::PortingDialog(PortType dialogType, DatabaseConnection* db, QWidget *parent)
: AShirtDialog(parent)
@ -156,7 +155,8 @@ QString PortingDialog::getPortPath() {
return portPath;
}
void PortingDialog::doExport(porting::SystemManifest* manifest, const QString& exportPath) {
void PortingDialog::doExport(porting::SystemManifest* manifest, const QString& exportPath)
{
porting::SystemManifestExportOptions options;
options.exportDb = portEvidenceCheckBox->isChecked();
options.exportConfig = portConfigCheckBox->isChecked();
@ -165,54 +165,42 @@ void PortingDialog::doExport(porting::SystemManifest* manifest, const QString& e
// the withconnection here that connects to the same database. Note: we shouldn't write to the db
// in this thread, if possible.
QString threadedDbName = QStringLiteral("%1_mt_forExport").arg(Constants::defaultDbName);
DatabaseConnection::withConnection(
auto success = DatabaseConnection::withConnection(
db->getDatabasePath(), threadedDbName, [this, &manifest, exportPath, options](DatabaseConnection conn) {
try {
manifest->exportManifest(&conn, exportPath, options);
}
catch(const FileError &e) {
portStatusLabel->setText(tr("Error during export: %1").arg(e.what()));
Q_EMIT onWorkComplete(false);
}
catch(const QSqlError &e) {
portStatusLabel->setText(tr("Error during export: %1").arg(e.text()));
Q_EMIT onWorkComplete(false);
}
});
if(success) {
Q_EMIT onWorkComplete(true);
return;
}
portStatusLabel->setText(tr("Error during export: %1").arg(db->errorString()));
Q_EMIT onWorkComplete(false);
}
porting::SystemManifest* PortingDialog::doPreImport(const QString& pathToSystemManifest) {
porting::SystemManifest* manifest = nullptr;
try {
manifest = porting::SystemManifest::readManifest(pathToSystemManifest);
}
catch(const FileError& e) {
if(!manifest) {
portStatusLabel->setText(tr("Unable to parse system file."));
onPortComplete(false);
}
return manifest;
}
void PortingDialog::doImport(porting::SystemManifest* manifest) {
void PortingDialog::doImport(porting::SystemManifest* manifest)
{
porting::SystemManifestImportOptions options;
options.importDb = portEvidenceCheckBox->isChecked() ? options.Merge : options.None;
options.importConfig = portConfigCheckBox->isChecked();
QString threadedDbName = QStringLiteral("%1_mt_forImport").arg(Constants::defaultDbName);
DatabaseConnection::withConnection(
auto success = DatabaseConnection::withConnection(
db->getDatabasePath(), threadedDbName, [this, &manifest, options](DatabaseConnection conn){
try {
manifest->applyManifest(options, &conn);
}
catch(const FileError &e) {
portStatusLabel->setText(tr("Error during import: %1").arg(e.what()));
Q_EMIT onWorkComplete(false);
}
catch(const QSqlError &e) {
portStatusLabel->setText(tr("Error during import: ").arg(e.text()));
Q_EMIT onWorkComplete(false);
}
});
if(success) {
Q_EMIT onWorkComplete(true);
return;
}
portStatusLabel->setText(tr("Error during import: %1").arg(db->errorString()));
Q_EMIT onWorkComplete(false);
}

View File

@ -122,7 +122,7 @@ public:
/// Callers should retrieve the result by listening for the releasesChecked signal
static void checkForNewRelease(QString owner, QString repo) {
if (owner.isEmpty() || repo.isEmpty()) {
qWarning() << "Skipping release check: no owner or repo set.";
qInfo() << "Skipping release check: no owner or repo set.";
return;
}
get()->githubReleaseReply = get()->getGithubReleases(owner, repo);

View File

@ -35,7 +35,7 @@ int main(int argc, char* argv[])
auto conn = new DatabaseConnection(Constants::dbLocation, Constants::defaultDbName);
if(!conn->connect()) {
showMsgBox(QT_TRANSLATE_NOOP("main", "Unable to connect to database"));
showMsgBox(QString(QT_TRANSLATE_NOOP("main", "Database Error: %1")).arg(conn->errorString()));
return -1;
}

View File

@ -121,8 +121,10 @@ void SystemManifest::exportManifest(DatabaseConnection* db, const QString& outpu
}
m_pathToManifest = QStringLiteral("%1/system.json").arg(basePath);
FileHelpers::writeFile(m_pathToManifest, QJsonDocument(serialize(*this)).toJson());
if(FileHelpers::writeFile(m_pathToManifest, QJsonDocument(serialize(*this)).toJson()))
Q_EMIT onComplete();
else
Q_EMIT onExportError(QStringLiteral("Error On Exporting manifest"));
}
porting::EvidenceManifest SystemManifest::copyEvidence(const QString& baseExportPath,

View File

@ -36,7 +36,6 @@ namespace porting {
* @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);
@ -46,7 +45,6 @@ namespace porting {
* @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);
@ -69,6 +67,8 @@ namespace porting {
void onFileProcessed(quint64 runningCount);
/// onComplete fires when the entire import/export is finished
void onComplete();
/// onComplete fires when the export has an error
void onExportError(QString errorString);
/// 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
@ -89,7 +89,6 @@ namespace porting {
/**
* @brief migrateConfig imports the config file associated with the started import
* @throws FileError if the config file cannot be copied
*/
void migrateConfig();

View File

@ -18,7 +18,6 @@
#include <iostream>
#include "appconfig.h"
#include "db/databaseconnection.h"
#include "exceptions/fileerror.h"
#include "forms/getinfo/getinfo.h"
#include "helpers/netman.h"
#include "helpers/screenshot.h"
@ -211,6 +210,7 @@ void TrayManager::onClipboardCapture()
QString clipboardContent = mimeData->text();
if (clipboardContent.isEmpty())
return;
Codeblock evidence(clipboardContent);
if(!Codeblock::saveCodeblock(evidence)) {
setTrayMessage(NO_ACTION, _recordErrorTitle, tr("Error Gathering Evidence from clipboard"), QSystemTrayIcon::Information);
@ -227,30 +227,27 @@ void TrayManager::onClipboardCapture()
return;
}
int evidenceID = 0;
try {
evidenceID = createNewEvidence(path, type);
}
catch (QSqlError& e) {
showDBWriteErrorTrayMessage(e.text());
int evidenceID = createNewEvidence(path, type);
if(evidenceID == -1) {
showDBWriteErrorTrayMessage();
return;
}
spawnGetInfoWindow(evidenceID);
}
void TrayManager::onScreenshotCaptured(const QString& path) {
try {
void TrayManager::onScreenshotCaptured(const QString& path)
{
auto evidenceID = createNewEvidence(path, QStringLiteral("image"));
if(evidenceID == -1) {
showDBWriteErrorTrayMessage();
return;
}
spawnGetInfoWindow(evidenceID);
}
catch (QSqlError& e) {
showDBWriteErrorTrayMessage(e.text());
}
}
void TrayManager::showDBWriteErrorTrayMessage(const QString &errorMessage)
void TrayManager::showDBWriteErrorTrayMessage()
{
setTrayMessage(NO_ACTION, _recordErrorTitle, tr("Could not write to database: %1").arg(errorMessage), QSystemTrayIcon::Warning);
setTrayMessage(NO_ACTION, _recordErrorTitle,tr("Could not write to database"), QSystemTrayIcon::Warning);
}
void TrayManager::showNoOperationSetTrayMessage() {

View File

@ -61,7 +61,7 @@ class TrayManager : public QDialog {
qint64 createNewEvidence(const QString& filepath, const QString& evidenceType);
void spawnGetInfoWindow(qint64 evidenceID);
void showNoOperationSetTrayMessage();
void showDBWriteErrorTrayMessage(const QString &errorMessage = QString());
void showDBWriteErrorTrayMessage();
void checkForUpdate();
void cleanChooseOpSubmenu();
/// setTrayMessage mostly mirrors QSystemTrayIcon::showMessage, but adds the ability to set a message type,