Compare commits

..

2 commits

Author SHA1 Message Date
grayTerminal-sh
26a8f08187 delete: docs/tickets/ 2026-07-18 10:04:58 +02:00
grayTerminal-sh
36caf3101b docs: clarify Forgejo as primary repository 2026-07-18 10:03:29 +02:00
43 changed files with 23 additions and 17247 deletions

View file

@ -1,5 +1,28 @@
# Labfy Investigation # Labfy Investigation
> [!IMPORTANT]
> **Forgejo est le dépôt principal du projet.**
>
> Le code peut également être publié sur GitHub comme miroir public, mais le suivi du développement, les tickets, les décisions techniques et la feuille de route se trouvent sur :
>
> **https://git.labfytools.com/fy59/labfy-investigation**
>
> Tickets :
>
> **https://git.labfytools.com/fy59/labfy-investigation/issues**
>
> Les tickets et pull requests ouverts uniquement sur GitHub risquent de ne pas être suivis.
Labfy Investigation est un poste de travail libre dinvestigation numérique et dOSINT, développé en **C17** avec **GTK4**.
Le projet vise à fournir un environnement local, modulaire et traçable pour organiser une enquête, préserver les preuves originales, analyser des données, corréler des entités et produire des rapports exploitables.
> **État du projet : développement actif**
>
> Le logiciel nest pas encore prêt pour un usage opérationnel en production. Les formats internes, linterface et les mécanismes dintégration peuvent encore évoluer.
---
Labfy Investigation est un poste de travail libre dinvestigation numérique et dOSINT, développé en **C17** avec **GTK4**. Labfy Investigation est un poste de travail libre dinvestigation numérique et dOSINT, développé en **C17** avec **GTK4**.
Le projet vise à fournir un environnement local, modulaire et traçable pour organiser une enquête, préserver les preuves originales, analyser des données, corréler des entités et produire des rapports exploitables. Le projet vise à fournir un environnement local, modulaire et traçable pour organiser une enquête, préserver les preuves originales, analyser des données, corréler des entités et produire des rapports exploitables.

View file

@ -1,37 +0,0 @@
# Ticket #001
## Titre
Créer le module Application.
## Objectif
Créer le point dentrée applicatif chargé de gérer le cycle de vie de GTK.
## Responsabilités
- créer lobjet GtkApplication ;
- lancer la boucle principale GTK ;
- libérer proprement les ressources ;
- afficher une fenêtre minimale au signal `activate`.
## Hors périmètre
- SQLite ;
- ouverture dune enquête ;
- sélecteur de dossier ;
- logique métier.
## Critères dacceptation
- [ ] Le projet compile en C17.
- [ ] Aucun warning.
- [ ] Aucun état global.
- [ ] Les fonctions publiques sont documentées avec Doxygen.
- [ ] Une fenêtre GTK minimale saffiche.
- [ ] La fermeture de lapplication est propre.
## Commit attendu
```text
feat(core): create application lifecycle

View file

@ -1,38 +0,0 @@
# Ticket #002
## Titre
Ajouter le sélecteur de dossier denquête.
## Objectif
Permettre à lutilisateur de sélectionner un dossier denquête au lancement de lapplication.
## Responsabilités
- afficher un dialogue GTK de sélection de dossier ;
- retourner le dossier sélectionné ;
- gérer lannulation sans erreur ;
- transmettre le chemin au module Application.
## Hors périmètre
- création de `00_BaseDeDonnees` ;
- ouverture de SQLite ;
- initialisation du schéma ;
- validation complète dune enquête.
## Critères dacceptation
- [ ] Le dialogue souvre au lancement.
- [ ] Un dossier peut être sélectionné.
- [ ] Lannulation est gérée proprement.
- [ ] Le chemin sélectionné est affiché dans la fenêtre.
- [ ] Aucun warning.
- [ ] Les fonctions publiques sont documentées.
- [ ] Aucun état global.
## Commit attendu
```text
feat(gui): add investigation folder selector

View file

@ -1,57 +0,0 @@
# Ticket #003
## Titre
Créer le module `Investigation`.
## Objectif
Créer l'objet métier représentant une enquête ouverte par Labfy Investigation.
Le module doit rester indépendant de GTK et de l'interface graphique.
## Responsabilités
Le module doit :
- mémoriser le chemin racine de l'enquête ;
- construire le chemin de la base de données modèle ;
- exposer ces chemins en lecture seule ;
- gérer proprement sa mémoire ;
- vérifier qu'un chemin d'enquête est exploitable.
## Hors périmètre
Ce ticket ne doit pas :
- ouvrir SQLite ;
- créer la base de données ;
- modifier l'arborescence de l'enquête ;
- afficher de message GTK ;
- gérer les preuves ou les entités.
## Dépendances
- libc ;
- GLib uniquement si nécessaire pour la gestion des chaînes et chemins.
Aucune dépendance GTK ou SQLite.
## API attendue
Le module doit permettre un usage proche de :
```c
Investigation *investigation = NULL;
investigation = investigation_new("/chemin/vers/enquete");
if (investigation == NULL)
{
/* Gestion de l'erreur */
}
const char *root_path = investigation_get_root_path(investigation);
const char *database_path = investigation_get_database_path(investigation);
investigation_free(investigation);

View file

@ -1,128 +0,0 @@
# Ticket #004
## Titre
Créer la fenêtre principale (`MainWindow`).
---
## Objectif
Créer la fenêtre principale de Labfy Investigation.
Cette fenêtre deviendra le point central de l'interface utilisateur.
À ce stade, elle ne contient qu'une structure vide destinée à accueillir les futurs composants.
---
## Responsabilités
Le module `MainWindow` doit :
- créer la fenêtre principale ;
- définir son titre ;
- définir sa taille par défaut ;
- afficher une zone centrale vide ;
- afficher une barre d'état.
---
## Hors périmètre
Ce ticket ne doit pas :
- afficher l'arborescence des fichiers ;
- ouvrir une enquête ;
- afficher les preuves ;
- communiquer avec SQLite ;
- gérer les menus.
---
## Architecture
Le module appartient à la couche **Views**.
Il est responsable uniquement de l'interface graphique.
Aucune logique métier ne doit être présente dans ce module.
---
## Dépendances
- GTK4
Aucune dépendance vers SQLite.
---
## Fichiers concernés
```text
include/views/main_window.h
src/views/main_window.c
```
---
## Interface attendue
```c
MainWindow *main_window_new(GtkApplication *application);
GtkWindow *main_window_get_window(
const MainWindow *main_window
);
void main_window_present(MainWindow *main_window);
void main_window_free(MainWindow *main_window);
```
---
## Interface graphique attendue
```text
┌───────────────────────────────────────────────────────────────┐
│ Labfy Investigation │
├───────────────────────────────────────────────────────────────┤
│ │
│ │
│ Zone de travail │
│ │
│ │
├───────────────────────────────────────────────────────────────┤
│ Aucune enquête ouverte │
└───────────────────────────────────────────────────────────────┘
```
---
## Critères d'acceptation
- [ ] Le projet compile sans warning.
- [ ] Une fenêtre s'ouvre correctement.
- [ ] La fenêtre est créée par le module `MainWindow`.
- [ ] `Application` n'appelle plus directement `gtk_application_window_new()`.
- [ ] Aucun état global.
- [ ] Documentation Doxygen.
- [ ] Gestion mémoire correcte.
---
## Tests
- Lancement de l'application.
- Fermeture de la fenêtre.
- Vérification de l'absence de fuite mémoire.
---
## Commit attendu
```text
feat(view): create main window
```

View file

@ -1,151 +0,0 @@
# Ticket #005
## Titre
Créer le panneau de navigation latéral (`Sidebar`).
---
## Objectif
Créer la structure principale de l'interface avec :
- un panneau latéral à gauche ;
- une zone de travail à droite ;
- une séparation redimensionnable entre les deux zones.
Le panneau latéral servira plus tard à afficher l'arborescence du dossier d'enquête.
---
## Responsabilités
Le module `Sidebar` doit :
- créer un panneau GTK réutilisable ;
- posséder une largeur initiale raisonnable ;
- afficher temporairement un titre ;
- exposer son widget racine en lecture seule.
Le module `MainWindow` doit :
- intégrer le panneau latéral ;
- conserver la zone de travail principale ;
- permettre le redimensionnement des deux zones.
---
## Hors périmètre
Ce ticket ne doit pas :
- afficher l'arborescence réelle des fichiers ;
- lire le contenu d'un dossier ;
- surveiller les changements du système de fichiers ;
- créer ou supprimer des fichiers ;
- ajouter le bouton permettant de masquer le panneau ;
- communiquer avec SQLite.
---
## Architecture
```text
MainWindow
├── Sidebar
├── Workspace
└── StatusLabel
```
Le module `Sidebar` appartient à la couche `widgets`.
Le module ne contient aucune logique métier.
---
## Fichiers concernés
```text
include/widgets/sidebar.h
src/widgets/sidebar.c
include/views/main_window.h
src/views/main_window.c
```
---
## Interface publique attendue
```c
Sidebar *sidebar_new(void);
GtkWidget *sidebar_get_widget(
const Sidebar *sidebar
);
void sidebar_free(Sidebar *sidebar);
```
---
## Interface graphique attendue
```text
┌───────────────────────┬──────────────────────────────────────┐
│ Dossier d'enquête │ │
│ │ │
│ │ │
│ │ Zone de travail │
│ │ │
│ │ │
├───────────────────────┴──────────────────────────────────────┤
│ Aucune enquête ouverte │
└──────────────────────────────────────────────────────────────┘
```
La séparation entre le panneau gauche et la zone centrale doit pouvoir être déplacée horizontalement.
---
## Contraintes techniques
- utiliser GTK4 ;
- utiliser un `GtkPaned` horizontal dans `MainWindow` ;
- ne pas utiliser de variable globale ;
- conserver une structure `Sidebar` opaque ;
- respecter les conventions de nommage du projet ;
- compiler en C17 sans warning.
---
## Critères d'acceptation
- [ ] Le projet compile sans warning.
- [ ] Le panneau latéral apparaît à gauche.
- [ ] La zone de travail apparaît à droite.
- [ ] La séparation peut être déplacée avec la souris.
- [ ] Le panneau est créé par le module `Sidebar`.
- [ ] `MainWindow` ne construit pas directement le contenu interne du panneau.
- [ ] Aucun état global.
- [ ] Les fonctions publiques sont documentées avec Doxygen.
- [ ] La fermeture de l'application ne provoque aucune erreur GTK.
---
## Tests
- lancer l'application ;
- sélectionner un dossier ;
- déplacer la séparation ;
- agrandir et réduire la fenêtre ;
- fermer l'application ;
- vérifier l'absence de warning ou de message critique.
---
## Commit attendu
```text
feat(widget): create navigation sidebar
```

View file

@ -1,153 +0,0 @@
# Ticket #006
## Titre
Créer le module `Workspace`.
---
## Objectif
Créer un composant graphique représentant la zone principale de travail de Labfy Investigation.
Le module doit être indépendant du reste de l'interface afin de pouvoir évoluer sans modifier `MainWindow`.
---
## Responsabilités
Le module `Workspace` doit :
- créer la zone centrale de l'application ;
- afficher une page d'accueil lorsqu'aucune enquête n'est ouverte ;
- fournir un widget racine réutilisable ;
- gérer uniquement son interface graphique.
---
## Hors périmètre
Ce ticket ne doit pas :
- afficher les preuves ;
- afficher les entités ;
- afficher un PDF ;
- afficher une image ;
- communiquer avec SQLite ;
- charger une enquête.
---
## Architecture
```text
MainWindow
├── Sidebar
├── Workspace
└── StatusBar
```
Le module appartient à la couche **Widgets**.
Aucune logique métier.
---
## Fichiers concernés
```text
include/widgets/workspace.h
src/widgets/workspace.c
```
---
## Interface publique
```c
Workspace *workspace_new(void);
GtkWidget *workspace_get_widget(
const Workspace *workspace
);
void workspace_free(
Workspace *workspace
);
```
---
## Interface graphique attendue
```text
+-------------------------------------------------------------+
| |
| |
| Labfy Investigation |
| |
| Aucune enquête ouverte |
| |
| Sélectionnez ou créez une enquête |
| |
| |
+-------------------------------------------------------------+
```
---
## Évolutions prévues
Cette zone accueillera plus tard :
- visualiseur d'image ;
- lecteur PDF ;
- chronologie ;
- fiche personne ;
- fiche IBAN ;
- fiche email ;
- fiche téléphone ;
- rapports ;
- cartes ;
- résultats OSINT.
Le module doit donc rester générique.
---
## Contraintes
- GTK4 uniquement ;
- structure opaque ;
- aucune variable globale ;
- documentation Doxygen ;
- C17 ;
- compilation sans warning.
---
## Critères d'acceptation
- [ ] Le projet compile.
- [ ] Le Workspace apparaît dans MainWindow.
- [ ] Le texte d'accueil est centré.
- [ ] Sidebar et Workspace sont totalement indépendants.
- [ ] Aucun warning.
- [ ] Aucun Gtk-CRITICAL.
---
## Tests
- lancement ;
- redimensionnement de la fenêtre ;
- fermeture ;
- vérification visuelle.
---
## Commit attendu
```text
feat(widget): create workspace
```

View file

@ -1,184 +0,0 @@
# Ticket #007
## Titre
Créer le module `InvestigationNode`.
---
## Objectif
Créer une structure métier représentant un élément de l'arborescence d'une enquête.
Un nœud peut représenter :
- un dossier ;
- un fichier.
Ce module servira de base au futur modèle d'arborescence.
---
## Responsabilités
Le module `InvestigationNode` doit :
- mémoriser le nom d'un élément ;
- mémoriser son type ;
- gérer sa propre mémoire ;
- exposer ses informations en lecture seule.
---
## Hors périmètre
Ce ticket ne doit pas :
- parcourir le système de fichiers ;
- construire une arborescence complète ;
- stocker des enfants ;
- afficher quoi que ce soit avec GTK ;
- communiquer avec SQLite ;
- ouvrir une enquête.
---
## Architecture
```text
InvestigationTreeModel
└── InvestigationNode
```
Le module appartient à la couche `core`.
Il ne doit contenir aucune dépendance vers GTK.
---
## Fichiers concernés
```text
include/core/investigation_node.h
src/core/investigation_node.c
```
---
## Type attendu
```c
typedef enum
{
INVESTIGATION_NODE_DIRECTORY,
INVESTIGATION_NODE_FILE
} InvestigationNodeType;
```
---
## Interface publique attendue
```c
InvestigationNode *investigation_node_new(
const char *name,
InvestigationNodeType type
);
void investigation_node_free(
InvestigationNode *node
);
const char *investigation_node_get_name(
const InvestigationNode *node
);
InvestigationNodeType investigation_node_get_type(
const InvestigationNode *node
);
```
---
## Comportement attendu
Exemple d'utilisation :
```c
InvestigationNode *node = NULL;
node = investigation_node_new(
"01_Preuves_Originales",
INVESTIGATION_NODE_DIRECTORY
);
if (node == NULL)
{
/* Gestion de l'erreur */
}
const char *name = investigation_node_get_name(node);
InvestigationNodeType type = investigation_node_get_type(node);
investigation_node_free(node);
```
---
## Cas à gérer
- nom valide ;
- nom vide ;
- pointeur `NULL` ;
- type dossier ;
- type fichier ;
- libération avec `NULL`.
---
## Contraintes techniques
- C17 ;
- structure opaque ;
- aucun état global ;
- aucune dépendance GTK ;
- documentation Doxygen ;
- compilation sans warning ;
- noms conformes à `DEVELOPMENT.md`.
---
## Critères d'acceptation
- [ ] Le projet compile sans warning.
- [ ] La structure `InvestigationNode` est opaque.
- [ ] Un nœud peut représenter un fichier.
- [ ] Un nœud peut représenter un dossier.
- [ ] Le nom est copié et possédé par le nœud.
- [ ] Les getters retournent des données en lecture seule.
- [ ] `investigation_node_free(NULL)` est accepté.
- [ ] Aucun code GTK.
- [ ] Aucun code SQLite.
- [ ] Les fonctions publiques sont documentées avec Doxygen.
---
## Tests
- créer un nœud dossier ;
- créer un nœud fichier ;
- lire le nom ;
- lire le type ;
- tester un nom vide ;
- tester un nom `NULL` ;
- libérer un nœud valide ;
- appeler `investigation_node_free(NULL)`.
---
## Commit attendu
```text
feat(core): create investigation node
```

View file

@ -1,158 +0,0 @@
# Ticket #008
## Titre
Créer le module `InvestigationTreeModel`.
---
## Objectif
Créer une structure métier représentant le modèle de l'arborescence d'une enquête.
Le modèle sera propriétaire du nœud racine et servira de base au futur explorateur d'enquête.
---
## Responsabilités
Le module `InvestigationTreeModel` doit :
- posséder un nœud racine ;
- gérer le cycle de vie du modèle ;
- exposer le nœud racine en lecture seule.
---
## Hors périmètre
Ce ticket ne doit pas :
- parcourir le système de fichiers ;
- créer les enfants d'un nœud ;
- afficher une interface GTK ;
- communiquer avec SQLite ;
- charger une enquête.
---
## Architecture
```text
Investigation
InvestigationTreeModel
InvestigationNode
```
Le module appartient à la couche **Core**.
Aucune dépendance vers GTK.
---
## Principe de propriété
Le module est **propriétaire** du nœud racine.
À partir du moment où un `InvestigationNode` est transmis au constructeur :
```c
investigation_tree_model_new(root_node);
```
le modèle devient responsable de sa destruction.
Le code appelant ne doit plus appeler :
```c
investigation_node_free(root_node);
```
La destruction du modèle doit automatiquement détruire son nœud racine.
---
## Fichiers concernés
```text
include/core/investigation_tree_model.h
src/core/investigation_tree_model.c
```
---
## Interface publique attendue
```c
InvestigationTreeModel *investigation_tree_model_new(
InvestigationNode *root_node
);
void investigation_tree_model_free(
InvestigationTreeModel *tree_model
);
const InvestigationNode *investigation_tree_model_get_root(
const InvestigationTreeModel *tree_model
);
```
---
## Comportement attendu
Le modèle contient uniquement un nœud racine.
Exemple :
```text
Template
```
Les enfants seront ajoutés dans un ticket ultérieur.
---
## Contraintes techniques
- C17
- Structure opaque
- Aucun état global
- Aucune dépendance GTK
- Documentation Doxygen
- Compilation sans warning
- Respect des conventions du projet
---
## Critères d'acceptation
- [ ] Le projet compile sans warning.
- [ ] Le modèle est opaque.
- [ ] Le modèle possède un nœud racine.
- [ ] Le getter retourne le nœud racine.
- [ ] La destruction du modèle détruit également le nœud racine.
- [ ] Aucun code GTK.
- [ ] Aucun code SQLite.
---
## Tests
- création d'un modèle valide ;
- lecture du nœud racine ;
- destruction du modèle ;
- création avec un nœud NULL ;
- destruction avec NULL.
---
## Commit attendu
```text
feat(core): create investigation tree model
```

View file

@ -1,103 +0,0 @@
# Ticket #009.5
## Titre
Ajouter une cible `make test`.
---
## Objectif
Automatiser la compilation et l'exécution de l'ensemble des tests du projet.
La commande suivante doit suffire :
```bash
make test
```
---
## Responsabilités
Le système de build doit :
- compiler chaque fichier de test ;
- lier uniquement les modules nécessaires ;
- exécuter tous les tests ;
- arrêter l'exécution si un test échoue ;
- supprimer les binaires de test avec `make clean`.
---
## Hors périmètre
Ce ticket ne doit pas :
- ajouter de nouveaux tests métier ;
- modifier le comportement des modules ;
- introduire un framework de test externe ;
- produire des rapports de couverture ;
- lancer Valgrind automatiquement.
---
## Fichiers concernés
```text
Makefile
```
Éventuellement :
```text
tests/Makefile
```
uniquement si le Makefile principal devient difficile à lire.
---
## Tests concernés
```text
tests/test_investigation_node.c
tests/test_investigation_tree_model.c
```
---
## Comportement attendu
La commande :
```bash
make test
```
doit :
1. compiler les binaires de test ;
2. exécuter chaque test ;
3. afficher clairement le résultat ;
4. retourner un code d'erreur si un test échoue.
---
## Critères d'acceptation
- [ ] `make test` compile tous les tests.
- [ ] `make test` exécute tous les tests.
- [ ] Un test en échec arrête la commande.
- [ ] `make clean` supprime les binaires de test.
- [ ] Aucun binaire de test n'est versionné.
- [ ] Le projet principal continue de compiler.
- [ ] Aucun warning.
---
## Commit attendu
```text
build: add automated test target
```

View file

@ -1,268 +0,0 @@
# Ticket #009
## Titre
Ajouter les relations parent/enfants à `InvestigationNode`.
---
## Objectif
Faire évoluer `InvestigationNode` afin qu'un nœud puisse appartenir à une arborescence.
Chaque nœud pourra :
- connaître son parent ;
- posséder plusieurs enfants ;
- exposer ses enfants en lecture seule via une API publique.
---
## Responsabilités
Le module `InvestigationNode` doit :
- mémoriser un pointeur vers son parent ;
- posséder un tableau dynamique d'enfants ;
- ajouter un enfant ;
- retourner un enfant par son index ;
- retourner le nombre d'enfants ;
- retourner son parent ;
- détruire récursivement les enfants qu'il possède.
---
## Hors périmètre
Ce ticket ne doit pas :
- parcourir le système de fichiers ;
- construire automatiquement une arborescence depuis un dossier ;
- afficher quoi que ce soit avec GTK ;
- communiquer avec SQLite ;
- gérer le tri des enfants ;
- gérer la suppression individuelle d'un enfant ;
- gérer le déplacement d'un nœud entre deux parents.
---
## Architecture
```text
InvestigationTreeModel
InvestigationNode
├── InvestigationNode
├── InvestigationNode
└── InvestigationNode
```
Le module appartient à la couche `core`.
Il peut dépendre de GLib, mais jamais de GTK.
---
## Gestion de la propriété
Un nœud devient propriétaire de chaque enfant ajouté avec :
```c
investigation_node_add_child(parent, child);
```
Après un ajout réussi :
- `parent` possède `child` ;
- le code appelant ne doit plus libérer `child` directement ;
- `child` conserve une référence non propriétaire vers `parent`.
Lors de la destruction du parent, tous ses enfants sont détruits récursivement.
---
## Structure interne attendue
```c
struct InvestigationNode
{
char *name;
InvestigationNodeType type;
InvestigationNode *parent;
GPtrArray *children;
};
```
La structure reste privée dans `investigation_node.c`.
---
## Interface publique attendue
Les fonctions existantes sont conservées :
```c
InvestigationNode *investigation_node_new(
const char *name,
InvestigationNodeType type
);
void investigation_node_free(
InvestigationNode *node
);
const char *investigation_node_get_name(
const InvestigationNode *node
);
InvestigationNodeType investigation_node_get_type(
const InvestigationNode *node
);
```
Les fonctions suivantes sont ajoutées :
```c
bool investigation_node_add_child(
InvestigationNode *parent,
InvestigationNode *child
);
const InvestigationNode *investigation_node_get_child(
const InvestigationNode *node,
size_t index
);
size_t investigation_node_get_children_count(
const InvestigationNode *node
);
const InvestigationNode *investigation_node_get_parent(
const InvestigationNode *node
);
```
---
## Comportement attendu
Exemple :
```c
InvestigationNode *root = NULL;
InvestigationNode *child = NULL;
root = investigation_node_new(
"Template",
INVESTIGATION_NODE_DIRECTORY
);
child = investigation_node_new(
"00_BaseDeDonnees",
INVESTIGATION_NODE_DIRECTORY
);
if (!investigation_node_add_child(root, child))
{
investigation_node_free(child);
investigation_node_free(root);
return;
}
```
Après l'ajout :
```text
Template
└── 00_BaseDeDonnees
```
Le nœud `root` devient propriétaire de `child`.
Le nettoyage correct est uniquement :
```c
investigation_node_free(root);
```
---
## Règles d'ajout
`investigation_node_add_child()` doit refuser :
- un parent `NULL` ;
- un enfant `NULL` ;
- un parent qui représente un fichier ;
- l'ajout d'un nœud comme enfant de lui-même ;
- un enfant possédant déjà un parent.
En cas d'échec, la propriété de l'enfant reste au code appelant.
---
## Dépendances
- C17 ;
- GLib.
Aucune dépendance GTK ou SQLite.
---
## Contraintes techniques
- structure opaque ;
- aucun état global ;
- utilisation de `GPtrArray` ;
- tableau créé avec une fonction de destruction ;
- documentation Doxygen ;
- compilation sans warning ;
- respect des conventions de nommage ;
- aucune modification directe du tableau en dehors du module.
---
## Critères d'acceptation
- [ ] Le projet compile sans warning.
- [ ] Un dossier peut posséder plusieurs enfants.
- [ ] Un fichier ne peut pas recevoir d'enfant.
- [ ] Un enfant connaît son parent.
- [ ] Le nombre d'enfants est correct.
- [ ] Un enfant peut être récupéré par son index.
- [ ] Un index invalide retourne `NULL`.
- [ ] Un enfant ne peut pas avoir deux parents.
- [ ] Un nœud ne peut pas être son propre enfant.
- [ ] La destruction d'un parent détruit récursivement ses enfants.
- [ ] Aucun code GTK.
- [ ] Aucun code SQLite.
---
## Tests
- créer un parent dossier ;
- ajouter un enfant dossier ;
- ajouter un enfant fichier ;
- vérifier le nombre d'enfants ;
- récupérer chaque enfant ;
- vérifier le parent d'un enfant ;
- tester un index invalide ;
- refuser l'ajout à un fichier ;
- refuser un parent `NULL` ;
- refuser un enfant `NULL` ;
- refuser l'auto-référence ;
- refuser un enfant possédant déjà un parent ;
- détruire un arbre complet ;
- appeler les getters avec `NULL`.
---
## Commit attendu
```text
feat(core): add investigation node hierarchy
```

View file

@ -1,243 +0,0 @@
# Ticket #010
## Titre
Construire l'arborescence d'une enquête depuis le système de fichiers.
---
## Objectif
Créer le module `InvestigationTreeBuilder`.
Ce module doit parcourir récursivement le dossier racine d'une enquête et
construire un `InvestigationTreeModel` représentant son contenu.
---
## Responsabilités
Le module `InvestigationTreeBuilder` doit :
- recevoir le chemin racine d'une enquête ;
- vérifier que ce chemin désigne un dossier existant ;
- créer le nœud racine ;
- parcourir récursivement les dossiers ;
- créer un `InvestigationNode` pour chaque dossier ;
- créer un `InvestigationNode` pour chaque fichier ;
- assembler les relations parent/enfants ;
- retourner un `InvestigationTreeModel` complet ;
- nettoyer toutes les ressources en cas d'erreur.
---
## Hors périmètre
Ce ticket ne doit pas :
- afficher l'arborescence avec GTK ;
- modifier le système de fichiers ;
- créer ou supprimer des fichiers ;
- trier les éléments ;
- filtrer les fichiers cachés ;
- surveiller les changements du disque ;
- suivre les liens symboliques ;
- communiquer avec SQLite.
---
## Architecture
```text
Investigation
InvestigationTreeBuilder
InvestigationTreeModel
InvestigationNode
```
Le module appartient à la couche `core`.
Il peut utiliser GLib et GIO, mais ne doit jamais dépendre de GTK.
---
## Fichiers concernés
```text
include/core/investigation_tree_builder.h
src/core/investigation_tree_builder.c
```
Les tests seront placés dans :
```text
tests/test_investigation_tree_builder.c
```
---
## Interface publique attendue
```c
InvestigationTreeModel *investigation_tree_builder_build(
const char *root_path
);
```
---
## Principe de propriété
En cas de succès, la fonction retourne un nouveau
`InvestigationTreeModel`.
Le code appelant devient propriétaire du modèle retourné et doit le libérer
avec :
```c
investigation_tree_model_free(tree_model);
```
En cas d'échec, la fonction retourne `NULL` et doit avoir libéré toutes les
ressources créées pendant la construction.
---
## Comportement attendu
À partir de :
```text
Enquete_Test/
├── 00_BaseDeDonnees/
│ └── Enquete.sqlite
├── 01_Preuves_Originales/
│ ├── Captures_Ecran/
│ └── Emails/
└── README.md
```
le module doit construire en mémoire :
```text
Enquete_Test
├── 00_BaseDeDonnees
│ └── Enquete.sqlite
├── 01_Preuves_Originales
│ ├── Captures_Ecran
│ └── Emails
└── README.md
```
---
## Gestion des liens symboliques
Les liens symboliques ne sont pas suivis.
Cette règle évite :
- les boucles récursives ;
- la sortie involontaire du dossier d'enquête ;
- l'analyse de fichiers extérieurs à l'enquête.
Leur prise en charge éventuelle fera l'objet d'un ticket distinct.
---
## Dépendances
- C17 ;
- GLib ;
- GIO.
Aucune dépendance GTK ou SQLite.
---
## Contraintes techniques
- aucune variable globale ;
- aucune modification du système de fichiers ;
- parcours récursif ;
- utilisation de `GFile` et `GFileEnumerator` ;
- libération correcte des `GObject` avec `g_object_unref()` ;
- respect de la règle « le propriétaire détruit » ;
- documentation Doxygen ;
- compilation sans warning ;
- aucune fuite mémoire.
---
## Cas à gérer
- chemin valide ;
- chemin relatif ;
- chemin `NULL` ;
- chemin vide ;
- chemin inexistant ;
- chemin désignant un fichier ;
- dossier vide ;
- plusieurs niveaux de sous-dossiers ;
- fichiers et dossiers mélangés ;
- erreur rencontrée pendant le parcours.
---
## Critères d'acceptation
- [ ] Le projet compile sans warning.
- [ ] Un dossier valide produit un modèle.
- [ ] Le nom du dossier racine est correct.
- [ ] Les dossiers produisent des nœuds de type `DIRECTORY`.
- [ ] Les fichiers produisent des nœuds de type `FILE`.
- [ ] Les relations parent/enfants sont correctes.
- [ ] Plusieurs niveaux de profondeur sont pris en charge.
- [ ] Un dossier vide est pris en charge.
- [ ] Un chemin invalide retourne `NULL`.
- [ ] Les liens symboliques ne sont pas suivis.
- [ ] Toutes les ressources sont libérées en cas d'erreur.
- [ ] Aucun code GTK.
- [ ] Aucun code SQLite.
- [ ] `make test` exécute le nouveau test.
---
## Tests
Le test doit créer une arborescence temporaire isolée :
```text
TestCase/
├── DirectoryA/
│ └── FileA.txt
├── DirectoryB/
└── RootFile.md
```
Il doit vérifier :
- le nom du nœud racine ;
- le nombre d'enfants de la racine ;
- la présence des deux dossiers ;
- la présence du fichier racine ;
- la présence de `FileA.txt` dans `DirectoryA` ;
- le type de chaque nœud ;
- le parent de chaque enfant ;
- la gestion d'un dossier vide ;
- les chemins invalides ;
- la destruction complète du modèle.
---
## Commit attendu
```text
feat(core): build investigation tree from filesystem
```

View file

@ -1,240 +0,0 @@
# Ticket #011
## Titre
Connecter le modèle d'arborescence à la `Sidebar`.
---
## Objectif
Relier l'enquête sélectionnée au panneau latéral.
Après la sélection d'un dossier, l'application doit :
1. créer l'objet `Investigation` ;
2. construire son `InvestigationTreeModel` ;
3. transmettre le modèle à la `Sidebar` ;
4. afficher temporairement le nom du nœud racine dans le panneau latéral.
Ce ticket valide la communication entre le Core et l'interface graphique.
---
## Architecture
```text
FolderDialog
Application
├── Investigation
└── InvestigationTreeModel
Sidebar
```
Le module `Application` reste responsable de la coordination.
La `Sidebar` ne parcourt jamais directement le système de fichiers.
---
## Responsabilités
### Application
Le module `Application` doit :
- recevoir le dossier sélectionné ;
- créer l'objet `Investigation` ;
- construire le modèle avec `InvestigationTreeBuilder` ;
- conserver le modèle pendant toute la durée de l'enquête ;
- transmettre le modèle à `MainWindow` ;
- libérer l'ancien modèle avant d'en ouvrir un nouveau ;
- libérer le modèle à la fermeture de l'application.
### MainWindow
Le module `MainWindow` doit :
- recevoir un modèle d'arborescence ;
- le transmettre au composant `Sidebar`.
### Sidebar
Le module `Sidebar` doit :
- recevoir un `InvestigationTreeModel` en lecture seule ;
- lire le nœud racine ;
- afficher temporairement son nom dans le titre du panneau ;
- ne jamais détruire le modèle reçu.
---
## Principe de propriété
`Application` est propriétaire de :
```text
Investigation
InvestigationTreeModel
MainWindow
```
La `Sidebar` reçoit uniquement une référence non propriétaire vers le modèle.
Elle ne doit jamais appeler :
```c
investigation_tree_model_free(tree_model);
```
Le modèle est libéré uniquement par `Application`.
---
## Fichiers concernés
```text
src/core/application.c
include/views/main_window.h
src/views/main_window.c
include/widgets/sidebar.h
src/widgets/sidebar.c
```
Aucun nouveau module n'est nécessaire.
---
## Interfaces publiques à ajouter
### MainWindow
```c
void main_window_set_tree_model(
MainWindow *main_window,
const InvestigationTreeModel *tree_model
);
```
### Sidebar
```c
void sidebar_set_tree_model(
Sidebar *sidebar,
const InvestigationTreeModel *tree_model
);
```
---
## Comportement attendu
Avant l'ouverture d'une enquête, la `Sidebar` affiche :
```text
Dossier d'enquête
```
Après la sélection du dossier :
```text
Template
```
ou le nom réel du dossier racine sélectionné.
À ce stade, les enfants ne sont pas encore affichés.
---
## Hors périmètre
Ce ticket ne doit pas :
- afficher les enfants du nœud racine ;
- utiliser `GtkTreeListModel` ;
- créer un explorateur de fichiers complet ;
- permettre de sélectionner un nœud ;
- ouvrir un fichier ;
- rafraîchir automatiquement le modèle ;
- modifier le système de fichiers ;
- communiquer avec SQLite.
---
## Gestion des erreurs
Si la construction du modèle échoue :
- l'application ne doit pas planter ;
- l'ancien modèle doit rester valide jusqu'à son remplacement explicite ;
- un message d'erreur doit être affiché dans le terminal ;
- la `Sidebar` ne doit recevoir aucun pointeur invalide.
Si une nouvelle enquête est ouverte avec succès :
1. créer la nouvelle enquête ;
2. construire le nouveau modèle ;
3. seulement ensuite libérer l'ancienne enquête et l'ancien modèle ;
4. installer les nouveaux objets.
Cette séquence évite de perdre l'enquête actuellement ouverte en cas d'échec.
---
## Contraintes techniques
- C17 ;
- aucun état global ;
- aucune lecture du système de fichiers dans `Sidebar` ;
- aucun transfert de propriété vers `Sidebar` ;
- documentation Doxygen ;
- compilation sans warning ;
- absence de `Gtk-CRITICAL` ;
- respect des conventions de nommage.
---
## Critères d'acceptation
- [ ] Le projet compile sans warning.
- [ ] `make test` reste valide.
- [ ] La sélection d'un dossier construit un modèle.
- [ ] `Application` conserve le modèle.
- [ ] `MainWindow` transmet le modèle à `Sidebar`.
- [ ] La `Sidebar` affiche le nom du nœud racine.
- [ ] L'ouverture successive de deux dossiers fonctionne.
- [ ] L'ancien modèle est correctement libéré.
- [ ] Une erreur de construction ne provoque pas de crash.
- [ ] La fermeture de l'application libère le modèle.
- [ ] Aucun code de parcours du disque n'apparaît dans `Sidebar`.
- [ ] Aucun enfant n'est encore affiché.
---
## Tests manuels
1. Lancer l'application.
2. Sélectionner le dossier `Template`.
3. Vérifier que la `Sidebar` affiche `Template`.
4. Fermer l'application.
5. Vérifier l'absence de warning critique.
6. Ouvrir successivement deux dossiers différents.
7. Vérifier que le titre de la `Sidebar` est mis à jour.
8. Vérifier que `make test` reste entièrement valide.
---
## Commit attendu
```text
feat(gui): connect investigation model to sidebar
```

View file

@ -1,167 +0,0 @@
# Ticket #012
## Titre
Créer le composant `InvestigationTreeView`.
---
## Objectif
Créer un composant graphique spécialisé chargé d'afficher un
`InvestigationTreeModel` à l'aide des widgets modernes de GTK4.
Ce composant servira d'adaptateur entre le modèle métier et les widgets GTK.
À ce stade, il n'est pas encore intégré dans la `Sidebar`.
---
## Architecture
```text
InvestigationTreeModel
InvestigationTreeView
GtkTreeListModel
GtkListView
```
Le composant appartient à la couche graphique.
Le Core ne dépend jamais de GTK.
---
## Responsabilités
Le module doit :
- recevoir un `InvestigationTreeModel` ;
- créer un `GtkTreeListModel` ;
- créer un `GtkListView` ;
- utiliser un `GtkSignalListItemFactory` ;
- utiliser un `GtkTreeExpander` ;
- afficher le nom des nœuds ;
- permettre le développement et le repli des dossiers ;
- fournir un widget GTK réutilisable.
---
## Hors périmètre
Ce ticket ne doit pas :
- intégrer le composant dans la Sidebar ;
- permettre la sélection d'un nœud ;
- afficher des icônes ;
- ouvrir des fichiers ;
- modifier l'arborescence ;
- communiquer avec SQLite ;
- rafraîchir automatiquement le modèle.
---
## Principe de propriété
Le composant ne devient jamais propriétaire du
`InvestigationTreeModel`.
Il reçoit uniquement une référence.
En revanche, il est propriétaire de tous les objets GTK qu'il crée.
---
## Fichiers concernés
```text
include/widgets/investigation_tree_view.h
src/widgets/investigation_tree_view.c
```
Aucun autre module ne doit être modifié.
---
## Interface publique
```c
typedef struct InvestigationTreeView InvestigationTreeView;
```
```c
InvestigationTreeView *investigation_tree_view_new(void);
```
```c
GtkWidget *investigation_tree_view_get_widget(
const InvestigationTreeView *tree_view
);
```
```c
void investigation_tree_view_set_model(
InvestigationTreeView *tree_view,
const InvestigationTreeModel *tree_model
);
```
```c
void investigation_tree_view_free(
InvestigationTreeView *tree_view
);
```
---
## Dépendances GTK
Le composant doit utiliser exclusivement :
- GtkTreeListModel
- GtkListView
- GtkTreeExpander
- GtkSignalListItemFactory
Aucun widget GTK3.
---
## Contraintes techniques
- C17
- GTK4 uniquement
- aucune variable globale
- aucune dépendance vers SQLite
- aucune lecture directe du système de fichiers
- documentation Doxygen
- compilation sans warning
- aucune fuite mémoire
---
## Critères d'acceptation
- [ ] Le projet compile.
- [ ] Le composant peut être créé.
- [ ] Le composant fournit un widget GTK.
- [ ] Un modèle peut être associé.
- [ ] Les dossiers peuvent être développés.
- [ ] Les fichiers apparaissent.
- [ ] Le composant ne possède pas le modèle.
- [ ] Aucun Gtk-CRITICAL.
- [ ] Aucun warning.
---
## Commit attendu
```text
feat(gui): create investigation tree view
```

View file

@ -1,217 +0,0 @@
# Ticket #013
## Titre
Intégrer `InvestigationTreeView` dans la `Sidebar`.
---
## Objectif
Afficher dans la barre latérale l'arborescence complète de l'enquête ouverte.
Le composant `InvestigationTreeView` existe déjà. Ce ticket consiste uniquement à l'intégrer dans `Sidebar` et à lui transmettre le modèle métier reçu.
---
## Architecture
```text
Application
MainWindow
Sidebar
InvestigationTreeView
GtkTreeListModel
```
La `Sidebar` héberge la vue.
Elle ne parcourt jamais directement le système de fichiers.
---
## Responsabilités
### Sidebar
Le module `Sidebar` doit :
- créer un `InvestigationTreeView` ;
- intégrer son widget racine sous le titre ;
- transmettre le `InvestigationTreeModel` au composant ;
- libérer la structure `InvestigationTreeView` lors de sa propre destruction.
### InvestigationTreeView
Le composant conserve ses responsabilités actuelles :
- adapter le modèle métier à GTK4 ;
- afficher les nœuds ;
- gérer l'ouverture et le repli des dossiers.
---
## Principe de propriété
`Sidebar` est propriétaire de :
```text
InvestigationTreeView
```
Elle doit donc appeler :
```c
investigation_tree_view_free(...)
```
lors de sa destruction.
En revanche, ni `Sidebar` ni `InvestigationTreeView` ne possèdent :
```text
InvestigationTreeModel
```
Le modèle reste la propriété de `Application`.
---
## Fichiers concernés
```text
src/widgets/sidebar.c
```
Éventuellement :
```text
include/widgets/sidebar.h
```
uniquement si l'API publique doit être précisée.
Aucun changement du Core n'est nécessaire.
---
## Modifications attendues
La structure privée `Sidebar` doit contenir :
```c
InvestigationTreeView *tree_view;
```
Le constructeur doit :
1. créer le composant ;
2. récupérer son widget racine ;
3. l'ajouter sous le titre ;
4. lui permettre d'occuper l'espace disponible.
La fonction :
```c
sidebar_set_tree_model(...)
```
doit transmettre le modèle à :
```c
investigation_tree_view_set_model(...)
```
Le titre de la `Sidebar` peut continuer à afficher le nom du nœud racine.
---
## Interface graphique attendue
```text
Template
▸ 00_BaseDeDonnees
▸ 01_Preuves_Originales
▸ 02_Preuves_traitees
▸ 03_Chronologie
▸ 04_Entites
...
```
Les dossiers peuvent être développés et repliés.
Les fichiers apparaissent dans les branches développées.
---
## Hors périmètre
Ce ticket ne doit pas :
- permettre la sélection d'un nœud ;
- afficher des icônes ;
- ouvrir un fichier ;
- ajouter un menu contextuel ;
- rafraîchir automatiquement l'arbre ;
- modifier le système de fichiers ;
- communiquer avec SQLite.
---
## Contraintes techniques
- GTK4 uniquement ;
- aucun `GtkTreeView` ;
- aucune lecture directe du disque dans `Sidebar` ;
- aucun transfert de propriété du modèle métier ;
- aucun état global ;
- documentation cohérente ;
- compilation sans warning ;
- aucun `Gtk-CRITICAL`.
---
## Critères d'acceptation
- [ ] Le projet compile sans warning.
- [ ] `make test` reste entièrement valide.
- [ ] La vue est intégrée dans la `Sidebar`.
- [ ] Le titre affiche le nom de l'enquête.
- [ ] Tous les dossiers racine apparaissent.
- [ ] Les branches peuvent être développées et repliées.
- [ ] Les fichiers apparaissent dans les branches.
- [ ] L'ouverture successive de deux enquêtes reconstruit correctement l'affichage.
- [ ] La fermeture ne produit aucun warning critique.
- [ ] Le Core n'est pas modifié.
---
## Tests manuels
1. Lancer l'application.
2. Ouvrir le dossier `Template`.
3. Vérifier l'affichage des dossiers racine.
4. Développer `00_BaseDeDonnees`.
5. Vérifier la présence de `Enquete.sqlite`.
6. Développer plusieurs niveaux.
7. Fermer l'application.
8. Rouvrir avec une autre enquête.
9. Vérifier que l'ancien arbre a disparu.
10. Lancer `make test`.
---
## Commit attendu
```text
feat(gui): integrate investigation tree into sidebar
```

View file

@ -1,244 +0,0 @@
# Ticket #014
## Titre
Ajouter la sélection d'un nœud dans l'arborescence.
---
## Objectif
Permettre à l'utilisateur de sélectionner un dossier ou un fichier dans
`InvestigationTreeView`.
Le nœud sélectionné doit être transmis jusqu'au module `Application`, qui
reste responsable de la coordination entre l'arborescence et le reste de
l'interface.
Ce ticket ne doit encore ouvrir aucun fichier.
---
## Architecture
```text
GtkListView
InvestigationTreeView
Sidebar
MainWindow
Application
```
Le Core reste indépendant de GTK.
---
## Responsabilités
### InvestigationTreeView
Le module doit :
- remplacer `GtkNoSelection` par `GtkSingleSelection` ;
- détecter les changements de sélection ;
- retrouver le `InvestigationNode` métier correspondant ;
- appeler un callback public avec le nœud sélectionné ;
- transmettre `NULL` lorsqu'aucun nœud n'est sélectionné.
### Sidebar
Le module doit :
- recevoir un callback de sélection ;
- le transmettre à `InvestigationTreeView` ;
- ne contenir aucune logique métier liée à la sélection.
### MainWindow
Le module doit :
- recevoir le callback depuis `Application` ;
- le transmettre à `Sidebar`.
### Application
Le module doit :
- recevoir le nœud sélectionné ;
- afficher temporairement son nom et son type dans le terminal ;
- ne pas modifier ni libérer le nœud reçu.
---
## Principe de propriété
Le nœud sélectionné appartient toujours au
`InvestigationTreeModel`, lui-même possédé par `Application`.
Le callback reçoit uniquement une référence non propriétaire :
```c
const InvestigationNode *node;
```
Le code appelé ne doit jamais faire :
```c
investigation_node_free(node);
```
La référence reste valide tant que le modèle courant n'est pas remplacé ou
détruit.
---
## Interfaces publiques à ajouter
### InvestigationTreeView
```c
typedef void (*InvestigationTreeViewSelectionCallback)(
const InvestigationNode *node,
gpointer user_data
);
```
```c
void investigation_tree_view_set_selection_callback(
InvestigationTreeView *tree_view,
InvestigationTreeViewSelectionCallback callback,
gpointer user_data
);
```
### Sidebar
```c
void sidebar_set_selection_callback(
Sidebar *sidebar,
InvestigationTreeViewSelectionCallback callback,
gpointer user_data
);
```
### MainWindow
```c
void main_window_set_tree_selection_callback(
MainWindow *main_window,
InvestigationTreeViewSelectionCallback callback,
gpointer user_data
);
```
---
## Comportement attendu
Lorsque l'utilisateur sélectionne :
```text
Enquete.sqlite
```
le terminal affiche temporairement :
```text
Nœud sélectionné : Enquete.sqlite
Type : fichier
```
Pour un dossier :
```text
Nœud sélectionné : 00_BaseDeDonnees
Type : dossier
```
Lorsqu'aucun élément n'est sélectionné, le callback reçoit `NULL`.
---
## Hors périmètre
Ce ticket ne doit pas :
- ouvrir un fichier ;
- modifier le Workspace ;
- afficher un aperçu ;
- sélectionner plusieurs nœuds ;
- ajouter un menu contextuel ;
- afficher des icônes ;
- modifier le Core ;
- modifier le système de fichiers.
---
## Contraintes techniques
- GTK4 uniquement ;
- utiliser `GtkSingleSelection` ;
- aucun `GtkTreeView` ;
- aucun état global ;
- callback documenté avec Doxygen ;
- aucune destruction du nœud sélectionné ;
- compilation sans warning ;
- aucun `Gtk-CRITICAL`.
---
## Gestion des changements de modèle
Lorsqu'un nouveau modèle est installé :
- la sélection précédente doit être supprimée ;
- aucun callback ne doit conserver une référence vers un nœud de l'ancien
modèle ;
- la nouvelle vue doit démarrer sans sélection.
---
## Critères d'acceptation
- [ ] Le projet compile sans warning.
- [ ] `make test` reste entièrement valide.
- [ ] Un seul nœud peut être sélectionné.
- [ ] La sélection d'un dossier est détectée.
- [ ] La sélection d'un fichier est détectée.
- [ ] Le bon `InvestigationNode` est transmis au callback.
- [ ] `Application` reçoit l'événement.
- [ ] Le nom et le type sont affichés dans le terminal.
- [ ] Le changement d'enquête efface l'ancienne sélection.
- [ ] Aucun module graphique ne libère le nœud.
- [ ] Aucun `Gtk-CRITICAL`.
---
## Tests manuels
1. Lancer l'application.
2. Ouvrir le dossier `Template`.
3. Développer `00_BaseDeDonnees`.
4. Sélectionner `Enquete.sqlite`.
5. Vérifier le nom et le type dans le terminal.
6. Sélectionner un dossier.
7. Vérifier que le type affiché est `dossier`.
8. Ouvrir une autre enquête.
9. Vérifier qu'aucune ancienne sélection n'est conservée.
10. Lancer `make test`.
---
## Commit attendu
```text
feat(gui): add investigation tree selection
```

View file

@ -1,225 +0,0 @@
# Ticket #015
## Titre
Afficher le nœud sélectionné dans le `Workspace`.
---
## Objectif
Afficher dans la zone de travail les informations principales du nœud sélectionné dans l'arborescence.
Lorsqu'un dossier ou un fichier est sélectionné, le `Workspace` doit afficher :
- son nom ;
- son type ;
- son parent éventuel ;
- son nombre d'enfants s'il s'agit d'un dossier.
Aucun fichier ne doit encore être ouvert.
---
## Architecture
```text
InvestigationTreeView
Application
MainWindow
Workspace
```
`Application` reste le coordinateur.
`InvestigationTreeView` détecte la sélection.
`Workspace` affiche les informations reçues.
---
## Responsabilités
### Application
Le module doit :
- recevoir le nœud sélectionné ;
- transmettre ce nœud à `MainWindow` ;
- ne pas modifier ni libérer le nœud.
### MainWindow
Le module doit :
- recevoir un nœud en lecture seule ;
- le transmettre au `Workspace`.
### Workspace
Le module doit :
- recevoir un `InvestigationNode` en lecture seule ;
- afficher son nom ;
- afficher son type ;
- afficher le nom de son parent si disponible ;
- afficher le nombre d'enfants pour un dossier ;
- restaurer la page d'accueil si le nœud vaut `NULL`.
---
## Principe de propriété
Le nœud sélectionné reste la propriété du `InvestigationTreeModel`.
`Application`, `MainWindow` et `Workspace` ne reçoivent qu'une référence non propriétaire :
```c
const InvestigationNode *node;
```
Aucun de ces modules ne doit appeler :
```c
investigation_node_free(node);
```
---
## Fichiers concernés
```text
include/widgets/workspace.h
src/widgets/workspace.c
include/views/main_window.h
src/views/main_window.c
src/core/application.c
```
Aucune modification du Core métier n'est nécessaire.
---
## Interfaces publiques à ajouter
### Workspace
```c
void workspace_set_selected_node(
Workspace *workspace,
const InvestigationNode *node
);
```
### MainWindow
```c
void main_window_set_selected_node(
MainWindow *main_window,
const InvestigationNode *node
);
```
---
## Comportement attendu
### Aucun nœud sélectionné
```text
Labfy Investigation
Aucune enquête ouverte
Sélectionnez ou créez une enquête
```
### Dossier sélectionné
```text
00_BaseDeDonnees
Type : dossier
Parent : Template
Enfants : 2
```
### Fichier sélectionné
```text
Enquete.sqlite
Type : fichier
Parent : 00_BaseDeDonnees
```
---
## Hors périmètre
Ce ticket ne doit pas :
- ouvrir le contenu d'un fichier ;
- afficher un aperçu ;
- lire les métadonnées du système de fichiers ;
- afficher la taille d'un fichier ;
- communiquer avec SQLite ;
- modifier l'arborescence ;
- gérer plusieurs sélections.
---
## Contraintes techniques
- C17 ;
- GTK4 uniquement ;
- aucun état global ;
- aucune propriété du nœud transférée au `Workspace` ;
- documentation Doxygen ;
- compilation sans warning ;
- aucun `Gtk-CRITICAL`.
---
## Critères d'acceptation
- [ ] Le projet compile sans warning.
- [ ] `make test` reste valide.
- [ ] La sélection d'un dossier met à jour le `Workspace`.
- [ ] La sélection d'un fichier met à jour le `Workspace`.
- [ ] Le nom est correct.
- [ ] Le type est correct.
- [ ] Le parent est correct.
- [ ] Le nombre d'enfants est correct pour un dossier.
- [ ] Une sélection `NULL` restaure la page d'accueil.
- [ ] Aucun module graphique ne libère le nœud.
- [ ] Aucun `Gtk-CRITICAL`.
---
## Tests manuels
1. Ouvrir une enquête.
2. Sélectionner un dossier.
3. Vérifier les informations affichées.
4. Sélectionner un fichier.
5. Vérifier les informations affichées.
6. Changer d'enquête.
7. Vérifier que l'ancien nœud n'est plus affiché.
8. Lancer `make test`.
---
## Commit attendu
```text
feat(gui): display selected node in workspace
```

View file

@ -1,289 +0,0 @@
# Ticket #016
## Titre
Structurer le `Workspace` avec `GtkStack`.
---
## Objectif
Remplacer la gestion manuelle de visibilité des pages du `Workspace` par un
`GtkStack`.
Le `Workspace` doit pouvoir afficher proprement plusieurs pages internes sans
modifier son API publique actuelle.
Ce ticket prépare l'ajout futur de nouvelles vues :
- aperçu texte ;
- aperçu image ;
- aperçu PDF ;
- chronologie ;
- fiches d'entités ;
- rapports.
---
## Architecture
```text
Workspace
└── GtkStack
├── WelcomePage
└── NodeInformationPage
```
Plus tard :
```text
Workspace
└── GtkStack
├── WelcomePage
├── NodeInformationPage
├── TextPreviewPage
├── ImagePreviewPage
├── PdfPreviewPage
└── EntityPage
```
Le `Workspace` reste responsable de son affichage interne.
---
## Responsabilités
Le module `Workspace` doit :
- créer un `GtkStack` comme widget racine interne ;
- ajouter une page d'accueil ;
- ajouter une page d'informations sur le nœud sélectionné ;
- afficher la page d'accueil lorsqu'aucun nœud n'est sélectionné ;
- afficher la page d'informations lorsqu'un nœud est sélectionné ;
- conserver l'API publique existante.
---
## API publique conservée
Aucune nouvelle fonction publique n'est requise.
L'API reste :
```c
Workspace *workspace_new(void);
GtkWidget *workspace_get_widget(
const Workspace *workspace
);
void workspace_set_selected_node(
Workspace *workspace,
const InvestigationNode *node
);
void workspace_free(
Workspace *workspace
);
```
---
## Fichiers concernés
```text
src/widgets/workspace.c
```
Éventuellement :
```text
include/widgets/workspace.h
```
uniquement pour mettre à jour la documentation.
Aucun autre module ne doit être modifié.
---
## Structure interne attendue
```c
struct Workspace
{
GtkWidget *root_widget;
GtkWidget *stack;
GtkWidget *welcome_page;
GtkWidget *welcome_title_label;
GtkWidget *welcome_status_label;
GtkWidget *welcome_instruction_label;
GtkWidget *node_page;
GtkWidget *node_name_label;
GtkWidget *node_type_label;
GtkWidget *node_parent_label;
GtkWidget *node_children_label;
};
```
Les noms exacts peuvent varier, mais doivent rester explicites.
---
## Pages attendues
### Page d'accueil
Nom interne :
```text
welcome
```
Contenu :
```text
Labfy Investigation
Aucune enquête ouverte
Sélectionnez ou créez une enquête
```
### Page d'informations
Nom interne :
```text
node-information
```
Contenu pour un dossier :
```text
00_BaseDeDonnees
Type : dossier
Parent : Template
Enfants : 2
```
Contenu pour un fichier :
```text
Enquete.sqlite
Type : fichier
Parent : 00_BaseDeDonnees
```
---
## Comportement attendu
Lorsque :
```c
workspace_set_selected_node(workspace, NULL);
```
le `GtkStack` doit afficher :
```text
welcome
```
Lorsqu'un nœud valide est transmis :
```c
workspace_set_selected_node(workspace, node);
```
le `GtkStack` doit afficher :
```text
node-information
```
---
## Hors périmètre
Ce ticket ne doit pas :
- ouvrir un fichier ;
- afficher son contenu ;
- ajouter une barre de navigation ;
- ajouter une animation personnalisée ;
- afficher plusieurs nœuds simultanément ;
- modifier le Core ;
- communiquer avec SQLite.
---
## Contraintes techniques
- GTK4 uniquement ;
- utiliser `GtkStack` ;
- aucune gestion manuelle de visibilité entre les pages ;
- aucun état global ;
- aucun changement de propriété des nœuds ;
- documentation cohérente ;
- compilation sans warning ;
- aucun `Gtk-CRITICAL`.
---
## Gestion de la propriété
Le `Workspace` possède uniquement sa structure d'encapsulation.
Les widgets sont gérés par l'arbre GTK une fois intégrés dans la fenêtre.
Le nœud reçu reste la propriété du `InvestigationTreeModel`.
Le `Workspace` ne doit jamais appeler :
```c
investigation_node_free(node);
```
---
## Critères d'acceptation
- [ ] Le projet compile sans warning.
- [ ] `make test` reste entièrement valide.
- [ ] `GtkStack` est utilisé.
- [ ] La page d'accueil apparaît sans sélection.
- [ ] La page d'informations apparaît après sélection.
- [ ] Le passage d'une page à l'autre fonctionne.
- [ ] Aucun appel manuel à `gtk_widget_set_visible()` n'est utilisé pour changer de page.
- [ ] L'API publique de `Workspace` reste stable.
- [ ] Aucun autre module n'est modifié.
- [ ] Aucun `Gtk-CRITICAL`.
---
## Tests manuels
1. Lancer l'application.
2. Ouvrir une enquête.
3. Vérifier que la page d'accueil est affichée sans sélection.
4. Sélectionner un dossier.
5. Vérifier l'affichage de la page d'informations.
6. Sélectionner un fichier.
7. Vérifier la mise à jour de la même page.
8. Changer d'enquête.
9. Vérifier le retour à la page d'accueil.
10. Lancer `make test`.
---
## Commit attendu
```text
refactor(gui): use GtkStack in workspace
```

View file

@ -1,241 +0,0 @@
# Ticket #017
## Titre
Ajouter le chemin complet à `InvestigationNode`.
---
## Objectif
Faire évoluer `InvestigationNode` afin que chaque nœud connaisse le chemin
complet qu'il représente sur le système de fichiers.
Cette information permettra ensuite au `Workspace` et aux futurs visualiseurs
de savoir exactement quel fichier ou dossier est sélectionné.
---
## Responsabilités
Le module `InvestigationNode` doit :
- mémoriser un chemin complet ;
- posséder sa propre copie de ce chemin ;
- exposer le chemin en lecture seule ;
- libérer correctement cette chaîne lors de sa destruction.
Le module `InvestigationTreeBuilder` doit :
- fournir le chemin complet lors de la création de chaque nœud ;
- construire correctement le chemin des enfants ;
- ne jamais reconstruire le chemin dans la GUI.
---
## Architecture
```text
InvestigationTreeBuilder
InvestigationNode
├── name
├── path
├── type
├── parent
└── children
```
Le chemin appartient au Core.
La couche graphique ne fait que le lire.
---
## Fichiers concernés
```text
include/core/investigation_node.h
src/core/investigation_node.c
src/core/investigation_tree_builder.c
tests/test_investigation_node.c
tests/test_investigation_tree_builder.c
```
Aucune modification graphique dans ce ticket.
---
## API publique à faire évoluer
Le constructeur devient :
```c
InvestigationNode *investigation_node_new(
const char *name,
const char *path,
InvestigationNodeType type
);
```
Un nouveau getter est ajouté :
```c
const char *investigation_node_get_path(
const InvestigationNode *node
);
```
---
## Principe de propriété
Le nœud copie le chemin reçu avec une allocation dédiée.
Après :
```c
node = investigation_node_new(
"Enquete.sqlite",
"/home/fy59/Enquetes/Test/00_BaseDeDonnees/Enquete.sqlite",
INVESTIGATION_NODE_FILE
);
```
le code appelant peut modifier ou libérer ses propres chaînes.
Le nœud reste propriétaire de ses copies internes.
---
## Structure interne attendue
```c
struct InvestigationNode
{
char *name;
char *path;
InvestigationNodeType type;
InvestigationNode *parent;
GPtrArray *children;
};
```
La structure reste opaque.
---
## Comportement attendu
Pour le dossier racine :
```text
Nom :
Test
Chemin :
/home/fy59/Enquetes/Test
```
Pour un fichier enfant :
```text
Nom :
Enquete.sqlite
Chemin :
/home/fy59/Enquetes/Test/00_BaseDeDonnees/Enquete.sqlite
```
---
## Règles de validation
Le constructeur doit refuser :
- `name == NULL` ;
- un nom vide ;
- `path == NULL` ;
- un chemin vide.
Le getter doit retourner :
```c
NULL
```
si le nœud vaut `NULL`.
---
## Hors périmètre
Ce ticket ne doit pas :
- afficher le chemin dans le `Workspace` ;
- ouvrir un fichier ;
- normaliser les permissions ;
- résoudre les liens symboliques ;
- calculer un chemin relatif ;
- modifier le système de fichiers ;
- ajouter des métadonnées.
---
## Contraintes techniques
- C17 ;
- GLib autorisée ;
- aucune dépendance GTK ;
- structure opaque ;
- aucun état global ;
- documentation Doxygen ;
- compilation sans warning ;
- aucune fuite mémoire.
---
## Critères d'acceptation
- [ ] Le projet compile sans warning.
- [ ] `InvestigationNode` possède un chemin.
- [ ] Le chemin est copié.
- [ ] Le getter retourne le bon chemin.
- [ ] Les chemins racine et enfants sont corrects.
- [ ] Les noms invalides sont refusés.
- [ ] Les chemins invalides sont refusés.
- [ ] Le builder construit correctement tous les chemins.
- [ ] Tous les tests existants sont adaptés.
- [ ] `make test` reste entièrement valide.
- [ ] Aucun code GTK n'est modifié.
---
## Tests
### InvestigationNode
- créer un nœud avec un chemin valide ;
- lire le chemin ;
- tester `path == NULL` ;
- tester un chemin vide ;
- vérifier que la chaîne est copiée ;
- tester `investigation_node_get_path(NULL)`.
### InvestigationTreeBuilder
- vérifier le chemin de la racine ;
- vérifier le chemin de `DirectoryA` ;
- vérifier le chemin de `FileA.txt` ;
- vérifier le chemin de `RootFile.md`.
---
## Commit attendu
```text
feat(core): add path to investigation nodes
```

View file

@ -1,196 +0,0 @@
# Ticket #018
## Titre
Afficher le chemin complet du nœud sélectionné dans le Workspace.
---
## Objectif
Afficher le chemin complet du fichier ou du dossier actuellement sélectionné.
Le `Workspace` doit utiliser exclusivement le getter :
```c
investigation_node_get_path()
```
Aucune reconstruction du chemin ne doit être réalisée dans la couche graphique.
---
## Architecture
```text
InvestigationNode
Application
MainWindow
Workspace
```
Le Core reste propriétaire des informations.
Le `Workspace` les affiche uniquement.
---
## Responsabilités
### InvestigationNode
Aucune modification.
### Application
Aucune modification.
### MainWindow
Aucune modification.
### Workspace
Le module doit :
- afficher le chemin complet du nœud sélectionné ;
- masquer cette information lorsqu'aucun nœud n'est sélectionné.
---
## Fichiers concernés
```text
src/widgets/workspace.c
```
Aucune modification du Core.
---
## Interface publique
Aucune modification.
L'API du `Workspace` reste inchangée.
---
## Affichage attendu
### Dossier
```text
00_BaseDeDonnees
Chemin :
/home/fy59/Documents/Enquetes/Test/00_BaseDeDonnees
Type :
Dossier
Parent :
Template
Enfants :
2
```
### Fichier
```text
Enquete.sqlite
Chemin :
/home/fy59/Documents/Enquetes/Test/00_BaseDeDonnees/Enquete.sqlite
Type :
Fichier
Parent :
00_BaseDeDonnees
```
---
## Comportement attendu
Le chemin affiché doit toujours être identique à :
```c
investigation_node_get_path(node)
```
Le `Workspace` ne doit jamais construire lui-même :
```text
parent + "/" + nom
```
---
## Hors périmètre
Ce ticket ne doit pas :
- ouvrir le fichier ;
- vérifier que le chemin existe encore ;
- afficher la taille ;
- afficher les permissions ;
- afficher la date de modification ;
- ouvrir le gestionnaire de fichiers.
---
## Contraintes techniques
- GTK4 uniquement ;
- aucun état global ;
- aucune allocation mémoire supplémentaire pour le chemin ;
- utiliser directement le pointeur retourné par
`investigation_node_get_path()`;
- documentation Doxygen ;
- compilation sans warning.
---
## Critères d'acceptation
- [ ] Le projet compile sans warning.
- [ ] `make test` reste valide.
- [ ] Le chemin du dossier est affiché.
- [ ] Le chemin du fichier est affiché.
- [ ] La page d'accueil reste inchangée.
- [ ] Aucun chemin n'est reconstruit dans la GUI.
- [ ] Aucun `Gtk-CRITICAL`.
---
## Tests
1. Lancer l'application.
2. Sélectionner un dossier.
3. Vérifier le chemin affiché.
4. Sélectionner un fichier.
5. Vérifier le chemin affiché.
6. Comparer avec :
```c
investigation_node_get_path(node)
```
7. Vérifier que `make test` reste valide.
---
## Commit attendu
```text
feat(gui): display selected node path
```

View file

@ -1,208 +0,0 @@
# Ticket #019
## Titre
Afficher une icône selon le type de nœud dans l'arborescence.
---
## Objectif
Améliorer la lisibilité de `InvestigationTreeView` en affichant une icône
devant chaque dossier ou fichier.
Les icônes doivent être déterminées uniquement dans la couche graphique.
---
## Architecture
```text
InvestigationNode
InvestigationTreeView
GtkImage + GtkLabel
```
Le Core fournit :
- le nom ;
- le chemin ;
- le type.
La vue choisit l'icône à afficher.
---
## Responsabilités
### InvestigationTreeView
Le module doit :
- afficher une icône devant chaque nœud ;
- utiliser une icône de dossier pour les répertoires ;
- utiliser une icône générique pour les fichiers inconnus ;
- choisir certaines icônes spécialisées selon l'extension ;
- conserver le nom du nœud à côté de l'icône ;
- rester compatible avec `GtkTreeExpander`.
---
## Fichiers concernés
```text
src/widgets/investigation_tree_view.c
```
Éventuellement :
```text
include/widgets/investigation_tree_view.h
```
uniquement si la documentation doit être précisée.
Aucune modification du Core.
---
## Extensions reconnues
Première version minimale :
```text
.sqlite .db
.jpg .jpeg .png .webp
.pdf
.txt .md .log
.csv
.zip .tar .gz .xz
.mp4 .mkv .avi .mov
```
Les extensions inconnues utilisent une icône de fichier générique.
La comparaison doit être insensible à la casse.
---
## Icônes GTK attendues
Utiliser les noms d'icônes du thème système, par exemple :
```text
folder-symbolic
text-x-generic-symbolic
application-pdf-symbolic
image-x-generic-symbolic
video-x-generic-symbolic
package-x-generic-symbolic
x-office-spreadsheet-symbolic
folder-database-symbolic
```
Si une icône spécialisée n'est pas disponible dans le thème, GTK doit
retomber proprement sur une icône générique.
---
## Structure visuelle d'une ligne
```text
GtkTreeExpander
└── GtkBox horizontal
├── GtkImage
└── GtkLabel
```
Le label continue d'afficher le nom du nœud.
---
## Fonctions privées suggérées
```c
static const char *investigation_tree_view_get_icon_name(
const InvestigationNode *node
);
```
Cette fonction doit :
- retourner `folder-symbolic` pour un dossier ;
- inspecter l'extension pour un fichier ;
- retourner une icône générique si aucune extension ne correspond.
---
## Hors périmètre
Ce ticket ne doit pas :
- charger des miniatures ;
- analyser le contenu réel des fichiers ;
- lire les types MIME ;
- modifier le Core ;
- ouvrir les fichiers ;
- colorer les lignes ;
- ajouter des badges ;
- ajouter un menu contextuel.
---
## Contraintes techniques
- GTK4 uniquement ;
- utiliser `GtkImage` ;
- utiliser les icônes du thème système ;
- aucun fichier d'icône embarqué ;
- aucun état global ;
- aucune lecture directe du système de fichiers ;
- compilation sans warning ;
- aucun `Gtk-CRITICAL`.
---
## Critères d'acceptation
- [ ] Le projet compile sans warning.
- [ ] `make test` reste valide.
- [ ] Les dossiers affichent une icône de dossier.
- [ ] Les fichiers génériques affichent une icône de fichier.
- [ ] Les PDF ont une icône dédiée.
- [ ] Les images ont une icône dédiée.
- [ ] Les vidéos ont une icône dédiée.
- [ ] Les archives ont une icône dédiée.
- [ ] Les bases SQLite ont une icône dédiée.
- [ ] Le nom du nœud reste visible.
- [ ] Le développement et le repli fonctionnent toujours.
- [ ] La sélection fonctionne toujours.
- [ ] Aucun code Core n'est modifié.
- [ ] Aucun `Gtk-CRITICAL`.
---
## Tests manuels
1. Ouvrir une enquête contenant plusieurs types de fichiers.
2. Vérifier l'icône des dossiers.
3. Vérifier une image.
4. Vérifier un PDF.
5. Vérifier une base SQLite.
6. Vérifier une archive.
7. Vérifier un fichier inconnu.
8. Développer et replier plusieurs branches.
9. Sélectionner plusieurs nœuds.
10. Lancer `make test`.
---
## Commit attendu
```text
feat(gui): add icons to investigation tree
```

View file

@ -1,257 +0,0 @@
# Ticket #020
## Titre
Créer une nouvelle enquête.
---
## Objectif
Ajouter la capacité de créer automatiquement une nouvelle enquête dans un dossier choisi par l'utilisateur.
La création doit produire une arborescence standard et préparer l'emplacement de la future base SQLite.
Ce ticket ne crée pas encore le schéma SQL complet.
---
## Architecture
```text
Application
InvestigationProject
FileSystem
```
Le module `InvestigationProject` devient le point d'entrée métier pour la création d'une enquête.
La GUI ne doit jamais créer directement les dossiers.
---
## Responsabilités
### InvestigationProject
Le module doit :
- recevoir le chemin du dossier parent ;
- recevoir le nom de la nouvelle enquête ;
- créer le dossier racine de l'enquête ;
- créer l'arborescence standard ;
- créer le dossier `00_BaseDeDonnees` ;
- créer un fichier SQLite vide nommé `Enquete.sqlite` ;
- nettoyer ce qui a été créé en cas d'échec partiel ;
- retourner le chemin complet de l'enquête créée.
---
## Arborescence à créer
```text
NomEnquete/
├── 00_BaseDeDonnees/
│ └── Enquete.sqlite
├── 01_Preuves_Originales/
│ ├── Captures_Ecran/
│ ├── Conversations/
│ ├── Documents/
│ ├── Emails/
│ ├── Photos/
│ └── Videos/
├── 02_Preuves_Traitees/
│ ├── Annotations/
│ ├── Extractions/
│ ├── OCR/
│ └── Redactions/
├── 03_Chronologie/
├── 04_Entites/
│ ├── Adresses_Email/
│ ├── Comptes_Bancaires/
│ ├── Comptes_Facebook/
│ ├── Comptes_Instagram/
│ ├── Documents_Identite/
│ ├── IBAN/
│ ├── Personnes/
│ ├── Pseudonymes/
│ ├── Telephones/
│ └── Autres/
├── 05_Rapports/
├── 06_Exports/
├── 07_Notes/
├── 08_Sources/
└── 09_Hash/
```
Les noms de dossiers ne doivent pas contenir d'accents.
---
## Interface publique attendue
Créer :
```text
include/core/investigation_project.h
src/core/investigation_project.c
```
Avec :
```c
char *investigation_project_create(
const char *parent_directory,
const char *investigation_name
);
```
---
## Contrat de propriété
En cas de succès, la fonction retourne une nouvelle chaîne allouée contenant le chemin complet du dossier d'enquête.
Le code appelant devient propriétaire de cette chaîne et doit la libérer avec :
```c
g_free(investigation_path);
```
En cas d'échec, la fonction retourne :
```c
NULL
```
---
## Règles de validation
La fonction doit refuser :
- `parent_directory == NULL` ;
- un chemin parent vide ;
- `investigation_name == NULL` ;
- un nom vide ;
- un dossier parent inexistant ;
- un chemin parent qui n'est pas un dossier ;
- un dossier d'enquête qui existe déjà ;
- un nom contenant `/`.
---
## Gestion des erreurs
Si une étape échoue après la création partielle :
- tous les fichiers créés par la fonction doivent être supprimés ;
- tous les dossiers créés par la fonction doivent être supprimés ;
- aucun dossier partiel ne doit rester sur le disque ;
- la fonction retourne `NULL`.
La fonction ne doit jamais supprimer un dossier qui existait avant son appel.
---
## Création de `Enquete.sqlite`
Pour ce ticket, le fichier doit seulement être créé.
Le schéma SQLite sera initialisé dans le ticket #022.
Le fichier attendu est :
```text
00_BaseDeDonnees/Enquete.sqlite
```
---
## Hors périmètre
Ce ticket ne doit pas :
- créer les tables SQLite ;
- générer un UUID ;
- créer les métadonnées ;
- importer une enquête existante ;
- modifier l'interface GTK ;
- ouvrir automatiquement l'enquête créée ;
- créer un rapport ;
- copier des preuves.
---
## Dépendances
- C17 ;
- GLib ;
- GIO autorisé.
Aucune dépendance GTK ou SQLite requise dans ce ticket.
---
## Contraintes techniques
- structure modulaire ;
- aucun état global ;
- aucune logique de création dans `Application` ;
- aucune logique de création dans les widgets ;
- documentation Doxygen ;
- compilation sans warning ;
- nettoyage complet en cas d'erreur ;
- noms de fonctions préfixés par `investigation_project_`.
---
## Tests
Créer :
```text
tests/test_investigation_project.c
```
Le test doit :
- créer un dossier temporaire ;
- créer une enquête valide ;
- vérifier chaque dossier attendu ;
- vérifier la présence de `Enquete.sqlite` ;
- vérifier que la fonction retourne le bon chemin ;
- vérifier le refus des paramètres `NULL` ;
- vérifier le refus des chaînes vides ;
- vérifier le refus d'un nom contenant `/` ;
- vérifier le refus si le dossier existe déjà ;
- supprimer complètement l'enquête de test ;
- vérifier qu'aucun résidu ne reste.
---
## Critères d'acceptation
- [ ] Le projet compile sans warning.
- [ ] `make test` reste valide.
- [ ] Une enquête complète peut être créée.
- [ ] Tous les dossiers attendus existent.
- [ ] `Enquete.sqlite` existe.
- [ ] Aucun dossier partiel ne reste en cas d'échec.
- [ ] Un dossier existant n'est jamais écrasé.
- [ ] Le chemin retourné est correct.
- [ ] Aucune dépendance GTK.
- [ ] Aucun code SQLite métier.
- [ ] Le nouveau test est intégré à `make test`.
---
## Commit attendu
```text
feat(core): create investigation project structure
```

View file

@ -1,254 +0,0 @@
# Ticket #021
## Titre
Valider une enquête existante.
---
## Objectif
Ajouter au module `InvestigationProject` la capacité de vérifier quun dossier
correspond bien à une enquête Labfy Investigation valide.
La validation doit contrôler la présence et le type des éléments obligatoires,
sans modifier le contenu du dossier.
---
## Architecture
```text
Application
InvestigationProject
FileSystem
```
Toute ouverture denquête existante doit passer par
`InvestigationProject`.
La GUI ne doit jamais décider seule si un dossier est valide.
---
## Responsabilités
### InvestigationProject
Le module doit :
- recevoir le chemin dun dossier ;
- vérifier que le chemin existe ;
- vérifier quil désigne un dossier ;
- vérifier la présence de larborescence obligatoire ;
- vérifier que les dossiers attendus sont bien des dossiers ;
- vérifier la présence de `00_BaseDeDonnees/Enquete.sqlite` ;
- vérifier que `Enquete.sqlite` est un fichier régulier ;
- retourner un résultat clair sans modifier le dossier.
---
## Interface publique attendue
Faire évoluer :
```text
include/core/investigation_project.h
src/core/investigation_project.c
```
Ajouter :
```c
bool investigation_project_validate(
const char *investigation_path
);
```
---
## Contrat
La fonction retourne :
```c
true
```
si le dossier est une enquête valide.
Elle retourne :
```c
false
```
si :
- le chemin est invalide ;
- le dossier nexiste pas ;
- le chemin désigne un fichier ;
- un dossier obligatoire manque ;
- un élément attendu comme dossier est en réalité un fichier ;
- `Enquete.sqlite` manque ;
- `Enquete.sqlite` nest pas un fichier régulier.
La fonction ne doit jamais modifier le système de fichiers.
---
## Structure obligatoire
Les dossiers suivants doivent exister :
```text
00_BaseDeDonnees
01_Preuves_Originales
02_Preuves_Traitees
03_Chronologie
04_Entites
05_Rapports
06_Exports
07_Notes
08_Sources
09_Hash
```
Les sous-dossiers suivants doivent également exister :
```text
01_Preuves_Originales/Captures_Ecran
01_Preuves_Originales/Conversations
01_Preuves_Originales/Documents
01_Preuves_Originales/Emails
01_Preuves_Originales/Photos
01_Preuves_Originales/Videos
02_Preuves_Traitees/Annotations
02_Preuves_Traitees/Extractions
02_Preuves_Traitees/OCR
02_Preuves_Traitees/Redactions
04_Entites/Adresses_Email
04_Entites/Comptes_Bancaires
04_Entites/Comptes_Facebook
04_Entites/Comptes_Instagram
04_Entites/Documents_Identite
04_Entites/IBAN
04_Entites/Personnes
04_Entites/Pseudonymes
04_Entites/Telephones
04_Entites/Autres
```
Le fichier suivant doit exister :
```text
00_BaseDeDonnees/Enquete.sqlite
```
---
## Réutilisation de la structure déclarative
La validation doit réutiliser la même liste de chemins que la création.
Il ne doit pas exister deux listes indépendantes décrivant larborescence.
La structure de référence doit rester centralisée dans
`investigation_project.c`.
---
## Hors périmètre
Ce ticket ne doit pas :
- ouvrir SQLite ;
- lire le schéma SQL ;
- vérifier la version de la base ;
- créer les éléments manquants ;
- réparer une enquête ;
- importer automatiquement des fichiers ;
- modifier GTK ;
- ouvrir automatiquement lenquête dans lapplication.
---
## Gestion des erreurs
Dans ce ticket, la fonction retourne uniquement un booléen.
Les détails derreur plus précis pourront être ajoutés plus tard avec :
```c
GError
```
ou une énumération métier dédiée.
La validation ne doit produire aucun `g_warning()` pour un dossier simplement
invalide : un résultat `false` suffit.
---
## Contraintes techniques
- C17 ;
- GLib autorisée ;
- aucune dépendance GTK ;
- aucune dépendance SQLite requise ;
- aucune écriture sur le disque ;
- aucun état global ;
- documentation Doxygen ;
- compilation sans warning.
---
## Tests
Faire évoluer :
```text
tests/test_investigation_project.c
```
Le test doit vérifier :
- une enquête complète est valide ;
- `NULL` est refusé ;
- une chaîne vide est refusée ;
- un chemin inexistant est refusé ;
- un fichier simple est refusé ;
- une enquête sans `Enquete.sqlite` est refusée ;
- une enquête avec un dossier obligatoire manquant est refusée ;
- un élément attendu comme dossier mais remplacé par un fichier est refusé ;
- une enquête valide reste inchangée après validation.
---
## Critères dacceptation
- [ ] Le projet compile sans warning.
- [ ] `make test` reste entièrement valide.
- [ ] Une enquête créée par `investigation_project_create()` est valide.
- [ ] Un chemin invalide est refusé.
- [ ] Un dossier incomplet est refusé.
- [ ] Un faux fichier `Enquete.sqlite` incorrect est refusé.
- [ ] Aucun élément nest créé pendant la validation.
- [ ] Aucun élément nest supprimé pendant la validation.
- [ ] La structure de référence nest pas dupliquée.
- [ ] Aucune dépendance GTK.
- [ ] Aucun `Gtk-CRITICAL`.
---
## Commit attendu
```text
feat(core): validate investigation project structure
```

View file

@ -1,368 +0,0 @@
# Ticket #022
## Titre
Initialiser la base SQLite d'une enquête.
---
## Objectif
Créer et initialiser correctement le fichier :
```text
00_BaseDeDonnees/Enquete.sqlite
```
avec un premier schéma SQLite versionné.
La base doit contenir les métadonnées minimales permettant d'identifier
l'enquête et la version du schéma.
---
## Architecture
```text
InvestigationProject
Database
SQLite
```
`InvestigationProject` orchestre la création de l'enquête.
Le module `Database` est seul responsable de l'ouverture de SQLite,
de l'exécution du schéma et de la fermeture de la base.
---
## Responsabilités
### InvestigationProject
Le module doit :
- créer l'arborescence ;
- demander au module `Database` d'initialiser `Enquete.sqlite` ;
- considérer la création comme échouée si l'initialisation SQLite échoue ;
- nettoyer toute l'enquête créée en cas d'échec.
### Database
Le module doit :
- ouvrir ou créer le fichier SQLite ;
- démarrer une transaction ;
- créer le schéma initial ;
- insérer les métadonnées ;
- valider la transaction ;
- annuler la transaction en cas d'erreur ;
- fermer proprement la connexion.
---
## Nouveaux fichiers
```text
include/database/database.h
src/database/database.c
```
Éventuellement :
```text
include/database/schema.h
src/database/schema.c
```
si le schéma devient trop volumineux pour rester dans `database.c`.
Pour ce ticket, un seul module `database.c` est acceptable.
---
## Interface publique attendue
```c
bool database_initialize(
const char *database_path
);
```
La fonction retourne :
```c
true
```
si la base a été correctement initialisée.
Elle retourne :
```c
false
```
en cas d'erreur.
---
## Schéma initial
### Table `metadata`
```sql
CREATE TABLE metadata
(
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
```
### Métadonnées obligatoires
```text
schema_version
application
created_at
investigation_uuid
```
Valeurs attendues :
```text
schema_version = 1
application = Labfy Investigation
created_at = date UTC ISO 8601
investigation_uuid = UUID unique
```
Exemple :
```text
2026-07-14T18:42:15Z
```
---
## Table `investigation`
Créer également une table minimale représentant l'enquête :
```sql
CREATE TABLE investigation
(
id INTEGER PRIMARY KEY CHECK (id = 1),
name TEXT NOT NULL,
root_path TEXT NOT NULL,
created_at TEXT NOT NULL
);
```
La table contient une seule ligne correspondant à l'enquête courante.
---
## Données nécessaires
`database_initialize()` doit recevoir suffisamment d'informations pour
initialiser correctement la base.
L'interface pourra donc évoluer vers :
```c
bool database_initialize(
const char *database_path,
const char *investigation_name,
const char *investigation_root_path
);
```
Cette signature est préférée pour éviter que le module `Database`
reconstruise ou devine des informations métier.
---
## UUID
L'UUID doit être généré avec GLib :
```c
g_uuid_string_random()
```
La chaîne retournée doit être libérée avec :
```c
g_free(uuid);
```
---
## Date de création
La date doit être produite en UTC avec GLib.
Format attendu :
```text
YYYY-MM-DDTHH:MM:SSZ
```
La date doit être enregistrée à la fois :
- dans `metadata.created_at` ;
- dans `investigation.created_at`.
---
## Transaction
Toute l'initialisation doit se dérouler dans une transaction :
```sql
BEGIN IMMEDIATE;
```
Puis :
```sql
COMMIT;
```
En cas d'erreur :
```sql
ROLLBACK;
```
Une base partiellement initialisée ne doit jamais être considérée comme valide.
---
## Intégration avec InvestigationProject
`investigation_project_create()` ne doit plus créer un fichier vide avec :
```c
g_file_set_contents(...)
```
Il doit construire le chemin de la base puis appeler :
```c
database_initialize(
database_path,
investigation_name,
investigation_path
);
```
Si l'appel échoue :
- le fichier SQLite éventuel est supprimé ;
- tous les dossiers créés sont supprimés ;
- `investigation_project_create()` retourne `NULL`.
---
## Validation
À partir de ce ticket, `investigation_project_validate()` doit toujours
vérifier la présence du fichier SQLite, mais pas encore son contenu SQL.
La validation du schéma sera ajoutée dans un prochain ticket dédié.
---
## Hors périmètre
Ce ticket ne doit pas :
- créer les tables Preuves ;
- créer les tables Entites ;
- créer les relations ;
- gérer les migrations ;
- ouvrir une enquête existante ;
- modifier GTK ;
- exposer directement `sqlite3 *` hors du module Database.
---
## Contraintes techniques
- C17 ;
- SQLite3 ;
- GLib ;
- aucune dépendance GTK ;
- aucun état global ;
- requêtes SQL centralisées dans `database.c` ;
- fermeture garantie de la connexion ;
- transaction obligatoire ;
- documentation Doxygen ;
- compilation sans warning.
---
## Tests
Créer :
```text
tests/test_database.c
```
Le test doit :
- créer un dossier temporaire ;
- initialiser une base SQLite ;
- vérifier que le fichier existe ;
- ouvrir la base en lecture ;
- vérifier la présence de la table `metadata` ;
- vérifier la présence de la table `investigation` ;
- vérifier `schema_version = 1` ;
- vérifier `application = Labfy Investigation` ;
- vérifier que `created_at` n'est pas vide ;
- vérifier que l'UUID n'est pas vide ;
- vérifier la ligne unique de la table `investigation` ;
- vérifier le nom et le chemin racine ;
- vérifier qu'une initialisation sur un chemin invalide échoue ;
- nettoyer complètement les fichiers temporaires.
Faire également évoluer :
```text
tests/test_investigation_project.c
```
pour vérifier que la base créée n'est plus vide.
---
## Critères d'acceptation
- [ ] Le projet compile sans warning.
- [ ] `make test` reste entièrement valide.
- [ ] `Enquete.sqlite` est une vraie base SQLite.
- [ ] La table `metadata` existe.
- [ ] La table `investigation` existe.
- [ ] `schema_version` vaut `1`.
- [ ] Une date UTC est enregistrée.
- [ ] Un UUID est généré.
- [ ] L'enquête est enregistrée dans la base.
- [ ] L'initialisation est transactionnelle.
- [ ] Toute erreur provoque un nettoyage complet.
- [ ] Aucun type `sqlite3 *` n'est exposé publiquement.
- [ ] Aucune dépendance GTK.
---
## Commit attendu
```text
feat(database): initialize investigation database
```

View file

@ -1,643 +0,0 @@
# Ticket #023
## Titre
Consolider le schéma métier initial de l'enquête.
---
## Objectif
Transformer le schéma SQLite préparé lors des premières phases du projet en
un schéma métier officiel, cohérent, documenté et testable.
La base existante sert de fondation.
Le travail ne consiste pas à repartir de zéro, mais à :
- conserver les concepts métier déjà définis ;
- corriger les incohérences ;
- renforcer les contraintes ;
- uniformiser les conventions ;
- intégrer le schéma au module `Database`.
---
## Base de travail
La base historique contient notamment les tables suivantes :
```text
preuves
entites
personnes
chronologie
sources
recherche
hypotheses
journal
tags
types_preuve
types_entite
types_source
types_outils
associations
personnes_entites
entite_tags
preuves_tag
```
Les tables créées au ticket #022 restent également obligatoires :
```text
metadata
investigation
```
Le schéma officiel doit donc préserver ces concepts tout en les consolidant.
---
## Architecture
```text
InvestigationProject
Database
Schéma SQLite versionné
├── Métadonnées
├── Enquête
├── Preuves
├── Entités
├── Personnes
├── Sources
├── Chronologie
├── Recherches
├── Hypothèses
├── Journal
└── Tags et relations
```
Le module `Database` reste le seul autorisé à exécuter du SQL.
---
## Décisions de conception à officialiser
### Identifiants
Les entités métier utiliseront des identifiants texte de type UUID.
Exemple :
```text
550e8400-e29b-41d4-a716-446655440000
```
Les identifiants sont générés par l'application avec :
```c
g_uuid_string_random()
```
Les tables de référence simples peuvent utiliser des identifiants entiers.
---
### Dates
Les dates techniques sont stockées en UTC au format ISO 8601 :
```text
YYYY-MM-DDTHH:MM:SSZ
```
Exemple :
```text
2026-07-15T08:42:17Z
```
---
### Nommage
Les noms SQL utilisent uniquement :
- des minuscules ;
- du `snake_case` ;
- aucun accent ;
- aucun espace.
Les noms des tables de liaison sont uniformisés.
Exemple :
```text
preuve_tags
entite_tags
personne_entites
```
Aucune forme singulier/pluriel incohérente ne doit subsister.
---
### Clés étrangères
Toutes les relations métier doivent utiliser des clés étrangères explicites.
Le module `Database` doit activer :
```sql
PRAGMA foreign_keys = ON;
```
à chaque ouverture de connexion.
Les comportements `ON DELETE` doivent être décidés pour chaque relation.
---
### Tables de liaison
Chaque table de liaison doit empêcher les doublons grâce à :
- une clé primaire composite ;
- ou une contrainte `UNIQUE`.
Exemple :
```sql
PRIMARY KEY (preuve_id, tag_id)
```
---
## Tables métier à consolider
### `preuves`
Responsabilité :
- représenter un élément de preuve ;
- mémoriser son nom ;
- son chemin relatif ;
- son type ;
- sa description ;
- ses dates ;
- son hash éventuel ;
- son statut.
Contraintes minimales :
- identifiant obligatoire ;
- nom obligatoire ;
- chemin relatif obligatoire ;
- type obligatoire ;
- date de création obligatoire ;
- chemin unique au sein d'une enquête.
---
### `entites`
Responsabilité :
- représenter une entité identifiée pendant l'enquête.
Exemples :
- adresse email ;
- compte bancaire ;
- profil social ;
- pseudonyme ;
- téléphone ;
- document d'identité.
Contraintes minimales :
- identifiant obligatoire ;
- type obligatoire ;
- valeur obligatoire ;
- date de création obligatoire.
---
### `personnes`
Responsabilité :
- représenter une personne physique ou un profil humain étudié.
Le modèle ne doit pas obliger à connaître l'identité civile complète.
Une personne peut n'avoir qu'un pseudonyme ou une désignation temporaire.
---
### `sources`
Responsabilité :
- représenter l'origine d'une information ou d'une preuve.
Exemples :
- site web ;
- email ;
- réseau social ;
- document ;
- témoignage ;
- outil OSINT.
---
### `chronologie`
Responsabilité :
- mémoriser les événements importants de l'enquête ;
- relier éventuellement un événement à une preuve, une entité ou une source.
---
### `recherches`
Responsabilité :
- documenter une opération de recherche ;
- mémoriser la requête ;
- l'outil employé ;
- la date ;
- le résultat ;
- les observations.
---
### `hypotheses`
Responsabilité :
- enregistrer une hypothèse de travail ;
- mémoriser son statut ;
- sa justification ;
- les éléments qui la soutiennent ou la contredisent.
---
### `journal`
Responsabilité :
- conserver la trace des actions significatives réalisées dans l'enquête.
Exemples :
- création d'une preuve ;
- modification d'une entité ;
- calcul d'un hash ;
- génération d'un rapport ;
- import d'un fichier.
---
### `tags`
Responsabilité :
- permettre une classification libre des preuves et des entités.
---
## Tables de référence
Les tables suivantes doivent être consolidées :
```text
types_preuve
types_entite
types_source
types_outils
```
Elles doivent comporter au minimum :
```sql
id INTEGER PRIMARY KEY
code TEXT NOT NULL UNIQUE
label TEXT NOT NULL
description TEXT
```
Les valeurs initiales sont insérées lors de la création de la base.
---
## Tables de liaison
Les relations suivantes doivent être prévues :
```text
preuve_tags
entite_tags
personne_entites
```
Le schéma existant contient également une table générique :
```text
associations
```
Cette table doit être auditée avant conservation.
Elle ne doit être gardée que si son rôle est clairement défini et ne fait pas
doublon avec des relations spécialisées.
---
## Audit obligatoire
Avant d'écrire le schéma définitif, chaque table de la base historique doit
être classée dans l'une des catégories suivantes :
```text
CONSERVER
MODIFIER
RENOMMER
FUSIONNER
SUPPRIMER
```
La décision doit être documentée dans :
```text
docs/database/SCHEMA_AUDIT_V1.md
```
Le document doit contenir au minimum :
```text
Nom historique
Décision
Nom final
Justification
Modifications prévues
```
---
## Organisation du module Database
Le code SQL est organisé par domaine métier.
```
include/
└── database/
├── database.h
├── schema.h
├── preuve.h
├── entite.h
├── personne.h
├── source.h
├── chronologie.h
├── recherche.h
├── hypothese.h
├── journal.h
├── tag.h
├── type_preuve.h
├── type_entite.h
├── type_source.h
└── type_outil.h
src/
└── database/
├── database.c
├── schema.c
├── preuve.c
├── entite.c
├── personne.c
├── source.c
├── chronologie.c
├── recherche.c
├── hypothese.c
├── journal.c
├── tag.c
├── type_preuve.c
├── type_entite.c
├── type_source.c
└── type_outil.c
database/
├── schema_v1.sql
├── indexes.sql
├── triggers.sql
└── reference_data.sql
```
---
### Responsabilités
`database.c`
- ouverture de SQLite ;
- fermeture ;
- transactions ;
- erreurs ;
- point d'entrée du module Database.
`schema.c`
- installation du schéma ;
- migrations ;
- exécution des scripts SQL.
Chaque module métier est responsable exclusivement de son objet métier.
Exemple :
```
preuve.c
```
gère uniquement :
- création ;
- lecture ;
- modification ;
- suppression logique ;
- gestion des tags de la preuve.
Les tables de liaison simples ne possèdent pas de module dédié.
Exemples :
```
preuve_tags
personne_entites
entite_tags
```
Leur manipulation est réalisée par le module métier correspondant.
---
## Intégration dans `Database`
Le module `database_initialize()` doit exécuter le schéma officiel contenu
dans :
```text
database/schema_v1.sql
```
Deux approches sont acceptables :
1. intégrer le schéma au binaire via une ressource GLib ;
2. générer une chaîne C depuis le fichier SQL lors de la compilation.
Le logiciel installé ne doit pas dépendre d'un chemin relatif fragile vers le
dépôt source.
---
## Principe d'organisation
Une table métier importante implique systématiquement :
- une structure C ;
- un module C ;
- des tests ;
- une documentation.
La correspondance est la suivante :
| SQL | C |
|-----|---|
| preuves | preuve.c |
| entites | entite.c |
| personnes | personne.c |
| sources | source.c |
| chronologie | chronologie.c |
| journal | journal.c |
| recherches | recherche.c |
| hypotheses | hypothese.c |
---
## Version du schéma
Le numéro de version reste :
```text
1
```
tant que Labfy Investigation n'a pas produit de base officiellement publiée.
Une fois le schéma V1 considéré comme stable et distribué, toute modification
incompatible nécessitera :
- une nouvelle version ;
- une migration dédiée ;
- une mise à jour de `schema_version`.
---
## Hors périmètre
Ce ticket ne doit pas :
- développer les fonctions CRUD ;
- créer les structures C `Preuve`, `Entite` ou `Personne` ;
- connecter les tables à GTK ;
- importer les anciennes données automatiquement ;
- créer les migrations V2 ;
- produire un rapport ;
- ajouter la recherche plein texte.
---
## Contraintes techniques
- C17 ;
- SQLite3 ;
- GLib ;
- aucune dépendance GTK ;
- toutes les tables utilisent des contraintes explicites ;
- toutes les clés étrangères sont documentées ;
- toutes les tables de liaison empêchent les doublons ;
- aucun SQL métier hors du module `Database` ;
- aucun identifiant généré par SQLite pour les objets métier en UUID ;
- compilation sans warning.
---
## Tests
Faire évoluer :
```text
tests/test_database.c
```
Les tests doivent vérifier :
- la présence de toutes les tables officielles ;
- la présence des tables de référence ;
- l'insertion des valeurs initiales ;
- l'activation des clés étrangères ;
- le refus d'une clé étrangère invalide ;
- le refus d'un doublon dans une table de liaison ;
- le refus des colonnes obligatoires manquantes ;
- l'unicité des codes des tables de référence ;
- la valeur `schema_version = 1` ;
- l'intégrité SQLite avec :
```sql
PRAGMA integrity_check;
```
---
## Critères d'acceptation
- [ ] Le schéma historique a été audité.
- [ ] Chaque table possède une décision documentée.
- [ ] Un schéma V1 officiel existe.
- [ ] Les conventions de nommage sont uniformes.
- [ ] Toutes les clés primaires sont explicites.
- [ ] Toutes les clés étrangères sont explicites.
- [ ] Les relations empêchent les doublons.
- [ ] Les colonnes obligatoires utilisent `NOT NULL`.
- [ ] Les tables de référence sont initialisées.
- [ ] `database_initialize()` installe le schéma complet.
- [ ] `PRAGMA integrity_check` retourne `ok`.
- [ ] Tous les tests restent valides.
- [ ] Aucune dépendance GTK.
---
## Livrables
```text
docs/database/SCHEMA_AUDIT_V1.md
database/schema_v1.sql
include/database/database.h
src/database/database.c
tests/test_database.c
```
---
## Commit attendu
```text
feat(database): consolidate initial investigation schema
```

View file

@ -1,436 +0,0 @@
# Ticket #024
## Titre
Documenter l'architecture de la base de données V1.
---
## Objectif
Créer une documentation de référence expliquant l'architecture SQLite V1 de
Labfy Investigation.
Cette documentation doit permettre à un développeur de comprendre :
- le rôle de chaque table ;
- les relations entre les objets métier ;
- les conventions d'identifiants ;
- les règles sur les dates ;
- la suppression logique ;
- les tables de liaison ;
- les contraintes d'intégrité ;
- la politique de versionnement du schéma.
Le document ne doit pas recopier intégralement le SQL.
Il doit expliquer le modèle et les décisions prises.
---
## Livrable principal
Créer :
```text
docs/database/DATABASE_ARCHITECTURE.md
```
---
## Sources de référence
La documentation doit rester cohérente avec :
```text
database/schema_v1.sql
docs/database/SCHEMA_AUDIT_V1.md
docs/CONVENTIONS.md
```
En cas de contradiction, le schéma SQL exécuté fait foi jusqu'à correction de
la documentation.
---
## Contenu attendu
### 1. Vue d'ensemble
Présenter le rôle de la base SQLite dans une enquête.
Chaque enquête possède sa propre base :
```text
00_BaseDeDonnees/Enquete.sqlite
```
La base est autonome et liée au dossier d'enquête.
---
### 2. Principes généraux
Documenter les choix suivants :
- une base par enquête ;
- aucun chemin absolu dans les objets métier ;
- UUID pour les objets métier ;
- identifiants entiers pour les tables de référence ;
- dates UTC au format ISO 8601 ;
- clés étrangères activées ;
- suppression logique ;
- requêtes préparées obligatoires dans le code C ;
- aucune requête SQL métier hors du module Database.
---
### 3. Domaines du modèle
Présenter les grands ensembles :
```text
Métadonnées
Référentiels
Collecte
Connaissance
Raisonnement
Traçabilité
Classification
```
Exemple de classement :
```text
Métadonnées
├── metadata
└── investigation
Référentiels
├── types_preuve
├── types_entite
├── types_source
└── types_outil
Collecte
├── sources
├── recherches
└── preuves
Connaissance
├── entites
└── relations
Raisonnement
└── hypotheses
Traçabilité
├── chronologie
└── journal
Classification
├── categories
└── tags
```
---
### 4. Chaîne d'investigation
Décrire le flux métier principal :
```text
Source
Recherche
Preuve
Entité
Relation
Hypothèse
```
Préciser que ce flux n'est pas strictement linéaire.
Une recherche peut :
- utiliser une preuve existante ;
- produire plusieurs preuves ;
- découvrir plusieurs entités ;
- confirmer ou contredire une relation ;
- enrichir une hypothèse.
---
### 5. Description des tables métier
Pour chaque table importante, documenter :
- responsabilité ;
- identifiant ;
- colonnes principales ;
- relations ;
- suppression ;
- points d'attention.
Tables concernées :
```text
investigation
sources
recherches
preuves
entites
relations
chronologie
journal
hypotheses
categories
tags
```
---
### 6. Tables de liaison
Documenter le rôle des tables de liaison :
```text
recherche_preuves
recherche_entites
recherche_relations
recherche_hypotheses
preuve_entites
relation_preuves
recherche_chronologie
preuve_chronologie
entite_chronologie
relation_chronologie
hypothese_preuves
hypothese_entites
hypothese_relations
tag_preuves
tag_recherches
tag_entites
tag_relations
tag_hypotheses
tag_chronologie
```
Expliquer :
- les clés primaires composites ;
- l'interdiction des doublons ;
- les colonnes `role` lorsqu'elles existent ;
- le comportement `ON DELETE`.
---
### 7. Relations principales
Inclure un diagramme textuel ou Mermaid.
Exemple :
```mermaid
erDiagram
SOURCES ||--o{ RECHERCHES : "interrogee par"
RECHERCHES ||--o{ RECHERCHE_PREUVES : produit
PREUVES ||--o{ RECHERCHE_PREUVES : participe
PREUVES ||--o{ PREUVE_ENTITES : contient
ENTITES ||--o{ PREUVE_ENTITES : est extraite
ENTITES ||--o{ RELATIONS : source
ENTITES ||--o{ RELATIONS : cible
HYPOTHESES ||--o{ HYPOTHESE_PREUVES : s_appuie_sur
```
Le diagramme peut être simplifié afin de rester lisible.
---
### 8. Identifiants
Expliquer :
```sql
id TEXT PRIMARY KEY
```
pour les objets métier.
Préciser :
- UUID généré dans l'application ;
- pas de colonne `uuid` supplémentaire ;
- aucune dépendance aux numéros de ligne SQLite ;
- meilleure portabilité pour l'import, l'export et la fusion.
---
### 9. Dates
Expliquer la différence entre :
```text
created_at
updated_at
imported_at
file_created_at
started_at
completed_at
event_time
```
Toutes les dates techniques sont enregistrées en UTC.
---
### 10. Suppression logique
Documenter les statuts comme :
```text
active
archived
deleted
```
Préciser que :
- les preuves originales ne sont pas modifiées ;
- les objets supprimés logiquement restent référencés ;
- les purges physiques sont hors des opérations ordinaires ;
- le journal est append-only dans le fonctionnement normal.
---
### 11. Intégrité
Documenter :
- `PRAGMA foreign_keys = ON` ;
- contraintes `NOT NULL` ;
- contraintes `CHECK` ;
- contraintes `UNIQUE` ;
- clés primaires composites ;
- validation applicative complémentaire.
Préciser que SQLite ne suffit pas à valider :
- la syntaxe réelle d'un UUID ;
- la validité complète d'une couleur ;
- la normalisation d'une adresse email ;
- la validité d'un IBAN ;
- l'existence d'une référence polymorphe du journal.
---
### 12. Index
Expliquer que les index couvrent principalement :
- les clés étrangères ;
- les statuts ;
- les dates ;
- les hashes ;
- les valeurs recherchées ;
- les catégories ;
- les relations orientées.
Le document ne doit pas recopier chaque index sans explication.
---
### 13. Versionnement
Documenter :
```text
schema_version = 1
```
Préciser :
- aucune modification destructive d'une V1 publiée ;
- toute évolution incompatible nécessite une migration ;
- les migrations devront être transactionnelles ;
- une sauvegarde devra précéder toute migration ;
- le schéma V1 reste la référence jusqu'à publication d'une V2.
---
### 14. Couche C
Documenter l'organisation prévue :
```text
include/database/
src/database/
```
Avec un module par objet métier :
```text
preuve.c
entite.c
relation.c
source.c
recherche.c
chronologie.c
journal.c
hypothese.c
categorie.c
tag.c
```
Le document doit préciser que cette organisation représente la cible
d'architecture, même si tous les modules ne sont pas encore implémentés.
---
## Hors périmètre
Ce ticket ne doit pas :
- modifier le schéma SQL ;
- ajouter de table ;
- écrire un CRUD ;
- ajouter une migration ;
- modifier GTK ;
- intégrer GResource ;
- modifier le packaging.
Toute incohérence réellement détectée doit être documentée avant de faire
l'objet d'un ticket de correction séparé.
---
## Critères d'acceptation
- [ ] `DATABASE_ARCHITECTURE.md` existe.
- [ ] Le rôle de chaque table métier est expliqué.
- [ ] Les tables de liaison sont documentées.
- [ ] Les UUID sont expliqués.
- [ ] Les dates sont expliquées.
- [ ] La suppression logique est expliquée.
- [ ] Les contraintes d'intégrité sont expliquées.
- [ ] La politique de migrations est définie.
- [ ] Un diagramme global est présent.
- [ ] Le document est cohérent avec `schema_v1.sql`.
- [ ] Aucun changement fonctionnel n'est introduit.
- [ ] `make test` reste entièrement valide.
---
## Commit attendu
```text
docs(database): document v1 database architecture
```

View file

@ -1,215 +0,0 @@
# Ticket #025
## Titre
Refactoriser la couche Database et établir l'infrastructure d'accès aux données.
---
## Objectif
Mettre en place l'architecture définitive de la couche Database afin de fournir
une API interne homogène pour tous les futurs modules métier.
Ce ticket ne comprend **aucun CRUD métier**.
Son objectif est uniquement de construire les fondations techniques qui seront
réutilisées par l'ensemble de l'application.
---
## Motivations
À terme, Labfy Investigation manipulera de nombreux objets métier :
- investigations ;
- sources ;
- recherches ;
- preuves ;
- entités ;
- relations ;
- chronologie ;
- journal ;
- hypothèses ;
- catégories ;
- tags.
Sans une couche d'abstraction, chaque module devrait manipuler directement
SQLite, ce qui entraînerait :
- une forte duplication de code ;
- une gestion incohérente des erreurs ;
- des transactions difficiles à maintenir ;
- une architecture difficile à faire évoluer.
L'objectif de ce ticket est d'éviter cette dette technique.
---
## Architecture cible
La couche Database devra évoluer vers l'organisation suivante :
```text
include/database/
├── database.h
├── connection.h
├── transaction.h
├── statement.h
├── error.h
├── schema.h
```
```text
src/database/
├── database.c
├── connection.c
├── transaction.c
├── statement.c
├── error.c
├── schema.c
```
Les futurs modules métier seront ajoutés ultérieurement.
---
## Structure Database
Créer un type opaque :
```c
typedef struct Database Database;
```
L'implémentation reste privée.
La structure interne contiendra au minimum :
- le pointeur `sqlite3 *`;
- le chemin de la base ;
- l'état de la transaction ;
- la version du schéma.
Cette abstraction permettra de faire évoluer facilement l'implémentation sans
modifier l'API publique.
---
## Connexion
Créer les fonctions :
```c
Database *database_open(
const char *database_path
);
void database_close(
Database *database
);
```
Ces fonctions sont responsables de :
- l'ouverture de SQLite ;
- l'activation des PRAGMA nécessaires ;
- la fermeture propre de la base.
---
## Transactions
Créer les fonctions :
```c
bool database_begin(
Database *database
);
bool database_commit(
Database *database
);
bool database_rollback(
Database *database
);
```
Toute gestion des transactions devra passer exclusivement par ces fonctions.
---
## Gestion des requêtes préparées
Créer une couche d'abstraction pour les instructions préparées.
L'objectif est de centraliser :
- `sqlite3_prepare_v2()`
- `sqlite3_bind_*()`
- `sqlite3_step()`
- `sqlite3_reset()`
- `sqlite3_finalize()`
Aucun futur module métier ne devra appeler directement ces fonctions SQLite.
---
## Gestion des erreurs
Créer un point d'entrée unique pour les erreurs SQLite.
Toutes les erreurs devront passer par une fonction dédiée.
Cette centralisation facilitera :
- le débogage ;
- la journalisation ;
- l'intégration future avec le journal d'audit.
---
## Responsabilités
À la fin du ticket :
- la couche Database est la seule autorisée à manipuler SQLite ;
- les autres modules ne connaissent pas `sqlite3`;
- les transactions sont centralisées ;
- les erreurs sont uniformisées ;
- les requêtes préparées sont encapsulées.
---
## Hors périmètre
Ce ticket ne doit pas :
- ajouter de nouvelles tables ;
- modifier le schéma SQL ;
- créer un CRUD métier ;
- modifier GTK ;
- implémenter des migrations ;
- ajouter des fonctionnalités d'investigation.
---
## Critères d'acceptation
- [ ] Création du type opaque `Database`.
- [ ] API publique de connexion disponible.
- [ ] API publique des transactions disponible.
- [ ] Encapsulation des requêtes préparées.
- [ ] Gestion centralisée des erreurs.
- [ ] Aucune utilisation directe de `sqlite3` en dehors du dossier `database`.
- [ ] Documentation des nouvelles API.
- [ ] Tous les tests existants restent valides.
---
## Commit attendu
```text
refactor(database): introduce database infrastructure layer
```

View file

@ -1,366 +0,0 @@
# Ticket #026 — Migrer linitialisation vers la couche Database
## Contexte
Le ticket #025 a introduit linfrastructure principale de la couche Database :
- contexte opaque `Database` ;
- ouverture et fermeture des connexions ;
- requêtes préparées avec `DatabaseStatement` ;
- bindings et lecture typée ;
- gestion des transactions ;
- infrastructure derreurs.
Cependant, `database_initialize()` utilise encore directement plusieurs fonctions de lAPI SQLite :
```c
sqlite3_open_v2()
sqlite3_prepare_v2()
sqlite3_bind_text()
sqlite3_step()
sqlite3_reset()
sqlite3_clear_bindings()
sqlite3_finalize()
sqlite3_close()
La fonction exécute également directement les commandes SQL suivantes :
```sql
BEGIN IMMEDIATE;
COMMIT;
ROLLBACK;
```
De son côté, `schema_install_v1()` reçoit toujours directement un `sqlite3 *`, ce qui contourne labstraction `Database`.
## Objectif
Réécrire linitialisation dune nouvelle enquête afin quelle utilise linfrastructure créée au ticket #025.
Linitialisation doit rester atomique :
- soit la base est entièrement initialisée ;
- soit aucune donnée partielle nest conservée.
## Travail à réaliser
### Adapter linstallation du schéma
Modifier la signature actuelle :
```C
bool schema_install_v1(
sqlite3 *database
);
```
afin quelle reçoive un contexte Database :
```C
bool schema_install_v1(
Database *database
);
```
Linstallation du fichier SQL complet pourra continuer à utiliser `sqlite3_exec()` en interne, car le schéma contient plusieurs instructions SQL.
Laccès au handle SQLite devra passer par lAPI interne :
```C
database_get_handle()
```
La fonction ne devra réaliser ni `COMMIT` ni `ROLLBACK`.
### Migrer linsertion des métadonnées
Réécrire les fonctions responsables de linsertion dans la table `metadata` avec `DatabaseStatement`.
Les opérations devront utiliser lAPI suivante :
```C
database_statement_prepare()
database_statement_bind_text()
database_statement_step()
database_statement_reset()
database_statement_clear_bindings()
database_statement_finalize()
```
Les métadonnées obligatoires restent :
```
schema_version
application
created_at
investigation_uuid
```
Une seule requête préparée devra pouvoir être réutilisée pour insérer les quatre métadonnées.
### Migrer linsertion de lenquête
Réécrire linsertion dans la table `investigation` avec `DatabaseStatement`.
Les champs insérés restent :
```C
database_open()
database_transaction_begin()
schema_install_v1()
database_transaction_commit()
database_transaction_rollback()
database_close()
```
Le déroulement attendu est :
```
validation des paramètres
création du timestamp UTC
création de lUUID
ouverture de Database
début de transaction
installation du schéma V1
insertion des métadonnées
insertion de lenquête
commit
fermeture de Database
```
Tout échec après le début de la transaction doit provoquer un rollback.
### Utiliser linfrastructure derreurs
Les erreurs rencontrées pendant :
- linstallation du schéma ;
- la préparation dune requête ;
- le binding dun paramètre ;
- lexécution dune requête ;
- le début dune transaction ;
- le commit ;
- le rollback ;
doivent être enregistrées dans le contexte Database lorsque celui-ci est disponible.
Linfrastructure interne existante pourra être utilisée :
```C
database_set_error()
database_clear_error_internal()
```
```C
DATABASE_ERROR_SQLITE
```
```C
DATABASE_ERROR_INVALID_ARGUMENT
```
```C
DATABASE_ERROR_INVALID_STATE
```
### Nettoyer lancien code SQLite
Supprimer de la logique dinitialisation les appels directs à :
```C
sqlite3_open_v2()
sqlite3_prepare_v2()
sqlite3_bind_text()
sqlite3_step()
sqlite3_reset()
sqlite3_clear_bindings()
sqlite3_finalize()
sqlite3_close()
```
Supprimer également les commandes manuelles :
```SQL
BEGIN IMMEDIATE;
COMMIT;
ROLLBACK;
```
La fonction utilitaire `database_execute_sql()` pourra être conservée uniquement si elle reste nécessaire à `database_open()` pour lactivation des clés étrangères.
### Mettre à jour la documentation publique
Dans `include/database/database.h`, supprimer la mention temporaire :
```
Cette fonction conserve temporairement son rôle actuel pendant le
refactoring du ticket #025.
```
La documentation de `database_initialize()` devra préciser que :
- linitialisation est transactionnelle ;
- un échec provoque un rollback ;
- la base est fermée avant le retour de la fonction.
## Tests à ajouter ou adapter
### Initialisation réussie
Vérifier quune initialisation valide crée :
- le schéma V1 ;
- les tables attendues ;
- les métadonnées obligatoires ;
- une seule ligne dans investigation ;
- le bon nom denquête ;
- le bon chemin racine ;
- un UUID non vide ;
- une date de création non vide ;
- une date de modification non vide.
### Paramètres invalides
Vérifier le refus des cas suivants :
```
database_path == NULL
database_path vide
investigation_name == NULL
investigation_name vide
investigation_root_path == NULL
investigation_root_path vide
```
### Rollback
Provoquer un échec après le démarrage de la transaction.
Une seconde tentative dinitialisation sur une base déjà initialisée pourra être utilisée pour provoquer une erreur de contrainte ou de création de schéma.
Après léchec, vérifier que :
- aucune donnée partielle supplémentaire nest conservée ;
- aucune seconde enquête nest ajoutée ;
- les métadonnées existantes ne sont pas dupliquées ;
- la transaction est annulée ;
- la base reste lisible ;
- la connexion peut être fermée proprement.
### Régression
Tous les tests existants doivent rester valides :
```
InvestigationNode
InvestigationTreeModel
InvestigationTreeBuilder
InvestigationProject
Database
DatabaseStatement
DatabaseTransaction
DatabaseError
```
## Critères dacceptation
- [ ] `database_initialize()` nappelle plus directement `sqlite3_open_v2()`.
- [ ] `database_initialize()` utilise `database_open()`.
- [ ] `database_initialize()` utilise `database_close()`.
- [ ] Les transactions utilisent le module `transaction`.
- [ ] Les insertions utilisent `DatabaseStatement`.
- [ ] `InvestigationNode` Investigation `schema_install_v1()` reçoit un `Database *`.
InvestigationNode
Investigation `schema_install_v1()` reçoit un `Database *`.
- [ ] `schema_install_v1()` récupère le handle avec lAPI interne.
- [ ] Aucun `sqlite3_stmt *` nest manipulé dans le code dinitialisation.
- [ ] Aucun `BEGIN`, `COMMIT` ou `ROLLBACK` manuel ne reste dans `database.c`.
- [ ] Tout échec après le début de la transaction provoque un rollback.
- [ ] Les erreurs sont enregistrées dans la couche Database.
- [ ] Les tests de réussite sont valides.
- [ ] Les tests de paramètres invalides sont valides.
- [ ] Les tests de rollback sont valides.
- [ ] Les tests de régression sont valides.
- [ ] `make` réussit sans erreur.
- [ ] `make test` réussit.
- [ ] `git diff --check` ne retourne aucune erreur.
## Hors périmètre
Ce ticket ne doit pas ajouter :
- de CRUD métier ;
- de dépôt ou repository métier ;
- de migration entre plusieurs versions de schéma ;
- de système GResource pour embarquer `schema_v1.sql` ;
- de création automatique des dossiers parents ;
- de modification du schéma SQL V1 ;
- de nouvelles tables ;
- de logique dinterface graphique ;
- de nouvelles fonctionnalités utilisateur.
## Fichiers principalement concernés
```text
include/database/database.h
include/database/schema.h
src/database/database.c
src/database/database_internal.h
src/database/schema.c
tests/test_database.c
Makefile
```
## Résultat attendu
À la fin du ticket, `database_initialize()` doit être un utilisateur normal de la couche Database.
La fonction ne doit plus contourner cette couche avec des appels SQLite directs.
Linitialisation dune enquête doit être entièrement transactionnelle, testée et cohérente avec larchitecture mise en place au ticket #025.
## Commit attendu
Une fois tous les critères dacceptation validés, le ticket pourra être enregistré avec le commit suivant :
```text
refactor(database): migrate initialization to database layer
```
Le commit doit principalement contenir :
```
include/database/database.h
include/database/schema.h
src/database/database.c
src/database/database_internal.h
src/database/schema.c
tests/test_database.c
Makefile
```
Avant le commit, exécuter :
```bash
make clean
make
make test
git diff --check
git status --short
```

View file

@ -1,535 +0,0 @@
# Ticket #027 — Ajouter le modèle de lecture et le DAO de lenquête
## Contexte
Les tickets précédents ont permis de mettre en place :
- le schéma SQLite V1 ;
- linfrastructure `Database` ;
- les requêtes préparées `DatabaseStatement` ;
- les transactions ;
- la gestion centralisée des erreurs ;
- linitialisation transactionnelle dune nouvelle enquête.
La table SQLite :
```text
investigation
```
contient les informations principales de lenquête courante :
```text
id
name
root_path
created_at
updated_at
```
Le projet possède déjà un type :
```C
Investigation
```
dans le module `core`.
Cet objet représente actuellement le contexte de travail sur le système de
fichiers :
- chemin racine de lenquête ;
- chemin du fichier SQLite.
Il ne doit pas être transformé directement en représentation dune ligne SQL.
Larchitecture du projet prévoit une séparation entre :
```text
Core
Models
DAO
Database
SQLite
```
Le DAO doit être responsable des requêtes SQL métier.
## Objectif
Créer un modèle de données en lecture seule représentant la ligne de la table
investigation, ainsi quun DAO permettant de charger cette ligne depuis une
connexion Database.
Le ticket doit permettre de lire les informations persistées dune enquête
sans utiliser directement lAPI SQLite hors de la couche Database.
## Architecture attendue
```
InvestigationRecord
│ construit par
InvestigationDao
DatabaseStatement
Database
SQLite
```
## Travail à réaliser
### Créer le modèle InvestigationRecord
Créer les fichiers :
```text
include/models/investigation_record.h
src/models/investigation_record.c
```
Le modèle doit être opaque :
```C
typedef struct InvestigationRecord InvestigationRecord;
```
Sa représentation privée doit contenir :
```C
struct InvestigationRecord
{
char *id;
char *name;
char *root_path;
char *created_at;
char *updated_at;
};
```
Le modèle ne doit dépendre ni de SQLite, ni de GTK.
### Ajouter le constructeur
Créer une fonction :
```C
InvestigationRecord *investigation_record_new(
const char *id,
const char *name,
const char *root_path,
const char *created_at,
const char *updated_at
);
```
Le constructeur doit :
- refuser les pointeurs `NULL` ;
- refuser les chaînes vides ;
- allouer une copie de chaque chaîne ;
- nettoyer correctement toutes les allocations en cas déchec ;
- retourner `NULL` si les données sont invalides.
### Ajouter la fonction de libération
Créer :
```C
void investigation_record_free(
InvestigationRecord *record
);
```
Cette fonction doit :
- accepter `NULL` ;
- libérer toutes les chaînes ;
- libérer la structure.
### Ajouter les accesseurs
Créer les accesseurs en lecture seule :
```C
const char *investigation_record_get_id(
const InvestigationRecord *record
);
const char *investigation_record_get_name(
const InvestigationRecord *record
);
const char *investigation_record_get_root_path(
const InvestigationRecord *record
);
const char *investigation_record_get_created_at(
const InvestigationRecord *record
);
const char *investigation_record_get_updated_at(
const InvestigationRecord *record
);
```
Les pointeurs retournés appartiennent au modèle et ne doivent pas être libérés.
Les accesseurs doivent retourner `NULL` si le modèle reçu est `NULL`.
### Créer le DAO de lenquête
Créer les fichiers :
```text
include/dao/investigation_dao.h
src/dao/investigation_dao.c
```
Déclarer la fonction :
```C
InvestigationRecord *investigation_dao_load(
Database *database
);
```
Cette fonction doit charger lunique ligne de la table :
```text
investigation
```
La requête doit lire :
```SQL
SELECT
id,
name,
root_path,
created_at,
updated_at
FROM investigation;
```
### Utiliser exclusivement DatabaseStatement
Le DAO doit utiliser :
```C
database_statement_prepare()
database_statement_step()
database_statement_column_text()
database_statement_finalize()
```
Le DAO ne doit pas utiliser directement :
```C
sqlite3_prepare_v2()
sqlite3_step()
sqlite3_column_text()
sqlite3_finalize()
```
Aucun type SQLite ne doit apparaître dans linterface publique du DAO.
### Vérifier le nombre de lignes
La base dune enquête doit contenir exactement une ligne dans la table
`investigation`.
Le DAO doit gérer les cas suivants :
Une ligne
```
ROW
DONE
```
Résultat :
```
succès
```
Le DAO construit et retourne un `InvestigationRecord`.
#### Aucune ligne
`DONE` dès le premier step
Résultat :
```
échec
```
Le DAO retourne `NULL` et enregistre :
```
DATABASE_ERROR_INVALID_STATE
```
#### Plusieurs lignes
```
ROW
ROW
```
Résultat :
`échec`
Le DAO libère le modèle temporaire, retourne `NULL` et enregistre :
```C
DATABASE_ERROR_INVALID_STATE
```
### Propager les erreurs
Les erreurs de préparation ou dexécution provenant de `DatabaseStatement`
doivent rester disponibles dans le contexte `Database`.
Le DAO doit enregistrer une erreur explicite pour :
- une table vide ;
- plusieurs lignes dans investigation ;
- une colonne obligatoire invalide ;
- une allocation impossible ;
- un état incohérent.
Les codes prévus sont :
```C
DATABASE_ERROR_INVALID_ARGUMENT
DATABASE_ERROR_INVALID_STATE
DATABASE_ERROR_MEMORY
DATABASE_ERROR_SQLITE
```
Une lecture réussie doit laisser :
```C
DATABASE_ERROR_NONE
```
### Gérer proprement la mémoire
Le DAO doit libérer toutes les chaînes temporaires retournées par :
```C
database_statement_column_text()
```
après la construction du modèle.
Toutes les sorties déchec doivent :
- finaliser la requête préparée ;
- libérer les chaînes déjà lues ;
- libérer le modèle partiellement construit ;
- ne provoquer aucune fuite mémoire.
- Tests à ajouter
#### Test du modèle
Créer :
```
tests/test_investigation_record.c
```
Vérifier :
- la création avec des données valides ;
- la copie des chaînes ;
- les cinq accesseurs ;
- le refus des paramètres `NULL` ;
- le refus des chaînes vides ;
- `investigation_record_free(NULL)`.
#### Test du DAO
Créer :
```
tests/test_investigation_dao.c
Chargement valide
```
Créer une base temporaire avec :
```C
database_initialize()
```
Puis :
- ouvrir la base avec `database_open()` ;
- charger lenquête avec `investigation_dao_load()` ;
- vérifier que le modèle nest pas `NULL` ;
- vérifier lUUID ;
- vérifier le nom ;
- vérifier le chemin racine ;
- vérifier `created_at` ;
- vérifier `updated_at` ;
- vérifier que lerreur Database vaut `DATABASE_ERROR_NONE`.
#### Paramètre invalide
Vérifier :
```C
investigation_dao_load(NULL) == NULL
```
#### Table vide
Créer une base contenant la table investigation sans ligne.
Vérifier :
```
résultat == NULL
DATABASE_ERROR_INVALID_STATE
message non vide
```
#### Plusieurs lignes
Créer une base contenant deux lignes dans la table investigation.
Vérifier :
```
résultat == NULL
DATABASE_ERROR_INVALID_STATE
message non vide
```
#### Données invalides
Créer une situation dans laquelle une colonne obligatoire ne peut pas être
correctement lue.
Vérifier :
```
résultat == NULL
erreur enregistrée
aucune fuite de mémoire
```
## Makefile
Ajouter les exécutables :
```
tests/test_investigation_record
tests/test_investigation_dao
```
Ajouter leurs règles de compilation.
Les tests du DAO devront notamment compiler avec :
```
src/models/investigation_record.c
src/dao/investigation_dao.c
src/database/database.c
src/database/schema.c
src/database/statement.c
src/database/transaction.c
src/database/error.c
```
Ajouter les nouveaux tests aux cibles :
```
test
clean
```
### Critères dacceptation
- [ ] Le type `InvestigationRecord` est opaque.
- [ ] Le modèle ne dépend ni de SQLite ni de GTK.
- [ ] Le constructeur valide tous les champs obligatoires.
- [ ] Toutes les chaînes sont copiées.
- [ ] Une fonction de libération complète est disponible.
- [ ] Les cinq accesseurs en lecture seule sont disponibles.
- [ ] `investigation_dao_load()` charge lenquête courante.
- [ ] Le DAO utilise uniquement lAPI `DatabaseStatement`.
- [ ] Aucun appel direct à SQLite napparaît dans le DAO.
- [ ] Une base contenant exactement une enquête est acceptée.
- [ ] Une table vide est refusée.
- [ ] Plusieurs lignes sont refusées.
- [ ] Les erreurs sont enregistrées dans `Database`.
- [ ] Toutes les ressources temporaires sont libérées.
- [ ] Les tests du modèle sont valides.
- [ ] Les tests du DAO sont valides.
- [ ] Tous les anciens tests restent valides.
- [ ] `make` réussit sans erreur.
- [ ] `make test` réussit.
- [ ] `git diff --check` ne retourne aucune erreur.
### Hors périmètre
Ce ticket ne doit pas ajouter :
- la création dune enquête par le DAO ;
- la modification du nom de lenquête ;
- la modification du chemin racine ;
- la suppression dune enquête ;
- un CRUD complet ;
- louverture automatique dans linterface GTK ;
- laffichage des informations dans la fenêtre ;
- les DAO des preuves, sources ou entités ;
- les migrations de schéma ;
- une nouvelle version du schéma SQLite.
### Fichiers principalement concernés
```
include/models/investigation_record.h
include/dao/investigation_dao.h
src/models/investigation_record.c
src/dao/investigation_dao.c
tests/test_investigation_record.c
tests/test_investigation_dao.c
Makefile
```
## Résultat attendu
À la fin du ticket, le projet doit être capable de charger les informations
persistées de lenquête courante dans un modèle C indépendant de SQLite.
Le DAO devient le premier point daccès métier structuré à la base de données.
### Commit attendu
Une fois tous les critères validés :
`feat(dao): add investigation record loader`
Avant le commit :
```bash
make clean
make
make test
git diff --check
git status --short
git add .
git diff --cached --stat
git diff --cached
git commit -m "feat(dao): add investigation record loader"
git push
```

View file

@ -1,776 +0,0 @@
# Ticket #028 — Ajouter louverture dune enquête existante
## Contexte
Les tickets précédents ont permis de mettre en place :
- la création transactionnelle dune base denquête ;
- la couche `Database` ;
- les requêtes préparées `DatabaseStatement` ;
- la gestion des transactions et des erreurs ;
- le modèle `InvestigationRecord` ;
- le DAO `InvestigationDao` permettant de charger lunique ligne de la table `investigation`.
Le projet possède également un type `InvestigationProject` chargé de représenter les chemins du projet sur le système de fichiers, notamment :
- le dossier racine de lenquête ;
- le chemin du fichier `Enquete.sqlite`.
Cependant, lapplication ne possède pas encore dobjet représentant une enquête réellement ouverte pendant son exécution.
Les différentes ressources sont encore séparées :
```text
InvestigationProject
Database
InvestigationRecord
```
Il faut désormais les regrouper dans un contexte cohérent dont la durée de vie correspond à celle dune enquête ouverte.
## Objectif
Créer un type opaque `InvestigationSession` chargé douvrir une enquête existante et de conserver :
- son contexte de fichiers `InvestigationProject` ;
- sa connexion `Database` ;
- ses informations persistées `InvestigationRecord`.
Louverture doit vérifier que le dossier sélectionné correspond bien aux informations enregistrées dans la base SQLite.
La connexion SQLite doit rester ouverte pendant toute la durée de vie de la session afin de permettre les futurs appels aux DAO.
## Architecture attendue
```text
Application
InvestigationSession
├── InvestigationProject
├── Database
└── InvestigationRecord
InvestigationDao
DatabaseStatement
SQLite
```
## Travail à réaliser
### 1. Créer le type `InvestigationSession`
Créer les fichiers :
```text
include/core/investigation_session.h
src/core/investigation_session.c
```
Le type doit être opaque :
```c
typedef struct InvestigationSession InvestigationSession;
```
Sa représentation privée doit contenir au minimum :
```c
struct InvestigationSession
{
InvestigationProject *project;
Database *database;
InvestigationRecord *record;
};
```
Le header public ne doit pas exposer cette structure.
### 2. Définir les erreurs douverture
Créer une énumération dédiée :
```c
typedef enum
{
INVESTIGATION_SESSION_ERROR_INVALID_ARGUMENT,
INVESTIGATION_SESSION_ERROR_ROOT_NOT_FOUND,
INVESTIGATION_SESSION_ERROR_DATABASE_NOT_FOUND,
INVESTIGATION_SESSION_ERROR_PROJECT,
INVESTIGATION_SESSION_ERROR_DATABASE,
INVESTIGATION_SESSION_ERROR_RECORD,
INVESTIGATION_SESSION_ERROR_ROOT_MISMATCH,
INVESTIGATION_SESSION_ERROR_MEMORY
} InvestigationSessionError;
```
Définir un domaine derreur GLib :
```c
#define INVESTIGATION_SESSION_ERROR \
investigation_session_error_quark()
GQuark investigation_session_error_quark(void);
```
Les erreurs doivent être transmises avec un paramètre :
```c
GError **error
```
Lorsquune erreur provenant de `Database` doit être propagée, son message doit être copié dans le `GError` avant la fermeture de la connexion.
### 3. Ajouter la fonction douverture
Déclarer :
```c
InvestigationSession *investigation_session_open(
const char *investigation_root_path,
GError **error
);
```
La fonction doit ouvrir une enquête déjà existante à partir de son dossier racine.
Elle ne doit pas créer une nouvelle enquête.
### 4. Valider les paramètres
La fonction doit refuser :
```text
investigation_root_path == NULL
investigation_root_path vide
```
Le code derreur attendu est :
```c
INVESTIGATION_SESSION_ERROR_INVALID_ARGUMENT
```
Le paramètre `error` peut être `NULL`.
Si `error` nest pas `NULL`, il doit respecter les conventions GLib :
```c
*error == NULL
```
au moment de lappel.
### 5. Vérifier le dossier racine
Le chemin fourni doit correspondre à un dossier existant.
La fonction doit vérifier :
```c
G_FILE_TEST_IS_DIR
```
Un chemin inexistant ou qui ne représente pas un dossier doit produire :
```c
INVESTIGATION_SESSION_ERROR_ROOT_NOT_FOUND
```
Le chemin doit être normalisé avec :
```c
g_canonicalize_filename()
```
La session doit travailler à partir de ce chemin canonique.
### 6. Construire `InvestigationProject`
Réutiliser lAPI existante de `InvestigationProject`.
La logique de construction du chemin de la base ne doit pas être dupliquée dans `InvestigationSession`.
Le chemin attendu reste géré par `InvestigationProject` :
```text
<racine>/00_BaseDeDonnees/Enquete.sqlite
```
Si lAPI actuelle de `InvestigationProject` ne permet pas de représenter un projet existant, elle peut être étendue de manière minimale.
Aucune logique SQLite ne doit être ajoutée dans `InvestigationProject`.
### 7. Vérifier le fichier SQLite
Avant louverture de la connexion, vérifier que le chemin retourné par `InvestigationProject` correspond à un fichier régulier :
```c
G_FILE_TEST_IS_REGULAR
```
Si le fichier nexiste pas, retourner :
```c
INVESTIGATION_SESSION_ERROR_DATABASE_NOT_FOUND
```
Louverture dune enquête existante ne doit jamais créer silencieusement une nouvelle base vide.
### 8. Ouvrir la connexion Database
Utiliser :
```c
database_open()
```
La fonction `investigation_session_open()` ne doit pas appeler directement :
```c
sqlite3_open()
sqlite3_open_v2()
sqlite3_close()
```
Si `database_open()` échoue, retourner :
```c
INVESTIGATION_SESSION_ERROR_DATABASE
```
La connexion doit rester ouverte si la session est créée avec succès.
### 9. Charger lenquête persistée
Utiliser :
```c
investigation_dao_load()
```
Le DAO doit retourner un `InvestigationRecord`.
Si le chargement échoue :
- récupérer le code et le message de la dernière erreur `Database` ;
- copier le message dans un `GError` ;
- retourner `NULL` ;
- fermer proprement la connexion ;
- libérer le projet ;
- ne laisser aucune ressource allouée.
Le code derreur de session attendu est :
```c
INVESTIGATION_SESSION_ERROR_RECORD
```
### 10. Vérifier la cohérence du chemin racine
Le chemin sélectionné doit correspondre au champ persistant :
```text
investigation.root_path
```
Comparer les versions canoniques de :
```text
chemin racine sélectionné
chemin racine enregistré dans InvestigationRecord
```
La comparaison doit être faite après normalisation avec :
```c
g_canonicalize_filename()
```
Si les chemins ne correspondent pas, louverture doit échouer avec :
```c
INVESTIGATION_SESSION_ERROR_ROOT_MISMATCH
```
Cette vérification évite douvrir une base copiée ou déplacée sans détecter lincohérence.
Le déplacement volontaire dune enquête sera traité dans un ticket distinct.
### 11. Construire la session
La session ne doit être créée quaprès validation complète :
```text
dossier racine valide
InvestigationProject valide
fichier SQLite présent
Database ouverte
InvestigationRecord chargé
chemin racine cohérent
InvestigationSession créée
```
En cas déchec dallocation, retourner :
```c
INVESTIGATION_SESSION_ERROR_MEMORY
```
### 12. Ajouter la fonction de fermeture
Déclarer :
```c
void investigation_session_close(
InvestigationSession *session
);
```
Cette fonction doit accepter `NULL`.
Elle doit libérer toutes les ressources possédées par la session :
```text
InvestigationRecord
Database
InvestigationProject
InvestigationSession
```
La session devient propriétaire de ces trois objets dès que son ouverture réussit.
### 13. Ajouter les accesseurs
Ajouter :
```c
const InvestigationProject *investigation_session_get_project(
const InvestigationSession *session
);
const InvestigationRecord *investigation_session_get_record(
const InvestigationSession *session
);
Database *investigation_session_get_database(
InvestigationSession *session
);
```
Les pointeurs retournés appartiennent à la session et ne doivent pas être libérés par lappelant.
Les accesseurs doivent retourner `NULL` si la session reçue est `NULL`.
`Database` reste non constante car les futurs DAO auront besoin dune connexion modifiable.
### 14. Interdire les dépendances SQLite et GTK
Le module `InvestigationSession` ne doit pas inclure :
```c
#include <sqlite3.h>
#include <gtk/gtk.h>
```
Il doit exclusivement utiliser les abstractions existantes :
```text
InvestigationProject
Database
InvestigationDao
InvestigationRecord
GLib
```
## Tests à ajouter
Créer :
```text
tests/test_investigation_session.c
```
### Test douverture valide
Créer un dossier temporaire.
Initialiser une base avec :
```c
database_initialize()
```
Ouvrir ensuite lenquête avec :
```c
investigation_session_open()
```
Vérifier :
- la session nest pas `NULL` ;
- aucune erreur nest produite ;
- le projet est disponible ;
- la connexion Database est disponible ;
- le record est disponible ;
- le nom de lenquête est correct ;
- le chemin racine est correct ;
- lUUID est valide ;
- `created_at` nest pas vide ;
- `updated_at` nest pas vide ;
- la connexion peut encore être utilisée par un DAO ;
- la fermeture libère correctement les ressources.
### Test des paramètres invalides
Vérifier :
```c
investigation_session_open(NULL, &error) == NULL
investigation_session_open("", &error) == NULL
```
Le code attendu est :
```c
INVESTIGATION_SESSION_ERROR_INVALID_ARGUMENT
```
Vérifier également le comportement avec :
```c
error == NULL
```
### Test dun dossier inexistant
Utiliser un chemin inexistant.
Vérifier :
```text
résultat == NULL
erreur == INVESTIGATION_SESSION_ERROR_ROOT_NOT_FOUND
message non vide
```
### Test dun chemin qui nest pas un dossier
Créer un fichier temporaire et utiliser son chemin comme racine.
Vérifier :
```text
résultat == NULL
erreur == INVESTIGATION_SESSION_ERROR_ROOT_NOT_FOUND
```
### Test dune base absente
Créer une structure de projet valide sans fichier :
```text
00_BaseDeDonnees/Enquete.sqlite
```
Vérifier :
```text
résultat == NULL
erreur == INVESTIGATION_SESSION_ERROR_DATABASE_NOT_FOUND
```
La fonction ne doit pas créer de nouveau fichier SQLite.
### Test dune base invalide
Créer un fichier SQLite vide ou une base ne contenant pas la table :
```text
investigation
```
Vérifier :
```text
résultat == NULL
erreur == INVESTIGATION_SESSION_ERROR_RECORD
message non vide
```
### Test dun chemin racine incohérent
Créer une base avec un chemin racine enregistré différent du dossier utilisé pour louverture.
Vérifier :
```text
résultat == NULL
erreur == INVESTIGATION_SESSION_ERROR_ROOT_MISMATCH
message non vide
```
### Test des accesseurs avec NULL
Vérifier :
```c
investigation_session_get_project(NULL) == NULL
investigation_session_get_record(NULL) == NULL
investigation_session_get_database(NULL) == NULL
```
Vérifier également :
```c
investigation_session_close(NULL);
```
### Test de réutilisation du DAO
Après louverture valide dune session, appeler de nouveau :
```c
investigation_dao_load(
investigation_session_get_database(session)
);
```
Vérifier que la connexion reste fonctionnelle pendant toute la durée de vie de la session.
## Gestion de la mémoire
Toutes les sorties déchec de `investigation_session_open()` doivent libérer les ressources déjà créées.
Le nettoyage doit couvrir les cas suivants :
```text
échec avant création du projet
échec après création du projet
échec après ouverture de Database
échec après chargement du record
échec lors de la comparaison des chemins
échec lors de lallocation de la session
```
Aucun objet ne doit être libéré deux fois.
Aucune ressource temporaire ne doit rester allouée :
```text
chemins canoniques
messages copiés
GError temporaires
InvestigationProject
Database
InvestigationRecord
```
## Makefile
Ajouter :
```make
TEST_INVESTIGATION_SESSION := tests/test_investigation_session
```
Ajouter une règle compilant au minimum :
```text
tests/test_investigation_session.c
src/core/investigation_session.c
src/core/investigation_project.c
src/dao/investigation_dao.c
src/models/investigation_record.c
src/database/database.c
src/database/schema.c
src/database/statement.c
src/database/transaction.c
src/database/error.c
```
Lier avec :
```text
GLib
SQLite
```
Ajouter le test aux cibles :
```text
test
clean
```
La nouvelle sortie attendue est :
```text
InvestigationSession : tous les tests sont valides.
```
## Critères dacceptation
- [ ] Le type `InvestigationSession` est opaque.
- [ ] Une session possède un `InvestigationProject`.
- [ ] Une session possède une connexion `Database`.
- [ ] Une session possède un `InvestigationRecord`.
- [ ] Le dossier racine est validé avant louverture.
- [ ] Le chemin racine est normalisé.
- [ ] Le chemin de la base provient de `InvestigationProject`.
- [ ] Le fichier SQLite doit exister avant lappel à `database_open()`.
- [ ] Louverture ne crée jamais silencieusement une nouvelle base.
- [ ] Le record est chargé avec `InvestigationDao`.
- [ ] Le chemin enregistré est comparé au chemin sélectionné.
- [ ] Une incohérence de chemin empêche louverture.
- [ ] La connexion reste ouverte pendant la durée de vie de la session.
- [ ] La fermeture libère toutes les ressources.
- [ ] Les accesseurs acceptent une session `NULL`.
- [ ] Les erreurs sont propagées avec `GError`.
- [ ] Aucun type SQLite napparaît dans lAPI de la session.
- [ ] Aucune dépendance GTK nest ajoutée.
- [ ] Les tests douverture valide sont présents.
- [ ] Les tests de paramètres invalides sont présents.
- [ ] Les tests de dossier absent sont présents.
- [ ] Les tests de base absente sont présents.
- [ ] Les tests de base invalide sont présents.
- [ ] Les tests de chemin incohérent sont présents.
- [ ] Les anciens tests restent valides.
- [ ] `make` réussit sans erreur.
- [ ] `make test` réussit.
- [ ] `git diff --check` ne retourne aucune erreur.
## Audit attendu
Les commandes suivantes ne doivent rien afficher :
```bash
rg -n 'sqlite3_|#include <sqlite3.h>|#include <gtk' \
include/core/investigation_session.h \
src/core/investigation_session.c
```
Vérifier également que la logique du chemin SQLite nest pas dupliquée :
```bash
rg -n '00_BaseDeDonnees|Enquete.sqlite' \
src/core/investigation_session.c
```
Le résultat attendu est aucune occurrence, sauf éventuellement dans un commentaire de documentation justifié.
## Hors périmètre
Ce ticket ne doit pas ajouter :
- la création dune nouvelle enquête ;
- le déplacement dune enquête ;
- la réparation automatique dun chemin racine incohérent ;
- la modification de `investigation.root_path` ;
- lintégration dans la fenêtre GTK ;
- une boîte de dialogue de sélection ;
- laffichage du nom de lenquête ;
- les DAO des preuves, sources ou entités ;
- la fermeture demandée par linterface utilisateur ;
- la sauvegarde automatique ;
- une migration de schéma ;
- un verrouillage multi-instance de la base.
## Fichiers principalement concernés
```text
include/core/investigation_session.h
src/core/investigation_session.c
tests/test_investigation_session.c
Makefile
```
Une adaptation limitée de ces fichiers est autorisée si nécessaire :
```text
include/core/investigation_project.h
src/core/investigation_project.c
```
## Résultat attendu
À la fin du ticket, le programme doit pouvoir ouvrir une enquête existante à partir de son dossier racine.
Une session valide doit conserver ensemble :
```text
le projet de fichiers
la connexion SQLite
les informations persistées de lenquête
```
Cette session deviendra le contexte principal utilisé ultérieurement par linterface et les futurs DAO.
## Commit attendu
Une fois tous les critères dacceptation validés :
```text
feat(core): add investigation session loader
```
Avant le commit :
```bash
make clean
make
make test
git diff --check
git status --short
```
Préparer les fichiers :
```bash
git add \
Makefile \
include/core/investigation_session.h \
src/core/investigation_session.c \
tests/test_investigation_session.c
```
Ajouter également les fichiers `InvestigationProject` uniquement sils ont réellement été modifiés :
```bash
git add \
include/core/investigation_project.h \
src/core/investigation_project.c
```
Contrôler le contenu préparé :
```bash
git diff --cached --stat
git diff --cached
```
Créer le commit :
```bash
git commit -m "feat(core): add investigation session loader"
```
Le push ne doit être effectué quaprès validation complète de la compilation, des tests et du contenu du commit.

View file

@ -1,933 +0,0 @@
# Ticket #029 — Intégrer `InvestigationSession` au cycle de vie GTK
## Contexte
Le ticket #028 a ajouté `InvestigationSession`, qui permet douvrir une enquête existante de manière contrôlée.
Une session valide possède désormais :
- un `InvestigationProject` ;
- une connexion `Database` ouverte ;
- un `InvestigationRecord` chargé depuis SQLite.
Lapplication GTK utilise encore lancien objet `Investigation` pour représenter le dossier sélectionné.
Son fonctionnement actuel est approximativement le suivant :
```text
FolderDialog
Investigation
InvestigationTreeBuilder
InvestigationTreeModel
MainWindow
```
Cette organisation ne conserve pas la connexion SQLite et nutilise pas les informations persistées dans la table `investigation`.
Il faut désormais remplacer lancien objet détenu par `Application` par une véritable `InvestigationSession`.
## Objectif
Faire de `InvestigationSession` le contexte actif de lapplication.
Après la sélection dun dossier :
1. ouvrir une nouvelle `InvestigationSession` ;
2. récupérer le chemin racine depuis son `InvestigationProject` ;
3. construire le nouvel `InvestigationTreeModel` ;
4. ne remplacer lancienne enquête quaprès validation complète ;
5. mettre à jour la fenêtre principale ;
6. conserver la session jusquà la fermeture de lapplication.
Lapplication ne doit posséder quune seule session active à la fois.
## Architecture attendue
```text
FolderDialog
Application
├── InvestigationSession
│ ├── InvestigationProject
│ ├── Database
│ └── InvestigationRecord
├── InvestigationTreeModel
└── MainWindow
├── Sidebar
├── Workspace
└── Barre détat
```
## Principe de remplacement transactionnel
Louverture dune nouvelle enquête doit suivre cet ordre :
```text
ancienne session toujours active
ouvrir la nouvelle session
construire le nouvel arbre
vérifier que les deux objets sont valides
remplacer lancien arbre
fermer lancienne session
installer la nouvelle session
mettre à jour MainWindow
```
En cas déchec avant le remplacement :
```text
ancienne session conservée
ancienne arborescence conservée
nouvelle ressource libérée
message derreur produit
```
Lapplication ne doit jamais perdre une enquête déjà ouverte simplement parce quune nouvelle sélection est invalide.
---
# Travail à réaliser
## 1. Remplacer lancien objet dans `Application`
Modifier :
```text
src/core/application.c
```
La structure privée actuelle contient notamment :
```c
Investigation *investigation;
InvestigationTreeModel *tree_model;
```
Remplacer le premier champ par :
```c
InvestigationSession *session;
```
La structure doit devenir au minimum :
```c
struct Application
{
GtkApplication *gtk_application;
MainWindow *main_window;
InvestigationSession *session;
InvestigationTreeModel *tree_model;
};
```
Ne pas exposer cette structure dans le header public.
## 2. Modifier les dépendances de `application.c`
Supprimer :
```c
#include "core/investigation.h"
```
Ajouter :
```c
#include "core/investigation_session.h"
#include "core/investigation_project.h"
#include "models/investigation_record.h"
```
Conserver les dépendances nécessaires à :
```text
FolderDialog
MainWindow
InvestigationTreeBuilder
InvestigationTreeModel
GTK
```
`application.c` ne doit pas inclure directement :
```c
#include <sqlite3.h>
```
et ne doit appeler aucune fonction `sqlite3_*`.
## 3. Ouvrir la session après la sélection du dossier
Dans :
```c
application_on_folder_selected()
```
remplacer la création de lancien objet :
```c
investigation_new(folder_path)
```
par :
```c
investigation_session_open(
folder_path,
&error
);
```
Utiliser des variables temporaires :
```c
InvestigationSession *new_session = NULL;
InvestigationTreeModel *new_tree_model = NULL;
GError *error = NULL;
```
Ne jamais écrire directement dans :
```c
application->session
application->tree_model
```
avant que toute louverture soit validée.
## 4. Gérer une sélection annulée
Lorsque :
```c
folder_path == NULL
```
la fonction doit simplement retourner.
Lenquête déjà ouverte doit rester active.
Aucune session ne doit être fermée.
Aucune erreur ne doit être affichée.
Un message de diagnostic avec `g_print()` reste acceptable :
```text
Sélection annulée.
```
## 5. Gérer léchec douverture de session
Si :
```c
new_session == NULL
```
la fonction doit :
- afficher un avertissement contenant le message du `GError` ;
- libérer le `GError` ;
- conserver lancienne session ;
- conserver lancien arbre ;
- ne pas modifier la fenêtre principale.
Exemple de diagnostic acceptable :
```c
g_warning(
"Impossible d'ouvrir l'enquête : %s",
error != NULL ? error->message : "erreur inconnue"
);
```
Le dialogue graphique derreur est hors périmètre de ce ticket.
## 6. Construire larborescence depuis la session
Récupérer le projet de la nouvelle session :
```c
const InvestigationProject *project = NULL;
```
avec :
```c
project = investigation_session_get_project(
new_session
);
```
Récupérer ensuite son chemin racine :
```c
const char *root_path = NULL;
```
avec :
```c
root_path = investigation_project_get_root_path(
project
);
```
Construire le nouvel arbre avec :
```c
new_tree_model = investigation_tree_builder_build(
root_path
);
```
La logique de construction du chemin ne doit pas être dupliquée dans `application.c`.
## 7. Gérer léchec de construction de larbre
Si `InvestigationTreeBuilder` échoue après louverture de la session :
```text
new_session valide
new_tree_model == NULL
```
la fonction doit :
```c
investigation_session_close(new_session);
```
puis retourner.
Lancienne session et lancien arbre doivent rester actifs.
Le message suivant est acceptable :
```text
Impossible de construire l'arborescence de l'enquête.
```
## 8. Remplacer les anciens objets uniquement après validation
Lorsque `new_session` et `new_tree_model` sont tous deux valides :
```c
investigation_tree_model_free(
application->tree_model
);
investigation_session_close(
application->session
);
application->tree_model = new_tree_model;
application->session = new_session;
```
Après le transfert :
```c
new_tree_model = NULL;
new_session = NULL;
```
Cette remise à `NULL` nest pas obligatoire si la fonction retourne immédiatement, mais elle est recommandée pour rendre la propriété explicite.
## 9. Conserver la connexion SQLite ouverte
Après une ouverture réussie, la session doit rester stockée dans :
```c
application->session
```
Il est interdit de fermer la session à la fin de :
```c
application_on_folder_selected()
```
La connexion `Database` doit rester utilisable pendant toute la durée douverture de lenquête.
## 10. Ajouter un accesseur interne ou public
Ajouter dans :
```text
include/core/application.h
```
la déclaration suivante :
```c
const InvestigationSession *application_get_session(
const Application *application
);
```
Implémenter dans :
```text
src/core/application.c
```
```c
const InvestigationSession *application_get_session(
const Application *application
)
{
if (application == NULL)
{
return NULL;
}
return application->session;
}
```
Le pointeur retourné appartient à `Application`.
Le code appelant ne doit pas fermer cette session.
Cet accesseur servira aux futurs contrôleurs et DAO.
## 11. Mettre à jour la fenêtre principale
Ajouter dans :
```text
include/views/main_window.h
```
la fonction :
```c
void main_window_set_investigation(
MainWindow *main_window,
const char *investigation_name,
const char *investigation_root_path
);
```
Implémenter dans :
```text
src/views/main_window.c
```
Cette fonction doit :
- accepter `main_window == NULL` ;
- accepter des chaînes `NULL` sans planter ;
- mettre à jour le titre de la fenêtre ;
- mettre à jour la barre détat.
### Titre attendu
Lorsque le nom est valide :
```text
Labfy Investigation — <nom>
```
Exemple :
```text
Labfy Investigation — Enquete_Session
```
Lorsque le nom est absent :
```text
Labfy Investigation
```
Construire le titre avec GLib :
```c
g_strdup_printf()
```
puis le libérer avec :
```c
g_free()
```
### Barre détat attendue
Lorsque lenquête est ouverte :
```text
Enquête ouverte : <nom><chemin racine>
```
Lorsque certaines valeurs sont absentes, utiliser une formulation sûre sans déréférencer `NULL`.
La barre détat ne doit pas conserver un pointeur vers une chaîne temporaire.
`gtk_label_set_text()` copie le texte fourni.
## 12. Récupérer le nom persistant
Dans `application_on_folder_selected()`, récupérer :
```c
const InvestigationRecord *record = NULL;
const char *investigation_name = NULL;
```
avec :
```c
record = investigation_session_get_record(
application->session
);
investigation_name = investigation_record_get_name(
record
);
```
Utiliser les données du `InvestigationRecord`.
Ne pas reconstruire le nom à partir du dernier composant du chemin.
## 13. Mettre à jour la fenêtre après le modèle
Après linstallation de la nouvelle session et du nouvel arbre :
```c
main_window_set_tree_model(
application->main_window,
application->tree_model
);
```
puis :
```c
main_window_set_investigation(
application->main_window,
investigation_name,
root_path
);
```
Lordre attendu est :
```text
nouvelle session installée
nouvel arbre installé
sidebar mise à jour
titre et statut mis à jour
```
## 14. Fermer la session dans `application_free()`
Remplacer :
```c
investigation_free(application->investigation);
```
par :
```c
investigation_session_close(
application->session
);
```
Lordre de nettoyage conseillé est :
```c
investigation_tree_model_free(
application->tree_model
);
investigation_session_close(
application->session
);
main_window_free(
application->main_window
);
```
Puis libérer `GtkApplication` et `Application` comme actuellement.
`application_free(NULL)` doit rester valide.
## 15. Ne plus utiliser lancien objet dans lapplication
À la fin du ticket, les symboles suivants ne doivent plus apparaître dans `application.c` :
```text
Investigation *
investigation_new
investigation_free
investigation_get_root_path
investigation_get_database_path
```
Le module historique `investigation.c` nest pas supprimé dans ce ticket.
Sa suppression éventuelle sera effectuée séparément après vérification quaucun autre composant ne lutilise.
---
# Tests et validations
## 16. Tests automatisés existants
Aucun nouveau test GTK automatisé nest obligatoire dans ce ticket.
Tous les tests existants doivent rester valides :
```bash
make test
```
En particulier :
```text
InvestigationProject
InvestigationSession
InvestigationTreeBuilder
InvestigationTreeModel
InvestigationDao
```
## 17. Test manuel douverture valide
Créer ou utiliser une enquête valide.
Lancer :
```bash
make
make run
```
Sélectionner le dossier racine de lenquête.
Vérifier :
- la fenêtre reste ouverte ;
- larborescence apparaît dans la sidebar ;
- le titre contient le nom persistant de lenquête ;
- la barre détat contient le nom et le chemin racine ;
- aucune erreur SQLite napparaît ;
- la session reste active après la fin du callback.
## 18. Test manuel dannulation
Relancer lapplication et annuler la sélection.
Vérifier :
- aucun crash ;
- aucun warning critique ;
- la fenêtre reste ouverte ;
- la barre détat reste sur :
```text
Aucune enquête ouverte
```
## 19. Test manuel dun dossier invalide
Sélectionner un dossier qui ne contient pas :
```text
00_BaseDeDonnees/Enquete.sqlite
```
Vérifier :
- aucun crash ;
- un warning explicite est affiché ;
- aucun fichier SQLite nest créé ;
- la fenêtre reste utilisable ;
- aucune fausse enquête napparaît dans la sidebar.
## 20. Test manuel de remplacement
Si linterface permet une seconde sélection pendant la même exécution :
1. ouvrir une enquête valide A ;
2. tenter douvrir un dossier invalide ;
3. vérifier que A reste affichée ;
4. ouvrir une enquête valide B ;
5. vérifier que B remplace A.
Si une seconde sélection nest pas encore accessible dans linterface, cette validation sera complétée lors de lajout de laction « Ouvrir ».
La logique du callback doit néanmoins déjà préserver lancienne session.
---
# Gestion de la mémoire
Les propriétaires doivent être clairement définis.
## `Application` possède
```text
GtkApplication
MainWindow
InvestigationSession
InvestigationTreeModel
```
## `InvestigationSession` possède
```text
InvestigationProject
Database
InvestigationRecord
```
## Variables temporaires du callback
```text
new_session
new_tree_model
GError
```
En cas déchec :
```text
new_session fermée si elle existe
new_tree_model libéré si nécessaire
GError libéré
ancienne session conservée
ancien tree model conservé
```
En cas de succès :
```text
anciens objets libérés
nouveaux objets transférés dans Application
aucun double free
```
---
# Critères dacceptation
- [ ] `Application` possède un `InvestigationSession *`.
- [ ] `Application` ne possède plus d`Investigation *`.
- [ ] Le callback ouvre une session avec `investigation_session_open()`.
- [ ] Le chemin racine provient de `InvestigationProject`.
- [ ] Le nom provient d`InvestigationRecord`.
- [ ] Larbre est construit depuis le chemin de la session.
- [ ] Lancienne session reste active si louverture échoue.
- [ ] Lancien arbre reste actif si louverture échoue.
- [ ] La nouvelle session est fermée si la construction de larbre échoue.
- [ ] Les anciens objets ne sont remplacés quaprès validation complète.
- [ ] La session reste ouverte après le callback.
- [ ] `application_free()` ferme la session.
- [ ] `application_get_session(NULL)` retourne `NULL`.
- [ ] `main_window_set_investigation()` accepte `NULL`.
- [ ] Le titre affiche le nom de lenquête.
- [ ] La barre détat affiche le nom et le chemin racine.
- [ ] `application.c` nappelle aucune fonction SQLite.
- [ ] Aucun chemin SQLite nest reconstruit dans `application.c`.
- [ ] Les anciens tests restent valides.
- [ ] `make` réussit sans warning.
- [ ] `make test` réussit.
- [ ] Le test manuel douverture valide réussit.
- [ ] Le test manuel dannulation réussit.
- [ ] Le test manuel de dossier invalide réussit.
- [ ] `git diff --check` ne retourne aucune erreur.
---
# Audit attendu
La commande suivante ne doit rien afficher :
```bash
rg -n \
'investigation_new|investigation_free|investigation_get_root_path|investigation_get_database_path|Investigation \*' \
src/core/application.c
```
La commande suivante ne doit rien afficher :
```bash
rg -n \
'sqlite3_|#include <sqlite3.h>|00_BaseDeDonnees|Enquete.sqlite' \
src/core/application.c
```
Vérifier la présence de la nouvelle session :
```bash
rg -n \
'InvestigationSession|investigation_session_' \
src/core/application.c \
include/core/application.h
```
Vérifier la mise à jour de la fenêtre :
```bash
rg -n \
'main_window_set_investigation' \
include/views/main_window.h \
src/views/main_window.c \
src/core/application.c
```
---
# Hors périmètre
Ce ticket ne doit pas ajouter :
- une action de menu « Ouvrir » ;
- un raccourci clavier ;
- un dialogue graphique détaillé pour les erreurs ;
- la création dune enquête ;
- la fermeture manuelle dune enquête ;
- la réparation dun chemin racine incohérent ;
- la modification de la base SQLite ;
- les DAO des preuves ou des entités ;
- la restauration automatique de la dernière enquête ;
- la persistance des préférences utilisateur ;
- le verrouillage multi-instance ;
- la suppression de lancien module `Investigation`.
---
# Fichiers principalement concernés
```text
include/core/application.h
src/core/application.c
include/views/main_window.h
src/views/main_window.c
```
Fichiers utilisés sans modification attendue :
```text
include/core/investigation_session.h
src/core/investigation_session.c
include/core/investigation_project.h
src/core/investigation_project.c
include/models/investigation_record.h
src/models/investigation_record.c
include/core/investigation_tree_builder.h
src/core/investigation_tree_builder.c
```
Le `Makefile` ne devrait pas nécessiter de nouvelle cible, car les sources de production sont détectées automatiquement avec :
```make
SRC := $(shell find src -name "*.c")
```
---
# Résultat attendu
À la fin du ticket, la sélection dun dossier doit ouvrir une véritable session denquête.
Lapplication doit conserver ensemble :
```text
la connexion SQLite
les métadonnées persistées
le contexte de fichiers
larborescence
létat visuel de la fenêtre
```
Le flux final doit être :
```text
sélection du dossier
InvestigationSession ouverte
InvestigationTreeModel construit
session installée dans Application
arbre affiché dans MainWindow
nom et chemin affichés
```
---
# Commit attendu
Avant le commit :
```bash
make clean
make
make test
git diff --check
git status --short
```
Préparer les fichiers :
```bash
git add \
include/core/application.h \
src/core/application.c \
include/views/main_window.h \
src/views/main_window.c
```
Contrôler :
```bash
git diff --cached --stat
git diff --cached
```
Créer le commit :
```bash
git commit -m "feat(app): integrate investigation session"
```
Puis pousser après validation complète :
```bash
git push
```

File diff suppressed because it is too large Load diff

View file

@ -1,280 +0,0 @@
# Ticket #031.1 — Ajouter une fermeture propre de lapplication
## Contexte
Lapplication permet désormais :
- de créer une enquête ;
- douvrir une enquête existante ;
- de remplacer proprement la session active.
La fermeture se fait encore avec :
```text
Ctrl+C
```
Cette méthode interrompt brutalement le processus et ne constitue pas un parcours utilisateur normal.
## Objectif
Ajouter un bouton :
```text
Quitter
```
permettant de fermer proprement lapplication GTK.
La fermeture doit déclencher le cycle normal de nettoyage :
```text
GtkApplication
fin de la boucle principale
application_free()
InvestigationTreeModel libéré
InvestigationSession fermée
Database fermée
MainWindow libérée
```
## Travail à réaliser
### 1. Ajouter le bouton dans `MainWindow`
Modifier :
```text
include/views/main_window.h
src/views/main_window.c
```
La barre dactions doit devenir :
```text
[ Nouvelle enquête ] [ Ouvrir une enquête ] [ Quitter ]
```
Ajouter dans la structure privée :
```c
GtkWidget *quit_button;
```
### 2. Ajouter le type de callback
Dans `include/views/main_window.h`, ajouter :
```c
typedef void (*MainWindowQuitCallback)(
gpointer user_data
);
```
Puis déclarer :
```c
void main_window_set_quit_callback(
MainWindow *main_window,
MainWindowQuitCallback callback,
gpointer user_data
);
```
### 3. Conserver le callback dans `MainWindow`
Ajouter dans la structure privée :
```c
MainWindowQuitCallback
quit_callback;
gpointer
quit_user_data;
```
### 4. Ajouter le callback privé du bouton
Dans `src/views/main_window.c`, ajouter :
```c
static void main_window_on_quit_clicked(
GtkButton *button,
gpointer user_data
);
```
Cette fonction doit :
- accepter `button == NULL` ;
- accepter `main_window == NULL` ;
- ne rien faire si aucun callback nest défini ;
- appeler le callback configuré sinon.
### 5. Créer le bouton
Dans `main_window_new()` :
```c
main_window->quit_button =
gtk_button_new_with_label(
"Quitter"
);
```
Ajouter le bouton à `action_bar`.
Relier son signal `clicked` à :
```c
main_window_on_quit_clicked
```
### 6. Ajouter le setter public
Implémenter :
```c
void main_window_set_quit_callback(
MainWindow *main_window,
MainWindowQuitCallback callback,
gpointer user_data
)
{
if (main_window == NULL)
{
return;
}
main_window->quit_callback = callback;
main_window->quit_user_data = user_data;
}
```
### 7. Ajouter le contrôleur dans `Application`
Dans `src/core/application.c`, ajouter :
```c
static void application_on_quit_requested(
gpointer user_data
);
```
Cette fonction doit utiliser :
```c
g_application_quit(
G_APPLICATION(
application->gtk_application
)
);
```
Aucun appel direct à `exit()` ne doit être ajouté.
### 8. Relier le callback dans `application_on_activate()`
Ajouter :
```c
main_window_set_quit_callback(
application->main_window,
application_on_quit_requested,
application
);
```
## Tests manuels
### Fermeture sans enquête
1. lancer lapplication ;
2. cliquer sur `Quitter` ;
3. vérifier que le processus se termine normalement.
### Fermeture avec une enquête ouverte
1. ouvrir ou créer une enquête ;
2. cliquer sur `Quitter` ;
3. vérifier que la fenêtre se ferme ;
4. vérifier quaucun crash napparaît ;
5. relancer lapplication ;
6. rouvrir la même enquête.
### Fermeture après remplacement de session
1. ouvrir une enquête A ;
2. ouvrir une enquête B ;
3. cliquer sur `Quitter` ;
4. vérifier labsence de crash ou de double libération.
## Critères dacceptation
- [ ] Le bouton `Quitter` est visible.
- [ ] Le bouton ferme lapplication.
- [ ] Aucun `exit()` direct nest utilisé.
- [ ] `g_application_quit()` est utilisé.
- [ ] `MainWindow` ne connaît pas `GtkApplication`.
- [ ] Le nettoyage reste géré par `Application`.
- [ ] La fermeture fonctionne sans enquête ouverte.
- [ ] La fermeture fonctionne avec une enquête ouverte.
- [ ] La fermeture fonctionne après un changement denquête.
- [ ] `make` réussit.
- [ ] `make test` réussit.
- [ ] `git diff --check` ne retourne aucune erreur.
## Audit attendu
```bash
rg -n \
'quit_button|quit_callback|g_application_quit' \
include/views/main_window.h \
src/views/main_window.c \
src/core/application.c
```
La commande suivante ne doit rien afficher :
```bash
rg -n \
'\bexit\s*\(' \
src/core/application.c \
src/views/main_window.c
```
## Fichiers concernés
```text
include/views/main_window.h
src/views/main_window.c
src/core/application.c
```
## Commit attendu
```bash
make clean
make
make test
git diff --check
git status --short
```
```bash
git add \
include/views/main_window.h \
src/views/main_window.c \
src/core/application.c
```
```bash
git commit -m "feat(ui): add clean application quit action"
git push
```

View file

@ -1,301 +0,0 @@
# Ticket #031 — Ouvrir une enquête existante depuis GTK
## Contexte
Les tickets #029 et #030 ont permis de :
- créer une enquête depuis GTK ;
- initialiser son arborescence et sa base SQLite ;
- ouvrir une `InvestigationSession` ;
- construire le `InvestigationTreeModel` ;
- installer la session dans `Application` ;
- afficher le nom et le chemin de lenquête dans `MainWindow`.
Lapplication ne permet cependant pas encore douvrir explicitement une enquête existante.
Lancien sélecteur automatique au démarrage a été supprimé afin de ne pas forcer lutilisateur à choisir un dossier à chaque lancement.
## Objectif
Ajouter un bouton :
```text
Ouvrir une enquête
```
Ce bouton doit permettre de sélectionner le dossier racine dune enquête existante, puis de louvrir avec le flux déjà présent :
```text
FolderDialog
investigation_session_open()
investigation_tree_builder_build()
application_install_session()
MainWindow mise à jour
```
La logique dinstallation dune session ne doit pas être dupliquée.
## Travail à réaliser
### 1. Ajouter le bouton dans `MainWindow`
Modifier :
```text
include/views/main_window.h
src/views/main_window.c
```
Ajouter un bouton visible dans la barre dactions :
```text
Ouvrir une enquête
```
La barre doit contenir au minimum :
```text
[ Nouvelle enquête ] [ Ouvrir une enquête ]
```
Ajouter dans la structure privée :
```c
GtkWidget *open_investigation_button;
```
### 2. Ajouter le type de callback
Dans `include/views/main_window.h`, ajouter :
```c
typedef void (*MainWindowOpenInvestigationCallback)(
gpointer user_data
);
```
Puis déclarer :
```c
void main_window_set_open_investigation_callback(
MainWindow *main_window,
MainWindowOpenInvestigationCallback callback,
gpointer user_data
);
```
### 3. Conserver le callback dans `MainWindow`
Ajouter dans la structure privée :
```c
MainWindowOpenInvestigationCallback
open_investigation_callback;
gpointer
open_investigation_user_data;
```
Ajouter un callback privé :
```c
static void main_window_on_open_investigation_clicked(
GtkButton *button,
gpointer user_data
);
```
Il doit appeler le callback configuré uniquement sil existe.
`MainWindow` ne doit pas ouvrir elle-même la session.
### 4. Relier le bouton à `Application`
Dans `src/core/application.c`, réactiver lutilisation de :
```c
#include "views/folder_dialog.h"
```
Ajouter :
```c
static void application_on_open_investigation_requested(
gpointer user_data
);
```
Cette fonction doit ouvrir le sélecteur avec :
```c
folder_dialog_select_folder(
main_window_get_window(application->main_window),
application_on_folder_selected,
application
);
```
### 5. Réutiliser `application_on_folder_selected()`
La fonction doit :
1. accepter lannulation ;
2. ouvrir la session avec `investigation_session_open()` ;
3. récupérer le chemin racine depuis `InvestigationProject` ;
4. construire larbre avec `investigation_tree_builder_build()` ;
5. installer les objets avec `application_install_session()`.
En cas déchec :
```text
ancienne session conservée
ancien arbre conservé
nouvelle session libérée
nouvel arbre libéré
warning explicite
```
### 6. Enregistrer le callback dans `application_on_activate()`
Après la création de `MainWindow`, appeler :
```c
main_window_set_open_investigation_callback(
application->main_window,
application_on_open_investigation_requested,
application
);
```
Le sélecteur ne doit pas être lancé automatiquement au démarrage.
## Tests manuels
### Ouverture valide
1. lancer lapplication ;
2. cliquer sur `Ouvrir une enquête` ;
3. sélectionner une enquête créée avec le ticket #030 ;
4. vérifier laffichage de larborescence ;
5. vérifier le titre ;
6. vérifier la barre détat ;
7. vérifier labsence derreur SQLite.
### Annulation
Ouvrir le sélecteur puis annuler.
Vérifier :
```text
aucun crash
aucun changement de session
aucun changement darbre
```
### Dossier invalide
Sélectionner un dossier sans base SQLite.
Vérifier :
```text
warning explicite
aucune base créée
ancienne session conservée
```
### Remplacement
1. ouvrir une enquête A ;
2. ouvrir une enquête B ;
3. vérifier que B remplace A.
### Échec pendant le remplacement
1. ouvrir une enquête valide A ;
2. tenter douvrir un dossier invalide ;
3. vérifier que A reste active.
## Critères dacceptation
- [ ] Le bouton `Ouvrir une enquête` est visible.
- [ ] Le bouton ouvre un sélecteur de dossier.
- [ ] Le sélecteur ne souvre pas automatiquement au démarrage.
- [ ] Une enquête valide peut être ouverte.
- [ ] Larborescence est affichée.
- [ ] Le titre est mis à jour.
- [ ] La barre détat est mise à jour.
- [ ] Lannulation ne modifie aucun état.
- [ ] Un dossier invalide est refusé.
- [ ] Une base absente nest pas créée.
- [ ] Lancienne session reste active en cas déchec.
- [ ] Lancien arbre reste actif en cas déchec.
- [ ] `application_install_session()` est réutilisée.
- [ ] Aucun appel SQLite direct nest ajouté.
- [ ] `make` réussit.
- [ ] `make test` réussit.
- [ ] `git diff --check` ne retourne aucune erreur.
## Audit attendu
```bash
rg -n 'open_investigation|Ouvrir une enquête' include/views/main_window.h src/views/main_window.c src/core/application.c
```
```bash
rg -n 'folder_dialog_select_folder' src/core/application.c
```
```bash
rg -n 'sqlite3_|#include <sqlite3.h>' src/core/application.c src/views/main_window.c
```
Résultat attendu pour la dernière commande :
```text
aucune sortie
```
## Fichiers principalement concernés
```text
include/views/main_window.h
src/views/main_window.c
src/core/application.c
```
## Résultat attendu
À la fin du ticket, lapplication doit proposer deux actions explicites :
```text
Nouvelle enquête
Ouvrir une enquête
```
Lutilisateur doit pouvoir créer une enquête, fermer lapplication, puis la rouvrir plus tard depuis GTK.
## Commit attendu
```bash
make clean
make
make test
git diff --check
git status --short
```
```bash
git add include/views/main_window.h src/views/main_window.c src/core/application.c
```
```bash
git commit -m "feat(ui): add investigation opening action"
git push
```

View file

@ -1,537 +0,0 @@
# Ticket #032 — Factoriser le chargement et linstallation dune enquête
## Contexte
Après le ticket #031.1, lapplication sait :
- créer une enquête ;
- ouvrir une enquête existante ;
- remplacer la session active ;
- fermer proprement lapplication.
Cependant, les flux « Nouvelle enquête » et « Ouvrir une enquête » dupliquent encore une partie importante de la logique :
```text
investigation_session_open()
→ récupération du projet
→ récupération du chemin racine
→ investigation_tree_builder_build()
→ application_install_session()
→ nettoyage en cas déchec
```
Cette duplication augmentera avec les futures fonctions :
- enquêtes récentes ;
- ouverture depuis la ligne de commande ;
- restauration de session ;
- ouverture depuis un rapport ou une archive.
## Objectif
Créer dans `src/core/application.c` une fonction interne unique qui ouvre et installe une enquête à partir de son dossier racine.
Contrat attendu :
```c
static gboolean application_open_and_install_investigation(
Application *application,
const char *root_path,
GError **error
);
```
Cette fonction doit garantir :
```text
succès :
Application devient propriétaire de la nouvelle session
Application devient propriétaire du nouvel arbre
ancienne session libérée
ancien arbre libéré
fenêtre mise à jour
échec :
ancienne session conservée
ancien arbre conservé
nouvelle session libérée
nouvel arbre libéré
erreur transmise à lappelant
```
---
# Travail à réaliser
## 1. Ajouter un domaine derreur privé
Dans `src/core/application.c`, ajouter un domaine derreur uniquement utilisé par le contrôleur.
Exemple :
```c
typedef enum
{
APPLICATION_OPEN_ERROR_INVALID_ARGUMENT,
APPLICATION_OPEN_ERROR_INVALID_PROJECT,
APPLICATION_OPEN_ERROR_TREE_BUILD,
APPLICATION_OPEN_ERROR_INSTALL
} ApplicationOpenError;
```
Ajouter :
```c
#define APPLICATION_OPEN_ERROR \
application_open_error_quark()
```
Puis :
```c
static GQuark application_open_error_quark(void)
{
return g_quark_from_static_string(
"labfy-investigation-application-open-error"
);
}
```
Le domaine reste privé à `application.c`.
---
## 2. Créer la fonction factorisée
Ajouter :
```c
static gboolean application_open_and_install_investigation(
Application *application,
const char *root_path,
GError **error
);
```
La fonction doit :
1. valider `application` ;
2. valider `application->main_window` ;
3. valider `root_path` ;
4. ouvrir une nouvelle `InvestigationSession` ;
5. récupérer son `InvestigationProject` ;
6. récupérer le chemin racine canonique ;
7. construire un nouvel `InvestigationTreeModel` ;
8. appeler `application_install_session()` ;
9. transférer la propriété uniquement en cas de succès.
### Règle de propriété
Avant `application_install_session()` :
```text
la fonction possède new_session
la fonction possède new_tree_model
```
Après succès :
```text
Application possède new_session
Application possède new_tree_model
```
Après échec :
```text
la fonction doit libérer les objets quelle possède encore
```
### Validation de `GError`
La fonction doit respecter la convention GLib :
```c
g_return_val_if_fail(
error == NULL || *error == NULL,
FALSE
);
```
Lutilisation de `g_return_val_if_fail()` est acceptable ici pour vérifier le contrat du développeur.
Les erreurs utilisateur doivent être produites avec :
```c
g_set_error()
g_set_error_literal()
g_propagate_prefixed_error()
```
---
## 3. Propager lerreur de `InvestigationSession`
Si :
```c
investigation_session_open()
```
échoue, la fonction doit conserver lerreur métier dorigine et lui ajouter du contexte.
Exemple conceptuel :
```text
Impossible douvrir lenquête : la base SQLite est absente
```
Ne pas remplacer lerreur précise par un simple :
```text
Erreur inconnue
```
lorsquun `GError` est disponible.
---
## 4. Refactoriser louverture dune enquête existante
`application_on_folder_selected()` ne doit plus contenir directement :
```c
investigation_session_open()
investigation_tree_builder_build()
application_install_session()
```
Elle doit seulement :
1. accepter lannulation ;
2. appeler `application_open_and_install_investigation()` ;
3. journaliser lerreur ;
4. libérer le `GError`.
Structure attendue :
```c
static void application_on_folder_selected(
const char *folder_path,
gpointer user_data
)
{
Application *application = user_data;
GError *error = NULL;
if (application == NULL ||
folder_path == NULL)
{
return;
}
if (!application_open_and_install_investigation(
application,
folder_path,
&error
))
{
g_warning(
"Impossible d'ouvrir l'enquête '%s' : %s",
folder_path,
error != NULL
? error->message
: "erreur inconnue"
);
g_clear_error(&error);
}
}
```
Lannulation ne doit pas produire de warning.
---
## 5. Refactoriser la création dune enquête
`application_on_create_investigation()` doit conserver uniquement :
1. la création physique du projet ;
2. lappel à la fonction factorisée ;
3. le message spécifique indiquant que le projet existe sur le disque si son ouverture échoue.
Le flux devient :
```text
investigation_project_create()
→ application_open_and_install_investigation()
```
La fonction ne doit plus reconstruire elle-même :
```text
session
projet
chemin canonique
arbre
installation
```
En cas déchec après création, le dossier créé reste volontairement sur le disque.
Le message doit être explicite :
```text
Lenquête a été créée dans « ... », mais son ouverture a échoué : ...
```
---
## 6. Ne pas modifier `application_install_session()`
Cette fonction conserve sa responsabilité actuelle :
- valider les objets prêts à installer ;
- libérer lancien état ;
- transférer la propriété ;
- mettre à jour `MainWindow`.
Le nouveau helper prépare les objets.
`application_install_session()` réalise le remplacement final.
---
# Pseudo-code de la fonction factorisée
```c
static gboolean application_open_and_install_investigation(
Application *application,
const char *root_path,
GError **error
)
{
InvestigationSession *new_session = NULL;
InvestigationTreeModel *new_tree_model = NULL;
const InvestigationProject *project = NULL;
const char *canonical_root_path = NULL;
GError *session_error = NULL;
valider les arguments;
new_session = investigation_session_open(
root_path,
&session_error
);
si échec :
propager session_error avec contexte;
return FALSE;
project = investigation_session_get_project(
new_session
);
valider project;
canonical_root_path =
investigation_project_get_root_path(project);
valider canonical_root_path;
new_tree_model =
investigation_tree_builder_build(
canonical_root_path
);
si échec :
produire GError;
fermer new_session;
return FALSE;
si application_install_session() échoue :
produire GError;
libérer new_tree_model;
fermer new_session;
return FALSE;
return TRUE;
}
```
---
# Tests manuels
## Création valide
1. lancer lapplication ;
2. créer une enquête ;
3. vérifier le titre ;
4. vérifier la barre détat ;
5. vérifier larborescence.
## Ouverture valide
1. fermer et relancer ;
2. ouvrir lenquête créée ;
3. vérifier le même résultat.
## Remplacement valide
1. ouvrir une enquête A ;
2. ouvrir une enquête B ;
3. vérifier que B remplace A.
## Échec douverture
1. ouvrir une enquête valide A ;
2. tenter douvrir un dossier invalide ;
3. vérifier que A reste active ;
4. vérifier que lerreur est journalisée.
## Échec après création
Provoquer si possible un échec douverture après création.
Vérifier :
- le dossier créé reste présent ;
- lancienne session reste active ;
- aucun double `free` ;
- aucun crash.
## Annulation
Annuler le sélecteur de dossier.
Vérifier :
- aucun warning ;
- aucun changement détat ;
- aucun crash.
---
# Critères dacceptation
- [ ] Une seule fonction ouvre une session et construit son arbre.
- [ ] La création utilise cette fonction.
- [ ] Louverture utilise cette fonction.
- [ ] `application_install_session()` nest pas dupliquée.
- [ ] Lancienne session reste active en cas déchec.
- [ ] Lancien arbre reste actif en cas déchec.
- [ ] Les nouveaux objets sont libérés en cas déchec.
- [ ] Les erreurs de `InvestigationSession` sont propagées.
- [ ] Lannulation ne produit pas derreur.
- [ ] Aucun code SQLite direct nest ajouté.
- [ ] Aucun comportement GTK visible nest cassé.
- [ ] `make` réussit.
- [ ] `make test` réussit.
- [ ] `git diff --check` ne retourne aucune erreur.
---
# Audit attendu
La logique douverture ne doit apparaître quune seule fois :
```bash
rg -n \
'investigation_session_open|investigation_tree_builder_build' \
src/core/application.c
```
Résultat attendu :
```text
une occurrence de investigation_session_open
une occurrence de investigation_tree_builder_build
```
La création et louverture doivent appeler le helper :
```bash
rg -n \
'application_open_and_install_investigation' \
src/core/application.c
```
Vérifier labsence de SQLite direct :
```bash
rg -n \
'sqlite3_|#include <sqlite3.h>' \
src/core/application.c
```
Résultat attendu :
```text
aucune sortie
```
Vérifier le format :
```bash
git diff --check
```
---
# Fichiers concernés
```text
src/core/application.c
```
Aucune modification publique nest normalement nécessaire dans :
```text
include/core/application.h
```
---
# Commit attendu
```bash
make clean
make
make test
git diff --check
git status --short
```
```bash
git add src/core/application.c
```
```bash
git diff --cached --stat
git diff --cached
```
```bash
git commit -m "refactor(core): centralize investigation loading"
git push
```
---
# Résultat attendu
Après ce ticket, tous les futurs points dentrée utiliseront le même flux :
```text
Création
Ouverture manuelle
Enquêtes récentes
Ligne de commande
Restauration de session
application_open_and_install_investigation()
```
Le ticket #033 pourra ensuite afficher graphiquement les `GError` déjà correctement produits par ce flux.

View file

@ -1,694 +0,0 @@
# Ticket #033 — Afficher les erreurs dans linterface GTK
## Contexte
Le ticket #032 a centralisé le chargement dune enquête dans :
```c
application_open_and_install_investigation()
```
Cette fonction produit maintenant des `GError` précis et conserve lancienne session en cas déchec.
Actuellement, les erreurs sont seulement écrites dans le terminal avec :
```c
g_warning()
```
Un utilisateur qui lance Labfy Investigation depuis son menu dapplications ne verra pas ces messages.
## Objectif
Créer un module GTK réutilisable capable dafficher un message derreur compréhensible dans une fenêtre modale.
Le contrôleur doit conserver deux niveaux dinformation :
```text
Terminal :
message technique complet avec g_warning()
Interface GTK :
titre clair + message compréhensible
```
Le module doit rester générique afin de pouvoir afficher plus tard :
- erreurs ;
- avertissements ;
- informations.
---
# Architecture attendue
Créer :
```text
include/views/application_message_dialog.h
src/views/application_message_dialog.c
```
Responsabilités :
```text
Application
├── décide quand afficher une erreur
├── fournit le titre
└── fournit le message
ApplicationMessageDialog
├── construit la fenêtre GTK
├── affiche le contenu
└── gère sa fermeture
```
Le module de dialogue ne doit connaître ni :
- `Application` ;
- `InvestigationSession` ;
- SQLite ;
- les règles métier de lenquête.
---
# Travail à réaliser
## 1. Créer len-tête public
Créer :
```text
include/views/application_message_dialog.h
```
Contenu attendu :
```c
/******************************************************************************
* @file application_message_dialog.h
* @brief Fenêtre modale affichant un message à l'utilisateur.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_APPLICATION_MESSAGE_DIALOG_H
#define LABFY_INVESTIGATION_APPLICATION_MESSAGE_DIALOG_H
#include <gtk/gtk.h>
/**
* @brief Nature du message affiché.
*/
typedef enum
{
APPLICATION_MESSAGE_DIALOG_ERROR,
APPLICATION_MESSAGE_DIALOG_WARNING,
APPLICATION_MESSAGE_DIALOG_INFORMATION
} ApplicationMessageDialogType;
/**
* @brief Affiche une fenêtre modale contenant un message.
*
* Les chaînes sont copiées par les widgets GTK.
*
* @param parent_window Fenêtre parente, ou NULL.
* @param message_type Type de message.
* @param title Titre de la fenêtre.
* @param message Message principal.
*/
void application_message_dialog_present(
GtkWindow *parent_window,
ApplicationMessageDialogType message_type,
const char *title,
const char *message
);
#endif
```
---
## 2. Créer limplémentation GTK
Créer :
```text
src/views/application_message_dialog.c
```
Le dialogue doit être construit avec des widgets GTK4 simples afin de rester indépendant dune API de dialogue plus récente.
Structure visuelle recommandée :
```text
┌─────────────────────────────────────────────┐
│ Titre │
│ │
│ Message pouvant occuper plusieurs lignes │
│ │
│ [ Fermer ] │
└─────────────────────────────────────────────┘
```
Le module doit utiliser au minimum :
- `GtkWindow` ;
- `GtkBox` ;
- `GtkLabel` ;
- `GtkButton`.
Propriétés recommandées :
```c
gtk_window_set_modal(dialog_window, TRUE);
gtk_window_set_destroy_with_parent(dialog_window, TRUE);
gtk_window_set_resizable(dialog_window, FALSE);
```
Si `parent_window` nest pas `NULL` :
```c
gtk_window_set_transient_for(
dialog_window,
parent_window
);
```
Le message doit :
- revenir automatiquement à la ligne ;
- être aligné à gauche ;
- être sélectionnable pour permettre sa copie ;
- avoir une largeur raisonnable.
Exemple :
```c
gtk_label_set_wrap(
GTK_LABEL(message_label),
TRUE
);
gtk_label_set_selectable(
GTK_LABEL(message_label),
TRUE
);
gtk_label_set_xalign(
GTK_LABEL(message_label),
0.0F
);
gtk_label_set_max_width_chars(
GTK_LABEL(message_label),
70
);
```
---
## 3. Gérer les arguments invalides
La fonction doit accepter :
```text
parent_window == NULL
title == NULL
title vide
message == NULL
message vide
```
Valeurs de remplacement recommandées :
```text
Titre :
Erreur
Avertissement
Information
Message :
Aucun détail supplémentaire n'est disponible.
```
Le titre par défaut dépend de `message_type`.
Une valeur inconnue de `message_type` doit être traitée comme une information ou un avertissement, sans crash.
---
## 4. Ajouter un callback privé de fermeture
Dans limplémentation, ajouter :
```c
static void application_message_dialog_on_close_clicked(
GtkButton *button,
gpointer user_data
);
```
Ce callback doit :
1. ignorer proprement `button` ;
2. vérifier le pointeur de fenêtre ;
3. appeler `gtk_window_destroy()`.
Exemple :
```c
static void application_message_dialog_on_close_clicked(
GtkButton *button,
gpointer user_data
)
{
GtkWindow *dialog_window = user_data;
(void) button;
if (dialog_window == NULL)
{
return;
}
gtk_window_destroy(
dialog_window
);
}
```
Aucune structure allouée manuellement ne doit être nécessaire pour ce premier dialogue.
---
## 5. Différencier visuellement les types
Le dialogue doit au minimum afficher un libellé distinct :
```text
Erreur
Avertissement
Information
```
Une différenciation légère peut être ajoutée avec des classes CSS GTK existantes sur le titre ou le bouton.
La couleur ne doit jamais être le seul moyen de distinguer le type.
Aucune feuille CSS spécifique nest nécessaire dans ce ticket.
---
## 6. Ajouter la source au Makefile
Ajouter :
```text
src/views/application_message_dialog.c
```
à la liste des sources de lapplication.
Le module doit être compilé avec les mêmes options strictes :
```text
-std=c17
-Wall
-Wextra
-Wpedantic
-Werror
```
---
# Intégration dans `Application`
## 7. Ajouter len-tête
Dans :
```text
src/core/application.c
```
ajouter :
```c
#include "views/application_message_dialog.h"
```
---
## 8. Créer un helper privé dans `application.c`
Ajouter :
```c
static void application_present_error(
Application *application,
const char *title,
const char *message
);
```
Implémentation attendue :
```c
static void application_present_error(
Application *application,
const char *title,
const char *message
)
{
GtkWindow *parent_window = NULL;
if (application != NULL &&
application->main_window != NULL)
{
parent_window = main_window_get_window(
application->main_window
);
}
application_message_dialog_present(
parent_window,
APPLICATION_MESSAGE_DIALOG_ERROR,
title,
message
);
}
```
Ce helper centralise le choix du parent GTK.
---
## 9. Afficher les erreurs douverture
Dans :
```c
application_on_folder_selected()
```
conserver le `g_warning()` existant.
Ajouter ensuite :
```c
application_present_error(
application,
"Ouverture impossible",
error != NULL
? error->message
: "L'enquête sélectionnée n'a pas pu être ouverte."
);
```
Lerreur doit être affichée avant :
```c
g_clear_error(&error);
```
Lannulation du sélecteur ne doit toujours afficher aucun message.
---
## 10. Afficher les erreurs de création
Dans :
```c
application_on_create_investigation()
```
traiter les deux échecs.
### Échec de création physique
Conserver le `g_warning()` puis afficher :
```text
Titre :
Création impossible
Message :
L'enquête n'a pas pu être créée dans le dossier sélectionné.
```
Le message peut contenir :
- le nom de lenquête ;
- le dossier parent.
### Projet créé mais impossible à ouvrir
Conserver le `g_warning()` puis afficher un message indiquant clairement :
```text
L'enquête a été créée sur le disque, mais Labfy n'a pas pu l'ouvrir.
```
Ajouter ensuite le détail du `GError`.
Le message doit éviter de laisser croire que le dossier créé a été supprimé.
Une chaîne dynamique peut être construite avec :
```c
g_strdup_printf()
```
Elle doit être libérée après lappel au dialogue.
---
# Exemple dintégration
```c
char *user_message = NULL;
user_message = g_strdup_printf(
"L'enquête a été créée dans :\n%s\n\n"
"Elle n'a cependant pas pu être ouverte :\n%s",
created_root_path,
error != NULL
? error->message
: "erreur inconnue"
);
application_present_error(
application,
"Enquête créée mais non ouverte",
user_message
);
g_free(
user_message
);
```
---
# Gestion de plusieurs erreurs
Ce ticket ne crée pas encore de file de notifications.
Si plusieurs erreurs surviennent successivement, plusieurs fenêtres peuvent être affichées.
La gestion centralisée des tâches et notifications arrivera avec les tickets #034 et #035.
---
# Tests manuels
## Dossier invalide
1. lancer lapplication ;
2. ouvrir une enquête valide A ;
3. cliquer sur `Ouvrir une enquête` ;
4. sélectionner un dossier qui nest pas une enquête.
Vérifier :
- une fenêtre derreur apparaît ;
- le message explique léchec ;
- A reste active ;
- le terminal contient encore le `g_warning()` ;
- aucun crash.
## Annulation
1. ouvrir le sélecteur ;
2. annuler.
Vérifier :
- aucun dialogue ;
- aucun warning ;
- aucun changement de session.
## Création dans un emplacement invalide
Provoquer si possible un échec de création :
- dossier non accessible en écriture ;
- nom déjà utilisé ;
- emplacement invalide.
Vérifier :
- dialogue visible ;
- message compréhensible ;
- aucune session remplacée.
## Projet créé mais ouverture échouée
Provoquer si possible un échec après la création.
Vérifier que le dialogue précise :
- que le dossier existe ;
- où il se trouve ;
- que seule louverture a échoué.
## Fermeture du dialogue
Tester :
- bouton `Fermer` ;
- bouton de fermeture de la fenêtre ;
- fermeture de la fenêtre principale.
Aucun segfault ne doit apparaître.
## Répétition
Produire plusieurs erreurs successives.
Vérifier quune nouvelle erreur reste affichable après fermeture de la précédente.
---
# Critères dacceptation
- [ ] Le module `application_message_dialog` existe.
- [ ] Le module ne dépend pas du cœur métier.
- [ ] Le dialogue est modal.
- [ ] Le dialogue possède une fenêtre parente lorsquelle existe.
- [ ] Le message revient à la ligne.
- [ ] Le message est sélectionnable.
- [ ] Les arguments `NULL` sont acceptés.
- [ ] Un bouton `Fermer` fonctionne.
- [ ] Les erreurs douverture sont visibles dans GTK.
- [ ] Les erreurs de création sont visibles dans GTK.
- [ ] Les `g_warning()` techniques sont conservés.
- [ ] Lannulation ne produit aucun dialogue.
- [ ] Lancienne session est conservée en cas déchec.
- [ ] Aucun appel SQLite direct nest ajouté.
- [ ] Aucun `exit()` direct nest ajouté.
- [ ] `make` réussit.
- [ ] `make test` réussit.
- [ ] `git diff --check` ne retourne aucune erreur.
---
# Audit attendu
Vérifier lutilisation du module :
```bash
rg -n \
'application_message_dialog|application_present_error' \
include/views/application_message_dialog.h \
src/views/application_message_dialog.c \
src/core/application.c \
Makefile
```
Vérifier que les erreurs restent journalisées :
```bash
rg -n \
'g_warning' \
src/core/application.c
```
Vérifier labsence de logique métier dans le dialogue :
```bash
rg -n \
'Investigation|Database|sqlite3_' \
include/views/application_message_dialog.h \
src/views/application_message_dialog.c
```
Résultat attendu :
```text
aucune sortie
```
Vérifier labsence de sortie forcée :
```bash
rg -n \
'\bexit\s*\(' \
src/core/application.c \
src/views/application_message_dialog.c
```
Résultat attendu :
```text
aucune sortie
```
---
# Fichiers concernés
```text
include/views/application_message_dialog.h
src/views/application_message_dialog.c
src/core/application.c
Makefile
```
---
# Commit attendu
```bash
make clean
make
make test
git diff --check
git status --short
```
```bash
git add \
include/views/application_message_dialog.h \
src/views/application_message_dialog.c \
src/core/application.c \
Makefile
```
```bash
git diff --cached --stat
git diff --cached
```
```bash
git commit -m "feat(ui): display application errors in GTK"
git push
```
---
# Résultat attendu
Après ce ticket, lutilisateur ne dépendra plus du terminal pour comprendre pourquoi une enquête na pas pu être créée ou ouverte.
Le ticket #034 pourra ensuite introduire le gestionnaire de tâches asynchrones sans mélanger la présentation des erreurs avec lexécution des traitements longs.

View file

@ -1,967 +0,0 @@
# Ticket #034 — Modèle et exécuteur de tâches asynchrones
## Contexte
Labfy Investigation va bientôt exécuter des traitements potentiellement longs :
- calcul dempreintes ;
- copie de fichiers ;
- extraction de métadonnées ;
- lancement doutils externes ;
- recherches DNS et réseau ;
- analyse de résultats ;
- génération de rapports.
Ces opérations ne doivent jamais bloquer la boucle principale GTK.
Le ticket #035 ajoutera une file de tâches et un panneau dactivité. Avant cela, il faut créer une abstraction asynchrone fiable, indépendante de GTK et réutilisable par tous les futurs modules.
## Objectif
Créer un type opaque :
```c
BackgroundTask
```
capable de :
- exécuter une fonction de travail dans un thread GLib ;
- conserver son état ;
- suivre sa progression ;
- accepter une demande dannulation ;
- conserver un résultat ;
- conserver une erreur ;
- enregistrer ses dates de début et de fin ;
- appeler un callback de fin sur le contexte principal ;
- garantir une gestion correcte de sa durée de vie.
Le module doit sappuyer sur :
```text
GTask
GCancellable
GMutex
gatomicrefcount
```
Il ne doit dépendre ni de GTK, ni de SQLite, ni dune enquête particulière.
---
# Architecture attendue
Créer :
```text
include/core/background_task.h
src/core/background_task.c
tests/test_background_task.c
```
Le flux général doit être :
```text
background_task_new()
background_task_start()
GTask exécute le worker dans un thread
le worker signale sa progression
succès / erreur / annulation
callback de fin sur le contexte principal
background_task_unref()
```
---
# 1. Définir les états publics
Dans :
```text
include/core/background_task.h
```
définir :
```c
typedef enum
{
BACKGROUND_TASK_STATE_PENDING,
BACKGROUND_TASK_STATE_RUNNING,
BACKGROUND_TASK_STATE_COMPLETED,
BACKGROUND_TASK_STATE_FAILED,
BACKGROUND_TASK_STATE_CANCELLED
} BackgroundTaskState;
```
Transitions autorisées :
```text
PENDING → RUNNING
RUNNING → COMPLETED
RUNNING → FAILED
RUNNING → CANCELLED
```
Une tâche terminée ne peut jamais être redémarrée.
---
# 2. Définir le type opaque
```c
typedef struct BackgroundTask BackgroundTask;
```
La structure interne ne doit jamais apparaître dans le header.
---
# 3. Définir le worker
Ajouter :
```c
typedef gboolean (*BackgroundTaskWorker)(
BackgroundTask *task,
GCancellable *cancellable,
gpointer worker_data,
gpointer *result,
GError **error
);
```
Contrat :
```text
succès :
retourne TRUE
error reste NULL
result peut être NULL ou contenir un résultat
échec :
retourne FALSE
error doit normalement être renseignée
result doit rester NULL
annulation :
retourne FALSE
error appartient au domaine G_IO_ERROR
code G_IO_ERROR_CANCELLED
```
Le worker sexécute dans un thread secondaire.
Il ne doit jamais :
- manipuler directement GTK ;
- accéder à un widget ;
- modifier une structure non protégée ;
- appeler le callback final lui-même.
Le worker peut appeler :
```c
background_task_report_progress()
```
depuis son thread.
---
# 4. Définir le callback final
Ajouter :
```c
typedef void (*BackgroundTaskCompletionCallback)(
BackgroundTask *task,
gpointer user_data
);
```
Le callback doit être appelé après la mise à jour de létat final.
Lorsquune tâche est lancée depuis le thread principal GTK, le callback doit revenir sur ce contexte principal grâce au comportement de `GTask`.
---
# 5. Définir le domaine derreur
Ajouter :
```c
typedef enum
{
BACKGROUND_TASK_ERROR_INVALID_ARGUMENT,
BACKGROUND_TASK_ERROR_ALREADY_STARTED,
BACKGROUND_TASK_ERROR_WORKER_PROTOCOL
} BackgroundTaskError;
```
Puis :
```c
#define BACKGROUND_TASK_ERROR \
background_task_error_quark()
GQuark background_task_error_quark(void);
```
Utilisation :
- argument invalide ;
- seconde tentative de démarrage ;
- worker retournant `FALSE` sans fournir de `GError` ;
- worker retournant `TRUE` tout en fournissant une erreur.
---
# 6. API publique attendue
## Construction et références
```c
BackgroundTask *background_task_new(
const char *title
);
BackgroundTask *background_task_ref(
BackgroundTask *task
);
void background_task_unref(
BackgroundTask *task
);
```
`background_task_new()` doit refuser :
```text
title == NULL
title vide
```
La tâche utilise un comptage de références atomique.
Ne pas exposer une fonction `background_task_free()`.
Cette décision est importante : la tâche doit pouvoir rester vivante pendant lexécution même si son propriétaire visuel disparaît.
---
## Démarrage
```c
gboolean background_task_start(
BackgroundTask *task,
BackgroundTaskWorker worker,
gpointer worker_data,
GDestroyNotify worker_data_destroy,
GDestroyNotify result_destroy,
BackgroundTaskCompletionCallback completion_callback,
gpointer completion_data,
GDestroyNotify completion_data_destroy,
GError **error
);
```
### Propriété des paramètres
Si le démarrage réussit :
```text
BackgroundTask prend en charge worker_data
BackgroundTask prend en charge completion_data
BackgroundTask prend en charge le futur result
```
Les fonctions de destruction correspondantes seront appelées au moment approprié.
Si le démarrage échoue :
```text
lappelant conserve worker_data
lappelant conserve completion_data
```
### Contraintes
La fonction doit :
1. vérifier `task` ;
2. vérifier `worker` ;
3. vérifier la convention `GError` ;
4. refuser une tâche qui nest plus `PENDING` ;
5. créer un `GCancellable` ;
6. passer létat à `RUNNING` ;
7. enregistrer la date de début ;
8. lancer le worker avec `g_task_run_in_thread()` ;
9. conserver une référence interne jusquau callback final.
---
## Annulation
```c
void background_task_cancel(
BackgroundTask *task
);
gboolean background_task_is_cancelled(
const BackgroundTask *task
);
```
`background_task_cancel()` exprime une demande.
Le worker reste responsable de vérifier régulièrement :
```c
g_cancellable_set_error_if_cancelled()
```
ou :
```c
g_cancellable_is_cancelled()
```
Lannulation ne doit jamais tuer brutalement un thread.
---
## Progression
```c
void background_task_report_progress(
BackgroundTask *task,
double progress,
const char *status_message
);
```
Règles :
- utilisable depuis le worker ;
- protégée par `GMutex` ;
- valeur limitée entre `0.0` et `1.0` ;
- ignorée si la tâche nest pas `RUNNING` ;
- `status_message` est copié ;
- `status_message == NULL` est accepté.
Le ticket #035 pourra lire régulièrement ces valeurs pour mettre à jour le panneau dactivité.
---
## Accesseurs
```c
const char *background_task_get_title(
const BackgroundTask *task
);
BackgroundTaskState background_task_get_state(
const BackgroundTask *task
);
double background_task_get_progress(
const BackgroundTask *task
);
char *background_task_dup_status_message(
const BackgroundTask *task
);
gint64 background_task_get_started_at_us(
const BackgroundTask *task
);
gint64 background_task_get_finished_at_us(
const BackgroundTask *task
);
GError *background_task_dup_error(
const BackgroundTask *task
);
gpointer background_task_get_result(
const BackgroundTask *task
);
```
### Propriété des valeurs
```text
get_title() :
pointeur emprunté
valide pendant la durée de vie de task
titre immuable
dup_status_message() :
nouvelle chaîne
lappelant doit appeler g_free()
dup_error() :
nouvelle copie
lappelant doit appeler g_error_free()
get_result() :
pointeur emprunté
ne doit jamais être libéré par lappelant
```
`get_result()` ne doit être considéré comme valide quaprès létat :
```text
BACKGROUND_TASK_STATE_COMPLETED
```
---
# 7. Structure interne recommandée
Dans :
```text
src/core/background_task.c
```
la structure peut contenir :
```c
struct BackgroundTask
{
gatomicrefcount reference_count;
GMutex mutex;
char *title;
char *status_message;
BackgroundTaskState state;
double progress;
gint64 started_at_us;
gint64 finished_at_us;
GCancellable *cancellable;
gpointer result;
GDestroyNotify result_destroy;
GError *error;
BackgroundTaskCompletionCallback
completion_callback;
gpointer completion_data;
GDestroyNotify completion_data_destroy;
};
```
Les champs mutables doivent être protégés par `mutex`.
Le titre est immuable après construction.
---
# 8. Contexte interne dexécution
Créer une structure privée, par exemple :
```c
typedef struct
{
BackgroundTask *task;
BackgroundTaskWorker worker;
gpointer worker_data;
GDestroyNotify worker_data_destroy;
GDestroyNotify result_destroy;
} BackgroundTaskRunContext;
```
Le `worker_data` doit être détruit lorsque le contexte `GTask` est libéré.
Le pointeur `task` peut être non propriétaire si une référence interne distincte garantit sa durée de vie jusquau callback final.
---
# 9. Fonction exécutée dans le thread
Créer un trampoline privé compatible avec :
```c
GTaskThreadFunc
```
Il doit :
1. récupérer le contexte ;
2. appeler le worker ;
3. vérifier le contrat de retour ;
4. retourner le résultat avec `g_task_return_pointer()` ;
5. retourner lerreur avec `g_task_return_error()` ;
6. créer une erreur `BACKGROUND_TASK_ERROR_WORKER_PROTOCOL` si le worker viole son contrat.
Cas à traiter :
```text
FALSE + error valide :
échec normal
FALSE + error NULL :
erreur de protocole
TRUE + error non NULL :
erreur de protocole
TRUE + result quelconque :
succès
```
Si un résultat a été produit alors que lexécution échoue, il doit être détruit avec `result_destroy`.
---
# 10. Callback interne de fin
Créer un callback privé compatible avec :
```c
GAsyncReadyCallback
```
Il doit :
1. appeler `g_task_propagate_pointer()` ;
2. déterminer le nouvel état ;
3. conserver le résultat ou lerreur ;
4. enregistrer la date de fin ;
5. forcer la progression à `1.0` en cas de succès ;
6. appeler le callback utilisateur ;
7. détruire les données du callback utilisateur ;
8. libérer la référence interne de la tâche.
Détermination de létat :
```text
aucune erreur :
COMPLETED
G_IO_ERROR_CANCELLED :
CANCELLED
autre erreur :
FAILED
```
Le callback utilisateur doit observer un objet déjà entièrement finalisé.
---
# 11. Destruction de la tâche
Quand la dernière référence est libérée :
1. vérifier quaucune référence interne dexécution ne subsiste ;
2. détruire `result` avec `result_destroy` ;
3. libérer `error` ;
4. libérer les chaînes ;
5. libérer `GCancellable` ;
6. libérer les éventuelles données de callback restantes ;
7. nettoyer `GMutex` ;
8. libérer la structure.
Lappel suivant doit être accepté :
```c
background_task_unref(NULL);
```
---
# 12. Sécurité des threads
Les opérations suivantes doivent utiliser `GMutex` :
- lecture et écriture de létat ;
- progression ;
- message de progression ;
- dates ;
- résultat ;
- erreur ;
- accès au cancellable si nécessaire.
Ne jamais conserver le mutex verrouillé pendant :
- lappel du worker ;
- lappel du callback utilisateur ;
- une fonction de destruction fournie par lappelant ;
- un appel potentiellement bloquant.
---
# 13. Tests unitaires
Créer :
```text
tests/test_background_task.c
```
Les tests doivent utiliser un `GMainLoop` pour attendre le callback final.
## Test de construction
Vérifier :
```c
background_task_new(NULL) == NULL
background_task_new("") == NULL
```
Vérifier quune tâche valide commence avec :
```text
PENDING
progression 0.0
date de début 0
date de fin 0
aucune erreur
aucun résultat
```
## Test de succès
Créer un worker qui :
1. signale plusieurs progressions ;
2. renvoie une chaîne allouée ;
3. retourne `TRUE`.
Vérifier dans le callback :
```text
état COMPLETED
progression 1.0
résultat correct
erreur NULL
date de début > 0
date de fin >= date de début
callback appelé une seule fois
```
Vérifier que `result_destroy` est appelé lors du dernier `unref`.
## Test déchec
Créer un worker qui retourne :
```c
FALSE
```
avec une erreur :
```text
G_IO_ERROR_FAILED
```
Vérifier :
```text
état FAILED
résultat NULL
erreur conservée
message conservé
```
## Test de protocole invalide
Créer un worker qui retourne :
```c
FALSE
```
sans renseigner `GError`.
Vérifier :
```text
état FAILED
domaine BACKGROUND_TASK_ERROR
code BACKGROUND_TASK_ERROR_WORKER_PROTOCOL
```
## Test dannulation
Créer un worker qui travaille par petites étapes et vérifie régulièrement le `GCancellable`.
Programmer :
```c
background_task_cancel()
```
depuis le contexte principal avec `g_timeout_add()`.
Vérifier :
```text
état CANCELLED
erreur G_IO_ERROR_CANCELLED
callback final appelé
aucun crash
```
## Test de double démarrage
Démarrer une tâche puis rappeler immédiatement :
```c
background_task_start()
```
Vérifier :
```text
FALSE
BACKGROUND_TASK_ERROR_ALREADY_STARTED
```
La première exécution doit continuer normalement.
## Test des données utilisateur
Vérifier que :
- `worker_data_destroy` est appelé exactement une fois ;
- `completion_data_destroy` est appelé exactement une fois ;
- aucune donnée nest détruite lorsque `background_task_start()` échoue avant transfert de propriété.
## Test du comptage de références
Démarrer une tâche puis libérer immédiatement la référence de lappelant.
Vérifier que :
- la tâche reste vivante jusquau callback ;
- aucun accès mémoire invalide na lieu ;
- la destruction finale intervient après la fin de lexécution.
---
# 14. Makefile
Le code de production est déjà découvert automatiquement si le Makefile utilise :
```make
SRC := $(shell find src -name "*.c")
```
Ajouter toutefois une cible de test dédiée :
```make
TEST_BACKGROUND_TASK := tests/test_background_task
```
La cible doit compiler au minimum :
```text
tests/test_background_task.c
src/core/background_task.c
```
Lier avec les paquets GLib/GIO déjà utilisés par le projet.
Ajouter le binaire aux cibles :
```text
test
clean
```
Sortie attendue :
```text
BackgroundTask : tous les tests sont valides.
```
---
# 15. Hors périmètre
Ce ticket ne doit pas encore ajouter :
- de file de tâches ;
- de limite de concurrence ;
- de panneau GTK ;
- de persistance SQLite ;
- de tâche associée à une enquête ;
- dexécution de commande externe ;
- dadaptateur ExifTool ;
- de recherche DNS ;
- de système de notifications ;
- de reprise après redémarrage ;
- de priorité entre tâches.
Ces fonctions viendront dans les tickets suivants.
---
# 16. Critères dacceptation
- [ ] `BackgroundTask` est opaque.
- [ ] Le module ne dépend pas de GTK.
- [ ] Le module ne dépend pas de SQLite.
- [ ] Le module utilise `GTask`.
- [ ] Le module utilise `GCancellable`.
- [ ] Le module utilise un comptage de références.
- [ ] Le module protège son état avec `GMutex`.
- [ ] Une tâche ne peut être démarrée quune fois.
- [ ] Le worker sexécute dans un thread secondaire.
- [ ] Le callback final revient sur le contexte principal.
- [ ] La progression est comprise entre `0.0` et `1.0`.
- [ ] Lannulation est coopérative.
- [ ] Le résultat est conservé jusquà la destruction.
- [ ] Lerreur est conservée jusquà la destruction.
- [ ] Les dates de début et de fin sont enregistrées.
- [ ] Les données utilisateur sont détruites exactement une fois.
- [ ] La tâche reste vivante pendant son exécution.
- [ ] Aucun callback utilisateur nest appelé sous mutex.
- [ ] Tous les tests unitaires passent.
- [ ] Les anciens tests restent valides.
- [ ] `make` réussit.
- [ ] `make test` réussit.
- [ ] `git diff --check` ne retourne aucune erreur.
---
# 17. Audit attendu
Vérifier labsence de GTK et SQLite :
```bash
rg -n \
'#include <gtk|sqlite3_|Database|Investigation' \
include/core/background_task.h \
src/core/background_task.c
```
Résultat attendu :
```text
aucune sortie
```
Vérifier les primitives asynchrones :
```bash
rg -n \
'GTask|GCancellable|GMutex|g_atomic_ref_count' \
include/core/background_task.h \
src/core/background_task.c
```
Vérifier quaucun thread POSIX brut nest ajouté :
```bash
rg -n \
'pthread_|pthread.h' \
include/core/background_task.h \
src/core/background_task.c
```
Résultat attendu :
```text
aucune sortie
```
Vérifier labsence de sortie forcée :
```bash
rg -n \
'\bexit\s*\(' \
src/core/background_task.c \
tests/test_background_task.c
```
Résultat attendu :
```text
aucune sortie
```
---
# 18. Fichiers concernés
```text
include/core/background_task.h
src/core/background_task.c
tests/test_background_task.c
Makefile
```
Aucune modification de `Application` ou de GTK nest attendue.
---
# 19. Commit attendu
```bash
make clean
make
make test
git diff --check
git status --short
```
```bash
git add \
include/core/background_task.h \
src/core/background_task.c \
tests/test_background_task.c \
Makefile
```
```bash
git diff --cached --stat
git diff --cached
```
```bash
git commit -m "feat(core): add asynchronous background task"
git push
```
---
# Résultat attendu
Après ce ticket, Labfy disposera dune primitive générique pour exécuter proprement les futurs traitements longs :
```text
SHA-256
copie de preuve
ExifTool
dig
RDAP
TLS
HTTP
recherche Web
génération de rapport
```
Le ticket #035 pourra ensuite construire une file de tâches et un panneau dactivité au-dessus de cette abstraction.

View file

@ -1,790 +0,0 @@
# Ticket #035 — File de tâches et panneau dactivité
## Contexte
Le ticket #034 a introduit `BackgroundTask`, une primitive asynchrone capable de :
- lancer un worker dans un thread GLib ;
- suivre son état ;
- signaler sa progression ;
- gérer lannulation ;
- conserver un résultat ou une erreur ;
- revenir sur le contexte principal à la fin ;
- rester vivante grâce au comptage de références.
Cette primitive ne gère cependant pas encore plusieurs tâches ni leur présentation dans linterface.
## Objectif
Créer un gestionnaire opaque :
```c
TaskManager
```
chargé de suivre les tâches actives et terminées, puis ajouter un panneau GTK permettant de les consulter.
Le ticket doit permettre de voir :
```text
Titre
État
Progression
Message courant
Bouton Annuler
```
Le gestionnaire reste indépendant de GTK.
---
# Architecture attendue
Créer :
```text
include/core/task_manager.h
src/core/task_manager.c
tests/test_task_manager.c
include/widgets/task_panel.h
src/widgets/task_panel.c
```
Relations :
```text
Application
├── possède TaskManager
└── transmet TaskManager à MainWindow
MainWindow
└── possède TaskPanel
TaskPanel
└── observe TaskManager
TaskManager
└── conserve des références vers BackgroundTask
```
---
# Phase A — `TaskManager`
## 1. Type opaque
Dans :
```text
include/core/task_manager.h
```
déclarer :
```c
typedef struct TaskManager TaskManager;
```
Le type doit rester indépendant de GTK, SQLite et des enquêtes.
---
## 2. Callback de changement
Définir :
```c
typedef void (*TaskManagerChangedCallback)(
TaskManager *task_manager,
gpointer user_data
);
```
Le callback signale quun changement visible a eu lieu :
- tâche ajoutée ;
- progression modifiée ;
- tâche terminée ;
- tâche retirée ;
- annulation demandée.
Le callback est une notification globale. Il ne transmet pas directement une tâche particulière.
---
## 3. API publique
### Construction
```c
TaskManager *task_manager_new(void);
void task_manager_free(
TaskManager *task_manager
);
```
### Ajouter une tâche
```c
gboolean task_manager_add(
TaskManager *task_manager,
BackgroundTask *task,
GError **error
);
```
Règles :
- `task_manager` devient propriétaire dune référence supplémentaire ;
- lappelant conserve sa propre référence ;
- une même tâche ne peut pas être ajoutée deux fois ;
- une tâche déjà terminée peut être ajoutée, mais elle est immédiatement visible comme terminée ;
- `NULL` est refusé.
### Consulter les tâches
```c
gsize task_manager_get_count(
const TaskManager *task_manager
);
BackgroundTask *task_manager_get_task(
const TaskManager *task_manager,
gsize index
);
```
`task_manager_get_task()` retourne une nouvelle référence que lappelant doit libérer.
### Retirer une tâche
```c
gboolean task_manager_remove(
TaskManager *task_manager,
BackgroundTask *task
);
```
La tâche nest pas annulée automatiquement.
### Supprimer les tâches terminées
```c
gsize task_manager_remove_finished(
TaskManager *task_manager
);
```
États concernés :
```text
COMPLETED
FAILED
CANCELLED
```
### Annuler toutes les tâches actives
```c
void task_manager_cancel_all(
TaskManager *task_manager
);
```
### Callback de changement
```c
void task_manager_set_changed_callback(
TaskManager *task_manager,
TaskManagerChangedCallback callback,
gpointer user_data,
GDestroyNotify user_data_destroy
);
```
Le manager possède `user_data` après lappel.
Remplacer le callback existant doit détruire les anciennes données exactement une fois.
---
## 4. Domaine derreur
Définir :
```c
typedef enum
{
TASK_MANAGER_ERROR_INVALID_ARGUMENT,
TASK_MANAGER_ERROR_ALREADY_ADDED
} TaskManagerError;
```
Puis :
```c
#define TASK_MANAGER_ERROR \
task_manager_error_quark()
GQuark task_manager_error_quark(void);
```
---
## 5. Structure interne recommandée
```c
struct TaskManager
{
GMutex mutex;
GPtrArray *tasks;
TaskManagerChangedCallback changed_callback;
gpointer changed_user_data;
GDestroyNotify changed_user_data_destroy;
};
```
`tasks` doit contenir des références `BackgroundTask *`.
Configurer le `GPtrArray` avec :
```c
background_task_unref
```
comme fonction de destruction.
---
## 6. Notification périodique
`BackgroundTask` ne possède pas encore de callback de progression.
Pour ce ticket, `TaskPanel` peut rafraîchir périodiquement laffichage avec :
```c
g_timeout_add()
```
fréquence recommandée :
```text
200 à 300 ms
```
`TaskManager` notifie immédiatement les changements structurels.
La progression sera relue par le panneau.
Une API dobservation plus fine pourra être ajoutée plus tard si nécessaire.
---
# Phase B — Tests de `TaskManager`
Créer :
```text
tests/test_task_manager.c
```
## Tests minimaux
### Construction
Vérifier :
```text
manager non NULL
compteur initial à zéro
```
### Ajout
Ajouter une tâche et vérifier :
```text
compteur à un
callback déclenché
tâche récupérable
référence indépendante
```
### Doublon
Ajouter deux fois la même tâche :
```text
FALSE
TASK_MANAGER_ERROR_ALREADY_ADDED
compteur inchangé
```
### Retrait
Retirer une tâche :
```text
TRUE
compteur décrémenté
callback déclenché
```
### Retrait inconnu
Retirer une tâche absente :
```text
FALSE
aucun crash
```
### Nettoyage des tâches terminées
Ajouter :
- une tâche en attente ;
- une tâche terminée ;
- une tâche échouée ;
- une tâche annulée.
Vérifier que seules les tâches terminées sont supprimées.
### Annulation globale
Ajouter plusieurs tâches actives et appeler :
```c
task_manager_cancel_all()
```
Vérifier que chaque `GCancellable` reçoit une demande dannulation.
### Durée de vie
Vérifier que :
- le manager conserve ses références ;
- la destruction du manager libère toutes les tâches ;
- le remplacement du callback détruit les anciennes données une seule fois.
---
# Phase C — `TaskPanel`
## 7. Type opaque
Dans :
```text
include/widgets/task_panel.h
```
déclarer :
```c
typedef struct TaskPanel TaskPanel;
```
API :
```c
TaskPanel *task_panel_new(
TaskManager *task_manager
);
GtkWidget *task_panel_get_widget(
const TaskPanel *task_panel
);
void task_panel_refresh(
TaskPanel *task_panel
);
void task_panel_free(
TaskPanel *task_panel
);
```
`TaskPanel` ne devient pas propriétaire de `TaskManager`.
`TaskPanel` doit rester valide tant que le manager existe.
---
## 8. Interface recommandée
Premier rendu simple :
```text
┌──────────────────────────────────────────────────────────┐
│ Activité [ Nettoyer ] │
├──────────────────────────────────────────────────────────┤
│ Extraction des métadonnées │
│ En cours — 45 % │
│ [████████░░░░░░░░░░] [ Annuler ] │
├──────────────────────────────────────────────────────────┤
│ Calcul SHA-256 │
│ Terminé │
│ [████████████████████] │
└──────────────────────────────────────────────────────────┘
```
Widgets GTK possibles :
- `GtkBox` ;
- `GtkLabel` ;
- `GtkProgressBar` ;
- `GtkButton` ;
- `GtkScrolledWindow`.
Ne pas utiliser encore de `GtkListView` si cela complexifie inutilement le ticket.
---
## 9. État visuel
Créer une fonction privée traduisant les états :
```text
PENDING → En attente
RUNNING → En cours
COMPLETED → Terminée
FAILED → Échouée
CANCELLED → Annulée
```
Une tâche en erreur doit afficher son message derreur sous forme courte.
Une tâche en cours doit afficher son message de progression lorsquil existe.
---
## 10. Bouton dannulation
Le bouton `Annuler` doit être visible uniquement pour :
```text
RUNNING
```
Son callback appelle :
```c
background_task_cancel(task);
```
Le bouton ne doit pas retirer la tâche.
---
## 11. Bouton de nettoyage
Ajouter :
```text
Nettoyer
```
Il appelle :
```c
task_manager_remove_finished()
```
Les tâches actives restent visibles.
---
## 12. Rafraîchissement périodique
`TaskPanel` doit enregistrer une source GLib :
```c
g_timeout_add()
```
Elle appelle :
```c
task_panel_refresh()
```
Lors de `task_panel_free()` :
- retirer la source avec `g_source_remove()` ;
- empêcher tout callback après destruction ;
- ne pas détruire `TaskManager`.
---
# Phase D — Intégration GTK
## 13. Ajouter le panneau à `MainWindow`
Modifier :
```text
include/views/main_window.h
src/views/main_window.c
```
Changer la construction :
```c
MainWindow *main_window_new(
GtkApplication *application,
TaskManager *task_manager
);
```
`MainWindow` doit créer :
```c
TaskPanel *task_panel;
```
Le panneau peut être placé :
- sous la zone de travail ;
- dans un volet inférieur ;
- ou temporairement dans une colonne latérale secondaire.
Pour ce ticket, un volet inférieur sous `GtkPaned` est acceptable.
---
## 14. Ajouter `TaskManager` à `Application`
Dans la structure privée :
```c
TaskManager *task_manager;
```
Dans `application_new()` :
```c
application->task_manager =
task_manager_new();
```
En cas déchec, nettoyer lapplication.
Dans `application_on_activate()` :
```c
application->main_window = main_window_new(
gtk_application,
application->task_manager
);
```
Dans `application_free()` :
1. fermer/détruire `MainWindow` ;
2. libérer `TaskManager` après le panneau ;
3. poursuivre le nettoyage existant.
Lordre doit éviter que `TaskPanel` lise un manager déjà détruit.
---
# Phase E — Tâche de démonstration
## 15. Ajouter temporairement une tâche de test
Pour valider linterface, ajouter un bouton temporaire :
```text
Tâche de test
```
Il lance une `BackgroundTask` qui :
- dure environ deux secondes ;
- progresse de 0 à 100 % ;
- accepte lannulation ;
- retourne un résultat simple.
Cette tâche doit être ajoutée au `TaskManager`.
Le bouton pourra être supprimé lorsque le premier véritable traitement asynchrone sera disponible.
Le code de démonstration doit rester clairement identifié :
```c
/* Temporary demonstration task for ticket #035. */
```
---
# Hors périmètre
Ne pas ajouter encore :
- de limite de concurrence ;
- de priorité ;
- de persistance SQLite ;
- de reprise après redémarrage ;
- dhistorique permanent ;
- dexécution de commandes ;
- de recherche DNS ;
- dExifTool ;
- de notifications système ;
- de tri avancé ;
- de pagination.
---
# Critères dacceptation
- [ ] `TaskManager` est opaque.
- [ ] `TaskManager` ne dépend pas de GTK.
- [ ] `TaskManager` protège sa collection avec `GMutex`.
- [ ] Le manager conserve une référence par tâche.
- [ ] Une tâche ne peut pas être ajoutée deux fois.
- [ ] Une tâche peut être retirée.
- [ ] Les tâches terminées peuvent être nettoyées.
- [ ] Toutes les tâches actives peuvent être annulées.
- [ ] Le callback de changement fonctionne.
- [ ] `TaskPanel` affiche toutes les tâches.
- [ ] La progression est visible.
- [ ] Létat est lisible.
- [ ] Une tâche active peut être annulée.
- [ ] Les tâches terminées peuvent être supprimées.
- [ ] Le rafraîchissement périodique est correctement détruit.
- [ ] `MainWindow` ne devient pas propriétaire du manager.
- [ ] Lordre de destruction ne provoque aucun crash.
- [ ] La tâche de démonstration fonctionne.
- [ ] `make` réussit.
- [ ] `make test` réussit.
- [ ] `git diff --check` ne retourne aucune erreur.
---
# Audit attendu
Vérifier lindépendance du manager :
```bash
rg -n \
'#include <gtk|sqlite3_|Database|Investigation' \
include/core/task_manager.h \
src/core/task_manager.c
```
Résultat attendu :
```text
aucune sortie
```
Vérifier les références :
```bash
rg -n \
'background_task_ref|background_task_unref' \
src/core/task_manager.c
```
Vérifier le rafraîchissement GTK :
```bash
rg -n \
'g_timeout_add|g_source_remove|task_panel_refresh' \
src/widgets/task_panel.c
```
Vérifier labsence de threads bruts :
```bash
rg -n \
'pthread_|pthread.h' \
include/core/task_manager.h \
src/core/task_manager.c \
src/widgets/task_panel.c
```
Résultat attendu :
```text
aucune sortie
```
---
# Fichiers concernés
```text
include/core/task_manager.h
src/core/task_manager.c
tests/test_task_manager.c
include/widgets/task_panel.h
src/widgets/task_panel.c
include/views/main_window.h
src/views/main_window.c
src/core/application.c
Makefile
```
---
# Commit attendu
```bash
make clean
make
make test
git diff --check
git status --short
```
```bash
git add \
include/core/task_manager.h \
src/core/task_manager.c \
tests/test_task_manager.c \
include/widgets/task_panel.h \
src/widgets/task_panel.c \
include/views/main_window.h \
src/views/main_window.c \
src/core/application.c \
Makefile
```
```bash
git diff --cached --stat
git diff --cached
```
```bash
git commit -m "feat(ui): add task manager and activity panel"
git push
```
---
# Résultat attendu
Après ce ticket, Labfy possédera une infrastructure visible pour tous les futurs traitements longs :
```text
Utilisateur
Action GTK
BackgroundTask
TaskManager
TaskPanel
```
Le ticket suivant pourra ajouter le registre des dépendances et lancer les premiers contrôles doutils externes sans bloquer linterface.

View file

@ -1,825 +0,0 @@
# TICKET-036 — Registre des outils et dépendances externes
## Statut
À faire
## Priorité
Haute
## Objectif
Créer un module central capable de représenter les outils externes utilisés par Labfy Investigation et de déterminer sils sont disponibles sur la machine.
Ce registre deviendra la source unique permettant à lapplication de savoir :
- quel outil est demandé par une fonctionnalité ;
- si cet outil est obligatoire ou optionnel ;
- sous quel nom il est recherché dans le `PATH` ;
- où se trouve son exécutable ;
- si sa version est connue ;
- pourquoi une fonctionnalité doit être désactivée lorsquune dépendance manque.
Le module doit rester indépendant de GTK.
---
## Contexte
Labfy Investigation utilisera progressivement plusieurs outils externes :
- outils DNS ;
- outils RDAP et WHOIS ;
- outils TLS ;
- outils HTTP ;
- outils de traitement de métadonnées ;
- outils de conversion ou danalyse ;
- adaptateurs spécialisés OSINT.
Ces outils ne seront pas tous installés sur chaque machine.
La cible Ubuntu de la gendarmerie peut également disposer :
- de dépôts limités ;
- dune liste de paquets spécifique ;
- doutils installés dans des chemins non standards ;
- de versions différentes de celles présentes sur Arch Linux.
Le code métier ne doit donc jamais supposer quun exécutable est disponible.
---
## Périmètre
Ce ticket doit fournir :
1. un registre opaque `ToolRegistry` ;
2. une représentation opaque ou privée dun outil enregistré ;
3. lenregistrement dun outil à partir de métadonnées stables ;
4. la détection de lexécutable dans le `PATH` ;
5. la mémorisation du chemin détecté ;
6. la distinction entre dépendance obligatoire et optionnelle ;
7. la recherche dun outil par identifiant ;
8. lénumération des outils enregistrés ;
9. la détection des dépendances obligatoires manquantes ;
10. un champ permettant de mémoriser une version lorsquelle sera détectée ;
11. des tests unitaires sans dépendre des outils réellement installés sur la machine.
---
## Hors périmètre
Ce ticket ne doit pas encore :
- créer dinterface GTK ;
- lancer une commande OSINT réelle ;
- interpréter la sortie de `dig`, `whois`, `curl` ou `openssl` ;
- exécuter automatiquement `--version` ;
- installer des paquets ;
- modifier les dépôts du système ;
- télécharger un outil manquant ;
- utiliser une commande construite sous forme de chaîne shell ;
- contenir une liste définitive des outils OSINT de lapplication.
Lexécution contrôlée des programmes externes sera traitée dans un ticket dédié.
---
## Fichiers attendus
```text
include/core/tool_registry.h
src/core/tool_registry.c
tests/test_tool_registry.c
```
Le `Makefile` devra être mis à jour pour compiler le module et son test.
---
## Contraintes générales
- Standard C17.
- Compilation avec :
```text
-Wall -Wextra -Wpedantic -Werror
```
- Utiliser GLib.
- Respecter les conventions du projet :
- fichiers en `snake_case` ;
- fonctions préfixées par `tool_registry_` ou `tool_info_` ;
- noms de variables explicites ;
- structure publique opaque ;
- aucune variable globale mutable ;
- aucune dépendance à GTK.
- Ne jamais construire une commande shell.
- Ne jamais appeler `system()`, `popen()` ou équivalent.
- Ne pas coder en dur un chemin comme `/usr/bin/dig`.
---
## Modèle de données
### ToolRequirement
nvim.lsp.b_248_save BufWritePost
<buffer=248>
<Lua 3597: /usr/share/nvim/runtime/lua/vim/lsp.lua:943> [vim.lsp: textDocument/didSave handler]
Modifié la dernière fois dans ~/.dotfiles/nvim/.config/nvim/init.lua (run Nvim with -V1 for more details
)
nvim.lsp.b_249_save BufWritePost
<buffer=249>
<Lua 3645: /usr/share/nvim/runtime/lua/vim/lsp.lua:943> [vim.lsp: textDocument/didSave handler]
Modifié la dernière fois dans ~/.dotfiles/nvim/.config/nvim/init.lua (run Nvim with -V1 for more details
)
nvim.lsp.b_252_save BufWritePost
<buffer=252>
<Lua 3681: /usr/share/nvim/runtime/lua/vim/lsp.lua:943> [vim.lsp: textDocument/didSave handler]
Modifié la dernière fois dans ~/.dotfiles/nvim/.config/nvim/init.lua (run Nvim with -V1 for more details
)
nvim.lsp.b_255_save BufWritePost
<buffer=255>
<Lua 2223: /usr/share/nvim/runtime/lua/vim/lsp.lua:943> [vim.lsp: textDocument/didSave handler]
Modifié la dernière fois dans ~/.dotfiles/nvim/.config/nvim/init.lua (run Nvim with -V1 for more details
)
nvim.lsp.b_259_save BufWritePost
<buffer=259>
<Lua 3935: /usr/share/nvim/runtime/lua/vim/lsp.lua:943> [vim.lsp: textDocument/didSave handler]
Modifié la dernière fois dans ~/.dotfiles/nvim/.config/nvim/init.lua (run Nvim with -V1 for more details
)
nvim.lsp.b_262_save BufWritePost
<buffer=262>
<Lua 3099: /usr/share/nvim/runtime/lua/vim/lsp.lua:943> [vim.lsp: textDocument/didSave handler]
Modifié la dernière fois dans ~/.dotfiles/nvim/.config/nvim/init.lua (run Nvim with -V1 for more details
)
nvim.lsp.b_273_save BufWritePost
<buffer=273>
<Lua 4130: /usr/share/nv
Créer une énumération représentant limportance dune dépendance :
```c
typedef enum
{
TOOL_REQUIREMENT_OPTIONAL,
TOOL_REQUIREMENT_REQUIRED
} ToolRequirement;
```
### ToolAvailability
Créer une énumération représentant létat de détection :
```c
typedef enum
{
TOOL_AVAILABILITY_UNKNOWN,
TOOL_AVAILABILITY_AVAILABLE,
TOOL_AVAILABILITY_MISSING
} ToolAvailability;
```
### ToolRegistry
La structure doit rester opaque :
```c
typedef struct ToolRegistry ToolRegistry;
```
### ToolInfo
La représentation dun outil doit également être opaque ou exposée uniquement en lecture :
```c
typedef struct ToolInfo ToolInfo;
```
Un outil enregistré doit au minimum contenir :
```text
identifier
display_name
executable_name
requirement
availability
resolved_path
detected_version
```
### Règles des champs
#### identifier
Identifiant interne stable et unique.
Exemples :
```text
dns.dig
network.curl
tls.openssl
metadata.exiftool
```
Il ne doit pas dépendre du nom du paquet de la distribution.
#### display_name
Nom compréhensible par lutilisateur.
Exemples :
```text
dig
cURL
OpenSSL
ExifTool
```
#### executable_name
Nom recherché dans le `PATH`.
Exemples :
```text
dig
curl
openssl
exiftool
```
#### resolved_path
Chemin absolu détecté.
Exemple :
```text
/usr/bin/dig
```
Ce champ vaut `NULL` lorsque loutil est absent ou na pas encore été vérifié.
#### detected_version
Version connue de loutil.
Ce champ peut rester `NULL` dans ce ticket. Le registre doit néanmoins pouvoir la mémoriser pour les tickets suivants.
---
## Domaine derreur
Créer un domaine derreur dédié :
```c
#define TOOL_REGISTRY_ERROR \
tool_registry_error_quark()
```
Erreurs minimales :
```c
typedef enum
{
TOOL_REGISTRY_ERROR_INVALID_ARGUMENT,
TOOL_REGISTRY_ERROR_DUPLICATE_IDENTIFIER,
TOOL_REGISTRY_ERROR_NOT_FOUND
} ToolRegistryError;
```
Fonction attendue :
```c
GQuark tool_registry_error_quark(void);
```
---
## API publique attendue
LAPI exacte peut être ajustée si une meilleure solution est justifiée, mais elle doit couvrir les opérations suivantes.
### Cycle de vie
```c
ToolRegistry *tool_registry_new(void);
void tool_registry_free(
ToolRegistry *tool_registry
);
```
### Enregistrement
```c
gboolean tool_registry_register(
ToolRegistry *tool_registry,
const char *identifier,
const char *display_name,
const char *executable_name,
ToolRequirement requirement,
GError **error
);
```
Règles :
- tous les textes doivent être non `NULL` et non vides ;
- lidentifiant doit être unique ;
- le registre doit dupliquer les chaînes reçues ;
- loutil nouvellement enregistré commence avec :
- disponibilité `UNKNOWN` ;
- chemin `NULL` ;
- version `NULL`.
### Détection
```c
gboolean tool_registry_refresh(
ToolRegistry *tool_registry,
GError **error
);
```
Cette fonction doit vérifier tous les outils enregistrés.
Pour la recherche dans le `PATH`, utiliser lAPI GLib adaptée, par exemple :
```c
g_find_program_in_path()
```
Règles :
- si lexécutable est trouvé :
- état `AVAILABLE` ;
- chemin détecté mémorisé ;
- sil nest pas trouvé :
- état `MISSING` ;
- ancien chemin supprimé ;
- un second rafraîchissement doit remplacer proprement les anciennes valeurs ;
- aucune fuite mémoire ne doit être produite.
### Consultation
```c
gsize tool_registry_get_count(
const ToolRegistry *tool_registry
);
```
```c
const ToolInfo *tool_registry_get_tool(
const ToolRegistry *tool_registry,
gsize index
);
```
```c
const ToolInfo *tool_registry_find(
const ToolRegistry *tool_registry,
const char *identifier
);
```
Le comportement de propriété doit être documenté clairement.
Pour une première version, les pointeurs retournés peuvent être empruntés et rester valides jusquà la prochaine modification du registre ou jusquà sa destruction.
### Version détectée
```c
gboolean tool_registry_set_version(
ToolRegistry *tool_registry,
const char *identifier,
const char *detected_version,
GError **error
);
```
Règles :
- loutil doit exister ;
- la version peut être remplacée ;
- une chaîne vide ou `NULL` efface la version connue ;
- le registre doit dupliquer la chaîne.
Cette fonction sera utilisée plus tard par le module chargé dinterroger les exécutables.
### État global
```c
gboolean tool_registry_has_missing_required_tools(
const ToolRegistry *tool_registry
);
```
Cette fonction retourne `TRUE` lorsquau moins un outil obligatoire est dans létat `MISSING`.
Un outil obligatoire encore dans létat `UNKNOWN` ne doit pas être considéré comme disponible.
Une seconde fonction est recommandée :
```c
gboolean tool_registry_all_required_tools_available(
const ToolRegistry *tool_registry
);
```
Elle ne retourne `TRUE` que lorsque tous les outils obligatoires sont explicitement `AVAILABLE`.
---
## Accesseurs de ToolInfo
Prévoir au minimum :
```c
const char *tool_info_get_identifier(
const ToolInfo *tool_info
);
```
```c
const char *tool_info_get_display_name(
const ToolInfo *tool_info
);
```
```c
const char *tool_info_get_executable_name(
const ToolInfo *tool_info
);
```
```c
ToolRequirement tool_info_get_requirement(
const ToolInfo *tool_info
);
```
```c
ToolAvailability tool_info_get_availability(
const ToolInfo *tool_info
);
```
```c
const char *tool_info_get_resolved_path(
const ToolInfo *tool_info
);
```
```c
const char *tool_info_get_detected_version(
const ToolInfo *tool_info
);
```
Les chaînes retournées sont empruntées et ne doivent jamais être libérées par lappelant.
---
## Implémentation interne recommandée
Le registre peut utiliser :
```c
GPtrArray
```
pour conserver lordre denregistrement.
Une recherche linéaire par identifiant est acceptable pour cette première version, car le nombre doutils restera faible.
Une table de hachage pourra être ajoutée plus tard seulement si elle devient utile.
Le registre devient propriétaire de tous les outils enregistrés.
Chaque outil doit libérer :
- son identifiant ;
- son nom affiché ;
- son nom dexécutable ;
- son chemin résolu ;
- sa version ;
- sa structure.
---
## Détection testable
Les tests ne doivent pas dépendre de la présence réelle de `dig`, `curl`, `openssl` ou dautres outils.
Le test doit créer un répertoire temporaire contenant un faux exécutable.
Exemple de stratégie :
1. créer un dossier temporaire avec GLib ;
2. créer un fichier nommé `fake_osint_tool` ;
3. lui donner les permissions dexécution ;
4. sauvegarder la valeur actuelle de `PATH` ;
5. placer temporairement le dossier au début de `PATH` ;
6. enregistrer `fake_osint_tool` ;
7. appeler `tool_registry_refresh()` ;
8. vérifier que loutil est disponible ;
9. restaurer impérativement le `PATH` initial ;
10. supprimer les fichiers temporaires.
Le test doit restaurer lenvironnement même en cas déchec dune assertion intermédiaire.
Une fonction de préparation et une fonction de nettoyage de fixture sont recommandées.
---
## Tests unitaires obligatoires
### 1. Construction vide
Vérifier :
- création du registre ;
- nombre doutils égal à zéro ;
- recherche dun identifiant absent ;
- destruction sans erreur.
### 2. Enregistrement valide
Vérifier :
- nombre doutils ;
- conservation de lordre ;
- contenu des accesseurs ;
- état initial `UNKNOWN` ;
- chemin initial `NULL` ;
- version initiale `NULL`.
### 3. Duplication des chaînes
Créer des chaînes dynamiques, enregistrer loutil, puis libérer les chaînes originales.
Le registre doit conserver des valeurs valides.
### 4. Identifiant en double
Enregistrer deux outils avec le même identifiant.
Le second enregistrement doit échouer avec :
```text
TOOL_REGISTRY_ERROR_DUPLICATE_IDENTIFIER
```
Le premier outil ne doit pas être modifié.
### 5. Arguments invalides
Tester au minimum :
- registre `NULL` ;
- identifiant `NULL` ;
- identifiant vide ;
- nom affiché vide ;
- nom dexécutable vide ;
- `GError` déjà initialisé si le projet applique cette règle.
### 6. Recherche par identifiant
Vérifier :
- outil existant ;
- outil absent ;
- identifiant `NULL` ;
- identifiant vide.
### 7. Outil disponible
Avec un faux exécutable placé temporairement dans le `PATH`, vérifier :
- état `AVAILABLE` ;
- chemin absolu non `NULL` ;
- chemin correspondant au faux exécutable.
### 8. Outil absent
Avec un nom improbable, vérifier :
- état `MISSING` ;
- chemin `NULL`.
Exemple :
```text
labfy_tool_that_must_not_exist_036
```
### 9. Rafraîchissement successif
Vérifier quun outil peut passer :
```text
MISSING → AVAILABLE
```
puis :
```text
AVAILABLE → MISSING
```
après modification contrôlée du `PATH`.
### 10. Version
Vérifier :
- ajout dune version ;
- remplacement dune version ;
- effacement avec `NULL` ;
- erreur sur identifiant inconnu.
### 11. Dépendances obligatoires
Tester les combinaisons :
```text
obligatoire + disponible
obligatoire + absent
obligatoire + inconnu
optionnel + absent
```
Une dépendance optionnelle absente ne doit pas rendre létat global invalide.
### 12. Destruction complète
Exécuter les tests avec les outils de vérification mémoire du projet.
---
## Noms de tests suggérés
```text
/tool_registry/construction
/tool_registry/register
/tool_registry/string_ownership
/tool_registry/duplicate_identifier
/tool_registry/invalid_arguments
/tool_registry/find
/tool_registry/available_tool
/tool_registry/missing_tool
/tool_registry/successive_refresh
/tool_registry/version
/tool_registry/required_tools
```
---
## Makefile
Ajouter le nouveau module à la compilation principale.
Ajouter une cible :
```text
test_tool_registry
```
La cible doit compiler au minimum :
```text
tests/test_tool_registry.c
src/core/tool_registry.c
```
avec GLib.
Ajouter le test à la cible globale de tests du projet.
---
## Vérifications manuelles
Commandes attendues :
```bash
make clean
make
make test_tool_registry
./test_tool_registry
```
Puis, si une cible globale existe :
```bash
make test
```
Aucun warning de compilation ne doit être toléré.
---
## Vérification mémoire recommandée
Selon les outils déjà utilisés dans le projet :
```bash
G_DEBUG=gc-friendly \
G_SLICE=always-malloc \
valgrind \
--leak-check=full \
--show-leak-kinds=all \
./test_tool_registry
```
Le test ne doit laisser aucune allocation définitivement perdue provenant du registre.
Les allocations internes conservées par GLib doivent être interprétées avec prudence.
---
## Critères dacceptation
Le ticket est validé lorsque :
- le projet compile avec les options strictes ;
- le registre est opaque ;
- plusieurs outils peuvent être enregistrés ;
- les identifiants en double sont refusés ;
- les chaînes sont copiées ;
- les outils sont recherchés dans le `PATH` ;
- le chemin détecté est mémorisé ;
- un outil absent est marqué `MISSING` ;
- un rafraîchissement remplace correctement létat précédent ;
- une version peut être stockée et effacée ;
- les dépendances obligatoires manquantes sont détectées ;
- les outils optionnels absents ne bloquent pas létat global ;
- les tests ne dépendent pas des outils réellement installés ;
- le `PATH` original est restauré après chaque test ;
- aucune dépendance GTK nest introduite ;
- aucun appel shell nest utilisé ;
- tous les tests passent ;
- aucune erreur mémoire liée au module nest détectée.
---
## Résultat attendu
À la fin de ce ticket, le code suivant doit être conceptuellement possible :
```c
ToolRegistry *tool_registry = NULL;
const ToolInfo *dig_tool = NULL;
GError *error = NULL;
tool_registry = tool_registry_new();
tool_registry_register(
tool_registry,
"dns.dig",
"dig",
"dig",
TOOL_REQUIREMENT_OPTIONAL,
&error
);
tool_registry_refresh(
tool_registry,
&error
);
dig_tool = tool_registry_find(
tool_registry,
"dns.dig"
);
if (tool_info_get_availability(dig_tool) ==
TOOL_AVAILABILITY_AVAILABLE)
{
g_print(
"dig détecté dans : %s\n",
tool_info_get_resolved_path(dig_tool)
);
}
```
Cet exemple décrit le comportement attendu. Il ne constitue pas une obligation dorganisation interne.
---
## Suite prévue
Après validation de ce ticket :
1. catalogue initial des outils utilisés par Labfy Investigation ;
2. abstraction contrôlée autour de `GSubprocess` ;
3. interrogation des versions ;
4. adaptateurs doutils OSINT ;
5. affichage des dépendances dans linterface ;
6. intégration avec `BackgroundTask` et `TaskManager` ;
7. conservation des sorties brutes et de leur provenance ;
8. prise en compte du paquet Ubuntu et du script dinstallation.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,515 +0,0 @@
# TICKET-039 — Catalogue initial des outils externes et détection de leurs versions
## Statut
À faire
## Priorité
Haute
## Objectif
Créer le catalogue initial des outils externes utilisés par Labfy Investigation et fournir une détection fiable de leur version.
Ce ticket complète les modules déjà validés :
```text
ToolRegistry
ToolProcess
ToolTask
```
Le catalogue décrit les outils connus par lapplication. Le registre décrit leur état sur la machine courante.
```text
ToolCatalog
description statique des outils connus
ToolRegistry
disponibilité, chemin résolu, version détectée
ToolProcess
exécution sécurisée de la commande de version
```
## Contexte
`ToolRegistry` permet déjà denregistrer un outil, de rechercher son exécutable dans le `PATH`, de conserver son chemin résolu et sa version détectée, et de distinguer les états `UNKNOWN`, `AVAILABLE` et `MISSING`.
Il manque une source centrale définissant :
- les outils reconnus par lapplication ;
- leur identifiant interne ;
- leur nom affiché ;
- leur exécutable ;
- leur importance ;
- les arguments permettant de demander leur version ;
- la manière de normaliser la sortie obtenue.
Sans catalogue central, chaque fonctionnalité finirait par enregistrer elle-même ses dépendances.
## Modules attendus
```text
include/core/tool_catalog.h
src/core/tool_catalog.c
tests/test_tool_catalog.c
```
Le ticket ne doit pas modifier lAPI publique de `ToolRegistry`, sauf nécessité démontrée pendant limplémentation.
## Responsabilités de ToolCatalog
Le module doit :
1. conserver une liste statique et immuable des outils connus ;
2. garantir lunicité de leurs identifiants ;
3. exposer leurs informations descriptives ;
4. enregistrer lensemble du catalogue dans un `ToolRegistry` ;
5. définir les arguments de détection de version ;
6. exécuter ces arguments avec `ToolProcess` ;
7. normaliser la sortie de version ;
8. retourner la version détectée sans modifier directement le registre ;
9. gérer lannulation ;
10. rester indépendant de GTK.
Le module ne doit pas installer de dépendance, modifier le `PATH`, utiliser un shell, analyser un résultat OSINT ni modifier linterface graphique.
## Catalogue initial
| Identifiant interne | Nom affiché | Exécutable | Importance | Arguments de version |
|---|---|---|---|---|
| `dns.dig` | `dig` | `dig` | optionnel | `-v` |
| `dns.host` | `host` | `host` | optionnel | `-V` |
| `network.whois` | `whois` | `whois` | optionnel | `--version` |
| `http.curl` | `curl` | `curl` | optionnel | `--version` |
| `tls.openssl` | `OpenSSL` | `openssl` | optionnel | `version` |
Tous ces outils sont optionnels au niveau de lapplication entière.
Ne pas ajouter encore `nslookup`, `traceroute`, `jq`, `file`, `exiftool`, `nmap`, `subfinder`, `amass`, les outils Python ou les outils installés depuis GitHub.
## Structure ToolCatalogEntry
Créer une structure opaque :
```c
typedef struct ToolCatalogEntry ToolCatalogEntry;
```
Elle conserve au minimum :
```text
identifier
display_name
executable_name
requirement
version_arguments
```
Les données du catalogue sont statiques et immuables. Elles ne doivent jamais être libérées par lappelant.
## Domaine derreur
```c
#define TOOL_CATALOG_ERROR \
tool_catalog_error_quark()
```
```c
typedef enum
{
TOOL_CATALOG_ERROR_INVALID_ARGUMENT,
TOOL_CATALOG_ERROR_ENTRY_NOT_FOUND,
TOOL_CATALOG_ERROR_REGISTRATION,
TOOL_CATALOG_ERROR_TOOL_NOT_REGISTERED,
TOOL_CATALOG_ERROR_TOOL_NOT_CHECKED,
TOOL_CATALOG_ERROR_TOOL_MISSING,
TOOL_CATALOG_ERROR_INVALID_TOOL_STATE,
TOOL_CATALOG_ERROR_PROCESS,
TOOL_CATALOG_ERROR_VERSION_COMMAND,
TOOL_CATALOG_ERROR_VERSION_OUTPUT,
TOOL_CATALOG_ERROR_CANCELLED
} ToolCatalogError;
```
```c
GQuark tool_catalog_error_quark(void);
```
## API publique proposée
### Consultation
```c
gsize tool_catalog_get_count(void);
```
```c
const ToolCatalogEntry *tool_catalog_get_entry(
gsize index
);
```
```c
const ToolCatalogEntry *tool_catalog_find(
const char *identifier
);
```
### Accesseurs
```c
const char *tool_catalog_entry_get_identifier(
const ToolCatalogEntry *entry
);
```
```c
const char *tool_catalog_entry_get_display_name(
const ToolCatalogEntry *entry
);
```
```c
const char *tool_catalog_entry_get_executable_name(
const ToolCatalogEntry *entry
);
```
```c
ToolRequirement tool_catalog_entry_get_requirement(
const ToolCatalogEntry *entry
);
```
```c
gsize tool_catalog_entry_get_version_argument_count(
const ToolCatalogEntry *entry
);
```
```c
const char *tool_catalog_entry_get_version_argument(
const ToolCatalogEntry *entry,
gsize index
);
```
### Enregistrement
```c
gboolean tool_catalog_register_defaults(
ToolRegistry *tool_registry,
GError **error
);
```
### Détection dune version
```c
gboolean tool_catalog_detect_version(
const ToolRegistry *tool_registry,
const char *identifier,
GCancellable *cancellable,
char **out_version,
GError **error
);
```
`out_version` reçoit une chaîne nouvellement allouée, à libérer avec `g_free()`.
La fonction ne doit pas appeler `tool_registry_set_version()`.
## Règles denregistrement
`tool_catalog_register_defaults()` doit vérifier avant toute insertion quaucun identifiant du catalogue nexiste déjà dans le registre.
En cas de doublon :
- aucune entrée ne doit être ajoutée ;
- la fonction retourne `FALSE` ;
- lerreur vaut `TOOL_CATALOG_ERROR_REGISTRATION`.
Cette prévalidation évite une insertion partielle, car `ToolRegistry` ne fournit pas de suppression.
## Validation de la détection
La fonction doit refuser :
- un registre `NULL` ;
- un identifiant `NULL` ou vide ;
- un `out_version` égal à `NULL` ;
- un `out_version` pointant déjà vers une chaîne ;
- un `GError` déjà initialisé.
Au début dun appel valide :
```c
*out_version = NULL;
```
Cas derreur :
```text
entrée absente du catalogue → TOOL_CATALOG_ERROR_ENTRY_NOT_FOUND
outil absent du registre → TOOL_CATALOG_ERROR_TOOL_NOT_REGISTERED
état UNKNOWN → TOOL_CATALOG_ERROR_TOOL_NOT_CHECKED
état MISSING → TOOL_CATALOG_ERROR_TOOL_MISSING
AVAILABLE sans chemin résolu → TOOL_CATALOG_ERROR_INVALID_TOOL_STATE
```
## Exécution de la commande de version
Utiliser exclusivement :
```c
tool_process_run()
```
avec :
```text
executable_path = chemin résolu du ToolRegistry
arguments = arguments statiques du ToolCatalogEntry
working_directory = NULL
cancellable = paramètre reçu
```
Interdictions :
- `/bin/sh -c` ;
- `system()` ;
- `popen()` ;
- concaténation dune ligne de commande ;
- redirections interprétées ;
- ajout ou transformation des arguments.
## Annulation
Si `ToolProcess` retourne `TOOL_PROCESS_ERROR_CANCELLED`, la fonction doit produire :
```text
G_IO_ERROR
G_IO_ERROR_CANCELLED
```
Aucune version partielle ne doit être retournée.
## Code de sortie
Pour une commande de version, un code de sortie non nul est un échec :
```text
TOOL_CATALOG_ERROR_VERSION_COMMAND
```
Une terminaison par signal produit la même catégorie derreur.
La sortie ne doit pas être interprétée lorsque le processus na pas terminé normalement avec le code zéro.
## Sélection de la sortie
Règle générique :
1. prendre la première ligne non vide de stdout ;
2. si stdout ne contient rien dexploitable, essayer stderr ;
3. si aucun flux ne contient de texte exploitable, échouer.
Cette règle évite une logique spéciale par outil pendant ce premier ticket.
## Normalisation
Le catalogue ne doit pas extraire seulement un numéro sémantique.
La version conservée est la première ligne descriptive exploitable, par exemple :
```text
DiG 9.20.8
curl 8.14.1 (x86_64-pc-linux-gnu)
OpenSSL 3.5.1 1 Jul 2025
```
Algorithme :
1. obtenir les octets du flux ;
2. inspecter au maximum 4096 octets par flux ;
3. convertir les octets invalides avec `g_utf8_make_valid()` ;
4. séparer les lignes ;
5. ignorer les lignes vides ;
6. choisir la première ligne non vide ;
7. retirer espaces et tabulations en début et fin ;
8. retirer `\r` et `\n` ;
9. refuser un résultat vide ;
10. retourner une nouvelle chaîne.
## Propriété des données
Les entrées et chaînes du catalogue sont empruntées et statiques.
La chaîne placée dans `*out_version` appartient à lappelant et doit être libérée avec `g_free()`.
`ToolCatalog` ne devient jamais propriétaire du registre.
## Contraintes de thread
Le catalogue statique est immuable.
`tool_catalog_detect_version()` :
- ne modifie pas le catalogue ;
- ne modifie pas le registre ;
- copie le chemin nécessaire avant lexécution ;
- ne conserve aucun pointeur vers `ToolInfo` après la préparation.
Le stockage avec `tool_registry_set_version()` reste la responsabilité du propriétaire du registre, idéalement dans le thread principal.
## Tests unitaires obligatoires
Les tests utilisent uniquement de faux exécutables temporaires.
Ils ne doivent pas dépendre des outils réellement installés.
### Tests du catalogue
1. nombre exact dentrées ;
2. contenu exact des cinq entrées ;
3. index invalide ;
4. recherche par identifiant ;
5. identifiant inconnu ;
6. unicité des identifiants ;
7. arguments de version ;
8. enregistrement complet dans un registre vide ;
9. état initial `UNKNOWN` ;
10. prévalidation dun doublon sans insertion partielle.
### Tests de détection
11. arguments invalides ;
12. entrée inconnue ;
13. outil non enregistré ;
14. outil non vérifié ;
15. outil absent ;
16. version sur stdout ;
17. version sur stderr ;
18. première ligne non vide ;
19. normalisation des espaces et fins de ligne ;
20. sortie non UTF-8 ;
21. sortie vide ;
22. code de sortie non nul ;
23. terminaison par signal ;
24. annulation ;
25. indépendance du registre ;
26. plusieurs détections successives.
## Noms de tests suggérés
```text
/tool_catalog/entries
/tool_catalog/invalid_index
/tool_catalog/find
/tool_catalog/unique_entries
/tool_catalog/register_defaults
/tool_catalog/register_duplicate
/tool_catalog/detect_invalid_arguments
/tool_catalog/detect_unknown_entry
/tool_catalog/detect_unregistered_tool
/tool_catalog/detect_unchecked_tool
/tool_catalog/detect_missing_tool
/tool_catalog/version_stdout
/tool_catalog/version_stderr
/tool_catalog/version_first_nonempty_line
/tool_catalog/version_normalization
/tool_catalog/version_non_utf8
/tool_catalog/version_empty
/tool_catalog/version_nonzero_exit
/tool_catalog/version_signaled
/tool_catalog/version_cancelled
/tool_catalog/version_registry_independence
/tool_catalog/version_successive_runs
```
## Makefile
```make
TEST_TOOL_CATALOG := tests/test_tool_catalog
```
```make
$(TEST_TOOL_CATALOG): \
tests/test_tool_catalog.c \
src/core/tool_catalog.c \
src/core/tool_registry.c \
src/core/tool_process.c
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
```
Ajouter le binaire à `make test` et `make clean`.
## Vérifications
```bash
make clean
make
make tests/test_tool_catalog
./tests/test_tool_catalog
make test
git diff --check
```
## Vérification mémoire
```bash
G_DEBUG=gc-friendly \
G_SLICE=always-malloc \
valgrind \
--leak-check=full \
--show-leak-kinds=all \
./tests/test_tool_catalog
```
## Critères dacceptation
Le ticket est validé lorsque :
- le catalogue contient exactement les cinq outils prévus ;
- toutes les entrées sont immuables ;
- tous les identifiants sont uniques ;
- le catalogue senregistre dans un registre vide ;
- un doublon empêche toute insertion partielle ;
- la détection utilise `ToolProcess` ;
- aucun shell nest utilisé ;
- stdout et stderr sont pris en charge ;
- la première ligne non vide est normalisée ;
- une sortie invalide devient un UTF-8 valide ;
- linspection est limitée à 4096 octets par flux ;
- un code non nul ou un signal est refusé ;
- lannulation produit `G_IO_ERROR_CANCELLED` ;
- la fonction ne modifie pas directement le registre ;
- les versions successives sont indépendantes ;
- les tests ne dépendent daucun outil installé ;
- tous les tests passent ;
- aucune fuite mémoire nest détectée ;
- le test est intégré au `Makefile`.
## Démonstration finale
Après validation :
1. créer un `ToolRegistry` ;
2. appeler `tool_catalog_register_defaults()` ;
3. appeler `tool_registry_refresh()` ;
4. afficher les outils présents et absents ;
5. détecter la version dun outil disponible ;
6. stocker explicitement la version avec `tool_registry_set_version()`.
Cette démonstration restera hors de linterface GTK principale.
## Suite prévue
```text
#040 — Initialisation des dépendances et analyse asynchrone au démarrage
```
Ce ticket devra créer le registre global de lapplication, enregistrer le catalogue, rafraîchir les disponibilités hors du thread GTK, détecter les versions et publier une tâche dans `TaskManager`.

View file

@ -1,681 +0,0 @@
# TICKET-040 — Inventaire et qualification de larsenal OSINT
## Statut
À faire
## Priorité
Très haute
## Nature
Recherche, audit juridique, architecture et préparation des futures intégrations.
La présence dun outil dans cet inventaire ne signifie pas quil est approuvé.
---
## Objectif
Constituer une base large, structurée et maintenable doutils OSINT pouvant être intégrés à Labfy Investigation afin daider à enquêter légalement sur :
- pseudonymes et comptes publics ;
- adresses e-mail ;
- numéros de téléphone ;
- domaines, IP, certificats et infrastructures publiques ;
- documents, faux papiers, photos et vidéos ;
- historiques de pages supprimées ;
- dépôts publics ;
- IBAN, entreprises et cryptomonnaies ;
- relations entre personnes, comptes, domaines et moyens de paiement ;
- réseaux descrocs réutilisant les mêmes identifiants.
Le résultat doit être traçable, reproductible et présentable à des enquêteurs.
---
## Cadre légal non négociable
Labfy Investigation reste limité à :
- sources publiquement accessibles ;
- données remises légalement par une victime ou un enquêteur ;
- API et comptes utilisés conformément à leurs autorisations ;
- collecte sans contournement dauthentification ;
- recherche passive ou explicitement autorisée ;
- conservation de la méthode, de la source et de la sortie brute.
Sont exclus :
- intrusion ;
- exploitation de vulnérabilité ;
- brute force dauthentification ;
- credential stuffing ;
- phishing ;
- vol ou réutilisation de jetons ;
- usurpation ;
- social engineering ;
- accès à une ressource privée ;
- modification dune ressource distante ;
- utilisation de données obtenues illicitement.
---
## Livrables
```text
docs/osint/TOOL_INVENTORY.md
docs/osint/LEGAL_AND_OPERATIONAL_POLICY.md
data/osint_tools.json
data/osint_tools.csv
```
Le JSON deviendra la future source de génération du catalogue étendu.
---
## Schéma dune fiche outil
```text
identifier
name
repository
homepage
publisher
category
subcategories
description
license
license_verified
open_source_status
latest_release
last_activity
primary_language
installation_methods
ubuntu_packages
runtime_dependencies
input_types
output_formats
supports_json
supports_csv
supports_stdout
supports_file_output
requires_account
requires_api_key
requires_paid_service
network_activity
collection_mode
authentication_mode
rate_limit_risk
terms_of_service_risk
personal_data_risk
false_positive_risk
evidentiary_value
reproducibility
tool_version_command
integration_mode
integration_priority
legal_status
maintenance_status
decision
decision_reason
notes
reviewed_at
reviewed_by
```
---
## Valeurs normalisées
### open_source_status
```text
OPEN_SOURCE
SOURCE_AVAILABLE
OPEN_CLIENT_CLOSED_SERVICE
CLOSED_SOURCE
UNKNOWN
```
### collection_mode
```text
LOCAL_ONLY
PASSIVE_REMOTE
REMOTE_PUBLIC_REQUESTS
AUTHENTICATED_PUBLIC_API
ACTIVE_LOW_IMPACT
ACTIVE_INTRUSIVE
MIXED
```
### legal_status
```text
APPROVED_PASSIVE
APPROVED_WITH_CONFIGURATION
MANUAL_REVIEW_REQUIRED
RESTRICTED_TO_AUTHORIZED_TARGETS
REJECTED
UNKNOWN
```
### integration_mode
```text
DIRECT_CLI
DIRECT_LIBRARY
LOCAL_SERVICE
REMOTE_API
BROWSER_ASSISTED
EXPORT_IMPORT
REFERENCE_ONLY
REJECTED
```
### integration_priority
```text
P0
P1
P2
P3
BACKLOG
REJECTED
```
### decision
```text
INTEGRATE
PROTOTYPE
MONITOR
MANUAL_ONLY
REJECT
UNREVIEWED
```
---
## Notation sur 100
- valeur opérationnelle : 30 ;
- intégrabilité : 20 ;
- légalité et maîtrise du risque : 20 ;
- traçabilité : 15 ;
- maintenance : 10 ;
- coût : 5.
Un outil sous 50/100 ne doit pas être intégré sans justification écrite.
---
# Inventaire initial à auditer
## A. Pseudonymes, comptes et identités publiques
| Outil | Dépôt/projet | Usage | Priorité |
|---|---|---|---|
| Sherlock | `sherlock-project/sherlock` | Recherche de pseudonymes | P0 |
| Maigret | `soxoj/maigret` | Recherche approfondie de pseudonymes | P0 |
| Blackbird | `p1ngul1n0/blackbird` | Comptes et enrichissement | P1 |
| WhatsMyName | `WebBreacher/WhatsMyName` | Base de règles de présence | P0 |
| Enola | `theyahya/enola` | Recherche rapide de pseudonymes | P2 |
| Social Analyzer | `qeeqbox/social-analyzer` | Analyse multi-plateformes | P2 |
| socialscan | `iojw/socialscan` | Pseudonymes et e-mails | P1 |
| GHunt | `mxrch/GHunt` | Écosystème Google | P1 |
| GitFive | `mxrch/GitFive` | Corrélation GitHub | P1 |
| Holehe | `megadose/holehe` | Présence dune adresse sur des services | P0 |
| MOSINT | `alpkeskin/mosint` | Enrichissement de-mails | P2 |
| h8mail | `khast3x/h8mail` | Jeux de données autorisés uniquement | P1 restreint |
| Ignorant | `megadose/ignorant` | Présence dun téléphone sur des services | Revue |
| Instaloader | `instaloader/instaloader` | Contenus Instagram publics | Revue |
| snscrape | `JustAnotherArchivist/snscrape` | Collecte sociale publique | Revue |
| gallery-dl | `mikf/gallery-dl` | Conservation de médias publics | P1 |
| yt-dlp | `yt-dlp/yt-dlp` | Métadonnées et médias publics | P0 |
## B. E-mails
| Outil | Usage | Priorité |
|---|---|---|
| Holehe | Présence sur des services | P0 |
| MOSINT | Enrichissement multi-source | P2 |
| h8mail | Recherche dans des sources licites | Restreint |
| GHunt | Enrichissement Google | P1 |
| theHarvester | E-mails liés à un domaine | P0 |
| SpiderFoot | Modules denrichissement | P1 |
| Recon-ng | Framework modulaire | P2 |
| python-email-validator | Validation et normalisation | P1 |
| dnspython | Vérification DNS et MX | P1 |
| Clients OpenPGP | Identifiants publiés dans des clés | P2 |
## C. Téléphones
| Outil | Dépôt/projet | Usage | Priorité |
|---|---|---|---|
| PhoneInfoga | `sundowndev/phoneinfoga` | Pays, opérateur, formats, sources publiques | P0 |
| libphonenumber | `google/libphonenumber` | Parsing et validation | P0 |
| python-phonenumbers | `daviddrysdale/python-phonenumbers` | Adaptateur Python | P2 |
| Ignorant | `megadose/ignorant` | Présence sur certains services | Revue |
| Normalisation E.164 interne | Labfy | Canonicalisation | P0 |
Aucun outil ne doit prétendre identifier le titulaire réel dun numéro sans source officielle ou judiciaire.
## D. Domaines, DNS, certificats et infrastructures
| Outil | Dépôt/projet | Usage | Priorité |
|---|---|---|---|
| Amass | `owasp-amass/amass` | Cartographie dactifs | P1 |
| Subfinder | `projectdiscovery/subfinder` | Sous-domaines passifs | P0 |
| theHarvester | `laramies/theHarvester` | Domaines, e-mails, IP, noms | P0 |
| SpiderFoot | `smicallef/spiderfoot` | Automatisation multi-source | P1 |
| Recon-ng | `lanmaster53/recon-ng` | Framework modulaire | P2 |
| sn0int | `kpcyrd/sn0int` | Framework semi-automatique | P2 |
| BBOT | `blacklanternsecurity/bbot` | Orchestration récursive | Revue |
| dnsx | `projectdiscovery/dnsx` | Requêtes DNS structurées | P1 |
| Findomain | `Findomain/Findomain` | Sous-domaines | P2 |
| assetfinder | `tomnomnom/assetfinder` | Actifs passifs | P2 |
| dnstwist | `elceef/dnstwist` | Typosquatting et variantes | P0 |
| URLCrazy | `urbanadventurer/urlcrazy` | Variantes de domaines | P1 |
| CertStream | `CaliDog/certstream-python` | Certificats publics | P1 |
| massdns | `blechschmidt/massdns` | Résolution de masse | Restreint |
| puredns | `d3mondev/puredns` | Résolution de masse | Restreint |
| shuffledns | `projectdiscovery/shuffledns` | Résolution de masse | Restreint |
| Knockpy | `guelfoweb/knock` | Sous-domaines | Revue |
| Sublist3r | `aboul3la/Sublist3r` | Sous-domaines | Surveillance |
| httpx | `projectdiscovery/httpx` | Qualification HTTP | Restreint |
| httprobe | `tomnomnom/httprobe` | Services HTTP | Restreint |
| Katana | `projectdiscovery/katana` | Exploration Web | Restreint |
| RDAP clients | À sélectionner | Enregistrements normalisés | P0 |
| curl | Projet curl | Requêtes HTTP reproductibles | P0 |
| OpenSSL | Projet OpenSSL | Certificats TLS | P0 |
| dig / host / whois | Paquets système | DNS et enregistrements | P0 |
Les outils à forte volumétrie sont désactivés par défaut.
## E. Historique du Web et conservation
| Outil | Dépôt/projet | Usage | Priorité |
|---|---|---|---|
| waybackurls | `tomnomnom/waybackurls` | URLs Wayback | P0 |
| gau | `lc/gau` | URLs darchives multiples | P0 |
| waymore | `xnl-h4ck3r/waymore` | Historique approfondi | P1 |
| ArchiveBox | `ArchiveBox/ArchiveBox` | Archivage local | P0 |
| Browsertrix Crawler | `webrecorder/browsertrix-crawler` | WARC/WACZ | P0 |
| Browsertrix | `webrecorder/browsertrix` | Service local | P2 |
| ArchiveWeb.page | `webrecorder/archiveweb.page` | Capture navigateur | P0 |
| pywb | `webrecorder/pywb` | Relecture WARC | P1 |
| warcio | `webrecorder/warcio` | Lecture/écriture WARC | P0 |
| ReplayWeb.page | `webrecorder/replayweb.page` | Relecture darchives | P1 |
| SingleFile | `gildas-lormeau/SingleFile` | Copie autonome dune page | P0 |
| monolith | `Y2Z/monolith` | Sauvegarde autonome | P1 |
| grab-site | `ArchiveTeam/grab-site` | Archivage WARC | Revue |
| Heritrix | `internetarchive/heritrix3` | Crawl darchivage | Restreint |
| waybackpy | `akamhy/waybackpy` | Client Wayback | P2 |
| Perma | `harvard-lil/perma` | Préservation de références | Manuel/API |
## F. Documents, photos et vidéos
| Outil | Dépôt/projet | Usage | Priorité |
|---|---|---|---|
| ExifTool | `exiftool/exiftool` | Métadonnées multi-format | P0 |
| MediaInfo | `MediaArea/MediaInfo` | Audio et vidéo | P0 |
| FFmpeg / ffprobe | `FFmpeg/FFmpeg` | Analyse technique | P0 |
| Exiv2 | `Exiv2/exiv2` | Métadonnées dimages | P1 |
| Hachoir | `hachoir/hachoir` | Formats binaires | P1 |
| oletools | `decalage2/oletools` | Documents Office | P0 |
| DidierStevensSuite | `DidierStevens/DidierStevensSuite` | PDF et OLE | P1 |
| Poppler tools | Projet Poppler | PDF, texte et images | P0 |
| qpdf | `qpdf/qpdf` | Structure PDF | P0 |
| Tesseract OCR | `tesseract-ocr/tesseract` | OCR local | P0 |
| ImageMagick | `ImageMagick/ImageMagick` | Inspection de copies | P1 |
| OpenCV | `opencv/opencv` | Comparaison dimages | P2 |
| ImageHash | `JohannesBuchner/imagehash` | Empreintes perceptuelles | P1 |
| pHash | Projet pHash | Empreintes perceptuelles | P2 |
| file/libmagic | Paquet système | Identification de format | P0 |
| strings/binutils | Paquet système | Chaînes de caractères | P1 |
| MAT2 | `jvoisin/mat2` | Métadonnées sur copies | P2 |
| yt-dlp | `yt-dlp/yt-dlp` | Conservation de médias publics | P0 |
| gallery-dl | `mikf/gallery-dl` | Galeries publiques | P1 |
| InVID-WeVerify | Extension/service | Vérification manuelle | Manuel |
| face_recognition | `ageitgey/face_recognition` | Comparaison locale | Restreint |
| DeepFace | `serengil/deepface` | Comparaison locale | Restreint |
| InsightFace | `deepinsight/insightface` | Analyse faciale | Restreint |
Aucune recherche biométrique massive ne doit être intégrée.
## G. Dépôts publics et code
| Outil | Usage | Priorité |
|---|---|---|
| GitFive | Corrélation GitHub | P1 |
| GitHub CLI | API et données publiques | P0 |
| TruffleHog | Dépôts publics autorisés | Revue |
| Gitleaks | Secrets publiés | Revue |
| ggshield | Dépôts autorisés | Revue |
| ripgrep | Recherche locale | P0 |
| git-sizer | Structure de dépôt | P2 |
| Git natif | Historique, auteurs et dates | P0 |
Un secret découvert ne doit jamais être utilisé pour accéder à un compte.
## H. Réputation et APIs
| Outil/client | Usage | Priorité |
|---|---|---|
| vt-cli | VirusTotal | API |
| OTX Python SDK | AlienVault OTX | API |
| urlscan-go | URLScan | API |
| Shodan Python | Shodan | API |
| Censys Python | Censys | API |
| pyGreynoise | GreyNoise | API |
| PyMISP | MISP | P2 |
| OpenCTI client | OpenCTI | P2 |
| YARA | Classement local | P2 |
| capa | Analyse locale | Backlog |
Toute donnée envoyée à un tiers doit être affichée avant lappel.
## I. Corrélation et graphes
| Outil | Usage | Priorité |
|---|---|---|
| NetworkX | Algorithmes de graphe | P1 |
| igraph | Graphes performants | P2 |
| Graphviz | Rendu | P0 |
| Gephi | Analyse visuelle externe | Export |
| Cytoscape | Analyse visuelle externe | Export |
| OpenCTI | Référence de modèle | Référence |
| MISP | Référence dindicateurs | Référence |
| Aleph | Recherche documentaire et entités | P1 |
| OpenSanctions | Entités et sanctions publiques | P1 |
| FollowTheMoney | Modèle de relations | P1 |
| Neo4j Community | Base graphe externe | P3 |
| OSINTBuddy | Graphe denquête | Surveillance |
Labfy conserve son propre modèle de données.
## J. Géolocalisation
| Outil | Usage | Priorité |
|---|---|---|
| geopy | Géocodage configurable | P2 |
| Nominatim | Géocodage OpenStreetMap | P1 |
| OSMnx | Réseaux géographiques | P3 |
| Overpass API | Données OpenStreetMap | P2 |
| QGIS | Analyse externe | Export |
| SunCalc | Position du soleil | P2 |
| Skyfield | Calculs astronomiques | P3 |
| ExifTool | Coordonnées de médias | P0 |
## K. IBAN, entreprises et cryptomonnaies
| Outil | Usage | Priorité |
|---|---|---|
| python-stdnum | Validation IBAN et identifiants | P0 |
| iban4j | Validation IBAN | P3 |
| OpenSanctions | Personnes et organisations | P1 |
| FollowTheMoney | Relations structurées | P1 |
| bitcoin-etl | Données publiques Bitcoin | P2 |
| ethereum-etl | Données publiques Ethereum | P2 |
| GraphSense | Analyse de blockchains | P2 |
| BlockSci | Analyse locale | Surveillance |
| mempool | Explorateur Bitcoin open source | P2 |
| Bitcoin Core | Nœud local | P3 |
| Geth | Nœud Ethereum local | P3 |
Un IBAN ne permet pas didentifier publiquement son titulaire. Labfy se limite à la validation, au pays, à la banque lorsquelle est publique et aux corrélations entre dossiers.
---
# Outils exclus du mode OSINT standard
```text
nmap
masscan
naabu
nuclei
gobuster
ffuf
dirsearch
sqlmap
hydra
metasploit
Sn1per
Nikto
WPScan
scanners actifs Burp
outils dexploitation
outils de credential stuffing
outils de phishing
```
Décision initiale :
```text
integration_mode = REJECTED
decision = REJECT
```
---
# Vague prioritaire pour laffaire descroquerie
## P0
```text
Sherlock
Maigret
WhatsMyName
Holehe
PhoneInfoga
libphonenumber
GitHub CLI
ExifTool
MediaInfo
ffprobe
Tesseract OCR
Poppler tools
qpdf
oletools
file/libmagic
yt-dlp
waybackurls
gau
ArchiveBox
Browsertrix Crawler
ArchiveWeb.page
warcio
SingleFile
theHarvester
Subfinder
dnstwist
python-stdnum
Graphviz
```
## P1
```text
Blackbird
GHunt
GitFive
h8mail avec sources licites uniquement
gallery-dl
SpiderFoot
Amass
dnsx
URLCrazy
OpenSanctions
FollowTheMoney
Aleph
NetworkX
ImageHash
VirusTotal CLI
urlscan
```
---
# Audit obligatoire par outil
1. données envoyées sur Internet ;
2. domaines et API contactés ;
3. compte, cookie ou jeton requis ;
4. compatibilité avec les CGU ;
5. volume de requêtes ;
6. accès strictement public ;
7. capacité de modifier une ressource distante ;
8. risque de faux positif ;
9. précision des sources ;
10. sortie brute conservable ;
11. reproductibilité ;
12. commande de version ;
13. licence et redistribution ;
14. installation Ubuntu ;
15. dépendances ;
16. coût ou API obligatoire ;
17. activité du projet ;
18. vulnérabilités connues ;
19. télémétrie ;
20. scripts dinstallation distants.
Aucun `curl | sh`, `wget | bash`, `sudo pip install` ou `sudo npm install -g` ne doit être exécuté sans audit.
---
# Modèle de sécurité dintégration
```text
arguments séparés
aucun shell
utilisateur non privilégié
dossier temporaire
environnement minimal
timeout
limites stdout/stderr
annulation
journal
empreinte du binaire
version
domaines contactés
sortie brute UTC
SHA-256
```
---
# Valeur probatoire
Labfy doit distinguer :
```text
résultat de loutil
source publique
copie locale
interprétation
corrélation
hypothèse
```
Chaque exécution doit conserver :
```text
outil
version
commande
arguments
date UTC
source
URL
sortie brute
capture
archive
SHA-256
statut HTTP
notes
niveau de confiance
```
Un résultat Sherlock, Maigret, Holehe ou PhoneInfoga ne prouve jamais une identité à lui seul.
---
# Phases
## Phase 1
Créer le JSON, le CSV et le schéma.
## Phase 2
Auditer toute la liste P0.
## Phase 3
Auditer la liste P1.
## Phase 4
Attribuer note, priorité, mode dintégration, statut juridique et décision.
## Phase 5
Produire le rapport des outils retenus, manuels, surveillés et rejetés.
## Phase 6
Validation humaine avant tout passage à `INTEGRATE`.
---
# Contrôles automatisés futurs
- identifiants uniques ;
- noms non vides ;
- dépôt renseigné ;
- licence vérifiée pour tout outil approuvé ;
- décision renseignée ;
- aucune priorité P0 pour un outil rejeté ;
- aucun outil actif marqué passif ;
- JSON valide ;
- CSV généré depuis le JSON ;
- aucune clé API ;
- aucun secret ;
- aucune donnée issue dune enquête réelle.
---
# Critères dacceptation
- au moins 80 outils ou composants recensés ;
- toutes les catégories couvertes ;
- tous les P0 audités ;
- licences P0 vérifiées ;
- installation Ubuntu P0 documentée ;
- qualification juridique P0 ;
- commande de version P0 ;
- séparation claire passif/actif/intrusif ;
- aucun outil rejeté proposé à lutilisateur ;
- inventaire Markdown, JSON et CSV ;
- décisions justifiées ;
- feuille de route dintégration ;
- document présentable à des enquêteurs.
---
# Suite prévue
```text
#041 — Initialisation asynchrone des dépendances
#042 — Adaptateurs pseudonymes
#043 — Adaptateurs e-mail
#044 — Adaptateurs téléphone
#045 — Documents et métadonnées
#046 — Archivage probatoire du Web
#047 — Domaines et infrastructures passives
#048 — Graphe de corrélation
```