labfy-investigation/src/core/tool_registry.c
2026-07-17 15:14:05 +02:00

635 lines
12 KiB
C

/******************************************************************************
* @file tool_registry.c
* @brief Implémentation du registre des outils externes.
******************************************************************************/
#include "core/tool_registry.h"
/**
* @struct ToolInfo
* @brief Informations internes décrivant un outil externe.
*/
struct ToolInfo
{
char *identifier;
char *display_name;
char *executable_name;
ToolRequirement requirement;
ToolAvailability availability;
char *resolved_path;
char *detected_version;
};
/**
* @struct ToolRegistry
* @brief Registre interne des outils externes.
*/
struct ToolRegistry
{
GPtrArray *tools;
};
/**
* @brief Vérifie qu'une chaîne est définie et non vide.
*
* @param text Chaîne à vérifier.
*
* @return TRUE si la chaîne est valide.
*/
static gboolean tool_registry_string_is_valid(
const char *text
)
{
return text != NULL &&
text[0] != '\0';
}
/**
* @brief Vérifie qu'une importance de dépendance est valide.
*
* @param requirement Valeur à vérifier.
*
* @return TRUE si la valeur est reconnue.
*/
static gboolean tool_registry_requirement_is_valid(
ToolRequirement requirement
)
{
return requirement == TOOL_REQUIREMENT_OPTIONAL ||
requirement == TOOL_REQUIREMENT_REQUIRED;
}
/**
* @brief Libère un outil et toutes ses chaînes.
*
* Cette fonction est utilisée comme fonction de destruction du GPtrArray.
*
* @param user_data Pointeur vers ToolInfo.
*/
static void tool_info_free(
gpointer user_data
)
{
ToolInfo *tool_info = user_data;
if (tool_info == NULL)
{
return;
}
g_clear_pointer(
&tool_info->identifier,
g_free
);
g_clear_pointer(
&tool_info->display_name,
g_free
);
g_clear_pointer(
&tool_info->executable_name,
g_free
);
g_clear_pointer(
&tool_info->resolved_path,
g_free
);
g_clear_pointer(
&tool_info->detected_version,
g_free
);
g_free(
tool_info
);
}
/**
* @brief Recherche un outil modifiable dans le registre.
*
* @param tool_registry Registre consulté.
* @param identifier Identifiant recherché.
*
* @return Outil correspondant, ou NULL.
*/
static ToolInfo *tool_registry_find_internal(
const ToolRegistry *tool_registry,
const char *identifier
)
{
guint index = 0;
if (tool_registry == NULL ||
tool_registry->tools == NULL ||
!tool_registry_string_is_valid(identifier))
{
return NULL;
}
for (index = 0;
index < tool_registry->tools->len;
index++)
{
ToolInfo *tool_info = NULL;
tool_info = g_ptr_array_index(
tool_registry->tools,
index
);
if (tool_info != NULL &&
g_strcmp0(
tool_info->identifier,
identifier
) == 0)
{
return tool_info;
}
}
return NULL;
}
GQuark tool_registry_error_quark(void)
{
return g_quark_from_static_string(
"labfy-investigation-tool-registry-error"
);
}
ToolRegistry *tool_registry_new(void)
{
ToolRegistry *tool_registry = NULL;
tool_registry = g_new0(
ToolRegistry,
1
);
tool_registry->tools =
g_ptr_array_new_with_free_func(
tool_info_free
);
return tool_registry;
}
void tool_registry_free(
ToolRegistry *tool_registry
)
{
if (tool_registry == NULL)
{
return;
}
if (tool_registry->tools != NULL)
{
g_ptr_array_unref(
tool_registry->tools
);
tool_registry->tools = NULL;
}
g_free(
tool_registry
);
}
gboolean tool_registry_register(
ToolRegistry *tool_registry,
const char *identifier,
const char *display_name,
const char *executable_name,
ToolRequirement requirement,
GError **error
)
{
ToolInfo *tool_info = NULL;
g_return_val_if_fail(
error == NULL || *error == NULL,
FALSE
);
if (tool_registry == NULL ||
tool_registry->tools == NULL ||
!tool_registry_string_is_valid(identifier) ||
!tool_registry_string_is_valid(display_name) ||
!tool_registry_string_is_valid(executable_name) ||
!tool_registry_requirement_is_valid(requirement))
{
g_set_error_literal(
error,
TOOL_REGISTRY_ERROR,
TOOL_REGISTRY_ERROR_INVALID_ARGUMENT,
"Les arguments fournis au registre d'outils sont invalides."
);
return FALSE;
}
if (tool_registry_find_internal(
tool_registry,
identifier
) != NULL)
{
g_set_error(
error,
TOOL_REGISTRY_ERROR,
TOOL_REGISTRY_ERROR_DUPLICATE_IDENTIFIER,
"Un outil portant l'identifiant '%s' est déjà enregistré.",
identifier
);
return FALSE;
}
tool_info = g_new0(
ToolInfo,
1
);
tool_info->identifier = g_strdup(
identifier
);
tool_info->display_name = g_strdup(
display_name
);
tool_info->executable_name = g_strdup(
executable_name
);
tool_info->requirement = requirement;
tool_info->availability =
TOOL_AVAILABILITY_UNKNOWN;
tool_info->resolved_path = NULL;
tool_info->detected_version = NULL;
g_ptr_array_add(
tool_registry->tools,
tool_info
);
return TRUE;
}
gboolean tool_registry_refresh(
ToolRegistry *tool_registry,
GError **error
)
{
guint index = 0;
g_return_val_if_fail(
error == NULL || *error == NULL,
FALSE
);
if (tool_registry == NULL ||
tool_registry->tools == NULL)
{
g_set_error_literal(
error,
TOOL_REGISTRY_ERROR,
TOOL_REGISTRY_ERROR_INVALID_ARGUMENT,
"Le registre d'outils est invalide."
);
return FALSE;
}
for (index = 0;
index < tool_registry->tools->len;
index++)
{
ToolInfo *tool_info = NULL;
char *found_path = NULL;
char *canonical_path = NULL;
tool_info = g_ptr_array_index(
tool_registry->tools,
index
);
if (tool_info == NULL)
{
continue;
}
found_path = g_find_program_in_path(
tool_info->executable_name
);
g_clear_pointer(
&tool_info->resolved_path,
g_free
);
if (found_path == NULL)
{
tool_info->availability =
TOOL_AVAILABILITY_MISSING;
continue;
}
/*
* g_find_program_in_path() retourne une nouvelle chaîne.
*
* La canonicalisation garantit que le registre mémorise un
* chemin absolu et normalisé.
*/
canonical_path = g_canonicalize_filename(
found_path,
NULL
);
g_free(
found_path
);
tool_info->resolved_path =
canonical_path;
tool_info->availability =
TOOL_AVAILABILITY_AVAILABLE;
}
return TRUE;
}
gsize tool_registry_get_count(
const ToolRegistry *tool_registry
)
{
if (tool_registry == NULL ||
tool_registry->tools == NULL)
{
return 0;
}
return tool_registry->tools->len;
}
const ToolInfo *tool_registry_get_tool(
const ToolRegistry *tool_registry,
gsize index
)
{
if (tool_registry == NULL ||
tool_registry->tools == NULL ||
index >= tool_registry->tools->len)
{
return NULL;
}
return g_ptr_array_index(
tool_registry->tools,
(guint) index
);
}
const ToolInfo *tool_registry_find(
const ToolRegistry *tool_registry,
const char *identifier
)
{
return tool_registry_find_internal(
tool_registry,
identifier
);
}
gboolean tool_registry_set_version(
ToolRegistry *tool_registry,
const char *identifier,
const char *detected_version,
GError **error
)
{
ToolInfo *tool_info = NULL;
g_return_val_if_fail(
error == NULL || *error == NULL,
FALSE
);
if (tool_registry == NULL ||
tool_registry->tools == NULL ||
!tool_registry_string_is_valid(identifier))
{
g_set_error_literal(
error,
TOOL_REGISTRY_ERROR,
TOOL_REGISTRY_ERROR_INVALID_ARGUMENT,
"Le registre ou l'identifiant de l'outil est invalide."
);
return FALSE;
}
tool_info = tool_registry_find_internal(
tool_registry,
identifier
);
if (tool_info == NULL)
{
g_set_error(
error,
TOOL_REGISTRY_ERROR,
TOOL_REGISTRY_ERROR_NOT_FOUND,
"Aucun outil portant l'identifiant '%s' n'est enregistré.",
identifier
);
return FALSE;
}
g_clear_pointer(
&tool_info->detected_version,
g_free
);
if (tool_registry_string_is_valid(
detected_version
))
{
tool_info->detected_version =
g_strdup(
detected_version
);
}
return TRUE;
}
gboolean tool_registry_has_missing_required_tools(
const ToolRegistry *tool_registry
)
{
guint index = 0;
if (tool_registry == NULL ||
tool_registry->tools == NULL)
{
return FALSE;
}
for (index = 0;
index < tool_registry->tools->len;
index++)
{
const ToolInfo *tool_info = NULL;
tool_info = g_ptr_array_index(
tool_registry->tools,
index
);
if (tool_info != NULL &&
tool_info->requirement ==
TOOL_REQUIREMENT_REQUIRED &&
tool_info->availability ==
TOOL_AVAILABILITY_MISSING)
{
return TRUE;
}
}
return FALSE;
}
gboolean tool_registry_all_required_tools_available(
const ToolRegistry *tool_registry
)
{
guint index = 0;
if (tool_registry == NULL ||
tool_registry->tools == NULL)
{
return FALSE;
}
for (index = 0;
index < tool_registry->tools->len;
index++)
{
const ToolInfo *tool_info = NULL;
tool_info = g_ptr_array_index(
tool_registry->tools,
index
);
if (tool_info != NULL &&
tool_info->requirement ==
TOOL_REQUIREMENT_REQUIRED &&
tool_info->availability !=
TOOL_AVAILABILITY_AVAILABLE)
{
return FALSE;
}
}
/*
* Lorsqu'aucun outil obligatoire n'est enregistré, toutes les
* dépendances obligatoires sont considérées comme satisfaites.
*/
return TRUE;
}
const char *tool_info_get_identifier(
const ToolInfo *tool_info
)
{
if (tool_info == NULL)
{
return NULL;
}
return tool_info->identifier;
}
const char *tool_info_get_display_name(
const ToolInfo *tool_info
)
{
if (tool_info == NULL)
{
return NULL;
}
return tool_info->display_name;
}
const char *tool_info_get_executable_name(
const ToolInfo *tool_info
)
{
if (tool_info == NULL)
{
return NULL;
}
return tool_info->executable_name;
}
ToolRequirement tool_info_get_requirement(
const ToolInfo *tool_info
)
{
if (tool_info == NULL)
{
return TOOL_REQUIREMENT_OPTIONAL;
}
return tool_info->requirement;
}
ToolAvailability tool_info_get_availability(
const ToolInfo *tool_info
)
{
if (tool_info == NULL)
{
return TOOL_AVAILABILITY_UNKNOWN;
}
return tool_info->availability;
}
const char *tool_info_get_resolved_path(
const ToolInfo *tool_info
)
{
if (tool_info == NULL)
{
return NULL;
}
return tool_info->resolved_path;
}
const char *tool_info_get_detected_version(
const ToolInfo *tool_info
)
{
if (tool_info == NULL)
{
return NULL;
}
return tool_info->detected_version;
}