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