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
parent
45ee47b145
commit
5928942399
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)
|
|
@ -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) {}
|
||||
};
|
|
@ -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());
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue