From fbde6e0ebfc5b0203eda1c5fe3c6ba72d1427896 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Tue, 22 Feb 2022 13:57:05 +0100
Subject: Introduce a generic way to subclass Python analysis clients.

---
 plugins/pychrysalide/analysis/db/analyst.c | 48 +++++++++------
 plugins/pychrysalide/helpers.c             | 58 +++++++++++++++++
 plugins/pychrysalide/helpers.h             | 16 +++++
 src/analysis/db/analyst-int.h              | 76 +++++++++++++++++++++++
 src/analysis/db/analyst.c                  | 99 ++++++++++++++----------------
 src/analysis/db/analyst.h                  |  3 +-
 6 files changed, 225 insertions(+), 75 deletions(-)
 create mode 100644 src/analysis/db/analyst-int.h

diff --git a/plugins/pychrysalide/analysis/db/analyst.c b/plugins/pychrysalide/analysis/db/analyst.c
index c55f34a..3cb77d1 100644
--- a/plugins/pychrysalide/analysis/db/analyst.c
+++ b/plugins/pychrysalide/analysis/db/analyst.c
@@ -30,7 +30,7 @@
 
 
 #include <i18n.h>
-#include <analysis/db/analyst.h>
+#include <analysis/db/analyst-int.h>
 #include <core/collections.h>
 
 
@@ -45,8 +45,8 @@
 
 
 
-/* Crée un nouvel objet Python de type 'AnalystClient'. */
-static PyObject *py_analyst_client_new(PyTypeObject *, PyObject *, PyObject *);
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_analyst_client_init(PyObject *, PyObject *, PyObject *);
 
 /* Envoie un contenu binaire pour conservation côté serveur. */
 static PyObject *py_analyst_client_send_content(PyObject *, PyObject *);
@@ -80,23 +80,26 @@ static PyObject *py_analyst_client_get_current_snapshot(PyObject *, void *);
 
 
 
+CREATE_DYN_CONSTRUCTOR(analyst_client, G_TYPE_ANALYST_CLIENT);
+
+
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : type = type de l'objet à instancier.                         *
+*  Paramètres  : self = objet à initialiser (théoriquement).                  *
 *                args = arguments fournis à l'appel.                          *
 *                kwds = arguments de type key=val fournis.                    *
 *                                                                             *
-*  Description : Crée un nouvel objet Python de type 'AnalystClient'.         *
+*  Description : Initialise une instance sur la base du dérivé de GObject.    *
 *                                                                             *
-*  Retour      : Instance Python mise en place.                               *
+*  Retour      : 0.                                                           *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static PyObject *py_analyst_client_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+static int py_analyst_client_init(PyObject *self, PyObject *args, PyObject *kwds)
 {
-    PyObject *result;                       /* Instance à retourner        */
+    int result;                             /* Bilan à retourner           */
     GLoadedContent *loaded;                 /* Contenu local déjà chargé   */
     const char *hash;                       /* Empreinte du binaire visé   */
     const char *class;                      /* Nature du contenu analysé   */
@@ -107,7 +110,8 @@ static PyObject *py_analyst_client_new(PyTypeObject *type, PyObject *args, PyObj
     Py_ssize_t i;                           /* Boucle de parcours          */
     PyObject *item;                         /* Elément de la liste Python  */
     GDbCollection *collec;                  /* Version équivalente native  */
-    GAnalystClient *client;                 /* Serveur mis en place        */
+    GAnalystClient *client;                 /* Client mis en place         */
+    bool status;                            /* Bilan d'initialisation      */
 
 #define ANALYST_CLIENT_DOC                                                              \
     "AnalystClient provides and receives binary updates to and from a connected"        \
@@ -142,12 +146,17 @@ static PyObject *py_analyst_client_new(PyTypeObject *type, PyObject *args, PyObj
     loaded = NULL;
 
     ret = PyArg_ParseTuple(args, "ssO|O&", &hash, &class, &list, convert_to_loaded_content, &loaded);
-    if (!ret) return NULL;
+    if (!ret) return -1;
+
+    /* Initialisation d'un objet GLib */
+
+    ret = forward_pygobjet_init(self);
+    if (ret == -1) return -1;
 
     if (!PySequence_Check(list))
     {
         PyErr_SetString(PyExc_TypeError, _("The second argument must be a collection list"));
-        return NULL;
+        return -1;
     }
 
     length = PySequence_Length(list);
@@ -165,7 +174,7 @@ static PyObject *py_analyst_client_new(PyTypeObject *type, PyObject *args, PyObj
         if (ret != 1)
         {
             delete_collections_list(&collections);
-            result = NULL;
+            result = -1;
             goto exit;
         }
 
@@ -174,14 +183,11 @@ static PyObject *py_analyst_client_new(PyTypeObject *type, PyObject *args, PyObj
 
     }
 
-    client = g_analyst_client_new(hash, class, collections, loaded);
+    client = G_ANALYST_CLIENT(pygobject_get(self));
 
-    if (client != NULL)
-    {
-        result = pygobject_new(G_OBJECT(client));
-        g_object_unref(client);
-    }
-    else result = NULL;
+    status = g_analyst_client_setup(client, hash, class, collections, loaded);
+
+    result = status ? 0 : -1;
 
  exit:
 
@@ -861,7 +867,9 @@ PyTypeObject *get_python_analyst_client_type(void)
 
         .tp_methods     = py_analyst_client_methods,
         .tp_getset      = py_analyst_client_getseters,
-        .tp_new         = py_analyst_client_new,
+
+        .tp_init        = py_analyst_client_init,
+        .tp_new         = py_analyst_client_new
 
     };
 
diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c
index 320e40a..b04c801 100644
--- a/plugins/pychrysalide/helpers.c
+++ b/plugins/pychrysalide/helpers.c
@@ -37,6 +37,7 @@
 
 #include <i18n.h>
 #include <common/extstr.h>
+#include <plugins/dt.h>
 
 
 #include "access.h"
@@ -509,6 +510,63 @@ bool register_python_module_object(PyObject *module, PyTypeObject *type)
 *                args = éventuelle liste d'arguments.                         *
 *                kwds = éventuel dictionnaire de valeurs mises à disposition. *
 *                                                                             *
+*  Description : Accompagne la création d'une instance dérivée en Python.     *
+*                                                                             *
+*  Retour      : Nouvel objet Python mis en place ou NULL en cas d'échec.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+PyObject *python_constructor_with_dynamic_gtype(PyTypeObject *type, PyObject *args, PyObject *kwds, PyTypeObject *base, GType base_gtype)
+{
+    PyObject *result;                       /* Objet à retourner           */
+    bool first_time;                        /* Evite les multiples passages*/
+    GType gtype;                            /* Nouveau type de processeur  */
+    bool status;                            /* Bilan d'un enregistrement   */
+
+    /* Validations diverses */
+
+    if (type == base)
+        goto simple_way;
+
+    /* Mise en place d'un type dédié */
+
+    first_time = (g_type_from_name(type->tp_name) == 0);
+
+    gtype = build_dynamic_type(base_gtype, type->tp_name, NULL, NULL, NULL);
+
+    if (first_time)
+    {
+        status = register_class_for_dynamic_pygobject(gtype, type, base);
+
+        if (!status)
+        {
+            result = NULL;
+            goto exit;
+        }
+
+    }
+
+    /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */
+
+ simple_way:
+
+    result = PyType_GenericNew(type, args, kwds);
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : type = type du nouvel objet à mettre en place.               *
+*                args = éventuelle liste d'arguments.                         *
+*                kwds = éventuel dictionnaire de valeurs mises à disposition. *
+*                                                                             *
 *  Description : Marque l'interdiction d'une instanciation depuis Python.     *
 *                                                                             *
 *  Retour      : NULL pour la levée d'exception.                              *
diff --git a/plugins/pychrysalide/helpers.h b/plugins/pychrysalide/helpers.h
index 0ef8adc..32851c0 100644
--- a/plugins/pychrysalide/helpers.h
+++ b/plugins/pychrysalide/helpers.h
@@ -158,6 +158,22 @@ bool register_python_module_object(PyObject *, PyTypeObject *);
 #define APPLY_ABSTRACT_FLAG(tp) tp->tp_new = PyBaseObject_Type.tp_new
 
 
+/* Accompagne la création d'une instance dérivée en Python. */
+PyObject *python_constructor_with_dynamic_gtype(PyTypeObject *, PyObject *, PyObject *, PyTypeObject *, GType);
+
+
+#define CREATE_DYN_CONSTRUCTOR(pyname, gbase)                                               \
+static PyObject *py_ ## pyname ## _new(PyTypeObject *, PyObject *, PyObject *);             \
+static PyObject *py_ ## pyname ## _new(PyTypeObject *type, PyObject *args, PyObject *kwds)  \
+{                                                                                           \
+    PyObject *result;                       /* Objet à retourner           */               \
+    PyTypeObject *base;                     /* Type de base à dériver      */               \
+    base = get_python_ ## pyname ## _type();                                                \
+    result = python_constructor_with_dynamic_gtype(type, args, kwds, base, gbase);          \
+    return result;                                                                          \
+}
+
+
 /* Marque l'interdiction d'une instanciation depuis Python. */
 PyObject *no_python_constructor_allowed(PyTypeObject *, PyObject *, PyObject *);
 
diff --git a/src/analysis/db/analyst-int.h b/src/analysis/db/analyst-int.h
new file mode 100644
index 0000000..4f76eff
--- /dev/null
+++ b/src/analysis/db/analyst-int.h
@@ -0,0 +1,76 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * analyst-int.h - prototypes pour la définition interne des connexions en analyste à un serveur Chrysalide
+ *
+ * Copyright (C) 2022 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _ANALYSIS_DB_ANALYST_INT_H
+#define _ANALYSIS_DB_ANALYST_INT_H
+
+
+#include "analyst.h"
+#include "client-int.h"
+
+
+
+/* Description de client à l'écoute (instance) */
+struct _GAnalystClient
+{
+    GHubClient parent;                      /* A laisser en premier        */
+
+    char *cnt_hash;                         /* Empreinte du binaire lié    */
+    char *cnt_class;                        /* Interprétation du contenu   */
+
+    GLoadedContent *loaded;                 /* Contenu chargé              */
+    GList *collections;                     /* Collections d'un binaire    */
+
+    bool can_get_updates;                   /* Réception de maj possibles ?*/
+
+    snapshot_info_t *snapshots;             /* Liste des instantanés       */
+    size_t snap_count;                      /* Taille de cette liste       */
+    GMutex snap_lock;                       /* Concurrence des accès       */
+
+    snapshot_id_t current;                  /* Instantané courant          */
+    bool has_current;                       /* Validité de l'identifiant   */
+    GMutex cur_lock;                        /* Concurrence des accès       */
+
+};
+
+/* Description de client à l'écoute (classe) */
+struct _GAnalystClientClass
+{
+    GHubClientClass parent;                 /* A laisser en premier        */
+
+    /* Signaux */
+
+    void (* ready) (GAnalystClient *);
+    void (* server_status_changed) (GAnalystClient *, LoadingStatusHint);
+    void (* snapshots_updated) (GAnalystClient *);
+    void (* snapshot_changed) (GAnalystClient *);
+
+};
+
+
+/* Prépare un client pour une connexion à une BD. */
+bool g_analyst_client_setup(GAnalystClient *, const char *, const char *, GList *, GLoadedContent *);
+
+
+
+#endif  /* _ANALYSIS_DB_ANALYST_INT_H */
diff --git a/src/analysis/db/analyst.c b/src/analysis/db/analyst.c
index 2e1cc23..43fb840 100644
--- a/src/analysis/db/analyst.c
+++ b/src/analysis/db/analyst.c
@@ -29,59 +29,13 @@
 #include <string.h>
 
 
-#include "client-int.h"
+#include "analyst-int.h"
 #include "../storage/storage.h"
 #include "../../core/logs.h"
 
 
 
-
-
-
-
-/* ------------------------- PRISES EN COMPTE DES COMMANDES ------------------------- */
-
-
-
-
-
-
-/* Description de client à l'écoute (instance) */
-struct _GAnalystClient
-{
-    GHubClient parent;                      /* A laisser en premier        */
-
-    char *cnt_hash;                         /* Empreinte du binaire lié    */
-    char *cnt_class;                        /* Interprétation du contenu   */
-
-    GLoadedContent *loaded;                 /* Contenu chargé              */
-    GList *collections;                     /* Collections d'un binaire    */
-
-    bool can_get_updates;                   /* Réception de maj possibles ?*/
-
-    snapshot_info_t *snapshots;             /* Liste des instantanés       */
-    size_t snap_count;                      /* Taille de cette liste       */
-    GMutex snap_lock;                       /* Concurrence des accès       */
-
-    snapshot_id_t current;                  /* Instantané courant          */
-    bool has_current;                       /* Validité de l'identifiant   */
-    GMutex cur_lock;                        /* Concurrence des accès       */
-
-};
-
-/* Description de client à l'écoute (classe) */
-struct _GAnalystClientClass
-{
-    GHubClientClass parent;                 /* A laisser en premier        */
-
-    /* Signaux */
-
-    void (* ready) (GAnalystClient *);
-    void (* server_status_changed) (GAnalystClient *, LoadingStatusHint);
-    void (* snapshots_updated) (GAnalystClient *);
-    void (* snapshot_changed) (GAnalystClient *);
-
-};
+/* ----------------------- DEFINITION D'ANALYSTE COMME CLIENT ----------------------- */
 
 
 /* Initialise la classe des descriptions de fichier binaire. */
@@ -118,7 +72,6 @@ static bool g_analyst_client_handle_loading_hints(GAnalystClient *, packed_buffe
 
 
 
-
 /* ---------------------------------------------------------------------------------- */
 /*                                 GLUES POUR LA GLIB                                 */
 /* ---------------------------------------------------------------------------------- */
@@ -158,6 +111,9 @@ GType g_loading_status_hint_type(void)
 
 
 
+/* ---------------------------------------------------------------------------------- */
+/*                         DEFINITION D'ANALYSTE COMME CLIENT                         */
+/* ---------------------------------------------------------------------------------- */
 
 
 /* Indique le type défini pour une description de client à l'écoute. */
@@ -330,7 +286,7 @@ static void g_analyst_client_finalize(GAnalystClient *client)
 *                collections = ensemble de collections existantes.            *
 *                loaded      = éventuel élément local préchargé.              *
 *                                                                             *
-*  Description : Prépare un client pour une connexion à une BD.               *
+*  Description : Met en place un client pour une connexion à une BD.          *
 *                                                                             *
 *  Retour      : Structure mise en place ou NULL en cas d'échec.              *
 *                                                                             *
@@ -341,16 +297,51 @@ static void g_analyst_client_finalize(GAnalystClient *client)
 GAnalystClient *g_analyst_client_new(const char *hash, const char *class, GList *collections, GLoadedContent *loaded)
 {
     GAnalystClient *result;                     /* Adresse à retourner         */
+    bool status;                                /* Bilan de l'initialisation   */
 
     result = g_object_new(G_TYPE_ANALYST_CLIENT, NULL);
 
-    result->cnt_hash = strdup(hash);
-    result->cnt_class = strdup(class);
+    status = g_analyst_client_setup(result, hash, class, collections, loaded);
+
+    assert(status);
+
+    if (!status)
+        g_clear_object(&result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : client      = client pour les accès distants à initialiser.  *
+*                hash        = empreinte d'un binaire en cours d'analyse.     *
+*                class       = nature de l'interprétation de ce contenu.      *
+*                collections = ensemble de collections existantes.            *
+*                loaded      = éventuel élément local préchargé.              *
+*                                                                             *
+*  Description : Prépare un client pour une connexion à une BD.               *
+*                                                                             *
+*  Retour      : Structure mise en place ou NULL en cas d'échec.              *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool g_analyst_client_setup(GAnalystClient *client, const char *hash, const char *class, GList *collections, GLoadedContent *loaded)
+{
+    bool result;                            /* Bilan à retourner           */
+
+    result = true;
+
+    client->cnt_hash = strdup(hash);
+    client->cnt_class = strdup(class);
 
-    result->loaded = loaded;
+    client->loaded = loaded;
     if (loaded != NULL) g_object_ref(G_OBJECT(loaded));
 
-    result->collections = collections;
+    client->collections = collections;
 
     return result;
 
diff --git a/src/analysis/db/analyst.h b/src/analysis/db/analyst.h
index 459034e..ff189ba 100644
--- a/src/analysis/db/analyst.h
+++ b/src/analysis/db/analyst.h
@@ -50,6 +50,7 @@ GType g_loading_status_hint_type(void);
 
 
 
+/* ----------------------- DEFINITION D'ANALYSTE COMME CLIENT ----------------------- */
 
 
 #define G_TYPE_ANALYST_CLIENT            g_analyst_client_get_type()
@@ -70,7 +71,7 @@ typedef struct _GAnalystClientClass GAnalystClientClass;
 /* Indique le type défini pour une description de client à l'écoute. */
 GType g_analyst_client_get_type(void);
 
-/* Prépare un client pour une connexion à une BD. */
+/* Met en place un client pour une connexion à une BD. */
 GAnalystClient *g_analyst_client_new(const char *, const char *, GList *, GLoadedContent *);
 
 /* Envoie un contenu binaire pour conservation côté serveur. */
-- 
cgit v0.11.2-87-g4458