delete: docs/tickets/
This commit is contained in:
parent
36caf3101b
commit
26a8f08187
42 changed files with 0 additions and 17247 deletions
|
|
@ -1,37 +0,0 @@
|
||||||
# Ticket #001
|
|
||||||
|
|
||||||
## Titre
|
|
||||||
|
|
||||||
Créer le module Application.
|
|
||||||
|
|
||||||
## Objectif
|
|
||||||
|
|
||||||
Créer le point d’entrée applicatif chargé de gérer le cycle de vie de GTK.
|
|
||||||
|
|
||||||
## Responsabilités
|
|
||||||
|
|
||||||
- créer l’objet 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 d’une enquête ;
|
|
||||||
- sélecteur de dossier ;
|
|
||||||
- logique métier.
|
|
||||||
|
|
||||||
## Critères d’acceptation
|
|
||||||
|
|
||||||
- [ ] Le projet compile en C17.
|
|
||||||
- [ ] Aucun warning.
|
|
||||||
- [ ] Aucun état global.
|
|
||||||
- [ ] Les fonctions publiques sont documentées avec Doxygen.
|
|
||||||
- [ ] Une fenêtre GTK minimale s’affiche.
|
|
||||||
- [ ] La fermeture de l’application est propre.
|
|
||||||
|
|
||||||
## Commit attendu
|
|
||||||
|
|
||||||
```text
|
|
||||||
feat(core): create application lifecycle
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
# Ticket #002
|
|
||||||
|
|
||||||
## Titre
|
|
||||||
|
|
||||||
Ajouter le sélecteur de dossier d’enquête.
|
|
||||||
|
|
||||||
## Objectif
|
|
||||||
|
|
||||||
Permettre à l’utilisateur de sélectionner un dossier d’enquête au lancement de l’application.
|
|
||||||
|
|
||||||
## Responsabilités
|
|
||||||
|
|
||||||
- afficher un dialogue GTK de sélection de dossier ;
|
|
||||||
- retourner le dossier sélectionné ;
|
|
||||||
- gérer l’annulation 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 d’une enquête.
|
|
||||||
|
|
||||||
## Critères d’acceptation
|
|
||||||
|
|
||||||
- [ ] Le dialogue s’ouvre au lancement.
|
|
||||||
- [ ] Un dossier peut être sélectionné.
|
|
||||||
- [ ] L’annulation 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
|
|
||||||
|
|
@ -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);
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -1,254 +0,0 @@
|
||||||
# Ticket #021
|
|
||||||
|
|
||||||
## Titre
|
|
||||||
|
|
||||||
Valider une enquête existante.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Objectif
|
|
||||||
|
|
||||||
Ajouter au module `InvestigationProject` la capacité de vérifier qu’un 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 d’enquê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 d’un dossier ;
|
|
||||||
- vérifier que le chemin existe ;
|
|
||||||
- vérifier qu’il désigne un dossier ;
|
|
||||||
- vérifier la présence de l’arborescence 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 n’existe 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` n’est 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 l’arborescence.
|
|
||||||
|
|
||||||
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 l’enquête dans l’application.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Gestion des erreurs
|
|
||||||
|
|
||||||
Dans ce ticket, la fonction retourne uniquement un booléen.
|
|
||||||
|
|
||||||
Les détails d’erreur 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 d’acceptation
|
|
||||||
|
|
||||||
- [ ] 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 n’est créé pendant la validation.
|
|
||||||
- [ ] Aucun élément n’est supprimé pendant la validation.
|
|
||||||
- [ ] La structure de référence n’est pas dupliquée.
|
|
||||||
- [ ] Aucune dépendance GTK.
|
|
||||||
- [ ] Aucun `Gtk-CRITICAL`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Commit attendu
|
|
||||||
|
|
||||||
```text
|
|
||||||
feat(core): validate investigation project structure
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
@ -1,366 +0,0 @@
|
||||||
# Ticket #026 — Migrer l’initialisation vers la couche Database
|
|
||||||
|
|
||||||
## Contexte
|
|
||||||
|
|
||||||
Le ticket #025 a introduit l’infrastructure 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 d’erreurs.
|
|
||||||
|
|
||||||
Cependant, `database_initialize()` utilise encore directement plusieurs fonctions de l’API 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 l’abstraction `Database`.
|
|
||||||
|
|
||||||
## Objectif
|
|
||||||
|
|
||||||
Réécrire l’initialisation d’une nouvelle enquête afin qu’elle utilise l’infrastructure créée au ticket #025.
|
|
||||||
|
|
||||||
L’initialisation doit rester atomique :
|
|
||||||
|
|
||||||
- soit la base est entièrement initialisée ;
|
|
||||||
- soit aucune donnée partielle n’est conservée.
|
|
||||||
|
|
||||||
## Travail à réaliser
|
|
||||||
|
|
||||||
### Adapter l’installation du schéma
|
|
||||||
|
|
||||||
Modifier la signature actuelle :
|
|
||||||
|
|
||||||
```C
|
|
||||||
bool schema_install_v1(
|
|
||||||
sqlite3 *database
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
afin qu’elle reçoive un contexte Database :
|
|
||||||
|
|
||||||
```C
|
|
||||||
bool schema_install_v1(
|
|
||||||
Database *database
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
L’installation du fichier SQL complet pourra continuer à utiliser `sqlite3_exec()` en interne, car le schéma contient plusieurs instructions SQL.
|
|
||||||
|
|
||||||
L’accès au handle SQLite devra passer par l’API interne :
|
|
||||||
|
|
||||||
```C
|
|
||||||
database_get_handle()
|
|
||||||
```
|
|
||||||
|
|
||||||
La fonction ne devra réaliser ni `COMMIT` ni `ROLLBACK`.
|
|
||||||
|
|
||||||
### Migrer l’insertion des métadonnées
|
|
||||||
|
|
||||||
Réécrire les fonctions responsables de l’insertion dans la table `metadata` avec `DatabaseStatement`.
|
|
||||||
|
|
||||||
Les opérations devront utiliser l’API 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 l’insertion de l’enquête
|
|
||||||
|
|
||||||
Réécrire l’insertion 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 l’UUID
|
|
||||||
↓
|
|
||||||
ouverture de Database
|
|
||||||
↓
|
|
||||||
début de transaction
|
|
||||||
↓
|
|
||||||
installation du schéma V1
|
|
||||||
↓
|
|
||||||
insertion des métadonnées
|
|
||||||
↓
|
|
||||||
insertion de l’enquête
|
|
||||||
↓
|
|
||||||
commit
|
|
||||||
↓
|
|
||||||
fermeture de Database
|
|
||||||
```
|
|
||||||
|
|
||||||
Tout échec après le début de la transaction doit provoquer un rollback.
|
|
||||||
|
|
||||||
### Utiliser l’infrastructure d’erreurs
|
|
||||||
|
|
||||||
Les erreurs rencontrées pendant :
|
|
||||||
|
|
||||||
- l’installation du schéma ;
|
|
||||||
- la préparation d’une requête ;
|
|
||||||
- le binding d’un paramètre ;
|
|
||||||
- l’exécution d’une requête ;
|
|
||||||
- le début d’une transaction ;
|
|
||||||
- le commit ;
|
|
||||||
- le rollback ;
|
|
||||||
|
|
||||||
doivent être enregistrées dans le contexte Database lorsque celui-ci est disponible.
|
|
||||||
|
|
||||||
L’infrastructure 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 l’ancien code SQLite
|
|
||||||
|
|
||||||
Supprimer de la logique d’initialisation 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 l’activation 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 :
|
|
||||||
|
|
||||||
- l’initialisation 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 qu’une initialisation valide crée :
|
|
||||||
|
|
||||||
- le schéma V1 ;
|
|
||||||
- les tables attendues ;
|
|
||||||
- les métadonnées obligatoires ;
|
|
||||||
- une seule ligne dans investigation ;
|
|
||||||
- le bon nom d’enquê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 d’initialisation 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 n’est conservée ;
|
|
||||||
- aucune seconde enquête n’est 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 d’acceptation
|
|
||||||
|
|
||||||
- [ ] `database_initialize()` n’appelle 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 l’API interne.
|
|
||||||
- [ ] Aucun `sqlite3_stmt *` n’est manipulé dans le code d’initialisation.
|
|
||||||
- [ ] 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 d’interface 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.
|
|
||||||
|
|
||||||
L’initialisation d’une enquête doit être entièrement transactionnelle, testée et cohérente avec l’architecture mise en place au ticket #025.
|
|
||||||
|
|
||||||
## Commit attendu
|
|
||||||
|
|
||||||
Une fois tous les critères d’acceptation 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
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
@ -1,535 +0,0 @@
|
||||||
# Ticket #027 — Ajouter le modèle de lecture et le DAO de l’enquête
|
|
||||||
|
|
||||||
## Contexte
|
|
||||||
|
|
||||||
Les tickets précédents ont permis de mettre en place :
|
|
||||||
|
|
||||||
- le schéma SQLite V1 ;
|
|
||||||
- l’infrastructure `Database` ;
|
|
||||||
- les requêtes préparées `DatabaseStatement` ;
|
|
||||||
- les transactions ;
|
|
||||||
- la gestion centralisée des erreurs ;
|
|
||||||
- l’initialisation transactionnelle d’une nouvelle enquête.
|
|
||||||
|
|
||||||
La table SQLite :
|
|
||||||
|
|
||||||
```text
|
|
||||||
investigation
|
|
||||||
```
|
|
||||||
contient les informations principales de l’enquê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 l’enquête ;
|
|
||||||
- chemin du fichier SQLite.
|
|
||||||
|
|
||||||
Il ne doit pas être transformé directement en représentation d’une ligne SQL.
|
|
||||||
|
|
||||||
L’architecture 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 qu’un DAO permettant de charger cette ligne depuis une
|
|
||||||
connexion Database.
|
|
||||||
|
|
||||||
Le ticket doit permettre de lire les informations persistées d’une enquête
|
|
||||||
sans utiliser directement l’API 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 l’enquê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 l’unique 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 l’interface publique du DAO.
|
|
||||||
|
|
||||||
### Vérifier le nombre de lignes
|
|
||||||
|
|
||||||
La base d’une 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 d’exé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 l’enquête avec `investigation_dao_load()` ;
|
|
||||||
- vérifier que le modèle n’est pas `NULL` ;
|
|
||||||
- vérifier l’UUID ;
|
|
||||||
- vérifier le nom ;
|
|
||||||
- vérifier le chemin racine ;
|
|
||||||
- vérifier `created_at` ;
|
|
||||||
- vérifier `updated_at` ;
|
|
||||||
- vérifier que l’erreur 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 d’acceptation
|
|
||||||
|
|
||||||
- [ ] 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 l’enquête courante.
|
|
||||||
- [ ] Le DAO utilise uniquement l’API `DatabaseStatement`.
|
|
||||||
- [ ] Aucun appel direct à SQLite n’apparaî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 d’une enquête par le DAO ;
|
|
||||||
- la modification du nom de l’enquête ;
|
|
||||||
- la modification du chemin racine ;
|
|
||||||
- la suppression d’une enquête ;
|
|
||||||
- un CRUD complet ;
|
|
||||||
- l’ouverture automatique dans l’interface GTK ;
|
|
||||||
- l’affichage 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 l’enquête courante dans un modèle C indépendant de SQLite.
|
|
||||||
|
|
||||||
Le DAO devient le premier point d’accè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
|
|
||||||
```
|
|
||||||
|
|
@ -1,776 +0,0 @@
|
||||||
# Ticket #028 — Ajouter l’ouverture d’une enquête existante
|
|
||||||
|
|
||||||
## Contexte
|
|
||||||
|
|
||||||
Les tickets précédents ont permis de mettre en place :
|
|
||||||
|
|
||||||
- la création transactionnelle d’une base d’enquê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 l’unique 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 l’enquête ;
|
|
||||||
- le chemin du fichier `Enquete.sqlite`.
|
|
||||||
|
|
||||||
Cependant, l’application ne possède pas encore d’objet 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 d’une enquête ouverte.
|
|
||||||
|
|
||||||
## Objectif
|
|
||||||
|
|
||||||
Créer un type opaque `InvestigationSession` chargé d’ouvrir une enquête existante et de conserver :
|
|
||||||
|
|
||||||
- son contexte de fichiers `InvestigationProject` ;
|
|
||||||
- sa connexion `Database` ;
|
|
||||||
- ses informations persistées `InvestigationRecord`.
|
|
||||||
|
|
||||||
L’ouverture 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 d’ouverture
|
|
||||||
|
|
||||||
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 d’erreur 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
|
|
||||||
```
|
|
||||||
|
|
||||||
Lorsqu’une 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 d’ouverture
|
|
||||||
|
|
||||||
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 d’erreur attendu est :
|
|
||||||
|
|
||||||
```c
|
|
||||||
INVESTIGATION_SESSION_ERROR_INVALID_ARGUMENT
|
|
||||||
```
|
|
||||||
|
|
||||||
Le paramètre `error` peut être `NULL`.
|
|
||||||
|
|
||||||
Si `error` n’est pas `NULL`, il doit respecter les conventions GLib :
|
|
||||||
|
|
||||||
```c
|
|
||||||
*error == NULL
|
|
||||||
```
|
|
||||||
|
|
||||||
au moment de l’appel.
|
|
||||||
|
|
||||||
### 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 l’API 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 l’API 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 l’ouverture 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 n’existe pas, retourner :
|
|
||||||
|
|
||||||
```c
|
|
||||||
INVESTIGATION_SESSION_ERROR_DATABASE_NOT_FOUND
|
|
||||||
```
|
|
||||||
|
|
||||||
L’ouverture d’une 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 l’enquê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 d’erreur 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, l’ouverture doit échouer avec :
|
|
||||||
|
|
||||||
```c
|
|
||||||
INVESTIGATION_SESSION_ERROR_ROOT_MISMATCH
|
|
||||||
```
|
|
||||||
|
|
||||||
Cette vérification évite d’ouvrir une base copiée ou déplacée sans détecter l’incohérence.
|
|
||||||
|
|
||||||
Le déplacement volontaire d’une enquête sera traité dans un ticket distinct.
|
|
||||||
|
|
||||||
### 11. Construire la session
|
|
||||||
|
|
||||||
La session ne doit être créée qu’aprè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 d’allocation, 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 l’appelant.
|
|
||||||
|
|
||||||
Les accesseurs doivent retourner `NULL` si la session reçue est `NULL`.
|
|
||||||
|
|
||||||
`Database` reste non constante car les futurs DAO auront besoin d’une 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 d’ouverture valide
|
|
||||||
|
|
||||||
Créer un dossier temporaire.
|
|
||||||
|
|
||||||
Initialiser une base avec :
|
|
||||||
|
|
||||||
```c
|
|
||||||
database_initialize()
|
|
||||||
```
|
|
||||||
|
|
||||||
Ouvrir ensuite l’enquête avec :
|
|
||||||
|
|
||||||
```c
|
|
||||||
investigation_session_open()
|
|
||||||
```
|
|
||||||
|
|
||||||
Vérifier :
|
|
||||||
|
|
||||||
- la session n’est pas `NULL` ;
|
|
||||||
- aucune erreur n’est produite ;
|
|
||||||
- le projet est disponible ;
|
|
||||||
- la connexion Database est disponible ;
|
|
||||||
- le record est disponible ;
|
|
||||||
- le nom de l’enquête est correct ;
|
|
||||||
- le chemin racine est correct ;
|
|
||||||
- l’UUID est valide ;
|
|
||||||
- `created_at` n’est pas vide ;
|
|
||||||
- `updated_at` n’est 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 d’un dossier inexistant
|
|
||||||
|
|
||||||
Utiliser un chemin inexistant.
|
|
||||||
|
|
||||||
Vérifier :
|
|
||||||
|
|
||||||
```text
|
|
||||||
résultat == NULL
|
|
||||||
erreur == INVESTIGATION_SESSION_ERROR_ROOT_NOT_FOUND
|
|
||||||
message non vide
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test d’un chemin qui n’est 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 d’une 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 d’une 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 d’un chemin racine incohérent
|
|
||||||
|
|
||||||
Créer une base avec un chemin racine enregistré différent du dossier utilisé pour l’ouverture.
|
|
||||||
|
|
||||||
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 l’ouverture valide d’une 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 l’allocation 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 d’acceptation
|
|
||||||
|
|
||||||
- [ ] 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 l’ouverture.
|
|
||||||
- [ ] Le chemin racine est normalisé.
|
|
||||||
- [ ] Le chemin de la base provient de `InvestigationProject`.
|
|
||||||
- [ ] Le fichier SQLite doit exister avant l’appel à `database_open()`.
|
|
||||||
- [ ] L’ouverture 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 l’ouverture.
|
|
||||||
- [ ] 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 n’apparaît dans l’API de la session.
|
|
||||||
- [ ] Aucune dépendance GTK n’est ajoutée.
|
|
||||||
- [ ] Les tests d’ouverture 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 n’est 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 d’une nouvelle enquête ;
|
|
||||||
- le déplacement d’une enquête ;
|
|
||||||
- la réparation automatique d’un chemin racine incohérent ;
|
|
||||||
- la modification de `investigation.root_path` ;
|
|
||||||
- l’intégration dans la fenêtre GTK ;
|
|
||||||
- une boîte de dialogue de sélection ;
|
|
||||||
- l’affichage du nom de l’enquête ;
|
|
||||||
- les DAO des preuves, sources ou entités ;
|
|
||||||
- la fermeture demandée par l’interface 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 l’enquête
|
|
||||||
```
|
|
||||||
|
|
||||||
Cette session deviendra le contexte principal utilisé ultérieurement par l’interface et les futurs DAO.
|
|
||||||
|
|
||||||
## Commit attendu
|
|
||||||
|
|
||||||
Une fois tous les critères d’acceptation 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 s’ils 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é qu’après validation complète de la compilation, des tests et du contenu du commit.
|
|
||||||
|
|
@ -1,933 +0,0 @@
|
||||||
# Ticket #029 — Intégrer `InvestigationSession` au cycle de vie GTK
|
|
||||||
|
|
||||||
## Contexte
|
|
||||||
|
|
||||||
Le ticket #028 a ajouté `InvestigationSession`, qui permet d’ouvrir 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.
|
|
||||||
|
|
||||||
L’application GTK utilise encore l’ancien 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 n’utilise pas les informations persistées dans la table `investigation`.
|
|
||||||
|
|
||||||
Il faut désormais remplacer l’ancien objet détenu par `Application` par une véritable `InvestigationSession`.
|
|
||||||
|
|
||||||
## Objectif
|
|
||||||
|
|
||||||
Faire de `InvestigationSession` le contexte actif de l’application.
|
|
||||||
|
|
||||||
Après la sélection d’un dossier :
|
|
||||||
|
|
||||||
1. ouvrir une nouvelle `InvestigationSession` ;
|
|
||||||
2. récupérer le chemin racine depuis son `InvestigationProject` ;
|
|
||||||
3. construire le nouvel `InvestigationTreeModel` ;
|
|
||||||
4. ne remplacer l’ancienne enquête qu’après validation complète ;
|
|
||||||
5. mettre à jour la fenêtre principale ;
|
|
||||||
6. conserver la session jusqu’à la fermeture de l’application.
|
|
||||||
|
|
||||||
L’application ne doit posséder qu’une 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
|
|
||||||
|
|
||||||
L’ouverture d’une 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 l’ancien arbre
|
|
||||||
↓
|
|
||||||
fermer l’ancienne 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 d’erreur produit
|
|
||||||
```
|
|
||||||
|
|
||||||
L’application ne doit jamais perdre une enquête déjà ouverte simplement parce qu’une nouvelle sélection est invalide.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Travail à réaliser
|
|
||||||
|
|
||||||
## 1. Remplacer l’ancien 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 l’ancien 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 l’ouverture soit validée.
|
|
||||||
|
|
||||||
## 4. Gérer une sélection annulée
|
|
||||||
|
|
||||||
Lorsque :
|
|
||||||
|
|
||||||
```c
|
|
||||||
folder_path == NULL
|
|
||||||
```
|
|
||||||
|
|
||||||
la fonction doit simplement retourner.
|
|
||||||
|
|
||||||
L’enquê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 d’ouverture de session
|
|
||||||
|
|
||||||
Si :
|
|
||||||
|
|
||||||
```c
|
|
||||||
new_session == NULL
|
|
||||||
```
|
|
||||||
|
|
||||||
la fonction doit :
|
|
||||||
|
|
||||||
- afficher un avertissement contenant le message du `GError` ;
|
|
||||||
- libérer le `GError` ;
|
|
||||||
- conserver l’ancienne session ;
|
|
||||||
- conserver l’ancien 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 d’erreur est hors périmètre de ce ticket.
|
|
||||||
|
|
||||||
## 6. Construire l’arborescence 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 l’arbre
|
|
||||||
|
|
||||||
Si `InvestigationTreeBuilder` échoue après l’ouverture de la session :
|
|
||||||
|
|
||||||
```text
|
|
||||||
new_session valide
|
|
||||||
new_tree_model == NULL
|
|
||||||
```
|
|
||||||
|
|
||||||
la fonction doit :
|
|
||||||
|
|
||||||
```c
|
|
||||||
investigation_session_close(new_session);
|
|
||||||
```
|
|
||||||
|
|
||||||
puis retourner.
|
|
||||||
|
|
||||||
L’ancienne session et l’ancien 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` n’est 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 d’ouverture de l’enquê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 l’enquê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 l’installation 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
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
L’ordre 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
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
L’ordre 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 l’ancien objet dans l’application
|
|
||||||
|
|
||||||
À 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` n’est pas supprimé dans ce ticket.
|
|
||||||
|
|
||||||
Sa suppression éventuelle sera effectuée séparément après vérification qu’aucun autre composant ne l’utilise.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Tests et validations
|
|
||||||
|
|
||||||
## 16. Tests automatisés existants
|
|
||||||
|
|
||||||
Aucun nouveau test GTK automatisé n’est 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 d’ouverture valide
|
|
||||||
|
|
||||||
Créer ou utiliser une enquête valide.
|
|
||||||
|
|
||||||
Lancer :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
make
|
|
||||||
make run
|
|
||||||
```
|
|
||||||
|
|
||||||
Sélectionner le dossier racine de l’enquête.
|
|
||||||
|
|
||||||
Vérifier :
|
|
||||||
|
|
||||||
- la fenêtre reste ouverte ;
|
|
||||||
- l’arborescence apparaît dans la sidebar ;
|
|
||||||
- le titre contient le nom persistant de l’enquête ;
|
|
||||||
- la barre d’état contient le nom et le chemin racine ;
|
|
||||||
- aucune erreur SQLite n’apparaît ;
|
|
||||||
- la session reste active après la fin du callback.
|
|
||||||
|
|
||||||
## 18. Test manuel d’annulation
|
|
||||||
|
|
||||||
Relancer l’application 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 d’un 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 n’est créé ;
|
|
||||||
- la fenêtre reste utilisable ;
|
|
||||||
- aucune fausse enquête n’apparaît dans la sidebar.
|
|
||||||
|
|
||||||
## 20. Test manuel de remplacement
|
|
||||||
|
|
||||||
Si l’interface permet une seconde sélection pendant la même exécution :
|
|
||||||
|
|
||||||
1. ouvrir une enquête valide A ;
|
|
||||||
2. tenter d’ouvrir 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 n’est pas encore accessible dans l’interface, cette validation sera complétée lors de l’ajout de l’action « Ouvrir ».
|
|
||||||
|
|
||||||
La logique du callback doit néanmoins déjà préserver l’ancienne 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 d’acceptation
|
|
||||||
|
|
||||||
- [ ] `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`.
|
|
||||||
- [ ] L’arbre est construit depuis le chemin de la session.
|
|
||||||
- [ ] L’ancienne session reste active si l’ouverture échoue.
|
|
||||||
- [ ] L’ancien arbre reste actif si l’ouverture échoue.
|
|
||||||
- [ ] La nouvelle session est fermée si la construction de l’arbre échoue.
|
|
||||||
- [ ] Les anciens objets ne sont remplacés qu’aprè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 l’enquête.
|
|
||||||
- [ ] La barre d’état affiche le nom et le chemin racine.
|
|
||||||
- [ ] `application.c` n’appelle aucune fonction SQLite.
|
|
||||||
- [ ] Aucun chemin SQLite n’est reconstruit dans `application.c`.
|
|
||||||
- [ ] Les anciens tests restent valides.
|
|
||||||
- [ ] `make` réussit sans warning.
|
|
||||||
- [ ] `make test` réussit.
|
|
||||||
- [ ] Le test manuel d’ouverture valide réussit.
|
|
||||||
- [ ] Le test manuel d’annulation 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 d’une enquête ;
|
|
||||||
- la fermeture manuelle d’une enquête ;
|
|
||||||
- la réparation d’un 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 l’ancien 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 d’un dossier doit ouvrir une véritable session d’enquête.
|
|
||||||
|
|
||||||
L’application doit conserver ensemble :
|
|
||||||
|
|
||||||
```text
|
|
||||||
la connexion SQLite
|
|
||||||
les métadonnées persistées
|
|
||||||
le contexte de fichiers
|
|
||||||
l’arborescence
|
|
||||||
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
|
|
@ -1,280 +0,0 @@
|
||||||
# Ticket #031.1 — Ajouter une fermeture propre de l’application
|
|
||||||
|
|
||||||
## Contexte
|
|
||||||
|
|
||||||
L’application permet désormais :
|
|
||||||
|
|
||||||
- de créer une enquête ;
|
|
||||||
- d’ouvrir 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 l’application 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 d’actions 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 n’est 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 l’application ;
|
|
||||||
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 qu’aucun crash n’apparaît ;
|
|
||||||
5. relancer l’application ;
|
|
||||||
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 l’absence de crash ou de double libération.
|
|
||||||
|
|
||||||
## Critères d’acceptation
|
|
||||||
|
|
||||||
- [ ] Le bouton `Quitter` est visible.
|
|
||||||
- [ ] Le bouton ferme l’application.
|
|
||||||
- [ ] Aucun `exit()` direct n’est 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 d’enquê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
|
|
||||||
```
|
|
||||||
|
|
@ -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 l’enquête dans `MainWindow`.
|
|
||||||
|
|
||||||
L’application ne permet cependant pas encore d’ouvrir explicitement une enquête existante.
|
|
||||||
|
|
||||||
L’ancien sélecteur automatique au démarrage a été supprimé afin de ne pas forcer l’utilisateur à choisir un dossier à chaque lancement.
|
|
||||||
|
|
||||||
## Objectif
|
|
||||||
|
|
||||||
Ajouter un bouton :
|
|
||||||
|
|
||||||
```text
|
|
||||||
Ouvrir une enquête
|
|
||||||
```
|
|
||||||
|
|
||||||
Ce bouton doit permettre de sélectionner le dossier racine d’une enquête existante, puis de l’ouvrir avec le flux déjà présent :
|
|
||||||
|
|
||||||
```text
|
|
||||||
FolderDialog
|
|
||||||
↓
|
|
||||||
investigation_session_open()
|
|
||||||
↓
|
|
||||||
investigation_tree_builder_build()
|
|
||||||
↓
|
|
||||||
application_install_session()
|
|
||||||
↓
|
|
||||||
MainWindow mise à jour
|
|
||||||
```
|
|
||||||
|
|
||||||
La logique d’installation d’une 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 d’actions :
|
|
||||||
|
|
||||||
```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 s’il existe.
|
|
||||||
|
|
||||||
`MainWindow` ne doit pas ouvrir elle-même la session.
|
|
||||||
|
|
||||||
### 4. Relier le bouton à `Application`
|
|
||||||
|
|
||||||
Dans `src/core/application.c`, réactiver l’utilisation 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 l’annulation ;
|
|
||||||
2. ouvrir la session avec `investigation_session_open()` ;
|
|
||||||
3. récupérer le chemin racine depuis `InvestigationProject` ;
|
|
||||||
4. construire l’arbre 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 l’application ;
|
|
||||||
2. cliquer sur `Ouvrir une enquête` ;
|
|
||||||
3. sélectionner une enquête créée avec le ticket #030 ;
|
|
||||||
4. vérifier l’affichage de l’arborescence ;
|
|
||||||
5. vérifier le titre ;
|
|
||||||
6. vérifier la barre d’état ;
|
|
||||||
7. vérifier l’absence d’erreur SQLite.
|
|
||||||
|
|
||||||
### Annulation
|
|
||||||
|
|
||||||
Ouvrir le sélecteur puis annuler.
|
|
||||||
|
|
||||||
Vérifier :
|
|
||||||
|
|
||||||
```text
|
|
||||||
aucun crash
|
|
||||||
aucun changement de session
|
|
||||||
aucun changement d’arbre
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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 d’ouvrir un dossier invalide ;
|
|
||||||
3. vérifier que A reste active.
|
|
||||||
|
|
||||||
## Critères d’acceptation
|
|
||||||
|
|
||||||
- [ ] Le bouton `Ouvrir une enquête` est visible.
|
|
||||||
- [ ] Le bouton ouvre un sélecteur de dossier.
|
|
||||||
- [ ] Le sélecteur ne s’ouvre pas automatiquement au démarrage.
|
|
||||||
- [ ] Une enquête valide peut être ouverte.
|
|
||||||
- [ ] L’arborescence est affichée.
|
|
||||||
- [ ] Le titre est mis à jour.
|
|
||||||
- [ ] La barre d’état est mise à jour.
|
|
||||||
- [ ] L’annulation ne modifie aucun état.
|
|
||||||
- [ ] Un dossier invalide est refusé.
|
|
||||||
- [ ] Une base absente n’est pas créée.
|
|
||||||
- [ ] L’ancienne session reste active en cas d’échec.
|
|
||||||
- [ ] L’ancien arbre reste actif en cas d’échec.
|
|
||||||
- [ ] `application_install_session()` est réutilisée.
|
|
||||||
- [ ] Aucun appel SQLite direct n’est 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, l’application doit proposer deux actions explicites :
|
|
||||||
|
|
||||||
```text
|
|
||||||
Nouvelle enquête
|
|
||||||
Ouvrir une enquête
|
|
||||||
```
|
|
||||||
|
|
||||||
L’utilisateur doit pouvoir créer une enquête, fermer l’application, 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
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
@ -1,537 +0,0 @@
|
||||||
# Ticket #032 — Factoriser le chargement et l’installation d’une enquête
|
|
||||||
|
|
||||||
## Contexte
|
|
||||||
|
|
||||||
Après le ticket #031.1, l’application sait :
|
|
||||||
|
|
||||||
- créer une enquête ;
|
|
||||||
- ouvrir une enquête existante ;
|
|
||||||
- remplacer la session active ;
|
|
||||||
- fermer proprement l’application.
|
|
||||||
|
|
||||||
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 à l’appelant
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Travail à réaliser
|
|
||||||
|
|
||||||
## 1. Ajouter un domaine d’erreur privé
|
|
||||||
|
|
||||||
Dans `src/core/application.c`, ajouter un domaine d’erreur 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 qu’elle possède encore
|
|
||||||
```
|
|
||||||
|
|
||||||
### Validation de `GError`
|
|
||||||
|
|
||||||
La fonction doit respecter la convention GLib :
|
|
||||||
|
|
||||||
```c
|
|
||||||
g_return_val_if_fail(
|
|
||||||
error == NULL || *error == NULL,
|
|
||||||
FALSE
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
L’utilisation 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 l’erreur de `InvestigationSession`
|
|
||||||
|
|
||||||
Si :
|
|
||||||
|
|
||||||
```c
|
|
||||||
investigation_session_open()
|
|
||||||
```
|
|
||||||
|
|
||||||
échoue, la fonction doit conserver l’erreur métier d’origine et lui ajouter du contexte.
|
|
||||||
|
|
||||||
Exemple conceptuel :
|
|
||||||
|
|
||||||
```text
|
|
||||||
Impossible d’ouvrir l’enquête : la base SQLite est absente
|
|
||||||
```
|
|
||||||
|
|
||||||
Ne pas remplacer l’erreur précise par un simple :
|
|
||||||
|
|
||||||
```text
|
|
||||||
Erreur inconnue
|
|
||||||
```
|
|
||||||
|
|
||||||
lorsqu’un `GError` est disponible.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Refactoriser l’ouverture d’une 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 l’annulation ;
|
|
||||||
2. appeler `application_open_and_install_investigation()` ;
|
|
||||||
3. journaliser l’erreur ;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
L’annulation ne doit pas produire de warning.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Refactoriser la création d’une enquête
|
|
||||||
|
|
||||||
`application_on_create_investigation()` doit conserver uniquement :
|
|
||||||
|
|
||||||
1. la création physique du projet ;
|
|
||||||
2. l’appel à 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
|
|
||||||
L’enquê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 l’ancien é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 l’application ;
|
|
||||||
2. créer une enquête ;
|
|
||||||
3. vérifier le titre ;
|
|
||||||
4. vérifier la barre d’état ;
|
|
||||||
5. vérifier l’arborescence.
|
|
||||||
|
|
||||||
## Ouverture valide
|
|
||||||
|
|
||||||
1. fermer et relancer ;
|
|
||||||
2. ouvrir l’enquê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 d’ouverture
|
|
||||||
|
|
||||||
1. ouvrir une enquête valide A ;
|
|
||||||
2. tenter d’ouvrir un dossier invalide ;
|
|
||||||
3. vérifier que A reste active ;
|
|
||||||
4. vérifier que l’erreur est journalisée.
|
|
||||||
|
|
||||||
## Échec après création
|
|
||||||
|
|
||||||
Provoquer si possible un échec d’ouverture après création.
|
|
||||||
|
|
||||||
Vérifier :
|
|
||||||
|
|
||||||
- le dossier créé reste présent ;
|
|
||||||
- l’ancienne 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 d’acceptation
|
|
||||||
|
|
||||||
- [ ] Une seule fonction ouvre une session et construit son arbre.
|
|
||||||
- [ ] La création utilise cette fonction.
|
|
||||||
- [ ] L’ouverture utilise cette fonction.
|
|
||||||
- [ ] `application_install_session()` n’est pas dupliquée.
|
|
||||||
- [ ] L’ancienne session reste active en cas d’échec.
|
|
||||||
- [ ] L’ancien 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.
|
|
||||||
- [ ] L’annulation ne produit pas d’erreur.
|
|
||||||
- [ ] Aucun code SQLite direct n’est ajouté.
|
|
||||||
- [ ] Aucun comportement GTK visible n’est cassé.
|
|
||||||
- [ ] `make` réussit.
|
|
||||||
- [ ] `make test` réussit.
|
|
||||||
- [ ] `git diff --check` ne retourne aucune erreur.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Audit attendu
|
|
||||||
|
|
||||||
La logique d’ouverture ne doit apparaître qu’une 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 l’ouverture doivent appeler le helper :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
rg -n \
|
|
||||||
'application_open_and_install_investigation' \
|
|
||||||
src/core/application.c
|
|
||||||
```
|
|
||||||
|
|
||||||
Vérifier l’absence 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 n’est 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 d’entré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.
|
|
||||||
|
|
@ -1,694 +0,0 @@
|
||||||
# Ticket #033 — Afficher les erreurs dans l’interface GTK
|
|
||||||
|
|
||||||
## Contexte
|
|
||||||
|
|
||||||
Le ticket #032 a centralisé le chargement d’une enquête dans :
|
|
||||||
|
|
||||||
```c
|
|
||||||
application_open_and_install_investigation()
|
|
||||||
```
|
|
||||||
|
|
||||||
Cette fonction produit maintenant des `GError` précis et conserve l’ancienne 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 d’applications ne verra pas ces messages.
|
|
||||||
|
|
||||||
## Objectif
|
|
||||||
|
|
||||||
Créer un module GTK réutilisable capable d’afficher un message d’erreur compréhensible dans une fenêtre modale.
|
|
||||||
|
|
||||||
Le contrôleur doit conserver deux niveaux d’information :
|
|
||||||
|
|
||||||
```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 l’enquête.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Travail à réaliser
|
|
||||||
|
|
||||||
## 1. Créer l’en-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 l’implé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 d’une 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` n’est 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 l’implé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 n’est nécessaire dans ce ticket.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Ajouter la source au Makefile
|
|
||||||
|
|
||||||
Ajouter :
|
|
||||||
|
|
||||||
```text
|
|
||||||
src/views/application_message_dialog.c
|
|
||||||
```
|
|
||||||
|
|
||||||
à la liste des sources de l’application.
|
|
||||||
|
|
||||||
Le module doit être compilé avec les mêmes options strictes :
|
|
||||||
|
|
||||||
```text
|
|
||||||
-std=c17
|
|
||||||
-Wall
|
|
||||||
-Wextra
|
|
||||||
-Wpedantic
|
|
||||||
-Werror
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Intégration dans `Application`
|
|
||||||
|
|
||||||
## 7. Ajouter l’en-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 d’ouverture
|
|
||||||
|
|
||||||
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."
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
L’erreur doit être affichée avant :
|
|
||||||
|
|
||||||
```c
|
|
||||||
g_clear_error(&error);
|
|
||||||
```
|
|
||||||
|
|
||||||
L’annulation 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 l’enquê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 l’appel au dialogue.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Exemple d’inté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 l’application ;
|
|
||||||
2. ouvrir une enquête valide A ;
|
|
||||||
3. cliquer sur `Ouvrir une enquête` ;
|
|
||||||
4. sélectionner un dossier qui n’est pas une enquête.
|
|
||||||
|
|
||||||
Vérifier :
|
|
||||||
|
|
||||||
- une fenêtre d’erreur 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 l’ouverture 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 qu’une nouvelle erreur reste affichable après fermeture de la précédente.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Critères d’acceptation
|
|
||||||
|
|
||||||
- [ ] 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 lorsqu’elle existe.
|
|
||||||
- [ ] Le message revient à la ligne.
|
|
||||||
- [ ] Le message est sélectionnable.
|
|
||||||
- [ ] Les arguments `NULL` sont acceptés.
|
|
||||||
- [ ] Un bouton `Fermer` fonctionne.
|
|
||||||
- [ ] Les erreurs d’ouverture sont visibles dans GTK.
|
|
||||||
- [ ] Les erreurs de création sont visibles dans GTK.
|
|
||||||
- [ ] Les `g_warning()` techniques sont conservés.
|
|
||||||
- [ ] L’annulation ne produit aucun dialogue.
|
|
||||||
- [ ] L’ancienne session est conservée en cas d’échec.
|
|
||||||
- [ ] Aucun appel SQLite direct n’est ajouté.
|
|
||||||
- [ ] Aucun `exit()` direct n’est ajouté.
|
|
||||||
- [ ] `make` réussit.
|
|
||||||
- [ ] `make test` réussit.
|
|
||||||
- [ ] `git diff --check` ne retourne aucune erreur.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Audit attendu
|
|
||||||
|
|
||||||
Vérifier l’utilisation 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 l’absence 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 l’absence 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, l’utilisateur ne dépendra plus du terminal pour comprendre pourquoi une enquête n’a 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 l’exécution des traitements longs.
|
|
||||||
|
|
@ -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 d’empreintes ;
|
|
||||||
- copie de fichiers ;
|
|
||||||
- extraction de métadonnées ;
|
|
||||||
- lancement d’outils 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 d’activité. 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 d’annulation ;
|
|
||||||
- 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 s’appuyer sur :
|
|
||||||
|
|
||||||
```text
|
|
||||||
GTask
|
|
||||||
GCancellable
|
|
||||||
GMutex
|
|
||||||
gatomicrefcount
|
|
||||||
```
|
|
||||||
|
|
||||||
Il ne doit dépendre ni de GTK, ni de SQLite, ni d’une 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 s’exé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.
|
|
||||||
|
|
||||||
Lorsqu’une 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 d’erreur
|
|
||||||
|
|
||||||
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 l’exé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
|
|
||||||
l’appelant conserve worker_data
|
|
||||||
l’appelant 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 n’est 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 jusqu’au 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()
|
|
||||||
```
|
|
||||||
|
|
||||||
L’annulation 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 n’est 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 d’activité.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 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
|
|
||||||
l’appelant doit appeler g_free()
|
|
||||||
|
|
||||||
dup_error() :
|
|
||||||
nouvelle copie
|
|
||||||
l’appelant doit appeler g_error_free()
|
|
||||||
|
|
||||||
get_result() :
|
|
||||||
pointeur emprunté
|
|
||||||
ne doit jamais être libéré par l’appelant
|
|
||||||
```
|
|
||||||
|
|
||||||
`get_result()` ne doit être considéré comme valide qu’aprè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 d’exé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 jusqu’au 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 l’erreur 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 l’exé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 l’erreur ;
|
|
||||||
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 qu’aucune référence interne d’exé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.
|
|
||||||
|
|
||||||
L’appel 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 :
|
|
||||||
|
|
||||||
- l’appel du worker ;
|
|
||||||
- l’appel du callback utilisateur ;
|
|
||||||
- une fonction de destruction fournie par l’appelant ;
|
|
||||||
- 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 qu’une 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 d’annulation
|
|
||||||
|
|
||||||
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 n’est 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 l’appelant.
|
|
||||||
|
|
||||||
Vérifier que :
|
|
||||||
|
|
||||||
- la tâche reste vivante jusqu’au callback ;
|
|
||||||
- aucun accès mémoire invalide n’a lieu ;
|
|
||||||
- la destruction finale intervient après la fin de l’exé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 ;
|
|
||||||
- d’exécution de commande externe ;
|
|
||||||
- d’adaptateur 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 d’acceptation
|
|
||||||
|
|
||||||
- [ ] `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 qu’une fois.
|
|
||||||
- [ ] Le worker s’exécute dans un thread secondaire.
|
|
||||||
- [ ] Le callback final revient sur le contexte principal.
|
|
||||||
- [ ] La progression est comprise entre `0.0` et `1.0`.
|
|
||||||
- [ ] L’annulation est coopérative.
|
|
||||||
- [ ] Le résultat est conservé jusqu’à la destruction.
|
|
||||||
- [ ] L’erreur 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 n’est 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 l’absence 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 qu’aucun thread POSIX brut n’est 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 l’absence 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 n’est 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 d’une 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 d’activité au-dessus de cette abstraction.
|
|
||||||
|
|
@ -1,790 +0,0 @@
|
||||||
# Ticket #035 — File de tâches et panneau d’activité
|
|
||||||
|
|
||||||
## 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 l’annulation ;
|
|
||||||
- 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 l’interface.
|
|
||||||
|
|
||||||
## 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 qu’un 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 d’une référence supplémentaire ;
|
|
||||||
- l’appelant 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 l’appelant doit libérer.
|
|
||||||
|
|
||||||
### Retirer une tâche
|
|
||||||
|
|
||||||
```c
|
|
||||||
gboolean task_manager_remove(
|
|
||||||
TaskManager *task_manager,
|
|
||||||
BackgroundTask *task
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
La tâche n’est 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 l’appel.
|
|
||||||
|
|
||||||
Remplacer le callback existant doit détruire les anciennes données exactement une fois.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Domaine d’erreur
|
|
||||||
|
|
||||||
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 l’affichage 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 d’observation 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 d’annulation.
|
|
||||||
|
|
||||||
### 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 d’erreur sous forme courte.
|
|
||||||
|
|
||||||
Une tâche en cours doit afficher son message de progression lorsqu’il existe.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. Bouton d’annulation
|
|
||||||
|
|
||||||
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 l’application.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
L’ordre 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 l’interface, ajouter un bouton temporaire :
|
|
||||||
|
|
||||||
```text
|
|
||||||
Tâche de test
|
|
||||||
```
|
|
||||||
|
|
||||||
Il lance une `BackgroundTask` qui :
|
|
||||||
|
|
||||||
- dure environ deux secondes ;
|
|
||||||
- progresse de 0 à 100 % ;
|
|
||||||
- accepte l’annulation ;
|
|
||||||
- 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 ;
|
|
||||||
- d’historique permanent ;
|
|
||||||
- d’exécution de commandes ;
|
|
||||||
- de recherche DNS ;
|
|
||||||
- d’ExifTool ;
|
|
||||||
- de notifications système ;
|
|
||||||
- de tri avancé ;
|
|
||||||
- de pagination.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Critères d’acceptation
|
|
||||||
|
|
||||||
- [ ] `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.
|
|
||||||
- [ ] L’ordre 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 l’indé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 l’absence 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 d’outils externes sans bloquer l’interface.
|
|
||||||
|
|
@ -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 s’ils sont disponibles sur la machine.
|
|
||||||
|
|
||||||
Ce registre deviendra la source unique permettant à l’application 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 lorsqu’une 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 d’analyse ;
|
|
||||||
- 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 ;
|
|
||||||
- d’une liste de paquets spécifique ;
|
|
||||||
- d’outils 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 qu’un exécutable est disponible.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Périmètre
|
|
||||||
|
|
||||||
Ce ticket doit fournir :
|
|
||||||
|
|
||||||
1. un registre opaque `ToolRegistry` ;
|
|
||||||
2. une représentation opaque ou privée d’un outil enregistré ;
|
|
||||||
3. l’enregistrement d’un outil à partir de métadonnées stables ;
|
|
||||||
4. la détection de l’exécutable dans le `PATH` ;
|
|
||||||
5. la mémorisation du chemin détecté ;
|
|
||||||
6. la distinction entre dépendance obligatoire et optionnelle ;
|
|
||||||
7. la recherche d’un 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 lorsqu’elle 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 d’interface 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 l’application.
|
|
||||||
|
|
||||||
L’exé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 l’importance d’une 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 d’un 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 l’utilisateur.
|
|
||||||
|
|
||||||
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 l’outil est absent ou n’a pas encore été vérifié.
|
|
||||||
|
|
||||||
#### detected_version
|
|
||||||
|
|
||||||
Version connue de l’outil.
|
|
||||||
|
|
||||||
Ce champ peut rester `NULL` dans ce ticket. Le registre doit néanmoins pouvoir la mémoriser pour les tickets suivants.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Domaine d’erreur
|
|
||||||
|
|
||||||
Créer un domaine d’erreur 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
|
|
||||||
|
|
||||||
L’API 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 ;
|
|
||||||
- l’identifiant doit être unique ;
|
|
||||||
- le registre doit dupliquer les chaînes reçues ;
|
|
||||||
- l’outil 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 l’API GLib adaptée, par exemple :
|
|
||||||
|
|
||||||
```c
|
|
||||||
g_find_program_in_path()
|
|
||||||
```
|
|
||||||
|
|
||||||
Règles :
|
|
||||||
|
|
||||||
- si l’exécutable est trouvé :
|
|
||||||
- état `AVAILABLE` ;
|
|
||||||
- chemin détecté mémorisé ;
|
|
||||||
- s’il n’est 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 :
|
|
||||||
|
|
||||||
- l’outil 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é d’interroger les exécutables.
|
|
||||||
|
|
||||||
### État global
|
|
||||||
|
|
||||||
```c
|
|
||||||
gboolean tool_registry_has_missing_required_tools(
|
|
||||||
const ToolRegistry *tool_registry
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
Cette fonction retourne `TRUE` lorsqu’au 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 l’appelant.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Implémentation interne recommandée
|
|
||||||
|
|
||||||
Le registre peut utiliser :
|
|
||||||
|
|
||||||
```c
|
|
||||||
GPtrArray
|
|
||||||
```
|
|
||||||
|
|
||||||
pour conserver l’ordre d’enregistrement.
|
|
||||||
|
|
||||||
Une recherche linéaire par identifiant est acceptable pour cette première version, car le nombre d’outils 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 d’exé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 d’autres 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 d’exé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 l’outil est disponible ;
|
|
||||||
9. restaurer impérativement le `PATH` initial ;
|
|
||||||
10. supprimer les fichiers temporaires.
|
|
||||||
|
|
||||||
Le test doit restaurer l’environnement même en cas d’échec d’une 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 d’outils égal à zéro ;
|
|
||||||
- recherche d’un identifiant absent ;
|
|
||||||
- destruction sans erreur.
|
|
||||||
|
|
||||||
### 2. Enregistrement valide
|
|
||||||
|
|
||||||
Vérifier :
|
|
||||||
|
|
||||||
- nombre d’outils ;
|
|
||||||
- conservation de l’ordre ;
|
|
||||||
- contenu des accesseurs ;
|
|
||||||
- état initial `UNKNOWN` ;
|
|
||||||
- chemin initial `NULL` ;
|
|
||||||
- version initiale `NULL`.
|
|
||||||
|
|
||||||
### 3. Duplication des chaînes
|
|
||||||
|
|
||||||
Créer des chaînes dynamiques, enregistrer l’outil, 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 d’exé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 qu’un outil peut passer :
|
|
||||||
|
|
||||||
```text
|
|
||||||
MISSING → AVAILABLE
|
|
||||||
```
|
|
||||||
|
|
||||||
puis :
|
|
||||||
|
|
||||||
```text
|
|
||||||
AVAILABLE → MISSING
|
|
||||||
```
|
|
||||||
|
|
||||||
après modification contrôlée du `PATH`.
|
|
||||||
|
|
||||||
### 10. Version
|
|
||||||
|
|
||||||
Vérifier :
|
|
||||||
|
|
||||||
- ajout d’une version ;
|
|
||||||
- remplacement d’une 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 d’acceptation
|
|
||||||
|
|
||||||
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 n’est introduite ;
|
|
||||||
- aucun appel shell n’est utilisé ;
|
|
||||||
- tous les tests passent ;
|
|
||||||
- aucune erreur mémoire liée au module n’est 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 d’organisation 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 d’outils OSINT ;
|
|
||||||
5. affichage des dépendances dans l’interface ;
|
|
||||||
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 d’installation.
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -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 l’application. 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à d’enregistrer 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 l’application ;
|
|
||||||
- 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 l’API publique de `ToolRegistry`, sauf nécessité démontrée pendant l’implémentation.
|
|
||||||
|
|
||||||
## Responsabilités de ToolCatalog
|
|
||||||
|
|
||||||
Le module doit :
|
|
||||||
|
|
||||||
1. conserver une liste statique et immuable des outils connus ;
|
|
||||||
2. garantir l’unicité de leurs identifiants ;
|
|
||||||
3. exposer leurs informations descriptives ;
|
|
||||||
4. enregistrer l’ensemble 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 l’annulation ;
|
|
||||||
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 l’interface 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 l’application 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 l’appelant.
|
|
||||||
|
|
||||||
## Domaine d’erreur
|
|
||||||
|
|
||||||
```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 d’une 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 d’enregistrement
|
|
||||||
|
|
||||||
`tool_catalog_register_defaults()` doit vérifier avant toute insertion qu’aucun identifiant du catalogue n’existe déjà dans le registre.
|
|
||||||
|
|
||||||
En cas de doublon :
|
|
||||||
|
|
||||||
- aucune entrée ne doit être ajoutée ;
|
|
||||||
- la fonction retourne `FALSE` ;
|
|
||||||
- l’erreur 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 d’un appel valide :
|
|
||||||
|
|
||||||
```c
|
|
||||||
*out_version = NULL;
|
|
||||||
```
|
|
||||||
|
|
||||||
Cas d’erreur :
|
|
||||||
|
|
||||||
```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 d’une 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 d’erreur.
|
|
||||||
|
|
||||||
La sortie ne doit pas être interprétée lorsque le processus n’a 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 d’exploitable, 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 à l’appelant 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 l’exé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 d’entré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 d’un 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 d’acceptation
|
|
||||||
|
|
||||||
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 s’enregistre dans un registre vide ;
|
|
||||||
- un doublon empêche toute insertion partielle ;
|
|
||||||
- la détection utilise `ToolProcess` ;
|
|
||||||
- aucun shell n’est 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 ;
|
|
||||||
- l’inspection est limitée à 4096 octets par flux ;
|
|
||||||
- un code non nul ou un signal est refusé ;
|
|
||||||
- l’annulation produit `G_IO_ERROR_CANCELLED` ;
|
|
||||||
- la fonction ne modifie pas directement le registre ;
|
|
||||||
- les versions successives sont indépendantes ;
|
|
||||||
- les tests ne dépendent d’aucun outil installé ;
|
|
||||||
- tous les tests passent ;
|
|
||||||
- aucune fuite mémoire n’est 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 d’un outil disponible ;
|
|
||||||
6. stocker explicitement la version avec `tool_registry_set_version()`.
|
|
||||||
|
|
||||||
Cette démonstration restera hors de l’interface 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 l’application, enregistrer le catalogue, rafraîchir les disponibilités hors du thread GTK, détecter les versions et publier une tâche dans `TaskManager`.
|
|
||||||
|
|
@ -1,681 +0,0 @@
|
||||||
# TICKET-040 — Inventaire et qualification de l’arsenal OSINT
|
|
||||||
|
|
||||||
## Statut
|
|
||||||
|
|
||||||
À faire
|
|
||||||
|
|
||||||
## Priorité
|
|
||||||
|
|
||||||
Très haute
|
|
||||||
|
|
||||||
## Nature
|
|
||||||
|
|
||||||
Recherche, audit juridique, architecture et préparation des futures intégrations.
|
|
||||||
|
|
||||||
La présence d’un outil dans cet inventaire ne signifie pas qu’il est approuvé.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Objectif
|
|
||||||
|
|
||||||
Constituer une base large, structurée et maintenable d’outils OSINT pouvant être intégrés à Labfy Investigation afin d’aider à 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 d’escrocs 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 d’authentification ;
|
|
||||||
- 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 d’authentification ;
|
|
||||||
- credential stuffing ;
|
|
||||||
- phishing ;
|
|
||||||
- vol ou réutilisation de jetons ;
|
|
||||||
- usurpation ;
|
|
||||||
- social engineering ;
|
|
||||||
- accès à une ressource privée ;
|
|
||||||
- modification d’une 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 d’une 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 d’une adresse sur des services | P0 |
|
|
||||||
| MOSINT | `alpkeskin/mosint` | Enrichissement d’e-mails | P2 |
|
|
||||||
| h8mail | `khast3x/h8mail` | Jeux de données autorisés uniquement | P1 restreint |
|
|
||||||
| Ignorant | `megadose/ignorant` | Présence d’un 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 d’enrichissement | 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 d’un 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 d’actifs | 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 d’archives 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 d’archives | P1 |
|
|
||||||
| SingleFile | `gildas-lormeau/SingleFile` | Copie autonome d’une page | P0 |
|
|
||||||
| monolith | `Y2Z/monolith` | Sauvegarde autonome | P1 |
|
|
||||||
| grab-site | `ArchiveTeam/grab-site` | Archivage WARC | Revue |
|
|
||||||
| Heritrix | `internetarchive/heritrix3` | Crawl d’archivage | 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 d’images | 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 d’images | 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 l’appel.
|
|
||||||
|
|
||||||
## 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 d’indicateurs | 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 d’enquê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 d’identifier publiquement son titulaire. Labfy se limite à la validation, au pays, à la banque lorsqu’elle 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 d’exploitation
|
|
||||||
outils de credential stuffing
|
|
||||||
outils de phishing
|
|
||||||
```
|
|
||||||
|
|
||||||
Décision initiale :
|
|
||||||
|
|
||||||
```text
|
|
||||||
integration_mode = REJECTED
|
|
||||||
decision = REJECT
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Vague prioritaire pour l’affaire d’escroquerie
|
|
||||||
|
|
||||||
## 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 d’installation 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é d’inté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 l’outil
|
|
||||||
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 d’inté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 d’une enquête réelle.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Critères d’acceptation
|
|
||||||
|
|
||||||
- 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é à l’utilisateur ;
|
|
||||||
- inventaire Markdown, JSON et CSV ;
|
|
||||||
- décisions justifiées ;
|
|
||||||
- feuille de route d’inté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
|
|
||||||
```
|
|
||||||
|
|
||||||
Loading…
Reference in a new issue