feat(evidence): extract file metadata with ExifTool
This commit is contained in:
parent
e6298e4f8a
commit
45fed7fcd6
13 changed files with 433 additions and 1 deletions
9
Makefile
9
Makefile
|
|
@ -126,6 +126,7 @@ TEST_SOCIAL_PLATFORM := tests/test_social_platform
|
|||
TEST_PERSON_ENTITY_SERVICE := tests/test_person_entity_service
|
||||
TEST_EML_ANALYZER := tests/test_eml_analyzer
|
||||
TEST_IBAN_ANALYZER := tests/test_iban_analyzer
|
||||
TEST_EXIFTOOL_METADATA := tests/test_exiftool_metadata
|
||||
|
||||
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
|
||||
$(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): \
|
||||
tests/test_investigation_graph_load_task.c \
|
||||
src/core/investigation_graph_load_task.c \
|
||||
|
|
@ -731,7 +736,8 @@ test: \
|
|||
$(TEST_SOCIAL_PLATFORM) \
|
||||
$(TEST_PERSON_ENTITY_SERVICE) \
|
||||
$(TEST_EML_ANALYZER) \
|
||||
$(TEST_IBAN_ANALYZER)
|
||||
$(TEST_IBAN_ANALYZER) \
|
||||
$(TEST_EXIFTOOL_METADATA)
|
||||
@echo "Exécution des tests..."
|
||||
@./$(TEST_NODE)
|
||||
@./$(TEST_TREE_MODEL)
|
||||
|
|
@ -792,6 +798,7 @@ test: \
|
|||
@$(TEST_PERSON_ENTITY_SERVICE)
|
||||
@$(TEST_EML_ANALYZER)
|
||||
@$(TEST_IBAN_ANALYZER)
|
||||
@$(TEST_EXIFTOOL_METADATA)
|
||||
@echo "Tous les tests sont valides."
|
||||
|
||||
%.o: %.c
|
||||
|
|
|
|||
27
include/core/exiftool_metadata.h
Normal file
27
include/core/exiftool_metadata.h
Normal 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
|
||||
|
|
@ -104,6 +104,9 @@ typedef void (*MainWindowAnalyzeEmlCallback)(const char *evidence_identifier,
|
|||
/** @brief Callback appelé pour analyser un RIB par OCR. */
|
||||
typedef void (*MainWindowAnalyzeRibCallback)(const char *evidence_identifier,
|
||||
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.
|
||||
|
|
@ -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. */
|
||||
void main_window_set_analyze_rib_callback(MainWindow *main_window,
|
||||
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.
|
||||
|
|
|
|||
25
include/views/metadata_analysis_dialog.h
Normal file
25
include/views/metadata_analysis_dialog.h
Normal 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
|
||||
|
|
@ -138,6 +138,9 @@ typedef void (*WorkspaceAnalyzeEmlCallback)(const char *evidence_identifier,
|
|||
/** @brief Callback appelé pour analyser un RIB par OCR. */
|
||||
typedef void (*WorkspaceAnalyzeRibCallback)(const char *evidence_identifier,
|
||||
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.
|
||||
|
|
@ -274,6 +277,9 @@ void workspace_set_analyze_eml_callback(Workspace *workspace,
|
|||
/** @brief Définit le callback d'analyse OCR d'un RIB. */
|
||||
void workspace_set_analyze_rib_callback(Workspace *workspace,
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@
|
|||
#include "views/rib_ocr_review_dialog.h"
|
||||
#include "core/rib_ocr.h"
|
||||
#include "core/iban_analyzer.h"
|
||||
#include "core/exiftool_metadata.h"
|
||||
#include "views/metadata_analysis_dialog.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <errno.h>
|
||||
|
|
@ -3527,6 +3529,101 @@ cleanup:
|
|||
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(©_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 l’import asynchrone d’un fichier.
|
||||
*/
|
||||
|
|
@ -7098,6 +7195,8 @@ static void application_on_activate(
|
|||
application_on_analyze_eml_requested, application);
|
||||
main_window_set_analyze_rib_callback(application->main_window,
|
||||
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(
|
||||
application->main_window,
|
||||
|
|
|
|||
82
src/core/exiftool_metadata.c
Normal file
82
src/core/exiftool_metadata.c
Normal 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;
|
||||
}
|
||||
|
|
@ -64,6 +64,11 @@ static const char *const tool_catalog_tesseract_version_arguments[] =
|
|||
"--version",
|
||||
NULL
|
||||
};
|
||||
static const char *const tool_catalog_exiftool_version_arguments[] =
|
||||
{
|
||||
"-ver",
|
||||
NULL
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Catalogue statique initial.
|
||||
|
|
@ -122,6 +127,14 @@ static const ToolCatalogEntry tool_catalog_entries[] =
|
|||
.requirement = TOOL_REQUIREMENT_OPTIONAL,
|
||||
.version_arguments = tool_catalog_tesseract_version_arguments,
|
||||
.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
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -112,6 +112,8 @@ struct MainWindow
|
|||
gpointer analyze_eml_user_data;
|
||||
MainWindowAnalyzeRibCallback analyze_rib_callback;
|
||||
gpointer analyze_rib_user_data;
|
||||
MainWindowExtractMetadataCallback extract_metadata_callback;
|
||||
gpointer extract_metadata_user_data;
|
||||
|
||||
MainWindowGraphNodeMovedCallback
|
||||
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)
|
||||
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.
|
||||
|
|
@ -975,6 +986,8 @@ MainWindow *main_window_new(
|
|||
main_window_on_analyze_eml_requested, main_window);
|
||||
workspace_set_analyze_rib_callback(main_window->workspace,
|
||||
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(
|
||||
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_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(
|
||||
MainWindow *main_window,
|
||||
|
|
|
|||
66
src/views/metadata_analysis_dialog.c
Normal file
66
src/views/metadata_analysis_dialog.c
Normal 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);
|
||||
}
|
||||
|
|
@ -93,6 +93,7 @@ struct Workspace
|
|||
GtkWidget *edit_evidence_button;
|
||||
GtkWidget *analyze_eml_button;
|
||||
GtkWidget *analyze_rib_button;
|
||||
GtkWidget *extract_metadata_button;
|
||||
GtkWidget *evidence_preview_stack;
|
||||
GtkWidget *evidence_preview_status;
|
||||
GtkWidget *evidence_preview_picture;
|
||||
|
|
@ -115,6 +116,8 @@ struct Workspace
|
|||
gpointer analyze_eml_user_data;
|
||||
WorkspaceAnalyzeRibCallback analyze_rib_callback;
|
||||
gpointer analyze_rib_user_data;
|
||||
WorkspaceExtractMetadataCallback extract_metadata_callback;
|
||||
gpointer extract_metadata_user_data;
|
||||
|
||||
WorkspaceGraphNodeMovedCallback
|
||||
graph_node_moved_callback;
|
||||
|
|
@ -1092,6 +1095,19 @@ static void workspace_on_analyze_rib_clicked(GtkButton *button, gpointer 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)
|
||||
{
|
||||
GtkWidget *evidence_content = NULL;
|
||||
|
|
@ -1468,6 +1484,16 @@ Workspace *workspace_new(void)
|
|||
g_signal_connect(workspace->analyze_rib_button, "clicked",
|
||||
G_CALLBACK(workspace_on_analyze_rib_clicked), workspace);
|
||||
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 =
|
||||
gtk_separator_new(
|
||||
|
|
@ -2454,6 +2480,8 @@ void workspace_set_selected_node(
|
|||
gtk_widget_set_sensitive(workspace->analyze_eml_button, FALSE);
|
||||
if (workspace->analyze_rib_button != NULL)
|
||||
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)
|
||||
{
|
||||
|
|
@ -2622,6 +2650,13 @@ void workspace_set_selected_evidence(
|
|||
gtk_widget_set_sensitive(workspace->analyze_rib_button,
|
||||
lower != NULL && (g_str_has_suffix(lower, ".jpg") ||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -3257,6 +3292,13 @@ void workspace_set_analyze_rib_callback(Workspace *workspace,
|
|||
workspace->analyze_rib_callback = callback;
|
||||
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(
|
||||
Workspace *workspace,
|
||||
|
|
|
|||
33
tests/test_exiftool_metadata.c
Normal file
33
tests/test_exiftool_metadata.c
Normal 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();
|
||||
}
|
||||
|
|
@ -62,6 +62,12 @@ static const ExpectedToolCatalogEntry expected_catalog_entries[] =
|
|||
.display_name = "Tesseract OCR",
|
||||
.executable_name = "tesseract",
|
||||
.version_argument = "--version"
|
||||
},
|
||||
{
|
||||
.identifier = "metadata.exiftool",
|
||||
.display_name = "ExifTool",
|
||||
.executable_name = "exiftool",
|
||||
.version_argument = "-ver"
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue