labfy-investigation/src/views/eml_analysis_dialog.c

208 lines
12 KiB
C

/******************************************************************************
* @file eml_analysis_dialog.c
* @brief Présentation en lecture seule d'une analyse EML.
******************************************************************************/
#include "views/eml_analysis_dialog.h"
#include "widgets/controlled_vocab_dropdown.h"
typedef struct { GtkWindow *window; GtkWidget *proposals_box;
EmlAnalysisDialogCallback callback; gpointer user_data; gboolean completed;
} EmlAnalysisDialogState;
/** @brief Libère l'état de révision. */
static void eml_analysis_dialog_state_free(gpointer data) { g_free(data); }
/** @brief Signale une annulation une seule fois. */
static void eml_analysis_dialog_cancel(EmlAnalysisDialogState *state)
{ if (state == NULL || state->completed) return; state->completed = TRUE;
if (state->callback != NULL) state->callback(NULL, state->user_data); }
/** @brief Traite la fermeture native. */
static gboolean eml_analysis_dialog_on_window_close(GtkWindow *window, gpointer data)
{ (void) window; eml_analysis_dialog_cancel(data); return FALSE; }
/** @brief Ferme la fenêtre de résultat. */
static void eml_analysis_dialog_on_close(GtkButton *button, gpointer data)
{ EmlAnalysisDialogState *state = data; (void) button;
eml_analysis_dialog_cancel(state); gtk_window_close(state->window); }
/** @brief Transmet uniquement les propositions explicitement cochées. */
static void eml_analysis_dialog_on_integrate(GtkButton *button, gpointer data)
{
EmlAnalysisDialogState *state = data;
GPtrArray *selected = g_ptr_array_new_with_free_func(
(GDestroyNotify) eml_entity_proposal_free);
(void) button;
for (GtkWidget *child = gtk_widget_get_first_child(state->proposals_box);
child != NULL; child = gtk_widget_get_next_sibling(child))
{
GtkWidget *check = GTK_IS_BOX(child) ? gtk_widget_get_first_child(child) : NULL;
if (check != NULL && GTK_IS_CHECK_BUTTON(check) &&
gtk_check_button_get_active(GTK_CHECK_BUTTON(check)))
{
const char *type = g_object_get_data(G_OBJECT(child), "eml-type");
const char *value = g_object_get_data(G_OBJECT(child), "eml-value");
GtkWidget *status = g_object_get_data(G_OBJECT(child), "eml-status");
GtkWidget *provenance = g_object_get_data(G_OBJECT(child),
"eml-provenance");
g_ptr_array_add(selected, eml_entity_proposal_new_with_metadata(
type, value,
controlled_vocab_dropdown_get_selected_code(status),
controlled_vocab_dropdown_get_selected_code(provenance)));
}
}
if (selected->len == 0)
{ g_ptr_array_unref(selected); return; }
state->completed = TRUE;
if (state->callback != NULL) state->callback(selected, state->user_data);
else g_ptr_array_unref(selected);
gtk_window_close(state->window);
}
/** @brief Ajoute les propositions d'un type sous forme de cases décochées. */
static void eml_analysis_dialog_add_proposals(GtkWidget *box,
const char *type, const char *label, const GPtrArray *values)
{
for (guint i = 0; values != NULL && i < values->len; i++)
{
const char *value = g_ptr_array_index((GPtrArray *) values, i);
char *text = g_strdup_printf("%s : %s", label, value);
GtkWidget *row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
GtkWidget *check = gtk_check_button_new_with_label(text);
GtkWidget *status = controlled_vocab_dropdown_new(
CONTROLLED_VOCAB_VERIFICATION_STATUS, "proposed");
GtkWidget *provenance = controlled_vocab_dropdown_new(
CONTROLLED_VOCAB_PROVENANCE_KIND,
g_strcmp0(type, "ip_address") == 0 ? "header" : "header");
g_object_set_data_full(G_OBJECT(row), "eml-type", g_strdup(type), g_free);
g_object_set_data_full(G_OBJECT(row), "eml-value", g_strdup(value), g_free);
g_object_set_data(G_OBJECT(row), "eml-status", status);
g_object_set_data(G_OBJECT(row), "eml-provenance", provenance);
gtk_widget_set_hexpand(check, TRUE);
gtk_box_append(GTK_BOX(row), check);
gtk_box_append(GTK_BOX(row), status);
gtk_box_append(GTK_BOX(row), provenance);
gtk_box_append(GTK_BOX(box), row); g_free(text);
}
}
/** @brief Ajoute une ligne de métadonnée sélectionnable. */
static void eml_analysis_dialog_add_field(GtkGrid *grid, int row,
const char *title, const char *value)
{
GtkWidget *name = gtk_label_new(title);
GtkWidget *content = gtk_label_new(value != NULL ? value : "Non présent");
gtk_label_set_xalign(GTK_LABEL(name), 0.0f);
gtk_label_set_xalign(GTK_LABEL(content), 0.0f);
gtk_label_set_selectable(GTK_LABEL(content), TRUE);
gtk_label_set_wrap(GTK_LABEL(content), TRUE);
gtk_widget_set_valign(name, GTK_ALIGN_START);
gtk_grid_attach(grid, name, 0, row, 1, 1);
gtk_grid_attach(grid, content, 1, row, 1, 1);
}
/** @brief Concatène un tableau de chaînes pour son affichage. */
static char *eml_analysis_dialog_join(const GPtrArray *values)
{
GString *text = g_string_new(NULL);
for (guint i = 0; values != NULL && i < values->len; i++)
g_string_append_printf(text, "%s%s", i > 0 ? "\n" : "",
(const char *) g_ptr_array_index((GPtrArray *) values, i));
return g_string_free(text, FALSE);
}
void eml_analysis_dialog_present(GtkWindow *parent,
const EmlProcessingResult *result, EmlAnalysisDialogCallback callback,
gpointer user_data)
{
const EmlAnalysis *analysis = eml_processing_result_get_analysis(result);
GtkWindow *window = NULL;
GtkWidget *box = NULL, *content = NULL, *scroll = NULL;
GtkWidget *grid = NULL, *raw_scroll = NULL, *raw = NULL;
GtkWidget *close = NULL, *integrate = NULL, *actions = NULL;
EmlAnalysisDialogState *state = NULL;
char *received = NULL, *emails = NULL, *domains = NULL;
char *sender_ips = NULL, *destination_ips = NULL;
if (parent == NULL || analysis == NULL) return;
state = g_new0(EmlAnalysisDialogState, 1);
state->callback = callback; state->user_data = user_data;
window = GTK_WINDOW(gtk_window_new());
gtk_window_set_title(window, "Analyse locale du courriel EML");
gtk_window_set_transient_for(window, parent); gtk_window_set_modal(window, TRUE);
gtk_window_set_default_size(window, 720, 560);
state->window = window;
box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
gtk_widget_set_margin_start(box, 14); gtk_widget_set_margin_end(box, 14);
gtk_widget_set_margin_top(box, 14); gtk_widget_set_margin_bottom(box, 14);
content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
gtk_box_append(GTK_BOX(content), gtk_label_new(
"Analyse effectuée sur la copie vérifiée de 02_Preuves_Traitees/Extractions."));
grid = gtk_grid_new(); gtk_grid_set_row_spacing(GTK_GRID(grid), 6);
gtk_grid_set_column_spacing(GTK_GRID(grid), 12);
eml_analysis_dialog_add_field(GTK_GRID(grid), 0, "Copie analysée",
eml_processing_result_get_copy_path(result));
eml_analysis_dialog_add_field(GTK_GRID(grid), 1, "From",
eml_analysis_get_first_header(analysis, "from"));
eml_analysis_dialog_add_field(GTK_GRID(grid), 2, "Reply-To",
eml_analysis_get_first_header(analysis, "reply-to"));
eml_analysis_dialog_add_field(GTK_GRID(grid), 3, "To",
eml_analysis_get_first_header(analysis, "to"));
eml_analysis_dialog_add_field(GTK_GRID(grid), 4, "Sujet",
eml_analysis_get_first_header(analysis, "subject"));
eml_analysis_dialog_add_field(GTK_GRID(grid), 5, "Date",
eml_analysis_get_first_header(analysis, "date"));
eml_analysis_dialog_add_field(GTK_GRID(grid), 6, "Message-ID",
eml_analysis_get_first_header(analysis, "message-id"));
eml_analysis_dialog_add_field(GTK_GRID(grid), 7, "Authentification",
eml_analysis_get_first_header(analysis, "authentication-results"));
received = eml_analysis_dialog_join(
eml_analysis_get_header_values(analysis, "received"));
emails = eml_analysis_dialog_join(eml_analysis_get_email_addresses(analysis));
domains = eml_analysis_dialog_join(eml_analysis_get_domains(analysis));
sender_ips = eml_analysis_dialog_join(
eml_analysis_get_sender_ip_addresses(analysis));
destination_ips = eml_analysis_dialog_join(
eml_analysis_get_destination_ip_addresses(analysis));
eml_analysis_dialog_add_field(GTK_GRID(grid), 8, "Received (ordre original)", received);
eml_analysis_dialog_add_field(GTK_GRID(grid), 9, "Emails proposés", emails);
eml_analysis_dialog_add_field(GTK_GRID(grid), 10, "Domaines proposés", domains);
eml_analysis_dialog_add_field(GTK_GRID(grid), 11, "IP expéditeur",
sender_ips);
eml_analysis_dialog_add_field(GTK_GRID(grid), 12, "IP destinataire",
destination_ips);
gtk_box_append(GTK_BOX(content), grid);
gtk_box_append(GTK_BOX(content), gtk_label_new(
"Sélection explicite des entités à intégrer"));
state->proposals_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
eml_analysis_dialog_add_proposals(state->proposals_box, "email_address",
"Email", eml_analysis_get_email_addresses(analysis));
eml_analysis_dialog_add_proposals(state->proposals_box, "domain_name",
"Domaine", eml_analysis_get_domains(analysis));
eml_analysis_dialog_add_proposals(state->proposals_box, "ip_address",
"IP expéditeur", eml_analysis_get_sender_ip_addresses(analysis));
eml_analysis_dialog_add_proposals(state->proposals_box, "ip_address",
"IP destinataire", eml_analysis_get_destination_ip_addresses(analysis));
gtk_box_append(GTK_BOX(content), state->proposals_box);
gtk_box_append(GTK_BOX(content), gtk_label_new("En-têtes bruts (lecture seule)"));
raw = gtk_text_view_new(); gtk_text_view_set_editable(GTK_TEXT_VIEW(raw), FALSE);
gtk_text_view_set_monospace(GTK_TEXT_VIEW(raw), TRUE);
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(raw), GTK_WRAP_WORD_CHAR);
gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(raw)),
eml_analysis_get_raw_headers(analysis), -1);
raw_scroll = gtk_scrolled_window_new();
gtk_widget_set_size_request(raw_scroll, -1, 180);
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(raw_scroll), raw);
gtk_box_append(GTK_BOX(content), raw_scroll);
scroll = gtk_scrolled_window_new();
gtk_widget_set_vexpand(scroll, TRUE);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), content);
gtk_box_append(GTK_BOX(box), scroll);
actions = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
gtk_widget_set_halign(actions, GTK_ALIGN_END);
close = gtk_button_new_with_label("Fermer");
integrate = gtk_button_new_with_label("Intégrer la sélection");
gtk_widget_add_css_class(integrate, "suggested-action");
g_signal_connect(close, "clicked", G_CALLBACK(eml_analysis_dialog_on_close), state);
g_signal_connect(integrate, "clicked", G_CALLBACK(eml_analysis_dialog_on_integrate), state);
gtk_box_append(GTK_BOX(actions), close); gtk_box_append(GTK_BOX(actions), integrate);
gtk_box_append(GTK_BOX(box), actions); gtk_window_set_child(window, box);
g_signal_connect(window, "close-request",
G_CALLBACK(eml_analysis_dialog_on_window_close), state);
g_object_set_data_full(G_OBJECT(window), "eml-dialog-state", state,
eml_analysis_dialog_state_free);
gtk_window_present(window);
g_free(received); g_free(emails); g_free(domains);
g_free(sender_ips); g_free(destination_ips);
}