labfy-investigation/src/views/osint_dns_review_dialog.c
2026-07-22 12:29:17 +02:00

283 lines
10 KiB
C

/******************************************************************************
* @file osint_dns_review_dialog.c
* @brief Dialogue de sélection des propositions issues d'une réponse DNS.
******************************************************************************/
#include "views/osint_dns_review_dialog.h"
#include "models/osint_dns_proposal.h"
typedef struct
{
GtkWindow *window;
GtkWidget *select_all_button;
GtkWidget *confirm_button;
GPtrArray *proposals;
GPtrArray *selection_buttons;
OsintDnsReviewDialogConfirmCallback callback;
gpointer user_data;
} OsintDnsReviewDialogContext;
/** @brief Libère le contexte possédé par la fenêtre. */
static void osint_dns_review_dialog_context_free(gpointer data)
{
OsintDnsReviewDialogContext *context = data;
if (context == NULL) return;
g_clear_pointer(&context->selection_buttons, g_ptr_array_unref);
g_clear_pointer(&context->proposals, g_ptr_array_unref);
g_free(context);
}
/** @brief Ferme le dialogue sans intégrer de proposition. */
static void osint_dns_review_dialog_on_cancel_clicked(
GtkButton *button,
gpointer user_data
)
{
OsintDnsReviewDialogContext *context = user_data;
(void) button;
if (context != NULL) gtk_window_destroy(context->window);
}
/** @brief Actualise l'état du bouton de confirmation. */
static void osint_dns_review_dialog_update_confirmation(
OsintDnsReviewDialogContext *context
)
{
gboolean has_selection = FALSE;
for (guint index = 0;
context != NULL && index < context->selection_buttons->len;
index++)
{
GtkCheckButton *button = g_ptr_array_index(
context->selection_buttons, index
);
if (gtk_widget_get_sensitive(GTK_WIDGET(button)) &&
gtk_check_button_get_active(button))
{
has_selection = TRUE;
break;
}
}
if (context != NULL)
gtk_widget_set_sensitive(context->confirm_button, has_selection);
}
/** @brief Réagit à la sélection individuelle d'une proposition. */
static void osint_dns_review_dialog_on_proposal_toggled(
GtkCheckButton *button,
gpointer user_data
)
{
(void) button;
osint_dns_review_dialog_update_confirmation(user_data);
}
/** @brief Sélectionne ou désélectionne toutes les propositions compatibles. */
static void osint_dns_review_dialog_on_select_all_toggled(
GtkCheckButton *button,
gpointer user_data
)
{
OsintDnsReviewDialogContext *context = user_data;
const gboolean selected = gtk_check_button_get_active(button);
for (guint index = 0;
context != NULL && index < context->selection_buttons->len;
index++)
{
GtkWidget *selection_button = g_ptr_array_index(
context->selection_buttons, index
);
if (gtk_widget_get_sensitive(selection_button))
gtk_check_button_set_active(
GTK_CHECK_BUTTON(selection_button), selected
);
}
osint_dns_review_dialog_update_confirmation(context);
}
/** @brief Transmet les propositions cochées puis ferme le dialogue. */
static void osint_dns_review_dialog_on_confirm_clicked(
GtkButton *button,
gpointer user_data
)
{
OsintDnsReviewDialogContext *context = user_data;
GPtrArray *selected_proposals = g_ptr_array_new();
(void) button;
for (guint index = 0;
context != NULL && index < context->selection_buttons->len;
index++)
{
GtkCheckButton *selection_button = g_ptr_array_index(
context->selection_buttons, index
);
if (gtk_widget_get_sensitive(GTK_WIDGET(selection_button)) &&
gtk_check_button_get_active(selection_button))
{
g_ptr_array_add(
selected_proposals,
g_object_get_data(
G_OBJECT(selection_button), "osint-dns-proposal"
)
);
}
}
if (context != NULL && context->callback != NULL &&
selected_proposals->len > 0U)
{
context->callback(selected_proposals, context->user_data);
}
g_ptr_array_unref(selected_proposals);
if (context != NULL) gtk_window_destroy(context->window);
}
/** @brief Crée une ligne décrivant une proposition DNS. */
static GtkWidget *osint_dns_review_dialog_create_row(
OsintDnsReviewDialogContext *context,
const OsintDnsProposal *proposal
)
{
GtkWidget *row = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
const char *entity_type = osint_dns_proposal_get_entity_type(proposal);
const char *relation_type = osint_dns_proposal_get_relation_type(proposal);
char *normalized_value = osint_dns_proposal_dup_normalized_value(proposal);
const gboolean compatible = entity_type != NULL && relation_type != NULL &&
normalized_value != NULL;
char *title = g_strdup_printf(
"%s → %s",
osint_dns_proposal_get_record_type(proposal),
osint_dns_proposal_get_value(proposal)
);
char *details = g_strdup_printf(
"Cible : %s\nPropriétaire : %s\nType Labfy : %s\n"
"Relation prévue : %s — %s → %s",
osint_dns_proposal_get_target(proposal),
osint_dns_proposal_get_owner(proposal),
compatible ? entity_type : "non pris en charge",
osint_dns_proposal_get_target(proposal),
compatible ? relation_type : "non prise en charge",
normalized_value != NULL ? normalized_value
: osint_dns_proposal_get_value(proposal)
);
GtkWidget *selection_button = gtk_check_button_new_with_label(title);
GtkWidget *details_label = gtk_label_new(details);
gtk_widget_set_sensitive(selection_button, compatible);
gtk_widget_set_margin_start(details_label, 28);
gtk_widget_set_halign(details_label, GTK_ALIGN_START);
gtk_label_set_xalign(GTK_LABEL(details_label), 0.0F);
gtk_label_set_selectable(GTK_LABEL(details_label), TRUE);
g_object_set_data(
G_OBJECT(selection_button), "osint-dns-proposal", (gpointer) proposal
);
g_signal_connect(
selection_button, "toggled",
G_CALLBACK(osint_dns_review_dialog_on_proposal_toggled), context
);
g_ptr_array_add(context->selection_buttons, selection_button);
gtk_box_append(GTK_BOX(row), selection_button);
gtk_box_append(GTK_BOX(row), details_label);
g_free(normalized_value);
g_free(title);
g_free(details);
return row;
}
void osint_dns_review_dialog_present(
GtkWindow *parent_window,
GPtrArray *proposals,
OsintDnsReviewDialogConfirmCallback callback,
gpointer user_data
)
{
OsintDnsReviewDialogContext *context = NULL;
GtkWidget *main_box = NULL;
GtkWidget *title_label = NULL;
GtkWidget *notice_label = NULL;
GtkWidget *scrolled_window = NULL;
GtkWidget *proposal_box = NULL;
GtkWidget *button_box = NULL;
GtkWidget *cancel_button = NULL;
if (proposals == NULL || proposals->len == 0U) return;
context = g_new0(OsintDnsReviewDialogContext, 1);
context->window = GTK_WINDOW(gtk_window_new());
context->proposals = g_ptr_array_ref(proposals);
context->selection_buttons = g_ptr_array_new();
context->callback = callback;
context->user_data = user_data;
gtk_window_set_title(context->window, "Intégrer les propositions DNS");
gtk_window_set_default_size(context->window, 720, 560);
gtk_window_set_modal(context->window, TRUE);
gtk_window_set_destroy_with_parent(context->window, TRUE);
if (parent_window != NULL)
gtk_window_set_transient_for(context->window, parent_window);
g_object_set_data_full(
G_OBJECT(context->window), "osint-dns-review-context", context,
osint_dns_review_dialog_context_free
);
main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
gtk_widget_set_margin_start(main_box, 20);
gtk_widget_set_margin_end(main_box, 20);
gtk_widget_set_margin_top(main_box, 20);
gtk_widget_set_margin_bottom(main_box, 20);
title_label = gtk_label_new("Sélectionner les propositions à intégrer");
gtk_widget_set_halign(title_label, GTK_ALIGN_START);
gtk_widget_add_css_class(title_label, "title-2");
notice_label = gtk_label_new(
"Aucune écriture ne sera effectuée avant confirmation. "
"Les types non pris en charge restent visibles mais désactivés."
);
gtk_label_set_wrap(GTK_LABEL(notice_label), TRUE);
gtk_label_set_xalign(GTK_LABEL(notice_label), 0.0F);
context->select_all_button = gtk_check_button_new_with_label(
"Sélectionner toutes les propositions compatibles"
);
g_signal_connect(
context->select_all_button, "toggled",
G_CALLBACK(osint_dns_review_dialog_on_select_all_toggled), context
);
proposal_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 14);
for (guint index = 0; index < proposals->len; index++)
gtk_box_append(
GTK_BOX(proposal_box),
osint_dns_review_dialog_create_row(
context, g_ptr_array_index(proposals, index)
)
);
scrolled_window = gtk_scrolled_window_new();
gtk_widget_set_vexpand(scrolled_window, TRUE);
gtk_scrolled_window_set_child(
GTK_SCROLLED_WINDOW(scrolled_window), proposal_box
);
button_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
gtk_widget_set_halign(button_box, GTK_ALIGN_END);
cancel_button = gtk_button_new_with_label("Annuler");
context->confirm_button = gtk_button_new_with_label("Intégrer la sélection");
gtk_widget_add_css_class(context->confirm_button, "suggested-action");
gtk_widget_set_sensitive(context->confirm_button, FALSE);
g_signal_connect(
cancel_button, "clicked",
G_CALLBACK(osint_dns_review_dialog_on_cancel_clicked), context
);
g_signal_connect(
context->confirm_button, "clicked",
G_CALLBACK(osint_dns_review_dialog_on_confirm_clicked), context
);
gtk_box_append(GTK_BOX(button_box), cancel_button);
gtk_box_append(GTK_BOX(button_box), context->confirm_button);
gtk_box_append(GTK_BOX(main_box), title_label);
gtk_box_append(GTK_BOX(main_box), notice_label);
gtk_box_append(GTK_BOX(main_box), context->select_all_button);
gtk_box_append(GTK_BOX(main_box), scrolled_window);
gtk_box_append(GTK_BOX(main_box), button_box);
gtk_window_set_child(context->window, main_box);
gtk_window_present(context->window);
}