LogDoctor/logdoctor/modules/craplog/modules/hash.cpp
2024-01-22 00:14:26 +01:00

278 lines
9 KiB
C++

#include "hash.h"
#include "utilities/checks.h"
#include "utilities/gzip.h"
#include "utilities/io.h"
#include "utilities/vectors.h"
#include "modules/dialogs.h"
#include "modules/exceptions.h"
#include "sha256.h"
#include <ios>
#include <QVariant>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
void HashOps::setDialogLevel( const int new_level ) noexcept
{
this->dialog_level = new_level;
}
// reads the database holding the already used hashes
bool HashOps::loadUsedHashesLists( const std::string& db_path ) noexcept
{
bool successful{ true };
const QString db_name{ QString::fromStdString( db_path.substr( db_path.find_last_of( '/' ) + 1ul ) ) };
QSqlDatabase db;
if ( QSqlDatabase::contains("qt_sql_default_connection") ) {
db = QSqlDatabase::database("qt_sql_default_connection");
} else {
db = QSqlDatabase::addDatabase("QSQLITE");
}
db.setDatabaseName( QString::fromStdString( db_path ) );
if ( ! CheckSec::checkDatabaseFile( db_path, db_name ) ) {
successful &= false;
} else if ( ! db.open() ) {
// error opening database
successful &= false;
QString err_msg;
if ( this->dialog_level == 2 ) {
err_msg = db.lastError().text();
}
DialogSec::errDatabaseFailedOpening( db_name, err_msg );
} else {
QSqlQuery query{ db };
for ( const auto& [wid,name] : this->ws_names ) {
if ( ! query.exec("SELECT hash FROM "+name+";") ) {
// error querying database
successful &= false;
DialogSec::errDatabaseFailedExecuting( db_name, query.lastQuery(), query.lastError().text() );
break;
} else {
// iterate over results
while ( query.next() ) {
std::string hash{ query.value(0).toString().toStdString() };
if ( hash.size() != 64ul ) {
// not a valid sha256 hash
continue;
}
this->hashes.at( wid ).push_back( hash );
}
}
if ( ! successful ) { break; }
}
}
if ( db.isOpen() ) {
db.close();
}
return successful;
}
// returns the hash
void HashOps::digestFile( const std::string& file_path, std::string& hash )
{
std::string content;
try {
try {
// try reading as gzip compressed file
GZutils::readFile( file_path, content );
} catch ( const GenericException& ) {
// failed closing file pointer
throw;
} catch (...) {
// failed as gzip, try as text file
if ( content.size() > 0ul ) {
content.clear();
}
IOutils::readFile( file_path, content );
}
// re-catched in craplog
} catch ( const GenericException& ) {
// failed closing gzip file pointer
throw GenericException( QString("%1:\n%2").arg(
DialogSec::tr("An error occured while reading the gzipped file"),
QString::fromStdString( file_path )
).toStdString() );
} catch ( const std::ios_base::failure& ) {
// failed reading as text
throw GenericException( QString("%1:\n%2").arg(
DialogSec::tr("An error occured while reading the file"),
QString::fromStdString( file_path )
).toStdString() );
} catch (...) {
// failed somehow
throw GenericException( QString("%1:\n%2").arg(
DialogSec::tr("Something failed while handling the file"),
QString::fromStdString( file_path )
).toStdString() );
}
SHA256 sha;
sha.update( content );
content.clear();
uint8_t* digest{ sha.digest() };
// return the hex digest
hash.append( SHA256::toString(digest) );
delete digest;
}
// check if the given hash is from a file which has been used already
bool HashOps::hasBeenUsed( const std::string &file_hash, const WebServer& web_server) const noexcept
{
const auto& ws_hashes{ this->hashes.at( web_server ) };
return std::any_of(
ws_hashes.cbegin(), ws_hashes.cend(),
[&file_hash]( const std::string& hash )
{ return file_hash == hash; } );
}
// insert the given hash/es in the relative list
bool HashOps::insertUsedHash( QSqlQuery& query, const QString& db_name, const std::string& hash, const WebServer& web_server ) noexcept
{
bool successful{ true };
try {
if( ! VecOps::contains<std::string>( this->hashes.at( web_server ), hash ) ) {
this->hashes.at( web_server ).push_back( hash );
// insert tnto the database
QString stmt = QString("INSERT INTO %1 ( hash ) VALUES ( '%2' );")
.arg( this->ws_names.at(web_server), QString::fromStdString(hash).replace("'","''") );
if ( ! query.exec( stmt ) ) {
// error opening database
successful &= false;
QString query_msg, err_msg;
if ( this->dialog_level > 0 ) {
query_msg = "query.exec()";
if ( this->dialog_level == 2 ) {
err_msg = query.lastError().text();
}
}
DialogSec::errDatabaseFailedExecuting( db_name, query_msg, err_msg );
}
}/* else {
// hash already stored
}*/
} catch (...) {
// failed to insert the hash
successful &= false;
}
query.finish();
return successful;
}
bool HashOps::insertUsedHashes( const std::string& db_path, const std::vector<std::string>& hashes, const WebServer& web_server )
{
bool successful{ true };
const QString db_name{ QString::fromStdString( db_path.substr( db_path.find_last_of( '/' ) + 1ul ) ) };
QSqlDatabase db{ QSqlDatabase::addDatabase("QSQLITE") };
db.setDatabaseName( QString::fromStdString( db_path ) );
if ( ! CheckSec::checkDatabaseFile( db_path, db_name ) ) {
successful &= false;
} else if ( ! db.open() ) {
// error opening database
successful &= false;
QString err_msg;
if ( this->dialog_level == 2 ) {
err_msg = db.lastError().text();
}
DialogSec::errDatabaseFailedOpening( db_name, err_msg );
} else {
QSqlQuery query{ db };
if ( ! db.transaction() ) {
// error opening database
successful &= false;
QString stmt_msg, err_msg;
if ( this->dialog_level > 0 ) {
stmt_msg = "db.transaction()";
if ( this->dialog_level == 2 ) {
err_msg = db.lastError().text();
}
}
DialogSec::errDatabaseFailedExecuting( db_name, stmt_msg, err_msg );
} else {
try {
for ( const std::string& hash : hashes ) {
successful = this->insertUsedHash( query, db_name, hash, web_server );
if ( ! successful ) {
break;
}
}
query.finish();
if ( successful ) {
// commit the transaction
if ( ! db.commit() ) {
// error opening database
successful &= false;
QString stmt_msg, err_msg;
if ( this->dialog_level > 0 ) {
stmt_msg = "db.commit()";
if ( this->dialog_level == 2 ) {
err_msg= db.lastError().text();
}
}
DialogSec::errDatabaseFailedExecuting( db_name, stmt_msg, err_msg );
}
}
if ( ! successful ) {
// rollback
throw (std::exception());
}
} catch (...) {
// wrongthing w3nt some.,.
successful &= false;
bool err_shown{ false };
// rollback the transaction
if ( ! db.rollback() ) {
// error rolling back commits
QString stmt_msg, err_msg;
if ( this->dialog_level > 0 ) {
stmt_msg = "db.rollback()";
if ( this->dialog_level == 2 ) {
err_msg = db.lastError().text();
}
}
DialogSec::errDatabaseFailedExecuting( db_name, stmt_msg, err_msg );
err_shown |= true;
}
if ( ! err_shown ) {
// show a message
QString msg{ DialogSec::tr("An error occured while working on the database\n\nAborting") };
DialogSec::errGeneric( msg );
}
}
}
}
if ( db.isOpen() ) {
db.close();
}
return successful;
}