feat: import evidence from GTK interface

This commit is contained in:
grayTerminal-sh 2026-07-20 00:04:50 +02:00
parent 0912e7263e
commit 6c34287feb
10 changed files with 2658 additions and 231 deletions

View file

@ -54,6 +54,7 @@ TEST_TOOL_INITIALIZER := tests/test_tool_initializer
TEST_FILE_HASH := tests/test_file_hash
TEST_EVIDENCE_COPY := tests/test_evidence_copy
TEST_EVIDENCE_IMPORTER := tests/test_evidence_importer
TEST_EVIDENCE_IMPORT_TASK := tests/test_evidence_import_task
all: $(TARGET)
@ -245,6 +246,25 @@ $(TEST_EVIDENCE_IMPORTER): \
-DEVIDENCE_IMPORTER_ENABLE_TEST_HOOKS \
$^ -o $@ $(TEST_LDFLAGS) -lsqlite3
$(TEST_EVIDENCE_IMPORT_TASK): \
tests/test_evidence_import_task.c \
src/core/evidence_import_task.c \
src/core/evidence_importer.c \
src/core/evidence_copy.c \
src/core/file_hash.c \
src/core/background_task.c \
src/core/task_manager.c \
src/dao/evidence_dao.c \
src/models/evidence_record.c \
src/database/database.c \
src/database/schema.c \
src/database/statement.c \
src/database/transaction.c \
src/database/error.c
$(CC) $(TEST_CFLAGS) \
-DEVIDENCE_IMPORTER_ENABLE_TEST_HOOKS \
$^ -o $@ $(TEST_LDFLAGS) -lsqlite3
test: \
$(TEST_NODE) \
$(TEST_TREE_MODEL) \
@ -268,7 +288,8 @@ test: \
$(TEST_TOOL_INITIALIZER) \
$(TEST_FILE_HASH) \
$(TEST_EVIDENCE_COPY) \
$(TEST_EVIDENCE_IMPORTER)
$(TEST_EVIDENCE_IMPORTER) \
$(TEST_EVIDENCE_IMPORT_TASK)
@echo "Exécution des tests..."
@./$(TEST_NODE)
@./$(TEST_TREE_MODEL)
@ -293,6 +314,7 @@ test: \
@$(TEST_FILE_HASH)
@$(TEST_EVIDENCE_COPY)
@$(TEST_EVIDENCE_IMPORTER)
@$(TEST_EVIDENCE_IMPORT_TASK)
@echo "Tous les tests sont valides."
%.o: %.c
@ -325,7 +347,8 @@ clean:
$(TEST_TOOL_INITIALIZER) \
$(TEST_FILE_HASH) \
$(TEST_EVIDENCE_COPY) \
$(TEST_EVIDENCE_IMPORTER)
$(TEST_EVIDENCE_IMPORTER) \
$(TEST_EVIDENCE_IMPORT_TASK)
.PHONY: clean run test

View file

@ -0,0 +1,89 @@
/******************************************************************************
* @file evidence_import_task.h
* @brief Tâche asynchrone dimport dune preuve.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_EVIDENCE_IMPORT_TASK_H
#define LABFY_INVESTIGATION_EVIDENCE_IMPORT_TASK_H
#include "core/background_task.h"
#include "core/task_manager.h"
#include <glib.h>
/**
* @brief Codes derreur propres à la tâche dimport.
*/
typedef enum
{
EVIDENCE_IMPORT_TASK_ERROR_INVALID_ARGUMENT,
EVIDENCE_IMPORT_TASK_ERROR_DATABASE,
EVIDENCE_IMPORT_TASK_ERROR_IMPORT
} EvidenceImportTaskError;
/**
* @brief Domaine derreur du module.
*/
#define EVIDENCE_IMPORT_TASK_ERROR \
evidence_import_task_error_quark()
/**
* @brief Paramètres nécessaires à une tâche dimport.
*
* Toutes les chaînes sont copiées avant le démarrage du worker.
*/
typedef struct
{
const char *database_path;
const char *source_path;
const char *destination_directory;
const char *relative_directory;
const char *type_identifier;
const char *collected_at;
const char *source;
const char *description;
} EvidenceImportTaskRequest;
/**
* @brief Retourne le domaine derreur du module.
*
* @return Quark GLib du domaine derreur.
*/
GQuark evidence_import_task_error_quark(void);
/**
* @brief Crée, enregistre et démarre une tâche dimport.
*
* La tâche ouvre sa propre connexion SQLite dans le thread secondaire.
*
* En cas de succès :
*
* - TaskManager conserve une référence ;
* - le worker conserve une référence pendant son exécution ;
* - lappelant reçoit sa référence initiale et doit la libérer avec
* background_task_unref().
*
* completion_data est transféré à BackgroundTask uniquement si le
* démarrage réussit. En cas déchec, lappelant en reste propriétaire.
*
* @param task_manager Gestionnaire recevant la tâche.
* @param request Paramètres de limport.
* @param completion_callback Callback final facultatif.
* @param completion_data Données du callback final.
* @param completion_data_destroy Destructeur de completion_data.
* @param error Emplacement facultatif recevant une erreur.
*
* @return Nouvelle tâche démarrée, ou NULL.
*/
BackgroundTask *evidence_import_task_start(
TaskManager *task_manager,
const EvidenceImportTaskRequest *request,
BackgroundTaskCompletionCallback completion_callback,
gpointer completion_data,
GDestroyNotify completion_data_destroy,
GError **error
);
#endif

View file

@ -0,0 +1,45 @@
/******************************************************************************
* @file file_dialog.h
* @brief Interface du dialogue de sélection dun fichier de preuve.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_FILE_DIALOG_H
#define LABFY_INVESTIGATION_FILE_DIALOG_H
#include <gtk/gtk.h>
/**
* @brief Fonction appelée après la fermeture du sélecteur.
*
* Cas possibles :
*
* - succès : file_path non NULL et error NULL ;
* - annulation : file_path NULL et error NULL ;
* - erreur : file_path NULL et error non NULL.
*
* Le chemin et lerreur restent valides uniquement pendant lappel.
*
* @param file_path Chemin local sélectionné, ou NULL.
* @param error Erreur de sélection, ou NULL.
* @param user_data Données transmises lors de louverture.
*/
typedef void (*FileDialogCallback)(
const char *file_path,
const GError *error,
gpointer user_data
);
/**
* @brief Ouvre un dialogue permettant de sélectionner un fichier.
*
* @param parent Fenêtre parente, éventuellement NULL.
* @param callback Fonction appelée lorsque le dialogue est terminé.
* @param user_data Données transmises au callback.
*/
void file_dialog_select_file(
GtkWindow *parent,
FileDialogCallback callback,
gpointer user_data
);
#endif

View file

@ -27,6 +27,16 @@ typedef void (*MainWindowNewInvestigationCallback)(
gpointer user_data
);
/**
* @brief Callback appelé lorsque l'utilisateur demande
* l'import d'une preuve.
*
* @param user_data Données utilisateur associées au callback.
*/
typedef void (*MainWindowImportEvidenceCallback)(
gpointer user_data
);
/**
* @brief Définit le callback du bouton « Nouvelle enquête ».
*
@ -163,16 +173,6 @@ typedef void (*MainWindowQuitCallback)(
gpointer user_data
);
/**
* @brief Callback appelé lorsque l'utilisateur demande une tâche
* de démonstration.
*
* @param user_data Données utilisateur associées au callback.
*/
typedef void (*MainWindowDemoTaskCallback)(
gpointer user_data
);
/**
* @brief Définit le callback du bouton « Ouvrir une enquête ».
*
@ -204,20 +204,31 @@ void main_window_set_quit_callback(
);
/**
* @brief Définit le callback du bouton « Tâche de test ».
* @brief Définit le callback de l'action « Importer une preuve ».
*
* Ce bouton est temporaire et sert à valider le panneau d'activité.
* MainWindow transmet uniquement la demande au contrôleur.
*
* @param main_window Fenêtre principale.
* @param callback Fonction appelée lors du clic.
* @param user_data Données transmises au callback.
*/
void main_window_set_demo_task_callback(
void main_window_set_import_evidence_callback(
MainWindow *main_window,
MainWindowDemoTaskCallback callback,
MainWindowImportEvidenceCallback callback,
gpointer user_data
);
/**
* @brief Active ou désactive l'action d'import d'une preuve.
*
* @param main_window Fenêtre principale.
* @param enabled TRUE pour autoriser l'import.
*/
void main_window_set_import_evidence_enabled(
MainWindow *main_window,
gboolean enabled
);
/**
* @brief Libère les ressources de la fenêtre.
*

View file

@ -18,14 +18,30 @@
#include "core/task_manager.h"
#include "core/background_task.h"
#include "core/tool_initializer.h"
#include "views/file_dialog.h"
#include "core/evidence_import_task.h"
#include "models/evidence_record.h"
#include <gtk/gtk.h>
#include <errno.h>
/**
* @brief Identifiant unique utilisé par GLib pour l'application.
*/
#define APPLICATION_ID "com.labfytools.investigation"
/**
* @brief Dossier physique recevant les documents importés.
*/
#define APPLICATION_EVIDENCE_DIRECTORY \
"01_Preuves_Originales/Documents"
/**
* @brief Type utilisé avant lajout du formulaire de métadonnées.
*/
#define APPLICATION_DEFAULT_EVIDENCE_TYPE \
"document"
/**
* @brief Erreurs produites lors du chargement d'une enquête.
*/
@ -71,6 +87,78 @@ struct Application
ToolInitializer *tool_initializer;
};
/**
* @brief Contexte conservé jusquà la fin dun import.
*/
typedef struct
{
Application *application;
char *investigation_root_path;
} ApplicationEvidenceImportContext;
/**
* @brief Libère le contexte dun import asynchrone.
*/
static void application_evidence_import_context_free(
gpointer user_data
)
{
ApplicationEvidenceImportContext *context =
user_data;
if (context == NULL)
{
return;
}
g_free(
context->investigation_root_path
);
g_free(
context
);
}
/**
* @brief Vérifie que lenquête active correspond à un chemin racine.
*/
static gboolean application_session_matches_root(
const Application *application,
const char *expected_root_path
)
{
const InvestigationProject *project = NULL;
const char *current_root_path = NULL;
if (application == NULL ||
application->session == NULL ||
expected_root_path == NULL)
{
return FALSE;
}
project =
investigation_session_get_project(
application->session
);
if (project == NULL)
{
return FALSE;
}
current_root_path =
investigation_project_get_root_path(
project
);
return g_strcmp0(
current_root_path,
expected_root_path
) == 0;
}
static void application_present_error(
Application *application,
const char *title,
@ -359,6 +447,11 @@ static gboolean application_install_session(
root_path
);
main_window_set_import_evidence_enabled(
application->main_window,
TRUE
);
return TRUE;
}
@ -775,265 +868,631 @@ static void application_on_open_investigation_requested(
}
/**
* @brief Exécute une tâche temporaire servant à tester le panneau.
* @brief Reconstruit et remplace larborescence active.
*
* Cette fonction s'exécute dans un thread secondaire.
* Le nouveau modèle est entièrement construit avant que lancien
* soit détaché et libéré.
*
* @param task Tâche en cours.
* @param cancellable Objet d'annulation.
* @param worker_data Données inutilisées.
* @param result Emplacement recevant le résultat.
* @param error Emplacement recevant une erreur.
* @param application Application concernée.
* @param investigation_root_path Racine à parcourir.
*
* @return TRUE en cas de succès, sinon FALSE.
* @return TRUE si le remplacement a réussi.
*/
static gboolean application_demo_task_worker(
BackgroundTask *task,
GCancellable *cancellable,
gpointer worker_data,
gpointer *result,
GError **error
static gboolean application_refresh_investigation_tree(
Application *application,
const char *investigation_root_path
)
{
guint step = 0;
char status_message[64];
InvestigationTreeModel *new_tree_model = NULL;
InvestigationTreeModel *old_tree_model = NULL;
(void) worker_data;
if (task == NULL ||
cancellable == NULL ||
result == NULL ||
error == NULL)
if (application == NULL ||
application->main_window == NULL ||
investigation_root_path == NULL ||
investigation_root_path[0] == '\0')
{
return FALSE;
}
for (step = 0; step <= 20; step++)
new_tree_model =
investigation_tree_builder_build(
investigation_root_path
);
if (new_tree_model == NULL)
{
if (g_cancellable_set_error_if_cancelled(
cancellable,
error
))
{
return FALSE;
}
g_snprintf(
status_message,
sizeof(status_message),
"Étape %u sur 20",
step
);
background_task_report_progress(
task,
(double) step / 20.0,
status_message
);
if (step < 20)
{
g_usleep(
500000
);
}
return FALSE;
}
*result = g_strdup(
"Tâche de démonstration terminée."
old_tree_model =
application->tree_model;
/*
* MainWindow transmet le nouveau modèle à la Sidebar.
*
* InvestigationTreeView détache dabord son ancien modèle GTK,
* puis construit le nouveau. Lancien modèle métier reste vivant
* pendant toute cette opération.
*/
main_window_set_tree_model(
application->main_window,
new_tree_model
);
application->tree_model =
new_tree_model;
/*
* La vue ne référence plus les anciens nœuds.
*/
investigation_tree_model_free(
old_tree_model
);
return TRUE;
}
/**
* @brief Journalise la fin de la tâche de démonstration.
* @brief Traite la fin dune tâche dimport.
*
* @param task Tâche terminée.
* @param user_data Données inutilisées.
* @param task Tâche dimport terminée.
* @param user_data Contexte ApplicationEvidenceImportContext.
*/
static void application_demo_task_completed(
static void application_on_evidence_import_completed(
BackgroundTask *task,
gpointer user_data
)
{
ApplicationEvidenceImportContext *context =
user_data;
Application *application = NULL;
BackgroundTaskState state;
const char *result = NULL;
EvidenceRecord *evidence_record = NULL;
const char *original_name = NULL;
char *status_message = NULL;
GError *error = NULL;
(void) user_data;
if (task == NULL)
if (task == NULL ||
context == NULL)
{
return;
}
state = background_task_get_state(
task
application =
context->application;
if (application == NULL ||
application->main_window == NULL)
{
return;
}
main_window_set_import_evidence_enabled(
application->main_window,
application->session != NULL
);
if (state == BACKGROUND_TASK_STATE_COMPLETED)
{
result = background_task_get_result(
state =
background_task_get_state(
task
);
g_print(
"%s\n",
result != NULL
? result
: "Tâche terminée."
);
return;
}
if (state == BACKGROUND_TASK_STATE_CANCELLED)
if (state ==
BACKGROUND_TASK_STATE_COMPLETED)
{
g_print(
"Tâche de démonstration annulée.\n"
evidence_record =
background_task_get_result(
task
);
if (evidence_record == NULL)
{
application_present_error(
application,
"Import incomplet",
"La tâche est terminée sans fournir de preuve."
);
return;
}
original_name =
evidence_record_get_original_name(
evidence_record
);
if (application_session_matches_root(
application,
context->investigation_root_path
))
{
if (application_refresh_investigation_tree(
application,
context->investigation_root_path
))
{
status_message =
g_strdup_printf(
"Preuve importée : %s",
original_name != NULL
? original_name
: "(nom indisponible)"
);
}
else
{
/*
* Limport reste valide même si le rafraîchissement visuel
* échoue. Le fichier et la ligne SQLite ne doivent surtout
* pas être présentés comme un échec.
*/
status_message =
g_strdup_printf(
"Preuve importée : %s — "
"arborescence non actualisée",
original_name != NULL
? original_name
: "(nom indisponible)"
);
g_warning(
"La preuve a été importée, mais larborescence de '%s' "
"na pas pu être reconstruite.",
context->investigation_root_path
);
}
main_window_set_status(
application->main_window,
status_message
);
}
g_message(
"Preuve importée dans '%s' : %s",
context->investigation_root_path,
original_name != NULL
? original_name
: "(nom indisponible)"
);
g_free(
status_message
);
return;
}
error = background_task_dup_error(
task
);
if (state ==
BACKGROUND_TASK_STATE_CANCELLED)
{
if (application_session_matches_root(
application,
context->investigation_root_path
))
{
main_window_set_status(
application->main_window,
"Import de preuve annulé."
);
}
return;
}
error =
background_task_dup_error(
task
);
g_warning(
"La tâche de démonstration a échoué : %s",
"Limport de la preuve a échoué : %s",
error != NULL
? error->message
: "erreur inconnue"
);
application_present_error(
application,
"Import impossible",
error != NULL
? error->message
: "La preuve na pas pu être importée."
);
g_clear_error(
&error
);
}
/**
* @brief Crée et lance une tâche de démonstration.
* @brief Prépare et démarre limport asynchrone dun fichier.
*/
static void application_start_evidence_import(
Application *application,
const char *file_path
)
{
const InvestigationProject *project = NULL;
const char *root_path = NULL;
const char *database_path = NULL;
EvidenceImportTaskRequest request =
{0};
ApplicationEvidenceImportContext *context =
NULL;
BackgroundTask *task = NULL;
char *destination_directory = NULL;
char *source_name = NULL;
char *status_message = NULL;
GError *error = NULL;
if (application == NULL ||
application->main_window == NULL ||
application->session == NULL ||
application->task_manager == NULL ||
file_path == NULL ||
file_path[0] == '\0')
{
return;
}
project =
investigation_session_get_project(
application->session
);
if (project == NULL)
{
application_present_error(
application,
"Import impossible",
"La session ne fournit aucun projet denquête."
);
return;
}
root_path =
investigation_project_get_root_path(
project
);
database_path =
investigation_project_get_database_path(
project
);
if (root_path == NULL ||
root_path[0] == '\0' ||
database_path == NULL ||
database_path[0] == '\0')
{
application_present_error(
application,
"Import impossible",
"Les chemins de lenquête sont invalides."
);
return;
}
destination_directory =
g_build_filename(
root_path,
"01_Preuves_Originales",
"Documents",
NULL
);
if (destination_directory == NULL)
{
application_present_error(
application,
"Import impossible",
"Impossible de construire le dossier de destination."
);
return;
}
/*
* Le dossier devrait exister dans une enquête normale.
* Cette création rend néanmoins limport robuste face à un dossier
* supprimé accidentellement.
*/
if (g_mkdir_with_parents(
destination_directory,
0700
) != 0)
{
status_message =
g_strdup_printf(
"Impossible de préparer le dossier '%s' : %s",
destination_directory,
g_strerror(errno)
);
application_present_error(
application,
"Import impossible",
status_message
);
g_free(
status_message
);
g_free(
destination_directory
);
return;
}
context =
g_try_new0(
ApplicationEvidenceImportContext,
1
);
if (context == NULL)
{
application_present_error(
application,
"Import impossible",
"Impossible dallouer le contexte de la tâche."
);
g_free(
destination_directory
);
return;
}
context->application =
application;
context->investigation_root_path =
g_strdup(
root_path
);
if (context->investigation_root_path == NULL)
{
application_evidence_import_context_free(
context
);
g_free(
destination_directory
);
application_present_error(
application,
"Import impossible",
"Impossible de conserver le chemin de lenquête."
);
return;
}
request.database_path =
database_path;
request.source_path =
file_path;
request.destination_directory =
destination_directory;
request.relative_directory =
APPLICATION_EVIDENCE_DIRECTORY;
request.type_identifier =
APPLICATION_DEFAULT_EVIDENCE_TYPE;
request.collected_at = NULL;
request.source = NULL;
request.description = NULL;
task =
evidence_import_task_start(
application->task_manager,
&request,
application_on_evidence_import_completed,
context,
application_evidence_import_context_free,
&error
);
if (task == NULL)
{
/*
* La tâche na pas pris possession du contexte.
*/
application_evidence_import_context_free(
context
);
application_present_error(
application,
"Import impossible",
error != NULL
? error->message
: "La tâche dimport na pas pu être démarrée."
);
g_clear_error(
&error
);
g_free(
destination_directory
);
return;
}
/*
* Le TaskManager et le worker conservent désormais leurs références.
*/
background_task_unref(
task
);
main_window_set_import_evidence_enabled(
application->main_window,
FALSE
);
source_name =
g_path_get_basename(
file_path
);
status_message =
g_strdup_printf(
"Import en cours : %s",
source_name != NULL
? source_name
: file_path
);
main_window_set_status(
application->main_window,
status_message
);
g_free(
status_message
);
g_free(
source_name
);
g_free(
destination_directory
);
}
/**
* @brief Traite le fichier de preuve sélectionné.
*
* @param file_path Chemin local sélectionné, ou NULL.
* @param error Erreur de sélection, ou NULL.
* @param user_data Pointeur vers Application.
*/
static void application_on_evidence_file_selected(
const char *file_path,
const GError *error,
gpointer user_data
)
{
Application *application =
user_data;
if (application == NULL ||
application->main_window == NULL)
{
return;
}
/*
* Une annulation ne constitue pas une erreur.
*/
if (file_path == NULL &&
error == NULL)
{
main_window_set_status(
application->main_window,
"Import de preuve annulé."
);
return;
}
if (error != NULL)
{
g_warning(
"Impossible de sélectionner le fichier : %s",
error->message
);
application_present_error(
application,
"Sélection impossible",
error->message
);
return;
}
if (file_path == NULL ||
file_path[0] == '\0')
{
application_present_error(
application,
"Sélection impossible",
"Le chemin du fichier sélectionné est invalide."
);
return;
}
application_start_evidence_import(
application,
file_path
);
}
/**
* @brief Traite la demande d'import d'une preuve.
*
* Le sélecteur de fichier sera ajouté à l'étape suivante.
*
* @param user_data Pointeur vers Application.
*/
static void application_on_demo_task_requested(
static void application_on_import_evidence_requested(
gpointer user_data
)
{
Application *application = user_data;
BackgroundTask *task = NULL;
GError *error = NULL;
if (application == NULL ||
application->task_manager == NULL)
application->main_window == NULL)
{
return;
}
task = background_task_new(
"Tâche de démonstration"
);
if (task == NULL)
if (application->session == NULL)
{
application_present_error(
application,
"Tâche impossible",
"La tâche de démonstration n'a pas pu être créée."
main_window_set_import_evidence_enabled(
application->main_window,
FALSE
);
return;
}
/*
* Le manager prend une référence supplémentaire.
*/
if (!task_manager_add(
application->task_manager,
task,
&error
))
{
g_warning(
"Impossible d'ajouter la tâche : %s",
error != NULL
? error->message
: "erreur inconnue"
);
application_present_error(
application,
"Tâche impossible",
error != NULL
? error->message
: "La tâche n'a pas pu être ajoutée."
);
g_clear_error(
&error
);
background_task_unref(
task
);
return;
}
if (!background_task_start(
task,
application_demo_task_worker,
NULL,
NULL,
g_free,
application_demo_task_completed,
NULL,
NULL,
&error
))
{
g_warning(
"Impossible de démarrer la tâche : %s",
error != NULL
? error->message
: "erreur inconnue"
);
application_present_error(
application,
"Démarrage impossible",
error != NULL
? error->message
: "La tâche n'a pas pu être démarrée."
);
g_clear_error(
&error
);
task_manager_remove(
application->task_manager,
task
);
background_task_unref(
task
);
return;
}
/*
* La référence locale n'est plus nécessaire :
*
* - TaskManager conserve une référence ;
* - BackgroundTask conserve une référence interne pendant
* l'exécution.
*/
background_task_unref(
task
file_dialog_select_file(
main_window_get_window(
application->main_window
),
application_on_evidence_file_selected,
application
);
}
@ -1183,12 +1642,17 @@ static void application_on_activate(
application
);
main_window_set_demo_task_callback(
main_window_set_import_evidence_callback(
application->main_window,
application_on_demo_task_requested,
application_on_import_evidence_requested,
application
);
main_window_set_import_evidence_enabled(
application->main_window,
application->session != NULL
);
main_window_set_quit_callback(
application->main_window,
application_on_quit_requested,

View file

@ -0,0 +1,663 @@
/******************************************************************************
* @file evidence_import_task.c
* @brief Exécution asynchrone de limport dune preuve.
******************************************************************************/
#include "core/evidence_import_task.h"
#include "core/evidence_importer.h"
#include "database/database.h"
#include "models/evidence_record.h"
#include <gio/gio.h>
#include <glib.h>
/**
* @brief Copie privée des paramètres nécessaires au worker.
*/
typedef struct
{
char *database_path;
char *source_path;
char *destination_directory;
char *relative_directory;
char *type_identifier;
char *collected_at;
char *source;
char *description;
} EvidenceImportTaskData;
/**
* @brief Libère les données privées du worker.
*/
static void evidence_import_task_data_free(
gpointer user_data
)
{
EvidenceImportTaskData *task_data =
user_data;
if (task_data == NULL)
{
return;
}
g_free(
task_data->description
);
g_free(
task_data->source
);
g_free(
task_data->collected_at
);
g_free(
task_data->type_identifier
);
g_free(
task_data->relative_directory
);
g_free(
task_data->destination_directory
);
g_free(
task_data->source_path
);
g_free(
task_data->database_path
);
g_free(
task_data
);
}
/**
* @brief Vérifie les paramètres obligatoires.
*/
static gboolean evidence_import_task_request_is_valid(
const EvidenceImportTaskRequest *request
)
{
if (request == NULL)
{
return FALSE;
}
if (request->database_path == NULL ||
request->database_path[0] == '\0')
{
return FALSE;
}
if (request->source_path == NULL ||
request->source_path[0] == '\0')
{
return FALSE;
}
if (request->destination_directory == NULL ||
request->destination_directory[0] == '\0')
{
return FALSE;
}
if (request->relative_directory == NULL ||
request->relative_directory[0] == '\0')
{
return FALSE;
}
if (request->type_identifier == NULL ||
request->type_identifier[0] == '\0')
{
return FALSE;
}
return TRUE;
}
/**
* @brief Copie les paramètres avant le lancement du thread.
*/
static EvidenceImportTaskData *
evidence_import_task_data_new(
const EvidenceImportTaskRequest *request
)
{
EvidenceImportTaskData *task_data =
NULL;
if (!evidence_import_task_request_is_valid(
request
))
{
return NULL;
}
task_data =
g_try_new0(
EvidenceImportTaskData,
1
);
if (task_data == NULL)
{
return NULL;
}
task_data->database_path =
g_strdup(
request->database_path
);
task_data->source_path =
g_strdup(
request->source_path
);
task_data->destination_directory =
g_strdup(
request->destination_directory
);
task_data->relative_directory =
g_strdup(
request->relative_directory
);
task_data->type_identifier =
g_strdup(
request->type_identifier
);
task_data->collected_at =
g_strdup(
request->collected_at
);
task_data->source =
g_strdup(
request->source
);
task_data->description =
g_strdup(
request->description
);
return task_data;
}
GQuark evidence_import_task_error_quark(void)
{
return g_quark_from_static_string(
"evidence-import-task-error-quark"
);
}
/**
* @brief Traduit lannulation métier en annulation GIO.
*
* BackgroundTask reconnaît une annulation uniquement avec :
*
* G_IO_ERROR / G_IO_ERROR_CANCELLED.
*/
static void evidence_import_task_propagate_import_error(
GError **error,
GError *import_error
)
{
if (import_error != NULL &&
g_error_matches(
import_error,
EVIDENCE_IMPORTER_ERROR,
EVIDENCE_IMPORTER_ERROR_CANCELLED
))
{
g_clear_error(
&import_error
);
g_set_error_literal(
error,
G_IO_ERROR,
G_IO_ERROR_CANCELLED,
"Limport de la preuve a été annulé."
);
return;
}
if (import_error != NULL)
{
g_propagate_error(
error,
import_error
);
return;
}
g_set_error_literal(
error,
EVIDENCE_IMPORT_TASK_ERROR,
EVIDENCE_IMPORT_TASK_ERROR_IMPORT,
"Limport a échoué sans fournir derreur."
);
}
/**
* @brief Exécute limport dans le thread secondaire.
*/
static gboolean evidence_import_task_worker(
BackgroundTask *task,
GCancellable *cancellable,
gpointer worker_data,
gpointer *result,
GError **error
)
{
EvidenceImportTaskData *task_data =
worker_data;
EvidenceImportRequest import_request =
{0};
Database *database = NULL;
EvidenceImporter *evidence_importer =
NULL;
EvidenceRecord *evidence_record =
NULL;
GError *importer_error = NULL;
GError *import_error = NULL;
if (task == NULL ||
cancellable == NULL ||
task_data == NULL ||
result == NULL ||
error == NULL)
{
return FALSE;
}
*result = NULL;
if (g_cancellable_set_error_if_cancelled(
cancellable,
error
))
{
return FALSE;
}
background_task_report_progress(
task,
0.05,
"Préparation de limport"
);
database =
database_open(
task_data->database_path
);
if (database == NULL)
{
g_set_error(
error,
EVIDENCE_IMPORT_TASK_ERROR,
EVIDENCE_IMPORT_TASK_ERROR_DATABASE,
"Impossible douvrir la base SQLite : %s",
task_data->database_path
);
return FALSE;
}
if (g_cancellable_set_error_if_cancelled(
cancellable,
error
))
{
database_close(
database
);
return FALSE;
}
evidence_importer =
evidence_importer_new(
database,
&importer_error
);
if (evidence_importer == NULL)
{
if (importer_error != NULL)
{
g_propagate_prefixed_error(
error,
importer_error,
"Impossible de préparer limport : "
);
}
else
{
g_set_error_literal(
error,
EVIDENCE_IMPORT_TASK_ERROR,
EVIDENCE_IMPORT_TASK_ERROR_IMPORT,
"Impossible de créer le service dimport."
);
}
database_close(
database
);
return FALSE;
}
import_request.source_path =
task_data->source_path;
import_request.destination_directory =
task_data->destination_directory;
import_request.relative_directory =
task_data->relative_directory;
import_request.type_identifier =
task_data->type_identifier;
import_request.collected_at =
task_data->collected_at;
import_request.source =
task_data->source;
import_request.description =
task_data->description;
background_task_report_progress(
task,
0.20,
"Copie et vérification de la preuve"
);
evidence_record =
evidence_importer_import(
evidence_importer,
&import_request,
cancellable,
&import_error
);
if (evidence_record == NULL)
{
evidence_import_task_propagate_import_error(
error,
import_error
);
evidence_importer_free(
evidence_importer
);
database_close(
database
);
return FALSE;
}
background_task_report_progress(
task,
1.0,
"Preuve importée"
);
evidence_importer_free(
evidence_importer
);
database_close(
database
);
*result =
evidence_record;
return TRUE;
}
/**
* @brief Transmet une erreur secondaire au code appelant.
*/
static void evidence_import_task_propagate_start_error(
GError **error,
GError *start_error,
const char *prefix
)
{
if (start_error == NULL)
{
g_set_error_literal(
error,
EVIDENCE_IMPORT_TASK_ERROR,
EVIDENCE_IMPORT_TASK_ERROR_IMPORT,
"La tâche dimport na pas pu être démarrée."
);
return;
}
if (error != NULL)
{
g_propagate_prefixed_error(
error,
start_error,
"%s",
prefix
);
return;
}
g_clear_error(
&start_error
);
}
/**
* @brief Libère le résultat EvidenceRecord dune tâche.
*
* Cette fonction adapte evidence_record_free() au type GDestroyNotify.
*
* @param user_data Pointeur vers EvidenceRecord.
*/
static void evidence_import_task_result_free(
gpointer user_data
)
{
EvidenceRecord *evidence_record =
user_data;
evidence_record_free(
evidence_record
);
}
BackgroundTask *evidence_import_task_start(
TaskManager *task_manager,
const EvidenceImportTaskRequest *request,
BackgroundTaskCompletionCallback completion_callback,
gpointer completion_data,
GDestroyNotify completion_data_destroy,
GError **error
)
{
EvidenceImportTaskData *task_data =
NULL;
BackgroundTask *task = NULL;
char *source_name = NULL;
char *task_title = NULL;
GError *start_error = NULL;
g_return_val_if_fail(
error == NULL || *error == NULL,
NULL
);
if (task_manager == NULL ||
!evidence_import_task_request_is_valid(
request
))
{
g_set_error_literal(
error,
EVIDENCE_IMPORT_TASK_ERROR,
EVIDENCE_IMPORT_TASK_ERROR_INVALID_ARGUMENT,
"Les paramètres de la tâche dimport sont invalides."
);
return NULL;
}
task_data =
evidence_import_task_data_new(
request
);
if (task_data == NULL)
{
g_set_error_literal(
error,
EVIDENCE_IMPORT_TASK_ERROR,
EVIDENCE_IMPORT_TASK_ERROR_IMPORT,
"Impossible dallouer les données de la tâche dimport."
);
return NULL;
}
source_name =
g_path_get_basename(
request->source_path
);
task_title =
g_strdup_printf(
"Importer la preuve : %s",
source_name
);
task =
background_task_new(
task_title
);
g_free(
task_title
);
g_free(
source_name
);
if (task == NULL)
{
evidence_import_task_data_free(
task_data
);
g_set_error_literal(
error,
EVIDENCE_IMPORT_TASK_ERROR,
EVIDENCE_IMPORT_TASK_ERROR_IMPORT,
"Impossible de créer la tâche dimport."
);
return NULL;
}
if (!task_manager_add(
task_manager,
task,
&start_error
))
{
evidence_import_task_data_free(
task_data
);
background_task_unref(
task
);
evidence_import_task_propagate_start_error(
error,
start_error,
"Impossible dajouter la tâche : "
);
return NULL;
}
if (!background_task_start(
task,
evidence_import_task_worker,
task_data,
evidence_import_task_data_free,
evidence_import_task_result_free,
completion_callback,
completion_data,
completion_data_destroy,
&start_error
))
{
/*
* background_task_start() na pas pris possession
* de task_data ni de completion_data en cas déchec.
*/
evidence_import_task_data_free(
task_data
);
task_manager_remove(
task_manager,
task
);
background_task_unref(
task
);
evidence_import_task_propagate_start_error(
error,
start_error,
"Impossible de démarrer la tâche : "
);
return NULL;
}
return task;
}

236
src/views/file_dialog.c Normal file
View file

@ -0,0 +1,236 @@
/******************************************************************************
* @file file_dialog.c
* @brief Implémentation du dialogue de sélection dun fichier de preuve.
******************************************************************************/
#include "views/file_dialog.h"
#include <gio/gio.h>
/**
* @brief Contexte conservé pendant lopération asynchrone.
*/
typedef struct
{
FileDialogCallback callback;
gpointer user_data;
} FileDialogContext;
/**
* @brief Libère le contexte du dialogue.
*/
static void file_dialog_context_free(
FileDialogContext *context
)
{
g_free(
context
);
}
/**
* @brief Transmet le résultat au code appelant.
*/
static void file_dialog_emit_result(
FileDialogContext *context,
const char *file_path,
const GError *error
)
{
if (context == NULL ||
context->callback == NULL)
{
return;
}
context->callback(
file_path,
error,
context->user_data
);
}
/**
* @brief Traite le résultat du sélecteur de fichier.
*/
static void file_dialog_on_file_selected(
GObject *source_object,
GAsyncResult *result,
gpointer user_data
)
{
GtkFileDialog *dialog =
GTK_FILE_DIALOG(
source_object
);
FileDialogContext *context =
user_data;
GFile *file = NULL;
char *file_path = NULL;
GError *error = NULL;
GError *path_error = NULL;
if (context == NULL)
{
return;
}
file =
gtk_file_dialog_open_finish(
dialog,
result,
&error
);
if (file == NULL)
{
/*
* Fermer volontairement le dialogue nest pas une erreur.
*/
if (error != NULL &&
g_error_matches(
error,
GTK_DIALOG_ERROR,
GTK_DIALOG_ERROR_DISMISSED
))
{
file_dialog_emit_result(
context,
NULL,
NULL
);
}
else
{
file_dialog_emit_result(
context,
NULL,
error
);
}
g_clear_error(
&error
);
file_dialog_context_free(
context
);
return;
}
file_path =
g_file_get_path(
file
);
/*
* g_file_get_path() retourne NULL pour une ressource qui ne
* correspond pas à un fichier local.
*/
if (file_path == NULL)
{
g_set_error_literal(
&path_error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
"Le fichier sélectionné ne possède pas de chemin local."
);
file_dialog_emit_result(
context,
NULL,
path_error
);
g_clear_error(
&path_error
);
g_object_unref(
file
);
file_dialog_context_free(
context
);
return;
}
file_dialog_emit_result(
context,
file_path,
NULL
);
g_free(
file_path
);
g_object_unref(
file
);
file_dialog_context_free(
context
);
}
void file_dialog_select_file(
GtkWindow *parent,
FileDialogCallback callback,
gpointer user_data
)
{
GtkFileDialog *dialog = NULL;
FileDialogContext *context = NULL;
if (callback == NULL)
{
return;
}
context =
g_new0(
FileDialogContext,
1
);
context->callback =
callback;
context->user_data =
user_data;
dialog =
gtk_file_dialog_new();
gtk_file_dialog_set_title(
dialog,
"Sélectionner un fichier de preuve"
);
gtk_file_dialog_set_modal(
dialog,
TRUE
);
gtk_file_dialog_open(
dialog,
parent,
NULL,
file_dialog_on_file_selected,
context
);
g_object_unref(
dialog
);
}

View file

@ -58,7 +58,7 @@ struct MainWindow
GtkWidget *action_bar;
GtkWidget *new_investigation_button;
GtkWidget *open_investigation_button;
GtkWidget *demo_task_button;
GtkWidget *import_evidence_button;
GtkWidget *content_paned;
GtkWidget *main_paned;
GtkWidget *status_label;
@ -80,17 +80,18 @@ struct MainWindow
gpointer
open_investigation_user_data;
MainWindowImportEvidenceCallback
import_evidence_callback;
gpointer
import_evidence_user_data;
MainWindowQuitCallback
quit_callback;
gpointer
quit_user_data;
MainWindowDemoTaskCallback
demo_task_callback;
gpointer
demo_task_user_data;
};
/**
@ -146,12 +147,12 @@ static void main_window_on_open_investigation_clicked(
}
/**
* @brief Transmet la demande de tâche de démonstration au contrôleur.
* @brief Transmet la demande d'import d'une preuve au contrôleur.
*
* @param button Bouton ayant reçu le clic.
* @param user_data Pointeur vers MainWindow.
*/
static void main_window_on_demo_task_clicked(
static void main_window_on_import_evidence_clicked(
GtkButton *button,
gpointer user_data
)
@ -161,13 +162,13 @@ static void main_window_on_demo_task_clicked(
(void) button;
if (main_window == NULL ||
main_window->demo_task_callback == NULL)
main_window->import_evidence_callback == NULL)
{
return;
}
main_window->demo_task_callback(
main_window->demo_task_user_data
main_window->import_evidence_callback(
main_window->import_evidence_user_data
);
}
@ -197,6 +198,54 @@ static void main_window_on_quit_clicked(
);
}
/**
* @brief Crée un bouton d'action compact avec une icône.
*
* @param icon_name Nom de l'icône symbolique GTK.
* @param tooltip_text Texte de l'infobulle.
*
* @return Nouveau bouton GTK.
*/
static GtkWidget *main_window_create_action_button(
const char *icon_name,
const char *tooltip_text
)
{
GtkWidget *button = NULL;
if (icon_name == NULL ||
icon_name[0] == '\0')
{
return NULL;
}
button =
gtk_button_new_from_icon_name(
icon_name
);
if (button == NULL)
{
return NULL;
}
gtk_widget_add_css_class(
button,
"flat"
);
if (tooltip_text != NULL &&
tooltip_text[0] != '\0')
{
gtk_widget_set_tooltip_text(
button,
tooltip_text
);
}
return button;
}
MainWindow *main_window_new(
GtkApplication *application,
TaskManager *task_manager
@ -206,6 +255,7 @@ MainWindow *main_window_new(
GtkWidget *sidebar_widget = NULL;
GtkWidget *workspace_widget = NULL;
GtkWidget *task_panel_widget = NULL;
GtkWidget *action_spacer = NULL;
if (application == NULL ||
task_manager == NULL)
@ -252,7 +302,7 @@ MainWindow *main_window_new(
*/
main_window->action_bar = gtk_box_new(
GTK_ORIENTATION_HORIZONTAL,
8
4
);
gtk_widget_set_margin_start(
@ -276,18 +326,46 @@ MainWindow *main_window_new(
);
main_window->new_investigation_button =
gtk_button_new_with_label(
"Nouvelle enquête"
main_window_create_action_button(
"document-new-symbolic",
"Créer une nouvelle enquête"
);
main_window->open_investigation_button =
gtk_button_new_with_label(
main_window_create_action_button(
"document-open-symbolic",
"Ouvrir une enquête"
);
main_window->demo_task_button =
gtk_button_new_with_label(
"Tâche de test"
main_window->import_evidence_button =
main_window_create_action_button(
"document-save-symbolic",
"Importer une preuve"
);
main_window->quit_button =
main_window_create_action_button(
"application-exit-symbolic",
"Quitter Labfy Investigation"
);
action_spacer =
gtk_box_new(
GTK_ORIENTATION_HORIZONTAL,
0
);
gtk_widget_set_hexpand(
action_spacer,
TRUE
);
/*
* Aucun import n'est autorisé sans enquête ouverte.
*/
gtk_widget_set_sensitive(
main_window->import_evidence_button,
FALSE
);
main_window->quit_button =
@ -307,7 +385,12 @@ MainWindow *main_window_new(
gtk_box_append(
GTK_BOX(main_window->action_bar),
main_window->demo_task_button
main_window->import_evidence_button
);
gtk_box_append(
GTK_BOX(main_window->action_bar),
action_spacer
);
gtk_box_append(
@ -334,10 +417,10 @@ MainWindow *main_window_new(
);
g_signal_connect(
main_window->demo_task_button,
main_window->import_evidence_button,
"clicked",
G_CALLBACK(
main_window_on_demo_task_clicked
main_window_on_import_evidence_clicked
),
main_window
);
@ -795,9 +878,9 @@ void main_window_set_open_investigation_callback(
main_window->open_investigation_user_data = user_data;
}
void main_window_set_demo_task_callback(
void main_window_set_import_evidence_callback(
MainWindow *main_window,
MainWindowDemoTaskCallback callback,
MainWindowImportEvidenceCallback callback,
gpointer user_data
)
{
@ -806,8 +889,28 @@ void main_window_set_demo_task_callback(
return;
}
main_window->demo_task_callback = callback;
main_window->demo_task_user_data = user_data;
main_window->import_evidence_callback =
callback;
main_window->import_evidence_user_data =
user_data;
}
void main_window_set_import_evidence_enabled(
MainWindow *main_window,
gboolean enabled
)
{
if (main_window == NULL ||
main_window->import_evidence_button == NULL)
{
return;
}
gtk_widget_set_sensitive(
main_window->import_evidence_button,
enabled
);
}
void main_window_set_quit_callback(

View file

@ -146,11 +146,6 @@ void sidebar_set_tree_model(
return;
}
if (sidebar == NULL)
{
return;
}
investigation_tree_view_set_model(
sidebar->tree_view,
tree_model

View file

@ -0,0 +1,798 @@
/******************************************************************************
* @file test_evidence_import_task.c
* @brief Tests de la tâche asynchrone dimport dune preuve.
******************************************************************************/
#include "core/evidence_import_task.h"
#include "core/background_task.h"
#include "core/task_manager.h"
#include "dao/evidence_dao.h"
#include "database/database.h"
#include "models/evidence_record.h"
#include "core/evidence_importer_test.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <glib.h>
#include <glib/gstdio.h>
#define TEST_RELATIVE_DIRECTORY \
"01_Preuves_Originales/Documents"
/**
* @brief Contexte utilisé pour attendre la fin asynchrone.
*/
typedef struct
{
GMainLoop *main_loop;
gboolean completed;
gboolean timed_out;
} TestCompletionContext;
/**
* @brief Environnement temporaire dun test.
*/
typedef struct
{
char *root_directory;
char *database_path;
char *source_directory;
char *evidence_directory;
char *destination_directory;
TaskManager *task_manager;
} TestEvidenceImportTaskFixture;
/**
* @brief Quitte la boucle principale lorsque la tâche se termine.
*/
static void test_evidence_import_task_completed(
BackgroundTask *task,
gpointer user_data
)
{
TestCompletionContext *context =
user_data;
assert(task != NULL);
assert(context != NULL);
context->completed = TRUE;
g_main_loop_quit(
context->main_loop
);
}
/**
* @brief Empêche un test asynchrone de rester bloqué.
*/
static gboolean test_evidence_import_task_timeout(
gpointer user_data
)
{
TestCompletionContext *context =
user_data;
assert(context != NULL);
context->timed_out = TRUE;
g_main_loop_quit(
context->main_loop
);
return G_SOURCE_REMOVE;
}
/**
* @brief Attend la fin dune tâche avec un timeout.
*/
static void test_evidence_import_task_wait(
TestCompletionContext *context
)
{
guint timeout_source_id = 0;
assert(context != NULL);
assert(context->main_loop != NULL);
timeout_source_id =
g_timeout_add_seconds(
5,
test_evidence_import_task_timeout,
context
);
assert(timeout_source_id != 0);
g_main_loop_run(
context->main_loop
);
if (!context->timed_out)
{
assert(
g_source_remove(
timeout_source_id
)
);
}
assert(!context->timed_out);
assert(context->completed);
}
/**
* @brief Compte les fichiers présents dans la destination.
*/
static guint test_evidence_import_task_count_destination_entries(
const TestEvidenceImportTaskFixture *fixture
)
{
GDir *directory = NULL;
const char *entry_name = NULL;
guint entry_count = 0;
GError *error = NULL;
assert(fixture != NULL);
directory =
g_dir_open(
fixture->destination_directory,
0,
&error
);
assert(directory != NULL);
assert(error == NULL);
while ((entry_name = g_dir_read_name(directory)) != NULL)
{
assert(entry_name[0] != '\0');
entry_count++;
}
g_dir_close(
directory
);
return entry_count;
}
/**
* @brief Crée une enquête temporaire.
*/
static TestEvidenceImportTaskFixture
test_evidence_import_task_fixture_create(void)
{
TestEvidenceImportTaskFixture fixture =
{0};
GError *error = NULL;
fixture.root_directory =
g_dir_make_tmp(
"labfy-evidence-import-task-test-XXXXXX",
&error
);
assert(fixture.root_directory != NULL);
assert(error == NULL);
fixture.database_path =
g_build_filename(
fixture.root_directory,
"Enquete.sqlite",
NULL
);
fixture.source_directory =
g_build_filename(
fixture.root_directory,
"sources",
NULL
);
fixture.evidence_directory =
g_build_filename(
fixture.root_directory,
"01_Preuves_Originales",
NULL
);
fixture.destination_directory =
g_build_filename(
fixture.evidence_directory,
"Documents",
NULL
);
assert(fixture.database_path != NULL);
assert(fixture.source_directory != NULL);
assert(fixture.evidence_directory != NULL);
assert(fixture.destination_directory != NULL);
assert(
g_mkdir(
fixture.source_directory,
0700
) == 0
);
assert(
g_mkdir(
fixture.evidence_directory,
0700
) == 0
);
assert(
g_mkdir(
fixture.destination_directory,
0700
) == 0
);
assert(
database_initialize(
fixture.database_path,
"Enquete_EvidenceImportTask",
fixture.root_directory
)
);
fixture.task_manager =
task_manager_new();
assert(fixture.task_manager != NULL);
return fixture;
}
/**
* @brief Détruit une fixture déjà débarrassée de ses fichiers.
*/
static void test_evidence_import_task_fixture_clear(
TestEvidenceImportTaskFixture *fixture
)
{
assert(fixture != NULL);
task_manager_free(
fixture->task_manager
);
assert(
g_remove(
fixture->database_path
) == 0
);
assert(
g_rmdir(
fixture->destination_directory
) == 0
);
assert(
g_rmdir(
fixture->evidence_directory
) == 0
);
assert(
g_rmdir(
fixture->source_directory
) == 0
);
assert(
g_rmdir(
fixture->root_directory
) == 0
);
g_free(
fixture->destination_directory
);
g_free(
fixture->evidence_directory
);
g_free(
fixture->source_directory
);
g_free(
fixture->database_path
);
g_free(
fixture->root_directory
);
}
/**
* @brief Vérifie le refus des paramètres invalides.
*/
static void test_evidence_import_task_invalid_arguments(void)
{
EvidenceImportTaskRequest request =
{0};
BackgroundTask *task = NULL;
GError *error = NULL;
task =
evidence_import_task_start(
NULL,
&request,
NULL,
NULL,
NULL,
&error
);
assert(task == NULL);
assert(error != NULL);
assert(
error->domain ==
EVIDENCE_IMPORT_TASK_ERROR
);
assert(
error->code ==
EVIDENCE_IMPORT_TASK_ERROR_INVALID_ARGUMENT
);
g_clear_error(
&error
);
}
/**
* @brief Vérifie un import asynchrone complet.
*/
static void test_evidence_import_task_success(void)
{
TestEvidenceImportTaskFixture fixture =
test_evidence_import_task_fixture_create();
TestCompletionContext completion_context =
{0};
EvidenceImportTaskRequest request =
{0};
BackgroundTask *task = NULL;
EvidenceRecord *evidence_record = NULL;
Database *database = NULL;
EvidenceDao *evidence_dao = NULL;
char *source_path = NULL;
char *destination_path = NULL;
guint64 evidence_count = 0;
GError *error = NULL;
source_path =
g_build_filename(
fixture.source_directory,
"preuve_asynchrone.txt",
NULL
);
assert(source_path != NULL);
assert(
g_file_set_contents(
source_path,
"preuve asynchrone",
-1,
&error
)
);
assert(error == NULL);
completion_context.main_loop =
g_main_loop_new(
NULL,
FALSE
);
assert(completion_context.main_loop != NULL);
request.database_path =
fixture.database_path;
request.source_path =
source_path;
request.destination_directory =
fixture.destination_directory;
request.relative_directory =
TEST_RELATIVE_DIRECTORY;
request.type_identifier =
"document";
request.source =
"Test automatisé EvidenceImportTask.";
request.description =
"Import réalisé depuis un thread secondaire.";
assert(
test_evidence_import_task_count_destination_entries(
&fixture
) == 0
);
task =
evidence_import_task_start(
fixture.task_manager,
&request,
test_evidence_import_task_completed,
&completion_context,
NULL,
&error
);
assert(task != NULL);
assert(error == NULL);
assert(
task_manager_get_count(
fixture.task_manager
) == 1
);
test_evidence_import_task_wait(
&completion_context
);
assert(
test_evidence_import_task_count_destination_entries(
&fixture
) == 1
);
assert(
background_task_get_state(
task
) ==
BACKGROUND_TASK_STATE_COMPLETED
);
assert(
background_task_get_progress(
task
) == 1.0
);
assert(
background_task_dup_error(
task
) == NULL
);
evidence_record =
background_task_get_result(
task
);
assert(evidence_record != NULL);
assert(
strcmp(
evidence_record_get_original_name(
evidence_record
),
"preuve_asynchrone.txt"
) == 0
);
assert(
evidence_record_get_integrity_status(
evidence_record
) ==
EVIDENCE_INTEGRITY_STATUS_VALID
);
destination_path =
g_build_filename(
fixture.destination_directory,
evidence_record_get_internal_name(
evidence_record
),
NULL
);
assert(destination_path != NULL);
assert(
g_file_test(
destination_path,
G_FILE_TEST_IS_REGULAR
)
);
/*
* Le worker a fermé sa connexion.
* Une nouvelle connexion permet de vérifier la persistance.
*/
database =
database_open(
fixture.database_path
);
assert(database != NULL);
evidence_dao =
evidence_dao_new(
database,
&error
);
assert(evidence_dao != NULL);
assert(error == NULL);
assert(
evidence_dao_count(
evidence_dao,
&evidence_count,
&error
)
);
assert(error == NULL);
assert(evidence_count == 1);
evidence_dao_free(
evidence_dao
);
database_close(
database
);
assert(
g_remove(
destination_path
) == 0
);
assert(
g_remove(
source_path
) == 0
);
g_free(
destination_path
);
g_free(
source_path
);
g_main_loop_unref(
completion_context.main_loop
);
/*
* La tâche reste encore possédée par le manager.
*/
background_task_unref(
task
);
test_evidence_import_task_fixture_clear(
&fixture
);
}
/**
* @brief Vérifie lannulation après la copie de la preuve.
*/
static void test_evidence_import_task_cancelled_after_copy(void)
{
TestEvidenceImportTaskFixture fixture =
test_evidence_import_task_fixture_create();
TestCompletionContext completion_context =
{0};
EvidenceImportTaskRequest request =
{0};
BackgroundTask *task = NULL;
char *source_path = NULL;
guint64 evidence_count = 42;
Database *database = NULL;
EvidenceDao *evidence_dao = NULL;
GError *error = NULL;
source_path =
g_build_filename(
fixture.source_directory,
"preuve_annulee.txt",
NULL
);
assert(source_path != NULL);
assert(
g_file_set_contents(
source_path,
"preuve annulée après copie",
-1,
&error
)
);
assert(error == NULL);
completion_context.main_loop =
g_main_loop_new(
NULL,
FALSE
);
assert(completion_context.main_loop != NULL);
request.database_path =
fixture.database_path;
request.source_path =
source_path;
request.destination_directory =
fixture.destination_directory;
request.relative_directory =
TEST_RELATIVE_DIRECTORY;
request.type_identifier =
"document";
evidence_importer_test_reset_hooks();
evidence_importer_test_set_cancel_after_copy(
TRUE
);
task =
evidence_import_task_start(
fixture.task_manager,
&request,
test_evidence_import_task_completed,
&completion_context,
NULL,
&error
);
assert(task != NULL);
assert(error == NULL);
test_evidence_import_task_wait(
&completion_context
);
assert(
background_task_get_state(
task
) ==
BACKGROUND_TASK_STATE_CANCELLED
);
assert(
test_evidence_import_task_count_destination_entries(
&fixture
) == 0
);
database =
database_open(
fixture.database_path
);
assert(database != NULL);
evidence_dao =
evidence_dao_new(
database,
&error
);
assert(evidence_dao != NULL);
assert(error == NULL);
assert(
evidence_dao_count(
evidence_dao,
&evidence_count,
&error
)
);
assert(error == NULL);
assert(evidence_count == 0);
evidence_dao_free(
evidence_dao
);
database_close(
database
);
/*
* Le fichier original reste intact.
*/
assert(
g_file_test(
source_path,
G_FILE_TEST_IS_REGULAR
)
);
evidence_importer_test_reset_hooks();
background_task_unref(
task
);
g_main_loop_unref(
completion_context.main_loop
);
assert(
g_remove(
source_path
) == 0
);
g_free(
source_path
);
test_evidence_import_task_fixture_clear(
&fixture
);
}
int main(void)
{
test_evidence_import_task_invalid_arguments();
test_evidence_import_task_success();
test_evidence_import_task_cancelled_after_copy();
printf(
"EvidenceImportTask : premiers tests valides.\n"
);
return 0;
}