208 lines
12 KiB
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);
|
|
}
|