diff --git a/README.md b/README.md index e5738c2..69e5116 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,8 @@ Le socle actuel comprend notamment : - détection de présence et de version ; - menu OSINT contextuel préparé pour les entités et relations du graphe ; - contexte de sélection OSINT indépendant de GTK et validé par des tests ; -- catalogue déterministe des actions compatibles avec la sélection OSINT. +- catalogue déterministe des actions compatibles avec la sélection OSINT ; +- disponibilité des actions OSINT synchronisée avec le registre d’outils. Les outils actuellement présents dans le catalogue initial sont : diff --git a/include/models/osint_action_catalog.h b/include/models/osint_action_catalog.h index 41f646b..7df961f 100644 --- a/include/models/osint_action_catalog.h +++ b/include/models/osint_action_catalog.h @@ -18,6 +18,15 @@ typedef struct OsintAction OsintAction; /** @brief Catalogue opaque possédant ses actions. */ typedef struct OsintActionCatalog OsintActionCatalog; +/** @brief État d'une dépendance externe vu par le catalogue OSINT. */ +typedef enum +{ + OSINT_ACTION_TOOL_STATE_UNKNOWN, + OSINT_ACTION_TOOL_STATE_AVAILABLE, + OSINT_ACTION_TOOL_STATE_MISSING, + OSINT_ACTION_TOOL_STATE_INCOMPATIBLE +} OsintActionToolState; + /** * @brief Crée le catalogue initial déterministe. * @@ -48,6 +57,21 @@ GPtrArray *osint_action_catalog_list_compatible( const OsintSelectionContext *context ); +/** + * @brief Actualise les actions dépendant d'un outil externe. + * + * @param catalog Catalogue à modifier. + * @param tool_identifier Identifiant stable de l'outil. + * @param state État traduit par la couche applicative. + * @param version Version détectée facultative. + */ +void osint_action_catalog_update_tool_state( + OsintActionCatalog *catalog, + const char *tool_identifier, + OsintActionToolState state, + const char *version +); + /** @brief Retourne l'identifiant stable emprunté d'une action. */ const char *osint_action_get_identifier(const OsintAction *action); @@ -68,6 +92,9 @@ gboolean osint_action_is_available(const OsintAction *action); /** @brief Retourne l'explication d'indisponibilité, ou NULL. */ const char *osint_action_get_unavailable_reason(const OsintAction *action); +/** @brief Retourne la version copiée de l'outil requis, ou NULL. */ +const char *osint_action_get_tool_version(const OsintAction *action); + G_END_DECLS #endif diff --git a/include/views/main_window.h b/include/views/main_window.h index 0492c28..a6a991a 100644 --- a/include/views/main_window.h +++ b/include/views/main_window.h @@ -12,6 +12,7 @@ #include "core/task_manager.h" #include "widgets/evidence_category_model.h" #include "models/evidence_record.h" +#include "models/osint_action_catalog.h" #include @@ -403,6 +404,21 @@ void main_window_set_status( const char *status_text ); +/** + * @brief Transmet au workspace l'état d'un outil OSINT externe. + * + * @param main_window Fenêtre principale. + * @param tool_identifier Identifiant stable de l'outil. + * @param state État traduit par l'application. + * @param version Version détectée facultative. + */ +void main_window_set_osint_tool_state( + MainWindow *main_window, + const char *tool_identifier, + OsintActionToolState state, + const char *version +); + /** * @brief Callback appelé lorsque l'utilisateur demande l'ouverture * d'une enquête existante. diff --git a/include/widgets/workspace.h b/include/widgets/workspace.h index 8555183..b13375d 100644 --- a/include/widgets/workspace.h +++ b/include/widgets/workspace.h @@ -8,6 +8,7 @@ #include "core/investigation_node.h" #include "models/evidence_record.h" +#include "models/osint_action_catalog.h" #include @@ -155,6 +156,21 @@ gboolean workspace_select_graph_relation( const char *relation_identifier ); +/** + * @brief Actualise l'état d'un outil requis par les actions OSINT. + * + * @param workspace Zone de travail. + * @param tool_identifier Identifiant stable de l'outil. + * @param state État traduit par la couche applicative. + * @param version Version détectée facultative. + */ +void workspace_set_osint_tool_state( + Workspace *workspace, + const char *tool_identifier, + OsintActionToolState state, + const char *version +); + /** * @brief Définit le callback de vérification de la preuve affichée. * diff --git a/src/core/application.c b/src/core/application.c index ead615a..10ef095 100644 --- a/src/core/application.c +++ b/src/core/application.c @@ -729,6 +729,42 @@ static void application_present_error( const char *message ); +/** + * @brief Publie dans l'interface les états du registre d'outils. + */ +static void application_publish_osint_tool_states( + Application *application +) +{ + ToolRegistry *tool_registry = NULL; + gsize tool_index = 0; + + if (application == NULL || application->main_window == NULL || + application->tool_initializer == NULL) + { + return; + } + + tool_registry = tool_initializer_get_registry(application->tool_initializer); + for (tool_index = 0; + tool_registry != NULL && tool_index < tool_registry_get_count(tool_registry); + tool_index++) + { + const ToolInfo *tool_info = tool_registry_get_tool(tool_registry, tool_index); + OsintActionToolState state = OSINT_ACTION_TOOL_STATE_UNKNOWN; + if (tool_info_get_availability(tool_info) == TOOL_AVAILABILITY_AVAILABLE) + state = OSINT_ACTION_TOOL_STATE_AVAILABLE; + else if (tool_info_get_availability(tool_info) == TOOL_AVAILABILITY_MISSING) + state = OSINT_ACTION_TOOL_STATE_MISSING; + main_window_set_osint_tool_state( + application->main_window, + tool_info_get_identifier(tool_info), + state, + tool_info_get_detected_version(tool_info) + ); + } +} + /** * @brief Traite la fin de l’initialisation des outils externes. * @@ -828,6 +864,8 @@ static void application_on_tool_initialization_completed( break; } + + application_publish_osint_tool_states(application); } /** diff --git a/src/models/osint_action_catalog.c b/src/models/osint_action_catalog.c index 5d9d29e..ef5d3f5 100644 --- a/src/models/osint_action_catalog.c +++ b/src/models/osint_action_catalog.c @@ -15,6 +15,7 @@ struct OsintAction char *required_tool_identifier; gboolean available; char *unavailable_reason; + char *tool_version; }; struct OsintActionCatalog @@ -28,6 +29,7 @@ static void osint_action_free(gpointer data) OsintAction *action = data; if (action == NULL) return; g_free(action->unavailable_reason); + g_free(action->tool_version); g_free(action->compatible_type); g_free(action->required_tool_identifier); g_free(action->description); @@ -83,8 +85,8 @@ OsintActionCatalog *osint_action_catalog_new_defaults(void) dns_action = osint_action_new( "dns-preview", "Résolution DNS", "Préfiguration de l'adaptateur DNS.", - OSINT_SELECTION_CONTEXT_KIND_ENTITY, "domain", "dig", FALSE, - "L'adaptateur DNS n'est pas encore intégré." + OSINT_SELECTION_CONTEXT_KIND_ENTITY, "domain_name", "dns.dig", FALSE, + "L'outil requis n'a pas encore été vérifié." ); if (catalog->actions == NULL || demonstration_action == NULL || dns_action == NULL) @@ -134,6 +136,35 @@ GPtrArray *osint_action_catalog_list_compatible( return compatible_actions; } +void osint_action_catalog_update_tool_state( + OsintActionCatalog *catalog, + const char *tool_identifier, + OsintActionToolState state, + const char *version +) +{ + guint index = 0; + if (catalog == NULL || catalog->actions == NULL || + tool_identifier == NULL || tool_identifier[0] == '\0') return; + for (index = 0; index < catalog->actions->len; index++) + { + OsintAction *action = g_ptr_array_index(catalog->actions, index); + if (g_strcmp0(action->required_tool_identifier, tool_identifier) != 0) + continue; + g_clear_pointer(&action->tool_version, g_free); + g_clear_pointer(&action->unavailable_reason, g_free); + action->available = state == OSINT_ACTION_TOOL_STATE_AVAILABLE; + if (action->available) + action->tool_version = g_strdup(version); + else if (state == OSINT_ACTION_TOOL_STATE_MISSING) + action->unavailable_reason = g_strdup("L'outil requis est absent."); + else if (state == OSINT_ACTION_TOOL_STATE_INCOMPATIBLE) + action->unavailable_reason = g_strdup("La version installée est incompatible."); + else + action->unavailable_reason = g_strdup("L'outil requis n'a pas encore été vérifié."); + } +} + void osint_action_catalog_free(OsintActionCatalog *catalog) { if (catalog == NULL) return; @@ -153,3 +184,5 @@ gboolean osint_action_is_available(const OsintAction *action) { return action != NULL && action->available; } const char *osint_action_get_unavailable_reason(const OsintAction *action) { return action != NULL ? action->unavailable_reason : NULL; } +const char *osint_action_get_tool_version(const OsintAction *action) +{ return action != NULL ? action->tool_version : NULL; } diff --git a/src/views/main_window.c b/src/views/main_window.c index f65d69f..01acb82 100644 --- a/src/views/main_window.c +++ b/src/views/main_window.c @@ -1225,6 +1225,26 @@ void main_window_set_status( ); } +void main_window_set_osint_tool_state( + MainWindow *main_window, + const char *tool_identifier, + OsintActionToolState state, + const char *version +) +{ + if (main_window == NULL) + { + return; + } + + workspace_set_osint_tool_state( + main_window->workspace, + tool_identifier, + state, + version + ); +} + void main_window_set_tree_selection_callback( MainWindow *main_window, InvestigationTreeViewSelectionCallback callback, diff --git a/src/widgets/workspace.c b/src/widgets/workspace.c index 0870e80..b8b6d1b 100644 --- a/src/widgets/workspace.c +++ b/src/widgets/workspace.c @@ -259,9 +259,13 @@ static void workspace_update_osint_tools_menu( compatible_actions, action_index ); - GtkWidget *action_button = gtk_button_new_with_label( - osint_action_get_label(action) - ); + const char *tool_version = osint_action_get_tool_version(action); + char *button_label = tool_version != NULL && tool_version[0] != '\0' + ? g_strdup_printf("%s — %s", osint_action_get_label(action), tool_version) + : g_strdup(osint_action_get_label(action)); + GtkWidget *action_button = gtk_button_new_with_label(button_label); + + g_free(button_label); if (action_button == NULL) { @@ -2324,6 +2328,30 @@ gboolean workspace_select_graph_relation( ); } +void workspace_set_osint_tool_state( + Workspace *workspace, + const char *tool_identifier, + OsintActionToolState state, + const char *version +) +{ + if (workspace == NULL || workspace->osint_action_catalog == NULL) + { + return; + } + + osint_action_catalog_update_tool_state( + workspace->osint_action_catalog, + tool_identifier, + state, + version + ); + workspace_update_osint_tools_menu( + workspace, + workspace->osint_selection_context + ); +} + void workspace_set_graph_loading( Workspace *workspace ) diff --git a/tests/test_osint_action_catalog.c b/tests/test_osint_action_catalog.c index e816634..ee0637d 100644 --- a/tests/test_osint_action_catalog.c +++ b/tests/test_osint_action_catalog.c @@ -18,7 +18,7 @@ static OsintSelectionContext *create_entity_context(const char *type) static void test_domain_actions(void) { OsintActionCatalog *catalog = osint_action_catalog_new_defaults(); - OsintSelectionContext *context = create_entity_context("domain"); + OsintSelectionContext *context = create_entity_context("domain_name"); GPtrArray *actions = osint_action_catalog_list_compatible(catalog, context); g_assert_nonnull(actions); @@ -28,7 +28,7 @@ static void test_domain_actions(void) g_assert_true(osint_action_is_available(g_ptr_array_index(actions, 0))); g_assert_false(osint_action_is_available(g_ptr_array_index(actions, 1))); g_assert_cmpstr(osint_action_get_required_tool_identifier( - g_ptr_array_index(actions, 1)), ==, "dig"); + g_ptr_array_index(actions, 1)), ==, "dns.dig"); g_assert_nonnull(osint_action_get_unavailable_reason( g_ptr_array_index(actions, 1))); @@ -59,11 +59,40 @@ static void test_invalid_arguments(void) osint_action_catalog_free(NULL); } +static void test_tool_states(void) +{ + OsintActionCatalog *catalog = osint_action_catalog_new_defaults(); + OsintSelectionContext *context = create_entity_context("domain_name"); + GPtrArray *actions = NULL; + + osint_action_catalog_update_tool_state( + catalog, "dns.dig", OSINT_ACTION_TOOL_STATE_AVAILABLE, "DiG 9.20" + ); + actions = osint_action_catalog_list_compatible(catalog, context); + g_assert_true(osint_action_is_available(g_ptr_array_index(actions, 1))); + g_assert_cmpstr(osint_action_get_tool_version(g_ptr_array_index(actions, 1)), + ==, "DiG 9.20"); + g_ptr_array_unref(actions); + + osint_action_catalog_update_tool_state( + catalog, "dns.dig", OSINT_ACTION_TOOL_STATE_MISSING, NULL + ); + actions = osint_action_catalog_list_compatible(catalog, context); + g_assert_false(osint_action_is_available(g_ptr_array_index(actions, 1))); + g_assert_nonnull(osint_action_get_unavailable_reason( + g_ptr_array_index(actions, 1))); + g_ptr_array_unref(actions); + + osint_selection_context_free(context); + osint_action_catalog_free(catalog); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_func("/osint-action-catalog/domain", test_domain_actions); g_test_add_func("/osint-action-catalog/non-domain", test_non_domain_actions); g_test_add_func("/osint-action-catalog/invalid", test_invalid_arguments); + g_test_add_func("/osint-action-catalog/tool-states", test_tool_states); return g_test_run(); }