Version upgrade 4.00 #45
|
@ -5,10 +5,10 @@
|
||||||
|
|
||||||
#include "modules/dialogs.h"
|
#include "modules/dialogs.h"
|
||||||
|
|
||||||
|
#include "modules/database/database.h"
|
||||||
|
|
||||||
#include "utilities/io.h"
|
#include "utilities/io.h"
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QSqlQuery>
|
|
||||||
#include <QSqlError>
|
#include <QSqlError>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
|
@ -16,6 +16,9 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
|
||||||
|
struct MakeNewDatabase {};
|
||||||
|
|
||||||
|
|
||||||
namespace CheckSec
|
namespace CheckSec
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -24,183 +27,158 @@ namespace /*private*/
|
||||||
|
|
||||||
//! Checks the tables' names integrity
|
//! Checks the tables' names integrity
|
||||||
/*!
|
/*!
|
||||||
\param db Database object, already initialized
|
\param query Query instance from the target database
|
||||||
\param db_name Database's name, used by the dialogs if necessary
|
\return Whether the database is valid or not
|
||||||
\return The result of the check: 0 if failed with an error, 1 if all the integrity checks passed, 2 if a rebuild is needed
|
\throw LogDoctorException, MakeNewDatabase
|
||||||
\see checkCollectionDatabase(), checkHashesDatabase(), newCollectionDatabase(), newHashesDatabase()
|
\see checkCollectionDatabase(), checkHashesDatabase(), newCollectionDatabase(), newHashesDatabase()
|
||||||
*/
|
*/
|
||||||
int checkDatabaseTablesNames( QSqlDatabase& db, const QString& db_name ) noexcept
|
bool checkDatabaseTablesNames( QueryWrapper query )
|
||||||
{
|
{
|
||||||
bool make_new{false}, ok{true};
|
query( QStringLiteral("SELECT name FROM sqlite_schema WHERE type = 'table';") );
|
||||||
QSqlQuery query{ QSqlQuery( db ) };
|
|
||||||
if ( ! query.exec("SELECT name FROM sqlite_schema WHERE type = 'table';") ) {
|
|
||||||
// error querying database
|
|
||||||
ok &= false;
|
|
||||||
DialogSec::errDatabaseFailedExecuting( db_name, query.lastQuery(), query.lastError().text() );
|
|
||||||
} else {
|
|
||||||
std::unordered_map<QString, bool> tables_checks{
|
|
||||||
{"apache", false},
|
|
||||||
{"nginx", false},
|
|
||||||
{"iis", false} };
|
|
||||||
while ( query.next() ) {
|
|
||||||
QString table_name{ query.value(0).toString() };
|
|
||||||
if ( tables_checks.find( table_name ) == tables_checks.end() ) {
|
|
||||||
// unexpected table name
|
|
||||||
if ( DialogSec::choiceDatabaseWrongTable( db_name, table_name ) ) {
|
|
||||||
// agreed to renew
|
|
||||||
make_new |= true;
|
|
||||||
} else {
|
|
||||||
// refused to renew
|
|
||||||
ok &= false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
} else {
|
std::unordered_map<QString, bool> checks{
|
||||||
// table found
|
{"apache", false},
|
||||||
tables_checks.at( table_name ) |= true;
|
{"nginx", false},
|
||||||
}
|
{"iis", false} };
|
||||||
}
|
|
||||||
if ( ok && !make_new ) {
|
while ( query->next() ) {
|
||||||
for ( const auto& [ tbl, res ] : tables_checks ) {
|
|
||||||
if ( ! res ) {
|
const QString table{ query[0].toString() };
|
||||||
// a table has not been found
|
|
||||||
if ( DialogSec::choiceDatabaseMissingTable( db_name, tbl ) ) {
|
if ( const auto tbl{ checks.find( table ) }; tbl != checks.end() ) {
|
||||||
// agreed to renew
|
tbl->second |= true;
|
||||||
make_new |= true;
|
|
||||||
} else {
|
|
||||||
// refused to renew
|
|
||||||
ok &= false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tables_checks.clear();
|
|
||||||
}
|
|
||||||
query.finish();
|
|
||||||
if ( ok ) {
|
|
||||||
if ( make_new ) {
|
|
||||||
return 2;
|
|
||||||
} else {
|
} else {
|
||||||
return 1;
|
// unexpected table name
|
||||||
|
if ( DialogSec::choiceDatabaseWrongTable( query.dbName(), table ) ) {
|
||||||
|
// agreed to renew
|
||||||
|
throw MakeNewDatabase{};
|
||||||
|
} else {
|
||||||
|
// refused to renew
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
for ( const auto& [tbl,res] : checks ) {
|
||||||
|
if ( ! res ) {
|
||||||
|
// a table has not been found
|
||||||
|
if ( DialogSec::choiceDatabaseMissingTable( query.dbName(), tbl ) ) {
|
||||||
|
// agreed to renew
|
||||||
|
throw MakeNewDatabase{};
|
||||||
|
} else {
|
||||||
|
// refused to renew
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//! Builds a new database for the logs Collection
|
//! Builds a new database for the logs Collection
|
||||||
/*!
|
/*!
|
||||||
\param db Database object, already initialized
|
\param db Database object
|
||||||
\param db_name Database's name, used by the dialogs if necessary
|
\param db_path The database file's path
|
||||||
\return The result of the operation
|
\param ws_names Database's tables names
|
||||||
\see checkCollectionDatabase(), checkHashesDatabase()
|
\return The result of the operation
|
||||||
|
\see checkCollectionDatabase(), checkHashesDatabase()
|
||||||
*/
|
*/
|
||||||
bool newCollectionDatabase( QSqlDatabase& db, const QString& db_name, const std::vector<QString>& ws_names ) noexcept
|
bool newCollectionDatabase( DatabaseWrapper db, const std::string& db_path, const std::vector<QString>& ws_names ) noexcept
|
||||||
{
|
{
|
||||||
bool successful{ true };
|
try {
|
||||||
// create the database
|
|
||||||
if ( ! db.open() ) {
|
db.open( db_path, true );
|
||||||
// error opening database
|
|
||||||
successful &= false;
|
|
||||||
DialogSec::errDatabaseFailedOpening( db_name, db.lastError().text() );
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// succesfully creted database file, now create the tables
|
// succesfully creted database file, now create the tables
|
||||||
QSqlQuery query;
|
const QString stmt{ QStringLiteral(R"(
|
||||||
|
CREATE TABLE "%1" (
|
||||||
|
"year" SMALLINT,
|
||||||
|
"month" TINYINT,
|
||||||
|
"day" TINYINT,
|
||||||
|
"hour" TINYINT,
|
||||||
|
"minute" TINYINT,
|
||||||
|
"second" TINYINT,
|
||||||
|
"protocol" TEXT,
|
||||||
|
"method" TEXT,
|
||||||
|
"uri" TEXT,
|
||||||
|
"query" TEXT,
|
||||||
|
"response" SMALLINT,
|
||||||
|
"time_taken" INTEGER,
|
||||||
|
"bytes_sent" INTEGER,
|
||||||
|
"bytes_received" INTEGER,
|
||||||
|
"client" TEXT,
|
||||||
|
"user_agent" TEXT,
|
||||||
|
"cookie" TEXT,
|
||||||
|
"referrer" TEXT
|
||||||
|
);)")};
|
||||||
|
|
||||||
for ( const QString& ws_name : ws_names ) {
|
for ( const QString& ws_name : ws_names ) {
|
||||||
if ( ! successful ) { break; }
|
|
||||||
// compose the statement with the table name for the access logs
|
QueryWrapper query{ db.getQuery() };
|
||||||
query.prepare( "\
|
|
||||||
CREATE TABLE \""+ws_name+"\" (\
|
if ( ! query->exec( stmt.arg( ws_name ) ) ) {
|
||||||
\"year\" SMALLINT,\
|
|
||||||
\"month\" TINYINT,\
|
|
||||||
\"day\" TINYINT,\
|
|
||||||
\"hour\" TINYINT,\
|
|
||||||
\"minute\" TINYINT,\
|
|
||||||
\"second\" TINYINT,\
|
|
||||||
\"protocol\" TEXT,\
|
|
||||||
\"method\" TEXT,\
|
|
||||||
\"uri\" TEXT,\
|
|
||||||
\"query\" TEXT,\
|
|
||||||
\"response\" SMALLINT,\
|
|
||||||
\"time_taken\" INTEGER,\
|
|
||||||
\"bytes_sent\" INTEGER,\
|
|
||||||
\"bytes_received\" INTEGER,\
|
|
||||||
\"client\" TEXT,\
|
|
||||||
\"user_agent\" TEXT,\
|
|
||||||
\"cookie\" TEXT,\
|
|
||||||
\"referrer\" TEXT\
|
|
||||||
);");
|
|
||||||
if ( ! query.exec() ) {
|
|
||||||
// error creating table
|
// error creating table
|
||||||
successful &= false;
|
|
||||||
DialogSec::errDatabaseFailedExecuting(
|
DialogSec::errDatabaseFailedExecuting(
|
||||||
QString( db_name ),
|
db.name(),
|
||||||
QString("CREATE TABLE \"%1\" (...)").arg( ws_name ),
|
QStringLiteral(R"(CREATE TABLE "%1" (...))").arg( ws_name ),
|
||||||
QString( query.lastError().text() ) );
|
query->lastError().text() );
|
||||||
|
throw LogDoctorException{};
|
||||||
}
|
}
|
||||||
query.finish();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// inform about creation
|
} catch (...) {
|
||||||
if ( successful ) {
|
DialogSec::errDatabaseFailedCreating( db.name() );
|
||||||
DialogSec::msgDatabaseCreated( db_name );
|
return false;
|
||||||
} else {
|
|
||||||
DialogSec::errDatabaseFailedCreating( db_name );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return successful;
|
|
||||||
|
DialogSec::msgDatabaseCreated( db.name() );
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//! Builds a new database for the used log files' Hashes
|
//! Builds a new database for the used log files' Hashes
|
||||||
/*!
|
/*!
|
||||||
\param db Database object, already initialized
|
\param db Database object, already initialized
|
||||||
\param db_name Database's name, used by the dialogs if necessary
|
\param db_path The database file's path
|
||||||
\return The result of the operation
|
\param ws_names Database's tables names
|
||||||
\see checkCollectionDatabase(), checkHashesDatabase()
|
\return The result of the operation
|
||||||
|
\see checkCollectionDatabase(), checkHashesDatabase()
|
||||||
*/
|
*/
|
||||||
bool newHashesDatabase( QSqlDatabase& db, const QString& db_name, const std::vector<QString>& ws_names ) noexcept
|
bool newHashesDatabase( DatabaseWrapper db, const std::string& db_path, const std::vector<QString>& ws_names ) noexcept
|
||||||
{
|
{
|
||||||
bool successful{ true };
|
try {
|
||||||
// create the database
|
|
||||||
if ( ! db.open() ) {
|
db.open( db_path, true );
|
||||||
// error opening database
|
|
||||||
successful &= false;
|
|
||||||
DialogSec::errDatabaseFailedOpening( db_name, db.lastError().text() );
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// succesfully creted database file, now create the tables
|
// succesfully creted database file, now create the tables
|
||||||
QSqlQuery query;
|
const QString stmt{ QStringLiteral(R"(
|
||||||
|
CREATE TABLE "%1" (
|
||||||
|
"hash" TEXT
|
||||||
|
);)")};
|
||||||
|
|
||||||
for ( const QString& ws_name : ws_names ) {
|
for ( const QString& ws_name : ws_names ) {
|
||||||
if ( ! successful ) { break; }
|
|
||||||
// compose the statement with the table name for the access logs
|
QueryWrapper query{ db.getQuery() };
|
||||||
query.prepare( "\
|
|
||||||
CREATE TABLE \""+ws_name+"\" (\
|
if ( ! query->exec( stmt.arg( ws_name ) ) ) {
|
||||||
\"hash\" TEXT\
|
|
||||||
);");
|
|
||||||
if ( ! query.exec() ) {
|
|
||||||
// error creating table
|
// error creating table
|
||||||
successful &= false;
|
|
||||||
DialogSec::errDatabaseFailedExecuting(
|
DialogSec::errDatabaseFailedExecuting(
|
||||||
QString( db_name ),
|
db.name(),
|
||||||
QString("CREATE TABLE \"%1\" (...)").arg( ws_name ),
|
QStringLiteral(R"(CREATE TABLE "%1" (...))").arg( ws_name ),
|
||||||
QString( query.lastError().text() ) );
|
query->lastError().text() );
|
||||||
|
throw LogDoctorException{};
|
||||||
}
|
}
|
||||||
query.finish();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// inform about creation
|
} catch (...) {
|
||||||
if ( successful ) {
|
DialogSec::errDatabaseFailedCreating( db.name() );
|
||||||
DialogSec::msgDatabaseCreated( db_name );
|
return false;
|
||||||
} else {
|
|
||||||
DialogSec::errDatabaseFailedCreating( db_name );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return successful;
|
|
||||||
|
DialogSec::msgDatabaseCreated( db.name() );
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace (private)
|
} // namespace (private)
|
||||||
|
@ -208,314 +186,240 @@ bool newHashesDatabase( QSqlDatabase& db, const QString& db_name, const std::vec
|
||||||
|
|
||||||
bool checkCollectionDatabase( const std::string& db_path ) noexcept
|
bool checkCollectionDatabase( const std::string& db_path ) noexcept
|
||||||
{
|
{
|
||||||
bool make_new{false}, ok{true};
|
|
||||||
std::error_code err;
|
|
||||||
QString err_msg;
|
|
||||||
const QString db_name{ QString::fromStdString( db_path.substr( db_path.find_last_of( '/' ) + 1 ) ) };
|
|
||||||
const std::vector<QString> ws_names{ "apache", "nginx", "iis" };
|
const std::vector<QString> ws_names{ "apache", "nginx", "iis" };
|
||||||
|
|
||||||
QSqlDatabase db{ QSqlDatabase::database(DatabasesNames::data) };
|
try {
|
||||||
db.setDatabaseName( QString::fromStdString( db_path ) );
|
|
||||||
|
|
||||||
// check the existence
|
DatabaseWrapper db{ DatabaseHandler::get( DatabaseType::Data ) };
|
||||||
if ( IOutils::exists( db_path ) ) {
|
|
||||||
// check file type and permissions
|
|
||||||
if ( ! checkDatabaseFile( db_path, db_name ) ) {
|
|
||||||
ok &= false;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// database file seems ok, now try to open
|
|
||||||
if ( ! db.open() ) {
|
|
||||||
// error opening database
|
|
||||||
ok &= false;
|
|
||||||
DialogSec::errDatabaseFailedOpening( db_name, db.lastError().text() );
|
|
||||||
|
|
||||||
|
if ( ! IOutils::exists( db_path ) ) {
|
||||||
|
// ask to create a new one
|
||||||
|
if ( DialogSec::choiceDatabaseNotFound( db.name() ) ) {
|
||||||
|
// choosed to create it
|
||||||
|
throw MakeNewDatabase{};
|
||||||
} else {
|
} else {
|
||||||
// database successfully opened, now check the tables
|
// refused to create it, abort
|
||||||
const int check{ checkDatabaseTablesNames( db, db_name ) };
|
return false;
|
||||||
if ( check == 0 ) {
|
}
|
||||||
ok &= false;
|
}
|
||||||
} else if ( check == 2 ) {
|
|
||||||
make_new |= true;
|
// check file type and permissions
|
||||||
|
if ( ! checkDatabaseFile( db_path, db.name() ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// file seems ok, try to open
|
||||||
|
db.open( db_path, true );
|
||||||
|
|
||||||
|
// check the tables
|
||||||
|
if ( ! checkDatabaseTablesNames( db.getQuery() ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString stmt{ QStringLiteral("SELECT name, type FROM pragma_table_info('%1') AS tbinfo;") };
|
||||||
|
|
||||||
|
// check every WebServer table
|
||||||
|
for ( const QString& table : ws_names ) {
|
||||||
|
|
||||||
|
bool has_warning_column{ false };
|
||||||
|
// column's name:type associations
|
||||||
|
std::unordered_map<QString, std::tuple<QString, bool>> checks {
|
||||||
|
{"year", { "SMALLINT", false} },
|
||||||
|
{"month", { "TINYINT", false} },
|
||||||
|
{"day", { "TINYINT", false} },
|
||||||
|
{"hour", { "TINYINT", false} },
|
||||||
|
{"minute", { "TINYINT", false} },
|
||||||
|
{"second", { "TINYINT", false} },
|
||||||
|
{"protocol", { "TEXT", false} },
|
||||||
|
{"method", { "TEXT", false} },
|
||||||
|
{"uri", { "TEXT", false} },
|
||||||
|
{"query", { "TEXT", false} },
|
||||||
|
{"response", { "SMALLINT", false} },
|
||||||
|
{"time_taken", { "INTEGER", false} },
|
||||||
|
{"bytes_sent", { "INTEGER", false} },
|
||||||
|
{"bytes_received", { "INTEGER", false} },
|
||||||
|
{"client", { "TEXT", false} },
|
||||||
|
{"user_agent", { "TEXT", false} },
|
||||||
|
{"cookie", { "TEXT", false} },
|
||||||
|
{"referrer", { "TEXT", false} }
|
||||||
|
};
|
||||||
|
|
||||||
|
QueryWrapper query{ db.getQuery() };
|
||||||
|
|
||||||
|
query( stmt.arg( table ) );
|
||||||
|
|
||||||
|
while ( query->next() ) {
|
||||||
|
const QString col_name{ query[0].toString() };
|
||||||
|
const QString col_type{ query[1].toString() };
|
||||||
|
|
||||||
|
if ( col_name == "warning" ) {
|
||||||
|
// provide backward compatibility, this column will be removed from the table
|
||||||
|
has_warning_column |= true;
|
||||||
|
|
||||||
|
} else if ( const auto it{ checks.find( col_name ) }; it != checks.end() ) {
|
||||||
|
// column found, check the data-type
|
||||||
|
auto& type{ it->second };
|
||||||
|
if ( col_type == std::get<0>( type ) ) {
|
||||||
|
// same data-type
|
||||||
|
std::get<1>( type ) |= true;
|
||||||
|
} else {
|
||||||
|
// different data-type, ask to renew
|
||||||
|
if ( DialogSec::choiceDatabaseWrongDataType( db.name(), table, col_name, col_type ) ) {
|
||||||
|
// agreed to renew
|
||||||
|
throw MakeNewDatabase{};
|
||||||
|
} else {
|
||||||
|
// refused to renew
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// unexpected column
|
||||||
|
if ( DialogSec::choiceDatabaseWrongColumn( db.name(), table, col_name ) ) {
|
||||||
|
// agreed to renew
|
||||||
|
throw MakeNewDatabase{};
|
||||||
|
} else {
|
||||||
|
// refused to renew
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
QSqlQuery query{ QSqlQuery( db ) };
|
}
|
||||||
if ( ok && !make_new ) {
|
|
||||||
|
|
||||||
// check every WebServer table, both access and error
|
if ( has_warning_column ) {
|
||||||
for ( const QString& table : ws_names ) {
|
// provide backward compatibility
|
||||||
|
query->finish();
|
||||||
|
query( QStringLiteral(R"(ALTER TABLE "%1" DROP COLUMN "warning";)").arg( table ) );
|
||||||
|
}
|
||||||
|
|
||||||
if ( !ok || make_new ) { break; }
|
for ( const auto& [col,type] : checks ) {
|
||||||
|
if ( ! std::get<1>( type ) ) {
|
||||||
bool has_warning_column{ false };
|
// a column has not been found
|
||||||
// column's name:type associations
|
if ( DialogSec::choiceDatabaseMissingColumn( db.name(), table, col ) ) {
|
||||||
std::unordered_map<QString, std::tuple<QString, bool>>
|
// agreed to renew
|
||||||
data_types {
|
throw MakeNewDatabase{};
|
||||||
{"year", { "SMALLINT", false} },
|
} else {
|
||||||
{"month", { "TINYINT", false} },
|
// refused to renew
|
||||||
{"day", { "TINYINT", false} },
|
return false;
|
||||||
{"hour", { "TINYINT", false} },
|
|
||||||
{"minute", { "TINYINT", false} },
|
|
||||||
{"second", { "TINYINT", false} },
|
|
||||||
{"protocol", { "TEXT", false} },
|
|
||||||
{"method", { "TEXT", false} },
|
|
||||||
{"uri", { "TEXT", false} },
|
|
||||||
{"query", { "TEXT", false} },
|
|
||||||
{"response", { "SMALLINT", false} },
|
|
||||||
{"time_taken", { "INTEGER", false} },
|
|
||||||
{"bytes_sent", { "INTEGER", false} },
|
|
||||||
{"bytes_received", { "INTEGER", false} },
|
|
||||||
{"client", { "TEXT", false} },
|
|
||||||
{"user_agent", { "TEXT", false} },
|
|
||||||
{"cookie", { "TEXT", false} },
|
|
||||||
{"referrer", { "TEXT", false} }
|
|
||||||
};
|
|
||||||
|
|
||||||
// query table's columns' infoes for access logs
|
|
||||||
if ( ! query.exec( "SELECT name, type FROM pragma_table_info('"+table+"') AS tbinfo;" ) ) {
|
|
||||||
// error opening database
|
|
||||||
ok &= false;
|
|
||||||
DialogSec::errDatabaseFailedExecuting( db_name, query.lastQuery(), query.lastError().text() );
|
|
||||||
}
|
|
||||||
// iterate over results
|
|
||||||
while ( query.next() ) {
|
|
||||||
const QString col_name{ query.value(0).toString() };
|
|
||||||
const QString col_type{ query.value(1).toString() };
|
|
||||||
if ( col_name == "warning" ) {
|
|
||||||
// provide backward compatibility, this column will be removed from the table
|
|
||||||
has_warning_column |= true;
|
|
||||||
|
|
||||||
} else if ( data_types.find( col_name ) == data_types.end() ) {
|
|
||||||
// unexpected column
|
|
||||||
if ( DialogSec::choiceDatabaseWrongColumn( db_name, table, col_name ) ) {
|
|
||||||
// agreed to renew
|
|
||||||
make_new |= true;
|
|
||||||
} else {
|
|
||||||
// refused to renew
|
|
||||||
ok &= false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// column found, check the data-type
|
|
||||||
const QString data_type{ std::get<0>( data_types.at( col_name ) ) };
|
|
||||||
if ( col_type == data_type ) {
|
|
||||||
// same data-type
|
|
||||||
data_types.at( col_name ) = std::tuple( data_type, true );
|
|
||||||
} else {
|
|
||||||
// different data-type, ask to renew
|
|
||||||
if ( DialogSec::choiceDatabaseWrongDataType( db_name, table, col_name, col_type ) ) {
|
|
||||||
// agreed to renew
|
|
||||||
make_new |= true;
|
|
||||||
} else {
|
|
||||||
// refused to renew
|
|
||||||
ok &= false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( ok && !make_new ) {
|
|
||||||
if ( has_warning_column ) {
|
|
||||||
// provide backward compatibility
|
|
||||||
query.finish();
|
|
||||||
if ( ! query.exec( "ALTER TABLE \""+table+"\" DROP COLUMN \"warning\";" ) ) {
|
|
||||||
// failed to remove the column
|
|
||||||
ok &= false;
|
|
||||||
DialogSec::errDatabaseFailedExecuting( db_name, query.lastQuery(), query.lastError().text() );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for ( const auto& [ col, tup ] : data_types ) {
|
|
||||||
if ( ! std::get<1>( tup ) ) {
|
|
||||||
// a column has not been found
|
|
||||||
if ( DialogSec::choiceDatabaseMissingColumn( db_name, table, col ) ) {
|
|
||||||
// agreed to renew
|
|
||||||
make_new |= true;
|
|
||||||
} else {
|
|
||||||
// refused to renew
|
|
||||||
ok &= false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
query.finish();
|
|
||||||
if ( !ok || make_new ) { break; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} catch (const MakeNewDatabase&) {
|
||||||
// database does not exist, yet, ask to create a new one
|
|
||||||
if ( DialogSec::choiceDatabaseNotFound( QString(db_name) ) ) {
|
|
||||||
// choosed to create it
|
|
||||||
make_new |= true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// refused to create it, abort
|
|
||||||
ok &= false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ok && make_new ) {
|
|
||||||
// rename the current db file as a 'copy'
|
|
||||||
if ( IOutils::exists( db_path ) ) {
|
if ( IOutils::exists( db_path ) ) {
|
||||||
// a database already exists, try rename it
|
// a database already exists, try rename it
|
||||||
|
std::error_code err;
|
||||||
if ( ! IOutils::renameAsCopy( db_path, err ) ) {
|
if ( ! IOutils::renameAsCopy( db_path, err ) ) {
|
||||||
// failed to rename
|
// failed to rename
|
||||||
ok &= false;
|
QString err_msg;
|
||||||
if ( err ) {
|
if ( err ) {
|
||||||
err_msg = QString::fromStdString( err.message() );
|
err_msg = QString::fromStdString( err.message() );
|
||||||
}
|
}
|
||||||
DialogSec::errRenaming( QString::fromStdString(db_path), err_msg );
|
DialogSec::errRenaming( QString::fromStdString(db_path), err_msg );
|
||||||
}/* else {
|
return false;
|
||||||
// renamed successfully, make new one
|
}
|
||||||
}*/
|
|
||||||
}
|
|
||||||
if ( ok ) {
|
|
||||||
ok = newCollectionDatabase( db, db_name, ws_names );
|
|
||||||
}
|
}
|
||||||
|
return newCollectionDatabase( DatabaseHandler::get( DatabaseType::Data ), db_path, ws_names );
|
||||||
|
|
||||||
|
} catch (const LogDoctorException&) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( db.isOpen() ) {
|
return true;
|
||||||
db.close();
|
|
||||||
}
|
|
||||||
return ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool checkHashesDatabase( const std::string& db_path ) noexcept
|
bool checkHashesDatabase( const std::string& db_path ) noexcept
|
||||||
{
|
{
|
||||||
bool make_new{false}, ok{true};
|
|
||||||
std::error_code err;
|
|
||||||
QString err_msg;
|
|
||||||
const QString db_name{ QString::fromStdString( db_path.substr( db_path.find_last_of( '/' ) + 1 ) ) };
|
|
||||||
const std::vector<QString> ws_names { "apache", "nginx", "iis" };
|
const std::vector<QString> ws_names { "apache", "nginx", "iis" };
|
||||||
|
|
||||||
QSqlDatabase db{ QSqlDatabase::database(DatabasesNames::hashes) };
|
try {
|
||||||
db.setDatabaseName( QString::fromStdString( db_path ) );
|
|
||||||
|
|
||||||
// check the existence
|
DatabaseWrapper db{ DatabaseHandler::get( DatabaseType::Hashes ) };
|
||||||
if ( IOutils::exists( db_path ) ) {
|
|
||||||
// check file type and permissions
|
|
||||||
if ( ! checkDatabaseFile( db_path, db_name ) ) {
|
|
||||||
ok &= false;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// database file seems ok, now try to open
|
|
||||||
if ( ! db.open() ) {
|
|
||||||
// error opening database
|
|
||||||
ok &= false;
|
|
||||||
DialogSec::errDatabaseFailedOpening( db_name, db.lastError().text() );
|
|
||||||
|
|
||||||
|
if ( ! IOutils::exists( db_path ) ) {
|
||||||
|
// database does not exist, yet, ask to create a new one
|
||||||
|
if ( DialogSec::choiceDatabaseNotFound( db.name() ) ) {
|
||||||
|
// choosed to create it
|
||||||
|
throw MakeNewDatabase{};
|
||||||
} else {
|
} else {
|
||||||
// database successfully opened, now check the tables
|
// refused to create it, abort
|
||||||
const int check = checkDatabaseTablesNames( db, db_name );
|
return false;
|
||||||
if ( check == 0 ) {
|
}
|
||||||
ok &= false;
|
}
|
||||||
} else if ( check == 2 ) {
|
|
||||||
make_new |= true;
|
|
||||||
}
|
|
||||||
QSqlQuery query{ QSqlQuery( db ) };
|
|
||||||
if ( ok && !make_new ) {
|
|
||||||
|
|
||||||
// check every WebServer table, both access and error
|
// check file type and permissions
|
||||||
for ( const QString& table : ws_names ) {
|
if ( ! checkDatabaseFile( db_path, db.name() ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if ( !ok || make_new ) { break; }
|
// file seems ok, try to open
|
||||||
// column's name:type associations
|
db.open( db_path, true );
|
||||||
bool name_ok{ false },
|
|
||||||
type_ok{ false };
|
|
||||||
|
|
||||||
// query table's columns' infoes for access logs
|
// check the tables
|
||||||
if ( ! query.exec( "SELECT name, type FROM pragma_table_info('"+table+"') AS tbinfo;" ) ) {
|
if ( ! checkDatabaseTablesNames( db.getQuery() ) ) {
|
||||||
// error opening database
|
return false;
|
||||||
ok &= false;
|
}
|
||||||
DialogSec::errDatabaseFailedExecuting( db_name, query.lastQuery(), query.lastError().text() );
|
|
||||||
}
|
|
||||||
// iterate over results
|
|
||||||
while ( query.next() ) {
|
|
||||||
const QString col_name{ query.value(0).toString() };
|
|
||||||
const QString col_type{ query.value(1).toString() };
|
|
||||||
if ( col_name != "hash" ) {
|
|
||||||
// unexpected column
|
|
||||||
if ( DialogSec::choiceDatabaseWrongColumn( db_name, table, col_name ) ) {
|
|
||||||
// agreed to renew
|
|
||||||
make_new |= true;
|
|
||||||
} else {
|
|
||||||
// refused to renew
|
|
||||||
ok &= false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
} else {
|
const QString stmt{ QStringLiteral("SELECT name, type FROM pragma_table_info('%1') AS tbinfo;") };
|
||||||
// column found, check the data-type
|
|
||||||
name_ok |= true;
|
// check every WebServer table, both access and error
|
||||||
if ( col_type == "TEXT" ) {
|
for ( const QString& table : ws_names ) {
|
||||||
// same data-type
|
|
||||||
type_ok |= true;
|
QueryWrapper query{ db.getQuery() };
|
||||||
} else {
|
|
||||||
// different data-type, ask to renew
|
query( stmt.arg( table ) );
|
||||||
if ( DialogSec::choiceDatabaseWrongDataType( db_name, table, col_name, col_type ) ) {
|
|
||||||
// agreed to renew
|
while ( query->next() ) {
|
||||||
make_new |= true;
|
const QString col_name{ query[0].toString() };
|
||||||
} else {
|
const QString col_type{ query[1].toString() };
|
||||||
// refused to renew
|
|
||||||
ok &= false;
|
if ( col_name != "hash" ) {
|
||||||
}
|
// unexpected column
|
||||||
break;
|
if ( DialogSec::choiceDatabaseWrongColumn( db.name(), table, col_name ) ) {
|
||||||
}
|
// agreed to renew
|
||||||
}
|
throw MakeNewDatabase{};
|
||||||
}
|
} else {
|
||||||
if ( ok && !make_new ) {
|
// refused to renew
|
||||||
if ( !name_ok || !type_ok ) {
|
return false;
|
||||||
ok &= false;
|
}
|
||||||
}
|
|
||||||
}
|
} else if ( col_type != "TEXT" ) {
|
||||||
query.finish();
|
// different data-type, ask to renew
|
||||||
|
if ( DialogSec::choiceDatabaseWrongDataType( db.name(), table, col_name, col_type ) ) {
|
||||||
|
// agreed to renew
|
||||||
|
throw MakeNewDatabase{};
|
||||||
|
} else {
|
||||||
|
// refused to renew
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} catch (const MakeNewDatabase&) {
|
||||||
// database does not exist, yet, ask to create a new one
|
|
||||||
if ( DialogSec::choiceDatabaseNotFound( QString(db_name) ) ) {
|
|
||||||
// choosed to create it
|
|
||||||
make_new |= true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// refused to create it, abort
|
|
||||||
ok &= false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ok && make_new ) {
|
|
||||||
// rename the current db file as a 'copy'
|
|
||||||
if ( IOutils::exists( db_path ) ) {
|
if ( IOutils::exists( db_path ) ) {
|
||||||
// a database already exists, try rename it
|
// a database already exists, try rename it
|
||||||
|
std::error_code err;
|
||||||
if ( ! IOutils::renameAsCopy( db_path, err ) ) {
|
if ( ! IOutils::renameAsCopy( db_path, err ) ) {
|
||||||
// failed to rename
|
// failed to rename
|
||||||
ok &= false;
|
QString err_msg;
|
||||||
if ( err ) {
|
if ( err ) {
|
||||||
err_msg = QString::fromStdString( err.message() );
|
err_msg = QString::fromStdString( err.message() );
|
||||||
}
|
}
|
||||||
DialogSec::errRenaming( QString::fromStdString(db_path), err_msg );
|
DialogSec::errRenaming( QString::fromStdString(db_path), err_msg );
|
||||||
}/* else {
|
return false;
|
||||||
// renamed successfully, make new one
|
}
|
||||||
}*/
|
|
||||||
}
|
|
||||||
if ( ok ) {
|
|
||||||
ok = newHashesDatabase( db, db_name, ws_names );
|
|
||||||
}
|
}
|
||||||
|
return newHashesDatabase( DatabaseHandler::get( DatabaseType::Hashes ), db_path, ws_names );
|
||||||
|
|
||||||
|
} catch (const LogDoctorException&) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( db.isOpen() ) {
|
return true;
|
||||||
db.close();
|
|
||||||
}
|
|
||||||
return ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -92,58 +92,36 @@ QString printableTime( const unsigned seconds ) noexcept
|
||||||
{
|
{
|
||||||
const unsigned mins{ seconds / 60u };
|
const unsigned mins{ seconds / 60u };
|
||||||
const unsigned secs{ seconds - (mins*60u) };
|
const unsigned secs{ seconds - (mins*60u) };
|
||||||
return QString("%1:%2").arg(
|
return QStringLiteral("%1:%2")
|
||||||
(mins<10u)
|
.arg( mins, 2, 10, QChar('0') )
|
||||||
? QString("0%1").arg( mins )
|
.arg( secs, 2, 10, QChar('0') );
|
||||||
: QString::number( mins ),
|
|
||||||
(secs<10u)
|
|
||||||
? QString("0%1").arg( secs )
|
|
||||||
: QString::number( secs )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QString printableTime( const int hour, const int minute, const int second ) noexcept
|
QString printableTime( const int hour, const int minute, const int second ) noexcept
|
||||||
{
|
{
|
||||||
return QString("%1:%2:%3").arg(
|
return QStringLiteral("%1:%2:%3")
|
||||||
(hour<10)
|
.arg( hour, 2, 10, QChar('0') )
|
||||||
? QString("0%1").arg( hour )
|
.arg( minute, 2, 10, QChar('0') )
|
||||||
: QString::number( hour ),
|
.arg( second, 2, 10, QChar('0') );
|
||||||
(minute<10)
|
|
||||||
? QString("0%1").arg( minute )
|
|
||||||
: QString::number( minute ),
|
|
||||||
(second<10)
|
|
||||||
? QString("0%1").arg( second )
|
|
||||||
: QString::number( second )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QString printableDate( const QString& year, const int month, const QString& day ) noexcept
|
QString printableDate( const QString& year, const int month, const QString& day ) noexcept
|
||||||
{
|
{
|
||||||
return QString("%1-%2-%3").arg(
|
return QStringLiteral("%1-%2-%3")
|
||||||
year,
|
.arg( year )
|
||||||
(month<10)
|
.arg( month, 2, 10, QChar('0') )
|
||||||
? QString("0%1").arg( month )
|
.arg( day, 2, QChar('0') );
|
||||||
: QString::number( month ),
|
|
||||||
(day.size()<2)
|
|
||||||
? QString("0%1").arg( day )
|
|
||||||
: day
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QString printableDate( const int year, const int month, const int day ) noexcept
|
QString printableDate( const int year, const int month, const int day ) noexcept
|
||||||
{
|
{
|
||||||
return QString("%1-%2-%3").arg(
|
return QStringLiteral("%1-%2-%3")
|
||||||
QString::number( year ),
|
.arg( year )
|
||||||
(month<10)
|
.arg( month, 2, 10, QChar('0') )
|
||||||
? QString("0%1").arg( month )
|
.arg( day, 2, 10, QChar('0') );
|
||||||
: QString::number( month ),
|
|
||||||
(day<10)
|
|
||||||
? QString("0%1").arg( day )
|
|
||||||
: QString::number( day )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue