feat: raccorder le pivot EML à l'interface
This commit is contained in:
parent
015841a20c
commit
a3c4b4c51a
12 changed files with 723 additions and 23 deletions
47
docs/testing/EML_PIVOT_MANUAL_TEST.md
Normal file
47
docs/testing/EML_PIVOT_MANUAL_TEST.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# Test manuel du parcours EML
|
||||
|
||||
Ce test emploie uniquement la fixture synthétique
|
||||
`tests/fixtures/eml/manual_smoke_test.eml`. Il ne nécessite aucune donnée
|
||||
réelle.
|
||||
|
||||
## Parcours synthétique
|
||||
|
||||
1. Compiler avec `make -j8`.
|
||||
2. Créer une enquête synthétique neuve dans un répertoire de test.
|
||||
3. Importer `tests/fixtures/eml/manual_smoke_test.eml` comme preuve.
|
||||
4. Sélectionner cette preuve dans la barre latérale.
|
||||
5. Vérifier que l’action compacte « Analyser l’e-mail » devient active.
|
||||
6. Lancer l’action et observer « Analyse complète de l’e-mail » dans le
|
||||
panneau d’activité.
|
||||
7. Pendant un premier essai, annuler la tâche depuis ce panneau et vérifier
|
||||
qu’aucun dialogue de succès ni aucune nouvelle entité n’apparaît.
|
||||
8. Relancer l’analyse et vérifier dans le dialogue les en-têtes, les deux
|
||||
pièces jointes, les textes, les métadonnées et les avertissements relatifs
|
||||
aux outils optionnels éventuellement absents.
|
||||
9. Vérifier que l’IBAN commençant par `FR00` est indiqué comme donnée de
|
||||
démonstration invalide et n’est pas proposé à l’intégration.
|
||||
10. Vérifier que les valeurs sont sélectionnables et copiables.
|
||||
11. Fermer avec « Rejeter et fermer » et confirmer l’absence de nouvelle
|
||||
entité, relation ou rattachement.
|
||||
12. Relancer, sélectionner quelques adresses ou domaines synthétiques, puis
|
||||
choisir « Intégrer les éléments sélectionnés ».
|
||||
13. Vérifier le message de bilan, le rafraîchissement du graphe, de la barre
|
||||
latérale et des détails de preuve.
|
||||
14. Fermer puis rouvrir l’enquête synthétique et vérifier que les objets
|
||||
confirmés sont toujours présents.
|
||||
15. Recalculer l’intégrité de la preuve et vérifier que son SHA-256 est
|
||||
inchangé.
|
||||
|
||||
Noter séparément le comportement lorsque ExifTool, Tesseract ou les outils
|
||||
Poppler ne sont pas installés : les en-têtes et l’extraction MIME doivent
|
||||
rester consultables.
|
||||
|
||||
## Test sur une copie d’enquête réelle
|
||||
|
||||
Ne jamais commencer sur l’unique exemplaire d’une enquête. Copier le dossier
|
||||
complet, vérifier que la copie s’ouvre, conserver une sauvegarde distincte,
|
||||
puis lancer Labfy Investigation uniquement sur cette copie.
|
||||
|
||||
Ne jamais transmettre à Codex une preuve, une base SQLite, une pièce jointe
|
||||
ou une donnée sensible. En cas d’anomalie, relever uniquement les étapes, les
|
||||
messages techniques expurgés et le comportement observé.
|
||||
|
|
@ -54,6 +54,16 @@ BackgroundTask *eml_pipeline_task_new_with_tools_and_limit(
|
|||
guint document_analysis_limit
|
||||
);
|
||||
|
||||
BackgroundTask *eml_pipeline_task_start(
|
||||
const char *eml_path,
|
||||
const char *staging_directory,
|
||||
const char *evidence_id,
|
||||
const DocumentAnalysisTools *tools,
|
||||
BackgroundTaskCompletionCallback completion_callback,
|
||||
gpointer completion_data,
|
||||
GDestroyNotify completion_data_destroy
|
||||
);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* LABFY_INVESTIGATION_EML_PIPELINE_TASK_H */
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#define LABFY_INVESTIGATION_EML_ANALYSIS_DIALOG_H
|
||||
#include "core/eml_processing.h"
|
||||
#include "core/eml_integration.h"
|
||||
#include "core/eml_pipeline_task.h"
|
||||
#include <gtk/gtk.h>
|
||||
G_BEGIN_DECLS
|
||||
/** @brief Callback recevant les propositions sélectionnées, ou NULL. */
|
||||
|
|
@ -19,5 +20,14 @@ typedef void (*EmlAnalysisDialogCallback)(GPtrArray *proposals,
|
|||
void eml_analysis_dialog_present(GtkWindow *parent,
|
||||
const EmlProcessingResult *result, EmlAnalysisDialogCallback callback,
|
||||
gpointer user_data);
|
||||
void eml_analysis_dialog_present_pipeline(
|
||||
GtkWindow *parent,
|
||||
const EmlPipelineResult *result,
|
||||
const char *evidence_name,
|
||||
const char *relative_path,
|
||||
const char *source_sha256,
|
||||
EmlAnalysisDialogCallback callback,
|
||||
gpointer user_data
|
||||
);
|
||||
G_END_DECLS
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -497,6 +497,10 @@ void main_window_set_selected_evidence(
|
|||
MainWindow *main_window,
|
||||
const EvidenceRecord *evidence_record
|
||||
);
|
||||
void main_window_set_eml_analysis_available(
|
||||
MainWindow *main_window,
|
||||
gboolean available
|
||||
);
|
||||
|
||||
/** @brief Transmet au workspace un chemin absolu validé pour l'aperçu. */
|
||||
void main_window_set_evidence_preview(MainWindow *main_window,
|
||||
|
|
|
|||
|
|
@ -203,6 +203,10 @@ void workspace_set_selected_evidence(
|
|||
Workspace *workspace,
|
||||
const EvidenceRecord *evidence_record
|
||||
);
|
||||
void workspace_set_eml_analysis_available(
|
||||
Workspace *workspace,
|
||||
gboolean available
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Affiche l'aperçu local d'un fichier image ou vidéo.
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@
|
|||
#include "core/person_entity_service.h"
|
||||
#include "core/eml_processing.h"
|
||||
#include "core/eml_integration.h"
|
||||
#include "core/eml_pipeline_task.h"
|
||||
#include "core/file_hash.h"
|
||||
#include "database/database.h"
|
||||
#include "database/transaction.h"
|
||||
#include "views/create_social_account_dialog.h"
|
||||
|
|
@ -249,7 +251,15 @@ typedef struct
|
|||
static char *application_osint_output_to_utf8(GBytes *bytes);
|
||||
static char *application_osint_create_timestamp(void);
|
||||
/** @brief Contexte possédé pendant la révision d'une analyse EML. */
|
||||
typedef struct { Application *application; char *evidence_identifier; }
|
||||
typedef struct {
|
||||
Application *application;
|
||||
InvestigationSession *expected_session;
|
||||
char *evidence_identifier;
|
||||
char *evidence_name;
|
||||
char *relative_path;
|
||||
char *source_sha256;
|
||||
char *staging_directory;
|
||||
}
|
||||
ApplicationEmlReviewContext;
|
||||
|
||||
static void application_start_graph_loading(
|
||||
|
|
@ -3738,7 +3748,48 @@ cleanup:
|
|||
|
||||
/** @brief Libère le contexte de révision EML. */
|
||||
static void application_eml_review_context_free(ApplicationEmlReviewContext *context)
|
||||
{ if (context == NULL) return; g_free(context->evidence_identifier); g_free(context); }
|
||||
{
|
||||
if (context == NULL) return;
|
||||
g_free(context->evidence_identifier);
|
||||
g_free(context->evidence_name);
|
||||
g_free(context->relative_path);
|
||||
g_free(context->source_sha256);
|
||||
g_free(context->staging_directory);
|
||||
g_free(context);
|
||||
}
|
||||
|
||||
/** @brief Supprime récursivement une zone de staging créée par l'application. */
|
||||
static void application_remove_staging_directory(const char *path)
|
||||
{
|
||||
if (path == NULL) return;
|
||||
GFile *directory = g_file_new_for_path(path);
|
||||
GError *error = NULL;
|
||||
GFileEnumerator *enumerator = g_file_enumerate_children(directory,
|
||||
G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error);
|
||||
if (enumerator != NULL)
|
||||
{
|
||||
GFileInfo *info = NULL;
|
||||
while ((info = g_file_enumerator_next_file(
|
||||
enumerator, NULL, &error)) != NULL)
|
||||
{
|
||||
GFile *child = g_file_get_child(directory,
|
||||
g_file_info_get_name(info));
|
||||
char *child_path = g_file_get_path(child);
|
||||
if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY)
|
||||
application_remove_staging_directory(child_path);
|
||||
else
|
||||
(void) g_file_delete(child, NULL, NULL);
|
||||
g_free(child_path);
|
||||
g_object_unref(child);
|
||||
g_object_unref(info);
|
||||
}
|
||||
g_object_unref(enumerator);
|
||||
}
|
||||
g_clear_error(&error);
|
||||
(void) g_file_delete(directory, NULL, NULL);
|
||||
g_object_unref(directory);
|
||||
}
|
||||
|
||||
/** @brief Intègre la sélection EML puis actualise le graphe. */
|
||||
static void application_on_eml_selection_completed(
|
||||
|
|
@ -3749,7 +3800,12 @@ static void application_on_eml_selection_completed(
|
|||
const InvestigationProject *project = NULL;
|
||||
GError *error = NULL; guint created = 0, reused = 0; char *message = NULL;
|
||||
if (proposals == NULL)
|
||||
{ application_eml_review_context_free(context); return; }
|
||||
{
|
||||
application_remove_staging_directory(context != NULL
|
||||
? context->staging_directory : NULL);
|
||||
application_eml_review_context_free(context);
|
||||
return;
|
||||
}
|
||||
if (application == NULL || application->session == NULL ||
|
||||
!eml_integration_apply(investigation_session_get_database(application->session),
|
||||
context->evidence_identifier, proposals, &created, &reused, &error))
|
||||
|
|
@ -3764,12 +3820,66 @@ static void application_on_eml_selection_completed(
|
|||
project = investigation_session_get_project(application->session);
|
||||
application_start_graph_loading(application,
|
||||
investigation_project_get_database_path(project));
|
||||
(void) application_refresh_evidence_models(application, NULL);
|
||||
}
|
||||
g_free(message); g_clear_error(&error); g_ptr_array_unref(proposals);
|
||||
application_remove_staging_directory(context != NULL
|
||||
? context->staging_directory : NULL);
|
||||
application_eml_review_context_free(context);
|
||||
}
|
||||
|
||||
/** @brief Prépare une copie vérifiée puis affiche l'analyse EML locale. */
|
||||
/** @brief Termine le pipeline EML sur le contexte GTK principal. */
|
||||
static void application_on_eml_pipeline_completed(
|
||||
BackgroundTask *task,
|
||||
gpointer user_data)
|
||||
{
|
||||
ApplicationEmlReviewContext *context = user_data;
|
||||
Application *application = context != NULL ? context->application : NULL;
|
||||
BackgroundTaskState state = background_task_get_state(task);
|
||||
if (application == NULL || application->main_window == NULL ||
|
||||
application->session == NULL ||
|
||||
application->session != context->expected_session)
|
||||
{
|
||||
application_remove_staging_directory(context != NULL
|
||||
? context->staging_directory : NULL);
|
||||
application_eml_review_context_free(context);
|
||||
return;
|
||||
}
|
||||
if (state == BACKGROUND_TASK_STATE_CANCELLED)
|
||||
{
|
||||
main_window_set_status(application->main_window,
|
||||
"Analyse de l’e-mail annulée.");
|
||||
application_remove_staging_directory(context->staging_directory);
|
||||
application_eml_review_context_free(context);
|
||||
return;
|
||||
}
|
||||
if (state != BACKGROUND_TASK_STATE_COMPLETED)
|
||||
{
|
||||
GError *error = background_task_dup_error(task);
|
||||
application_present_error(application, "Analyse EML impossible",
|
||||
error != NULL ? error->message : "Le pipeline EML a échoué.");
|
||||
g_clear_error(&error);
|
||||
application_remove_staging_directory(context->staging_directory);
|
||||
application_eml_review_context_free(context);
|
||||
return;
|
||||
}
|
||||
EmlPipelineResult *result = background_task_get_result(task);
|
||||
if (result == NULL)
|
||||
{
|
||||
application_present_error(application, "Analyse EML impossible",
|
||||
"Le pipeline n’a produit aucun résultat.");
|
||||
application_remove_staging_directory(context->staging_directory);
|
||||
application_eml_review_context_free(context);
|
||||
return;
|
||||
}
|
||||
eml_analysis_dialog_present_pipeline(
|
||||
main_window_get_window(application->main_window),
|
||||
result, context->evidence_name, context->relative_path,
|
||||
context->source_sha256, application_on_eml_selection_completed,
|
||||
context);
|
||||
}
|
||||
|
||||
/** @brief Vérifie la preuve puis lance le pipeline EML en arrière-plan. */
|
||||
static void application_on_analyze_eml_requested(
|
||||
const char *evidence_identifier, gpointer user_data)
|
||||
{
|
||||
|
|
@ -3777,8 +3887,14 @@ static void application_on_analyze_eml_requested(
|
|||
const InvestigationProject *project = NULL;
|
||||
EvidenceDao *dao = NULL;
|
||||
EvidenceRecord *record = NULL;
|
||||
EmlProcessingResult *result = NULL;
|
||||
ApplicationEmlReviewContext *context = NULL;
|
||||
BackgroundTask *task = NULL;
|
||||
char *root = NULL;
|
||||
char *candidate = NULL;
|
||||
char *source_path = NULL;
|
||||
char *current_sha256 = NULL;
|
||||
guint64 current_size = 0;
|
||||
char *staging = NULL;
|
||||
GError *error = NULL;
|
||||
if (application == NULL || application->session == NULL ||
|
||||
evidence_identifier == NULL) return;
|
||||
|
|
@ -3787,22 +3903,93 @@ static void application_on_analyze_eml_requested(
|
|||
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)
|
||||
result = eml_processing_prepare(
|
||||
investigation_project_get_root_path(project), record, &error);
|
||||
if (result == NULL)
|
||||
application_present_error(application, "Analyse EML impossible",
|
||||
error != NULL ? error->message : "La copie de travail n'a pas pu être analysée.");
|
||||
else
|
||||
if (record == NULL || project == NULL) goto failure;
|
||||
root = g_canonicalize_filename(
|
||||
investigation_project_get_root_path(project), NULL);
|
||||
candidate = g_build_filename(root,
|
||||
evidence_record_get_relative_path(record), NULL);
|
||||
source_path = g_canonicalize_filename(candidate, NULL);
|
||||
const char *name = evidence_record_get_original_name(record);
|
||||
char *lower = name != NULL ? g_ascii_strdown(name, -1) : NULL;
|
||||
gboolean is_eml = lower != NULL && g_str_has_suffix(lower, ".eml");
|
||||
g_free(lower);
|
||||
if (!is_eml || source_path == NULL ||
|
||||
!g_str_has_prefix(source_path, root) ||
|
||||
(source_path[strlen(root)] != G_DIR_SEPARATOR &&
|
||||
source_path[strlen(root)] != '\0') ||
|
||||
!g_file_test(source_path, G_FILE_TEST_IS_REGULAR))
|
||||
{
|
||||
context = g_new0(ApplicationEmlReviewContext, 1);
|
||||
context->application = application;
|
||||
context->evidence_identifier = g_strdup(evidence_identifier);
|
||||
eml_analysis_dialog_present(main_window_get_window(application->main_window),
|
||||
result, application_on_eml_selection_completed, context);
|
||||
g_set_error_literal(&error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
|
||||
"La preuve sélectionnée n’est pas un fichier EML exploitable.");
|
||||
goto failure;
|
||||
}
|
||||
eml_processing_result_free(result); evidence_record_free(record);
|
||||
evidence_dao_free(dao); g_clear_error(&error);
|
||||
if (!file_hash_compute_sha256(source_path, NULL, ¤t_sha256,
|
||||
¤t_size, &error) ||
|
||||
g_strcmp0(current_sha256, evidence_record_get_sha256(record)) != 0)
|
||||
{
|
||||
if (error == NULL)
|
||||
g_set_error_literal(&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
|
||||
"L’empreinte SHA-256 de la preuve diverge de "
|
||||
"l’empreinte enregistrée. L’analyse est bloquée.");
|
||||
goto failure;
|
||||
}
|
||||
staging = g_dir_make_tmp("labfy-eml-staging-XXXXXX", &error);
|
||||
if (staging == NULL) goto failure;
|
||||
context = g_new0(ApplicationEmlReviewContext, 1);
|
||||
context->application = application;
|
||||
context->expected_session = application->session;
|
||||
context->evidence_identifier = g_strdup(evidence_identifier);
|
||||
context->evidence_name = g_strdup(name);
|
||||
context->relative_path = g_strdup(
|
||||
evidence_record_get_relative_path(record));
|
||||
context->source_sha256 = g_strdup(current_sha256);
|
||||
context->staging_directory = staging;
|
||||
staging = NULL;
|
||||
DocumentAnalysisTools tools = {
|
||||
.exiftool = "exiftool", .tesseract = "tesseract",
|
||||
.pdfinfo = "pdfinfo", .pdftotext = "pdftotext",
|
||||
.pdftoppm = "pdftoppm"
|
||||
};
|
||||
task = eml_pipeline_task_start(source_path, context->staging_directory,
|
||||
evidence_identifier, &tools, application_on_eml_pipeline_completed,
|
||||
context, NULL);
|
||||
if (task == NULL)
|
||||
goto failure;
|
||||
context = NULL;
|
||||
if (!task_manager_add(application->task_manager, task, &error))
|
||||
{
|
||||
background_task_cancel(task);
|
||||
goto cleanup;
|
||||
}
|
||||
main_window_set_status(application->main_window,
|
||||
"Analyse de l’e-mail lancée en arrière-plan.");
|
||||
background_task_unref(task);
|
||||
task = NULL;
|
||||
goto cleanup;
|
||||
|
||||
failure:
|
||||
if (task != NULL)
|
||||
background_task_cancel(task);
|
||||
if (context != NULL)
|
||||
application_remove_staging_directory(context->staging_directory);
|
||||
else
|
||||
application_remove_staging_directory(staging);
|
||||
if (context != NULL)
|
||||
application_eml_review_context_free(context);
|
||||
if (application != NULL)
|
||||
application_present_error(application, "Analyse EML impossible",
|
||||
error != NULL ? error->message :
|
||||
"La preuve n’a pas pu être analysée.");
|
||||
cleanup:
|
||||
background_task_unref(task);
|
||||
g_free(staging);
|
||||
g_free(current_sha256);
|
||||
g_free(source_path);
|
||||
g_free(candidate);
|
||||
g_free(root);
|
||||
evidence_record_free(record);
|
||||
evidence_dao_free(dao);
|
||||
g_clear_error(&error);
|
||||
}
|
||||
|
||||
/** @brief Contexte possédé pendant la révision OCR d'un RIB. */
|
||||
|
|
@ -5495,6 +5682,7 @@ static void application_on_evidence_selected(
|
|||
investigation_root = project != NULL
|
||||
? investigation_project_get_root_path(project) : NULL;
|
||||
relative_path = evidence_record_get_relative_path(evidence_record);
|
||||
gboolean eml_analysis_available = FALSE;
|
||||
if (investigation_root != NULL && relative_path != NULL)
|
||||
{
|
||||
canonical_root = g_canonicalize_filename(investigation_root, NULL);
|
||||
|
|
@ -5506,11 +5694,20 @@ static void application_on_evidence_selected(
|
|||
canonical_path[strlen(canonical_root)] == G_DIR_SEPARATOR &&
|
||||
g_file_test(canonical_path, G_FILE_TEST_IS_REGULAR))
|
||||
{
|
||||
const char *selected_name =
|
||||
evidence_record_get_original_name(evidence_record);
|
||||
char *lower_name = selected_name != NULL
|
||||
? g_ascii_strdown(selected_name, -1) : NULL;
|
||||
eml_analysis_available = lower_name != NULL &&
|
||||
g_str_has_suffix(lower_name, ".eml");
|
||||
g_free(lower_name);
|
||||
main_window_set_evidence_preview(application->main_window,
|
||||
canonical_path,
|
||||
evidence_record_get_original_name(evidence_record));
|
||||
}
|
||||
}
|
||||
main_window_set_eml_analysis_available(
|
||||
application->main_window, eml_analysis_available);
|
||||
|
||||
original_name =
|
||||
evidence_record_get_original_name(
|
||||
|
|
|
|||
|
|
@ -113,7 +113,8 @@ static gboolean eml_pipeline_task_worker(BackgroundTask *task,
|
|||
g_ptr_array_add(mime_res->warnings, g_strdup("L'extraction MIME a échoué ou ne contient aucune pièce jointe."));
|
||||
}
|
||||
|
||||
background_task_report_progress(task, 0.75, "Analyse OCR et détection bancaire...");
|
||||
background_task_report_progress(task, 0.65,
|
||||
"Analyse des pièces jointes avec ExifTool…");
|
||||
GPtrArray *bank_proposals = g_ptr_array_new_with_free_func((GDestroyNotify) bank_proposal_free);
|
||||
GPtrArray *document_analyses = g_ptr_array_new_with_free_func(
|
||||
(GDestroyNotify) document_file_analysis_free);
|
||||
|
|
@ -162,6 +163,10 @@ static gboolean eml_pipeline_task_worker(BackgroundTask *task,
|
|||
g_strcmp0(att->detected_mime, "application/pdf") == 0 ||
|
||||
g_strcmp0(att->content_type, "application/pdf") == 0)
|
||||
{
|
||||
background_task_report_progress(task, 0.78,
|
||||
g_strcmp0(att->detected_mime, "application/pdf") == 0
|
||||
? "Extraction du texte PDF ou OCR…"
|
||||
: "Reconnaissance OCR de la pièce jointe…");
|
||||
if (document_analyses->len >= data->document_analysis_limit)
|
||||
{
|
||||
skipped_document_analyses++;
|
||||
|
|
@ -211,7 +216,10 @@ static gboolean eml_pipeline_task_worker(BackgroundTask *task,
|
|||
}
|
||||
}
|
||||
|
||||
background_task_report_progress(task, 1.0, "Analyse EML terminée avec succès.");
|
||||
background_task_report_progress(task, 0.92,
|
||||
"Détection des données bancaires terminée.");
|
||||
background_task_report_progress(task, 0.98,
|
||||
"Préparation du dialogue de révision…");
|
||||
|
||||
EmlPipelineResult *res = g_new0(EmlPipelineResult, 1);
|
||||
res->analysis = analysis;
|
||||
|
|
@ -232,6 +240,8 @@ static gboolean eml_pipeline_task_worker(BackgroundTask *task,
|
|||
if (out_result != NULL)
|
||||
*out_result = res;
|
||||
|
||||
background_task_report_progress(task, 1.0,
|
||||
"Analyse EML terminée avec succès.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
@ -300,3 +310,43 @@ BackgroundTask *eml_pipeline_task_new_with_tools_and_limit(
|
|||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
BackgroundTask *eml_pipeline_task_start(
|
||||
const char *eml_path,
|
||||
const char *staging_directory,
|
||||
const char *evidence_id,
|
||||
const DocumentAnalysisTools *tools,
|
||||
BackgroundTaskCompletionCallback completion_callback,
|
||||
gpointer completion_data,
|
||||
GDestroyNotify completion_data_destroy)
|
||||
{
|
||||
if (eml_path == NULL || staging_directory == NULL || tools == NULL)
|
||||
return NULL;
|
||||
EmlPipelineTaskData *data = g_new0(EmlPipelineTaskData, 1);
|
||||
data->eml_path = g_strdup(eml_path);
|
||||
data->processed_evidence_dir = g_strdup(staging_directory);
|
||||
data->evidence_id = g_strdup(evidence_id);
|
||||
data->exiftool = g_strdup(tools->exiftool);
|
||||
data->tesseract = g_strdup(tools->tesseract);
|
||||
data->pdfinfo = g_strdup(tools->pdfinfo);
|
||||
data->pdftotext = g_strdup(tools->pdftotext);
|
||||
data->pdftoppm = g_strdup(tools->pdftoppm);
|
||||
data->document_analysis_limit = DOCUMENT_ANALYSIS_MAX_PIPELINE_ITEMS;
|
||||
BackgroundTask *task = background_task_new(
|
||||
"Analyse complète de l’e-mail");
|
||||
GError *error = NULL;
|
||||
if (task == NULL || !background_task_start(task, eml_pipeline_task_worker,
|
||||
data, eml_pipeline_task_data_free,
|
||||
(GDestroyNotify) eml_pipeline_result_free,
|
||||
completion_callback, completion_data, completion_data_destroy,
|
||||
&error))
|
||||
{
|
||||
if (task != NULL)
|
||||
background_task_unref(task);
|
||||
else
|
||||
eml_pipeline_task_data_free(data);
|
||||
g_clear_error(&error);
|
||||
return NULL;
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,3 +206,233 @@ void eml_analysis_dialog_present(GtkWindow *parent,
|
|||
g_free(received); g_free(emails); g_free(domains);
|
||||
g_free(sender_ips); g_free(destination_ips);
|
||||
}
|
||||
|
||||
static void eml_analysis_dialog_append_attachment_summary(
|
||||
GString *text,
|
||||
const EmlPipelineResult *result)
|
||||
{
|
||||
for (guint index = 0; result->mime_result != NULL &&
|
||||
result->mime_result->attachments != NULL &&
|
||||
index < result->mime_result->attachments->len; index++)
|
||||
{
|
||||
EmlAttachment *attachment = g_ptr_array_index(
|
||||
result->mime_result->attachments, index);
|
||||
g_string_append_printf(text,
|
||||
"%s → %s | MIME %s / %s | partie %s | %"
|
||||
G_GSIZE_FORMAT " octets | SHA-256 %s%s\n",
|
||||
attachment->declared_filename != NULL
|
||||
? attachment->declared_filename : "(sans nom)",
|
||||
attachment->sanitized_filename != NULL
|
||||
? attachment->sanitized_filename : "(sans nom)",
|
||||
attachment->content_type != NULL
|
||||
? attachment->content_type : "inconnu",
|
||||
attachment->detected_mime != NULL
|
||||
? attachment->detected_mime : "inconnu",
|
||||
attachment->part_index != NULL ? attachment->part_index : "?",
|
||||
attachment->decoded_size,
|
||||
attachment->sha256 != NULL ? attachment->sha256 : "indisponible",
|
||||
attachment->is_truncated ? " | TRONQUÉ" : "");
|
||||
}
|
||||
}
|
||||
|
||||
static void eml_analysis_dialog_append_document_summary(
|
||||
GString *text,
|
||||
GString *metadata,
|
||||
GString *warnings,
|
||||
const EmlPipelineResult *result)
|
||||
{
|
||||
for (guint index = 0; result->document_analyses != NULL &&
|
||||
index < result->document_analyses->len; index++)
|
||||
{
|
||||
DocumentFileAnalysis *document = g_ptr_array_index(
|
||||
result->document_analyses, index);
|
||||
if (document->ocr != NULL && document->ocr->text != NULL)
|
||||
g_string_append_printf(text, "\nOCR (%s)%s\n%s\n",
|
||||
document->ocr->requested_languages,
|
||||
document->ocr->execution->stdout_truncated
|
||||
? " — texte tronqué/partiel" : "",
|
||||
document->ocr->text);
|
||||
if (document->pdf != NULL)
|
||||
for (guint page_index = 0;
|
||||
page_index < document->pdf->pages->len; page_index++)
|
||||
{
|
||||
PdfPageAnalysis *page = g_ptr_array_index(
|
||||
document->pdf->pages, page_index);
|
||||
g_string_append_printf(text,
|
||||
"\nPDF page %u — méthode %s — état %s\n%s\n",
|
||||
page->page_number,
|
||||
page->method == PDF_PAGE_METHOD_NATIVE
|
||||
? "texte natif" : "OCR",
|
||||
document_analysis_state_code(page->state),
|
||||
page->text != NULL ? page->text : "(aucun texte)");
|
||||
}
|
||||
if (document->metadata != NULL)
|
||||
{
|
||||
DocumentToolExecution *execution =
|
||||
document->metadata->execution;
|
||||
if (execution != NULL &&
|
||||
execution->state == DOCUMENT_ANALYSIS_STATE_UNAVAILABLE)
|
||||
g_string_append(warnings, "ExifTool indisponible.\n");
|
||||
for (guint metadata_index = 0;
|
||||
metadata_index < document->metadata->metadata->len;
|
||||
metadata_index++)
|
||||
{
|
||||
DocumentMetadataEntry *entry = g_ptr_array_index(
|
||||
document->metadata->metadata, metadata_index);
|
||||
g_string_append_printf(metadata,
|
||||
"%s | %s:%s | %s | %s%s\n",
|
||||
entry->code, entry->original_group,
|
||||
entry->original_tag, entry->raw_value,
|
||||
execution != NULL && execution->version != NULL
|
||||
? execution->version : "version inconnue",
|
||||
entry->sensitive
|
||||
? " | SENSIBLE — confirmation explicite requise" : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void eml_analysis_dialog_present_pipeline(
|
||||
GtkWindow *parent,
|
||||
const EmlPipelineResult *result,
|
||||
const char *evidence_name,
|
||||
const char *relative_path,
|
||||
const char *source_sha256,
|
||||
EmlAnalysisDialogCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (parent == NULL || result == NULL || result->analysis == NULL) return;
|
||||
EmlAnalysisDialogState *state = g_new0(EmlAnalysisDialogState, 1);
|
||||
state->callback = callback; state->user_data = user_data;
|
||||
state->window = GTK_WINDOW(gtk_window_new());
|
||||
gtk_window_set_title(state->window, "Révision de l’analyse EML");
|
||||
gtk_window_set_transient_for(state->window, parent);
|
||||
gtk_window_set_modal(state->window, TRUE);
|
||||
gtk_window_set_default_size(state->window, 900, 700);
|
||||
GtkWidget *outer = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
|
||||
gtk_widget_set_margin_start(outer, 14);
|
||||
gtk_widget_set_margin_end(outer, 14);
|
||||
gtk_widget_set_margin_top(outer, 14);
|
||||
gtk_widget_set_margin_bottom(outer, 14);
|
||||
GtkWidget *content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
|
||||
GtkWidget *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, "Preuve", evidence_name);
|
||||
eml_analysis_dialog_add_field(GTK_GRID(grid), 1, "Chemin relatif", relative_path);
|
||||
eml_analysis_dialog_add_field(GTK_GRID(grid), 2, "SHA-256", source_sha256);
|
||||
eml_analysis_dialog_add_field(GTK_GRID(grid), 3, "État global",
|
||||
document_analysis_state_code(result->state));
|
||||
eml_analysis_dialog_add_field(GTK_GRID(grid), 4, "From",
|
||||
eml_analysis_get_first_header(result->analysis, "from"));
|
||||
eml_analysis_dialog_add_field(GTK_GRID(grid), 5, "Sender",
|
||||
eml_analysis_get_first_header(result->analysis, "sender"));
|
||||
eml_analysis_dialog_add_field(GTK_GRID(grid), 6, "Reply-To",
|
||||
eml_analysis_get_first_header(result->analysis, "reply-to"));
|
||||
eml_analysis_dialog_add_field(GTK_GRID(grid), 7, "Return-Path",
|
||||
eml_analysis_get_first_header(result->analysis, "return-path"));
|
||||
eml_analysis_dialog_add_field(GTK_GRID(grid), 8, "To",
|
||||
eml_analysis_get_first_header(result->analysis, "to"));
|
||||
eml_analysis_dialog_add_field(GTK_GRID(grid), 9, "Cc",
|
||||
eml_analysis_get_first_header(result->analysis, "cc"));
|
||||
eml_analysis_dialog_add_field(GTK_GRID(grid), 10, "Subject",
|
||||
eml_analysis_get_first_header(result->analysis, "subject"));
|
||||
eml_analysis_dialog_add_field(GTK_GRID(grid), 11, "Date brute",
|
||||
eml_analysis_get_first_header(result->analysis, "date"));
|
||||
eml_analysis_dialog_add_field(GTK_GRID(grid), 12, "Message-ID",
|
||||
eml_analysis_get_first_header(result->analysis, "message-id"));
|
||||
char *received = eml_analysis_dialog_join(
|
||||
eml_analysis_get_header_values(result->analysis, "received"));
|
||||
eml_analysis_dialog_add_field(GTK_GRID(grid), 13,
|
||||
"Received (ordre original)", received);
|
||||
gtk_box_append(GTK_BOX(content), grid);
|
||||
|
||||
GString *attachments = g_string_new(NULL);
|
||||
GString *texts = g_string_new(NULL);
|
||||
GString *metadata = g_string_new(NULL);
|
||||
GString *warnings = g_string_new(NULL);
|
||||
eml_analysis_dialog_append_attachment_summary(attachments, result);
|
||||
eml_analysis_dialog_append_document_summary(
|
||||
texts, metadata, warnings, result);
|
||||
for (guint index = 0; result->warnings != NULL &&
|
||||
index < result->warnings->len; index++)
|
||||
g_string_append_printf(warnings, "%s\n",
|
||||
(char *) g_ptr_array_index(result->warnings, index));
|
||||
for (guint index = 0; result->bank_proposals != NULL &&
|
||||
index < result->bank_proposals->len; index++)
|
||||
{
|
||||
BankProposal *proposal = g_ptr_array_index(
|
||||
result->bank_proposals, index);
|
||||
if (!proposal->is_iban_valid ||
|
||||
proposal->suggested_ocr_fix != NULL)
|
||||
g_string_append_printf(warnings,
|
||||
"Proposition bancaire invalide ou corrigée, "
|
||||
"désélectionnée : %s\n",
|
||||
proposal->raw_iban != NULL ? proposal->raw_iban : "(vide)");
|
||||
}
|
||||
GtkWidget *details = gtk_grid_new();
|
||||
gtk_grid_set_row_spacing(GTK_GRID(details), 6);
|
||||
gtk_grid_set_column_spacing(GTK_GRID(details), 12);
|
||||
eml_analysis_dialog_add_field(GTK_GRID(details), 0,
|
||||
"Pièces jointes", attachments->str);
|
||||
eml_analysis_dialog_add_field(GTK_GRID(details), 1,
|
||||
"Texte PDF et OCR", texts->str);
|
||||
eml_analysis_dialog_add_field(GTK_GRID(details), 2,
|
||||
"Métadonnées", metadata->str);
|
||||
eml_analysis_dialog_add_field(GTK_GRID(details), 3,
|
||||
"Avertissements", warnings->str);
|
||||
gtk_box_append(GTK_BOX(content), details);
|
||||
|
||||
state->proposals_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
|
||||
eml_analysis_dialog_add_proposals(state->proposals_box, "email_address",
|
||||
"E-mail", eml_analysis_get_email_addresses(result->analysis));
|
||||
eml_analysis_dialog_add_proposals(state->proposals_box, "domain_name",
|
||||
"Domaine", eml_analysis_get_domains(result->analysis));
|
||||
eml_analysis_dialog_add_proposals(state->proposals_box, "ip_address",
|
||||
"IP expéditeur", eml_analysis_get_sender_ip_addresses(result->analysis));
|
||||
GPtrArray *valid_ibans = g_ptr_array_new();
|
||||
for (guint index = 0; result->bank_proposals != NULL &&
|
||||
index < result->bank_proposals->len; index++)
|
||||
{
|
||||
BankProposal *proposal = g_ptr_array_index(
|
||||
result->bank_proposals, index);
|
||||
if (proposal->is_iban_valid && proposal->normalized_iban != NULL &&
|
||||
proposal->suggested_ocr_fix == NULL)
|
||||
g_ptr_array_add(valid_ibans, proposal->normalized_iban);
|
||||
}
|
||||
eml_analysis_dialog_add_proposals(state->proposals_box, "iban",
|
||||
"IBAN valide", valid_ibans);
|
||||
g_ptr_array_unref(valid_ibans);
|
||||
gtk_box_append(GTK_BOX(content), gtk_label_new(
|
||||
"Propositions à intégrer explicitement"));
|
||||
gtk_box_append(GTK_BOX(content), state->proposals_box);
|
||||
|
||||
GtkWidget *scroll = gtk_scrolled_window_new();
|
||||
gtk_widget_set_vexpand(scroll, TRUE);
|
||||
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), content);
|
||||
gtk_box_append(GTK_BOX(outer), scroll);
|
||||
GtkWidget *actions = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
|
||||
gtk_widget_set_halign(actions, GTK_ALIGN_END);
|
||||
GtkWidget *close = gtk_button_new_with_label("Rejeter et fermer");
|
||||
GtkWidget *integrate = gtk_button_new_with_label(
|
||||
"Intégrer les éléments sélectionnés");
|
||||
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(outer), actions);
|
||||
gtk_window_set_child(state->window, outer);
|
||||
g_signal_connect(state->window, "close-request",
|
||||
G_CALLBACK(eml_analysis_dialog_on_window_close), state);
|
||||
g_object_set_data_full(G_OBJECT(state->window), "eml-dialog-state",
|
||||
state, eml_analysis_dialog_state_free);
|
||||
gtk_window_present(state->window);
|
||||
g_free(received);
|
||||
g_string_free(attachments, TRUE);
|
||||
g_string_free(texts, TRUE);
|
||||
g_string_free(metadata, TRUE);
|
||||
g_string_free(warnings, TRUE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1995,6 +1995,15 @@ void main_window_set_selected_evidence(
|
|||
);
|
||||
}
|
||||
|
||||
void main_window_set_eml_analysis_available(
|
||||
MainWindow *main_window,
|
||||
gboolean available)
|
||||
{
|
||||
if (main_window != NULL)
|
||||
workspace_set_eml_analysis_available(
|
||||
main_window->workspace, available);
|
||||
}
|
||||
|
||||
void main_window_set_evidence_preview(MainWindow *main_window,
|
||||
const char *file_path, const char *display_name)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1598,11 +1598,11 @@ Workspace *workspace_new(void)
|
|||
G_CALLBACK(workspace_on_edit_evidence_clicked), workspace);
|
||||
gtk_box_append(GTK_BOX(evidence_content), workspace->edit_evidence_button);
|
||||
workspace->analyze_eml_button = gtk_button_new_with_label(
|
||||
"Analyser les en-têtes EML");
|
||||
"Analyser l’e-mail");
|
||||
gtk_widget_set_halign(workspace->analyze_eml_button, GTK_ALIGN_START);
|
||||
gtk_widget_set_sensitive(workspace->analyze_eml_button, FALSE);
|
||||
gtk_widget_set_tooltip_text(workspace->analyze_eml_button,
|
||||
"Créer une copie vérifiée puis analyser localement ses en-têtes");
|
||||
"Vérifier la preuve puis lancer l’analyse complète en arrière-plan");
|
||||
g_signal_connect(workspace->analyze_eml_button, "clicked",
|
||||
G_CALLBACK(workspace_on_analyze_eml_clicked), workspace);
|
||||
gtk_box_append(GTK_BOX(evidence_content), workspace->analyze_eml_button);
|
||||
|
|
@ -2950,6 +2950,15 @@ void workspace_set_selected_evidence(
|
|||
);
|
||||
}
|
||||
|
||||
void workspace_set_eml_analysis_available(
|
||||
Workspace *workspace,
|
||||
gboolean available)
|
||||
{
|
||||
if (workspace != NULL && workspace->analyze_eml_button != NULL)
|
||||
gtk_widget_set_sensitive(
|
||||
workspace->analyze_eml_button, available);
|
||||
}
|
||||
|
||||
void workspace_set_evidence_preview(Workspace *workspace,
|
||||
const char *file_path, const char *display_name)
|
||||
{
|
||||
|
|
|
|||
38
tests/fixtures/eml/manual_smoke_test.eml
vendored
Normal file
38
tests/fixtures/eml/manual_smoke_test.eml
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
From: Expediteur Demo <sender@example.com>
|
||||
Sender: Service Demo <service@example.net>
|
||||
Reply-To: Reponse Differente <reply@example.org>
|
||||
Return-Path: <bounce@example.com>
|
||||
To: Destinataire Demo <recipient@example.net>
|
||||
Cc: Controle Demo <audit@example.org>
|
||||
Subject: Démonstration synthétique du parcours EML
|
||||
Date: Tue, 28 Jul 2026 10:30:00 +0200
|
||||
Message-ID: <manual-smoke-test-107@example.com>
|
||||
Received: from relay2.example.net (relay2.example.net [198.51.100.20]) by mx.example.org with ESMTP id DEMO2
|
||||
Received: from relay1.example.com (relay1.example.com [192.0.2.10]) by relay2.example.net with ESMTP id DEMO1
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/mixed; boundary="LABFY-DEMO-107"
|
||||
|
||||
--LABFY-DEMO-107
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Ceci est un message entièrement synthétique destiné au test manuel.
|
||||
IBAN DE DÉMONSTRATION VOLONTAIREMENT INVALIDE : FR00 0000 0000 0000 0000 0000 000
|
||||
BIC SYNTHÉTIQUE DE FORMAT CONTRÔLABLE : DEMOFRPP
|
||||
|
||||
--LABFY-DEMO-107
|
||||
Content-Type: text/plain; name="demonstration.txt"
|
||||
Content-Disposition: attachment; filename="demonstration.txt"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
RmljaGllciBzeW50aMOpdGlxdWUgZGUgZMOpbW9uc3RyYXRpb24uIElCQU4gREVNTyBJTlZBTElERTogRlIwMCAwMDAwIDAwMDAgMDAwMCAwMDAwIDAwMDAgMDAwLiBCSUMgREVNT0ZSUFAu
|
||||
|
||||
--LABFY-DEMO-107
|
||||
Content-Type: image/png; name="pixel_synthetique.png"
|
||||
Content-Disposition: inline; filename="pixel_synthetique.png"
|
||||
Content-ID: <pixel-demo@example.com>
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=
|
||||
|
||||
--LABFY-DEMO-107--
|
||||
|
|
@ -145,6 +145,35 @@ static void wait_for_task(BackgroundTask *task)
|
|||
g_main_context_iteration(NULL, TRUE);
|
||||
}
|
||||
|
||||
static void remove_tree(const char *path)
|
||||
{
|
||||
GFile *directory = g_file_new_for_path(path);
|
||||
GFileEnumerator *enumerator = g_file_enumerate_children(directory,
|
||||
G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL);
|
||||
if (enumerator != NULL)
|
||||
{
|
||||
GFileInfo *info = NULL;
|
||||
while ((info = g_file_enumerator_next_file(
|
||||
enumerator, NULL, NULL)) != NULL)
|
||||
{
|
||||
GFile *child = g_file_get_child(directory,
|
||||
g_file_info_get_name(info));
|
||||
char *child_path = g_file_get_path(child);
|
||||
if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY)
|
||||
remove_tree(child_path);
|
||||
else
|
||||
g_assert_true(g_file_delete(child, NULL, NULL));
|
||||
g_free(child_path);
|
||||
g_object_unref(child);
|
||||
g_object_unref(info);
|
||||
}
|
||||
g_object_unref(enumerator);
|
||||
}
|
||||
g_assert_true(g_file_delete(directory, NULL, NULL));
|
||||
g_object_unref(directory);
|
||||
}
|
||||
|
||||
static DocumentAnalysisTools synthetic_tools(void)
|
||||
{
|
||||
DocumentAnalysisTools tools = {
|
||||
|
|
@ -157,6 +186,67 @@ static DocumentAnalysisTools synthetic_tools(void)
|
|||
return tools;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gboolean called;
|
||||
BackgroundTaskState state;
|
||||
} PipelineCompletion;
|
||||
|
||||
static void pipeline_completed(BackgroundTask *task, gpointer user_data)
|
||||
{
|
||||
PipelineCompletion *completion = user_data;
|
||||
completion->called = TRUE;
|
||||
completion->state = background_task_get_state(task);
|
||||
}
|
||||
|
||||
static void test_manual_fixture_async_start(void)
|
||||
{
|
||||
GError *error = NULL;
|
||||
char *source_before = NULL;
|
||||
gsize source_length = 0;
|
||||
g_assert_true(g_file_get_contents(
|
||||
"tests/fixtures/eml/manual_smoke_test.eml",
|
||||
&source_before, &source_length, &error));
|
||||
g_assert_no_error(error);
|
||||
char *staging = g_dir_make_tmp("labfy-eml-manual-XXXXXX", &error);
|
||||
g_assert_no_error(error);
|
||||
DocumentAnalysisTools tools = synthetic_tools();
|
||||
PipelineCompletion completion = { 0 };
|
||||
BackgroundTask *task = eml_pipeline_task_start(
|
||||
"tests/fixtures/eml/manual_smoke_test.eml", staging,
|
||||
"synthetic-evidence", &tools, pipeline_completed,
|
||||
&completion, NULL);
|
||||
g_assert_nonnull(task);
|
||||
wait_for_task(task);
|
||||
while (!completion.called)
|
||||
g_main_context_iteration(NULL, TRUE);
|
||||
g_assert_cmpint(completion.state, ==,
|
||||
BACKGROUND_TASK_STATE_COMPLETED);
|
||||
EmlPipelineResult *result = background_task_get_result(task);
|
||||
g_assert_nonnull(result);
|
||||
g_assert_cmpuint(result->mime_result->attachments->len, ==, 2);
|
||||
g_assert_cmpuint(result->bank_proposals->len, ==, 1);
|
||||
BankProposal *bank = g_ptr_array_index(result->bank_proposals, 0);
|
||||
g_assert_false(bank->is_iban_valid);
|
||||
g_assert_cmpstr(bank->normalized_iban, ==,
|
||||
"FR0000000000000000000000000");
|
||||
g_assert_cmpuint(
|
||||
eml_analysis_get_header_values(result->analysis, "received")->len,
|
||||
==, 2);
|
||||
char *source_after = NULL;
|
||||
gsize after_length = 0;
|
||||
g_assert_true(g_file_get_contents(
|
||||
"tests/fixtures/eml/manual_smoke_test.eml",
|
||||
&source_after, &after_length, &error));
|
||||
g_assert_no_error(error);
|
||||
g_assert_cmpmem(source_after, after_length, source_before, source_length);
|
||||
g_free(source_after);
|
||||
g_free(source_before);
|
||||
background_task_unref(task);
|
||||
remove_tree(staging);
|
||||
g_free(staging);
|
||||
}
|
||||
|
||||
static void test_eml_pipeline_pdf_end_to_end_and_limit(void)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
|
@ -238,5 +328,7 @@ int main(int argc, char **argv)
|
|||
test_eml_pipeline_document_analysis);
|
||||
g_test_add_func("/eml-pipeline-task/pdf-end-to-end-limit",
|
||||
test_eml_pipeline_pdf_end_to_end_and_limit);
|
||||
g_test_add_func("/eml-pipeline-task/manual-fixture-async",
|
||||
test_manual_fixture_async_start);
|
||||
return g_test_run();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue