feat(evidence): extract file metadata with ExifTool

This commit is contained in:
grayTerminal-sh 2026-07-22 23:53:12 +02:00
parent e6298e4f8a
commit 45fed7fcd6
13 changed files with 433 additions and 1 deletions

View file

@ -126,6 +126,7 @@ TEST_SOCIAL_PLATFORM := tests/test_social_platform
TEST_PERSON_ENTITY_SERVICE := tests/test_person_entity_service TEST_PERSON_ENTITY_SERVICE := tests/test_person_entity_service
TEST_EML_ANALYZER := tests/test_eml_analyzer 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
all: $(TARGET) all: $(TARGET)
@ -651,6 +652,10 @@ $(TEST_EML_ANALYZER): tests/test_eml_analyzer.c src/core/eml_analyzer.c
$(TEST_IBAN_ANALYZER): tests/test_iban_analyzer.c src/core/iban_analyzer.c $(TEST_IBAN_ANALYZER): tests/test_iban_analyzer.c src/core/iban_analyzer.c
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) $(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
$(TEST_EXIFTOOL_METADATA): tests/test_exiftool_metadata.c \
src/core/exiftool_metadata.c
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
$(TEST_INVESTIGATION_GRAPH_LOAD_TASK): \ $(TEST_INVESTIGATION_GRAPH_LOAD_TASK): \
tests/test_investigation_graph_load_task.c \ tests/test_investigation_graph_load_task.c \
src/core/investigation_graph_load_task.c \ src/core/investigation_graph_load_task.c \
@ -731,7 +736,8 @@ test: \
$(TEST_SOCIAL_PLATFORM) \ $(TEST_SOCIAL_PLATFORM) \
$(TEST_PERSON_ENTITY_SERVICE) \ $(TEST_PERSON_ENTITY_SERVICE) \
$(TEST_EML_ANALYZER) \ $(TEST_EML_ANALYZER) \
$(TEST_IBAN_ANALYZER) $(TEST_IBAN_ANALYZER) \
$(TEST_EXIFTOOL_METADATA)
@echo "Exécution des tests..." @echo "Exécution des tests..."
@./$(TEST_NODE) @./$(TEST_NODE)
@./$(TEST_TREE_MODEL) @./$(TEST_TREE_MODEL)
@ -792,6 +798,7 @@ test: \
@$(TEST_PERSON_ENTITY_SERVICE) @$(TEST_PERSON_ENTITY_SERVICE)
@$(TEST_EML_ANALYZER) @$(TEST_EML_ANALYZER)
@$(TEST_IBAN_ANALYZER) @$(TEST_IBAN_ANALYZER)
@$(TEST_EXIFTOOL_METADATA)
@echo "Tous les tests sont valides." @echo "Tous les tests sont valides."
%.o: %.c %.o: %.c

View file

@ -0,0 +1,27 @@
/******************************************************************************
* @file exiftool_metadata.h
* @brief Extraction locale des métadonnées techniques d'une preuve.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_EXIFTOOL_METADATA_H
#define LABFY_INVESTIGATION_EXIFTOOL_METADATA_H
#include <gio/gio.h>
G_BEGIN_DECLS
/**
* @brief Extrait une synthèse lisible et la sortie JSON brute avec ExifTool.
* @param file_path Chemin du fichier de travail à analyser.
* @param summary Emplacement recevant la synthèse nouvellement allouée.
* @param json Emplacement recevant le JSON nouvellement alloué.
* @param version Emplacement facultatif recevant la version de l'outil.
* @param error Emplacement facultatif pour l'erreur.
* @return TRUE lorsque les deux extractions ont réussi.
*/
gboolean exiftool_metadata_extract(const char *file_path, char **summary,
char **json, char **version, GError **error);
G_END_DECLS
#endif

View file

@ -104,6 +104,9 @@ typedef void (*MainWindowAnalyzeEmlCallback)(const char *evidence_identifier,
/** @brief Callback appelé pour analyser un RIB par OCR. */ /** @brief Callback appelé pour analyser un RIB par OCR. */
typedef void (*MainWindowAnalyzeRibCallback)(const char *evidence_identifier, typedef void (*MainWindowAnalyzeRibCallback)(const char *evidence_identifier,
gpointer user_data); gpointer user_data);
/** @brief Callback appelé pour extraire les métadonnées d'une preuve. */
typedef void (*MainWindowExtractMetadataCallback)(const char *evidence_identifier,
gpointer user_data);
/** /**
* @brief Callback appelé après le déplacement effectif d'un nœud. * @brief Callback appelé après le déplacement effectif d'un nœud.
@ -218,6 +221,9 @@ void main_window_set_analyze_eml_callback(MainWindow *main_window,
/** @brief Définit le callback d'analyse OCR d'un RIB. */ /** @brief Définit le callback d'analyse OCR d'un RIB. */
void main_window_set_analyze_rib_callback(MainWindow *main_window, void main_window_set_analyze_rib_callback(MainWindow *main_window,
MainWindowAnalyzeRibCallback callback, gpointer user_data); MainWindowAnalyzeRibCallback callback, gpointer user_data);
/** @brief Définit le callback d'extraction locale des métadonnées. */
void main_window_set_extract_metadata_callback(MainWindow *main_window,
MainWindowExtractMetadataCallback callback, gpointer user_data);
/** /**
* @brief Définit le callback de fin de déplacement d'un nœud. * @brief Définit le callback de fin de déplacement d'un nœud.

View file

@ -0,0 +1,25 @@
/******************************************************************************
* @file metadata_analysis_dialog.h
* @brief Fenêtre de consultation des métadonnées extraites.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_METADATA_ANALYSIS_DIALOG_H
#define LABFY_INVESTIGATION_METADATA_ANALYSIS_DIALOG_H
#include <gtk/gtk.h>
G_BEGIN_DECLS
/**
* @brief Affiche une synthèse ExifTool dans une fenêtre défilable.
* @param parent Fenêtre parente facultative.
* @param summary Métadonnées lisibles empruntées.
* @param json_path Chemin emprunté de la sortie JSON conservée.
* @param version Version empruntée d'ExifTool.
*/
void metadata_analysis_dialog_present(GtkWindow *parent, const char *summary,
const char *json_path, const char *version);
G_END_DECLS
#endif

View file

@ -138,6 +138,9 @@ typedef void (*WorkspaceAnalyzeEmlCallback)(const char *evidence_identifier,
/** @brief Callback appelé pour analyser un RIB par OCR. */ /** @brief Callback appelé pour analyser un RIB par OCR. */
typedef void (*WorkspaceAnalyzeRibCallback)(const char *evidence_identifier, typedef void (*WorkspaceAnalyzeRibCallback)(const char *evidence_identifier,
gpointer user_data); gpointer user_data);
/** @brief Callback appelé pour extraire les métadonnées d'une preuve. */
typedef void (*WorkspaceExtractMetadataCallback)(const char *evidence_identifier,
gpointer user_data);
/** /**
* @brief Crée une nouvelle zone de travail. * @brief Crée une nouvelle zone de travail.
@ -274,6 +277,9 @@ void workspace_set_analyze_eml_callback(Workspace *workspace,
/** @brief Définit le callback d'analyse OCR d'un RIB. */ /** @brief Définit le callback d'analyse OCR d'un RIB. */
void workspace_set_analyze_rib_callback(Workspace *workspace, void workspace_set_analyze_rib_callback(Workspace *workspace,
WorkspaceAnalyzeRibCallback callback, gpointer user_data); WorkspaceAnalyzeRibCallback callback, gpointer user_data);
/** @brief Définit le callback d'extraction locale des métadonnées. */
void workspace_set_extract_metadata_callback(Workspace *workspace,
WorkspaceExtractMetadataCallback callback, gpointer user_data);
/** /**
* @brief Définit le callback de vérification de la preuve affichée. * @brief Définit le callback de vérification de la preuve affichée.

View file

@ -63,6 +63,8 @@
#include "views/rib_ocr_review_dialog.h" #include "views/rib_ocr_review_dialog.h"
#include "core/rib_ocr.h" #include "core/rib_ocr.h"
#include "core/iban_analyzer.h" #include "core/iban_analyzer.h"
#include "core/exiftool_metadata.h"
#include "views/metadata_analysis_dialog.h"
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <errno.h> #include <errno.h>
@ -3527,6 +3529,101 @@ cleanup:
evidence_record_free(record); evidence_dao_free(dao); evidence_record_free(record); evidence_dao_free(dao);
} }
/** @brief Analyse avec ExifTool une copie de travail de la preuve. */
static void application_on_extract_metadata_requested(
const char *evidence_identifier, gpointer user_data)
{
Application *application = user_data;
const InvestigationProject *project = NULL;
EvidenceDao *dao = NULL;
EvidenceRecord *record = NULL;
char *canonical_root = NULL;
char *candidate_path = NULL;
char *source_path = NULL;
char *output_directory = NULL;
char *copy_name = NULL;
char *copy_path = NULL;
char *json_name = NULL;
char *json_path = NULL;
char *summary = NULL;
char *json = NULL;
char *version = NULL;
GFile *source_file = NULL;
GFile *copy_file = NULL;
GError *error = NULL;
if (application == NULL || application->session == NULL ||
evidence_identifier == NULL) return;
dao = evidence_dao_new(
investigation_session_get_database(application->session), &error);
if (dao != NULL)
record = evidence_dao_find_by_identifier(dao, evidence_identifier,
&error);
project = investigation_session_get_project(application->session);
if (record == NULL || project == NULL) goto failure;
canonical_root = g_canonicalize_filename(
investigation_project_get_root_path(project), NULL);
candidate_path = g_build_filename(canonical_root,
evidence_record_get_relative_path(record), NULL);
source_path = g_canonicalize_filename(candidate_path, NULL);
if (source_path == NULL || !g_str_has_prefix(source_path, canonical_root) ||
(source_path[strlen(canonical_root)] != G_DIR_SEPARATOR &&
source_path[strlen(canonical_root)] != '\0'))
{
g_set_error_literal(&error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
"Le chemin de la preuve sort du dossier de l'enquête.");
goto failure;
}
output_directory = g_build_filename(canonical_root,
"02_Preuves_Traitees", "Extractions", "Metadata", NULL);
if (g_mkdir_with_parents(output_directory, 0750) != 0)
{
g_set_error(&error, G_IO_ERROR, g_io_error_from_errno(errno),
"Impossible de créer le dossier des métadonnées : %s",
g_strerror(errno));
goto failure;
}
copy_name = g_strdup_printf("%s-%s", evidence_identifier,
evidence_record_get_original_name(record));
copy_path = g_build_filename(output_directory, copy_name, NULL);
source_file = g_file_new_for_path(source_path);
copy_file = g_file_new_for_path(copy_path);
if (!g_file_copy(source_file, copy_file, G_FILE_COPY_OVERWRITE,
NULL, NULL, NULL, &error)) goto failure;
if (!exiftool_metadata_extract(copy_path, &summary, &json, &version,
&error)) goto failure;
json_name = g_strdup_printf("%s-exiftool.json", evidence_identifier);
json_path = g_build_filename(output_directory, json_name, NULL);
if (!g_file_set_contents(json_path, json, -1, &error)) goto failure;
metadata_analysis_dialog_present(
main_window_get_window(application->main_window), summary, json_path,
version);
main_window_set_status(application->main_window,
"Métadonnées extraites localement et sortie JSON conservée.");
goto cleanup;
failure:
application_present_error(application, "Extraction impossible",
error != NULL ? error->message :
"La preuve n'a pas pu être analysée avec ExifTool.");
cleanup:
g_clear_error(&error);
g_clear_object(&source_file);
g_clear_object(&copy_file);
g_free(canonical_root);
g_free(candidate_path);
g_free(source_path);
g_free(output_directory);
g_free(copy_name);
g_free(copy_path);
g_free(json_name);
g_free(json_path);
g_free(summary);
g_free(json);
g_free(version);
evidence_record_free(record);
evidence_dao_free(dao);
}
/** /**
* @brief Prépare et démarre limport asynchrone dun fichier. * @brief Prépare et démarre limport asynchrone dun fichier.
*/ */
@ -7098,6 +7195,8 @@ static void application_on_activate(
application_on_analyze_eml_requested, application); application_on_analyze_eml_requested, application);
main_window_set_analyze_rib_callback(application->main_window, main_window_set_analyze_rib_callback(application->main_window,
application_on_analyze_rib_requested, application); application_on_analyze_rib_requested, application);
main_window_set_extract_metadata_callback(application->main_window,
application_on_extract_metadata_requested, application);
main_window_set_graph_node_moved_callback( main_window_set_graph_node_moved_callback(
application->main_window, application->main_window,

View file

@ -0,0 +1,82 @@
/******************************************************************************
* @file exiftool_metadata.c
* @brief Extraction locale des métadonnées techniques d'une preuve.
******************************************************************************/
#include "core/exiftool_metadata.h"
#include <gio/gio.h>
/** @brief Exécute ExifTool et retourne sa sortie standard en UTF-8. */
static gboolean exiftool_metadata_run(const char *const *arguments,
char **output, GError **error)
{
GSubprocess *process = NULL;
char *stdout_text = NULL;
char *stderr_text = NULL;
gboolean success = FALSE;
process = g_subprocess_newv(arguments,
G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDERR_PIPE, error);
if (process == NULL) return FALSE;
if (!g_subprocess_communicate_utf8(process, NULL, NULL, &stdout_text,
&stderr_text, error)) goto cleanup;
if (!g_subprocess_get_successful(process))
{
g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED,
"ExifTool a échoué : %s", stderr_text != NULL ? stderr_text :
"aucun détail disponible");
goto cleanup;
}
if (stdout_text == NULL || stdout_text[0] == '\0')
{
g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
"ExifTool n'a retourné aucune métadonnée.");
goto cleanup;
}
*output = g_steal_pointer(&stdout_text);
success = TRUE;
cleanup:
g_free(stdout_text);
g_free(stderr_text);
g_object_unref(process);
return success;
}
gboolean exiftool_metadata_extract(const char *file_path, char **summary,
char **json, char **version, GError **error)
{
const char *summary_arguments[] =
{ "exiftool", "-a", "-G1", "-s", file_path, NULL };
const char *json_arguments[] =
{ "exiftool", "-j", "-a", "-G1", "-s", file_path, NULL };
const char *version_arguments[] = { "exiftool", "-ver", NULL };
char *local_summary = NULL;
char *local_json = NULL;
char *local_version = NULL;
if (file_path == NULL || file_path[0] == '\0' || summary == NULL ||
json == NULL)
{
g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"Le fichier ou les sorties ExifTool sont absents.");
return FALSE;
}
*summary = NULL;
*json = NULL;
if (version != NULL) *version = NULL;
if (!exiftool_metadata_run(summary_arguments, &local_summary, error) ||
!exiftool_metadata_run(json_arguments, &local_json, error))
goto failure;
if (version != NULL && !exiftool_metadata_run(version_arguments,
&local_version, error)) goto failure;
*summary = g_steal_pointer(&local_summary);
*json = g_steal_pointer(&local_json);
if (version != NULL) *version = g_steal_pointer(&local_version);
return TRUE;
failure:
g_free(local_summary);
g_free(local_json);
g_free(local_version);
return FALSE;
}

View file

@ -64,6 +64,11 @@ static const char *const tool_catalog_tesseract_version_arguments[] =
"--version", "--version",
NULL NULL
}; };
static const char *const tool_catalog_exiftool_version_arguments[] =
{
"-ver",
NULL
};
/** /**
* @brief Catalogue statique initial. * @brief Catalogue statique initial.
@ -122,6 +127,14 @@ static const ToolCatalogEntry tool_catalog_entries[] =
.requirement = TOOL_REQUIREMENT_OPTIONAL, .requirement = TOOL_REQUIREMENT_OPTIONAL,
.version_arguments = tool_catalog_tesseract_version_arguments, .version_arguments = tool_catalog_tesseract_version_arguments,
.version_argument_count = 1 .version_argument_count = 1
},
{
.identifier = "metadata.exiftool",
.display_name = "ExifTool",
.executable_name = "exiftool",
.requirement = TOOL_REQUIREMENT_OPTIONAL,
.version_arguments = tool_catalog_exiftool_version_arguments,
.version_argument_count = 1
} }
}; };

View file

@ -112,6 +112,8 @@ struct MainWindow
gpointer analyze_eml_user_data; gpointer analyze_eml_user_data;
MainWindowAnalyzeRibCallback analyze_rib_callback; MainWindowAnalyzeRibCallback analyze_rib_callback;
gpointer analyze_rib_user_data; gpointer analyze_rib_user_data;
MainWindowExtractMetadataCallback extract_metadata_callback;
gpointer extract_metadata_user_data;
MainWindowGraphNodeMovedCallback MainWindowGraphNodeMovedCallback
graph_node_moved_callback; graph_node_moved_callback;
@ -212,6 +214,15 @@ static void main_window_on_analyze_rib_requested(const char *identifier,
if (window != NULL && window->analyze_rib_callback != NULL) if (window != NULL && window->analyze_rib_callback != NULL)
window->analyze_rib_callback(identifier, window->analyze_rib_user_data); window->analyze_rib_callback(identifier, window->analyze_rib_user_data);
} }
/** @brief Relaie la demande d'extraction des métadonnées. */
static void main_window_on_extract_metadata_requested(const char *identifier,
gpointer data)
{
MainWindow *window = data;
if (window != NULL && window->extract_metadata_callback != NULL)
window->extract_metadata_callback(identifier,
window->extract_metadata_user_data);
}
/** /**
* @brief Ouvre dans le workspace l'entité choisie dans la sidebar. * @brief Ouvre dans le workspace l'entité choisie dans la sidebar.
@ -975,6 +986,8 @@ MainWindow *main_window_new(
main_window_on_analyze_eml_requested, main_window); main_window_on_analyze_eml_requested, main_window);
workspace_set_analyze_rib_callback(main_window->workspace, workspace_set_analyze_rib_callback(main_window->workspace,
main_window_on_analyze_rib_requested, main_window); main_window_on_analyze_rib_requested, main_window);
workspace_set_extract_metadata_callback(main_window->workspace,
main_window_on_extract_metadata_requested, main_window);
workspace_widget = workspace_get_widget( workspace_widget = workspace_get_widget(
main_window->workspace main_window->workspace
@ -1530,6 +1543,13 @@ void main_window_set_analyze_rib_callback(MainWindow *main_window,
main_window->analyze_rib_callback = callback; main_window->analyze_rib_callback = callback;
main_window->analyze_rib_user_data = user_data; main_window->analyze_rib_user_data = user_data;
} }
void main_window_set_extract_metadata_callback(MainWindow *main_window,
MainWindowExtractMetadataCallback callback, gpointer user_data)
{
if (main_window == NULL) return;
main_window->extract_metadata_callback = callback;
main_window->extract_metadata_user_data = user_data;
}
void main_window_set_tree_selection_callback( void main_window_set_tree_selection_callback(
MainWindow *main_window, MainWindow *main_window,

View file

@ -0,0 +1,66 @@
/******************************************************************************
* @file metadata_analysis_dialog.c
* @brief Fenêtre de consultation des métadonnées extraites.
******************************************************************************/
#include "views/metadata_analysis_dialog.h"
void metadata_analysis_dialog_present(GtkWindow *parent, const char *summary,
const char *json_path, const char *version)
{
GtkWidget *window = NULL;
GtkWidget *content = NULL;
GtkWidget *heading = NULL;
GtkWidget *scrolled = NULL;
GtkWidget *text_view = NULL;
GtkTextBuffer *buffer = NULL;
GtkWidget *path_label = NULL;
GtkWidget *close_button = NULL;
char *heading_text = NULL;
char *path_text = NULL;
window = gtk_window_new();
gtk_window_set_title(GTK_WINDOW(window), "Métadonnées de la preuve");
gtk_window_set_default_size(GTK_WINDOW(window), 760, 560);
gtk_window_set_modal(GTK_WINDOW(window), TRUE);
if (parent != NULL) gtk_window_set_transient_for(GTK_WINDOW(window), parent);
content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
gtk_widget_set_margin_top(content, 16);
gtk_widget_set_margin_bottom(content, 16);
gtk_widget_set_margin_start(content, 16);
gtk_widget_set_margin_end(content, 16);
heading_text = g_strdup_printf("Extraction locale — ExifTool %s",
version != NULL ? version : "version inconnue");
heading = gtk_label_new(heading_text);
gtk_widget_set_halign(heading, GTK_ALIGN_START);
gtk_box_append(GTK_BOX(content), heading);
scrolled = gtk_scrolled_window_new();
gtk_widget_set_vexpand(scrolled, TRUE);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
text_view = gtk_text_view_new();
gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view), FALSE);
gtk_text_view_set_monospace(GTK_TEXT_VIEW(text_view), TRUE);
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view), GTK_WRAP_WORD_CHAR);
buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
gtk_text_buffer_set_text(buffer, summary != NULL ? summary :
"Aucune métadonnée exploitable.", -1);
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolled), text_view);
gtk_box_append(GTK_BOX(content), scrolled);
path_text = g_strdup_printf("Sortie JSON conservée : %s",
json_path != NULL ? json_path : "indisponible");
path_label = gtk_label_new(path_text);
gtk_label_set_wrap(GTK_LABEL(path_label), TRUE);
gtk_label_set_selectable(GTK_LABEL(path_label), TRUE);
gtk_widget_set_halign(path_label, GTK_ALIGN_START);
gtk_box_append(GTK_BOX(content), path_label);
close_button = gtk_button_new_with_label("Fermer");
gtk_widget_set_halign(close_button, GTK_ALIGN_END);
g_signal_connect_swapped(close_button, "clicked",
G_CALLBACK(gtk_window_destroy), window);
gtk_box_append(GTK_BOX(content), close_button);
gtk_window_set_child(GTK_WINDOW(window), content);
gtk_window_present(GTK_WINDOW(window));
g_free(heading_text);
g_free(path_text);
}

View file

@ -93,6 +93,7 @@ struct Workspace
GtkWidget *edit_evidence_button; GtkWidget *edit_evidence_button;
GtkWidget *analyze_eml_button; GtkWidget *analyze_eml_button;
GtkWidget *analyze_rib_button; GtkWidget *analyze_rib_button;
GtkWidget *extract_metadata_button;
GtkWidget *evidence_preview_stack; GtkWidget *evidence_preview_stack;
GtkWidget *evidence_preview_status; GtkWidget *evidence_preview_status;
GtkWidget *evidence_preview_picture; GtkWidget *evidence_preview_picture;
@ -115,6 +116,8 @@ struct Workspace
gpointer analyze_eml_user_data; gpointer analyze_eml_user_data;
WorkspaceAnalyzeRibCallback analyze_rib_callback; WorkspaceAnalyzeRibCallback analyze_rib_callback;
gpointer analyze_rib_user_data; gpointer analyze_rib_user_data;
WorkspaceExtractMetadataCallback extract_metadata_callback;
gpointer extract_metadata_user_data;
WorkspaceGraphNodeMovedCallback WorkspaceGraphNodeMovedCallback
graph_node_moved_callback; graph_node_moved_callback;
@ -1092,6 +1095,19 @@ static void workspace_on_analyze_rib_clicked(GtkButton *button, gpointer data)
workspace->analyze_rib_user_data); workspace->analyze_rib_user_data);
} }
/** @brief Transmet la demande d'extraction des métadonnées. */
static void workspace_on_extract_metadata_clicked(GtkButton *button,
gpointer data)
{
Workspace *workspace = data;
(void) button;
if (workspace != NULL && workspace->extract_metadata_callback != NULL &&
workspace->selected_evidence_identifier != NULL)
workspace->extract_metadata_callback(
workspace->selected_evidence_identifier,
workspace->extract_metadata_user_data);
}
Workspace *workspace_new(void) Workspace *workspace_new(void)
{ {
GtkWidget *evidence_content = NULL; GtkWidget *evidence_content = NULL;
@ -1468,6 +1484,16 @@ Workspace *workspace_new(void)
g_signal_connect(workspace->analyze_rib_button, "clicked", g_signal_connect(workspace->analyze_rib_button, "clicked",
G_CALLBACK(workspace_on_analyze_rib_clicked), workspace); G_CALLBACK(workspace_on_analyze_rib_clicked), workspace);
gtk_box_append(GTK_BOX(evidence_content), workspace->analyze_rib_button); gtk_box_append(GTK_BOX(evidence_content), workspace->analyze_rib_button);
workspace->extract_metadata_button = gtk_button_new_with_label(
"Extraire les métadonnées");
gtk_widget_set_halign(workspace->extract_metadata_button, GTK_ALIGN_START);
gtk_widget_set_sensitive(workspace->extract_metadata_button, FALSE);
gtk_widget_set_tooltip_text(workspace->extract_metadata_button,
"Analyser localement une copie avec ExifTool");
g_signal_connect(workspace->extract_metadata_button, "clicked",
G_CALLBACK(workspace_on_extract_metadata_clicked), workspace);
gtk_box_append(GTK_BOX(evidence_content),
workspace->extract_metadata_button);
evidence_separator = evidence_separator =
gtk_separator_new( gtk_separator_new(
@ -2454,6 +2480,8 @@ void workspace_set_selected_node(
gtk_widget_set_sensitive(workspace->analyze_eml_button, FALSE); gtk_widget_set_sensitive(workspace->analyze_eml_button, FALSE);
if (workspace->analyze_rib_button != NULL) if (workspace->analyze_rib_button != NULL)
gtk_widget_set_sensitive(workspace->analyze_rib_button, FALSE); gtk_widget_set_sensitive(workspace->analyze_rib_button, FALSE);
if (workspace->extract_metadata_button != NULL)
gtk_widget_set_sensitive(workspace->extract_metadata_button, FALSE);
if (node == NULL) if (node == NULL)
{ {
@ -2622,6 +2650,13 @@ void workspace_set_selected_evidence(
gtk_widget_set_sensitive(workspace->analyze_rib_button, gtk_widget_set_sensitive(workspace->analyze_rib_button,
lower != NULL && (g_str_has_suffix(lower, ".jpg") || lower != NULL && (g_str_has_suffix(lower, ".jpg") ||
g_str_has_suffix(lower, ".jpeg") || g_str_has_suffix(lower, ".png"))); g_str_has_suffix(lower, ".jpeg") || g_str_has_suffix(lower, ".png")));
gtk_widget_set_sensitive(workspace->extract_metadata_button,
lower != NULL && (g_str_has_suffix(lower, ".jpg") ||
g_str_has_suffix(lower, ".jpeg") ||
g_str_has_suffix(lower, ".png") ||
g_str_has_suffix(lower, ".mov") ||
g_str_has_suffix(lower, ".mp4") ||
g_str_has_suffix(lower, ".pdf")));
g_free(lower); g_free(lower);
} }
@ -3257,6 +3292,13 @@ void workspace_set_analyze_rib_callback(Workspace *workspace,
workspace->analyze_rib_callback = callback; workspace->analyze_rib_callback = callback;
workspace->analyze_rib_user_data = user_data; workspace->analyze_rib_user_data = user_data;
} }
void workspace_set_extract_metadata_callback(Workspace *workspace,
WorkspaceExtractMetadataCallback callback, gpointer user_data)
{
if (workspace == NULL) return;
workspace->extract_metadata_callback = callback;
workspace->extract_metadata_user_data = user_data;
}
void workspace_set_graph_node_moved_callback( void workspace_set_graph_node_moved_callback(
Workspace *workspace, Workspace *workspace,

View file

@ -0,0 +1,33 @@
/******************************************************************************
* @file test_exiftool_metadata.c
* @brief Tests de validation de l'extraction ExifTool.
******************************************************************************/
#include "core/exiftool_metadata.h"
#include <glib.h>
/** @brief Vérifie le rejet des arguments incomplets. */
static void test_exiftool_metadata_invalid_arguments(void)
{
GError *error = NULL;
char *summary = NULL;
char *json = NULL;
g_assert_false(exiftool_metadata_extract(NULL, &summary, &json, NULL,
&error));
g_assert_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_clear_error(&error);
g_assert_false(exiftool_metadata_extract("preuve.jpg", NULL, &json, NULL,
&error));
g_assert_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_clear_error(&error);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/exiftool-metadata/invalid-arguments",
test_exiftool_metadata_invalid_arguments);
return g_test_run();
}

View file

@ -62,6 +62,12 @@ static const ExpectedToolCatalogEntry expected_catalog_entries[] =
.display_name = "Tesseract OCR", .display_name = "Tesseract OCR",
.executable_name = "tesseract", .executable_name = "tesseract",
.version_argument = "--version" .version_argument = "--version"
},
{
.identifier = "metadata.exiftool",
.display_name = "ExifTool",
.executable_name = "exiftool",
.version_argument = "-ver"
} }
}; };