Compare commits
No commits in common. "6b43379e75784b2b87add980ad2e8ecd7da0dd7a" and "8adfc3f10620e6c26e6f190973cb08fdc9939f10" have entirely different histories.
6b43379e75
...
8adfc3f106
26 changed files with 65 additions and 1511 deletions
|
|
@ -2,9 +2,6 @@
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Glisser-déposer confirmé des extractions texte vers le graphe, avec
|
|
||||||
persistance de leur association à une entité.
|
|
||||||
|
|
||||||
- Initialisation du cycle de vie de l'application.
|
- Initialisation du cycle de vie de l'application.
|
||||||
- Création du module `Application`.
|
- Création du module `Application`.
|
||||||
- Intégration de GTK4.
|
- Intégration de GTK4.
|
||||||
|
|
|
||||||
24
Makefile
24
Makefile
|
|
@ -128,7 +128,6 @@ TEST_EML_ANALYZER := tests/test_eml_analyzer
|
||||||
TEST_IBAN_ANALYZER := tests/test_iban_analyzer
|
TEST_IBAN_ANALYZER := tests/test_iban_analyzer
|
||||||
TEST_EXIFTOOL_METADATA := tests/test_exiftool_metadata
|
TEST_EXIFTOOL_METADATA := tests/test_exiftool_metadata
|
||||||
TEST_PDF_PASSWORD_RECOVERY := tests/test_pdf_password_recovery
|
TEST_PDF_PASSWORD_RECOVERY := tests/test_pdf_password_recovery
|
||||||
TEST_EXTRACTION_DROP_SERVICE := tests/test_extraction_drop_service
|
|
||||||
|
|
||||||
all: $(TARGET)
|
all: $(TARGET)
|
||||||
|
|
||||||
|
|
@ -198,22 +197,6 @@ $(TEST_ERROR): \
|
||||||
src/database/error.c
|
src/database/error.c
|
||||||
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) -lsqlite3
|
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) -lsqlite3
|
||||||
|
|
||||||
$(TEST_EXTRACTION_DROP_SERVICE): \
|
|
||||||
tests/test_extraction_drop_service.c \
|
|
||||||
src/core/extraction_drop_service.c \
|
|
||||||
src/core/file_hash.c \
|
|
||||||
src/dao/evidence_dao.c \
|
|
||||||
src/dao/entity_dao.c \
|
|
||||||
src/dao/evidence_entity_dao.c \
|
|
||||||
src/models/evidence_record.c \
|
|
||||||
src/models/entity_record.c \
|
|
||||||
src/database/database.c \
|
|
||||||
src/database/schema.c \
|
|
||||||
src/database/statement.c \
|
|
||||||
src/database/transaction.c \
|
|
||||||
src/database/error.c
|
|
||||||
$(CC) $(TEST_CFLAGS) -Wpedantic $^ -o $@ $(TEST_LDFLAGS) -lsqlite3
|
|
||||||
|
|
||||||
$(TEST_INVESTIGATION_RECORD): \
|
$(TEST_INVESTIGATION_RECORD): \
|
||||||
tests/test_investigation_record.c \
|
tests/test_investigation_record.c \
|
||||||
src/models/investigation_record.c
|
src/models/investigation_record.c
|
||||||
|
|
@ -763,8 +746,7 @@ test: \
|
||||||
$(TEST_EML_ANALYZER) \
|
$(TEST_EML_ANALYZER) \
|
||||||
$(TEST_IBAN_ANALYZER) \
|
$(TEST_IBAN_ANALYZER) \
|
||||||
$(TEST_EXIFTOOL_METADATA) \
|
$(TEST_EXIFTOOL_METADATA) \
|
||||||
$(TEST_PDF_PASSWORD_RECOVERY) \
|
$(TEST_PDF_PASSWORD_RECOVERY)
|
||||||
$(TEST_EXTRACTION_DROP_SERVICE)
|
|
||||||
@echo "Exécution des tests..."
|
@echo "Exécution des tests..."
|
||||||
@./$(TEST_NODE)
|
@./$(TEST_NODE)
|
||||||
@./$(TEST_TREE_MODEL)
|
@./$(TEST_TREE_MODEL)
|
||||||
|
|
@ -827,7 +809,6 @@ test: \
|
||||||
@$(TEST_IBAN_ANALYZER)
|
@$(TEST_IBAN_ANALYZER)
|
||||||
@$(TEST_EXIFTOOL_METADATA)
|
@$(TEST_EXIFTOOL_METADATA)
|
||||||
@$(TEST_PDF_PASSWORD_RECOVERY)
|
@$(TEST_PDF_PASSWORD_RECOVERY)
|
||||||
@$(TEST_EXTRACTION_DROP_SERVICE)
|
|
||||||
@echo "Tous les tests sont valides."
|
@echo "Tous les tests sont valides."
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|
@ -893,8 +874,7 @@ clean:
|
||||||
$(TEST_SOCIAL_ACCOUNT_SERVICE) \
|
$(TEST_SOCIAL_ACCOUNT_SERVICE) \
|
||||||
$(TEST_SOCIAL_PLATFORM) \
|
$(TEST_SOCIAL_PLATFORM) \
|
||||||
$(TEST_PERSON_ENTITY_SERVICE) \
|
$(TEST_PERSON_ENTITY_SERVICE) \
|
||||||
$(TEST_EML_ANALYZER) \
|
$(TEST_EML_ANALYZER)
|
||||||
$(TEST_EXTRACTION_DROP_SERVICE)
|
|
||||||
|
|
||||||
-include $(DEP)
|
-include $(DEP)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -173,9 +173,6 @@ Le socle actuel comprend notamment :
|
||||||
sorties standard et d'erreur, et objets créés ou réutilisés ;
|
sorties standard et d'erreur, et objets créés ou réutilisés ;
|
||||||
- vérification manuelle de l'intégrité des sorties OSINT enregistrées, sans
|
- vérification manuelle de l'intégrité des sorties OSINT enregistrées, sans
|
||||||
réécriture de l'empreinte ou des données contrôlées.
|
réécriture de l'empreinte ou des données contrôlées.
|
||||||
- glisser-déposer des extractions texte depuis l'arborescence vers le graphe,
|
|
||||||
avec confirmation explicite, création d'entité ou rattachement à une entité
|
|
||||||
existante, sans déplacement du fichier produit ;
|
|
||||||
|
|
||||||
Les outils actuellement présents dans le catalogue initial sont :
|
Les outils actuellement présents dans le catalogue initial sont :
|
||||||
|
|
||||||
|
|
|
||||||
45
TICKET-105.md
Normal file
45
TICKET-105.md
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
# Ticket 105 — Glisser-déposer des extractions vers le graphe
|
||||||
|
|
||||||
|
## Objectif
|
||||||
|
|
||||||
|
Permettre de prendre une extraction depuis `Dossier → Extractions` et de la
|
||||||
|
déposer dans le graphe afin de créer ou compléter les liens de l’enquête.
|
||||||
|
|
||||||
|
## Fonctionnement attendu
|
||||||
|
|
||||||
|
1. Une extraction sélectionnée dans la Sidebar peut être déplacée.
|
||||||
|
2. Le graphe accepte le dépôt uniquement sur une zone valide.
|
||||||
|
3. Une fenêtre de confirmation affiche le fichier, sa source et son contenu
|
||||||
|
textuel disponible.
|
||||||
|
4. L’utilisateur choisit explicitement :
|
||||||
|
- créer une nouvelle entité à partir de l’extraction ;
|
||||||
|
- rattacher l’extraction à une entité existante ;
|
||||||
|
- annuler.
|
||||||
|
5. L’association est enregistrée en base et visible dans la fiche concernée.
|
||||||
|
6. Une relation graphique peut être créée sans déplacer ni dupliquer le
|
||||||
|
fichier original.
|
||||||
|
|
||||||
|
## Contraintes
|
||||||
|
|
||||||
|
- Ne jamais créer de relation silencieusement.
|
||||||
|
- Refuser les fichiers hors du dossier de l’enquête.
|
||||||
|
- Conserver les associations déjà présentes.
|
||||||
|
- Utiliser les API GTK de glisser-déposer et les commentaires Doxygen prévus
|
||||||
|
par le projet.
|
||||||
|
|
||||||
|
## Critères d’acceptation
|
||||||
|
|
||||||
|
- Un fichier `.txt` d’OSINT est déplaçable depuis la Sidebar.
|
||||||
|
- Le dépôt sur le graphe ouvre le dialogue de choix.
|
||||||
|
- L’entité ou l’association créée survit à la réouverture de l’enquête.
|
||||||
|
- Les dépôts annulés ne modifient ni la base ni le graphe.
|
||||||
|
- Tests unitaires des chemins invalides, du choix d’action et de la
|
||||||
|
persistance.
|
||||||
|
|
||||||
|
## Vérification
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make -j2
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* Labfy Investigation
|
* Labfy Investigation
|
||||||
*
|
*
|
||||||
* Extensions idempotentes du schéma SQLite courant V8
|
* Extensions idempotentes du schéma SQLite courant V6
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -93,15 +93,6 @@ CREATE TABLE IF NOT EXISTS graph_layout_positions
|
||||||
CHECK (length(updated_at) = 20)
|
CHECK (length(updated_at) = 20)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS graph_viewport
|
|
||||||
(
|
|
||||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
||||||
zoom REAL NOT NULL CHECK (zoom = zoom AND zoom > 0),
|
|
||||||
offset_x REAL NOT NULL CHECK (offset_x = offset_x),
|
|
||||||
offset_y REAL NOT NULL CHECK (offset_y = offset_y),
|
|
||||||
updated_at TEXT NOT NULL CHECK (length(updated_at) = 20)
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Migration idempotente des positions d'entités déjà enregistrées. */
|
/* Migration idempotente des positions d'entités déjà enregistrées. */
|
||||||
INSERT OR IGNORE INTO graph_layout_positions(node_id, x, y, updated_at)
|
INSERT OR IGNORE INTO graph_layout_positions(node_id, x, y, updated_at)
|
||||||
SELECT entity_id, x, y, updated_at
|
SELECT entity_id, x, y, updated_at
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
CREATE TABLE IF NOT EXISTS graph_viewport
|
|
||||||
(
|
|
||||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
||||||
zoom REAL NOT NULL CHECK (zoom = zoom AND zoom > 0),
|
|
||||||
offset_x REAL NOT NULL CHECK (offset_x = offset_x),
|
|
||||||
offset_y REAL NOT NULL CHECK (offset_y = offset_y),
|
|
||||||
updated_at TEXT NOT NULL CHECK (length(updated_at) = 20)
|
|
||||||
);
|
|
||||||
|
|
@ -2269,12 +2269,6 @@ Cette stratégie préserve les positions existantes tout en permettant aux
|
||||||
nœuds de relation de suivre exactement le même cycle de chargement,
|
nœuds de relation de suivre exactement le même cycle de chargement,
|
||||||
d'enregistrement et de réinitialisation que les nœuds d'entité.
|
d'enregistrement et de réinitialisation que les nœuds d'entité.
|
||||||
|
|
||||||
Le cadrage global est stocké séparément dans la ligne unique de
|
|
||||||
`graph_viewport`. Elle conserve le niveau de zoom et les décalages horizontal
|
|
||||||
et vertical du canevas. Ces valeurs sont enregistrées à la fermeture de
|
|
||||||
l'enquête et restaurées lors de son premier chargement, afin que la vue
|
|
||||||
réapparaisse exactement à l'endroit où l'utilisateur l'avait laissée.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 12. Conclusion
|
# 12. Conclusion
|
||||||
|
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
/******************************************************************************
|
|
||||||
* @file extraction_drop_service.h
|
|
||||||
* @brief Validation et intégration d'une extraction déposée sur le graphe.
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
#ifndef LABFY_INVESTIGATION_EXTRACTION_DROP_SERVICE_H
|
|
||||||
#define LABFY_INVESTIGATION_EXTRACTION_DROP_SERVICE_H
|
|
||||||
|
|
||||||
#include "database/database.h"
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
typedef struct ExtractionDropInfo ExtractionDropInfo;
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_INVALID_ARGUMENT,
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_OUTSIDE_INVESTIGATION,
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_INVALID_FILE,
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_READ,
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_DATABASE
|
|
||||||
} ExtractionDropServiceError;
|
|
||||||
|
|
||||||
#define EXTRACTION_DROP_SERVICE_ERROR extraction_drop_service_error_quark()
|
|
||||||
|
|
||||||
GQuark extraction_drop_service_error_quark(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Valide et lit une extraction texte appartenant à l'enquête.
|
|
||||||
*
|
|
||||||
* Le fichier doit être un `.txt` régulier situé sous
|
|
||||||
* `02_Preuves_Traitees/Extractions`. Aucun fichier n'est déplacé ou modifié.
|
|
||||||
*/
|
|
||||||
ExtractionDropInfo *extraction_drop_service_prepare(
|
|
||||||
const char *investigation_root,
|
|
||||||
const char *file_path,
|
|
||||||
GError **error
|
|
||||||
);
|
|
||||||
|
|
||||||
void extraction_drop_info_free(ExtractionDropInfo *info);
|
|
||||||
const char *extraction_drop_info_get_file_path(const ExtractionDropInfo *info);
|
|
||||||
const char *extraction_drop_info_get_relative_path(
|
|
||||||
const ExtractionDropInfo *info);
|
|
||||||
const char *extraction_drop_info_get_file_name(const ExtractionDropInfo *info);
|
|
||||||
const char *extraction_drop_info_get_source(const ExtractionDropInfo *info);
|
|
||||||
const char *extraction_drop_info_get_content(const ExtractionDropInfo *info);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Enregistre l'extraction comme preuve traitée et la lie à une entité.
|
|
||||||
*
|
|
||||||
* Une preuve ou une association déjà présente est réutilisée.
|
|
||||||
*/
|
|
||||||
gboolean extraction_drop_service_attach(
|
|
||||||
Database *database,
|
|
||||||
const ExtractionDropInfo *info,
|
|
||||||
const char *entity_identifier,
|
|
||||||
GError **error
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Crée une entité générique puis lui associe l'extraction.
|
|
||||||
*
|
|
||||||
* @return UUID nouvellement alloué dans out_entity_identifier, si demandé.
|
|
||||||
*/
|
|
||||||
gboolean extraction_drop_service_create_entity(
|
|
||||||
Database *database,
|
|
||||||
const ExtractionDropInfo *info,
|
|
||||||
char **out_entity_identifier,
|
|
||||||
GError **error
|
|
||||||
);
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -143,23 +143,6 @@ gboolean graph_node_position_dao_delete_all(
|
||||||
GError **error
|
GError **error
|
||||||
);
|
);
|
||||||
|
|
||||||
gboolean graph_node_position_dao_load_viewport(
|
|
||||||
GraphNodePositionDao *position_dao,
|
|
||||||
double *zoom,
|
|
||||||
double *offset_x,
|
|
||||||
double *offset_y,
|
|
||||||
gboolean *found,
|
|
||||||
GError **error
|
|
||||||
);
|
|
||||||
|
|
||||||
gboolean graph_node_position_dao_upsert_viewport(
|
|
||||||
GraphNodePositionDao *position_dao,
|
|
||||||
double zoom,
|
|
||||||
double offset_x,
|
|
||||||
double offset_y,
|
|
||||||
GError **error
|
|
||||||
);
|
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,6 @@ bool schema_install_v5(Database *database);
|
||||||
bool schema_install_v6(Database *database);
|
bool schema_install_v6(Database *database);
|
||||||
|
|
||||||
bool schema_install_v7(Database *database);
|
bool schema_install_v7(Database *database);
|
||||||
bool schema_install_v8(Database *database);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Garantit la présence des extensions du schéma courant V2.
|
* @brief Garantit la présence des extensions du schéma courant V2.
|
||||||
|
|
|
||||||
|
|
@ -126,12 +126,6 @@ typedef void (*MainWindowGraphNodeMovedCallback)(
|
||||||
gpointer user_data
|
gpointer user_data
|
||||||
);
|
);
|
||||||
|
|
||||||
typedef void (*MainWindowExtractionDropCallback)(
|
|
||||||
const char *file_path,
|
|
||||||
const char *target_entity_identifier,
|
|
||||||
gpointer user_data
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callback appelé lors d'une demande de réinitialisation du graphe.
|
* @brief Callback appelé lors d'une demande de réinitialisation du graphe.
|
||||||
*
|
*
|
||||||
|
|
@ -252,12 +246,6 @@ void main_window_set_graph_node_moved_callback(
|
||||||
gpointer user_data
|
gpointer user_data
|
||||||
);
|
);
|
||||||
|
|
||||||
void main_window_set_extraction_drop_callback(
|
|
||||||
MainWindow *main_window,
|
|
||||||
MainWindowExtractionDropCallback callback,
|
|
||||||
gpointer user_data
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Définit le callback de demande de réinitialisation du graphe.
|
* @brief Définit le callback de demande de réinitialisation du graphe.
|
||||||
*
|
*
|
||||||
|
|
@ -526,11 +514,6 @@ void main_window_set_graph(
|
||||||
const InvestigationGraphLayout *graph_layout
|
const InvestigationGraphLayout *graph_layout
|
||||||
);
|
);
|
||||||
|
|
||||||
gboolean main_window_get_graph_view_transform(
|
|
||||||
MainWindow *main_window, double *zoom, double *offset_x, double *offset_y);
|
|
||||||
void main_window_set_graph_view_transform(
|
|
||||||
MainWindow *main_window, double zoom, double offset_x, double offset_y);
|
|
||||||
|
|
||||||
/** @brief Sélectionne une relation dans le graphe affiché. */
|
/** @brief Sélectionne une relation dans le graphe affiché. */
|
||||||
gboolean main_window_select_graph_relation(
|
gboolean main_window_select_graph_relation(
|
||||||
MainWindow *main_window,
|
MainWindow *main_window,
|
||||||
|
|
|
||||||
|
|
@ -69,9 +69,6 @@ typedef void (*EntityDetailsPanelPersonNameCallback)(
|
||||||
/** @brief Callback appelé pour gérer les preuves d'une personne. */
|
/** @brief Callback appelé pour gérer les preuves d'une personne. */
|
||||||
typedef void (*EntityDetailsPanelPersonEvidenceCallback)(
|
typedef void (*EntityDetailsPanelPersonEvidenceCallback)(
|
||||||
const char *entity_identifier, gpointer user_data);
|
const char *entity_identifier, gpointer user_data);
|
||||||
/** @brief Callback appelé lorsqu'une preuve associée est activée. */
|
|
||||||
typedef void (*EntityDetailsPanelEvidenceActivatedCallback)(
|
|
||||||
const char *evidence_identifier, gpointer user_data);
|
|
||||||
/** @brief Callback appelé pour ouvrir les recherches OSINT compatibles. */
|
/** @brief Callback appelé pour ouvrir les recherches OSINT compatibles. */
|
||||||
typedef void (*EntityDetailsPanelOsintCallback)(
|
typedef void (*EntityDetailsPanelOsintCallback)(
|
||||||
const char *entity_identifier, gpointer user_data);
|
const char *entity_identifier, gpointer user_data);
|
||||||
|
|
@ -181,10 +178,6 @@ void entity_details_panel_set_person_name_callback(
|
||||||
void entity_details_panel_set_person_evidence_callback(
|
void entity_details_panel_set_person_evidence_callback(
|
||||||
EntityDetailsPanel *details_panel,
|
EntityDetailsPanel *details_panel,
|
||||||
EntityDetailsPanelPersonEvidenceCallback callback, gpointer user_data);
|
EntityDetailsPanelPersonEvidenceCallback callback, gpointer user_data);
|
||||||
void entity_details_panel_set_evidence_activated_callback(
|
|
||||||
EntityDetailsPanel *details_panel,
|
|
||||||
EntityDetailsPanelEvidenceActivatedCallback callback,
|
|
||||||
gpointer user_data);
|
|
||||||
/** @brief Définit le callback du bouton de recherche OSINT. */
|
/** @brief Définit le callback du bouton de recherche OSINT. */
|
||||||
void entity_details_panel_set_osint_callback(EntityDetailsPanel *details_panel,
|
void entity_details_panel_set_osint_callback(EntityDetailsPanel *details_panel,
|
||||||
EntityDetailsPanelOsintCallback callback, gpointer user_data);
|
EntityDetailsPanelOsintCallback callback, gpointer user_data);
|
||||||
|
|
|
||||||
|
|
@ -71,13 +71,6 @@ typedef void (*InvestigationGraphViewNodeMovedCallback)(
|
||||||
gpointer user_data
|
gpointer user_data
|
||||||
);
|
);
|
||||||
|
|
||||||
/** @brief Callback appelé lorsqu'une extraction est déposée sur le graphe. */
|
|
||||||
typedef void (*InvestigationGraphViewExtractionDropCallback)(
|
|
||||||
const char *file_path,
|
|
||||||
const char *target_entity_identifier,
|
|
||||||
gpointer user_data
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Crée une nouvelle vue graphique vide.
|
* @brief Crée une nouvelle vue graphique vide.
|
||||||
*
|
*
|
||||||
|
|
@ -177,12 +170,6 @@ void investigation_graph_view_set_node_moved_callback(
|
||||||
gpointer user_data
|
gpointer user_data
|
||||||
);
|
);
|
||||||
|
|
||||||
void investigation_graph_view_set_extraction_drop_callback(
|
|
||||||
InvestigationGraphView *graph_view,
|
|
||||||
InvestigationGraphViewExtractionDropCallback callback,
|
|
||||||
gpointer user_data
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retourne l'entité actuellement sélectionnée.
|
* @brief Retourne l'entité actuellement sélectionnée.
|
||||||
*
|
*
|
||||||
|
|
@ -247,26 +234,6 @@ void investigation_graph_view_reset_view(
|
||||||
InvestigationGraphView *graph_view
|
InvestigationGraphView *graph_view
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Lit le zoom et le déplacement courants du canvas.
|
|
||||||
*
|
|
||||||
* @return TRUE lorsqu'un graphe est actuellement présenté.
|
|
||||||
*/
|
|
||||||
gboolean investigation_graph_view_get_view_transform(
|
|
||||||
const InvestigationGraphView *graph_view,
|
|
||||||
double *zoom,
|
|
||||||
double *offset_x,
|
|
||||||
double *offset_y
|
|
||||||
);
|
|
||||||
|
|
||||||
/** @brief Restaure un zoom et un déplacement précédemment lus. */
|
|
||||||
void investigation_graph_view_set_view_transform(
|
|
||||||
InvestigationGraphView *graph_view,
|
|
||||||
double zoom,
|
|
||||||
double offset_x,
|
|
||||||
double offset_y
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Restaure la disposition déterministe initiale des nœuds.
|
* @brief Restaure la disposition déterministe initiale des nœuds.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -56,12 +56,6 @@ typedef void (*WorkspaceGraphNodeMovedCallback)(
|
||||||
gpointer user_data
|
gpointer user_data
|
||||||
);
|
);
|
||||||
|
|
||||||
typedef void (*WorkspaceExtractionDropCallback)(
|
|
||||||
const char *file_path,
|
|
||||||
const char *target_entity_identifier,
|
|
||||||
gpointer user_data
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callback appelé lors d'une demande de réinitialisation du graphe.
|
* @brief Callback appelé lors d'une demande de réinitialisation du graphe.
|
||||||
*
|
*
|
||||||
|
|
@ -129,8 +123,6 @@ typedef void (*WorkspacePersonNameCallback)(const char *entity_identifier,
|
||||||
/** @brief Callback appelé pour gérer les preuves d'une personne. */
|
/** @brief Callback appelé pour gérer les preuves d'une personne. */
|
||||||
typedef void (*WorkspacePersonEvidenceCallback)(
|
typedef void (*WorkspacePersonEvidenceCallback)(
|
||||||
const char *entity_identifier, gpointer user_data);
|
const char *entity_identifier, gpointer user_data);
|
||||||
typedef void (*WorkspaceEvidenceActivatedCallback)(
|
|
||||||
const char *evidence_identifier, gpointer user_data);
|
|
||||||
/** @brief Callback appelé quand une entité devient sélectionnée. */
|
/** @brief Callback appelé quand une entité devient sélectionnée. */
|
||||||
typedef void (*WorkspaceEntitySelectedCallback)(
|
typedef void (*WorkspaceEntitySelectedCallback)(
|
||||||
const char *entity_identifier, gpointer user_data);
|
const char *entity_identifier, gpointer user_data);
|
||||||
|
|
@ -325,12 +317,6 @@ void workspace_set_graph_node_moved_callback(
|
||||||
gpointer user_data
|
gpointer user_data
|
||||||
);
|
);
|
||||||
|
|
||||||
void workspace_set_extraction_drop_callback(
|
|
||||||
Workspace *workspace,
|
|
||||||
WorkspaceExtractionDropCallback callback,
|
|
||||||
gpointer user_data
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Définit le callback de demande de réinitialisation du graphe.
|
* @brief Définit le callback de demande de réinitialisation du graphe.
|
||||||
*
|
*
|
||||||
|
|
@ -418,10 +404,6 @@ void workspace_set_person_name_callback(Workspace *workspace,
|
||||||
/** @brief Définit le callback de gestion des preuves d'une personne. */
|
/** @brief Définit le callback de gestion des preuves d'une personne. */
|
||||||
void workspace_set_person_evidence_callback(Workspace *workspace,
|
void workspace_set_person_evidence_callback(Workspace *workspace,
|
||||||
WorkspacePersonEvidenceCallback callback, gpointer user_data);
|
WorkspacePersonEvidenceCallback callback, gpointer user_data);
|
||||||
void workspace_set_evidence_activated_callback(
|
|
||||||
Workspace *workspace,
|
|
||||||
WorkspaceEvidenceActivatedCallback callback,
|
|
||||||
gpointer user_data);
|
|
||||||
/** @brief Définit le callback de sélection d'une entité. */
|
/** @brief Définit le callback de sélection d'une entité. */
|
||||||
void workspace_set_entity_selected_callback(Workspace *workspace,
|
void workspace_set_entity_selected_callback(Workspace *workspace,
|
||||||
WorkspaceEntitySelectedCallback callback, gpointer user_data);
|
WorkspaceEntitySelectedCallback callback, gpointer user_data);
|
||||||
|
|
@ -447,11 +429,6 @@ void workspace_set_graph(
|
||||||
const InvestigationGraphLayout *graph_layout
|
const InvestigationGraphLayout *graph_layout
|
||||||
);
|
);
|
||||||
|
|
||||||
gboolean workspace_get_graph_view_transform(
|
|
||||||
Workspace *workspace, double *zoom, double *offset_x, double *offset_y);
|
|
||||||
void workspace_set_graph_view_transform(
|
|
||||||
Workspace *workspace, double zoom, double offset_x, double offset_y);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Affiche une erreur de chargement du graphe.
|
* @brief Affiche une erreur de chargement du graphe.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,6 @@
|
||||||
#include "core/exiftool_metadata.h"
|
#include "core/exiftool_metadata.h"
|
||||||
#include "views/metadata_analysis_dialog.h"
|
#include "views/metadata_analysis_dialog.h"
|
||||||
#include "core/pdf_password_recovery.h"
|
#include "core/pdf_password_recovery.h"
|
||||||
#include "core/extraction_drop_service.h"
|
|
||||||
#include "views/pdf_password_dialog.h"
|
#include "views/pdf_password_dialog.h"
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
|
@ -137,7 +136,6 @@ struct Application
|
||||||
InvestigationGraphModel *graph_model;
|
InvestigationGraphModel *graph_model;
|
||||||
InvestigationGraphLayout *graph_layout;
|
InvestigationGraphLayout *graph_layout;
|
||||||
guint64 graph_load_generation;
|
guint64 graph_load_generation;
|
||||||
gboolean graph_viewport_restored;
|
|
||||||
|
|
||||||
char *selected_evidence_identifier;
|
char *selected_evidence_identifier;
|
||||||
char *pending_relation_selection_identifier;
|
char *pending_relation_selection_identifier;
|
||||||
|
|
@ -227,13 +225,6 @@ typedef struct
|
||||||
char *started_at;
|
char *started_at;
|
||||||
} ApplicationOsintActionContext;
|
} ApplicationOsintActionContext;
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
Application *application;
|
|
||||||
ExtractionDropInfo *info;
|
|
||||||
char *target_entity_identifier;
|
|
||||||
} ApplicationExtractionDropContext;
|
|
||||||
|
|
||||||
/** @brief Données possédées par l'étape de révision OSINT. */
|
/** @brief Données possédées par l'étape de révision OSINT. */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
|
@ -253,196 +244,12 @@ static void application_start_graph_loading(
|
||||||
const char *database_path
|
const char *database_path
|
||||||
);
|
);
|
||||||
|
|
||||||
static void application_save_graph_viewport(Application *application)
|
|
||||||
{
|
|
||||||
GraphNodePositionDao *dao = NULL;
|
|
||||||
GError *error = NULL;
|
|
||||||
double zoom = 0.0;
|
|
||||||
double offset_x = 0.0;
|
|
||||||
double offset_y = 0.0;
|
|
||||||
|
|
||||||
if (application == NULL || application->session == NULL ||
|
|
||||||
application->main_window == NULL ||
|
|
||||||
!main_window_get_graph_view_transform(application->main_window,
|
|
||||||
&zoom, &offset_x, &offset_y))
|
|
||||||
return;
|
|
||||||
|
|
||||||
dao = graph_node_position_dao_new(
|
|
||||||
investigation_session_get_database(application->session), &error);
|
|
||||||
if (dao == NULL || !graph_node_position_dao_upsert_viewport(
|
|
||||||
dao, zoom, offset_x, offset_y, &error))
|
|
||||||
g_warning("Impossible d'enregistrer le cadrage du graphe : %s",
|
|
||||||
error != NULL ? error->message : "erreur inconnue");
|
|
||||||
|
|
||||||
graph_node_position_dao_free(dao);
|
|
||||||
g_clear_error(&error);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void application_restore_graph_viewport(Application *application)
|
|
||||||
{
|
|
||||||
GraphNodePositionDao *dao = NULL;
|
|
||||||
GError *error = NULL;
|
|
||||||
gboolean found = FALSE;
|
|
||||||
double zoom = 0.0;
|
|
||||||
double offset_x = 0.0;
|
|
||||||
double offset_y = 0.0;
|
|
||||||
|
|
||||||
if (application == NULL || application->graph_viewport_restored ||
|
|
||||||
application->session == NULL || application->main_window == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
dao = graph_node_position_dao_new(
|
|
||||||
investigation_session_get_database(application->session), &error);
|
|
||||||
if (dao != NULL && graph_node_position_dao_load_viewport(
|
|
||||||
dao, &zoom, &offset_x, &offset_y, &found, &error) && found)
|
|
||||||
main_window_set_graph_view_transform(
|
|
||||||
application->main_window, zoom, offset_x, offset_y);
|
|
||||||
else if (error != NULL)
|
|
||||||
g_warning("Impossible de restaurer le cadrage du graphe : %s",
|
|
||||||
error->message);
|
|
||||||
|
|
||||||
application->graph_viewport_restored = TRUE;
|
|
||||||
graph_node_position_dao_free(dao);
|
|
||||||
g_clear_error(&error);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void application_present_error(
|
static void application_present_error(
|
||||||
Application *application,
|
Application *application,
|
||||||
const char *title,
|
const char *title,
|
||||||
const char *message
|
const char *message
|
||||||
);
|
);
|
||||||
|
|
||||||
static void application_extraction_drop_context_free(
|
|
||||||
ApplicationExtractionDropContext *context)
|
|
||||||
{
|
|
||||||
if (context == NULL) return;
|
|
||||||
extraction_drop_info_free(context->info);
|
|
||||||
g_free(context->target_entity_identifier);
|
|
||||||
g_free(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void application_on_extraction_choice_finished(
|
|
||||||
GObject *source_object, GAsyncResult *result, gpointer user_data)
|
|
||||||
{
|
|
||||||
ApplicationExtractionDropContext *context = user_data;
|
|
||||||
Application *application = context != NULL ? context->application : NULL;
|
|
||||||
GtkAlertDialog *dialog = GTK_ALERT_DIALOG(source_object);
|
|
||||||
const InvestigationProject *project = NULL;
|
|
||||||
Database *database = NULL;
|
|
||||||
GError *error = NULL;
|
|
||||||
int choice = gtk_alert_dialog_choose_finish(dialog, result, &error);
|
|
||||||
gboolean success = FALSE;
|
|
||||||
|
|
||||||
if (application == NULL || application->session == NULL) goto cleanup;
|
|
||||||
if (error != NULL || choice == 0)
|
|
||||||
{
|
|
||||||
main_window_set_status(application->main_window,
|
|
||||||
"Dépôt de l'extraction annulé.");
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
database = investigation_session_get_database(application->session);
|
|
||||||
if (choice == 1)
|
|
||||||
success = extraction_drop_service_create_entity(database,
|
|
||||||
context->info, NULL, &error);
|
|
||||||
else if (choice == 2 && context->target_entity_identifier != NULL)
|
|
||||||
success = extraction_drop_service_attach(database, context->info,
|
|
||||||
context->target_entity_identifier, &error);
|
|
||||||
else
|
|
||||||
g_set_error_literal(&error, EXTRACTION_DROP_SERVICE_ERROR,
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_INVALID_ARGUMENT,
|
|
||||||
"Déposez l'extraction directement sur une entité pour la rattacher.");
|
|
||||||
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
application_present_error(application,
|
|
||||||
"Intégration de l'extraction impossible",
|
|
||||||
error != NULL ? error->message : "L'association n'a pas été créée.");
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
main_window_set_status(application->main_window,
|
|
||||||
choice == 1 ? "Entité créée depuis l'extraction."
|
|
||||||
: "Extraction rattachée à l'entité.");
|
|
||||||
project = investigation_session_get_project(application->session);
|
|
||||||
if (project != NULL)
|
|
||||||
application_start_graph_loading(application,
|
|
||||||
investigation_project_get_database_path(project));
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
g_clear_error(&error);
|
|
||||||
application_extraction_drop_context_free(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void application_on_extraction_dropped(
|
|
||||||
const char *file_path,
|
|
||||||
const char *target_entity_identifier,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
Application *application = user_data;
|
|
||||||
ApplicationExtractionDropContext *context = NULL;
|
|
||||||
const InvestigationProject *project = NULL;
|
|
||||||
GtkAlertDialog *dialog = NULL;
|
|
||||||
const char *buttons[] = {
|
|
||||||
"Annuler", "Créer une entité", "Rattacher à l'entité", NULL
|
|
||||||
};
|
|
||||||
char *details = NULL;
|
|
||||||
char *content_preview = NULL;
|
|
||||||
GError *error = NULL;
|
|
||||||
|
|
||||||
if (application == NULL || application->session == NULL) return;
|
|
||||||
project = investigation_session_get_project(application->session);
|
|
||||||
context = g_new0(ApplicationExtractionDropContext, 1);
|
|
||||||
context->application = application;
|
|
||||||
context->target_entity_identifier = g_strdup(target_entity_identifier);
|
|
||||||
context->info = extraction_drop_service_prepare(
|
|
||||||
investigation_project_get_root_path(project), file_path, &error);
|
|
||||||
if (context->info == NULL)
|
|
||||||
{
|
|
||||||
application_present_error(application, "Dépôt refusé",
|
|
||||||
error != NULL ? error->message : "Extraction invalide.");
|
|
||||||
g_clear_error(&error);
|
|
||||||
application_extraction_drop_context_free(context);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const char *content = extraction_drop_info_get_content(context->info);
|
|
||||||
const char *preview_end = content;
|
|
||||||
guint line_count = 0U;
|
|
||||||
|
|
||||||
while (preview_end != NULL && *preview_end != '\0' &&
|
|
||||||
(gsize) (preview_end - content) < 800U &&
|
|
||||||
line_count < 12U)
|
|
||||||
{
|
|
||||||
if (*preview_end == '\n') line_count++;
|
|
||||||
preview_end = g_utf8_next_char(preview_end);
|
|
||||||
}
|
|
||||||
content_preview = g_strndup(content,
|
|
||||||
preview_end != NULL ? (gsize) (preview_end - content) : 0U);
|
|
||||||
if (preview_end != NULL && *preview_end != '\0')
|
|
||||||
{
|
|
||||||
char *truncated_preview = g_strconcat(content_preview,
|
|
||||||
"\n\n[… aperçu tronqué, le fichier complet sera conservé]",
|
|
||||||
NULL);
|
|
||||||
g_free(content_preview);
|
|
||||||
content_preview = truncated_preview;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
details = g_strdup_printf("Fichier : %s\nSource : %s\n\n%s",
|
|
||||||
extraction_drop_info_get_file_name(context->info),
|
|
||||||
extraction_drop_info_get_source(context->info),
|
|
||||||
content_preview != NULL ? content_preview : "");
|
|
||||||
dialog = gtk_alert_dialog_new("Intégrer cette extraction au graphe ?");
|
|
||||||
gtk_alert_dialog_set_detail(dialog, details);
|
|
||||||
gtk_alert_dialog_set_buttons(dialog, buttons);
|
|
||||||
gtk_alert_dialog_set_cancel_button(dialog, 0);
|
|
||||||
gtk_alert_dialog_set_default_button(dialog, 0);
|
|
||||||
gtk_alert_dialog_choose(dialog,
|
|
||||||
main_window_get_window(application->main_window), NULL,
|
|
||||||
application_on_extraction_choice_finished, context);
|
|
||||||
g_object_unref(dialog);
|
|
||||||
g_free(details);
|
|
||||||
g_free(content_preview);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void application_present_next_batch_metadata(
|
static void application_present_next_batch_metadata(
|
||||||
ApplicationEvidenceBatchContext *context
|
ApplicationEvidenceBatchContext *context
|
||||||
);
|
);
|
||||||
|
|
@ -1122,8 +929,6 @@ static void application_on_graph_loaded(
|
||||||
application->graph_layout
|
application->graph_layout
|
||||||
);
|
);
|
||||||
|
|
||||||
application_restore_graph_viewport(application);
|
|
||||||
|
|
||||||
if (application->pending_relation_selection_identifier != NULL)
|
if (application->pending_relation_selection_identifier != NULL)
|
||||||
{
|
{
|
||||||
main_window_select_graph_relation(application->main_window,
|
main_window_select_graph_relation(application->main_window,
|
||||||
|
|
@ -2665,14 +2470,12 @@ static gboolean application_install_session(
|
||||||
application->tree_model
|
application->tree_model
|
||||||
);
|
);
|
||||||
|
|
||||||
application_save_graph_viewport(application);
|
|
||||||
investigation_session_close(
|
investigation_session_close(
|
||||||
application->session
|
application->session
|
||||||
);
|
);
|
||||||
|
|
||||||
application->tree_model = new_tree_model;
|
application->tree_model = new_tree_model;
|
||||||
application->session = new_session;
|
application->session = new_session;
|
||||||
application->graph_viewport_restored = FALSE;
|
|
||||||
|
|
||||||
main_window_set_tree_model(
|
main_window_set_tree_model(
|
||||||
application->main_window,
|
application->main_window,
|
||||||
|
|
@ -7841,9 +7644,6 @@ static void application_on_activate(
|
||||||
application_on_graph_node_moved,
|
application_on_graph_node_moved,
|
||||||
application
|
application
|
||||||
);
|
);
|
||||||
main_window_set_extraction_drop_callback(
|
|
||||||
application->main_window, application_on_extraction_dropped,
|
|
||||||
application);
|
|
||||||
|
|
||||||
main_window_set_reset_graph_layout_callback(
|
main_window_set_reset_graph_layout_callback(
|
||||||
application->main_window,
|
application->main_window,
|
||||||
|
|
@ -8126,8 +7926,6 @@ void application_free(
|
||||||
application
|
application
|
||||||
);
|
);
|
||||||
|
|
||||||
application_save_graph_viewport(application);
|
|
||||||
|
|
||||||
application_clear_graph(
|
application_clear_graph(
|
||||||
application
|
application
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,361 +0,0 @@
|
||||||
/******************************************************************************
|
|
||||||
* @file extraction_drop_service.c
|
|
||||||
* @brief Validation et intégration d'une extraction déposée sur le graphe.
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
#include "core/extraction_drop_service.h"
|
|
||||||
|
|
||||||
#include "core/file_hash.h"
|
|
||||||
#include "dao/entity_dao.h"
|
|
||||||
#include "dao/evidence_dao.h"
|
|
||||||
#include "dao/evidence_entity_dao.h"
|
|
||||||
#include "database/transaction.h"
|
|
||||||
#include "models/entity_record.h"
|
|
||||||
#include "models/evidence_record.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
struct ExtractionDropInfo
|
|
||||||
{
|
|
||||||
char *file_path;
|
|
||||||
char *relative_path;
|
|
||||||
char *file_name;
|
|
||||||
char *source;
|
|
||||||
char *content;
|
|
||||||
};
|
|
||||||
|
|
||||||
GQuark extraction_drop_service_error_quark(void)
|
|
||||||
{
|
|
||||||
return g_quark_from_static_string("extraction-drop-service-error");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void extraction_drop_service_set_error(
|
|
||||||
GError **error,
|
|
||||||
ExtractionDropServiceError code,
|
|
||||||
const char *message)
|
|
||||||
{
|
|
||||||
if (error != NULL && *error == NULL)
|
|
||||||
{
|
|
||||||
g_set_error_literal(error, EXTRACTION_DROP_SERVICE_ERROR, code, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *extraction_drop_service_timestamp(void)
|
|
||||||
{
|
|
||||||
GDateTime *now = g_date_time_new_now_utc();
|
|
||||||
char *timestamp = now != NULL
|
|
||||||
? g_date_time_format(now, "%Y-%m-%dT%H:%M:%SZ") : NULL;
|
|
||||||
g_clear_pointer(&now, g_date_time_unref);
|
|
||||||
return timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *extraction_drop_service_source_from_content(const char *content)
|
|
||||||
{
|
|
||||||
const char *line_end = NULL;
|
|
||||||
if (content == NULL || content[0] == '\0')
|
|
||||||
{
|
|
||||||
return g_strdup("Extraction locale");
|
|
||||||
}
|
|
||||||
line_end = strchr(content, '\n');
|
|
||||||
if (line_end != NULL && line_end > content)
|
|
||||||
{
|
|
||||||
return g_strndup(content, (gsize) (line_end - content));
|
|
||||||
}
|
|
||||||
return g_strdup(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
ExtractionDropInfo *extraction_drop_service_prepare(
|
|
||||||
const char *investigation_root,
|
|
||||||
const char *file_path,
|
|
||||||
GError **error)
|
|
||||||
{
|
|
||||||
ExtractionDropInfo *info = NULL;
|
|
||||||
char *canonical_root = NULL;
|
|
||||||
char *extractions_root = NULL;
|
|
||||||
char *canonical_extractions_root = NULL;
|
|
||||||
char *canonical_file = NULL;
|
|
||||||
char *content = NULL;
|
|
||||||
gsize content_length = 0U;
|
|
||||||
|
|
||||||
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
||||||
if (investigation_root == NULL || investigation_root[0] == '\0' ||
|
|
||||||
file_path == NULL || file_path[0] == '\0')
|
|
||||||
{
|
|
||||||
extraction_drop_service_set_error(error,
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_INVALID_ARGUMENT,
|
|
||||||
"Le chemin de l'extraction est invalide.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
canonical_root = g_canonicalize_filename(investigation_root, NULL);
|
|
||||||
extractions_root = g_build_filename(canonical_root,
|
|
||||||
"02_Preuves_Traitees", "Extractions", NULL);
|
|
||||||
canonical_extractions_root = g_canonicalize_filename(extractions_root, NULL);
|
|
||||||
canonical_file = g_canonicalize_filename(file_path, NULL);
|
|
||||||
if (canonical_root == NULL || canonical_extractions_root == NULL ||
|
|
||||||
canonical_file == NULL)
|
|
||||||
{
|
|
||||||
extraction_drop_service_set_error(error,
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_INVALID_ARGUMENT,
|
|
||||||
"Impossible de normaliser le chemin de l'extraction.");
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
if (!g_str_has_prefix(canonical_file, canonical_extractions_root) ||
|
|
||||||
canonical_file[strlen(canonical_extractions_root)] != G_DIR_SEPARATOR)
|
|
||||||
{
|
|
||||||
extraction_drop_service_set_error(error,
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_OUTSIDE_INVESTIGATION,
|
|
||||||
"Le fichier ne se trouve pas dans les extractions de l'enquête.");
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
if (!g_file_test(canonical_file, G_FILE_TEST_IS_REGULAR) ||
|
|
||||||
g_file_test(canonical_file, G_FILE_TEST_IS_SYMLINK) ||
|
|
||||||
!g_str_has_suffix(canonical_file, ".txt"))
|
|
||||||
{
|
|
||||||
extraction_drop_service_set_error(error,
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_INVALID_FILE,
|
|
||||||
"Seuls les fichiers texte réguliers d'extraction sont acceptés.");
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
if (!g_file_get_contents(canonical_file, &content, &content_length, error))
|
|
||||||
{
|
|
||||||
if (error != NULL && *error != NULL)
|
|
||||||
{
|
|
||||||
(*error)->domain = EXTRACTION_DROP_SERVICE_ERROR;
|
|
||||||
(*error)->code = EXTRACTION_DROP_SERVICE_ERROR_READ;
|
|
||||||
}
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
if (!g_utf8_validate(content, (gssize) content_length, NULL))
|
|
||||||
{
|
|
||||||
extraction_drop_service_set_error(error,
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_INVALID_FILE,
|
|
||||||
"L'extraction texte n'est pas encodée en UTF-8.");
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
info = g_new0(ExtractionDropInfo, 1);
|
|
||||||
info->file_path = g_strdup(canonical_file);
|
|
||||||
info->relative_path = g_strdup(canonical_file + strlen(canonical_root) + 1U);
|
|
||||||
info->file_name = g_path_get_basename(canonical_file);
|
|
||||||
info->content = g_steal_pointer(&content);
|
|
||||||
info->source = extraction_drop_service_source_from_content(info->content);
|
|
||||||
if (info->file_path == NULL || info->relative_path == NULL ||
|
|
||||||
info->file_name == NULL || info->content == NULL || info->source == NULL)
|
|
||||||
{
|
|
||||||
extraction_drop_info_free(info);
|
|
||||||
info = NULL;
|
|
||||||
extraction_drop_service_set_error(error,
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_INVALID_ARGUMENT,
|
|
||||||
"Impossible d'allouer les informations de l'extraction.");
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
g_free(content);
|
|
||||||
g_free(canonical_file);
|
|
||||||
g_free(canonical_extractions_root);
|
|
||||||
g_free(extractions_root);
|
|
||||||
g_free(canonical_root);
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
void extraction_drop_info_free(ExtractionDropInfo *info)
|
|
||||||
{
|
|
||||||
if (info == NULL) return;
|
|
||||||
g_free(info->file_path);
|
|
||||||
g_free(info->relative_path);
|
|
||||||
g_free(info->file_name);
|
|
||||||
g_free(info->source);
|
|
||||||
g_free(info->content);
|
|
||||||
g_free(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define EXTRACTION_DROP_GETTER(name, field) \
|
|
||||||
const char *name(const ExtractionDropInfo *info) \
|
|
||||||
{ return info != NULL ? info->field : NULL; }
|
|
||||||
|
|
||||||
EXTRACTION_DROP_GETTER(extraction_drop_info_get_file_path, file_path)
|
|
||||||
EXTRACTION_DROP_GETTER(extraction_drop_info_get_relative_path, relative_path)
|
|
||||||
EXTRACTION_DROP_GETTER(extraction_drop_info_get_file_name, file_name)
|
|
||||||
EXTRACTION_DROP_GETTER(extraction_drop_info_get_source, source)
|
|
||||||
EXTRACTION_DROP_GETTER(extraction_drop_info_get_content, content)
|
|
||||||
|
|
||||||
static char *extraction_drop_service_ensure_evidence(
|
|
||||||
Database *database,
|
|
||||||
const ExtractionDropInfo *info,
|
|
||||||
GError **error)
|
|
||||||
{
|
|
||||||
EvidenceDao *dao = NULL;
|
|
||||||
EvidenceRecord *record = NULL;
|
|
||||||
GPtrArray *records = NULL;
|
|
||||||
char *identifier = NULL;
|
|
||||||
char *sha256 = NULL;
|
|
||||||
char *timestamp = NULL;
|
|
||||||
guint64 size_bytes = 0U;
|
|
||||||
|
|
||||||
dao = evidence_dao_new(database, error);
|
|
||||||
if (dao == NULL) goto cleanup;
|
|
||||||
records = evidence_dao_list_all(dao, error);
|
|
||||||
if (records == NULL) goto cleanup;
|
|
||||||
for (guint index = 0U; index < records->len; index++)
|
|
||||||
{
|
|
||||||
const EvidenceRecord *candidate = g_ptr_array_index(records, index);
|
|
||||||
if (g_strcmp0(evidence_record_get_relative_path(candidate),
|
|
||||||
info->relative_path) == 0)
|
|
||||||
{
|
|
||||||
identifier = g_strdup(evidence_record_get_identifier(candidate));
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!file_hash_compute_sha256(info->file_path, NULL, &sha256,
|
|
||||||
&size_bytes, error)) goto cleanup;
|
|
||||||
identifier = g_uuid_string_random();
|
|
||||||
timestamp = extraction_drop_service_timestamp();
|
|
||||||
record = evidence_record_new(identifier, info->file_name, info->file_name,
|
|
||||||
info->relative_path, "text", size_bytes, sha256, timestamp, NULL,
|
|
||||||
info->source, "Extraction intégrée depuis le graphe.",
|
|
||||||
EVIDENCE_INTEGRITY_STATUS_VALID, error);
|
|
||||||
if (record == NULL || !evidence_dao_insert(dao, record, error))
|
|
||||||
{
|
|
||||||
g_clear_pointer(&identifier, g_free);
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
evidence_record_free(record);
|
|
||||||
g_clear_pointer(&records, g_ptr_array_unref);
|
|
||||||
evidence_dao_free(dao);
|
|
||||||
g_free(timestamp);
|
|
||||||
g_free(sha256);
|
|
||||||
return identifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean extraction_drop_service_attach_in_transaction(
|
|
||||||
Database *database,
|
|
||||||
const ExtractionDropInfo *info,
|
|
||||||
const char *entity_identifier,
|
|
||||||
GError **error)
|
|
||||||
{
|
|
||||||
EvidenceEntityDao *link_dao = NULL;
|
|
||||||
char *evidence_identifier = NULL;
|
|
||||||
gboolean exists = FALSE;
|
|
||||||
gboolean success = FALSE;
|
|
||||||
|
|
||||||
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
||||||
if (database == NULL || info == NULL || entity_identifier == NULL ||
|
|
||||||
!g_uuid_string_is_valid(entity_identifier))
|
|
||||||
{
|
|
||||||
extraction_drop_service_set_error(error,
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_INVALID_ARGUMENT,
|
|
||||||
"L'entité de destination est invalide.");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
evidence_identifier = extraction_drop_service_ensure_evidence(database,
|
|
||||||
info, error);
|
|
||||||
if (evidence_identifier == NULL) goto cleanup;
|
|
||||||
link_dao = evidence_entity_dao_new(database, error);
|
|
||||||
if (link_dao == NULL ||
|
|
||||||
!evidence_entity_dao_exists(link_dao, evidence_identifier,
|
|
||||||
entity_identifier, &exists, error) ||
|
|
||||||
(!exists && !evidence_entity_dao_link(link_dao, evidence_identifier,
|
|
||||||
entity_identifier, error))) goto cleanup;
|
|
||||||
success = TRUE;
|
|
||||||
cleanup:
|
|
||||||
evidence_entity_dao_free(link_dao);
|
|
||||||
g_free(evidence_identifier);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean extraction_drop_service_attach(
|
|
||||||
Database *database,
|
|
||||||
const ExtractionDropInfo *info,
|
|
||||||
const char *entity_identifier,
|
|
||||||
GError **error)
|
|
||||||
{
|
|
||||||
gboolean success = FALSE;
|
|
||||||
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
||||||
if (database == NULL || info == NULL || entity_identifier == NULL ||
|
|
||||||
!g_uuid_string_is_valid(entity_identifier))
|
|
||||||
{
|
|
||||||
extraction_drop_service_set_error(error,
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_INVALID_ARGUMENT,
|
|
||||||
"L'entité de destination est invalide.");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (!database_transaction_begin(database)) goto database_failure;
|
|
||||||
if (!extraction_drop_service_attach_in_transaction(database, info,
|
|
||||||
entity_identifier, error))
|
|
||||||
{
|
|
||||||
database_transaction_rollback(database);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (!database_transaction_commit(database)) goto database_failure;
|
|
||||||
success = TRUE;
|
|
||||||
return success;
|
|
||||||
|
|
||||||
database_failure:
|
|
||||||
extraction_drop_service_set_error(error,
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_DATABASE,
|
|
||||||
"Impossible d'enregistrer l'association de l'extraction.");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean extraction_drop_service_create_entity(
|
|
||||||
Database *database,
|
|
||||||
const ExtractionDropInfo *info,
|
|
||||||
char **out_entity_identifier,
|
|
||||||
GError **error)
|
|
||||||
{
|
|
||||||
EntityDao *entity_dao = NULL;
|
|
||||||
EntityRecord *entity = NULL;
|
|
||||||
char *identifier = NULL;
|
|
||||||
char *timestamp = NULL;
|
|
||||||
gboolean success = FALSE;
|
|
||||||
|
|
||||||
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
||||||
if (out_entity_identifier != NULL) *out_entity_identifier = NULL;
|
|
||||||
if (database == NULL || info == NULL)
|
|
||||||
{
|
|
||||||
extraction_drop_service_set_error(error,
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_INVALID_ARGUMENT,
|
|
||||||
"Les paramètres de création d'entité sont invalides.");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
identifier = g_uuid_string_random();
|
|
||||||
timestamp = extraction_drop_service_timestamp();
|
|
||||||
entity = entity_record_new(identifier, "other", info->file_name,
|
|
||||||
info->file_name, info->content, 50, timestamp, timestamp,
|
|
||||||
ENTITY_STATUS_ACTIVE, error);
|
|
||||||
if (!database_transaction_begin(database))
|
|
||||||
{
|
|
||||||
extraction_drop_service_set_error(error,
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_DATABASE,
|
|
||||||
"Impossible de démarrer la création de l'entité.");
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
entity_dao = entity_dao_new(database, error);
|
|
||||||
if (entity == NULL || entity_dao == NULL ||
|
|
||||||
!entity_dao_insert(entity_dao, entity, error) ||
|
|
||||||
!extraction_drop_service_attach_in_transaction(database, info,
|
|
||||||
identifier, error))
|
|
||||||
{
|
|
||||||
database_transaction_rollback(database);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
if (!database_transaction_commit(database))
|
|
||||||
{
|
|
||||||
extraction_drop_service_set_error(error,
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_DATABASE,
|
|
||||||
"Impossible de valider la création de l'entité.");
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
if (out_entity_identifier != NULL)
|
|
||||||
*out_entity_identifier = g_strdup(identifier);
|
|
||||||
success = TRUE;
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
entity_dao_free(entity_dao);
|
|
||||||
entity_record_free(entity);
|
|
||||||
g_free(timestamp);
|
|
||||||
g_free(identifier);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
@ -71,16 +71,6 @@ static const char *const graph_node_position_dao_delete_sql =
|
||||||
static const char *const graph_node_position_dao_delete_all_sql =
|
static const char *const graph_node_position_dao_delete_all_sql =
|
||||||
"DELETE FROM graph_layout_positions;";
|
"DELETE FROM graph_layout_positions;";
|
||||||
|
|
||||||
static const char *const graph_node_position_dao_load_viewport_sql =
|
|
||||||
"SELECT zoom, offset_x, offset_y FROM graph_viewport WHERE id = 1;";
|
|
||||||
|
|
||||||
static const char *const graph_node_position_dao_upsert_viewport_sql =
|
|
||||||
"INSERT INTO graph_viewport(id, zoom, offset_x, offset_y, updated_at) "
|
|
||||||
"VALUES(1, ?, ?, ?, ?) "
|
|
||||||
"ON CONFLICT(id) DO UPDATE SET zoom = excluded.zoom, "
|
|
||||||
"offset_x = excluded.offset_x, offset_y = excluded.offset_y, "
|
|
||||||
"updated_at = excluded.updated_at;";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Enregistre une erreur littérale.
|
* @brief Enregistre une erreur littérale.
|
||||||
*/
|
*/
|
||||||
|
|
@ -879,125 +869,3 @@ cleanup:
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean graph_node_position_dao_load_viewport(
|
|
||||||
GraphNodePositionDao *position_dao,
|
|
||||||
double *zoom,
|
|
||||||
double *offset_x,
|
|
||||||
double *offset_y,
|
|
||||||
gboolean *found,
|
|
||||||
GError **error
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DatabaseStatement *statement = NULL;
|
|
||||||
DatabaseStatementStepResult step_result;
|
|
||||||
gboolean success = FALSE;
|
|
||||||
|
|
||||||
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
||||||
if (position_dao == NULL || position_dao->database == NULL ||
|
|
||||||
zoom == NULL || offset_x == NULL || offset_y == NULL || found == NULL)
|
|
||||||
{
|
|
||||||
graph_node_position_dao_set_error_literal(error,
|
|
||||||
GRAPH_NODE_POSITION_DAO_ERROR_INVALID_ARGUMENT,
|
|
||||||
"Les paramètres de chargement du cadrage sont invalides.");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
*found = FALSE;
|
|
||||||
statement = database_statement_prepare(position_dao->database,
|
|
||||||
graph_node_position_dao_load_viewport_sql);
|
|
||||||
if (statement == NULL)
|
|
||||||
{
|
|
||||||
graph_node_position_dao_set_database_error(position_dao, error,
|
|
||||||
GRAPH_NODE_POSITION_DAO_ERROR_PREPARE,
|
|
||||||
"Impossible de préparer le chargement du cadrage");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
step_result = database_statement_step(statement);
|
|
||||||
if (step_result == DATABASE_STATEMENT_STEP_ROW)
|
|
||||||
{
|
|
||||||
if (!database_statement_column_double(statement, 0, zoom) ||
|
|
||||||
!database_statement_column_double(statement, 1, offset_x) ||
|
|
||||||
!database_statement_column_double(statement, 2, offset_y) ||
|
|
||||||
!isfinite(*zoom) || *zoom <= 0.0 ||
|
|
||||||
!isfinite(*offset_x) || !isfinite(*offset_y))
|
|
||||||
{
|
|
||||||
graph_node_position_dao_set_error_literal(error,
|
|
||||||
GRAPH_NODE_POSITION_DAO_ERROR_READ,
|
|
||||||
"Le cadrage enregistré est invalide.");
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
*found = TRUE;
|
|
||||||
}
|
|
||||||
else if (step_result == DATABASE_STATEMENT_STEP_ERROR)
|
|
||||||
{
|
|
||||||
graph_node_position_dao_set_database_error(position_dao, error,
|
|
||||||
GRAPH_NODE_POSITION_DAO_ERROR_EXECUTE,
|
|
||||||
"Impossible de charger le cadrage");
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
success = TRUE;
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
database_statement_finalize(statement);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean graph_node_position_dao_upsert_viewport(
|
|
||||||
GraphNodePositionDao *position_dao,
|
|
||||||
double zoom,
|
|
||||||
double offset_x,
|
|
||||||
double offset_y,
|
|
||||||
GError **error
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DatabaseStatement *statement = NULL;
|
|
||||||
char *updated_at = NULL;
|
|
||||||
gboolean success = FALSE;
|
|
||||||
|
|
||||||
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
||||||
if (position_dao == NULL || position_dao->database == NULL ||
|
|
||||||
!isfinite(zoom) || zoom <= 0.0 ||
|
|
||||||
!isfinite(offset_x) || !isfinite(offset_y))
|
|
||||||
{
|
|
||||||
graph_node_position_dao_set_error_literal(error,
|
|
||||||
GRAPH_NODE_POSITION_DAO_ERROR_INVALID_ARGUMENT,
|
|
||||||
"Le cadrage du graphe est invalide.");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
updated_at = graph_node_position_dao_create_utc_timestamp();
|
|
||||||
statement = database_statement_prepare(position_dao->database,
|
|
||||||
graph_node_position_dao_upsert_viewport_sql);
|
|
||||||
if (updated_at == NULL || statement == NULL)
|
|
||||||
{
|
|
||||||
graph_node_position_dao_set_database_error(position_dao, error,
|
|
||||||
GRAPH_NODE_POSITION_DAO_ERROR_PREPARE,
|
|
||||||
"Impossible de préparer l'enregistrement du cadrage");
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
if (!database_statement_bind_double(statement, 1, zoom) ||
|
|
||||||
!database_statement_bind_double(statement, 2, offset_x) ||
|
|
||||||
!database_statement_bind_double(statement, 3, offset_y) ||
|
|
||||||
!database_statement_bind_text(statement, 4, updated_at))
|
|
||||||
{
|
|
||||||
graph_node_position_dao_set_database_error(position_dao, error,
|
|
||||||
GRAPH_NODE_POSITION_DAO_ERROR_BIND,
|
|
||||||
"Impossible de lier les données du cadrage");
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
if (database_statement_step(statement) != DATABASE_STATEMENT_STEP_DONE)
|
|
||||||
{
|
|
||||||
graph_node_position_dao_set_database_error(position_dao, error,
|
|
||||||
GRAPH_NODE_POSITION_DAO_ERROR_EXECUTE,
|
|
||||||
"Impossible d'enregistrer le cadrage");
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
success = TRUE;
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
database_statement_finalize(statement);
|
|
||||||
g_free(updated_at);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,12 @@
|
||||||
/**
|
/**
|
||||||
* @brief Version actuelle du schéma SQLite.
|
* @brief Version actuelle du schéma SQLite.
|
||||||
*/
|
*/
|
||||||
#define DATABASE_SCHEMA_VERSION_CURRENT 8
|
#define DATABASE_SCHEMA_VERSION_CURRENT 7
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Version actuelle sous forme textuelle pour metadata.
|
* @brief Version actuelle sous forme textuelle pour metadata.
|
||||||
*/
|
*/
|
||||||
#define DATABASE_SCHEMA_VERSION_CURRENT_TEXT "8"
|
#define DATABASE_SCHEMA_VERSION_CURRENT_TEXT "7"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Nom de l'application enregistré dans les métadonnées.
|
* @brief Nom de l'application enregistré dans les métadonnées.
|
||||||
|
|
@ -748,40 +748,6 @@ rollback:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool database_migrate_v6_to_v7(Database *database)
|
|
||||||
{
|
|
||||||
bool transaction_started = false;
|
|
||||||
if (database == NULL || !database_transaction_begin(database))
|
|
||||||
return false;
|
|
||||||
transaction_started = true;
|
|
||||||
if (!schema_install_v7(database) ||
|
|
||||||
!database_update_schema_version(database, "7") ||
|
|
||||||
!database_transaction_commit(database))
|
|
||||||
goto rollback;
|
|
||||||
return true;
|
|
||||||
rollback:
|
|
||||||
if (transaction_started && !database_transaction_rollback(database))
|
|
||||||
g_warning("Impossible d’annuler la migration SQLite V6 vers V7.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool database_migrate_v7_to_v8(Database *database)
|
|
||||||
{
|
|
||||||
bool transaction_started = false;
|
|
||||||
if (database == NULL || !database_transaction_begin(database))
|
|
||||||
return false;
|
|
||||||
transaction_started = true;
|
|
||||||
if (!schema_install_v8(database) ||
|
|
||||||
!database_update_schema_version(database, "8") ||
|
|
||||||
!database_transaction_commit(database))
|
|
||||||
goto rollback;
|
|
||||||
return true;
|
|
||||||
rollback:
|
|
||||||
if (transaction_started && !database_transaction_rollback(database))
|
|
||||||
g_warning("Impossible d’annuler la migration SQLite V7 vers V8.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Garantit atomiquement la présence des extensions du schéma courant.
|
* @brief Garantit atomiquement la présence des extensions du schéma courant.
|
||||||
*/
|
*/
|
||||||
|
|
@ -993,17 +959,11 @@ bool database_migrate_to_latest(
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
if (!database_migrate_v6_to_v7(database))
|
if (!schema_install_v7(database))
|
||||||
return false;
|
return false;
|
||||||
schema_version = 7;
|
schema_version = 7;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 7:
|
|
||||||
if (!database_migrate_v7_to_v8(database))
|
|
||||||
return false;
|
|
||||||
schema_version = 8;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
database_set_error(
|
database_set_error(
|
||||||
database,
|
database,
|
||||||
|
|
|
||||||
|
|
@ -233,12 +233,6 @@ bool schema_install_v7(Database *database)
|
||||||
"la migration SQLite V7");
|
"la migration SQLite V7");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool schema_install_v8(Database *database)
|
|
||||||
{
|
|
||||||
return schema_execute_file(database, "database/schema_v8.sql",
|
|
||||||
"la migration SQLite V8");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool schema_ensure_current(
|
bool schema_ensure_current(
|
||||||
Database *database
|
Database *database
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -106,9 +106,6 @@ struct MainWindow
|
||||||
gpointer
|
gpointer
|
||||||
verify_evidence_user_data;
|
verify_evidence_user_data;
|
||||||
|
|
||||||
MainWindowEvidenceSelectionCallback evidence_selection_callback;
|
|
||||||
gpointer evidence_selection_user_data;
|
|
||||||
|
|
||||||
MainWindowEditEvidenceCallback edit_evidence_callback;
|
MainWindowEditEvidenceCallback edit_evidence_callback;
|
||||||
gpointer edit_evidence_user_data;
|
gpointer edit_evidence_user_data;
|
||||||
MainWindowAnalyzeEmlCallback analyze_eml_callback;
|
MainWindowAnalyzeEmlCallback analyze_eml_callback;
|
||||||
|
|
@ -126,9 +123,6 @@ struct MainWindow
|
||||||
gpointer
|
gpointer
|
||||||
graph_node_moved_user_data;
|
graph_node_moved_user_data;
|
||||||
|
|
||||||
MainWindowExtractionDropCallback extraction_drop_callback;
|
|
||||||
gpointer extraction_drop_user_data;
|
|
||||||
|
|
||||||
MainWindowResetGraphLayoutCallback
|
MainWindowResetGraphLayoutCallback
|
||||||
reset_graph_layout_callback;
|
reset_graph_layout_callback;
|
||||||
|
|
||||||
|
|
@ -514,15 +508,6 @@ static void main_window_on_graph_node_moved(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void main_window_on_extraction_dropped(const char *file_path,
|
|
||||||
const char *target_entity_identifier, gpointer user_data)
|
|
||||||
{
|
|
||||||
MainWindow *main_window = user_data;
|
|
||||||
if (main_window != NULL && main_window->extraction_drop_callback != NULL)
|
|
||||||
main_window->extraction_drop_callback(file_path,
|
|
||||||
target_entity_identifier, main_window->extraction_drop_user_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Relaie la demande de vérification provenant du Workspace.
|
* @brief Relaie la demande de vérification provenant du Workspace.
|
||||||
*/
|
*/
|
||||||
|
|
@ -634,15 +619,6 @@ static void main_window_on_person_evidence_requested(const char *identifier,
|
||||||
window->person_evidence_callback(identifier,
|
window->person_evidence_callback(identifier,
|
||||||
window->person_evidence_user_data);
|
window->person_evidence_user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void main_window_on_evidence_activated(const char *identifier,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
MainWindow *window = user_data;
|
|
||||||
if (window != NULL && window->evidence_selection_callback != NULL)
|
|
||||||
window->evidence_selection_callback(identifier,
|
|
||||||
window->evidence_selection_user_data);
|
|
||||||
}
|
|
||||||
/** @brief Relaie la sélection d'une entité vers le contrôleur. */
|
/** @brief Relaie la sélection d'une entité vers le contrôleur. */
|
||||||
static void main_window_on_entity_selected(const char *identifier,
|
static void main_window_on_entity_selected(const char *identifier,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
|
|
@ -978,8 +954,6 @@ MainWindow *main_window_new(
|
||||||
main_window_on_graph_node_moved,
|
main_window_on_graph_node_moved,
|
||||||
main_window
|
main_window
|
||||||
);
|
);
|
||||||
workspace_set_extraction_drop_callback(main_window->workspace,
|
|
||||||
main_window_on_extraction_dropped, main_window);
|
|
||||||
|
|
||||||
workspace_set_reset_graph_layout_callback(
|
workspace_set_reset_graph_layout_callback(
|
||||||
main_window->workspace,
|
main_window->workspace,
|
||||||
|
|
@ -1005,8 +979,6 @@ MainWindow *main_window_new(
|
||||||
main_window_on_person_name_changed, main_window);
|
main_window_on_person_name_changed, main_window);
|
||||||
workspace_set_person_evidence_callback(main_window->workspace,
|
workspace_set_person_evidence_callback(main_window->workspace,
|
||||||
main_window_on_person_evidence_requested, main_window);
|
main_window_on_person_evidence_requested, main_window);
|
||||||
workspace_set_evidence_activated_callback(main_window->workspace,
|
|
||||||
main_window_on_evidence_activated, main_window);
|
|
||||||
workspace_set_entity_selected_callback(main_window->workspace,
|
workspace_set_entity_selected_callback(main_window->workspace,
|
||||||
main_window_on_entity_selected, main_window);
|
main_window_on_entity_selected, main_window);
|
||||||
|
|
||||||
|
|
@ -1421,22 +1393,6 @@ void main_window_set_graph(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean main_window_get_graph_view_transform(
|
|
||||||
MainWindow *main_window, double *zoom, double *offset_x, double *offset_y)
|
|
||||||
{
|
|
||||||
return main_window != NULL &&
|
|
||||||
workspace_get_graph_view_transform(
|
|
||||||
main_window->workspace, zoom, offset_x, offset_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main_window_set_graph_view_transform(
|
|
||||||
MainWindow *main_window, double zoom, double offset_x, double offset_y)
|
|
||||||
{
|
|
||||||
if (main_window != NULL)
|
|
||||||
workspace_set_graph_view_transform(
|
|
||||||
main_window->workspace, zoom, offset_x, offset_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean main_window_select_graph_relation(MainWindow *main_window,
|
gboolean main_window_select_graph_relation(MainWindow *main_window,
|
||||||
const char *relation_identifier)
|
const char *relation_identifier)
|
||||||
{
|
{
|
||||||
|
|
@ -1644,9 +1600,6 @@ void main_window_set_evidence_selection_callback(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
main_window->evidence_selection_callback = callback;
|
|
||||||
main_window->evidence_selection_user_data = user_data;
|
|
||||||
|
|
||||||
sidebar_set_evidence_selection_callback(
|
sidebar_set_evidence_selection_callback(
|
||||||
main_window->sidebar,
|
main_window->sidebar,
|
||||||
callback,
|
callback,
|
||||||
|
|
@ -1803,16 +1756,6 @@ void main_window_set_graph_node_moved_callback(
|
||||||
user_data;
|
user_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void main_window_set_extraction_drop_callback(
|
|
||||||
MainWindow *main_window,
|
|
||||||
MainWindowExtractionDropCallback callback,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
if (main_window == NULL) return;
|
|
||||||
main_window->extraction_drop_callback = callback;
|
|
||||||
main_window->extraction_drop_user_data = user_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void main_window_set_reset_graph_layout_callback(
|
void main_window_set_reset_graph_layout_callback(
|
||||||
MainWindow *main_window,
|
MainWindow *main_window,
|
||||||
MainWindowResetGraphLayoutCallback callback,
|
MainWindowResetGraphLayoutCallback callback,
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,6 @@ struct EntityDetailsPanel
|
||||||
GtkWidget *person_name_button;
|
GtkWidget *person_name_button;
|
||||||
GtkWidget *person_evidence_button;
|
GtkWidget *person_evidence_button;
|
||||||
GtkWidget *person_evidence_summary_label;
|
GtkWidget *person_evidence_summary_label;
|
||||||
GtkWidget *entity_evidence_box;
|
|
||||||
GtkWidget *evidence_buttons_box;
|
|
||||||
|
|
||||||
char *selected_entity_identifier;
|
char *selected_entity_identifier;
|
||||||
char *selected_entity_type;
|
char *selected_entity_type;
|
||||||
|
|
@ -63,8 +61,6 @@ struct EntityDetailsPanel
|
||||||
gpointer person_name_user_data;
|
gpointer person_name_user_data;
|
||||||
EntityDetailsPanelPersonEvidenceCallback person_evidence_callback;
|
EntityDetailsPanelPersonEvidenceCallback person_evidence_callback;
|
||||||
gpointer person_evidence_user_data;
|
gpointer person_evidence_user_data;
|
||||||
EntityDetailsPanelEvidenceActivatedCallback evidence_activated_callback;
|
|
||||||
gpointer evidence_activated_user_data;
|
|
||||||
EntityDetailsPanelOsintCallback osint_callback;
|
EntityDetailsPanelOsintCallback osint_callback;
|
||||||
gpointer osint_user_data;
|
gpointer osint_user_data;
|
||||||
};
|
};
|
||||||
|
|
@ -102,18 +98,6 @@ static void entity_details_panel_on_person_evidence_clicked(GtkButton *button,
|
||||||
panel->person_evidence_user_data);
|
panel->person_evidence_user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void entity_details_panel_on_evidence_activated(GtkButton *button,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
EntityDetailsPanel *panel = user_data;
|
|
||||||
const char *identifier = g_object_get_data(G_OBJECT(button),
|
|
||||||
"evidence-identifier");
|
|
||||||
if (panel != NULL && panel->evidence_activated_callback != NULL &&
|
|
||||||
identifier != NULL)
|
|
||||||
panel->evidence_activated_callback(identifier,
|
|
||||||
panel->evidence_activated_user_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @brief Relaie le nouveau nom affiché explicitement enregistré. */
|
/** @brief Relaie le nouveau nom affiché explicitement enregistré. */
|
||||||
static void entity_details_panel_on_person_name_clicked(GtkButton *button,
|
static void entity_details_panel_on_person_name_clicked(GtkButton *button,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
|
|
@ -862,30 +846,23 @@ EntityDetailsPanel *entity_details_panel_new(void)
|
||||||
GTK_WIDGET(details_panel->person_name_entry));
|
GTK_WIDGET(details_panel->person_name_entry));
|
||||||
gtk_box_append(GTK_BOX(details_panel->person_role_box),
|
gtk_box_append(GTK_BOX(details_panel->person_role_box),
|
||||||
details_panel->person_name_button);
|
details_panel->person_name_button);
|
||||||
details_panel->entity_evidence_box = gtk_box_new(
|
|
||||||
GTK_ORIENTATION_VERTICAL, 6);
|
|
||||||
details_panel->person_evidence_button = gtk_button_new_with_label(
|
details_panel->person_evidence_button = gtk_button_new_with_label(
|
||||||
"Gérer les pièces jointes");
|
"Gérer les pièces jointes");
|
||||||
details_panel->person_evidence_summary_label = gtk_label_new(
|
details_panel->person_evidence_summary_label = gtk_label_new(
|
||||||
"Pièces jointes : chargement…");
|
"Pièces jointes : chargement…");
|
||||||
details_panel->evidence_buttons_box = gtk_box_new(
|
|
||||||
GTK_ORIENTATION_VERTICAL, 4);
|
|
||||||
gtk_label_set_xalign(GTK_LABEL(
|
gtk_label_set_xalign(GTK_LABEL(
|
||||||
details_panel->person_evidence_summary_label), 0.0F);
|
details_panel->person_evidence_summary_label), 0.0F);
|
||||||
gtk_label_set_wrap(GTK_LABEL(
|
gtk_label_set_wrap(GTK_LABEL(
|
||||||
details_panel->person_evidence_summary_label), TRUE);
|
details_panel->person_evidence_summary_label), TRUE);
|
||||||
gtk_label_set_selectable(GTK_LABEL(
|
gtk_label_set_selectable(GTK_LABEL(
|
||||||
details_panel->person_evidence_summary_label), TRUE);
|
details_panel->person_evidence_summary_label), TRUE);
|
||||||
gtk_box_append(GTK_BOX(details_panel->entity_evidence_box),
|
gtk_box_append(GTK_BOX(details_panel->person_role_box),
|
||||||
gtk_label_new("Preuves associées à cette entité"));
|
gtk_label_new("Preuves associées à cette personne"));
|
||||||
gtk_box_append(GTK_BOX(details_panel->entity_evidence_box),
|
gtk_box_append(GTK_BOX(details_panel->person_role_box),
|
||||||
details_panel->person_evidence_summary_label);
|
details_panel->person_evidence_summary_label);
|
||||||
gtk_box_append(GTK_BOX(details_panel->entity_evidence_box),
|
gtk_box_append(GTK_BOX(details_panel->person_role_box),
|
||||||
details_panel->evidence_buttons_box);
|
|
||||||
gtk_box_append(GTK_BOX(details_panel->entity_evidence_box),
|
|
||||||
details_panel->person_evidence_button);
|
details_panel->person_evidence_button);
|
||||||
gtk_box_append(GTK_BOX(details_box), details_panel->person_role_box);
|
gtk_box_append(GTK_BOX(details_box), details_panel->person_role_box);
|
||||||
gtk_box_append(GTK_BOX(details_box), details_panel->entity_evidence_box);
|
|
||||||
g_signal_connect(details_panel->person_role_dropdown, "notify::selected",
|
g_signal_connect(details_panel->person_role_dropdown, "notify::selected",
|
||||||
G_CALLBACK(entity_details_panel_on_person_role_changed), details_panel);
|
G_CALLBACK(entity_details_panel_on_person_role_changed), details_panel);
|
||||||
g_signal_connect(details_panel->person_confidence_button, "clicked",
|
g_signal_connect(details_panel->person_confidence_button, "clicked",
|
||||||
|
|
@ -1318,15 +1295,6 @@ void entity_details_panel_set_person_evidence_callback(
|
||||||
details_panel->person_evidence_callback = callback;
|
details_panel->person_evidence_callback = callback;
|
||||||
details_panel->person_evidence_user_data = user_data;
|
details_panel->person_evidence_user_data = user_data;
|
||||||
}
|
}
|
||||||
void entity_details_panel_set_evidence_activated_callback(
|
|
||||||
EntityDetailsPanel *details_panel,
|
|
||||||
EntityDetailsPanelEvidenceActivatedCallback callback,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
if (details_panel == NULL) return;
|
|
||||||
details_panel->evidence_activated_callback = callback;
|
|
||||||
details_panel->evidence_activated_user_data = user_data;
|
|
||||||
}
|
|
||||||
void entity_details_panel_set_osint_callback(EntityDetailsPanel *details_panel,
|
void entity_details_panel_set_osint_callback(EntityDetailsPanel *details_panel,
|
||||||
EntityDetailsPanelOsintCallback callback, gpointer user_data)
|
EntityDetailsPanelOsintCallback callback, gpointer user_data)
|
||||||
{
|
{
|
||||||
|
|
@ -1342,52 +1310,24 @@ void entity_details_panel_set_osint_callback(EntityDetailsPanel *details_panel,
|
||||||
void entity_details_panel_set_person_evidences(EntityDetailsPanel *panel,
|
void entity_details_panel_set_person_evidences(EntityDetailsPanel *panel,
|
||||||
const GPtrArray *records)
|
const GPtrArray *records)
|
||||||
{
|
{
|
||||||
GtkWidget *child = NULL;
|
GString *text = NULL;
|
||||||
if (panel == NULL || panel->person_evidence_summary_label == NULL) return;
|
if (panel == NULL || panel->person_evidence_summary_label == NULL) return;
|
||||||
while (panel->evidence_buttons_box != NULL &&
|
text = g_string_new(NULL);
|
||||||
(child = gtk_widget_get_first_child(
|
|
||||||
panel->evidence_buttons_box)) != NULL)
|
|
||||||
gtk_box_remove(GTK_BOX(panel->evidence_buttons_box), child);
|
|
||||||
if (records == NULL || records->len == 0)
|
if (records == NULL || records->len == 0)
|
||||||
gtk_label_set_text(GTK_LABEL(panel->person_evidence_summary_label),
|
g_string_append(text, "Aucune pièce jointe");
|
||||||
"Aucune pièce jointe");
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
char *summary = g_strdup_printf("%u pièce%s jointe%s",
|
|
||||||
records->len, records->len > 1U ? "s" : "",
|
|
||||||
records->len > 1U ? "s" : "");
|
|
||||||
gtk_label_set_text(GTK_LABEL(panel->person_evidence_summary_label),
|
|
||||||
summary);
|
|
||||||
g_free(summary);
|
|
||||||
for (guint index = 0; index < records->len; index++)
|
for (guint index = 0; index < records->len; index++)
|
||||||
{
|
{
|
||||||
const EvidenceRecord *record = g_ptr_array_index(records, index);
|
const EvidenceRecord *record = g_ptr_array_index(records, index);
|
||||||
GtkWidget *button = NULL;
|
|
||||||
char *label = NULL;
|
|
||||||
if (record == NULL) continue;
|
if (record == NULL) continue;
|
||||||
label = g_strdup_printf("%s — %s",
|
if (text->len > 0) g_string_append_c(text, '\n');
|
||||||
|
g_string_append_printf(text, "• %s — %s",
|
||||||
evidence_record_get_original_name(record),
|
evidence_record_get_original_name(record),
|
||||||
evidence_record_get_type_identifier(record));
|
evidence_record_get_type_identifier(record));
|
||||||
button = gtk_button_new_with_label(label);
|
|
||||||
gtk_widget_set_halign(button, GTK_ALIGN_FILL);
|
|
||||||
gtk_widget_set_tooltip_text(button, label);
|
|
||||||
gtk_widget_add_css_class(button, "flat");
|
|
||||||
if (GTK_IS_LABEL(gtk_button_get_child(GTK_BUTTON(button))))
|
|
||||||
{
|
|
||||||
GtkLabel *button_label = GTK_LABEL(
|
|
||||||
gtk_button_get_child(GTK_BUTTON(button)));
|
|
||||||
gtk_label_set_xalign(button_label, 0.0F);
|
|
||||||
gtk_label_set_ellipsize(button_label, PANGO_ELLIPSIZE_END);
|
|
||||||
gtk_label_set_max_width_chars(button_label, 28);
|
|
||||||
}
|
|
||||||
g_object_set_data_full(G_OBJECT(button), "evidence-identifier",
|
|
||||||
g_strdup(evidence_record_get_identifier(record)), g_free);
|
|
||||||
g_signal_connect(button, "clicked",
|
|
||||||
G_CALLBACK(entity_details_panel_on_evidence_activated), panel);
|
|
||||||
gtk_box_append(GTK_BOX(panel->evidence_buttons_box), button);
|
|
||||||
g_free(label);
|
|
||||||
}
|
}
|
||||||
}
|
gtk_label_set_text(GTK_LABEL(panel->person_evidence_summary_label),
|
||||||
|
text->str);
|
||||||
|
g_string_free(text, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean entity_details_panel_is_open(
|
gboolean entity_details_panel_is_open(
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@
|
||||||
#include "models/social_platform.h"
|
#include "models/social_platform.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <math.h>
|
|
||||||
#include <pango/pangocairo.h>
|
#include <pango/pangocairo.h>
|
||||||
|
|
||||||
#define INVESTIGATION_GRAPH_VIEW_MARGIN 32.0
|
#define INVESTIGATION_GRAPH_VIEW_MARGIN 32.0
|
||||||
|
|
@ -121,8 +120,6 @@ struct InvestigationGraphView
|
||||||
|
|
||||||
InvestigationGraphViewNodeMovedCallback node_moved_callback;
|
InvestigationGraphViewNodeMovedCallback node_moved_callback;
|
||||||
gpointer node_moved_user_data;
|
gpointer node_moved_user_data;
|
||||||
InvestigationGraphViewExtractionDropCallback extraction_drop_callback;
|
|
||||||
gpointer extraction_drop_user_data;
|
|
||||||
|
|
||||||
double node_drag_pointer_offset_x;
|
double node_drag_pointer_offset_x;
|
||||||
double node_drag_pointer_offset_y;
|
double node_drag_pointer_offset_y;
|
||||||
|
|
@ -131,46 +128,8 @@ struct InvestigationGraphView
|
||||||
gboolean dragging;
|
gboolean dragging;
|
||||||
gboolean node_dragging;
|
gboolean node_dragging;
|
||||||
gboolean click_suppressed;
|
gboolean click_suppressed;
|
||||||
GtkDropTarget *extraction_drop_target;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static gboolean investigation_graph_view_screen_to_logical(
|
|
||||||
const InvestigationGraphView *graph_view, double screen_x, double screen_y,
|
|
||||||
double *logical_x, double *logical_y);
|
|
||||||
static InvestigationGraphNodeLayout *investigation_graph_view_find_node_at(
|
|
||||||
InvestigationGraphView *graph_view, double logical_x, double logical_y);
|
|
||||||
|
|
||||||
/** @brief Relaie un chemin d'extraction et l'éventuelle entité visée. */
|
|
||||||
static gboolean investigation_graph_view_on_extraction_drop(
|
|
||||||
GtkDropTarget *target, const GValue *value, double x, double y,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
InvestigationGraphView *graph_view = user_data;
|
|
||||||
InvestigationGraphNodeLayout *node_layout = NULL;
|
|
||||||
const char *path = NULL;
|
|
||||||
const char *entity_identifier = NULL;
|
|
||||||
double logical_x = 0.0;
|
|
||||||
double logical_y = 0.0;
|
|
||||||
(void) target;
|
|
||||||
if (graph_view == NULL || value == NULL ||
|
|
||||||
!G_VALUE_HOLDS_STRING(value)) return FALSE;
|
|
||||||
path = g_value_get_string(value);
|
|
||||||
if (path == NULL || path[0] == '\0') return FALSE;
|
|
||||||
if (investigation_graph_view_screen_to_logical(graph_view, x, y,
|
|
||||||
&logical_x, &logical_y))
|
|
||||||
{
|
|
||||||
node_layout = investigation_graph_view_find_node_at(graph_view,
|
|
||||||
logical_x, logical_y);
|
|
||||||
if (node_layout != NULL && node_layout->entity_record != NULL)
|
|
||||||
entity_identifier = entity_record_get_identifier(
|
|
||||||
node_layout->entity_record);
|
|
||||||
}
|
|
||||||
if (graph_view->extraction_drop_callback == NULL) return FALSE;
|
|
||||||
graph_view->extraction_drop_callback(path, entity_identifier,
|
|
||||||
graph_view->extraction_drop_user_data);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *investigation_graph_view_get_node_identifier(
|
static const char *investigation_graph_view_get_node_identifier(
|
||||||
const InvestigationGraphNodeLayout *node_layout
|
const InvestigationGraphNodeLayout *node_layout
|
||||||
);
|
);
|
||||||
|
|
@ -3989,13 +3948,6 @@ InvestigationGraphView *investigation_graph_view_new(void)
|
||||||
TRUE
|
TRUE
|
||||||
);
|
);
|
||||||
|
|
||||||
graph_view->extraction_drop_target = GTK_DROP_TARGET(
|
|
||||||
gtk_drop_target_new(G_TYPE_STRING, GDK_ACTION_COPY));
|
|
||||||
g_signal_connect(graph_view->extraction_drop_target, "drop",
|
|
||||||
G_CALLBACK(investigation_graph_view_on_extraction_drop), graph_view);
|
|
||||||
gtk_widget_add_controller(graph_view->drawing_area,
|
|
||||||
GTK_EVENT_CONTROLLER(graph_view->extraction_drop_target));
|
|
||||||
|
|
||||||
gtk_drawing_area_set_content_width(
|
gtk_drawing_area_set_content_width(
|
||||||
GTK_DRAWING_AREA(
|
GTK_DRAWING_AREA(
|
||||||
graph_view->drawing_area
|
graph_view->drawing_area
|
||||||
|
|
@ -4462,16 +4414,6 @@ void investigation_graph_view_set_node_moved_callback(
|
||||||
user_data;
|
user_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void investigation_graph_view_set_extraction_drop_callback(
|
|
||||||
InvestigationGraphView *graph_view,
|
|
||||||
InvestigationGraphViewExtractionDropCallback callback,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
if (graph_view == NULL) return;
|
|
||||||
graph_view->extraction_drop_callback = callback;
|
|
||||||
graph_view->extraction_drop_user_data = user_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
const EntityRecord *investigation_graph_view_get_selected_entity(
|
const EntityRecord *investigation_graph_view_get_selected_entity(
|
||||||
const InvestigationGraphView *graph_view
|
const InvestigationGraphView *graph_view
|
||||||
)
|
)
|
||||||
|
|
@ -4643,39 +4585,6 @@ void investigation_graph_view_reset_view(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean investigation_graph_view_get_view_transform(
|
|
||||||
const InvestigationGraphView *graph_view,
|
|
||||||
double *zoom,
|
|
||||||
double *offset_x,
|
|
||||||
double *offset_y)
|
|
||||||
{
|
|
||||||
if (graph_view == NULL || graph_view->graph_model == NULL ||
|
|
||||||
zoom == NULL || offset_x == NULL || offset_y == NULL)
|
|
||||||
return FALSE;
|
|
||||||
*zoom = graph_view->zoom;
|
|
||||||
*offset_x = graph_view->offset_x;
|
|
||||||
*offset_y = graph_view->offset_y;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void investigation_graph_view_set_view_transform(
|
|
||||||
InvestigationGraphView *graph_view,
|
|
||||||
double zoom,
|
|
||||||
double offset_x,
|
|
||||||
double offset_y)
|
|
||||||
{
|
|
||||||
if (graph_view == NULL ||
|
|
||||||
zoom < INVESTIGATION_GRAPH_VIEW_MIN_ZOOM ||
|
|
||||||
zoom > INVESTIGATION_GRAPH_VIEW_MAX_ZOOM ||
|
|
||||||
!isfinite(zoom) || !isfinite(offset_x) || !isfinite(offset_y))
|
|
||||||
return;
|
|
||||||
graph_view->zoom = zoom;
|
|
||||||
graph_view->offset_x = offset_x;
|
|
||||||
graph_view->offset_y = offset_y;
|
|
||||||
if (graph_view->drawing_area != NULL)
|
|
||||||
gtk_widget_queue_draw(graph_view->drawing_area);
|
|
||||||
}
|
|
||||||
|
|
||||||
void investigation_graph_view_reset_layout(
|
void investigation_graph_view_reset_layout(
|
||||||
InvestigationGraphView *graph_view
|
InvestigationGraphView *graph_view
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -13,38 +13,6 @@
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
/** @brief Prépare le chemin du fichier pour un glisser-déposer. */
|
|
||||||
static GdkContentProvider *investigation_tree_view_on_drag_prepare(
|
|
||||||
GtkDragSource *source, double x, double y, gpointer user_data)
|
|
||||||
{
|
|
||||||
const char *path = g_object_get_data(G_OBJECT(source),
|
|
||||||
"investigation-node-path");
|
|
||||||
(void) x; (void) y; (void) user_data;
|
|
||||||
if (path == NULL || path[0] == '\0') return NULL;
|
|
||||||
return gdk_content_provider_new_typed(G_TYPE_STRING, path);
|
|
||||||
}
|
|
||||||
/** @brief Vérifie qu'un nœud est un fichier `.txt` sous `Extractions`. */
|
|
||||||
static gboolean investigation_tree_view_node_is_extraction(
|
|
||||||
const InvestigationNode *node)
|
|
||||||
{
|
|
||||||
const InvestigationNode *ancestor = NULL;
|
|
||||||
const char *name = NULL;
|
|
||||||
|
|
||||||
if (node == NULL ||
|
|
||||||
investigation_node_get_type(node) != INVESTIGATION_NODE_FILE)
|
|
||||||
return FALSE;
|
|
||||||
name = investigation_node_get_name(node);
|
|
||||||
if (name == NULL || !g_str_has_suffix(name, ".txt")) return FALSE;
|
|
||||||
ancestor = investigation_node_get_parent(node);
|
|
||||||
while (ancestor != NULL)
|
|
||||||
{
|
|
||||||
if (g_strcmp0(investigation_node_get_name(ancestor),
|
|
||||||
"Extractions") == 0) return TRUE;
|
|
||||||
ancestor = investigation_node_get_parent(ancestor);
|
|
||||||
}
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* InvestigationTreeItem
|
* InvestigationTreeItem
|
||||||
* ---------------------
|
* ---------------------
|
||||||
|
|
@ -280,7 +248,6 @@ static void investigation_tree_view_factory_setup(
|
||||||
GtkWidget *row_box = NULL;
|
GtkWidget *row_box = NULL;
|
||||||
GtkWidget *image = NULL;
|
GtkWidget *image = NULL;
|
||||||
GtkWidget *label = NULL;
|
GtkWidget *label = NULL;
|
||||||
GtkDragSource *drag_source = NULL;
|
|
||||||
|
|
||||||
(void)factory;
|
(void)factory;
|
||||||
(void)user_data;
|
(void)user_data;
|
||||||
|
|
@ -325,14 +292,6 @@ static void investigation_tree_view_factory_setup(
|
||||||
row_box
|
row_box
|
||||||
);
|
);
|
||||||
|
|
||||||
drag_source = gtk_drag_source_new();
|
|
||||||
gtk_drag_source_set_actions(drag_source, GDK_ACTION_COPY);
|
|
||||||
g_signal_connect(drag_source, "prepare",
|
|
||||||
G_CALLBACK(investigation_tree_view_on_drag_prepare), NULL);
|
|
||||||
g_object_set_data(G_OBJECT(row_box), "investigation-drag-source",
|
|
||||||
drag_source);
|
|
||||||
gtk_widget_add_controller(row_box, GTK_EVENT_CONTROLLER(drag_source));
|
|
||||||
|
|
||||||
gtk_list_item_set_child(
|
gtk_list_item_set_child(
|
||||||
list_item,
|
list_item,
|
||||||
expander
|
expander
|
||||||
|
|
@ -447,7 +406,6 @@ static void investigation_tree_view_factory_bind(
|
||||||
GtkWidget *label = NULL;
|
GtkWidget *label = NULL;
|
||||||
|
|
||||||
const char *node_name = NULL;
|
const char *node_name = NULL;
|
||||||
GtkDragSource *drag_source = NULL;
|
|
||||||
|
|
||||||
(void)factory;
|
(void)factory;
|
||||||
(void)user_data;
|
(void)user_data;
|
||||||
|
|
@ -497,13 +455,6 @@ static void investigation_tree_view_factory_bind(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
drag_source = GTK_DRAG_SOURCE(g_object_get_data(G_OBJECT(row_box),
|
|
||||||
"investigation-drag-source"));
|
|
||||||
if (drag_source != NULL)
|
|
||||||
g_object_set_data_full(G_OBJECT(drag_source), "investigation-node-path",
|
|
||||||
investigation_tree_view_node_is_extraction(node)
|
|
||||||
? g_strdup(investigation_node_get_path(node)) : NULL, g_free);
|
|
||||||
|
|
||||||
image = gtk_widget_get_first_child(
|
image = gtk_widget_get_first_child(
|
||||||
row_box
|
row_box
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -129,9 +129,6 @@ struct Workspace
|
||||||
gpointer
|
gpointer
|
||||||
graph_node_moved_user_data;
|
graph_node_moved_user_data;
|
||||||
|
|
||||||
WorkspaceExtractionDropCallback extraction_drop_callback;
|
|
||||||
gpointer extraction_drop_user_data;
|
|
||||||
|
|
||||||
WorkspaceResetGraphLayoutCallback
|
WorkspaceResetGraphLayoutCallback
|
||||||
reset_graph_layout_callback;
|
reset_graph_layout_callback;
|
||||||
|
|
||||||
|
|
@ -158,8 +155,6 @@ struct Workspace
|
||||||
gpointer person_name_user_data;
|
gpointer person_name_user_data;
|
||||||
WorkspacePersonEvidenceCallback person_evidence_callback;
|
WorkspacePersonEvidenceCallback person_evidence_callback;
|
||||||
gpointer person_evidence_user_data;
|
gpointer person_evidence_user_data;
|
||||||
WorkspaceEvidenceActivatedCallback evidence_activated_callback;
|
|
||||||
gpointer evidence_activated_user_data;
|
|
||||||
WorkspaceEntitySelectedCallback entity_selected_callback;
|
WorkspaceEntitySelectedCallback entity_selected_callback;
|
||||||
gpointer entity_selected_user_data;
|
gpointer entity_selected_user_data;
|
||||||
|
|
||||||
|
|
@ -182,10 +177,6 @@ struct Workspace
|
||||||
OsintActionCatalog *osint_action_catalog;
|
OsintActionCatalog *osint_action_catalog;
|
||||||
|
|
||||||
WorkspaceGraphState graph_state;
|
WorkspaceGraphState graph_state;
|
||||||
gboolean saved_graph_transform_valid;
|
|
||||||
double saved_graph_zoom;
|
|
||||||
double saved_graph_offset_x;
|
|
||||||
double saved_graph_offset_y;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -969,24 +960,6 @@ static void workspace_on_person_evidence_requested(const char *identifier,
|
||||||
workspace->person_evidence_user_data);
|
workspace->person_evidence_user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void workspace_on_evidence_activated(const char *identifier,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
Workspace *workspace = user_data;
|
|
||||||
if (workspace != NULL && workspace->evidence_activated_callback != NULL)
|
|
||||||
workspace->evidence_activated_callback(identifier,
|
|
||||||
workspace->evidence_activated_user_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void workspace_on_extraction_dropped(const char *file_path,
|
|
||||||
const char *target_entity_identifier, gpointer user_data)
|
|
||||||
{
|
|
||||||
Workspace *workspace = user_data;
|
|
||||||
if (workspace != NULL && workspace->extraction_drop_callback != NULL)
|
|
||||||
workspace->extraction_drop_callback(file_path, target_entity_identifier,
|
|
||||||
workspace->extraction_drop_user_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Désélectionne le nœud lorsque le volet est fermé manuellement.
|
* @brief Désélectionne le nœud lorsque le volet est fermé manuellement.
|
||||||
*/
|
*/
|
||||||
|
|
@ -1966,8 +1939,6 @@ Workspace *workspace_new(void)
|
||||||
workspace_on_graph_node_moved,
|
workspace_on_graph_node_moved,
|
||||||
workspace
|
workspace
|
||||||
);
|
);
|
||||||
investigation_graph_view_set_extraction_drop_callback(
|
|
||||||
workspace->graph_view, workspace_on_extraction_dropped, workspace);
|
|
||||||
|
|
||||||
entity_details_panel_set_close_callback(
|
entity_details_panel_set_close_callback(
|
||||||
workspace->entity_details_panel,
|
workspace->entity_details_panel,
|
||||||
|
|
@ -1993,9 +1964,6 @@ Workspace *workspace_new(void)
|
||||||
entity_details_panel_set_person_evidence_callback(
|
entity_details_panel_set_person_evidence_callback(
|
||||||
workspace->entity_details_panel,
|
workspace->entity_details_panel,
|
||||||
workspace_on_person_evidence_requested, workspace);
|
workspace_on_person_evidence_requested, workspace);
|
||||||
entity_details_panel_set_evidence_activated_callback(
|
|
||||||
workspace->entity_details_panel, workspace_on_evidence_activated,
|
|
||||||
workspace);
|
|
||||||
entity_details_panel_set_osint_callback(workspace->entity_details_panel,
|
entity_details_panel_set_osint_callback(workspace->entity_details_panel,
|
||||||
workspace_on_entity_osint_requested, workspace);
|
workspace_on_entity_osint_requested, workspace);
|
||||||
|
|
||||||
|
|
@ -3069,13 +3037,6 @@ void workspace_set_graph_loading(
|
||||||
workspace->entity_details_panel
|
workspace->entity_details_panel
|
||||||
);
|
);
|
||||||
|
|
||||||
workspace->saved_graph_transform_valid =
|
|
||||||
investigation_graph_view_get_view_transform(
|
|
||||||
workspace->graph_view,
|
|
||||||
&workspace->saved_graph_zoom,
|
|
||||||
&workspace->saved_graph_offset_x,
|
|
||||||
&workspace->saved_graph_offset_y);
|
|
||||||
|
|
||||||
investigation_graph_view_clear(
|
investigation_graph_view_clear(
|
||||||
workspace->graph_view
|
workspace->graph_view
|
||||||
);
|
);
|
||||||
|
|
@ -3181,16 +3142,6 @@ void workspace_set_graph(
|
||||||
graph_model
|
graph_model
|
||||||
);
|
);
|
||||||
|
|
||||||
if (workspace->saved_graph_transform_valid)
|
|
||||||
{
|
|
||||||
investigation_graph_view_set_view_transform(
|
|
||||||
workspace->graph_view,
|
|
||||||
workspace->saved_graph_zoom,
|
|
||||||
workspace->saved_graph_offset_x,
|
|
||||||
workspace->saved_graph_offset_y);
|
|
||||||
workspace->saved_graph_transform_valid = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
workspace->graph_state =
|
workspace->graph_state =
|
||||||
WORKSPACE_GRAPH_STATE_READY;
|
WORKSPACE_GRAPH_STATE_READY;
|
||||||
|
|
||||||
|
|
@ -3229,24 +3180,6 @@ void workspace_set_graph(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean workspace_get_graph_view_transform(
|
|
||||||
Workspace *workspace, double *zoom, double *offset_x, double *offset_y)
|
|
||||||
{
|
|
||||||
if (workspace == NULL || workspace->graph_state != WORKSPACE_GRAPH_STATE_READY)
|
|
||||||
return FALSE;
|
|
||||||
return investigation_graph_view_get_view_transform(
|
|
||||||
workspace->graph_view, zoom, offset_x, offset_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void workspace_set_graph_view_transform(
|
|
||||||
Workspace *workspace, double zoom, double offset_x, double offset_y)
|
|
||||||
{
|
|
||||||
if (workspace == NULL || workspace->graph_state != WORKSPACE_GRAPH_STATE_READY)
|
|
||||||
return;
|
|
||||||
investigation_graph_view_set_view_transform(
|
|
||||||
workspace->graph_view, zoom, offset_x, offset_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void workspace_set_graph_error(
|
void workspace_set_graph_error(
|
||||||
Workspace *workspace,
|
Workspace *workspace,
|
||||||
const char *message
|
const char *message
|
||||||
|
|
@ -3473,16 +3406,6 @@ void workspace_set_graph_node_moved_callback(
|
||||||
user_data;
|
user_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void workspace_set_extraction_drop_callback(
|
|
||||||
Workspace *workspace,
|
|
||||||
WorkspaceExtractionDropCallback callback,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
if (workspace == NULL) return;
|
|
||||||
workspace->extraction_drop_callback = callback;
|
|
||||||
workspace->extraction_drop_user_data = user_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void workspace_set_reset_graph_layout_callback(
|
void workspace_set_reset_graph_layout_callback(
|
||||||
Workspace *workspace,
|
Workspace *workspace,
|
||||||
WorkspaceResetGraphLayoutCallback callback,
|
WorkspaceResetGraphLayoutCallback callback,
|
||||||
|
|
@ -3600,15 +3523,6 @@ void workspace_set_person_evidence_callback(Workspace *workspace,
|
||||||
workspace->person_evidence_callback = callback;
|
workspace->person_evidence_callback = callback;
|
||||||
workspace->person_evidence_user_data = user_data;
|
workspace->person_evidence_user_data = user_data;
|
||||||
}
|
}
|
||||||
void workspace_set_evidence_activated_callback(
|
|
||||||
Workspace *workspace,
|
|
||||||
WorkspaceEvidenceActivatedCallback callback,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
if (workspace == NULL) return;
|
|
||||||
workspace->evidence_activated_callback = callback;
|
|
||||||
workspace->evidence_activated_user_data = user_data;
|
|
||||||
}
|
|
||||||
void workspace_set_entity_selected_callback(Workspace *workspace,
|
void workspace_set_entity_selected_callback(Workspace *workspace,
|
||||||
WorkspaceEntitySelectedCallback callback, gpointer user_data)
|
WorkspaceEntitySelectedCallback callback, gpointer user_data)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -525,8 +525,7 @@ static void test_database_initialize_valid_database(void)
|
||||||
"FROM investigation;"
|
"FROM investigation;"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert(strcmp(schema_version, "8") == 0);
|
assert(strcmp(schema_version, "7") == 0);
|
||||||
test_database_assert_table_exists(database, "graph_viewport");
|
|
||||||
test_database_assert_table_exists(database, "osint_executions");
|
test_database_assert_table_exists(database, "osint_executions");
|
||||||
test_database_assert_table_exists(database, "osint_execution_entities");
|
test_database_assert_table_exists(database, "osint_execution_entities");
|
||||||
test_database_assert_table_exists(database, "osint_execution_relations");
|
test_database_assert_table_exists(database, "osint_execution_relations");
|
||||||
|
|
@ -990,7 +989,7 @@ static void test_database_migrate_v1_to_v2(void)
|
||||||
assert(
|
assert(
|
||||||
strcmp(
|
strcmp(
|
||||||
schema_version,
|
schema_version,
|
||||||
"8"
|
"6"
|
||||||
) == 0
|
) == 0
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,183 +0,0 @@
|
||||||
#include "core/extraction_drop_service.h"
|
|
||||||
#include "dao/entity_dao.h"
|
|
||||||
#include "dao/evidence_dao.h"
|
|
||||||
#include "dao/evidence_entity_dao.h"
|
|
||||||
#include "database/database.h"
|
|
||||||
#include "models/entity_record.h"
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
#include <glib/gstdio.h>
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
char *root;
|
|
||||||
char *extractions;
|
|
||||||
char *file_path;
|
|
||||||
char *database_path;
|
|
||||||
Database *database;
|
|
||||||
} ExtractionDropFixture;
|
|
||||||
|
|
||||||
static void fixture_setup(ExtractionDropFixture *fixture, gconstpointer data)
|
|
||||||
{
|
|
||||||
GError *error = NULL;
|
|
||||||
(void) data;
|
|
||||||
fixture->root = g_dir_make_tmp("labfy-extraction-drop-XXXXXX", &error);
|
|
||||||
g_assert_no_error(error);
|
|
||||||
fixture->extractions = g_build_filename(fixture->root,
|
|
||||||
"02_Preuves_Traitees", "Extractions", "OSINT", NULL);
|
|
||||||
g_assert_cmpint(g_mkdir_with_parents(fixture->extractions, 0700), ==, 0);
|
|
||||||
fixture->file_path = g_build_filename(fixture->extractions,
|
|
||||||
"resultat.txt", NULL);
|
|
||||||
g_assert_true(g_file_set_contents(fixture->file_path,
|
|
||||||
"Cible : exemple.test\nAction : test\n\nRésultat public", -1, &error));
|
|
||||||
g_assert_no_error(error);
|
|
||||||
fixture->database_path = g_build_filename(fixture->root,
|
|
||||||
"Enquete.sqlite", NULL);
|
|
||||||
g_assert_true(database_initialize(fixture->database_path,
|
|
||||||
"Extraction", fixture->root));
|
|
||||||
fixture->database = database_open(fixture->database_path);
|
|
||||||
g_assert_nonnull(fixture->database);
|
|
||||||
g_assert_true(database_migrate_to_latest(fixture->database));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void fixture_teardown(ExtractionDropFixture *fixture,
|
|
||||||
gconstpointer data)
|
|
||||||
{
|
|
||||||
(void) data;
|
|
||||||
database_close(fixture->database);
|
|
||||||
g_remove(fixture->file_path);
|
|
||||||
g_remove(fixture->database_path);
|
|
||||||
g_rmdir(fixture->extractions);
|
|
||||||
{
|
|
||||||
char *directory = g_path_get_dirname(fixture->extractions);
|
|
||||||
g_rmdir(directory);
|
|
||||||
g_free(directory);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
char *directory = g_build_filename(fixture->root,
|
|
||||||
"02_Preuves_Traitees", NULL);
|
|
||||||
g_rmdir(directory);
|
|
||||||
g_free(directory);
|
|
||||||
}
|
|
||||||
g_rmdir(fixture->root);
|
|
||||||
g_free(fixture->database_path);
|
|
||||||
g_free(fixture->file_path);
|
|
||||||
g_free(fixture->extractions);
|
|
||||||
g_free(fixture->root);
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *insert_entity(Database *database)
|
|
||||||
{
|
|
||||||
EntityDao *dao = NULL;
|
|
||||||
EntityRecord *record = NULL;
|
|
||||||
char *identifier = g_uuid_string_random();
|
|
||||||
GError *error = NULL;
|
|
||||||
dao = entity_dao_new(database, &error);
|
|
||||||
record = entity_record_new(identifier, "other", "Destination",
|
|
||||||
"Destination", NULL, 50, "2026-07-23T12:00:00Z",
|
|
||||||
"2026-07-23T12:00:00Z", ENTITY_STATUS_ACTIVE, &error);
|
|
||||||
g_assert_no_error(error);
|
|
||||||
g_assert_true(entity_dao_insert(dao, record, &error));
|
|
||||||
g_assert_no_error(error);
|
|
||||||
entity_record_free(record);
|
|
||||||
entity_dao_free(dao);
|
|
||||||
return identifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_prepare_rejects_outside(ExtractionDropFixture *fixture,
|
|
||||||
gconstpointer data)
|
|
||||||
{
|
|
||||||
ExtractionDropInfo *info = NULL;
|
|
||||||
char *outside = g_build_filename(fixture->root, "hors.txt", NULL);
|
|
||||||
GError *error = NULL;
|
|
||||||
(void) data;
|
|
||||||
g_assert_true(g_file_set_contents(outside, "hors enquête", -1, &error));
|
|
||||||
g_assert_no_error(error);
|
|
||||||
info = extraction_drop_service_prepare(fixture->root, outside, &error);
|
|
||||||
g_assert_null(info);
|
|
||||||
g_assert_error(error, EXTRACTION_DROP_SERVICE_ERROR,
|
|
||||||
EXTRACTION_DROP_SERVICE_ERROR_OUTSIDE_INVESTIGATION);
|
|
||||||
g_clear_error(&error);
|
|
||||||
g_remove(outside);
|
|
||||||
g_free(outside);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_attach_persists_and_is_idempotent(
|
|
||||||
ExtractionDropFixture *fixture, gconstpointer data)
|
|
||||||
{
|
|
||||||
ExtractionDropInfo *info = NULL;
|
|
||||||
EvidenceDao *evidence_dao = NULL;
|
|
||||||
EvidenceEntityDao *link_dao = NULL;
|
|
||||||
GPtrArray *records = NULL;
|
|
||||||
char *entity_identifier = NULL;
|
|
||||||
gboolean exists = FALSE;
|
|
||||||
GError *error = NULL;
|
|
||||||
(void) data;
|
|
||||||
|
|
||||||
info = extraction_drop_service_prepare(fixture->root,
|
|
||||||
fixture->file_path, &error);
|
|
||||||
g_assert_no_error(error);
|
|
||||||
g_assert_nonnull(info);
|
|
||||||
g_assert_cmpstr(extraction_drop_info_get_source(info), ==,
|
|
||||||
"Cible : exemple.test");
|
|
||||||
entity_identifier = insert_entity(fixture->database);
|
|
||||||
g_assert_true(extraction_drop_service_attach(fixture->database, info,
|
|
||||||
entity_identifier, &error));
|
|
||||||
g_assert_no_error(error);
|
|
||||||
g_assert_true(extraction_drop_service_attach(fixture->database, info,
|
|
||||||
entity_identifier, &error));
|
|
||||||
g_assert_no_error(error);
|
|
||||||
evidence_dao = evidence_dao_new(fixture->database, &error);
|
|
||||||
records = evidence_dao_list_all(evidence_dao, &error);
|
|
||||||
g_assert_no_error(error);
|
|
||||||
g_assert_cmpuint(records->len, ==, 1U);
|
|
||||||
link_dao = evidence_entity_dao_new(fixture->database, &error);
|
|
||||||
g_assert_true(evidence_entity_dao_exists(link_dao,
|
|
||||||
evidence_record_get_identifier(g_ptr_array_index(records, 0U)),
|
|
||||||
entity_identifier, &exists, &error));
|
|
||||||
g_assert_no_error(error);
|
|
||||||
g_assert_true(exists);
|
|
||||||
evidence_entity_dao_free(link_dao);
|
|
||||||
g_ptr_array_unref(records);
|
|
||||||
evidence_dao_free(evidence_dao);
|
|
||||||
g_free(entity_identifier);
|
|
||||||
extraction_drop_info_free(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_create_entity_persists(ExtractionDropFixture *fixture,
|
|
||||||
gconstpointer data)
|
|
||||||
{
|
|
||||||
ExtractionDropInfo *info = NULL;
|
|
||||||
EntityDao *entity_dao = NULL;
|
|
||||||
EntityRecord *entity = NULL;
|
|
||||||
char *identifier = NULL;
|
|
||||||
GError *error = NULL;
|
|
||||||
(void) data;
|
|
||||||
info = extraction_drop_service_prepare(fixture->root,
|
|
||||||
fixture->file_path, &error);
|
|
||||||
g_assert_true(extraction_drop_service_create_entity(fixture->database,
|
|
||||||
info, &identifier, &error));
|
|
||||||
g_assert_no_error(error);
|
|
||||||
g_assert_true(g_uuid_string_is_valid(identifier));
|
|
||||||
entity_dao = entity_dao_new(fixture->database, &error);
|
|
||||||
entity = entity_dao_find_by_identifier(entity_dao, identifier, &error);
|
|
||||||
g_assert_no_error(error);
|
|
||||||
g_assert_nonnull(entity);
|
|
||||||
g_assert_cmpstr(entity_record_get_type_identifier(entity), ==, "other");
|
|
||||||
entity_record_free(entity);
|
|
||||||
entity_dao_free(entity_dao);
|
|
||||||
g_free(identifier);
|
|
||||||
extraction_drop_info_free(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
g_test_init(&argc, &argv, NULL);
|
|
||||||
g_test_add("/extraction-drop/outside", ExtractionDropFixture, NULL,
|
|
||||||
fixture_setup, test_prepare_rejects_outside, fixture_teardown);
|
|
||||||
g_test_add("/extraction-drop/attach", ExtractionDropFixture, NULL,
|
|
||||||
fixture_setup, test_attach_persists_and_is_idempotent, fixture_teardown);
|
|
||||||
g_test_add("/extraction-drop/create", ExtractionDropFixture, NULL,
|
|
||||||
fixture_setup, test_create_entity_persists, fixture_teardown);
|
|
||||||
return g_test_run();
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue