From 94cb8acac1027a4deee933c84d7918f4a5ea4983 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Mon, 28 Jan 2019 01:24:40 +0100
Subject: Allowed to filter contents before running analysis.

---
 configure.ac                            |  2 +-
 plugins/pychrysalide/analysis/project.c | 80 ++++++++++++++++++++++++++++++---
 plugins/pychrysalide/helpers.c          | 45 +++++++++++++++++++
 plugins/pychrysalide/helpers.h          |  3 ++
 src/analysis/project.c                  | 69 ++++++++++++++++++++++------
 src/analysis/project.h                  |  8 +++-
 src/gui/editor.c                        | 49 +++++++++++++++++++-
 src/gui/menus/project.c                 |  2 +-
 src/main.c                              |  2 +-
 9 files changed, 236 insertions(+), 24 deletions(-)

diff --git a/configure.ac b/configure.ac
index 9effa17..c8e6cf1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -332,7 +332,7 @@ AC_SUBST(LIBPYGOBJECT_LIBS)
 
 AC_CONFIG_FILES([stamp-h po/Makefile.in], [echo timestamp > stamp-h])
 
-AC_CONFIG_COMMANDS([marshal], [echo -e "VOID:UINT64\nVOID:INT,UINT64,INT\nVOID:OBJECT,OBJECT\nVOID:ENUM,OBJECT\nVOID:ENUM,ENUM\nVOID:BOOLEAN,UINT64\nVOID:BOOLEAN,ULONG,ULONG\nVOID:INT,INT\nVOID:OBJECT,BOOLEAN\nVOID:ULONG,BOOLEAN" > src/glibext/chrysamarshal.list])
+AC_CONFIG_COMMANDS([marshal], [echo -e "VOID:UINT64\nVOID:INT,UINT64,INT\nVOID:OBJECT,OBJECT\nVOID:ENUM,OBJECT\nVOID:ENUM,ENUM\nVOID:BOOLEAN,UINT64\nVOID:BOOLEAN,ULONG,ULONG\nVOID:INT,INT\nVOID:OBJECT,BOOLEAN\nVOID:ULONG,BOOLEAN\nVOID:POINTER,UINT" > src/glibext/chrysamarshal.list])
 
 AC_CONFIG_FILES([Makefile
                  doc/Makefile
diff --git a/plugins/pychrysalide/analysis/project.c b/plugins/pychrysalide/analysis/project.c
index 1a85f71..137efbf 100644
--- a/plugins/pychrysalide/analysis/project.c
+++ b/plugins/pychrysalide/analysis/project.c
@@ -45,6 +45,9 @@ static PyObject *py_study_project_new(PyTypeObject *, PyObject *, PyObject *);
 /* Procède à l'enregistrement d'un projet donné. */
 static PyObject *py_study_project_save(PyObject *, PyObject *);
 
+/* Détermine si un contenu doit être écarté ou conservé. */
+static bool filter_loadable_content_with_python(GLoadedContent *, PyObject *);
+
 /* Assure l'intégration de contenus binaires dans un projet. */
 static PyObject *py_study_project_discover_binary_content(PyObject *, PyObject *);
 
@@ -135,6 +138,57 @@ static PyObject *py_study_project_save(PyObject *self, PyObject *args)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : content  = contenu chargeable à étudier ou NULL à la fin.    *
+*                callable = procédure de filtre en Python.                    *
+*                                                                             *
+*  Description : Détermine si un contenu doit être écarté ou conservé.        *
+*                                                                             *
+*  Retour      : true si le contenu doit être conservé, false sinon.          *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool filter_loadable_content_with_python(GLoadedContent *content, PyObject *callable)
+{
+    bool result;                            /* Bilan à retourner           */
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
+    PyObject *arg;                          /* Argument à fournir au filtre*/
+    PyObject *status;                       /* Bilan de l'analyse          */
+
+    gstate = PyGILState_Ensure();
+
+    if (content == NULL)
+    {
+        Py_DECREF(callable);
+        result = false;
+    }
+
+    else
+    {
+        arg = pygobject_new(G_OBJECT(content));
+
+        status = PyObject_CallFunctionObjArgs(callable, arg, NULL);
+
+        if (PyErr_Occurred())
+            PyErr_Print();
+
+        result = status == NULL || status == Py_False || status == Py_None ? false : true;
+        Py_XDECREF(status);
+
+        Py_DECREF(arg);
+
+    }
+
+    PyGILState_Release(gstate);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : self = projet d'étude à manipuler.                           *
 *                args = arguments accompagnant l'appel.                       *
 *                                                                             *
@@ -148,16 +202,32 @@ static PyObject *py_study_project_save(PyObject *self, PyObject *args)
 
 static PyObject *py_study_project_discover_binary_content(PyObject *self, PyObject *args)
 {
-    GStudyProject *project;                 /* Version GLib du format      */
-    int ret;                                /* Bilan de lecture des args.  */
+    PyObject *callable;                     /* Filtre de contenus éventuel */
     GBinContent *content;                   /* Instance de contenu binaire */
+    int ret;                                /* Bilan de lecture des args.  */
+    GStudyProject *project;                 /* Version GLib du format      */
 
-    project = G_STUDY_PROJECT(pygobject_get(self));
+    callable = NULL;
 
-    ret = PyArg_ParseTuple(args, "O&", convert_to_binary_content, &content);
+    ret = PyArg_ParseTuple(args, "O&|O&",
+                           convert_to_binary_content, &content,
+                           convert_to_callable, &callable);
     if (!ret) return NULL;
 
-    g_study_project_discover_binary_content(project, content);
+    project = G_STUDY_PROJECT(pygobject_get(self));
+
+    if (callable != NULL)
+    {
+        Py_INCREF(callable);
+
+        g_study_project_discover_binary_content(project, content,
+                                                (filter_loadable_cb)filter_loadable_content_with_python,
+                                                callable);
+
+    }
+
+    else
+        g_study_project_discover_binary_content(project, content, NULL, NULL);
 
     Py_RETURN_NONE;
 
diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c
index 7e31fa7..2b62e57 100644
--- a/plugins/pychrysalide/helpers.c
+++ b/plugins/pychrysalide/helpers.c
@@ -109,6 +109,51 @@ PyObject *status_to_rich_cmp_state(int status, int op)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : arg = argument quelconque à tenter de convertir.             *
+*                dst = destination des valeurs récupérées en cas de succès.   *
+*                                                                             *
+*  Description : Tente de convertir en élément appelable.                     *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_callable(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+
+    result = PyCallable_Check(arg);
+
+    switch (result)
+    {
+        case -1:
+            /* L'exception est déjà fixée par Python */
+            result = 0;
+            break;
+
+        case 0:
+            PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to a callable object");
+            break;
+
+        case 1:
+            *((PyObject **)dst) = arg;
+            break;
+
+        default:
+            assert(false);
+            break;
+
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : target = propriétaire de la routine visée.                   *
 *                method = désignation de la fonction à appeler.               *
 *                                                                             *
diff --git a/plugins/pychrysalide/helpers.h b/plugins/pychrysalide/helpers.h
index ce5840b..f7ebdc3 100644
--- a/plugins/pychrysalide/helpers.h
+++ b/plugins/pychrysalide/helpers.h
@@ -38,6 +38,9 @@
 /* Traduit pour Python le bilan d'une comparaison riche. */
 PyObject *status_to_rich_cmp_state(int, int);
 
+/* Tente de convertir en élément appelable. */
+int convert_to_callable(PyObject *, void *);
+
 /* Indique si une routine Python existe ou non. */
 bool has_python_method(PyObject *, const char *);
 
diff --git a/src/analysis/project.c b/src/analysis/project.c
index e01a126..8474610 100644
--- a/src/analysis/project.c
+++ b/src/analysis/project.c
@@ -38,6 +38,7 @@
 #include "../core/logs.h"
 #include "../core/params.h"
 #include "../core/queue.h"
+#include "../glibext/chrysamarshal.h"
 #include "../glibext/delayed-int.h"
 
 
@@ -66,6 +67,7 @@ struct _GStudyProjectClass
 
     /* Signaux */
 
+    void (* contents_available) (GStudyProject, GLoadedContent **, guint);
     void (* content_added) (GStudyProject *, GLoadedContent *);
     void (* content_removed) (GStudyProject *, GLoadedContent *);
 
@@ -92,9 +94,6 @@ static void g_study_project_finalize(GStudyProject *);
 /* Assure l'intégration de contenus listés dans du XML. */
 static void g_study_project_recover_binary_contents(GStudyProject *, xmlDoc *, xmlXPathContext *);
 
-/* Réceptionne la recette d'une analyse de contenu. */
-static void on_loaded_content_analyzed(GLoadedContent *, gboolean, GStudyProject *);
-
 
 
 /* ------------------------ CHARGEMENTS DE CONTENUS BINAIRES ------------------------ */
@@ -124,6 +123,9 @@ typedef struct _GLoadingHandler
     GCond wait_cond;                        /* Réveil d'attente de fin     */
     GMutex mutex;                           /* Encadrement des accès       */
 
+    filter_loadable_cb filter;              /* Filtre des contenus ?       */
+    void *data;                             /* Données utiles au filtrage  */
+
 } GLoadingHandler;
 
 /* Chargement de contenus binaires (classe) */
@@ -150,7 +152,7 @@ static void g_loading_handler_dispose(GLoadingHandler *);
 static void g_loading_handler_finalize(GLoadingHandler *);
 
 /* Crée une tâche de chargement de contenu bianire. */
-static GLoadingHandler *g_loading_handler_new_discovering(GStudyProject *, GBinContent *);
+static GLoadingHandler *g_loading_handler_new_discovering(GStudyProject *, GBinContent *, filter_loadable_cb, void *);
 
 /* Crée une tâche de chargement de contenu bianire. */
 static GLoadingHandler *g_loading_handler_new_recovering(GStudyProject *, xmlDoc *, xmlXPathContext *);
@@ -199,6 +201,14 @@ static void g_study_project_class_init(GStudyProjectClass *klass)
     object->dispose = (GObjectFinalizeFunc/* ! */)g_study_project_dispose;
     object->finalize = (GObjectFinalizeFunc)g_study_project_finalize;
 
+    g_signal_new("contents-available",
+                 G_TYPE_STUDY_PROJECT,
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET(GStudyProjectClass, contents_available),
+                 NULL, NULL,
+                 g_cclosure_user_marshal_VOID__POINTER_UINT,
+                 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT);
+
     g_signal_new("content-added",
                  G_TYPE_STUDY_PROJECT,
                  G_SIGNAL_RUN_LAST,
@@ -545,6 +555,8 @@ static void g_study_project_recover_binary_contents(GStudyProject *project, xmlD
 *                                                                             *
 *  Paramètres  : project = projet dont le contenu est à compléter.            *
 *                content = contenu binaire à mémoriser pour le projet.        *
+*                filter  = procédure de filtrage de contenus chargés.         *
+*                data    = données utiles à la procédure de filtre.           *
 *                                                                             *
 *  Description : Assure l'intégration de contenus binaires dans un projet.    *
 *                                                                             *
@@ -554,11 +566,11 @@ static void g_study_project_recover_binary_contents(GStudyProject *project, xmlD
 *                                                                             *
 ******************************************************************************/
 
-void g_study_project_discover_binary_content(GStudyProject *project, GBinContent *content)
+void g_study_project_discover_binary_content(GStudyProject *project, GBinContent *content, filter_loadable_cb filter, void *data)
 {
     GLoadingHandler *handler;               /* Encadrement du chargement   */
 
-    handler = g_loading_handler_new_discovering(project, content);
+    handler = g_loading_handler_new_discovering(project, content, filter, data);
 
     g_work_queue_schedule_work(get_work_queue(), G_DELAYED_WORK(handler), LOADING_WORK_GROUP);
 
@@ -579,7 +591,7 @@ void g_study_project_discover_binary_content(GStudyProject *project, GBinContent
 *                                                                             *
 ******************************************************************************/
 
-static void on_loaded_content_analyzed(GLoadedContent *content, gboolean success, GStudyProject *project)
+void on_loaded_content_analyzed(GLoadedContent *content, gboolean success, GStudyProject *project)
 {
     const char *desc;                       /* Description du contenu      */
 
@@ -908,6 +920,8 @@ static void g_loading_handler_finalize(GLoadingHandler *handler)
 *                                                                             *
 *  Paramètres  : project = projet dont le contenu est à compléter.            *
 *                content = contenu binaire à mémoriser pour le projet.        *
+*                filter  = procédure de filtrage de contenus chargés.         *
+*                data    = données utiles à la procédure de filtre.           *
 *                                                                             *
 *  Description : Crée une tâche de chargement de contenu bianire.             *
 *                                                                             *
@@ -917,7 +931,7 @@ static void g_loading_handler_finalize(GLoadingHandler *handler)
 *                                                                             *
 ******************************************************************************/
 
-static GLoadingHandler *g_loading_handler_new_discovering(GStudyProject *project, GBinContent *content)
+static GLoadingHandler *g_loading_handler_new_discovering(GStudyProject *project, GBinContent *content, filter_loadable_cb filter, void *data)
 {
     GLoadingHandler *result;                /* Tâche à retourner           */
     GContentExplorer *explorer;             /* Explorateur de contenus     */
@@ -935,6 +949,9 @@ static GLoadingHandler *g_loading_handler_new_discovering(GStudyProject *project
 
     result->resolved = 0;
 
+    result->filter = filter;
+    result->data = data;
+
     explorer = get_current_content_explorer();
 
     g_mutex_lock(&result->mutex);
@@ -993,6 +1010,9 @@ static GLoadingHandler *g_loading_handler_new_recovering(GStudyProject *project,
 
         result->resolved = 0;
 
+        result->filter = NULL;
+        result->data = NULL;
+
         explorer = get_current_content_explorer();
 
         explored = 0;
@@ -1211,6 +1231,7 @@ static void on_new_content_resolved(GContentResolver *resolver, wgroup_id_t wid,
 {
     GLoadedContent **available;             /* Contenus chargés valables   */
     size_t count;                           /* Quantité de ces contenus    */
+    bool keep;                              /* Conservation finale de liste*/
     size_t i;                               /* Boucle de parcours          */
     GBinContent *content;                   /* Contenu brut à manipuler    */
     const gchar *hash;                      /* Empreinte d'un contenu      */
@@ -1223,6 +1244,8 @@ static void on_new_content_resolved(GContentResolver *resolver, wgroup_id_t wid,
     {
         available = g_content_resolver_get_all(resolver, wid, &count);
 
+        keep = false;
+
         /* Rechargement à partir d'XML ? */
         if (handler->xdoc != NULL)
         {
@@ -1295,19 +1318,39 @@ static void on_new_content_resolved(GContentResolver *resolver, wgroup_id_t wid,
                  */
                 g_object_ref(G_OBJECT(available[i]));
 
-                g_signal_connect(available[i], "analyzed",
-                                 G_CALLBACK(on_loaded_content_analyzed), handler->project);
+            }
 
-                g_loaded_content_analyze(available[i]);
+            if (is_batch_mode())
+            {
+                for (i = 0; i < count; i++)
+                {
+                    if (handler->filter == NULL || handler->filter(available[i], handler->data))
+                    {
+                        g_signal_connect(available[i], "analyzed",
+                                         G_CALLBACK(on_loaded_content_analyzed), handler->project);
 
-                g_object_unref(G_OBJECT(available[i]));
+                        g_loaded_content_analyze(available[i]);
+
+                    }
+
+                    g_object_unref(G_OBJECT(available[i]));
+
+                }
+
+            }
+
+            else
+            {
+                g_signal_emit_by_name(handler->project, "contents-available", available, count);
+
+                keep = true;
 
             }
 
         }
 
         /* Dans tous les cas... */
-        if (available != NULL)
+        if (available != NULL && !keep)
             free(available);
 
         /* Si c'était la dernière résolution... */
diff --git a/src/analysis/project.h b/src/analysis/project.h
index c634fb2..9b42a47 100644
--- a/src/analysis/project.h
+++ b/src/analysis/project.h
@@ -74,8 +74,14 @@ const char *g_study_project_get_filename(const GStudyProject *);
 /* ------------------------ INTEGRATION DE CONTENUS BINAIRES ------------------------ */
 
 
+/* Filtre sur les contenus chargeables */
+typedef bool (* filter_loadable_cb) (GLoadedContent *, void *);
+
 /* Assure l'intégration de contenus binaires dans un projet. */
-void g_study_project_discover_binary_content(GStudyProject *, GBinContent *);
+void g_study_project_discover_binary_content(GStudyProject *, GBinContent *, filter_loadable_cb, void *);
+
+/* Réceptionne la recette d'une analyse de contenu. */
+void on_loaded_content_analyzed(GLoadedContent *, gboolean, GStudyProject *);
 
 #define g_study_project_lock_contents(p) \
     _g_study_project_lock_unlock_contents(p, true)
diff --git a/src/gui/editor.c b/src/gui/editor.c
index 16b7a59..b52ec9d 100644
--- a/src/gui/editor.c
+++ b/src/gui/editor.c
@@ -46,6 +46,7 @@
 #include "../common/extstr.h"
 #include "../core/global.h"
 #include "../core/params.h"
+#include "../glibext/chrysamarshal.h"
 #include "../glibext/signal.h"
 #include "../gtkext/easygtk.h"
 #include "../gtkext/gtkdisplaypanel.h"
@@ -129,8 +130,11 @@ static void notify_editor_project_change(GStudyProject *, bool);
 /* Assure un positionnement initial idéal. */
 static gboolean scroll_for_the_first_time(GtkWidget *, GdkEvent *, GLoadedContent *);
 
+/* Présente une possibilité de sélection des contenus chargés. */
+static void on_editor_contents_available(GStudyProject *, GLoadedContent **, guint, void *);
+
 /* Affiche le contenu qui vient de rejoindre un projet donné. */
-void on_editor_loaded_content_added(GStudyProject *, GLoadedContent *, void *);
+static void on_editor_loaded_content_added(GStudyProject *, GLoadedContent *, void *);
 
 /* Recherche et retirer de l'affichage un contenu chargé. */
 static void remove_loaded_content_from_editor(GtkWidget *, GLoadedContent *);
@@ -905,9 +909,15 @@ static void notify_editor_project_change(GStudyProject *project, bool new)
     contents = _g_study_project_get_contents(project, &count);
 
     if (new)
+    {
+        g_signal_connect_to_main(project, "contents-available", G_CALLBACK(on_editor_contents_available), NULL,
+                                 g_cclosure_user_marshal_VOID__POINTER_UINT);
+
         g_signal_connect_to_main(project, "content-added", G_CALLBACK(on_editor_loaded_content_added), NULL,
                                  g_cclosure_marshal_VOID__OBJECT);
 
+    }
+
     g_study_project_unlock_contents(project);
 
     if (new)
@@ -934,6 +944,41 @@ static void notify_editor_project_change(GStudyProject *project, bool new)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : project  = project impliqué dans l'opération.                *
+*                contents = nouveaux contenus à éventuellement charger.       *
+*                count    = taille de la liste fournie.                       *
+*                unused   = adresse non utilisée ici.                         *
+*                                                                             *
+*  Description : Présente une possibilité de sélection des contenus chargés.  *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_editor_contents_available(GStudyProject *project, GLoadedContent **contents, guint count, void *unused)
+{
+    guint i;                                /* Boucle de parcours          */
+
+    for (i = 0; i < count; i++)
+    {
+        g_signal_connect(contents[i], "analyzed", G_CALLBACK(on_loaded_content_analyzed), project);
+
+        g_loaded_content_analyze(contents[i]);
+
+        g_object_unref(G_OBJECT(contents[i]));
+
+    }
+
+    if (contents != NULL)
+        free(contents);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : project = project impliqué dans l'opération.                 *
 *                content = nouveau contenu à présenter dans l'éditeur.        *
 *                unused  = adresse non utilisée ici.                          *
@@ -946,7 +991,7 @@ static void notify_editor_project_change(GStudyProject *project, bool new)
 *                                                                             *
 ******************************************************************************/
 
-void on_editor_loaded_content_added(GStudyProject *project, GLoadedContent *content, void *unused)
+static void on_editor_loaded_content_added(GStudyProject *project, GLoadedContent *content, void *unused)
 {
     GtkWidget *selected;                    /* Interface de prédilection   */
     const char *name;                       /* Titre associé au binaire    */
diff --git a/src/gui/menus/project.c b/src/gui/menus/project.c
index a6a078b..80eff6f 100644
--- a/src/gui/menus/project.c
+++ b/src/gui/menus/project.c
@@ -205,7 +205,7 @@ static void mcb_project_add_binary_file(GtkMenuItem *menuitem, GMenuBar *bar)
 
         if (content != NULL)
         {
-            g_study_project_discover_binary_content(project, content);
+            g_study_project_discover_binary_content(project, content, NULL, NULL);
             g_object_unref(G_OBJECT(content));
         }
 
diff --git a/src/main.c b/src/main.c
index f00a61c..92df3fa 100644
--- a/src/main.c
+++ b/src/main.c
@@ -479,7 +479,7 @@ static int open_binaries(char **files, int count)
 
         if (content != NULL)
         {
-            g_study_project_discover_binary_content(project, content);
+            g_study_project_discover_binary_content(project, content, NULL, NULL);
             g_object_unref(G_OBJECT(content));
         }
 
-- 
cgit v0.11.2-87-g4458