labfy-investigation/src/dao/osint_execution_dao.c
2026-07-22 12:54:24 +02:00

287 lines
14 KiB
C

/******************************************************************************
* @file osint_execution_dao.c
* @brief Persistance de la provenance des exécutions OSINT.
******************************************************************************/
#include "dao/osint_execution_dao.h"
#include "database/error.h"
#include "database/statement.h"
struct OsintExecutionDao { Database *database; };
static const char *const insert_sql =
"INSERT INTO osint_executions(id,tool_identifier,tool_version,"
"action_identifier,selection_id,selection_kind,target_value,arguments,"
"started_at,finished_at,exit_code,final_state,stdout_raw,stderr_raw,"
"output_sha256) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);";
static const char *const find_sql =
"SELECT id,tool_identifier,tool_version,action_identifier,selection_id,"
"selection_kind,target_value,arguments,started_at,finished_at,exit_code,"
"final_state,stdout_raw,stderr_raw,output_sha256 FROM osint_executions "
"WHERE id=?;";
static const char *const list_by_selection_sql =
"SELECT id,tool_identifier,tool_version,action_identifier,selection_id,"
"selection_kind,target_value,arguments,started_at,finished_at,exit_code,"
"final_state,stdout_raw,stderr_raw,output_sha256 FROM osint_executions "
"WHERE (selection_kind=? AND selection_id=?) "
"OR (?='entity' AND id IN (SELECT execution_id "
"FROM osint_execution_entities WHERE entity_id=?)) "
"OR (?='relation' AND id IN (SELECT execution_id "
"FROM osint_execution_relations WHERE relation_id=?)) "
"ORDER BY finished_at DESC,id DESC;";
static const char *const list_linked_objects_sql =
"SELECT 'Entité',entity_id,disposition FROM osint_execution_entities "
"WHERE execution_id=? UNION ALL "
"SELECT 'Relation',relation_id,disposition FROM osint_execution_relations "
"WHERE execution_id=? ORDER BY 1,2;";
static void osint_execution_dao_set_error(
OsintExecutionDao *dao, GError **error, const char *context
)
{
if (error != NULL && *error == NULL)
g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s : %s", context,
dao != NULL && database_error_get_message(dao->database) != NULL
? database_error_get_message(dao->database) : "erreur SQLite");
}
OsintExecutionDao *osint_execution_dao_new(Database *database, GError **error)
{
if (database == NULL)
{
g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"La connexion de provenance OSINT est absente.");
return NULL;
}
OsintExecutionDao *dao = g_try_new0(OsintExecutionDao, 1);
if (dao != NULL) dao->database = database;
return dao;
}
void osint_execution_dao_free(OsintExecutionDao *dao) { g_free(dao); }
gboolean osint_execution_dao_insert(
OsintExecutionDao *dao, const OsintExecutionRecord *record, GError **error
)
{
DatabaseStatement *statement = NULL;
GBytes *stdout_raw = NULL;
GBytes *stderr_raw = NULL;
gboolean success = FALSE;
if (dao == NULL || record == NULL) return FALSE;
statement = database_statement_prepare(dao->database, insert_sql);
if (statement == NULL) goto cleanup;
stdout_raw = osint_execution_record_ref_stdout(record);
stderr_raw = osint_execution_record_ref_stderr(record);
success = database_statement_bind_text(statement, 1,
osint_execution_record_get_identifier(record)) &&
database_statement_bind_text(statement, 2,
osint_execution_record_get_tool_identifier(record)) &&
(osint_execution_record_get_tool_version(record) != NULL
? database_statement_bind_text(statement, 3,
osint_execution_record_get_tool_version(record))
: database_statement_bind_null(statement, 3)) &&
database_statement_bind_text(statement, 4,
osint_execution_record_get_action_identifier(record)) &&
database_statement_bind_text(statement, 5,
osint_execution_record_get_selection_identifier(record)) &&
database_statement_bind_text(statement, 6,
osint_execution_record_get_selection_kind(record)) &&
database_statement_bind_text(statement, 7,
osint_execution_record_get_target_value(record)) &&
database_statement_bind_text(statement, 8,
osint_execution_record_get_arguments(record)) &&
database_statement_bind_text(statement, 9,
osint_execution_record_get_started_at(record)) &&
database_statement_bind_text(statement, 10,
osint_execution_record_get_finished_at(record)) &&
(osint_execution_record_has_exit_code(record)
? database_statement_bind_int64(statement, 11,
osint_execution_record_get_exit_code(record))
: database_statement_bind_null(statement, 11)) &&
database_statement_bind_text(statement, 12,
osint_execution_record_get_final_state(record)) &&
database_statement_bind_blob(statement, 13, stdout_raw) &&
database_statement_bind_blob(statement, 14, stderr_raw) &&
database_statement_bind_text(statement, 15,
osint_execution_record_get_output_sha256(record)) &&
database_statement_step(statement) == DATABASE_STATEMENT_STEP_DONE;
cleanup:
if (!success) osint_execution_dao_set_error(dao, error,
"Impossible d'insérer l'exécution OSINT");
g_clear_pointer(&stdout_raw, g_bytes_unref);
g_clear_pointer(&stderr_raw, g_bytes_unref);
database_statement_finalize(statement);
return success;
}
static OsintExecutionRecord *osint_execution_dao_read_record(
DatabaseStatement *statement, GError **error
)
{
OsintExecutionRecord *record = NULL;
char *values[12] = {0};
GBytes *stdout_raw = NULL;
GBytes *stderr_raw = NULL;
int64_t exit_code = 0;
bool exit_is_null = true;
if (!database_statement_column_text(statement, 0, &values[0]) ||
!database_statement_column_text(statement, 1, &values[1]) ||
!database_statement_column_text(statement, 2, &values[2]) ||
!database_statement_column_text(statement, 3, &values[3]) ||
!database_statement_column_text(statement, 4, &values[4]) ||
!database_statement_column_text(statement, 5, &values[5]) ||
!database_statement_column_text(statement, 6, &values[6]) ||
!database_statement_column_text(statement, 7, &values[7]) ||
!database_statement_column_text(statement, 8, &values[8]) ||
!database_statement_column_text(statement, 9, &values[9]) ||
!database_statement_column_is_null(statement, 10, &exit_is_null) ||
(!exit_is_null && !database_statement_column_int64(statement, 10, &exit_code)) ||
!database_statement_column_text(statement, 11, &values[10]) ||
!database_statement_column_blob(statement, 12, &stdout_raw) ||
!database_statement_column_blob(statement, 13, &stderr_raw) ||
!database_statement_column_text(statement, 14, &values[11])) goto cleanup;
record = osint_execution_record_new(
values[0], values[1], values[2], values[3], values[4], values[5],
values[6], values[7], values[8], values[9], !exit_is_null,
(gint) exit_code, values[10], stdout_raw, stderr_raw, values[11], error);
cleanup:
for (guint index = 0; index < G_N_ELEMENTS(values); index++) g_free(values[index]);
g_clear_pointer(&stdout_raw, g_bytes_unref);
g_clear_pointer(&stderr_raw, g_bytes_unref);
return record;
}
OsintExecutionRecord *osint_execution_dao_find_by_identifier(
OsintExecutionDao *dao, const char *identifier, GError **error
)
{
DatabaseStatement *statement = NULL;
OsintExecutionRecord *record = NULL;
if (dao == NULL || identifier == NULL) return NULL;
statement = database_statement_prepare(dao->database, find_sql);
if (statement != NULL && database_statement_bind_text(statement, 1, identifier) &&
database_statement_step(statement) == DATABASE_STATEMENT_STEP_ROW)
record = osint_execution_dao_read_record(statement, error);
if (record == NULL && error != NULL && *error == NULL)
osint_execution_dao_set_error(dao, error,
"Impossible de lire l'exécution OSINT");
database_statement_finalize(statement);
return record;
}
GPtrArray *osint_execution_dao_list_by_selection(
OsintExecutionDao *dao, const char *selection_kind,
const char *selection_identifier, GError **error
)
{
DatabaseStatement *statement = NULL;
GPtrArray *records = NULL;
DatabaseStatementStepResult step = DATABASE_STATEMENT_STEP_ERROR;
if (dao == NULL || selection_identifier == NULL ||
(g_strcmp0(selection_kind, "entity") != 0 &&
g_strcmp0(selection_kind, "relation") != 0)) return NULL;
statement = database_statement_prepare(dao->database, list_by_selection_sql);
records = g_ptr_array_new_with_free_func(
(GDestroyNotify) osint_execution_record_free);
if (statement == NULL || records == NULL ||
!database_statement_bind_text(statement, 1, selection_kind) ||
!database_statement_bind_text(statement, 2, selection_identifier) ||
!database_statement_bind_text(statement, 3, selection_kind) ||
!database_statement_bind_text(statement, 4, selection_identifier) ||
!database_statement_bind_text(statement, 5, selection_kind) ||
!database_statement_bind_text(statement, 6, selection_identifier))
goto failure;
while ((step = database_statement_step(statement)) == DATABASE_STATEMENT_STEP_ROW)
{
OsintExecutionRecord *record = osint_execution_dao_read_record(statement, error);
if (record == NULL) goto failure;
g_ptr_array_add(records, record);
}
if (step != DATABASE_STATEMENT_STEP_DONE) goto failure;
database_statement_finalize(statement);
return records;
failure:
osint_execution_dao_set_error(dao, error,
"Impossible de lister l'historique OSINT");
database_statement_finalize(statement);
g_clear_pointer(&records, g_ptr_array_unref);
return NULL;
}
GPtrArray *osint_execution_dao_list_linked_objects(
OsintExecutionDao *dao, const char *execution_identifier, GError **error
)
{
DatabaseStatement *statement = NULL;
GPtrArray *objects = NULL;
DatabaseStatementStepResult step = DATABASE_STATEMENT_STEP_ERROR;
if (dao == NULL || execution_identifier == NULL) return NULL;
statement = database_statement_prepare(dao->database, list_linked_objects_sql);
objects = g_ptr_array_new_with_free_func(g_free);
if (statement == NULL || objects == NULL ||
!database_statement_bind_text(statement, 1, execution_identifier) ||
!database_statement_bind_text(statement, 2, execution_identifier))
goto failure;
while ((step = database_statement_step(statement)) == DATABASE_STATEMENT_STEP_ROW)
{
char *kind = NULL; char *identifier = NULL; char *disposition = NULL;
char *description = NULL;
if (!database_statement_column_text(statement, 0, &kind) ||
!database_statement_column_text(statement, 1, &identifier) ||
!database_statement_column_text(statement, 2, &disposition))
{ g_free(kind); g_free(identifier); g_free(disposition); goto failure; }
description = g_strdup_printf("%s %s — %s", kind, identifier,
g_strcmp0(disposition, "created") == 0 ? "créée" : "réutilisée");
g_free(kind); g_free(identifier); g_free(disposition);
if (description == NULL) goto failure;
g_ptr_array_add(objects, description);
}
if (step != DATABASE_STATEMENT_STEP_DONE) goto failure;
database_statement_finalize(statement);
return objects;
failure:
osint_execution_dao_set_error(dao, error,
"Impossible de lister les objets liés à l'exécution OSINT");
database_statement_finalize(statement);
g_clear_pointer(&objects, g_ptr_array_unref);
return NULL;
}
static gboolean osint_execution_dao_link(
OsintExecutionDao *dao, const char *table, const char *object_column,
const char *execution_identifier, const char *object_identifier,
const char *disposition, GError **error
)
{
char *sql = NULL;
DatabaseStatement *statement = NULL;
gboolean success = FALSE;
if (dao == NULL || execution_identifier == NULL || object_identifier == NULL ||
(g_strcmp0(disposition, "created") != 0 &&
g_strcmp0(disposition, "reused") != 0)) return FALSE;
sql = g_strdup_printf(
"INSERT OR IGNORE INTO %s(execution_id,%s,disposition) VALUES(?,?,?);",
table, object_column);
statement = database_statement_prepare(dao->database, sql);
success = statement != NULL &&
database_statement_bind_text(statement, 1, execution_identifier) &&
database_statement_bind_text(statement, 2, object_identifier) &&
database_statement_bind_text(statement, 3, disposition) &&
database_statement_step(statement) == DATABASE_STATEMENT_STEP_DONE;
if (!success) osint_execution_dao_set_error(dao, error,
"Impossible de lier la provenance OSINT");
database_statement_finalize(statement); g_free(sql); return success;
}
gboolean osint_execution_dao_link_entity(
OsintExecutionDao *dao, const char *execution_identifier,
const char *entity_identifier, const char *disposition, GError **error)
{ return osint_execution_dao_link(dao, "osint_execution_entities", "entity_id",
execution_identifier, entity_identifier, disposition, error); }
gboolean osint_execution_dao_link_relation(
OsintExecutionDao *dao, const char *execution_identifier,
const char *relation_identifier, const char *disposition, GError **error)
{ return osint_execution_dao_link(dao, "osint_execution_relations", "relation_id",
execution_identifier, relation_identifier, disposition, error); }