feat(evidence): preview image and video evidence
This commit is contained in:
parent
205c65f275
commit
f142de8663
5 changed files with 240 additions and 0 deletions
|
|
@ -411,6 +411,10 @@ void main_window_set_selected_evidence(
|
|||
const EvidenceRecord *evidence_record
|
||||
);
|
||||
|
||||
/** @brief Transmet au workspace un chemin absolu validé pour l'aperçu. */
|
||||
void main_window_set_evidence_preview(MainWindow *main_window,
|
||||
const char *file_path, const char *display_name);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Affiche l'état de chargement du graphe.
|
||||
|
|
|
|||
|
|
@ -165,6 +165,15 @@ void workspace_set_selected_evidence(
|
|||
const EvidenceRecord *evidence_record
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Affiche l'aperçu local d'un fichier image ou vidéo.
|
||||
*
|
||||
* file_path doit être un chemin absolu déjà validé par l'application.
|
||||
* Passer NULL libère l'aperçu courant.
|
||||
*/
|
||||
void workspace_set_evidence_preview(Workspace *workspace,
|
||||
const char *file_path, const char *display_name);
|
||||
|
||||
/**
|
||||
* @brief Sélectionne une entité dans le graphe affiché.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -4404,6 +4404,13 @@ static void application_on_evidence_selected(
|
|||
const char *original_name =
|
||||
NULL;
|
||||
|
||||
const InvestigationProject *project = NULL;
|
||||
const char *investigation_root = NULL;
|
||||
const char *relative_path = NULL;
|
||||
char *canonical_root = NULL;
|
||||
char *candidate_path = NULL;
|
||||
char *canonical_path = NULL;
|
||||
|
||||
char *status_message =
|
||||
NULL;
|
||||
|
||||
|
|
@ -4579,6 +4586,27 @@ static void application_on_evidence_selected(
|
|||
evidence_record
|
||||
);
|
||||
|
||||
project = investigation_session_get_project(application->session);
|
||||
investigation_root = project != NULL
|
||||
? investigation_project_get_root_path(project) : NULL;
|
||||
relative_path = evidence_record_get_relative_path(evidence_record);
|
||||
if (investigation_root != NULL && relative_path != NULL)
|
||||
{
|
||||
canonical_root = g_canonicalize_filename(investigation_root, NULL);
|
||||
candidate_path = g_build_filename(investigation_root,
|
||||
relative_path, NULL);
|
||||
canonical_path = g_canonicalize_filename(candidate_path, NULL);
|
||||
if (canonical_root != NULL && canonical_path != NULL &&
|
||||
g_str_has_prefix(canonical_path, canonical_root) &&
|
||||
canonical_path[strlen(canonical_root)] == G_DIR_SEPARATOR &&
|
||||
g_file_test(canonical_path, G_FILE_TEST_IS_REGULAR))
|
||||
{
|
||||
main_window_set_evidence_preview(application->main_window,
|
||||
canonical_path,
|
||||
evidence_record_get_original_name(evidence_record));
|
||||
}
|
||||
}
|
||||
|
||||
original_name =
|
||||
evidence_record_get_original_name(
|
||||
evidence_record
|
||||
|
|
@ -4601,6 +4629,9 @@ static void application_on_evidence_selected(
|
|||
g_free(
|
||||
status_message
|
||||
);
|
||||
g_free(canonical_path);
|
||||
g_free(candidate_path);
|
||||
g_free(canonical_root);
|
||||
|
||||
evidence_record_free(
|
||||
evidence_record
|
||||
|
|
|
|||
|
|
@ -1727,6 +1727,14 @@ void main_window_set_selected_evidence(
|
|||
);
|
||||
}
|
||||
|
||||
void main_window_set_evidence_preview(MainWindow *main_window,
|
||||
const char *file_path, const char *display_name)
|
||||
{
|
||||
if (main_window == NULL) return;
|
||||
workspace_set_evidence_preview(main_window->workspace, file_path,
|
||||
display_name);
|
||||
}
|
||||
|
||||
void main_window_free(
|
||||
MainWindow *main_window
|
||||
)
|
||||
|
|
|
|||
|
|
@ -90,6 +90,13 @@ struct Workspace
|
|||
GtkWidget *verify_evidence_button;
|
||||
GtkWidget *edit_evidence_button;
|
||||
GtkWidget *analyze_eml_button;
|
||||
GtkWidget *evidence_preview_stack;
|
||||
GtkWidget *evidence_preview_status;
|
||||
GtkWidget *evidence_preview_picture;
|
||||
GtkWidget *evidence_preview_video;
|
||||
GtkWidget *evidence_preview_expand_button;
|
||||
char *evidence_preview_path;
|
||||
gboolean evidence_preview_is_video;
|
||||
|
||||
char *selected_evidence_identifier;
|
||||
|
||||
|
|
@ -581,6 +588,79 @@ static const char *workspace_get_integrity_status_text(
|
|||
}
|
||||
}
|
||||
|
||||
/** @brief Affiche une erreur asynchrone produite par le moteur vidéo. */
|
||||
static void workspace_on_video_error_changed(GObject *object,
|
||||
GParamSpec *parameter, gpointer user_data)
|
||||
{
|
||||
Workspace *workspace = user_data;
|
||||
const GError *error = NULL;
|
||||
(void) parameter;
|
||||
if (workspace == NULL || !GTK_IS_MEDIA_STREAM(object)) return;
|
||||
error = gtk_media_stream_get_error(GTK_MEDIA_STREAM(object));
|
||||
if (error == NULL) return;
|
||||
gtk_label_set_text(GTK_LABEL(workspace->evidence_preview_status),
|
||||
error->message != NULL ? error->message :
|
||||
"Le moteur vidéo ne peut pas lire ce fichier.");
|
||||
gtk_stack_set_visible_child_name(GTK_STACK(
|
||||
workspace->evidence_preview_stack), "status");
|
||||
gtk_widget_set_sensitive(workspace->evidence_preview_expand_button,
|
||||
FALSE);
|
||||
}
|
||||
|
||||
/** @brief Arrête et détache le média vidéo actuellement affiché. */
|
||||
static void workspace_clear_evidence_preview(Workspace *workspace)
|
||||
{
|
||||
GtkMediaStream *stream = NULL;
|
||||
if (workspace == NULL) return;
|
||||
if (workspace->evidence_preview_video != NULL)
|
||||
{
|
||||
stream = gtk_video_get_media_stream(
|
||||
GTK_VIDEO(workspace->evidence_preview_video));
|
||||
if (stream != NULL)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_data(stream, workspace);
|
||||
gtk_media_stream_pause(stream);
|
||||
}
|
||||
gtk_video_set_media_stream(GTK_VIDEO(
|
||||
workspace->evidence_preview_video), NULL);
|
||||
}
|
||||
if (workspace->evidence_preview_picture != NULL)
|
||||
gtk_picture_set_paintable(GTK_PICTURE(
|
||||
workspace->evidence_preview_picture), NULL);
|
||||
g_clear_pointer(&workspace->evidence_preview_path, g_free);
|
||||
workspace->evidence_preview_is_video = FALSE;
|
||||
if (workspace->evidence_preview_expand_button != NULL)
|
||||
gtk_widget_set_sensitive(workspace->evidence_preview_expand_button,
|
||||
FALSE);
|
||||
}
|
||||
|
||||
/** @brief Ouvre le média courant dans une fenêtre d'aperçu agrandie. */
|
||||
static void workspace_on_expand_evidence_preview(GtkButton *button,
|
||||
gpointer user_data)
|
||||
{
|
||||
Workspace *workspace = user_data;
|
||||
GtkWindow *window = NULL;
|
||||
GtkWidget *content = NULL;
|
||||
(void) button;
|
||||
if (workspace == NULL || workspace->evidence_preview_path == NULL) return;
|
||||
window = GTK_WINDOW(gtk_window_new());
|
||||
gtk_window_set_title(window, "Aperçu de la preuve");
|
||||
gtk_window_set_default_size(window, 960, 720);
|
||||
if (workspace->evidence_preview_is_video)
|
||||
{
|
||||
content = gtk_video_new_for_filename(workspace->evidence_preview_path);
|
||||
gtk_video_set_autoplay(GTK_VIDEO(content), FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
content = gtk_picture_new_for_filename(workspace->evidence_preview_path);
|
||||
gtk_picture_set_can_shrink(GTK_PICTURE(content), TRUE);
|
||||
gtk_picture_set_content_fit(GTK_PICTURE(content), GTK_CONTENT_FIT_CONTAIN);
|
||||
}
|
||||
gtk_window_set_child(window, content);
|
||||
gtk_window_present(window);
|
||||
}
|
||||
|
||||
/** @brief Convertit le statut d'une relation en texte utilisateur. */
|
||||
static const char *workspace_get_relation_status_text(RelationStatus status)
|
||||
{
|
||||
|
|
@ -1209,6 +1289,38 @@ Workspace *workspace_new(void)
|
|||
workspace->evidence_name_label
|
||||
);
|
||||
|
||||
workspace->evidence_preview_stack = gtk_stack_new();
|
||||
workspace->evidence_preview_status = gtk_label_new(
|
||||
"Aucun aperçu disponible pour cette preuve.");
|
||||
workspace->evidence_preview_picture = gtk_picture_new();
|
||||
workspace->evidence_preview_video = gtk_video_new();
|
||||
workspace->evidence_preview_expand_button =
|
||||
gtk_button_new_with_label("Agrandir l’aperçu");
|
||||
gtk_widget_set_size_request(workspace->evidence_preview_stack, -1, 360);
|
||||
gtk_widget_set_hexpand(workspace->evidence_preview_stack, TRUE);
|
||||
gtk_picture_set_can_shrink(GTK_PICTURE(
|
||||
workspace->evidence_preview_picture), TRUE);
|
||||
gtk_picture_set_content_fit(GTK_PICTURE(
|
||||
workspace->evidence_preview_picture), GTK_CONTENT_FIT_CONTAIN);
|
||||
gtk_video_set_autoplay(GTK_VIDEO(workspace->evidence_preview_video), FALSE);
|
||||
gtk_stack_add_named(GTK_STACK(workspace->evidence_preview_stack),
|
||||
workspace->evidence_preview_status, "status");
|
||||
gtk_stack_add_named(GTK_STACK(workspace->evidence_preview_stack),
|
||||
workspace->evidence_preview_picture, "image");
|
||||
gtk_stack_add_named(GTK_STACK(workspace->evidence_preview_stack),
|
||||
workspace->evidence_preview_video, "video");
|
||||
gtk_stack_set_visible_child_name(GTK_STACK(
|
||||
workspace->evidence_preview_stack), "status");
|
||||
gtk_widget_set_halign(workspace->evidence_preview_expand_button,
|
||||
GTK_ALIGN_START);
|
||||
gtk_widget_set_sensitive(workspace->evidence_preview_expand_button, FALSE);
|
||||
g_signal_connect(workspace->evidence_preview_expand_button, "clicked",
|
||||
G_CALLBACK(workspace_on_expand_evidence_preview), workspace);
|
||||
gtk_box_append(GTK_BOX(evidence_content),
|
||||
workspace->evidence_preview_stack);
|
||||
gtk_box_append(GTK_BOX(evidence_content),
|
||||
workspace->evidence_preview_expand_button);
|
||||
|
||||
workspace->verify_evidence_button =
|
||||
gtk_button_new_with_label(
|
||||
"Vérifier l'intégrité"
|
||||
|
|
@ -2339,6 +2451,11 @@ void workspace_set_selected_evidence(
|
|||
return;
|
||||
}
|
||||
|
||||
workspace_clear_evidence_preview(workspace);
|
||||
if (workspace->evidence_preview_stack != NULL)
|
||||
gtk_stack_set_visible_child_name(GTK_STACK(
|
||||
workspace->evidence_preview_stack), "status");
|
||||
|
||||
g_clear_pointer(
|
||||
&workspace->selected_evidence_identifier,
|
||||
g_free
|
||||
|
|
@ -2520,6 +2637,75 @@ void workspace_set_selected_evidence(
|
|||
);
|
||||
}
|
||||
|
||||
void workspace_set_evidence_preview(Workspace *workspace,
|
||||
const char *file_path, const char *display_name)
|
||||
{
|
||||
GFile *file = NULL;
|
||||
GFileInfo *info = NULL;
|
||||
const char *content_type = NULL;
|
||||
char *mime_type = NULL;
|
||||
GtkMediaStream *stream = NULL;
|
||||
GError *error = NULL;
|
||||
if (workspace == NULL) return;
|
||||
workspace_clear_evidence_preview(workspace);
|
||||
if (file_path == NULL || file_path[0] == '\0')
|
||||
{
|
||||
gtk_label_set_text(GTK_LABEL(workspace->evidence_preview_status),
|
||||
"Aucun aperçu disponible pour cette preuve.");
|
||||
gtk_stack_set_visible_child_name(GTK_STACK(
|
||||
workspace->evidence_preview_stack), "status");
|
||||
return;
|
||||
}
|
||||
file = g_file_new_for_path(file_path);
|
||||
info = g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
|
||||
G_FILE_QUERY_INFO_NONE, NULL, &error);
|
||||
if (info != NULL) content_type = g_file_info_get_content_type(info);
|
||||
if (content_type != NULL) mime_type = g_content_type_get_mime_type(
|
||||
content_type);
|
||||
if (mime_type != NULL && g_str_has_prefix(mime_type, "image/"))
|
||||
{
|
||||
gtk_picture_set_filename(GTK_PICTURE(
|
||||
workspace->evidence_preview_picture), file_path);
|
||||
gtk_stack_set_visible_child_name(GTK_STACK(
|
||||
workspace->evidence_preview_stack), "image");
|
||||
workspace->evidence_preview_is_video = FALSE;
|
||||
}
|
||||
else if (mime_type != NULL && g_str_has_prefix(mime_type, "video/"))
|
||||
{
|
||||
stream = gtk_media_file_new_for_filename(file_path);
|
||||
g_signal_connect(stream, "notify::error",
|
||||
G_CALLBACK(workspace_on_video_error_changed), workspace);
|
||||
gtk_video_set_media_stream(GTK_VIDEO(
|
||||
workspace->evidence_preview_video), stream);
|
||||
g_object_unref(stream);
|
||||
gtk_stack_set_visible_child_name(GTK_STACK(
|
||||
workspace->evidence_preview_stack), "video");
|
||||
workspace->evidence_preview_is_video = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *message = g_strdup_printf(
|
||||
"Aperçu indisponible pour %s%s%s.",
|
||||
display_name != NULL ? display_name : "ce fichier",
|
||||
error != NULL ? " : " : "",
|
||||
error != NULL ? error->message : "format non pris en charge");
|
||||
gtk_label_set_text(GTK_LABEL(workspace->evidence_preview_status),
|
||||
message);
|
||||
gtk_stack_set_visible_child_name(GTK_STACK(
|
||||
workspace->evidence_preview_stack), "status");
|
||||
g_free(message);
|
||||
goto cleanup;
|
||||
}
|
||||
workspace->evidence_preview_path = g_strdup(file_path);
|
||||
gtk_widget_set_sensitive(workspace->evidence_preview_expand_button,
|
||||
workspace->evidence_preview_path != NULL);
|
||||
cleanup:
|
||||
g_clear_error(&error);
|
||||
g_free(mime_type);
|
||||
g_clear_object(&info);
|
||||
g_clear_object(&file);
|
||||
}
|
||||
|
||||
gboolean workspace_select_graph_entity(
|
||||
Workspace *workspace,
|
||||
const char *entity_identifier
|
||||
|
|
@ -3049,6 +3235,8 @@ void workspace_free(Workspace *workspace)
|
|||
return;
|
||||
}
|
||||
|
||||
workspace_clear_evidence_preview(workspace);
|
||||
|
||||
g_clear_pointer(
|
||||
&workspace->osint_selection_context,
|
||||
osint_selection_context_free
|
||||
|
|
|
|||
Loading…
Reference in a new issue