labfy-investigation/src/core/investigation_tree_builder.c
2026-07-13 16:14:41 +02:00

285 lines
6.7 KiB
C

/******************************************************************************
* @file investigation_tree_builder.c
* @brief Construction d'une arborescence d'enquête depuis le système de fichiers.
******************************************************************************/
#include "core/investigation_tree_builder.h"
#include "core/investigation_node.h"
#include "core/investigation_tree_model.h"
#include <stdbool.h>
#include <gio/gio.h>
#include <glib.h>
/**
* @brief Attributs demandés à GIO pendant le parcours d'un dossier.
*/
#define INVESTIGATION_TREE_FILE_ATTRIBUTES \
G_FILE_ATTRIBUTE_STANDARD_NAME "," \
G_FILE_ATTRIBUTE_STANDARD_TYPE "," \
G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK
/**
* @brief Construit récursivement les enfants d'un nœud dossier.
*
* @param directory Dossier GIO actuellement parcouru.
* @param parent_node Nœud représentant ce dossier dans notre modèle.
* @param error Adresse recevant une éventuelle erreur GIO.
*
* @return true si le parcours s'est terminé correctement, sinon false.
*/
static bool investigation_tree_builder_build_children(
GFile *directory,
InvestigationNode *parent_node,
GError **error
)
{
GFileEnumerator *enumerator = NULL;
GFileInfo *file_info = NULL;
bool success = true;
if (directory == NULL || parent_node == NULL)
{
return false;
}
enumerator = g_file_enumerate_children(
directory,
INVESTIGATION_TREE_FILE_ATTRIBUTES,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
NULL,
error
);
if (enumerator == NULL)
{
return false;
}
while (success)
{
const char *child_name = NULL;
GFileType child_file_type;
GFile *child_file = NULL;
InvestigationNode *child_node = NULL;
InvestigationNodeType child_node_type;
file_info = g_file_enumerator_next_file(
enumerator,
NULL,
error
);
if (file_info == NULL)
{
/*
* NULL signifie soit que le dossier est entièrement parcouru,
* soit qu'une erreur a eu lieu.
*/
if (error != NULL && *error != NULL)
{
success = false;
}
break;
}
/*
* Les liens symboliques sont volontairement ignorés afin d'éviter
* les boucles et les sorties involontaires du dossier d'enquête.
*/
if (g_file_info_get_is_symlink(file_info))
{
g_object_unref(file_info);
file_info = NULL;
continue;
}
child_name = g_file_info_get_name(file_info);
if (child_name == NULL || child_name[0] == '\0')
{
success = false;
g_object_unref(file_info);
file_info = NULL;
break;
}
child_file_type = g_file_info_get_file_type(file_info);
if (child_file_type == G_FILE_TYPE_DIRECTORY)
{
child_node_type = INVESTIGATION_NODE_DIRECTORY;
}
else
{
child_node_type = INVESTIGATION_NODE_FILE;
}
child_node = investigation_node_new(
child_name,
child_node_type
);
if (child_node == NULL)
{
success = false;
g_object_unref(file_info);
file_info = NULL;
break;
}
/*
* En cas de succès, parent_node devient propriétaire de child_node.
*/
if (!investigation_node_add_child(parent_node, child_node))
{
investigation_node_free(child_node);
success = false;
g_object_unref(file_info);
file_info = NULL;
break;
}
/*
* Seuls les dossiers sont parcourus récursivement.
*/
if (child_node_type == INVESTIGATION_NODE_DIRECTORY)
{
child_file = g_file_get_child(
directory,
child_name
);
if (child_file == NULL)
{
success = false;
g_object_unref(file_info);
file_info = NULL;
break;
}
success = investigation_tree_builder_build_children(
child_file,
child_node,
error
);
g_object_unref(child_file);
}
g_object_unref(file_info);
file_info = NULL;
}
/*
* Si la boucle a été interrompue avant la libération de file_info,
* cette vérification garantit un nettoyage correct.
*/
if (file_info != NULL)
{
g_object_unref(file_info);
}
g_object_unref(enumerator);
return success;
}
InvestigationTreeModel *investigation_tree_builder_build(
const char *root_path
)
{
GFile *root_file = NULL;
GFileType root_file_type;
char *root_name = NULL;
InvestigationNode *root_node = NULL;
InvestigationTreeModel *tree_model = NULL;
GError *error = NULL;
if (root_path == NULL || root_path[0] == '\0')
{
return NULL;
}
root_file = g_file_new_for_path(root_path);
if (root_file == NULL)
{
return NULL;
}
root_file_type = g_file_query_file_type(
root_file,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
NULL
);
if (root_file_type != G_FILE_TYPE_DIRECTORY)
{
g_object_unref(root_file);
return NULL;
}
root_name = g_file_get_basename(root_file);
if (root_name == NULL || root_name[0] == '\0')
{
g_free(root_name);
g_object_unref(root_file);
return NULL;
}
root_node = investigation_node_new(
root_name,
INVESTIGATION_NODE_DIRECTORY
);
g_free(root_name);
if (root_node == NULL)
{
g_object_unref(root_file);
return NULL;
}
if (!investigation_tree_builder_build_children(
root_file,
root_node,
&error
))
{
if (error != NULL)
{
g_warning(
"Impossible de construire l'arborescence : %s",
error->message
);
g_clear_error(&error);
}
investigation_node_free(root_node);
g_object_unref(root_file);
return NULL;
}
g_object_unref(root_file);
tree_model = investigation_tree_model_new(root_node);
if (tree_model == NULL)
{
/*
* Le transfert de propriété n'a pas eu lieu puisque la création
* du modèle a échoué.
*/
investigation_node_free(root_node);
return NULL;
}
return tree_model;
}