Code improvements and updates

Improved Craplog module and its submodules
This commit is contained in:
Valentino Orlandi 2024-02-03 17:44:28 +01:00
parent 5c92b71c37
commit 3158a4e629
Signed by: elB4RTO
GPG key ID: 1719E976DB2D4E71
9 changed files with 262 additions and 394 deletions

View file

@ -186,14 +186,14 @@ void Craplog::warnlistAdd( const WebServer& web_server, const int& log_field_id,
void Craplog::blacklistRemove( const WebServer& web_server, const int& log_field_id, const std::string& item ) noexcept void Craplog::blacklistRemove( const WebServer& web_server, const int& log_field_id, const std::string& item ) noexcept
{ {
auto& list = this->blacklists.at( web_server ).at( log_field_id ).list; auto& list{ this->blacklists.at( web_server ).at( log_field_id ).list };
if ( const auto it{ std::find( list.cbegin(), list.cend(), item ) }; it != list.cend() ) { if ( const auto it{ std::find( list.cbegin(), list.cend(), item ) }; it != list.cend() ) {
list.erase( it ); list.erase( it );
} }
} }
void Craplog::warnlistRemove( const WebServer& web_server, const int& log_field_id, const std::string& item ) noexcept void Craplog::warnlistRemove( const WebServer& web_server, const int& log_field_id, const std::string& item ) noexcept
{ {
auto& list = this->warnlists.at( web_server ).at( log_field_id ).list; auto& list{ this->warnlists.at( web_server ).at( log_field_id ).list };
if ( const auto it{ std::find( list.cbegin(), list.cend(), item ) }; it != list.cend() ) { if ( const auto it{ std::find( list.cbegin(), list.cend(), item ) }; it != list.cend() ) {
list.erase( it ); list.erase( it );
} }
@ -201,7 +201,7 @@ void Craplog::warnlistRemove( const WebServer& web_server, const int& log_field_
int Craplog::blacklistMoveUp( const WebServer& web_server, const int& log_field_id, const std::string& item ) noexcept int Craplog::blacklistMoveUp( const WebServer& web_server, const int& log_field_id, const std::string& item ) noexcept
{ {
auto& list = this->blacklists.at( web_server ).at( log_field_id ).list; auto& list{ this->blacklists.at( web_server ).at( log_field_id ).list };
if ( auto it{ std::find( std::next(list.begin()), list.end(), item ) }; it != list.cend() ) { if ( auto it{ std::find( std::next(list.begin()), list.end(), item ) }; it != list.cend() ) {
const int pos{ static_cast<int>( std::distance(list.begin(), it) ) - 1 }; const int pos{ static_cast<int>( std::distance(list.begin(), it) ) - 1 };
std::swap( *it, *std::prev(it) ); std::swap( *it, *std::prev(it) );
@ -211,7 +211,7 @@ int Craplog::blacklistMoveUp( const WebServer& web_server, const int& log_field_
} }
int Craplog::warnlistMoveUp( const WebServer& web_server, const int& log_field_id, const std::string& item ) noexcept int Craplog::warnlistMoveUp( const WebServer& web_server, const int& log_field_id, const std::string& item ) noexcept
{ {
auto& list = this->warnlists.at( web_server ).at( log_field_id ).list; auto& list{ this->warnlists.at( web_server ).at( log_field_id ).list };
if ( auto it{ std::find( std::next(list.begin()), list.end(), item ) }; it != list.cend() ) { if ( auto it{ std::find( std::next(list.begin()), list.end(), item ) }; it != list.cend() ) {
const int pos{ static_cast<int>( std::distance(list.begin(), it) ) - 1 }; const int pos{ static_cast<int>( std::distance(list.begin(), it) ) - 1 };
std::swap( *it, *std::prev(it) ); std::swap( *it, *std::prev(it) );
@ -222,7 +222,7 @@ int Craplog::warnlistMoveUp( const WebServer& web_server, const int& log_field_i
int Craplog::blacklistMoveDown( const WebServer& web_server, const int& log_field_id, const std::string& item ) noexcept int Craplog::blacklistMoveDown( const WebServer& web_server, const int& log_field_id, const std::string& item ) noexcept
{ {
auto& list = this->blacklists.at( web_server ).at( log_field_id ).list; auto& list{ this->blacklists.at( web_server ).at( log_field_id ).list };
if ( auto it{ std::find( list.begin(), std::prev(list.end()), item ) }; it != list.cend() ) { if ( auto it{ std::find( list.begin(), std::prev(list.end()), item ) }; it != list.cend() ) {
const int pos{ static_cast<int>( std::distance(list.begin(), it) ) + 1 }; const int pos{ static_cast<int>( std::distance(list.begin(), it) ) + 1 };
std::swap( *it, *std::next(it) ); std::swap( *it, *std::next(it) );
@ -232,7 +232,7 @@ int Craplog::blacklistMoveDown( const WebServer& web_server, const int& log_fiel
} }
int Craplog::warnlistMoveDown( const WebServer& web_server, const int& log_field_id, const std::string& item ) noexcept int Craplog::warnlistMoveDown( const WebServer& web_server, const int& log_field_id, const std::string& item ) noexcept
{ {
auto& list = this->warnlists.at( web_server ).at( log_field_id ).list; auto& list{ this->warnlists.at( web_server ).at( log_field_id ).list };
if ( auto it{ std::find( list.begin(), std::prev(list.end()), item ) }; it != list.cend() ) { if ( auto it{ std::find( list.begin(), std::prev(list.end()), item ) }; it != list.cend() ) {
const int pos{ static_cast<int>( std::distance(list.begin(), it) ) + 1 }; const int pos{ static_cast<int>( std::distance(list.begin(), it) ) + 1 };
std::swap( *it, *std::next(it) ); std::swap( *it, *std::next(it) );
@ -243,41 +243,41 @@ int Craplog::warnlistMoveDown( const WebServer& web_server, const int& log_field
std::string Craplog::sanitizeBWitem( const int& log_field_id, const std::string& new_item ) const std::string Craplog::sanitizeBWitem( const int& log_field_id, const std::string& new_item ) const
{ {
std::string sanitized_item;
switch ( log_field_id ) { switch ( log_field_id ) {
case 11: case 11:
sanitized_item = StringOps::strip( new_item ); {
const std::string sanitized_item{ StringOps::strip( new_item ) };
if ( ! StringOps::isAlphabetic( sanitized_item ) ) { if ( ! StringOps::isAlphabetic( sanitized_item ) ) {
// only letters allowed // only letters allowed
throw BWlistException("Invalid Method"); throw BWlistException("Invalid Method");
} }
sanitized_item = StringOps::toUpper( sanitized_item ); return StringOps::toUpper( sanitized_item );
break; }
case 12: case 12:
sanitized_item = StringOps::lstrip( new_item ); {
const std::string sanitized_item{ StringOps::lstrip( new_item ) };
if ( sanitized_item.empty() ) { if ( sanitized_item.empty() ) {
throw BWlistException("Invalid URI"); throw BWlistException("Invalid URI");
} }
sanitized_item = QUrl::toPercentEncoding( return QUrl::toPercentEncoding(
QString::fromStdString( sanitized_item ), QString::fromStdString( sanitized_item ),
"/#&?=+").toStdString(); "/#&?=+").toStdString();
break; }
case 20: case 20:
sanitized_item = StringOps::strip( new_item ); {
const std::string sanitized_item{ StringOps::strip( new_item ) };
if ( ! StringOps::isIP( sanitized_item ) ) { if ( ! StringOps::isIP( sanitized_item ) ) {
// only IPv4/IPv6 allowed // only IPv4/IPv6 allowed
throw BWlistException("Invalid Client"); throw BWlistException("Invalid Client");
} }
break; return sanitized_item;
}
case 21: case 21:
sanitized_item = StringOps::replace( new_item, "\"", "\\\"" ); return StringOps::replace( new_item, "\"", "\\\"" );
break;
default: default:
// shouldn't be here // shouldn't be here
throw GenericException("Unexpected LogField ID: "+std::to_string(log_field_id)); throw GenericException("Unexpected LogField ID: "+std::to_string(log_field_id));
break;
} }
return sanitized_item;
} }
@ -298,55 +298,49 @@ const LogsFormat& Craplog::getLogsFormat(const WebServer& web_server ) const noe
// set the logs format // set the logs format
bool Craplog::setApacheLogFormat( const std::string& format_string ) noexcept bool Craplog::setApacheLogFormat( const std::string& format_string ) noexcept
{ {
// apache
bool success{ true };
try { try {
this->logs_formats.at( WS_APACHE ) = this->logs_formats.at( WS_APACHE ) =
this->formatOps.processApacheFormatString( format_string ); this->formatOps.processApacheFormatString( format_string );
this->logs_format_strings.at( WS_APACHE ) = format_string; this->logs_format_strings.at( WS_APACHE ) = format_string;
} catch ( LogFormatException& e ) { } catch ( LogFormatException& e ) {
success &= false;
DialogSec::errInvalidLogFormatString( e.what() ); DialogSec::errInvalidLogFormatString( e.what() );
return false;
} catch (...) { } catch (...) {
success &= false;
DialogSec::errGeneric( DialogSec::tr("An error occured while parsing the format string"), true ); DialogSec::errGeneric( DialogSec::tr("An error occured while parsing the format string"), true );
return false;
} }
return success; return true;
} }
bool Craplog::setNginxLogFormat( const std::string& format_string ) noexcept bool Craplog::setNginxLogFormat( const std::string& format_string ) noexcept
{ {
// nginx
bool success{ true };
try { try {
this->logs_formats.at( WS_NGINX ) = this->logs_formats.at( WS_NGINX ) =
this->formatOps.processNginxFormatString( format_string ); this->formatOps.processNginxFormatString( format_string );
this->logs_format_strings.at( WS_NGINX ) = format_string; this->logs_format_strings.at( WS_NGINX ) = format_string;
} catch ( LogFormatException& e ) { } catch ( LogFormatException& e ) {
success &= false;
DialogSec::errInvalidLogFormatString( e.what() ); DialogSec::errInvalidLogFormatString( e.what() );
return false;
} catch (...) { } catch (...) {
success &= false;
DialogSec::errGeneric( DialogSec::tr("An error occured while parsing the format string"), true ); DialogSec::errGeneric( DialogSec::tr("An error occured while parsing the format string"), true );
return false;
} }
return success; return true;
} }
bool Craplog::setIisLogFormat( const std::string& format_string, const int log_module ) noexcept bool Craplog::setIisLogFormat( const std::string& format_string, const int log_module ) noexcept
{ {
// iis
bool success{ true };
try { try {
this->logs_formats.at( WS_IIS ) = this->logs_formats.at( WS_IIS ) =
this->formatOps.processIisFormatString( format_string, log_module ); this->formatOps.processIisFormatString( format_string, log_module );
this->logs_format_strings.at( WS_IIS ) = format_string; this->logs_format_strings.at( WS_IIS ) = format_string;
this->changeIisLogsBaseNames( log_module ); this->changeIisLogsBaseNames( log_module );
} catch ( LogFormatException& e ) { } catch ( LogFormatException& e ) {
success &= false;
DialogSec::errInvalidLogFormatString( e.what() ); DialogSec::errInvalidLogFormatString( e.what() );
return false;
} catch (...) { } catch (...) {
success &= false;
DialogSec::errGeneric( DialogSec::tr("An error occured while parsing the format string"), true ); DialogSec::errGeneric( DialogSec::tr("An error occured while parsing the format string"), true );
return false;
} }
return success; return true;
} }
QString Craplog::getLogsFormatSample( const WebServer& web_server ) const QString Craplog::getLogsFormatSample( const WebServer& web_server ) const
@ -437,9 +431,8 @@ const std::vector<LogFile>& Craplog::getLogsList() const noexcept
// return the LogFile instance of the file matching the given name // return the LogFile instance of the file matching the given name
const LogFile& Craplog::getLogFileItem( const QString& file_name ) const const LogFile& Craplog::getLogFileItem( const QString& file_name ) const
{ {
const auto& item{ std::find_if const auto item{ std::find_if( this->logs_list.begin(), this->logs_list.end(),
( this->logs_list.begin(), this->logs_list.end(), [&file_name](const LogFile& file){ return file.name()==file_name; } ) };
[&file_name](const LogFile& it){ return it.name()==file_name; } ) };
if ( item != this->logs_list.end() ) return *item; if ( item != this->logs_list.end() ) return *item;
// should be unreachable // should be unreachable
throw GenericException("File item not found"); throw GenericException("File item not found");
@ -449,10 +442,8 @@ const LogFile& Craplog::getLogFileItem( const QString& file_name ) const
// set a file as selected // set a file as selected
bool Craplog::setLogFileSelected( const QString& file_name ) noexcept bool Craplog::setLogFileSelected( const QString& file_name ) noexcept
{ {
const auto item{ std::find_if const auto item{ std::find_if( this->logs_list.begin(), this->logs_list.end(),
( this->logs_list.begin(), this->logs_list.end(), [&file_name](const LogFile& file){ return file.name() == file_name; } ) };
[&file_name]( const LogFile& it )
{ return it.name() == file_name; } ) };
if ( item != this->logs_list.end() ) { if ( item != this->logs_list.end() ) {
item->setSelected(); item->setSelected();
return true; return true;
@ -539,7 +530,6 @@ void Craplog::changeIisLogsBaseNames( const int module_id )
bool Craplog::isFileNameValid( const std::string& name ) const bool Craplog::isFileNameValid( const std::string& name ) const
{ {
bool valid{ true };
if ( ! this->logs_base_names.at( this->current_web_server ).starts.empty() ) { if ( ! this->logs_base_names.at( this->current_web_server ).starts.empty() ) {
if ( ! StringOps::startsWith( name, this->logs_base_names.at( this->current_web_server ).starts ) ) { if ( ! StringOps::startsWith( name, this->logs_base_names.at( this->current_web_server ).starts ) ) {
return false; return false;
@ -561,13 +551,13 @@ bool Craplog::isFileNameValid( const std::string& name ) const
switch ( this->current_web_server ) { switch ( this->current_web_server ) {
case WS_APACHE: case WS_APACHE:
[[fallthrough]]; [[fallthrough]];
case WS_NGINX: { case WS_NGINX:
{
// further checks for apache / nginx // further checks for apache / nginx
size_t start, stop; size_t start, stop;
start = name.rfind(".log." ); start = name.rfind(".log." );
if ( start == std::string::npos ) { if ( start == std::string::npos ) {
valid &= false; return false;
break;
} }
start += 5ul; start += 5ul;
stop = name.size()-1ul; stop = name.size()-1ul;
@ -577,19 +567,18 @@ bool Craplog::isFileNameValid( const std::string& name ) const
// serach for incremental numbers // serach for incremental numbers
for ( size_t i{start}; i<=stop; ++i ) { for ( size_t i{start}; i<=stop; ++i ) {
if ( ! CharOps::isNumeric( name.at( i ) ) ) { if ( ! CharOps::isNumeric( name.at( i ) ) ) {
valid &= false; return false;
break;
} }
} }
}break; }break;
case WS_IIS: { case WS_IIS:
{
// further checks for iis // further checks for iis
size_t start, stop; size_t start, stop;
start = name.find( this->logs_base_names.at( WS_IIS ).contains ) + 3ul; start = name.find( this->logs_base_names.at( WS_IIS ).contains ) + 3ul;
if ( start == std::string::npos ) { if ( start == std::string::npos ) {
valid &= false; return false;
break;
} }
stop = name.size()-5ul; // removing the finel '.log' extension stop = name.size()-5ul; // removing the finel '.log' extension
if ( StringOps::endsWith( name, ".gz" ) ) { if ( StringOps::endsWith( name, ".gz" ) ) {
@ -599,34 +588,31 @@ bool Craplog::isFileNameValid( const std::string& name ) const
std::string date; std::string date;
for ( size_t i{start}; i<=stop; ++i ) { for ( size_t i{start}; i<=stop; ++i ) {
if ( ! CharOps::isNumeric( name.at( i ) ) ) { if ( ! CharOps::isNumeric( name.at( i ) ) ) {
valid &= false; return false;
break;
} }
date.push_back( name.at( i ) ); date.push_back( name.at( i ) );
} }
if ( valid ) { // check if the file has today's date
// check if the file has today's date time_t t;
time_t t; time( &t );
time( &t ); struct tm* tmp = localtime( &t );
struct tm* tmp = localtime( &t ); char aux_date[7];
char aux_date[7]; // using strftime to display time
// using strftime to display time strftime( aux_date, 7, "%y%m%d", tmp );
strftime( aux_date, 7, "%y%m%d", tmp ); for ( size_t i{0}; i<6ul; ++i ) {
valid &= false; if ( date.at(i) != aux_date[i] ) {
for ( size_t i{0}; i<6ul; ++i ) { // different date, valid
if ( date.at(i) != aux_date[i] ) { return true;
// different date, valid break;
valid |= true;
break;
}
} }
} }
}break; }break;
default: default:
throw WebServerException( "Unexpected WebServer: " + toString(this->current_web_server) ); throw WebServerException( "Unexpected WebServer: " + toString(this->current_web_server) );
} }
return valid;
return true;
} }
@ -650,8 +636,6 @@ bool Craplog::checkStuff()
size_t logs_size{ 0ul }; size_t logs_size{ 0ul };
for ( const LogFile& file : this->logs_list ) { for ( const LogFile& file : this->logs_list ) {
if ( ! this->proceed ) { break; }
if ( ! file.isSelected() ) { if ( ! file.isSelected() ) {
// not selected, skip // not selected, skip
continue; continue;
@ -668,7 +652,7 @@ bool Craplog::checkStuff()
if ( choice == 0 ) { if ( choice == 0 ) {
// choosed to abort all // choosed to abort all
this->proceed &= false; this->proceed &= false;
break; return false;
} else if ( choice == 1 ) { } else if ( choice == 1 ) {
// choosed to discard the file and continue // choosed to discard the file and continue
continue; continue;
@ -689,18 +673,18 @@ bool Craplog::checkStuff()
} }
const int choice = DialogSec::choiceDuplicateFile( msg ); const int choice = DialogSec::choiceDuplicateFile( msg );
if ( choice == 0 ) { if ( choice == 0 ) {
// choosed to abort all // choosed to abort all
this->proceed &= false; this->proceed &= false;
break; return false;
} else if ( choice == 1 ) { } else if ( choice == 1 ) {
// choosed to discard the file and continue // choosed to discard the file and continue
continue; continue;
} else if ( choice == 2 ) { } else if ( choice == 2 ) {
// choosed to ignore and use the file anyway // choosed to ignore and use the file anyway
; ;
} else { } else {
// shouldn't be here // shouldn't be here
throw GenericException( "Unexpeced value returned: "+std::to_string(choice) ); throw GenericException( "Unexpeced value returned: "+std::to_string(choice) );
} }
} }
} }
@ -711,11 +695,11 @@ bool Craplog::checkStuff()
// exceeds the warning size // exceeds the warning size
QString msg{ file.name() }; QString msg{ file.name() };
if ( this->dialogs_level >= DL_NORMAL ) { if ( this->dialogs_level >= DL_NORMAL ) {
msg += QString("\n\n%1:\n%2").arg( msg += QStringLiteral("\n\n%1:\n%2").arg(
DialogSec::tr("Size of the file"), DialogSec::tr("Size of the file"),
PrintSec::printableSize( file.size() ) ); PrintSec::printableSize( file.size() ) );
if ( this->dialogs_level == DL_EXPLANATORY ) { if ( this->dialogs_level == DL_EXPLANATORY ) {
msg += QString("\n\n%1:\n%2").arg( msg += QStringLiteral("\n\n%1:\n%2").arg(
DialogSec::tr("Warning size parameter"), DialogSec::tr("Warning size parameter"),
PrintSec::printableSize( this->warning_size ) ); PrintSec::printableSize( this->warning_size ) );
} }
@ -724,7 +708,7 @@ bool Craplog::checkStuff()
if ( choice == 0 ) { if ( choice == 0 ) {
// choosed to abort all // choosed to abort all
this->proceed &= false; this->proceed &= false;
break; return false;
} else if ( choice == 1 ) { } else if ( choice == 1 ) {
// choosed to discard the file and continue // choosed to discard the file and continue
continue; continue;
@ -742,12 +726,12 @@ bool Craplog::checkStuff()
if ( ! CheckSec::checkCollectionDatabase( this->db_stats_path ) ) { if ( ! CheckSec::checkCollectionDatabase( this->db_stats_path ) ) {
// checks failed, abort // checks failed, abort
this->proceed &= false; this->proceed &= false;
break; return false;
} }
if ( ! CheckSec::checkHashesDatabase( this->db_hashes_path ) ) { if ( ! CheckSec::checkHashesDatabase( this->db_hashes_path ) ) {
// checks failed, abort // checks failed, abort
this->proceed &= false; this->proceed &= false;
break; return false;
} }
this->log_files_to_use.push_back( this->log_files_to_use.push_back(
@ -758,31 +742,33 @@ bool Craplog::checkStuff()
} }
// check if there are enough files to use // check if there are enough files to use
if ( this->proceed && this->log_files_to_use.size() == 0ul ) { if ( this->log_files_to_use.empty() ) {
// no files left, abort // no files left, abort
DialogSec::msgNoFileToParse(); DialogSec::msgNoFileToParse();
this->proceed &= false; this->proceed &= false;
return false;
} }
// check if the total size of the files do not exceed the available RAM // check if the total size of the files do not exceed the available RAM
if ( this->proceed && logs_size >= MemOps::availableMemory() ) { if ( logs_size >= MemOps::availableMemory() ) {
// no files left, abort // no files left, abort
QString msg; QString msg;
if ( this->dialogs_level >= DL_NORMAL ) { if ( this->dialogs_level >= DL_NORMAL ) {
msg += QString("\n\n%1: %2").arg( msg += QStringLiteral("\n\n%1: %2").arg(
DialogSec::tr("Available memory"), DialogSec::tr("Available memory"),
PrintSec::printableSize( MemOps::availableMemory() ) ); PrintSec::printableSize( MemOps::availableMemory() ) );
if ( this->dialogs_level == DL_EXPLANATORY ) { if ( this->dialogs_level == DL_EXPLANATORY ) {
msg += QString("\n%1: %2").arg( msg += QStringLiteral("\n%1: %2").arg(
DialogSec::tr("Size of the logs"), DialogSec::tr("Size of the logs"),
PrintSec::printableSize( logs_size ) ); PrintSec::printableSize( logs_size ) );
} }
} }
DialogSec::msgNotEnoughMemory( msg ); DialogSec::msgNotEnoughMemory( msg );
this->proceed &= false; this->proceed &= false;
return false;
} }
return this->proceed; return true;
} }
void Craplog::showWorkerDialog( const WorkerDialog dialog_type, const QStringList args ) const noexcept void Craplog::showWorkerDialog( const WorkerDialog dialog_type, const QStringList args ) const noexcept
@ -800,6 +786,18 @@ void Craplog::showWorkerDialog( const WorkerDialog dialog_type, const QStringLis
case WorkerDialog::errFailedParsingLogs: case WorkerDialog::errFailedParsingLogs:
DialogSec::errFailedParsingLogs( args.at(0) ); DialogSec::errFailedParsingLogs( args.at(0) );
break; break;
case WorkerDialog::errDatabaseFileNotFound:
DialogSec::errDatabaseNotFound( args.at(0) );
break;
case WorkerDialog::errDatabaseFileNotFile:
DialogSec::errDatabaseNotFile( args.at(0) );
break;
case WorkerDialog::errDatabaseFileNotReadable:
DialogSec::errDatabaseNotReadable( args.at(0) );
break;
case WorkerDialog::errDatabaseFileNotWritable:
DialogSec::errDatabaseNotWritable( args.at(0) );
break;
case WorkerDialog::errDatabaseFailedOpening: case WorkerDialog::errDatabaseFailedOpening:
if ( args.size() < 2 ) { if ( args.size() < 2 ) {
// do not throw, just print to stderr // do not throw, just print to stderr
@ -886,7 +884,11 @@ void Craplog::stopWorking( const bool successful )
this->db_edited = successful; this->db_edited = successful;
if ( successful ) { if ( successful ) {
// insert the hashes of the used files // insert the hashes of the used files
this->hashOps.insertUsedHashes( this->db_hashes_path, this->used_files_hashes, this->current_web_server ); try {
this->hashOps.insertUsedHashes( this->db_hashes_path, this->used_files_hashes, this->current_web_server );
} catch (...) {
DialogSec::errFailedInsertUsedHashes();
}
} }
emit this->finishedWorking(); emit this->finishedWorking();
} }

View file

@ -20,7 +20,7 @@ namespace /*private*/
\return The number of new lines in a single log line \return The number of new lines in a single log line
\see LogsFormat, processApacheFormatString(), processNginxFormatString() \see LogsFormat, processApacheFormatString(), processNginxFormatString()
*/ */
unsigned countNewLines( std::string_view initial, std::string_view final, const std::vector<std::string>& separators ) size_t countNewLines( std::string_view initial, std::string_view final, const std::vector<std::string>& separators )
{ {
size_t nl{ 0ul }; size_t nl{ 0ul };
nl += StringOps::count( initial, '\n' ); nl += StringOps::count( initial, '\n' );
@ -28,7 +28,7 @@ unsigned countNewLines( std::string_view initial, std::string_view final, const
for ( const std::string& sep : separators ) { for ( const std::string& sep : separators ) {
nl += StringOps::count( sep, '\n' ); nl += StringOps::count( sep, '\n' );
} }
return static_cast<unsigned>(nl); return nl;
} }

View file

@ -11,14 +11,13 @@
#include "modules/dialogs.h" #include "modules/dialogs.h"
#include "modules/exceptions.h" #include "modules/exceptions.h"
#include "modules/database/database.h"
#include "sha256.h" #include "sha256.h"
#include <ios> #include <ios>
#include <QVariant> #include <QVariant>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
void HashOps::setDialogLevel( const DialogsLevel new_level ) noexcept void HashOps::setDialogLevel( const DialogsLevel new_level ) noexcept
@ -30,51 +29,29 @@ void HashOps::setDialogLevel( const DialogsLevel new_level ) noexcept
// reads the database holding the already used hashes // reads the database holding the already used hashes
bool HashOps::loadUsedHashesLists( const std::string& db_path ) noexcept bool HashOps::loadUsedHashesLists( const std::string& db_path ) noexcept
{ {
bool successful{ true }; DatabaseWrapper db{ DatabaseHandler::get( DatabaseType::Hashes ) };
const QString db_name{ QString::fromStdString( db_path.substr( db_path.find_last_of( '/' ) + 1ul ) ) };
QSqlDatabase db{ QSqlDatabase::database(DatabasesNames::hashes) }; db.open( db_path, this->dialogs_level==DL_EXPLANATORY );
db.setDatabaseName( QString::fromStdString( db_path ) );
if ( ! CheckSec::checkDatabaseFile( db_path, db_name ) ) { const QString stmt{ QStringLiteral(R"(SELECT "hash" FROM "%1";)") };
successful &= false;
} else if ( ! db.open() ) { for ( const auto& [wid,name] : this->ws_names ) {
// error opening database
successful &= false;
QString err_msg;
if ( this->dialogs_level == DL_EXPLANATORY ) {
err_msg = db.lastError().text();
}
DialogSec::errDatabaseFailedOpening( db_name, err_msg );
} else { QueryWrapper query{ db.getQuery() };
QSqlQuery query{ db };
for ( const auto& [wid,name] : this->ws_names ) { query( stmt.arg( name ) );
if ( ! query.exec("SELECT hash FROM "+name+";") ) {
// error querying database while ( query->next() ) {
successful &= false; const QString hash{ query[0].toString() };
DialogSec::errDatabaseFailedExecuting( db_name, query.lastQuery(), query.lastError().text() ); if ( hash.size() != 64ul ) {
break; // not a valid sha256 hash
} else { continue;
// 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; } this->hashes.at( wid ).push_back( hash.toStdString() );
} }
} }
if ( db.isOpen() ) { return true;
db.close();
}
return successful;
} }
@ -93,7 +70,7 @@ void HashOps::digestFile( const std::string& file_path, std::string& hash )
} catch (...) { } catch (...) {
// failed as gzip, try as text file // failed as gzip, try as text file
if ( content.size() > 0ul ) { if ( ! content.empty() ) {
content.clear(); content.clear();
} }
IOutils::readFile( file_path, content ); IOutils::readFile( file_path, content );
@ -124,7 +101,6 @@ void HashOps::digestFile( const std::string& file_path, std::string& hash )
SHA256 sha; SHA256 sha;
sha.update( content ); sha.update( content );
content.clear();
uint8_t* digest{ sha.digest() }; uint8_t* digest{ sha.digest() };
// return the hex digest // return the hex digest
hash.append( SHA256::toString(digest) ); hash.append( SHA256::toString(digest) );
@ -143,133 +119,35 @@ bool HashOps::hasBeenUsed( const std::string &file_hash, const WebServer& web_se
} }
// insert the given hash/es in the relative list void HashOps::insertUsedHashes( const std::string& db_path, const std::vector<std::string>& hashes, const WebServer& web_server )
bool HashOps::insertUsedHash( QSqlQuery& query, const QString& db_name, const std::string& hash, const WebServer& web_server ) noexcept
{ {
bool successful{ true }; const bool explain_msg{ this->dialogs_level > DL_ESSENTIAL };
const bool explain_err{ this->dialogs_level == DL_EXPLANATORY };
DatabaseWrapper db{ DatabaseHandler::get( DatabaseType::Hashes ) };
db.open( db_path, explain_err );
db.startTransaction( explain_msg, explain_err );
try { try {
if( ! VecOps::contains<std::string>( this->hashes.at( web_server ), hash ) ) {
this->hashes.at( web_server ).push_back( hash ); for ( const std::string& hash : hashes ) {
// insert tnto the database
QString stmt = QString("INSERT INTO %1 ( hash ) VALUES ( '%2' );") if ( VecOps::contains( this->hashes.at( web_server ), hash ) ) {
.arg( this->ws_names.at(web_server), QString::fromStdString(hash).replace("'","''") );
if ( ! query.exec( stmt ) ) { db.getQuery()( QStringLiteral(R"(INSERT INTO "%1" ( hash ) VALUES ( '%2' );)")
// error opening database .arg( this->ws_names.at(web_server), QString::fromStdString(hash).replace(QLatin1Char('\''),QLatin1String("''")) ) );
successful &= false;
QString query_msg, err_msg;
if ( this->dialogs_level > DL_ESSENTIAL ) {
query_msg = "query.exec()";
if ( this->dialogs_level == DL_EXPLANATORY ) {
err_msg = query.lastError().text();
}
}
DialogSec::errDatabaseFailedExecuting( db_name, query_msg, err_msg );
} }
}/* else { }
// hash already stored
}*/ db.commitTransaction( explain_msg, explain_err );
auto& used_hashes{ this->hashes.at( web_server ) };
used_hashes.insert( used_hashes.end(), hashes.begin(), hashes.end() );
} catch (...) { } catch (...) {
// failed to insert the hash // rollback the transaction
successful &= false; db.rollbackTransaction( explain_msg, explain_err );
} }
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 };
QSqlDatabase db{ QSqlDatabase::database(DatabasesNames::hashes) };
db.setDatabaseName( QString::fromStdString( db_path ) );
const QString db_name{ QString::fromStdString( db_path.substr( db_path.find_last_of( '/' ) + 1ul ) ) };
if ( ! CheckSec::checkDatabaseFile( db_path, db_name ) ) {
successful &= false;
} else if ( ! db.open() ) {
// error opening database
successful &= false;
QString err_msg;
if ( this->dialogs_level == DL_EXPLANATORY ) {
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->dialogs_level > DL_ESSENTIAL ) {
stmt_msg = "db.transaction()";
if ( this->dialogs_level == DL_EXPLANATORY ) {
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->dialogs_level > DL_ESSENTIAL ) {
stmt_msg = "db.commit()";
if ( this->dialogs_level == DL_EXPLANATORY ) {
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->dialogs_level > DL_ESSENTIAL ) {
stmt_msg = "db.rollback()";
if ( this->dialogs_level == DL_EXPLANATORY ) {
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;
} }

View file

@ -30,6 +30,7 @@ public:
/*! /*!
\param db_path The path of the log files' Hashes database \param db_path The path of the log files' Hashes database
\return Whether the operation has been successful or not \return Whether the operation has been successful or not
\throw LogDoctorException
*/ */
bool loadUsedHashesLists( const std::string& db_path ) noexcept; bool loadUsedHashesLists( const std::string& db_path ) noexcept;
@ -55,9 +56,9 @@ public:
\param db_path The path of the Hashes database \param db_path The path of the Hashes database
\param hashes The list of hashes to insert \param hashes The list of hashes to insert
\param web_server_id The ID of the Web Server which generated the file \param web_server_id The ID of the Web Server which generated the file
\return Whether the operation has been successful or not \throw LogDoctorException
*/ */
bool insertUsedHashes( const std::string& db_path, const std::vector<std::string>& hashes, const WebServer& web_server ); void insertUsedHashes( const std::string& db_path, const std::vector<std::string>& hashes, const WebServer& web_server );
private: private:
@ -79,11 +80,6 @@ private:
{WS_IIS, {}} {WS_IIS, {}}
}; };
// Called by insertUsedHashes()
// Inserts a hash in the corresponding database table
bool insertUsedHash( QSqlQuery& query, const QString& db_name, const std::string& hash, const WebServer& web_server ) noexcept;
}; };

View file

@ -28,9 +28,12 @@ enum class LogType
//! Holds informations about a log file //! Holds informations about a log file
struct LogFile final { struct LogFile final {
explicit LogFile() noexcept = default; explicit LogFile() noexcept = default;
explicit LogFile explicit LogFile(const bool sel,const bool used,const size_t sz,const QString& nm,const std::string& hs,const std::string& pt) noexcept
(const bool sel,const bool used,const size_t sz,const QString& nm,const std::string& hs,const std::string& pt) noexcept
:selected{sel},used_already{used},size_{sz},name_{nm},hash_{hs},path_{pt}{} :selected{sel},used_already{used},size_{sz},name_{nm},hash_{hs},path_{pt}{}
LogFile(LogFile&& other) noexcept = default;
LogFile& operator=(LogFile&& other) noexcept = default;
LogFile(const LogFile& other) noexcept = default;
LogFile& operator=(const LogFile& other) noexcept = default;
//! Wheter the file has been selected to be used or not //! Wheter the file has been selected to be used or not
inline bool isSelected() const noexcept inline bool isSelected() const noexcept
{ return this->selected; } { return this->selected; }

View file

@ -98,15 +98,10 @@ bool deepTypeCheck( const std::string& line, const LogsFormat& format ) noexcept
// add the initial and final seps now // add the initial and final seps now
n_sep += 2; n_sep += 2;
// the result is considered ture if more then a half of the seps was found // the line is considered valid if all the seps have been found
// and more than a half of the found separators was not blank // and more than a half of them were not blank
bool result{ false }; return n_sep_found >= n_sep-1
if ( n_sep_found >= n_sep-1 && n_blank_sep <= n_sep_found/2;
&& n_blank_sep <= n_sep_found/2 ) {
result |= true;
}
return result;
} }
} // namespace (private) } // namespace (private)

View file

@ -14,6 +14,10 @@ enum class WorkerDialog {
errDirNotExists, errDirNotExists,
errFailedDefiningLogType, errFailedDefiningLogType,
errFailedParsingLogs, errFailedParsingLogs,
errDatabaseFileNotFound,
errDatabaseFileNotFile,
errDatabaseFileNotReadable,
errDatabaseFileNotWritable,
errDatabaseFailedOpening, errDatabaseFailedOpening,
errDatabaseFailedExecuting, errDatabaseFailedExecuting,
warnFileNotReadable, warnFileNotReadable,

View file

@ -12,10 +12,10 @@
#include "modules/dialogs.h" #include "modules/dialogs.h"
#include "modules/exceptions.h" #include "modules/exceptions.h"
#include "modules/database/database.h"
#include "modules/craplog/modules/workers/lib.h" #include "modules/craplog/modules/workers/lib.h"
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError> #include <QSqlError>
@ -84,6 +84,7 @@ void CraplogParser::work()
emit this->showDialog( WorkerDialog::errFailedParsingLogs, emit this->showDialog( WorkerDialog::errFailedParsingLogs,
{e.what()} ); {e.what()} );
this->proceed &= false; this->proceed &= false;
} }
// send the final data // send the final data
if ( ! this->proceed ) { if ( ! this->proceed ) {
@ -187,11 +188,18 @@ void CraplogParser::joinLogLines()
void CraplogParser::parseLogLines() void CraplogParser::parseLogLines()
{ {
const auto parseLine = [this]( const std::string& line, const LogsFormat& logs_format ) { const auto parseLine{ [this]( const std::string& line, const LogsFormat& logs_format ) {
this->data_collection.emplace_back( LogLineData(line, logs_format) ); this->data_collection.emplace_back( LogLineData(line, logs_format) );
this->parsed_size += line.size(); this->parsed_size += line.size();
++ this->parsed_lines; ++ this->parsed_lines;
}; }};
const auto signal_emission_gap{ [](const size_t n_lines)->size_t{
return n_lines>10000ul ? n_lines/1000ul
: n_lines>1000ul ? n_lines/100ul
: n_lines>100ul ? n_lines/10ul
: 10ul;
}};
// parse all the lines // parse all the lines
@ -200,20 +208,19 @@ void CraplogParser::parseLogLines()
const size_t nl{ this->logs_format.new_lines }; const size_t nl{ this->logs_format.new_lines };
size_t send{ 0ul }; size_t send{ 0ul };
if ( nl == 0ul ) { if ( nl == 0ul ) {
const size_t send_gap{ n_lines>1000ul ? n_lines/100 : n_lines>100ul ? n_lines/10 : 10 }; const size_t send_gap{ signal_emission_gap(n_lines) };
const LogsFormat& lf {this->logs_format}; const LogsFormat& lf {this->logs_format};
this->data_collection.reserve( n_lines ); this->data_collection.reserve( n_lines );
for ( const std::string& line : this->logs_lines ) { for ( const std::string& line : this->logs_lines ) {
parseLine( line, lf ); parseLine( line, lf );
if (send == send_gap) { if (++send == send_gap) {
this->sendPerfData(); this->sendPerfData();
send = 0ul; send = 0ul;
} }
++send;
} }
} else { } else {
const size_t real_lines{ n_lines / (nl+1ul) }; const size_t real_lines{ n_lines / (nl+1ul) };
const size_t send_gap{ real_lines>1000ul ? real_lines/100 : real_lines>100ul ? real_lines/10 : 10 }; const size_t send_gap{ signal_emission_gap(real_lines) };
const LogsFormat& lf {this->logs_format}; const LogsFormat& lf {this->logs_format};
this->data_collection.reserve( real_lines ); this->data_collection.reserve( real_lines );
for ( size_t i{0ul}; i<n_lines; ++i ) { for ( size_t i{0ul}; i<n_lines; ++i ) {
@ -223,11 +230,10 @@ void CraplogParser::parseLogLines()
line += "\n" + this->logs_lines.at( i ); line += "\n" + this->logs_lines.at( i );
} }
parseLine( line, lf ); parseLine( line, lf );
if (send == send_gap) { if (++send == send_gap) {
this->sendPerfData(); this->sendPerfData();
send = 0ul; send = 0ul;
} }
++send;
} }
} }
this->sendPerfData(); this->sendPerfData();
@ -241,136 +247,116 @@ void CraplogParser::storeLogLines()
QString db_path{ QString::fromStdString( this->db_data_path ) }; QString db_path{ QString::fromStdString( this->db_data_path ) };
QString db_name{ QString::fromStdString( this->db_data_path.substr( this->db_data_path.find_last_of( '/' ) + 1ul ) ) }; QString db_name{ QString::fromStdString( this->db_data_path.substr( this->db_data_path.find_last_of( '/' ) + 1ul ) ) };
QSqlDatabase db{ QSqlDatabase::database(DatabasesNames::data) }; DatabaseWrapper db{ DatabaseHandler::get( DatabaseType::Hashes ) };
db.setDatabaseName( db_path );
if ( ! CheckSec::checkDatabaseFile( this->db_data_path, db_name ) ) { db->setDatabaseName( db_path );
if ( ! this->checkDatabaseFile( db_name ) ) [[unlikely]] {
this->proceed &= false; this->proceed &= false;
return;
}
} else if ( ! db.open() ) { if ( ! db->open() ) [[unlikely]] {
// error opening database
this->proceed &= false; this->proceed &= false;
QString err_msg; QString err_msg;
if ( this->dialogs_level == DL_EXPLANATORY ) { if ( this->dialogs_level == DL_EXPLANATORY ) {
err_msg = db.lastError().text(); err_msg = db->lastError().text();
} }
emit this->showDialog( WorkerDialog::errDatabaseFailedOpening, emit this->showDialog( WorkerDialog::errDatabaseFailedOpening,
{db_name, err_msg} ); {db_name, err_msg} );
return;
}
} else { try {
try { if ( ! db->transaction() ) [[unlikely]] {
// ACID transaction this->proceed &= false;
if ( ! db.transaction() ) { QString stmt_msg, err_msg;
// error opening database if ( this->dialogs_level > DL_ESSENTIAL ) {
stmt_msg.append( "db.transaction()" );
if ( this->dialogs_level == DL_EXPLANATORY ) {
err_msg = db->lastError().text();
}
}
emit this->showDialog( WorkerDialog::errDatabaseFailedExecuting,
{db_name, stmt_msg, err_msg} );
return;
} else if ( this->storeData( db, db_name ) ) [[likely]] {
if ( ! db->commit() ) [[unlikely]] {
this->proceed &= false; this->proceed &= false;
QString stmt_msg, err_msg; QString stmt_msg, err_msg;
if ( this->dialogs_level > DL_ESSENTIAL ) { if ( this->dialogs_level > DL_ESSENTIAL ) {
stmt_msg = "db.transaction()"; stmt_msg.append( "db.commit()" );
if ( this->dialogs_level == DL_EXPLANATORY ) { if ( this->dialogs_level == DL_EXPLANATORY ) {
err_msg = db.lastError().text(); err_msg.append( db->lastError().text() );
} }
} }
emit this->showDialog( WorkerDialog::errDatabaseFailedExecuting, emit this->showDialog( WorkerDialog::errDatabaseFailedExecuting,
{db_name, stmt_msg, err_msg} ); {db_name, stmt_msg, err_msg} );
} }
}
if ( this->proceed ) { if ( ! this->proceed ) [[unlikely]] {
this->proceed &= this->storeData( db );
}
if ( this->proceed ) {
// commit the transaction
if ( ! db.commit() ) {
// error opening database
this->proceed &= false;
QString stmt_msg, err_msg;
if ( this->dialogs_level > DL_ESSENTIAL ) {
stmt_msg = "db.commit()";
if ( this->dialogs_level == DL_EXPLANATORY ) {
err_msg= db.lastError().text();
}
}
emit this->showDialog( WorkerDialog::errDatabaseFailedExecuting,
{db_name, stmt_msg, err_msg} );
}
}
if ( ! this->proceed ) {
// rollback
throw (std::exception());
}
} catch (...) {
// wrongthing w3nt some.,.
this->proceed &= false;
bool err_shown = false;
// rollback the transaction // rollback the transaction
if ( ! db.rollback() ) { if ( ! db->rollback() ) {
// error rolling back commits
QString stmt_msg, err_msg; QString stmt_msg, err_msg;
if ( this->dialogs_level > DL_ESSENTIAL ) { if ( this->dialogs_level > DL_ESSENTIAL ) {
stmt_msg = "db.rollback()"; stmt_msg = "db.rollback()";
if ( this->dialogs_level == DL_EXPLANATORY ) { if ( this->dialogs_level == DL_EXPLANATORY ) {
err_msg = db.lastError().text(); err_msg = db->lastError().text();
} }
} }
emit this->showDialog( WorkerDialog::errDatabaseFailedExecuting, emit this->showDialog( WorkerDialog::errDatabaseFailedExecuting,
{db_name, stmt_msg, err_msg} ); {db_name, stmt_msg, err_msg} );
err_shown = true; return;
}
if ( ! err_shown ) {
// show a message
emit this->showDialog(
WorkerDialog::errGeneric,
{QString("%1\n\n%2").arg(
DialogSec::tr("An error occured while working on the database"),
DialogSec::tr("Aborting") )} );
} }
} }
if ( db.isOpen() ) { } catch (...) {
db.close(); // wrongthing w3nt some.,.
} emit this->showDialog(
WorkerDialog::errGeneric,
{QStringLiteral("%1\n\n%2").arg(
DialogSec::tr("An error occured while working on the database"),
DialogSec::tr("Aborting") )} );
} }
} }
#define APPEND_TO_QUERY_AS_NUMBER(LOG_FIELD)\ #define APPEND_TO_QUERY_AS_NUMBER(LOG_FIELD)\
if ( LOG_FIELD ) {\ if ( LOG_FIELD ) {\
query_stmt += QString::fromStdString( *LOG_FIELD ).replace("'","''");\ stmt.append( QString::fromStdString( *LOG_FIELD ).replace(QLatin1Char('\''),QLatin1String("''")) );\
} else {\ } else {\
query_stmt += QStringLiteral("NULL");\ stmt.append( QStringLiteral("NULL") );\
} }
#define CONCAT_TO_QUERY_AS_NUMBER(LOG_FIELD)\ #define CONCAT_TO_QUERY_AS_NUMBER(LOG_FIELD)\
query_stmt += QStringLiteral(", ");\ stmt.append( QStringLiteral(", ") );\
APPEND_TO_QUERY_AS_NUMBER(LOG_FIELD) APPEND_TO_QUERY_AS_NUMBER(LOG_FIELD)
#define CONCAT_TO_QUERY_AS_STRING(LOG_FIELD)\ #define CONCAT_TO_QUERY_AS_STRING(LOG_FIELD)\
query_stmt += QStringLiteral(", ");\ stmt.append( QStringLiteral(", ") );\
if ( LOG_FIELD ) {\ if ( LOG_FIELD ) {\
query_stmt += QString("'%1'").arg( QString::fromStdString( *LOG_FIELD ).replace("'","''") );\ stmt.append( QString("'%1'").arg( QString::fromStdString( *LOG_FIELD ).replace(QLatin1Char('\''),QLatin1String("''")) ) );\
} else {\ } else {\
query_stmt += QStringLiteral("NULL");\ stmt.append( QStringLiteral("NULL") );\
} }
// in IIS logs the user-agent is logged with '+' instead of ' ' (whitespace) // in IIS logs the user-agent is logged with '+' instead of ' ' (whitespace)
#define CONCAT_TO_QUERY_USERAGENT(LOG_FIELD)\ #define CONCAT_TO_QUERY_USERAGENT(LOG_FIELD)\
query_stmt += QStringLiteral(", ");\ stmt.append( QStringLiteral(", ") );\
if ( LOG_FIELD ) {\ if ( LOG_FIELD ) {\
if ( this->web_server == WS_IIS ) {\ if ( this->web_server == WS_IIS ) {\
query_stmt += QString("'%1'").arg( QString::fromStdString( *LOG_FIELD ).replace("+"," ").replace("'","''") );\ stmt.append( QStringLiteral("'%1'").arg( QString::fromStdString( *LOG_FIELD ).replace(QLatin1Char('+'),QLatin1Char(' ')).replace(QLatin1Char('\''),QLatin1String("''")) ) );\
} else {\ } else {\
query_stmt += QString("'%1'").arg( QString::fromStdString( *LOG_FIELD ).replace("'","''") );\ stmt.append( QStringLiteral("'%1'").arg( QString::fromStdString( *LOG_FIELD ).replace(QLatin1Char('\''),QLatin1String("''")) ) );\
}\ }\
} else {\ } else {\
query_stmt += QStringLiteral("NULL");\ stmt.append( QStringLiteral("NULL") );\
} }
bool CraplogParser::storeData( QSqlDatabase& db ) bool CraplogParser::storeData( DatabaseWrapper& db, const QString& db_name )
{ {
const QString db_name{ QString::fromStdString(
this->db_data_path.substr(
this->db_data_path.find_last_of( '/' ) + 1ul ) ) };
// get blacklist items // get blacklist items
const bool check_bl_cli { this->blacklists.at( 20 ).used }; const bool check_bl_cli { this->blacklists.at( 20 ).used };
@ -383,22 +369,25 @@ bool CraplogParser::storeData( QSqlDatabase& db )
QString table; QString table;
switch ( this->web_server ) { switch ( this->web_server ) {
case WS_APACHE: case WS_APACHE:
table += "apache"; table.append( "apache" );
break; break;
case WS_NGINX: case WS_NGINX:
table += "nginx"; table.append( "nginx" );
break; break;
case WS_IIS: case WS_IIS:
table += "iis"; table.append( "iis" );
break; break;
default: default:
// wrong WebServerID, but should be unreachable because of the previous operations // wrong WebServerID, but should be unreachable because of the previous operations
throw WebServerException( "Unexpected WebServer: " + toString(this->web_server) ); throw WebServerException( "Unexpected WebServer: " + toString(this->web_server) );
} }
const QString stmt_template{
QStringLiteral(R"(INSERT INTO "%1" ("year", "month", "day", "hour", "minute", "second", "protocol", "method", "uri", "query", "response", "time_taken", "bytes_sent", "bytes_received", "referrer", "client", "user_agent", "cookie") )"
"VALUES (")
};
/*int perf_size;*/ /*int perf_size;*/
QSqlQuery query{ db };
// parse every row of data // parse every row of data
for ( const LogLineData& line_data : this->data_collection ) { for ( const LogLineData& line_data : this->data_collection ) {
@ -410,9 +399,7 @@ bool CraplogParser::storeData( QSqlDatabase& db )
} }
} }
// initialize the SQL statement QString stmt{ stmt_template.arg( table ) };
QString query_stmt{ "INSERT INTO \""+table+"\" (\"year\", \"month\", \"day\", \"hour\", \"minute\", \"second\", \"protocol\", \"method\", \"uri\", \"query\", \"response\", \"time_taken\", \"bytes_sent\", \"bytes_received\", \"referrer\", \"client\", \"user_agent\", \"cookie\") "
"VALUES (" };
// complete and execute the statement, binding NULL if not found // complete and execute the statement, binding NULL if not found
@ -441,29 +428,13 @@ bool CraplogParser::storeData( QSqlDatabase& db )
CONCAT_TO_QUERY_USERAGENT(line_data.user_agent) // 21 CONCAT_TO_QUERY_USERAGENT(line_data.user_agent) // 21
CONCAT_TO_QUERY_AS_STRING(line_data.cookie) // 22 CONCAT_TO_QUERY_AS_STRING(line_data.cookie) // 22
query_stmt += ");"; stmt.append( ");" );
// encode the statement if ( QSqlQuery query(*db); !query.exec( stmt ) ) [[unlikely]] {
if ( ! query.prepare( query_stmt ) ) {
// error opening database
QString query_msg, err_msg;
if ( this->dialogs_level > DL_ESSENTIAL ) {
query_msg = "query.prepare()";
if ( this->dialogs_level == DL_EXPLANATORY ) {
err_msg = query.lastError().text();
}
}
emit this->showDialog( WorkerDialog::errDatabaseFailedExecuting,
{db_name, query_msg, err_msg} );
return false;
}
// finalize this statement
if ( ! query.exec() ) {
// error finalizing step // error finalizing step
QString query_msg, err_msg; QString query_msg, err_msg;
if ( this->dialogs_level > DL_ESSENTIAL ) { if ( this->dialogs_level > DL_ESSENTIAL ) {
query_msg = "query.exec()"; query_msg.append( "query.exec()" );
if ( this->dialogs_level == DL_EXPLANATORY ) { if ( this->dialogs_level == DL_EXPLANATORY ) {
err_msg = query.lastError().text(); err_msg = query.lastError().text();
} }
@ -472,10 +443,26 @@ bool CraplogParser::storeData( QSqlDatabase& db )
{db_name, query_msg, err_msg} ); {db_name, query_msg, err_msg} );
return false; return false;
} }
// reset the statement to prepare for the next one
query.finish();
} }
return true; return true;
} }
bool CraplogParser::checkDatabaseFile( const QString& db_name ) noexcept
{
if ( ! IOutils::exists( this->db_data_path ) ) {
emit this->showDialog( WorkerDialog::errDatabaseFileNotFound, {db_name} );
return false;
} else if ( ! IOutils::isFile( this->db_data_path ) ) {
emit this->showDialog( WorkerDialog::errDatabaseFileNotFile, {db_name} );
return false;
} else if ( ! IOutils::checkFile( this->db_data_path, true ) ) {
emit this->showDialog( WorkerDialog::errDatabaseFileNotReadable, {db_name} );
return false;
} else if ( ! IOutils::checkFile( this->db_data_path, false, true ) ) {
emit this->showDialog( WorkerDialog::errDatabaseFileNotWritable, {db_name} );
return false;
}
return true;
}

View file

@ -14,9 +14,9 @@
struct BWlist; struct BWlist;
struct LogLineData; struct LogLineData;
enum class WorkerDialog; class DatabaseWrapper;
class QSqlDatabase; enum class WorkerDialog;
class CraplogParser final : public QObject class CraplogParser final : public QObject
@ -86,6 +86,8 @@ private:
std::string db_data_path; std::string db_data_path;
std::string db_hashes_path; std::string db_hashes_path;
bool checkDatabaseFile( const QString& db_name ) noexcept;
////////////////////// //////////////////////
//// PERFORMANCES //// //// PERFORMANCES ////
@ -136,10 +138,11 @@ private:
//! Stores the data collection in the logs Collection database //! Stores the data collection in the logs Collection database
/*! /*!
\param db A database instance, already initizlized \param db A database instance, already initizlized
\param db_name The database in use, to be shown in the dialogs
\return Whether the operation has been successful or not \return Whether the operation has been successful or not
\throw WebServerException \throw WebServerException
*/ */
bool storeData( QSqlDatabase& db ); bool storeData( DatabaseWrapper& db , const QString& db_name );
}; };