diff options
210 files changed, 16872 insertions, 8621 deletions
diff --git a/configure.ac b/configure.ac index 23e4e9b..300c970 100644 --- a/configure.ac +++ b/configure.ac @@ -241,6 +241,10 @@ AC_ARG_ENABLE([magic-support], AS_HELP_STRING([--disable-magic-support], [disable magic number recognition [default=no]]), [], [enable_magic_support=yes]) +AC_ARG_ENABLE([jsonglib-support], + AS_HELP_STRING([--disable-jsonglib-support], [disable jsonglib support [default=no]]), + [], [enable_jsonglib_support=yes]) + AC_ARG_ENABLE([python-bindings], AS_HELP_STRING([--disable-python-bindings], [disable Python bindings [default=no]]), [], [enable_python_bindings=yes]) @@ -593,6 +597,14 @@ AC_SUBST(LIBHS_LIBS) #--- Checks for json-glib-1.0 +AM_CONDITIONAL([BUILD_JSONGLIB_SUPPORT], [test "x$enable_jsonglib_support" = "xyes"]) + +if test "x$BUILD_JSONGLIB_SUPPORT_TRUE" = "x"; then + # json-glib support is available and enabled + CPPFLAGS="$CPPFLAGS -DINCLUDE_JSONGLIB_SUPPORT" +fi + + PKG_CHECK_MODULES(LIBJSONGLIB,json-glib-1.0 >= 1.6.6,[libjsonglib_found=yes],[libjsonglib_found=no]) if test "$libjsonglib_found" = "yes"; then @@ -601,8 +613,14 @@ else libjsonglib_version='-' fi -AC_SUBST(LIBJSONGLIB_CFLAGS) -AC_SUBST(LIBJSONGLIB_LIBS) +if test "x$enable_json_glib_support" = "xyes"; then + + AC_SUBST(LIBJSONGLIB_CFLAGS) + AC_SUBST(LIBJSONGLIB_LIBS) + + true # empty if/then body not allowed + +fi #--- Checks for Python @@ -784,6 +802,7 @@ AC_CONFIG_FILES([Makefile plugins/pychrysalide/gui/panels/Makefile plugins/pychrysalide/mangling/Makefile plugins/pychrysalide/plugins/Makefile + plugins/pynb/Makefile plugins/python/Makefile plugins/python/abackup/Makefile plugins/python/apkfiles/Makefile @@ -839,6 +858,7 @@ AC_CONFIG_FILES([Makefile src/gui/Makefile src/gui/core/Makefile src/gui/dialogs/Makefile + src/gui/dialogs/prefs/Makefile src/gui/menus/Makefile src/gui/panels/Makefile src/gui/tb/Makefile @@ -891,7 +911,7 @@ echo The client URL library....................... : $libcurl_version echo The YAML support library..................... : $libyaml_version echo The magic number recognition library......... : $libmagic_version echo The high-performance matching library........ : $libhs_version -echo The JSON Parser for GLib library............. : $libjsonglib_version +echo The JSON Parser for the GLib library......... : $libjsonglib_version echo echo Available Python programming language........ : $python3_version diff --git a/data/images/locked-symbolic.svg b/data/images/locked-symbolic.svg new file mode 100644 index 0000000..1b24147 --- /dev/null +++ b/data/images/locked-symbolic.svg @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="300" + height="300" + viewBox="0 0 79.375 79.374998" + version="1.1" + id="svg341" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" + sodipodi:docname="locked-symbolic.svg" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview343" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" + inkscape:document-units="px" + showgrid="true" + inkscape:zoom="2.3450501" + inkscape:cx="87.418175" + inkscape:cy="159.05844" + inkscape:window-width="1920" + inkscape:window-height="1011" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="layer1" + showguides="false"> + <inkscape:grid + type="xygrid" + id="grid462" + originx="0" + originy="0" /> + </sodipodi:namedview> + <defs + id="defs338" /> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1"> + <path + id="path1942" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 39.687499 1.3229166 A 13.229166 13.229166 0 0 0 26.458333 14.552083 A 13.229166 13.229166 0 0 0 31.75 25.124564 L 31.75 14.552083 A 7.9375 7.9375 0 0 1 39.687499 6.6145832 A 7.9375 7.9375 0 0 1 47.624999 14.552083 L 47.624999 25.135416 A 13.229166 13.229166 0 0 0 52.916666 14.552083 A 13.229166 13.229166 0 0 0 39.687499 1.3229166 z " /> + <path + id="rect1950" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 26.458333 14.552083 L 26.458333 39.687499 L 31.75 39.687499 L 31.75 14.552083 L 26.458333 14.552083 z M 47.624999 14.552083 L 47.624999 39.687499 L 52.916666 39.687499 L 52.916666 14.552083 L 47.624999 14.552083 z " /> + <path + id="rect2099" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 18.520833 35.71875 C 14.123462 35.71875 10.583333 39.258879 10.583333 43.656249 L 10.583333 70.114582 C 10.583333 74.511953 14.123462 78.052082 18.520833 78.052082 L 60.854166 78.052082 C 65.251537 78.052082 68.791666 74.511953 68.791666 70.114582 L 68.791666 43.656249 C 68.791666 39.258879 65.251537 35.71875 60.854166 35.71875 L 18.520833 35.71875 z M 37.703125 52.916666 L 41.671874 52.916666 C 42.40477 52.916666 42.994791 53.506688 42.994791 54.239583 L 42.994791 70.114582 C 42.994791 70.847478 42.40477 71.437499 41.671874 71.437499 L 37.703125 71.437499 C 36.970229 71.437499 36.380208 70.847478 36.380208 70.114582 L 36.380208 54.239583 C 36.380208 53.506688 36.970229 52.916666 37.703125 52.916666 z " /> + </g> +</svg> diff --git a/data/images/nolock-symbolic.svg b/data/images/nolock-symbolic.svg new file mode 100644 index 0000000..ed59858 --- /dev/null +++ b/data/images/nolock-symbolic.svg @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="300" + height="300" + viewBox="0 0 79.375 79.374998" + version="1.1" + id="svg341" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" + sodipodi:docname="nolock-symbolic.svg" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview343" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" + inkscape:document-units="px" + showgrid="true" + inkscape:zoom="2.3450501" + inkscape:cx="84.006734" + inkscape:cy="159.05844" + inkscape:window-width="1920" + inkscape:window-height="1011" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="layer1" + showguides="true"> + <inkscape:grid + type="xygrid" + id="grid462" + originx="0" + originy="0" /> + </sodipodi:namedview> + <defs + id="defs338" /> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1"> + <path + id="path1942" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 39.687499 1.3229166 A 13.229166 13.229166 0 0 0 26.458333 14.552083 A 13.229166 13.229166 0 0 0 31.75 25.124564 L 31.75 14.552083 A 7.9375 7.9375 0 0 1 39.687499 6.6145832 A 7.9375 7.9375 0 0 1 47.624999 14.552083 L 47.624999 25.135416 A 13.229166 13.229166 0 0 0 52.916666 14.552083 A 13.229166 13.229166 0 0 0 39.687499 1.3229166 z " /> + <path + id="rect1950" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 26.458333 14.552083 L 26.458333 39.687499 L 31.75 39.687499 L 31.75 14.552083 L 26.458333 14.552083 z M 47.624999 14.552083 L 47.624999 39.687499 L 52.916666 39.687499 L 52.916666 14.552083 L 47.624999 14.552083 z " /> + <path + id="rect2099" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 18.520833 35.71875 C 14.123466 35.71875 10.583333 39.258884 10.583333 43.656249 L 10.583333 70.114582 C 10.583333 70.465451 10.608349 70.810037 10.652063 71.148628 L 36.380208 57.337585 L 36.380208 54.239583 C 36.380208 53.506688 36.970229 52.916666 37.703125 52.916666 L 41.671874 52.916666 C 42.259126 52.916666 42.753343 53.295986 42.927095 53.823587 L 68.065612 40.329321 C 66.811534 37.603321 64.061787 35.71875 60.854166 35.71875 L 18.520833 35.71875 z M 68.791666 47.447232 L 42.994791 61.294449 L 42.994791 70.114582 C 42.994791 70.847478 42.40477 71.437499 41.671874 71.437499 L 37.703125 71.437499 C 36.970229 71.437499 36.380208 70.847478 36.380208 70.114582 L 36.380208 64.845137 L 14.176912 76.763789 C 15.423753 77.577929 16.914362 78.052082 18.520833 78.052082 L 60.854166 78.052082 C 65.251533 78.052082 68.791666 74.511949 68.791666 70.114582 L 68.791666 47.447232 z " /> + <rect + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + id="rect3055" + width="79.375" + height="6.614583" + x="-30.998156" + y="61.111198" + transform="rotate(-28.226475)" /> + </g> +</svg> diff --git a/data/images/unlocked-symbolic.svg b/data/images/unlocked-symbolic.svg new file mode 100644 index 0000000..da7b6ac --- /dev/null +++ b/data/images/unlocked-symbolic.svg @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="300" + height="300" + viewBox="0 0 79.375 79.374998" + version="1.1" + id="svg341" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" + sodipodi:docname="unlocked-symbolic.svg" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview343" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" + inkscape:document-units="px" + showgrid="true" + inkscape:zoom="2.3450501" + inkscape:cx="87.418175" + inkscape:cy="159.05844" + inkscape:window-width="1920" + inkscape:window-height="1011" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="layer1" + showguides="false"> + <inkscape:grid + type="xygrid" + id="grid462" + originx="0" + originy="0" /> + </sodipodi:namedview> + <defs + id="defs338" /> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1"> + <path + id="path1942" + style="display:none;fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 39.687499,1.3229166 A 13.229166,13.229166 0 0 0 26.458333,14.552083 V 39.687499 H 31.75 V 25.124564 14.552083 a 7.9375,7.9375 0 0 1 7.937499,-7.9374998 7.9375,7.9375 0 0 1 7.9375,7.9374998 v 10.583333 14.552083 h 5.291667 V 14.552083 A 13.229166,13.229166 0 0 0 39.687499,1.3229166 Z" /> + <path + id="rect2099" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 18.520833 35.71875 C 14.123462 35.71875 10.583333 39.258879 10.583333 43.656249 L 10.583333 70.114582 C 10.583333 74.511953 14.123462 78.052082 18.520833 78.052082 L 60.854166 78.052082 C 65.251537 78.052082 68.791666 74.511953 68.791666 70.114582 L 68.791666 43.656249 C 68.791666 39.258879 65.251537 35.71875 60.854166 35.71875 L 18.520833 35.71875 z M 37.703125 52.916666 L 41.671874 52.916666 C 42.40477 52.916666 42.994791 53.506688 42.994791 54.239583 L 42.994791 70.114582 C 42.994791 70.847478 42.40477 71.437499 41.671874 71.437499 L 37.703125 71.437499 C 36.970229 71.437499 36.380208 70.847478 36.380208 70.114582 L 36.380208 54.239583 C 36.380208 53.506688 36.970229 52.916666 37.703125 52.916666 z " /> + <path + id="path2191" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 60.854166 1.3229166 A 13.229166 13.229166 0 0 0 47.624999 14.552083 L 47.624999 39.687499 L 52.916666 39.687499 L 52.916666 25.124564 L 52.916666 14.552083 A 7.9375 7.9375 0 0 1 60.854166 6.6145832 A 7.9375 7.9375 0 0 1 68.791666 14.552083 L 68.791666 25.135416 L 68.791666 25.24497 C 69.221772 25.173377 69.663543 25.135416 70.114582 25.135416 L 74.083332 25.135416 L 74.083332 14.552083 A 13.229166 13.229166 0 0 0 60.854166 1.3229166 z " /> + </g> +</svg> diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 9e177fa..529f4fe 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -13,7 +13,9 @@ endif # androhelpers SUBDIRS = \ - $(PYTHON3_SUBDIRS) + $(PYTHON3_SUBDIRS) \ + pe \ + pynb # arm \ # bootimg \ diff --git a/plugins/dalvik/context.h b/plugins/dalvik/context.h index f09cfa6..1954b1e 100644 --- a/plugins/dalvik/context.h +++ b/plugins/dalvik/context.h @@ -58,7 +58,7 @@ GType g_dalvik_context_get_type(void); GDalvikContext *g_dalvik_context_new(void); /* Mémorise une zone comme étant des données de branchements. */ -bool g_dalvik_context_register_switch_data(GDalvikContext *ctx, const vmpa2t *start, phys_t length); +bool g_dalvik_context_register_switch_data(GDalvikContext *, const vmpa2t *, phys_t); /* Mémorise une zone comme étant des données d'un tableau. */ bool g_dalvik_context_register_array_data(GDalvikContext *, const vmpa2t *, uint16_t, phys_t); diff --git a/plugins/pe/Makefile.am b/plugins/pe/Makefile.am index e9cd482..ddc4ee4 100644 --- a/plugins/pe/Makefile.am +++ b/plugins/pe/Makefile.am @@ -39,22 +39,25 @@ endif libpe_la_SOURCES = \ core.h core.c \ - pe-int.h pe-int.c \ + format-int.h format-int.c \ format.h format.c \ - pe_def.h \ - rich.h rich.c \ - routine.h routine.c \ - section.h section.c \ - symbols.h symbols.c + pe-def.h + + +# rich.h rich.c \ +# routine.h routine.c \ +# symbols.h symbols.c + + libpe_la_LIBADD = \ $(PYTHON3_LIBADD) -libpe_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) -I$(top_srcdir)/src +libpe_la_CFLAGS = $(TOOLKIT_CFLAGS) -I$(top_srcdir)/src libpe_la_LDFLAGS = \ -avoid-version \ - -L$(top_srcdir)/src/.libs -lchrysacore \ + -L$(top_srcdir)/src/.libs -lchrysacore4 \ $(RUN_PATH) $(PYTHON3_LDFLAGS) diff --git a/plugins/pe/core-int.h b/plugins/pe/core-int.h new file mode 100644 index 0000000..1d1b817 --- /dev/null +++ b/plugins/pe/core-int.h @@ -0,0 +1,56 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-int.h - prototypes internes pour l'intégration du support du format PE + * + * Copyright (C) 2025 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 _PLUGINS_PE_CORE_INT_H +#define _PLUGINS_PE_CORE_INT_H + + +#include "core.h" + + +#include <plugins/native-int.h> + + + +/* Greffon natif pour le support du format PE (instance) */ +struct _GPePlugin +{ + GNativePlugin parent; /* A laisser en premier */ + +}; + + +/* Greffon natif pour le support du format PE (classe) */ +struct _GPePluginClass +{ + GNativePluginClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un module pour un greffon de support PE. */ +bool g_pe_plugin_create(GPePlugin *, GModule *); + + + +#endif /* _PLUGINS_PE_CORE_INT_H */ diff --git a/plugins/pe/core.c b/plugins/pe/core.c index ddbacf5..b752735 100644 --- a/plugins/pe/core.c +++ b/plugins/pe/core.c @@ -24,35 +24,244 @@ #include "core.h" -#include <core/global.h> +//#include <core/global.h> #include <plugins/self.h> +#include "core-int.h" #include "format.h" #ifdef INCLUDE_PYTHON3_BINDINGS # include "python/module.h" #endif + +/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */ + + +/* Initialise la classe des greffons de support PE. */ +static void g_pe_plugin_class_init(GPePluginClass *); + +/* Procède à l'initialisation de l'interface de gestion. */ +//static void g_pe_plugin_plugin_manager_interface_init(GPluginManagerInterface *); + +/* Initialise une instance de greffon de support PE. */ +static void g_pe_plugin_init(GPePlugin *); + +/* Supprime toutes les références externes. */ +static void g_pe_plugin_dispose(GPePlugin *); + +/* Procède à la libération totale de la mémoire. */ +static void g_pe_plugin_finalize(GPePlugin *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Prend acte de l'activation du greffon. */ +static bool g_pe_plugin_enable(GPePlugin *); + +/* Prend acte de la désactivation du greffon. */ +static bool g_pe_plugin_disable(GPePlugin *); + + + +/* ---------------------------------------------------------------------------------- */ +/* COMPOSITION DE NOUVEAU GREFFON NATIF */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un greffon de liaison Python */ +G_DEFINE_TYPE_WITH_CODE(GPePlugin, g_pe_plugin, G_TYPE_NATIVE_PLUGIN, + /*G_IMPLEMENT_INTERFACE(G_TYPE_PLUGIN_MANAGER, g_pe_plugin_plugin_manager_interface_init)*/); + + +NATIVE_PLUGIN_ENTRYPOINT(g_pe_plugin_new); + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* * +* Description : Initialise la classe des greffons de support PE. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pe_plugin_class_init(GPePluginClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GPluginModuleClass *plugin; /* Version parente de la classe*/ + + object = G_OBJECT_CLASS(class); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_pe_plugin_dispose; + object->finalize = (GObjectFinalizeFunc)g_pe_plugin_finalize; + + plugin = G_PLUGIN_MODULE_CLASS(class); + + plugin->enable = (pg_management_fc)g_pe_plugin_enable; + plugin->disable = (pg_management_fc)g_pe_plugin_disable; + +} + +#if 0 + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de gestion. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pe_plugin_plugin_manager_interface_init(GPluginManagerInterface *iface) +{ + iface->handle_native = (handle_native_plugins_cb)g_pe_plugin_handle_native_plugins_loaded_event; + +} + +#endif + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser. * +* * +* Description : Initialise une instance de greffon de support PE. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pe_plugin_init(GPePlugin *plugin) +{ + STORE_PLUGIN_ABI(plugin); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pe_plugin_dispose(GPePlugin *plugin) +{ + G_OBJECT_CLASS(g_pe_plugin_parent_class)->dispose(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pe_plugin_finalize(GPePlugin *plugin) +{ + G_OBJECT_CLASS(g_pe_plugin_parent_class)->finalize(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* Paramètres : module = extension vue du système. * +* * +* Description : Crée un module pour un greffon de support PE. * +* * +* Retour : Adresse de la structure mise en place. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +GPluginModule *g_pe_plugin_new(GModule *module) +{ + GPePlugin *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_PE_PLUGIN, NULL); + + if (!g_pe_plugin_create(result, module)) + g_clear_object(&result); + + return G_PLUGIN_MODULE(result); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser pleinement. * +* module = extension vue du système. * +* * +* Description : Met en place un module pour un greffon de support PE. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +bool g_pe_plugin_create(GPePlugin *plugin, GModule *module) +{ + bool result; /* Bilan à retourner */ + #ifdef INCLUDE_PYTHON3_BINDINGS -# define PG_REQ RL("PyChrysalide") +# define PG_REQ REQ_LIST("PyChrysalide") #else # define PG_REQ NO_REQ #endif + result = g_native_plugin_create(G_NATIVE_PLUGIN(plugin), + "PeFmt", + "PE format support", + PACKAGE_VERSION, + CHRYSALIDE_WEBSITE("doc/formats"), + PG_REQ, + module); + return result; -DEFINE_CHRYSALIDE_PLUGIN("Pe", "PE format support", - PACKAGE_VERSION, CHRYSALIDE_WEBSITE("doc/formats"), - PG_REQ, AL(PGA_PLUGIN_INIT, PGA_CONTENT_RESOLVER)); +} +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * * Paramètres : plugin = greffon à manipuler. * * * -* Description : Prend acte du chargement du greffon. * +* Description : Prend acte de l'activation du greffon. * * * * Retour : - * * * @@ -60,7 +269,7 @@ DEFINE_CHRYSALIDE_PLUGIN("Pe", "PE format support", * * ******************************************************************************/ -G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin) +static bool g_pe_plugin_enable(GPePlugin *plugin) { bool result; /* Bilan à retourner */ @@ -77,6 +286,65 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin) /****************************************************************************** * * +* Paramètres : plugin = greffon à manipuler. * +* * +* Description : Prend acte de la désactivation du greffon. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_pe_plugin_disable(GPePlugin *plugin) +{ + bool result; /* Bilan à retourner */ + + result = true; + + return result; + +} + + + +#if 0 + +/* ---------------------------------------------------------------------------------- */ +/* INTERVENTION DANS LA GESTION DE GREFFONS */ +/* INTERVENTION DANS LA GESTION DE GREFFONS */ +/* INTERVENTION DANS LA GESTION DE GREFFONS */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : plugin = interface à manipuler. * +* * +* Description : Accompagne la fin du chargement des modules natifs. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pe_plugin_handle_native_plugins_loaded_event(GPePlugin *plugin) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + + gstate = PyGILState_Ensure(); + + load_python_plugins(G_PLUGIN_MODULE(plugin)); + + PyGILState_Release(gstate); + +} + + + +/****************************************************************************** +* * * Paramètres : plugin = greffon à manipuler. * * action = type d'action attendue. * * content = contenu binaire à traiter. * @@ -90,7 +358,7 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin) * Remarques : - * * * ******************************************************************************/ - +#if 0 G_MODULE_EXPORT void chrysalide_plugin_handle_binary_content(const GPluginModule *plugin, PluginAction action, GBinContent *content, wgroup_id_t wid, GtkStatusStack *status) { bool test; /* Bilan des accès mémoire */ @@ -114,3 +382,7 @@ G_MODULE_EXPORT void chrysalide_plugin_handle_binary_content(const GPluginModule } } +#endif + + +#endif diff --git a/plugins/pe/core.h b/plugins/pe/core.h index 4497cf5..5c0696f 100644 --- a/plugins/pe/core.h +++ b/plugins/pe/core.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * core.h - prototypes pour l'intégration du support du format PE * - * Copyright (C) 2017-2018 Cyrille Bagard + * Copyright (C) 2017-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,16 +25,18 @@ #define _PLUGINS_PE_CORE_H +#include <glibext/helpers.h> #include <plugins/plugin.h> -#include <plugins/plugin-int.h> -/* Prend acte du chargement du greffon. */ -G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *); +#define G_TYPE_PE_PLUGIN (g_pe_plugin_get_type()) -/* Procède à une opération liée à un contenu binaire. */ -G_MODULE_EXPORT void chrysalide_plugin_handle_binary_content(const GPluginModule *, PluginAction, GBinContent *, wgroup_id_t, GtkStatusStack *); +DECLARE_GTYPE(GPePlugin, g_pe_plugin, G, PE_PLUGIN); + + +/* Crée un module pour un greffon de support PE. */ +GPluginModule *g_pe_plugin_new(GModule *); diff --git a/plugins/pe/pe-int.c b/plugins/pe/format-int.c index 4104ce1..2d1e6c8 100644 --- a/plugins/pe/pe-int.c +++ b/plugins/pe/format-int.c @@ -1,6 +1,6 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * pe-int.c - structures internes du format Portable Executable + * format-int.c - structures internes du format Portable Executable * * Copyright (C) 2010-2017 Cyrille Bagard * @@ -21,14 +21,14 @@ */ -#include "pe-int.h" +#include "format-int.h" #include <malloc.h> #include <string.h> #include <i18n.h> -#include <common/endianness.h> +#include <common/datatypes.h> #include <core/logs.h> @@ -46,7 +46,7 @@ * * ******************************************************************************/ -bool read_dos_image_header(const GPeFormat *format, image_dos_header *header) +bool read_dos_image_header(const GPeFormat *format, image_dos_header_t *header) { bool result; /* Bilan à retourner */ const GBinContent *content; /* Contenu binaire à lire */ @@ -104,7 +104,7 @@ bool read_dos_image_header(const GPeFormat *format, image_dos_header *header) * * ******************************************************************************/ -bool read_pe_file_header(const GPeFormat *format, vmpa2t *pos, image_file_header *header) +bool read_pe_file_header(const GPeFormat *format, vmpa2t *pos, image_file_header_t *header) { bool result; /* Bilan à retourner */ const GBinContent *content; /* Contenu binaire à lire */ @@ -140,13 +140,13 @@ bool read_pe_file_header(const GPeFormat *format, vmpa2t *pos, image_file_header * * ******************************************************************************/ -bool read_pe_optional_header(const GPeFormat *format, vmpa2t *pos, image_optional_header *header) +bool read_pe_optional_header(const GPeFormat *format, vmpa2t *pos, image_optional_header_t *header) { bool result; /* Bilan à retourner */ const GBinContent *content; /* Contenu binaire à lire */ - image_optional_header_32 *hdr32; /* Version 32 bits */ - image_optional_header_64 *hdr64; /* Version 64 bits */ - image_data_directory *directories; /* Répertoires à charger */ + image_optional_header_32_t *hdr32; /* Version 32 bits */ + image_optional_header_64_t *hdr64; /* Version 64 bits */ + image_data_directory_t *directories; /* Répertoires à charger */ uint32_t *number_of_rva_and_sizes; /* Quantité de ces répertoires */ uint32_t i; /* Boucle de parcours */ @@ -278,7 +278,7 @@ bool read_pe_optional_header(const GPeFormat *format, vmpa2t *pos, image_optiona * * ******************************************************************************/ -bool read_pe_nt_header(const GPeFormat *format, image_nt_headers *header, vmpa2t *next) +bool read_pe_nt_header(const GPeFormat *format, image_nt_headers_t *header, vmpa2t *next) { bool result; /* Bilan à retourner */ const GBinContent *content; /* Contenu binaire à lire */ @@ -318,7 +318,7 @@ bool read_pe_nt_header(const GPeFormat *format, image_nt_headers *header, vmpa2t * * ******************************************************************************/ -bool read_pe_image_section_header(const GPeFormat *format, vmpa2t *pos, image_section_header *section) +bool read_pe_image_section_header(const GPeFormat *format, vmpa2t *pos, image_section_header_t *section) { bool result; /* Bilan à retourner */ const GBinContent *content; /* Contenu binaire à lire */ @@ -331,7 +331,7 @@ bool read_pe_image_section_header(const GPeFormat *format, vmpa2t *pos, image_se for (i = 0; i < IMAGE_SIZEOF_SHORT_NAME && result; i++) result = g_binary_content_read_u8(content, pos, (uint8_t *)§ion->name[i]); - if (result) result = g_binary_content_read_u32(content, pos, SRE_LITTLE, §ion->misc.physical_address); + if (result) result = g_binary_content_read_u32(content, pos, SRE_LITTLE, §ion->physical_address); if (result) result = g_binary_content_read_u32(content, pos, SRE_LITTLE, §ion->virtual_address); if (result) result = g_binary_content_read_u32(content, pos, SRE_LITTLE, §ion->size_of_raw_data); @@ -361,7 +361,7 @@ bool read_pe_image_section_header(const GPeFormat *format, vmpa2t *pos, image_se * * ******************************************************************************/ -bool read_pe_image_export_directory(const GPeFormat *format, vmpa2t *pos, image_export_directory *dir) +bool read_pe_image_export_directory(const GPeFormat *format, vmpa2t *pos, image_export_directory_t *dir) { bool result; /* Bilan à retourner */ const GBinContent *content; /* Contenu binaire à lire */ @@ -401,7 +401,7 @@ bool read_pe_image_export_directory(const GPeFormat *format, vmpa2t *pos, image_ * * ******************************************************************************/ -bool read_pe_image_import_descriptor(const GPeFormat *format, vmpa2t *pos, image_import_descriptor *desc) +bool read_pe_image_import_descriptor(const GPeFormat *format, vmpa2t *pos, image_import_descriptor_t *desc) { bool result; /* Bilan à retourner */ const GBinContent *content; /* Contenu binaire à lire */ diff --git a/plugins/pe/pe-int.h b/plugins/pe/format-int.h index ebcf3f0..0b5ad4b 100644 --- a/plugins/pe/pe-int.h +++ b/plugins/pe/format-int.h @@ -1,6 +1,6 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * pe-int.h - prototypes pour les structures internes du format Portable Executable + * format-int.h - prototypes pour les structures internes du format Portable Executable * * Copyright (C) 2009-2017 Cyrille Bagard * @@ -21,14 +21,13 @@ */ -#ifndef _PLUGINS_PE_PE_INT_H -#define _PLUGINS_PE_PE_INT_H +#ifndef _PLUGINS_PE_FORMAT_INT_H +#define _PLUGINS_PE_FORMAT_INT_H #include <format/executable-int.h> -#include "pe_def.h" #include "format.h" @@ -36,13 +35,14 @@ /* Format d'exécutable PE (instance) */ struct _GPeFormat { - GExeFormat parent; /* A laisser en premier */ + GExecutableFormat parent; /* A laisser en premier */ - image_dos_header dos_header; /* En-tête DOS */ + image_dos_header_t dos_header; /* En-tête DOS */ mrange_t rich_header; /* En-tête enrichi */ - image_nt_headers nt_headers; /* En-tête Windows */ + image_nt_headers_t nt_headers; /* En-tête Windows */ - image_section_header *sections; /* Liste des sections */ + vmpa2t sections_start; /* Début de la zone de sections*/ + image_section_header_t *sections; /* Liste des sections */ bool loaded; /* Détection partielle menée */ @@ -51,32 +51,35 @@ struct _GPeFormat /* Format d'exécutable PE (classe) */ struct _GPeFormatClass { - GExeFormatClass parent; /* A laisser en premier */ + GExecutableFormatClass parent; /* A laisser en premier */ }; +/* Met en place une nouvelle instance de format PE. */ +bool g_pe_format_create(GPeFormat *, GBinContent *); + /* Procède à la lecture d'un en-tête de programme DOS. */ -bool read_dos_image_header(const GPeFormat *, image_dos_header *); +bool read_dos_image_header(const GPeFormat *, image_dos_header_t *); /* Procède à la lecture d'un en-tête de programme PE (1). */ -bool read_pe_file_header(const GPeFormat *, vmpa2t *, image_file_header *); +bool read_pe_file_header(const GPeFormat *, vmpa2t *, image_file_header_t *); /* Procède à la lecture d'un en-tête de programme PE (2). */ -bool read_pe_optional_header(const GPeFormat *, vmpa2t *, image_optional_header *); +bool read_pe_optional_header(const GPeFormat *, vmpa2t *, image_optional_header_t *); /* Procède à la lecture d'un en-tête de programme PE. */ -bool read_pe_nt_header(const GPeFormat *, image_nt_headers *, vmpa2t *); +bool read_pe_nt_header(const GPeFormat *, image_nt_headers_t *, vmpa2t *); /* Procède à la lecture d'un en-tête de section PE. */ -bool read_pe_image_section_header(const GPeFormat *, vmpa2t *, image_section_header *); +bool read_pe_image_section_header(const GPeFormat *, vmpa2t *, image_section_header_t *); /* Procède à la lecture d'un répertoire d'exportations. */ -bool read_pe_image_export_directory(const GPeFormat *, vmpa2t *, image_export_directory *); +bool read_pe_image_export_directory(const GPeFormat *, vmpa2t *, image_export_directory_t *); /* Procède à la lecture d'un répertoire de programme PE. */ -bool read_pe_image_import_descriptor(const GPeFormat *, vmpa2t *, image_import_descriptor *); +bool read_pe_image_import_descriptor(const GPeFormat *, vmpa2t *, image_import_descriptor_t *); -#endif /* _PLUGINS_PE_PE_INT_H */ +#endif /* _PLUGINS_PE_FORMAT_INT_H */ diff --git a/plugins/pe/format.c b/plugins/pe/format.c index 3839063..11bc116 100644 --- a/plugins/pe/format.c +++ b/plugins/pe/format.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * pe.c - support du format Portable Executable + * format.c - support du format Portable Executable * - * Copyright (C) 2010-2017 Cyrille Bagard + * Copyright (C) 2010-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,15 +25,18 @@ #include <assert.h> +#include <string.h> -#include "pe-int.h" -#include "rich.h" -#include "section.h" -#include "symbols.h" +#include "format-int.h" +//#include "rich.h" +//#include "symbols.h" +/* ------------------------- DEFINITION D'UN NOUVEAU FORMAT ------------------------- */ + + /* Initialise la classe des formats d'exécutables ELF. */ static void g_pe_format_class_init(GPeFormatClass *); @@ -46,6 +49,11 @@ static void g_pe_format_dispose(GPeFormat *); /* Procède à la libération totale de la mémoire. */ static void g_pe_format_finalize(GPeFormat *); + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + /* Indique la désignation interne du format. */ static char *g_pe_format_get_key(const GPeFormat *); @@ -53,39 +61,28 @@ static char *g_pe_format_get_key(const GPeFormat *); static char *g_pe_format_get_description(const GPeFormat *); /* Assure l'interprétation d'un format en différé. */ -static bool g_pe_format_analyze(GPeFormat *, wgroup_id_t, GtkStatusStack *); +static bool g_pe_format_analyze(GPeFormat *); /* Informe quant au boutisme utilisé. */ static SourceEndian g_pe_format_get_endianness(const GPeFormat *); /* Indique le type d'architecture visée par le format. */ -static const char *g_pe_format_get_target_machine(const GPeFormat *); +static char *g_pe_format_get_target_machine(const GPeFormat *); -/* Fournit l'adresse principale associée à un format Elf. */ +/* Fournit l'adresse principale associée à un format. */ static bool g_pe_format_get_main_address(GPeFormat *, vmpa2t *); - -#if 0 - /* Etend la définition des portions au sein d'un binaire. */ -static void g_pe_format_refine_portions(GPeFormat *); - -#endif - - - -/* Fournit l'emplacement correspondant à une adresse virtuelle. */ -bool g_pe_format_translate_address_into_vmpa_using_portions(GPeFormat *, virt_t, vmpa2t *); - - -#if 0 +static bool g_pe_format_refine_portions(GPeFormat *); /* Fournit l'emplacement d'une section donnée. */ -static bool g_pe_format_get_section_range_by_name(const GPeFormat *, const char *, mrange_t *); -#endif +static bool g_pe_format_find_section_range_by_name(const GPeFormat *, const char *, mrange_t *); +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION D'UN NOUVEAU FORMAT */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** @@ -116,7 +113,7 @@ bool check_pe_format(const GBinContent *content) if (result) { - G_KNOWN_FORMAT(&format)->content = (GBinContent *)content; + ((GKnownFormat *)&format)->content = (GBinContent *)content; result = read_dos_image_header(&format, &format.dos_header); } @@ -137,7 +134,7 @@ bool check_pe_format(const GBinContent *content) /* Indique le type défini pour un format d'exécutable ELF. */ -G_DEFINE_TYPE(GPeFormat, g_pe_format, G_TYPE_EXE_FORMAT); +G_DEFINE_TYPE(GPeFormat, g_pe_format, G_TYPE_EXECUTABLE_FORMAT); /****************************************************************************** @@ -156,8 +153,8 @@ static void g_pe_format_class_init(GPeFormatClass *klass) { GObjectClass *object; /* Autre version de la classe */ GKnownFormatClass *known; /* Version de format connu */ - GBinFormatClass *fmt; /* Version en format basique */ - GExeFormatClass *exe; /* Version en exécutable */ + GProgramFormatClass *prgm; /* Version en format basique */ + GExecutableFormatClass *exe; /* Version en exécutable */ object = G_OBJECT_CLASS(klass); @@ -170,20 +167,19 @@ static void g_pe_format_class_init(GPeFormatClass *klass) known->get_desc = (known_get_desc_fc)g_pe_format_get_description; known->analyze = (known_analyze_fc)g_pe_format_analyze; - fmt = G_BIN_FORMAT_CLASS(klass); + prgm = G_PROGRAM_FORMAT_CLASS(klass); - fmt->get_endian = (format_get_endian_fc)g_pe_format_get_endianness; + prgm->get_endian = (program_get_endian_fc)g_pe_format_get_endianness; + prgm->find_range_by_name = (find_range_by_name_fc)g_pe_format_find_section_range_by_name; - exe = G_EXE_FORMAT_CLASS(klass); + exe = G_EXECUTABLE_FORMAT_CLASS(klass); exe->get_machine = (get_target_machine_fc)g_pe_format_get_target_machine; exe->get_main_addr = (get_main_addr_fc)g_pe_format_get_main_address; - //exe->refine_portions = (refine_portions_fc)g_pe_format_refine_portions; + exe->refine_portions = (refine_portions_fc)g_pe_format_refine_portions; - //exe->translate_phys = (translate_phys_fc)g_exe_format_translate_offset_into_vmpa_using_portions; - exe->translate_virt = (translate_virt_fc)g_pe_format_translate_address_into_vmpa_using_portions; - - //exe->get_range_by_name = (get_range_by_name_fc)g_pe_format_get_section_range_by_name; + exe->translate_phys = g_executable_format_translate_offset_into_vmpa_with_portions; + exe->translate_virt = g_executable_format_translate_address_into_vmpa_with_portions; } @@ -262,39 +258,41 @@ static void g_pe_format_finalize(GPeFormat *format) * * ******************************************************************************/ -GExeFormat *g_pe_format_new(GBinContent *content) +GPeFormat *g_pe_format_new(GBinContent *content) { GPeFormat *result; /* Structure à retourner */ - if (!check_pe_format(content)) - return NULL; - result = g_object_new(G_TYPE_PE_FORMAT, NULL); - g_known_format_set_content(G_KNOWN_FORMAT(result), content); + if (!g_pe_format_create(result, content)) + g_clear_object(&result); - return G_EXE_FORMAT(result); + return result; } /****************************************************************************** * * -* Paramètres : format = description de l'exécutable à consulter. * +* Paramètres : format = description du format connu à consulter. * +* content = contenu binaire à parcourir. * * * -* Description : Indique la désignation interne du format. * +* Description : Met en place une nouvelle instance de format PE. * * * -* Retour : Désignation du format. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static char *g_pe_format_get_key(const GPeFormat *format) +bool g_pe_format_create(GPeFormat *format, GBinContent *content) { - char *result; /* Désignation à retourner */ + bool result; /* Bilan à retourner */ - result = strdup("pe"); + result = true;//check_pe_format(content); + + if (result) + result = g_executable_format_create(G_EXECUTABLE_FORMAT(format), content); return result; @@ -303,21 +301,21 @@ static char *g_pe_format_get_key(const GPeFormat *format) /****************************************************************************** * * -* Paramètres : format = description de l'exécutable à consulter. * +* Paramètres : format = format en place à consulter. * * * -* Description : Fournit une description humaine du format. * +* Description : Présente l'en-tête MS-DOS du format chargé. * * * -* Retour : Description du format. * +* Retour : Pointeur vers la description principale. * * * * Remarques : - * * * ******************************************************************************/ -static char *g_pe_format_get_description(const GPeFormat *format) +const image_dos_header_t *g_pe_format_get_dos_header(const GPeFormat *format) { - char *result; /* Désignation à retourner */ + const image_dos_header_t *result; /* Informations à retourner */ - result = strdup("Portable Executable"); + result = &format->dos_header; return result; @@ -326,44 +324,21 @@ static char *g_pe_format_get_description(const GPeFormat *format) /****************************************************************************** * * -* Paramètres : format = format chargé dont l'analyse est lancée. * -* gid = groupe de travail dédié. * -* status = barre de statut à tenir informée. * +* Paramètres : format = format en place à consulter. * * * -* Description : Assure l'interprétation d'un format en différé. * +* Description : Présente l'en-tête NT du format chargé. * * * -* Retour : Bilan de l'opération. * +* Retour : Pointeur vers la description principale. * * * * Remarques : - * * * ******************************************************************************/ -static bool g_pe_format_analyze(GPeFormat *format, wgroup_id_t gid, GtkStatusStack *status) +const image_nt_headers_t *g_pe_format_get_nt_headers(const GPeFormat *format) { - bool result; /* Bilan à retourner */ - GExeFormat *exe; /* Autre version du format */ - vmpa2t section_start; /* Zone de départ des sections */ - - exe = G_EXE_FORMAT(format); - - result = read_dos_image_header(format, &format->dos_header); - if (!result) goto error; - - result = read_pe_nt_header(format, &format->nt_headers, §ion_start); - if (!result) goto error; + const image_nt_headers_t *result; /* Informations à retourner */ - format->sections = read_all_pe_sections(format, §ion_start); - if (format->sections == NULL) goto error; - - extract_pe_rich_header(format); - - result = load_pe_symbols(format, gid, status); - if (!result) goto error; - - result = g_executable_format_complete_loading(exe, gid, status); - if (!result) goto error; - - error: + result = &format->nt_headers; return result; @@ -372,76 +347,73 @@ static bool g_pe_format_analyze(GPeFormat *format, wgroup_id_t gid, GtkStatusSta /****************************************************************************** * * -* Paramètres : format = informations chargées à consulter. * +* Paramètres : format = format en place à consulter. * * * -* Description : Informe quant au boutisme utilisé. * +* Description : Indique si le format PE est en 32 bits ou en 64 bits. * * * -* Retour : Indicateur de boutisme. * +* Retour : true si le format est en 32 bits, false sinon. * * * * Remarques : - * * * ******************************************************************************/ -static SourceEndian g_pe_format_get_endianness(const GPeFormat *format) +bool g_pe_format_get_is_32b(const GPeFormat *format) { - SourceEndian result; /* Boutisme à retourner */ + bool result; /* Nature à retourner */ - /** - * Sauf exception, le boutisme est généralement petit. - * - * Cf. https://reverseengineering.stackexchange.com/a/17923 - * https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions?view=msvc-160#endianness - */ + assert(format->loaded); - result = SRE_LITTLE; + switch (format->nt_headers.optional_header.header_32.magic) + { + case IMAGE_NT_OPTIONAL_HDR32_MAGIC: + result = true; + break; + case IMAGE_NT_OPTIONAL_HDR64_MAGIC: + result = false; + break; + default: + result = true; + assert(false); + break; + } return result; + } /****************************************************************************** * * -* Paramètres : format = informations chargées à consulter. * +* Paramètres : format = format en place à consulter. * +* count = taille (fixe) du tableau renvoyé. [OUT] * * * -* Description : Indique le type d'architecture visée par le format. * +* Description : Offre un raccourci vers les répertoires du format PE. * * * -* Retour : Identifiant de l'architecture ciblée par le format. * +* Retour : Pointeur vers le tableau des répertoires. * * * * Remarques : - * * * ******************************************************************************/ -static const char *g_pe_format_get_target_machine(const GPeFormat *format) +const image_data_directory_t *g_pe_format_get_directories(const GPeFormat *format, size_t *count) { - const char *result; /* Identifiant à retourner */ + const image_data_directory_t *result; /* Liste à retourner */ - switch (format->nt_headers.file_header.machine) + if (g_pe_format_get_is_32b(format)) { - case IMAGE_FILE_MACHINE_I386: - result = "i386"; - break; + result = format->nt_headers.optional_header.header_32.data_directory; - case IMAGE_FILE_MACHINE_R3000: - case IMAGE_FILE_MACHINE_R4000: - case IMAGE_FILE_MACHINE_R10000: - case IMAGE_FILE_MACHINE_WCEMIPSV2: - case IMAGE_FILE_MACHINE_MIPS16: - case IMAGE_FILE_MACHINE_MIPSFPU: - case IMAGE_FILE_MACHINE_MIPSFPU16: - result = "mips"; - break; + if (count != NULL) + *count = format->nt_headers.optional_header.header_32.number_of_rva_and_sizes; - case IMAGE_FILE_MACHINE_ARM: - case IMAGE_FILE_MACHINE_THUMB: - case IMAGE_FILE_MACHINE_ARMNT: - result = "armv7"; - break; + } + else + { + result = format->nt_headers.optional_header.header_64.data_directory; - case IMAGE_FILE_MACHINE_UNKNOWN: - default: - result = NULL; - break; + if (count != NULL) + *count = format->nt_headers.optional_header.header_64.number_of_rva_and_sizes; } @@ -452,140 +424,174 @@ static const char *g_pe_format_get_target_machine(const GPeFormat *format) /****************************************************************************** * * -* Paramètres : format = description de l'exécutable à consulter. * -* addr = adresse principale trouvée si possible. [OUT] * +* Paramètres : format = format en place à consulter. * +* index = indice du répertoire visé. * * * -* Description : Fournit l'adresse principale associée à un format Elf. * +* Description : Extrait le contenu d'un répertoire du format PE. * * * -* Retour : Bilan des recherches. * +* Retour : Pointeur vers un contenu chargé ou NULL. * * * * Remarques : - * * * ******************************************************************************/ -static bool g_pe_format_get_main_address(GPeFormat *format, vmpa2t *addr) +void *g_pe_format_get_directory(const GPeFormat *format, size_t index) { - bool result; /* Bilan à retourner */ - GBinSymbol *symbol; /* Point d'entrée trouvé */ - GBinFormat *base; /* Version d'instance parente */ - const mrange_t *range; /* Emplacement de ce point */ - - result = false; - symbol = NULL; + void *result; /* Données à retourner */ + size_t max; /* Quantité de répertoires */ + const image_data_directory_t *dir; /* Localisation du répertoire */ + vmpa2t pos; /* Tête de lecture */ + bool status; /* Bilan d'un traitement */ + image_export_directory_t *export; /* Répertoire de type 0 */ + image_import_descriptor_t *imports; /* Répertoire de type 1 */ + size_t imported_count; /* Quantité de DLL requises */ - base = G_BIN_FORMAT(format); + result = NULL; - if (g_binary_format_find_symbol_by_label(base, "main", &symbol)) - goto done; + dir = g_pe_format_get_directories(format, &max); - if (g_binary_format_find_symbol_by_label(base, "_start", &symbol)) - goto done; + if (index >= max) + goto exit; - if (g_binary_format_find_symbol_by_label(base, "entry_point", &symbol)) - goto done; + dir += index; - done: + status = g_executable_format_translate_address_into_vmpa(G_EXECUTABLE_FORMAT(format), + dir->virtual_address, &pos); + if (!status) goto exit; - if (symbol != NULL) + switch (index) { - result = true; + case IMAGE_DIRECTORY_ENTRY_EXPORT: + + export = malloc(sizeof(image_export_directory_t)); + + status = read_pe_image_export_directory(format, &pos, export); + + if (!status) + { + free(export); + goto exit; + } - range = g_binary_symbol_get_range(symbol); + result = export; + break; - copy_vmpa(addr, get_mrange_addr(range)); + case IMAGE_DIRECTORY_ENTRY_IMPORT: - g_object_unref(G_OBJECT(symbol)); + imports = NULL; + imported_count = 0; + + do + { + imports = realloc(imports, ++imported_count * sizeof(image_import_descriptor_t)); + + status = read_pe_image_import_descriptor(format, &pos, imports + (imported_count - 1)); + + if (!status) + { + free(imports); + goto exit; + } + + } + while (imports[imported_count - 1].original_first_thunk != 0); + + result = imports; + break; } + exit: + return result; } -#if 0 - /****************************************************************************** * * -* Paramètres : format = informations chargées à consulter. * +* Paramètres : format = format en place à consulter. * +* count = taille (fixe) du tableau renvoyé. [OUT] * * * -* Description : Etend la définition des portions au sein d'un binaire. * +* Description : Offre un raccourci vers les sections du format PE. * * * -* Retour : - * +* Retour : Pointeur vers la liste des sections. * * * * Remarques : - * * * ******************************************************************************/ -static void g_pe_format_refine_portions(GPeFormat *format) +const image_section_header_t *g_pe_format_get_sections(const GPeFormat *format, size_t *count) { + const image_section_header_t *result; /* Liste à retourner */ + + if (count != NULL) + *count = format->nt_headers.file_header.number_of_sections; + + result = format->sections; + + return result; } -#endif +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * -* addr = adresse virtuelle à retrouver. * -* pos = position correspondante. [OUT] * * * -* Description : Fournit l'emplacement correspondant à une adresse virtuelle. * +* Description : Indique la désignation interne du format. * * * -* Retour : Bilan de l'opération. * +* Retour : Désignation du format. * * * * Remarques : - * * * ******************************************************************************/ -bool g_pe_format_translate_address_into_vmpa_using_portions(GPeFormat *format, virt_t addr, vmpa2t *pos) +static char *g_pe_format_get_key(const GPeFormat *format) { - bool result; /* Bilan à retourner */ - uint16_t i; /* Boucle de parcours */ - const image_section_header *section; /* Section à consulter */ - phys_t diff; /* Décallage à appliquer */ - - result = false; - - for (i = 0; i < format->nt_headers.file_header.number_of_sections && !result; i++) - { - section = &format->sections[i]; + char *result; /* Désignation à retourner */ - if (addr < section->virtual_address) - continue; + result = strdup("pe"); - if (addr >= (section->virtual_address + section->size_of_raw_data)) - continue; + return result; - diff = addr - section->virtual_address; +} - init_vmpa(pos, section->pointer_to_raw_data + diff, addr); - result = true; +/****************************************************************************** +* * +* Paramètres : format = description de l'exécutable à consulter. * +* * +* Description : Fournit une description humaine du format. * +* * +* Retour : Description du format. * +* * +* Remarques : - * +* * +******************************************************************************/ - } +static char *g_pe_format_get_description(const GPeFormat *format) +{ + char *result; /* Désignation à retourner */ - //printf(" // trans // %x -> %x (valid? %d)\n", (unsigned int)addr, (unsigned int)pos->physical, result); + result = strdup("Portable Executable"); return result; } - - -#if 0 - - /****************************************************************************** * * -* Paramètres : format = description de l'exécutable à consulter. * -* name = nom de la section recherchée. * -* range = emplacement en mémoire à renseigner. [OUT] * +* Paramètres : format = format chargé dont l'analyse est lancée. * * * -* Description : Fournit l'emplacement d'une section donnée. * +* Description : Assure l'interprétation d'un format en différé. * * * * Retour : Bilan de l'opération. * * * @@ -593,51 +599,82 @@ bool g_pe_format_translate_address_into_vmpa_using_portions(GPeFormat *format, v * * ******************************************************************************/ -static bool g_pe_format_get_section_range_by_name(const GPeFormat *format, const char *name, mrange_t *range) +static bool g_pe_format_analyze(GPeFormat *format) { bool result; /* Bilan à retourner */ - phys_t offset; /* Position physique de section*/ - phys_t size; /* Taille de la section trouvée*/ - virt_t address; /* Adresse virtuelle de section*/ - vmpa2t tmp; /* Adresse à initialiser */ + vmpa2t start; /* Zone de départ des sections */ + uint16_t count; /* Quantité de sections */ + uint16_t i; /* Boucle de parcours */ - result = find_elf_section_content_by_name(format, name, &offset, &size, &address); + result = read_dos_image_header(format, &format->dos_header); + if (!result) goto error; - if (result) + result = read_pe_nt_header(format, &format->nt_headers, &start); + if (!result) goto error; + + /* Chargement des définitions des sections déclarées */ + + copy_vmpa(&format->sections_start, &start); + + count = format->nt_headers.file_header.number_of_sections; + + format->sections = malloc(count * sizeof(image_section_header_t)); + + for (i = 0; i < count; i++) { - init_vmpa(&tmp, offset, address); - init_mrange(range, &tmp, size); + result = read_pe_image_section_header(format, &start, &format->sections[i]); + if (!result) goto error; } - return result; + /* Passage de relais */ -} -#endif + if (result) + result = G_KNOWN_FORMAT_CLASS(g_pe_format_parent_class)->analyze(G_KNOWN_FORMAT(format)); + + + + +#if 0 + extract_pe_rich_header(format); + result = load_pe_symbols(format, gid, status); + if (!result) goto error; + +#endif + error: + return result; +} /****************************************************************************** * * -* Paramètres : format = format en place à consulter. * +* Paramètres : format = informations chargées à consulter. * * * -* Description : Présente l'en-tête MS-DOS du format chargé. * +* Description : Informe quant au boutisme utilisé. * * * -* Retour : Pointeur vers la description principale. * +* Retour : Indicateur de boutisme. * * * * Remarques : - * * * ******************************************************************************/ -const image_dos_header *g_pe_format_get_dos_header(const GPeFormat *format) +static SourceEndian g_pe_format_get_endianness(const GPeFormat *format) { - const image_dos_header *result; /* Informations à retourner */ + SourceEndian result; /* Boutisme à retourner */ - result = &format->dos_header; + /** + * Sauf exception, le boutisme est généralement petit. + * + * Cf. https://reverseengineering.stackexchange.com/a/17923 + * https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions?view=msvc-160#endianness + */ + + result = SRE_LITTLE; return result; @@ -646,21 +683,43 @@ const image_dos_header *g_pe_format_get_dos_header(const GPeFormat *format) /****************************************************************************** * * -* Paramètres : format = format en place à consulter. * +* Paramètres : format = description de l'exécutable à consulter. * +* name = nom de la section recherchée. * +* range = emplacement en mémoire à renseigner. [OUT] * * * -* Description : Présente l'en-tête NT du format chargé. * +* Description : Fournit l'emplacement d'une section donnée. * * * -* Retour : Pointeur vers la description principale. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -const image_nt_headers *g_pe_format_get_nt_headers(const GPeFormat *format) +static bool g_pe_format_find_section_range_by_name(const GPeFormat *format, const char *name, mrange_t *range) { - const image_nt_headers *result; /* Informations à retourner */ + bool result; /* Bilan à retourner */ + uint16_t i; /* Boucle de parcours */ + image_section_header_t *section; /* Section à traiter */ + char tmp[IMAGE_SIZEOF_SHORT_NAME + 1]; /* Nom de la section */ + vmpa2t addr; /* Emplacement dans le binaire */ - result = &format->nt_headers; + result = false; + + for (i = 0, section = format->sections; i < format->nt_headers.file_header.number_of_sections; i++, section++) + { + memcpy(tmp, section->name, IMAGE_SIZEOF_SHORT_NAME); + tmp[IMAGE_SIZEOF_SHORT_NAME] = '\0'; + + if (strcmp(name, tmp) != 0) + continue; + + init_vmpa(&addr, section->pointer_to_raw_data, section->virtual_address); + init_mrange(range, &addr, section->size_of_raw_data); + + result = true; + break; + + } return result; @@ -669,75 +728,78 @@ const image_nt_headers *g_pe_format_get_nt_headers(const GPeFormat *format) /****************************************************************************** * * -* Paramètres : format = format en place à consulter. * +* Paramètres : format = informations chargées à consulter. * * * -* Description : Indique si le format PE est en 32 bits ou en 64 bits. * +* Description : Indique le type d'architecture visée par le format. * * * -* Retour : true si le format est en 32 bits, false sinon. * +* Retour : Identifiant de l'architecture ciblée par le format. * * * * Remarques : - * * * ******************************************************************************/ -bool g_pe_format_get_is_32b(const GPeFormat *format) +static char *g_pe_format_get_target_machine(const GPeFormat *format) { - bool result; /* Nature à retourner */ - - assert(format->loaded); + char *result; /* Identifiant à retourner */ - switch (format->nt_headers.optional_header.header_32.magic) + switch (format->nt_headers.file_header.machine) { - case IMAGE_NT_OPTIONAL_HDR32_MAGIC: - result = true; + case IMAGE_FILE_MACHINE_I386: + result = strdup("i386"); break; - case IMAGE_NT_OPTIONAL_HDR64_MAGIC: - result = false; + + case IMAGE_FILE_MACHINE_R3000: + case IMAGE_FILE_MACHINE_R4000: + case IMAGE_FILE_MACHINE_R10000: + case IMAGE_FILE_MACHINE_WCEMIPSV2: + case IMAGE_FILE_MACHINE_MIPS16: + case IMAGE_FILE_MACHINE_MIPSFPU: + case IMAGE_FILE_MACHINE_MIPSFPU16: + result = strdup("mips"); break; + + case IMAGE_FILE_MACHINE_ARM: + case IMAGE_FILE_MACHINE_THUMB: + case IMAGE_FILE_MACHINE_ARMNT: + result = strdup("armv7"); + break; + + case IMAGE_FILE_MACHINE_UNKNOWN: default: - result = true; - assert(false); + result = NULL; break; + } return result; - } /****************************************************************************** * * -* Paramètres : format = format en place à consulter. * -* count = taille (fixe) du tableau renvoyé. [OUT] * +* Paramètres : format = description de l'exécutable à consulter. * +* addr = adresse principale trouvée si possible. [OUT] * * * -* Description : Offre un raccourci vers les répertoires du format PE. * +* Description : Fournit l'adresse principale associée à un format. * * * -* Retour : Pointeur vers le tableau des répertoires. * +* Retour : Bilan des recherches. * * * * Remarques : - * * * ******************************************************************************/ -const image_data_directory *g_pe_format_get_directories(const GPeFormat *format, size_t *count) +static bool g_pe_format_get_main_address(GPeFormat *format, vmpa2t *addr) { - const image_data_directory *result; /* Liste à retourner */ + bool result; /* Bilan à retourner */ + virt_t ep; /* Point d'entrée */ if (g_pe_format_get_is_32b(format)) - { - result = format->nt_headers.optional_header.header_32.data_directory; - - if (count != NULL) - *count = format->nt_headers.optional_header.header_32.number_of_rva_and_sizes; - - } + ep = format->nt_headers.optional_header.header_32.address_of_entry_point; else - { - result = format->nt_headers.optional_header.header_64.data_directory; - - if (count != NULL) - *count = format->nt_headers.optional_header.header_64.number_of_rva_and_sizes; + ep = format->nt_headers.optional_header.header_64.address_of_entry_point; - } + result = g_executable_format_translate_address_into_vmpa(G_EXECUTABLE_FORMAT(format), ep, addr); return result; @@ -746,83 +808,75 @@ const image_data_directory *g_pe_format_get_directories(const GPeFormat *format, /****************************************************************************** * * -* Paramètres : format = format en place à consulter. * -* index = indice du répertoire visé. * +* Paramètres : format = informations chargées à consulter. * * * -* Description : Extrait le contenu d'un répertoire du format PE. * +* Description : Etend la définition des portions au sein d'un binaire. * * * -* Retour : Pointeur vers un contenu chargé ou NULL. * +* Retour : Bilan des définitions de portions. * * * * Remarques : - * * * ******************************************************************************/ -void *g_pe_format_get_directory(const GPeFormat *format, size_t index) +static bool g_pe_format_refine_portions(GPeFormat *format) { - void *result; /* Données à retourner */ - size_t max; /* Quantité de répertoires */ - const image_data_directory *dir; /* Localisation du répertoire */ - vmpa2t pos; /* Tête de lecture */ - bool status; /* Bilan d'un traitement */ - image_export_directory *export; /* Répertoire de type 0 */ - image_import_descriptor *imports; /* Répertoire de type 1 */ - size_t imported_count; /* Quantité de DLL requises */ + bool result; /* Bilan à retourner */ + vmpa2t origin; /* Origine d'une définition */ + uint16_t i; /* Boucle de parcours */ + image_section_header_t *section; /* Section à traiter */ + vmpa2t addr; /* Emplacement dans le binaire */ + GBinaryPortion *portion; /* Nouvelle portion de binaire */ + char name[IMAGE_SIZEOF_SHORT_NAME + 1]; /* Nom de la section */ + PortionAccessRights rights; /* Droits d'accès à la section */ - result = NULL; + result = true; - dir = g_pe_format_get_directories(format, &max); + copy_vmpa(&origin, &format->sections_start); - if (index >= max) - goto exit; + for (i = 0, section = format->sections; i < format->nt_headers.file_header.number_of_sections; i++, section++) + { + if (section->pointer_to_raw_data == 0) + continue; - dir += index; + /* Emplacement */ - status = g_exe_format_translate_address_into_vmpa(G_EXE_FORMAT(format), dir->virtual_address, &pos); - if (!status) goto exit; + init_vmpa(&addr, section->pointer_to_raw_data, section->virtual_address); - switch (index) - { - case IMAGE_DIRECTORY_ENTRY_EXPORT: + portion = g_binary_portion_new(&addr, section->size_of_raw_data); - export = malloc(sizeof(image_export_directory)); + /* Nom */ - status = read_pe_image_export_directory(format, &pos, export); + memcpy(name, section->name, IMAGE_SIZEOF_SHORT_NAME); + name[IMAGE_SIZEOF_SHORT_NAME] = '\0'; - if (!status) - { - free(export); - goto exit; - } + g_binary_portion_set_desc(portion, name); - result = export; - break; + /* Droits d'accès */ - case IMAGE_DIRECTORY_ENTRY_IMPORT: + rights = PAC_NONE; - imports = NULL; - imported_count = 0; + if (section->characteristics & IMAGE_SCN_MEM_EXECUTE) + rights |= PAC_EXEC; - do - { - imports = realloc(imports, ++imported_count * sizeof(image_import_descriptor)); + if (section->characteristics & IMAGE_SCN_MEM_READ) + rights |= PAC_READ; - status = read_pe_image_import_descriptor(format, &pos, imports + (imported_count - 1)); + if (section->characteristics & IMAGE_SCN_MEM_WRITE) + rights |= PAC_WRITE; - if (!status) - { - free(imports); - goto exit; - } + g_binary_portion_set_rights(portion, rights); - } - while (imports[imported_count - 1].original_first_thunk != 0); + /* Inclusion finale */ - result = imports; - break; + result = g_executable_format_include_portion(G_EXECUTABLE_FORMAT(format), portion, &origin); - } + unref_object(portion); - exit: + if (!result) break; + + advance_vmpa(&origin, sizeof(image_section_header_t)); + + } return result; diff --git a/plugins/pe/format.h b/plugins/pe/format.h index a48e04d..829f4de 100644 --- a/plugins/pe/format.h +++ b/plugins/pe/format.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * pe.h - prototypes pour le support du format Portable Executable + * format.h - prototypes pour le support du format Portable Executable * - * Copyright (C) 2010-2017 Cyrille Bagard + * Copyright (C) 2010-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,31 +25,20 @@ #define _PLUGINS_PE_FORMAT_H -#include <glib-object.h> #include <stdbool.h> #include <analysis/content.h> -#include <format/executable.h> +#include <glibext/helpers.h> -#include "pe_def.h" +#include "pe-def.h" -#define G_TYPE_PE_FORMAT g_pe_format_get_type() -#define G_PE_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_PE_FORMAT, GPeFormat)) -#define G_IS_PE_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_PE_FORMAT)) -#define G_PE_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_PE_FORMAT, GPeFormatClass)) -#define G_IS_PE_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_PE_FORMAT)) -#define G_PE_FORMAT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_PE_FORMAT, GPeFormatClass)) +#define G_TYPE_PE_FORMAT (g_pe_format_get_type()) - -/* Format d'exécutable PE (instance) */ -typedef struct _GPeFormat GPeFormat; - -/* Format d'exécutable PE (classe) */ -typedef struct _GPeFormatClass GPeFormatClass; +DECLARE_GTYPE(GPeFormat, g_pe_format, G, PE_FORMAT); /* Valide un contenu comme étant un format PE. */ @@ -59,23 +48,26 @@ bool check_pe_format(const GBinContent *); GType g_pe_format_get_type(void); /* Prend en charge un nouveau format PE. */ -GExeFormat *g_pe_format_new(GBinContent *); +GPeFormat *g_pe_format_new(GBinContent *); /* Présente l'en-tête MS-DOS du format chargé. */ -const image_dos_header *g_pe_format_get_dos_header(const GPeFormat *); +const image_dos_header_t *g_pe_format_get_dos_header(const GPeFormat *); /* Présente l'en-tête NT du format chargé. */ -const image_nt_headers *g_pe_format_get_nt_headers(const GPeFormat *); +const image_nt_headers_t *g_pe_format_get_nt_headers(const GPeFormat *); /* Indique si le format PE est en 32 bits ou en 64 bits. */ bool g_pe_format_get_is_32b(const GPeFormat *); /* Offre un raccourci vers les répertoires du format PE. */ -const image_data_directory *g_pe_format_get_directories(const GPeFormat *, size_t *); +const image_data_directory_t *g_pe_format_get_directories(const GPeFormat *, size_t *); /* Extrait le contenu d'un répertoire du format PE. */ void *g_pe_format_get_directory(const GPeFormat *, size_t); +/* Offre un raccourci vers les sections du format PE. */ +const image_section_header_t *g_pe_format_get_sections(const GPeFormat *, size_t *); + #endif /* _PLUGINS_PE_FORMAT_H */ diff --git a/plugins/pe/pe_def.h b/plugins/pe/pe-def.h index 62b4607..4812897 100644 --- a/plugins/pe/pe_def.h +++ b/plugins/pe/pe-def.h @@ -31,6 +31,7 @@ /** * Références : * + * - https://learn.microsoft.com/en-us/windows/win32/debug/pe-format * - https://fr.wikipedia.org/wiki/Portable_Executable#En-tête_MZ_sous_MS-DOS * - https://www.nirsoft.net/kernel_struct/vista/IMAGE_DOS_HEADER.html * @@ -42,7 +43,7 @@ /* En-tête DOS */ -typedef struct _image_dos_header +typedef struct _image_dos_header_t { uint16_t e_magic; /* Numéro magique */ uint16_t e_cblp; /* Octets de la dernière page */ @@ -64,7 +65,7 @@ typedef struct _image_dos_header uint16_t e_res2[10]; /* Mots réservés */ uint32_t e_lfanew; /* Décalage de bon en-tête */ -} image_dos_header; +} image_dos_header_t; /* Archtectures supportées */ @@ -123,7 +124,7 @@ typedef struct _image_dos_header #define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 /* Octets inv. ; obsolète */ /* Première en-tête du "vrai" format */ -typedef struct _image_file_header +typedef struct _image_file_header_t { uint16_t machine; /* Type de machine visée */ uint16_t number_of_sections; /* Nombre de sections */ @@ -133,7 +134,7 @@ typedef struct _image_file_header uint16_t size_of_optional_header; /* Taille de l'en-tête n°2 */ uint16_t characteristics; /* Propriétés de l'image */ -} image_file_header; +} image_file_header_t; @@ -148,12 +149,12 @@ typedef struct _image_file_header */ /* Zone de données Windows */ -typedef struct _image_data_directory +typedef struct _image_data_directory_t { uint32_t virtual_address; /* Adresse de la table */ uint32_t size; /* Taille de la table */ -} image_data_directory; +} image_data_directory_t; // Directory Entries #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory @@ -182,7 +183,7 @@ typedef struct _image_data_directory /* Seconde en-tête, optionnelle */ -typedef struct _image_optional_header_32 +typedef struct _image_optional_header_32_t { uint16_t magic; /* Type de binaire manipulé */ uint8_t major_linker_version; /* Version majeure du linker */ @@ -214,11 +215,11 @@ typedef struct _image_optional_header_32 uint32_t size_of_heap_commit; /* Taille de tas au démarrage */ uint32_t loader_flags; /* Champ obslète */ uint32_t number_of_rva_and_sizes; /* Nombre d'entrées suivantes */ - image_data_directory data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; + image_data_directory_t data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; -} image_optional_header_32; +} image_optional_header_32_t; -typedef struct _image_optional_header_64 +typedef struct _image_optional_header_64_t { uint16_t magic; /* Type de binaire manipulé */ @@ -250,16 +251,16 @@ typedef struct _image_optional_header_64 uint64_t size_of_heap_commit; /* Taille de tas au démarrage */ uint32_t loader_flags; /* Champ obslète */ uint32_t number_of_rva_and_sizes; /* Nombre d'entrées suivantes */ - image_data_directory data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; + image_data_directory_t data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; -} image_optional_header_64; +} image_optional_header_64_t; -typedef union _image_optional_header +typedef union _image_optional_header_t { - image_optional_header_32 header_32; /* Version 32 bits */ - image_optional_header_64 header_64; /* Version 64 bits */ + image_optional_header_32_t header_32; /* Version 32 bits */ + image_optional_header_64_t header_64; /* Version 64 bits */ -} image_optional_header; +} image_optional_header_t; @@ -302,13 +303,13 @@ typedef union _image_optional_header /* Résumé global */ -typedef struct _image_nt_headers +typedef struct _image_nt_headers_t { uint32_t signature; /* Numéro magique */ - image_file_header file_header; /* En-tête n°1 */ - image_optional_header optional_header; /* En-tête n°2 */ + image_file_header_t file_header; /* En-tête n°1 */ + image_optional_header_t optional_header;/* En-tête n°2 */ -} image_nt_headers; +} image_nt_headers_t; @@ -322,7 +323,7 @@ typedef struct _image_nt_headers #define IMAGE_SIZEOF_SHORT_NAME 8 /* Description d'une section */ -typedef struct _image_section_header +typedef struct _image_section_header_t { char name[IMAGE_SIZEOF_SHORT_NAME]; /* Nom de la section */ @@ -331,7 +332,7 @@ typedef struct _image_section_header uint32_t physical_address; /* Adresse physique */ uint32_t virtual_size; /* Taille en mémoire */ - } misc; + }; uint32_t virtual_address; /* Adresse en mémoire */ uint32_t size_of_raw_data; /* Taille de données définies */ @@ -342,7 +343,7 @@ typedef struct _image_section_header uint16_t number_of_line_numbers; /* Quantité de numéros de ligne*/ uint32_t characteristics; /* Caractéristiques */ -} image_section_header; +} image_section_header_t; /* Détails des caractéristiques d'une image (champ 'characteristics') */ #define IMAGE_SCN_UNKNOWN_0 0x00000000 /* Réservé */ @@ -400,7 +401,7 @@ typedef struct _image_section_header */ /* Répertoire des importations */ -typedef struct _image_export_directory +typedef struct _image_export_directory_t { uint32_t characteristics; /* Zéro !? */ uint32_t time_date_stamp; /* Date de création du fichier */ @@ -414,7 +415,7 @@ typedef struct _image_export_directory uint32_t address_of_names; /* Liste de RVA de noms */ uint32_t address_of_name_ordinals; /* Liste de RVA d'ordinaux */ -} image_export_directory; +} image_export_directory_t; /** @@ -427,7 +428,7 @@ typedef struct _image_export_directory */ /* Point de départ de la chaîne des importations */ -typedef struct _image_import_descriptor +typedef struct _image_import_descriptor_t { uint32_t original_first_thunk; uint32_t time_date_stamp; @@ -435,7 +436,7 @@ typedef struct _image_import_descriptor uint32_t module_name; uint32_t first_thunk; -} image_import_descriptor; +} image_import_descriptor_t; diff --git a/plugins/pe/python/Makefile.am b/plugins/pe/python/Makefile.am index 5949821..4a70769 100644 --- a/plugins/pe/python/Makefile.am +++ b/plugins/pe/python/Makefile.am @@ -1,14 +1,16 @@ noinst_LTLIBRARIES = libpepython.la +# libpepython_la_SOURCES = \ +# constants.h constants.c \ +# routine.h routine.c + libpepython_la_SOURCES = \ - constants.h constants.c \ format.h format.c \ module.h module.c \ - routine.h routine.c \ translate.h translate.c -libpepython_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ +libpepython_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT diff --git a/plugins/pe/python/constants.c b/plugins/pe/python/constants.c index 9b4942d..bb10a7e 100644 --- a/plugins/pe/python/constants.c +++ b/plugins/pe/python/constants.c @@ -29,7 +29,7 @@ #include "../pe_def.h" -#include "../routine.h" +//#include "../routine.h" @@ -101,7 +101,7 @@ bool define_python_pe_format_constants(PyTypeObject *type) * Remarques : - * * * ******************************************************************************/ - +#if 0 bool define_python_pe_exported_routine_constants(PyTypeObject *type) { bool result; /* Bilan à retourner */ @@ -141,3 +141,4 @@ bool define_python_pe_exported_routine_constants(PyTypeObject *type) return result; } +#endif diff --git a/plugins/pe/python/constants.h b/plugins/pe/python/constants.h index 25b0adb..fe4293c 100644 --- a/plugins/pe/python/constants.h +++ b/plugins/pe/python/constants.h @@ -35,7 +35,7 @@ bool define_python_pe_format_constants(PyTypeObject *); /* Définit les constantes pour les routines du format PE. */ -bool define_python_pe_exported_routine_constants(PyTypeObject *); +//bool define_python_pe_exported_routine_constants(PyTypeObject *); diff --git a/plugins/pe/python/format.c b/plugins/pe/python/format.c index 4bbb99a..71d3305 100644 --- a/plugins/pe/python/format.c +++ b/plugins/pe/python/format.c @@ -28,25 +28,22 @@ #include <pygobject.h> -#include <format/known.h> -#include <plugins/dt.h> #include <plugins/pychrysalide/helpers.h> #include <plugins/pychrysalide/analysis/content.h> #include <plugins/pychrysalide/format/executable.h> -#include "constants.h" +//#include "constants.h" #include "translate.h" -#include "../format.h" -#include "../rich.h" +#include "../format-int.h" +//#include "../rich.h" /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_pe_format_new(PyTypeObject *, PyObject *, PyObject *); +CREATE_DYN_CONSTRUCTOR(pe_format, G_TYPE_PE_FORMAT); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_pe_format_init(PyObject *, PyObject *, PyObject *); @@ -59,9 +56,15 @@ static int py_pe_format_init(PyObject *, PyObject *, PyObject *); /* Présente l'en-tête MS-DOS du format chargé. */ static PyObject *py_pe_format_get_dos_header(PyObject *, void *); +/* Présente l'en-tête NT du format chargé. */ +static PyObject *py_pe_format_get_nt_headers(PyObject *, void *); + /* Offre un raccourci vers les répertoires du format PE. */ static PyObject *py_pe_format_get_directories(PyObject *, void *); +/* Offre un raccourci vers les sections du format PE. */ +static PyObject *py_pe_format_get_sections(PyObject *, void *); + /* Présente l'en-tête enrichi du format chargé. */ static PyObject *py_pe_format_get_rich_header(PyObject *, void *); @@ -80,66 +83,6 @@ static PyObject *py_pe_format_get_comp_ids(PyObject *, void *); /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* 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 : - * -* * -******************************************************************************/ - -static PyObject *py_pe_format_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type de base à dériver */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ - - /* Validations diverses */ - - base = get_python_pe_format_type(); - - 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(G_TYPE_PE_FORMAT, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - 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 : self = objet à initialiser (théoriquement). * * args = arguments fournis à l'appel. * * kwds = arguments de type key=val fournis. * @@ -183,7 +126,8 @@ static int py_pe_format_init(PyObject *self, PyObject *args, PyObject *kwds) format = G_PE_FORMAT(pygobject_get(self)); - g_known_format_set_content(G_KNOWN_FORMAT(format), content); + if (!g_pe_format_create(format, content)) + return -1; return 0; @@ -223,7 +167,6 @@ static PyObject *py_pe_format_get_dos_header(PyObject *self, void *closure) "\n" \ "The provided information is composed of the following" \ " properties :\n" \ - "\n" \ "* e_magic;\n" \ "* e_cblp;\n" \ "* e_cp;\n" \ @@ -259,6 +202,52 @@ static PyObject *py_pe_format_get_dos_header(PyObject *self, void *closure) * Paramètres : self = format en place à consulter. * * closure = non utilisé ici. * * * +* Description : Présente l'en-tête NT du format chargé. * +* * +* Retour : Structure Python créée pour l'occasion. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_pe_format_get_nt_headers(PyObject *self, void *closure) +{ + PyObject *result; /* Trouvaille à retourner */ + GPeFormat *format; /* Version GLib du format */ + +#define PE_FORMAT_NT_HEADERS_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + nt_headers, py_pe_format, \ + "NT headers of the file format.\n" \ + "\n" \ + "This property is a pychrysalide.StructObject instance." \ + "\n" \ + "The provided information is composed of the following" \ + " properties :\n" \ + "* signature;\n" \ + "* file_header;\n" \ + "* optional_header.\n" \ + "\n" \ + "The last two fields are pychrysalide.StructObject" \ + " which contain more fields. These fields can be" \ + " enumerated with the keys() method (for instance:" \ + " *mype.nt_headers.file_header.keys()*).\n" \ +) + + format = G_PE_FORMAT(pygobject_get(self)); + + result = translate_pe_nt_headers_to_python(format, g_pe_format_get_nt_headers(format)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = format en place à consulter. * +* closure = non utilisé ici. * +* * * Description : Offre un raccourci vers les répertoires du format PE. * * * * Retour : Structure Python créée pour l'occasion. * @@ -272,7 +261,7 @@ static PyObject *py_pe_format_get_directories(PyObject *self, void *closure) PyObject *result; /* Trouvaille à retourner */ GPeFormat *format; /* Version GLib du format */ size_t count; /* Quantité de répertoires */ - const image_data_directory *directories; /* Répertoires à exporter */ + const image_data_directory_t *directories; /* Répertoires à exporter */ size_t i; /* Boucle de parcours */ PyObject *item; /* Elément de tableau */ int ret; /* Bilan d'une mise en place */ @@ -323,6 +312,76 @@ static PyObject *py_pe_format_get_directories(PyObject *self, void *closure) * Paramètres : self = format en place à consulter. * * closure = non utilisé ici. * * * +* Description : Offre un raccourci vers les sections du format PE. * +* * +* Retour : Structure Python créée pour l'occasion. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_pe_format_get_sections(PyObject *self, void *closure) +{ + PyObject *result; /* Trouvaille à retourner */ + GPeFormat *format; /* Version GLib du format */ + size_t count; /* Quantité de répertoires */ + const image_section_header_t *sections; /* Sections à exporter */ + size_t i; /* Boucle de parcours */ + PyObject *item; /* Elément de tableau */ + int ret; /* Bilan d'une mise en place */ + +#define PE_FORMAT_SECTIONS_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + sections, py_pe_format, \ + "Shortcut to the definitions of all PE format sections.\n" \ + "\n" \ + "This property is a pychrysalide.StructObject instance.\n" \ + "\n" \ + "Each returned item is composed of the following properties :\n"\ + "\n" \ + "* name;\n" \ + "* misc.virtual_size;\n" \ + "* virtual_address;\n" \ + "* size_of_raw_data;\n" \ + "* pointer_to_raw_data;\n" \ + "* pointer_to_relocations;\n" \ + "* pointer_to_line_numbers;\n" \ + "* number_of_relocations;\n" \ + "* number_of_line_numbers;\n" \ + "* characteristics." \ +) + + format = G_PE_FORMAT(pygobject_get(self)); + + sections = g_pe_format_get_sections(format, &count); + + result = PyTuple_New(count); + + for (i = 0; i < count; i++) + { + item = translate_pe_section_header_to_python(format, sections + i); + + ret = PyTuple_SetItem(result, i, item); + + if (ret != 0) + { + Py_DECREF(result); + result = NULL; + break; + } + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = format en place à consulter. * +* closure = non utilisé ici. * +* * * Description : Présente l'en-tête enrichi du format chargé. * * * * Retour : Tableau de valeurs brutes d'information. * @@ -487,7 +546,9 @@ PyTypeObject *get_python_pe_format_type(void) static PyGetSetDef py_pe_format_getseters[] = { PE_FORMAT_DOS_HEADER_ATTRIB, + PE_FORMAT_NT_HEADERS_ATTRIB, PE_FORMAT_DIRECTORIES_ATTRIB, + PE_FORMAT_SECTIONS_ATTRIB, PE_FORMAT_RICH_HEADER_ATTRIB, PE_FORMAT_RICH_HEADER_CHECKSUM_ATTRIB, PE_FORMAT_COMP_IDS_ATTRIB, @@ -545,8 +606,8 @@ bool register_python_pe_format(PyObject *module) if (!register_class_for_pygobject(dict, G_TYPE_PE_FORMAT, type)) return false; - if (!define_python_pe_format_constants(type)) - return false; + //if (!define_python_pe_format_constants(type)) + // return false; return true; diff --git a/plugins/pe/python/module.c b/plugins/pe/python/module.c index 93b1337..ce0c8d7 100644 --- a/plugins/pe/python/module.c +++ b/plugins/pe/python/module.c @@ -33,7 +33,7 @@ #include "format.h" -#include "routine.h" +//#include "routine.h" @@ -83,8 +83,8 @@ bool add_format_pe_module_to_python_module(void) result = (module != NULL); if (result) result = register_python_pe_format(module); - if (result) result = register_python_pe_exported_routine(module); - if (result) result = register_python_pe_imported_routine(module); + //if (result) result = register_python_pe_exported_routine(module); + //if (result) result = register_python_pe_imported_routine(module); assert(result); diff --git a/plugins/pe/python/translate.c b/plugins/pe/python/translate.c index c01a337..1b4b3ce 100644 --- a/plugins/pe/python/translate.c +++ b/plugins/pe/python/translate.c @@ -45,7 +45,7 @@ * * ******************************************************************************/ -PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_header *header) +PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_header_t *header) { PyObject *result; /* Construction à retourner */ PyTypeObject *base; /* Modèle d'objet à créer */ @@ -64,6 +64,7 @@ PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_h { \ attrib = PyLong_FromUnsignedLongLong(header->e_ ## _f); \ ret = PyDict_SetItemString(result, "e_" #_f, attrib); \ + Py_DECREF(attrib); \ if (ret != 0) goto failed; \ } \ while (0); @@ -79,8 +80,13 @@ PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_h ret = PyTuple_SetItem(attrib, i, item); \ if (ret != 0) break; \ } \ - if (ret != 0) goto failed; \ + if (i < _n) \ + { \ + Py_DECREF(attrib); \ + goto failed; \ + } \ ret = PyDict_SetItemString(result, "e_" #_f, attrib); \ + Py_DECREF(attrib); \ if (ret != 0) goto failed; \ } \ while (0); @@ -119,6 +125,215 @@ PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_h /****************************************************************************** * * * Paramètres : format = format PE chargé sur lequel s'appuyer. * +* header = en-tête NT à décrire en Python. * +* * +* Description : Traduit un en-tête PE en Python. * +* * +* Retour : Structure mise en place ou NULL en cas d'erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyObject *translate_pe_nt_headers_to_python(GPeFormat *format, const image_nt_headers_t *header) +{ + PyObject *result; /* Construction à retourner */ + PyTypeObject *base; /* Modèle d'objet à créer */ + PyObject *attrib; /* Attribut à constituer */ + int ret; /* Bilan d'une mise en place */ + PyObject *sub; /* Sous-construction #1 */ + bool is_32b; /* Format en version 32 bits ? */ + const image_data_directory_t *directories; /* Répertoires à charger */ + uint32_t number_of_rva_and_sizes; /* Quantité de ces répertoires */ + uint32_t i; /* Boucle de parcours */ + PyObject *dirs; /* Répertoires de données */ + PyObject *subsub; /* Sous-construction #2 */ + + base = get_python_py_struct_type(); + + result = PyObject_CallFunction((PyObject *)base, NULL); + assert(result != NULL); + +#define TRANSLATE_IMAGE_NT_HEADERS_FIELD(_f) \ + do \ + { \ + attrib = PyLong_FromUnsignedLongLong(header->_f); \ + ret = PyDict_SetItemString(result, #_f, attrib); \ + Py_DECREF(attrib); \ + if (ret != 0) goto failed; \ + } \ + while (0); + + TRANSLATE_IMAGE_NT_HEADERS_FIELD(signature); + + /* Partie file_header */ + + sub = PyObject_CallFunction((PyObject *)base, NULL); + assert(sub != NULL); + +#define TRANSLATE_IMAGE_FILE_HEADER_FIELD(_f) \ + do \ + { \ + attrib = PyLong_FromUnsignedLongLong(header->file_header._f); \ + ret = PyDict_SetItemString(sub, #_f, attrib); \ + Py_DECREF(attrib); \ + if (ret != 0) \ + { \ + Py_DECREF(sub); \ + goto failed; \ + } \ + } \ + while (0); + + TRANSLATE_IMAGE_FILE_HEADER_FIELD(machine); + TRANSLATE_IMAGE_FILE_HEADER_FIELD(number_of_sections); + TRANSLATE_IMAGE_FILE_HEADER_FIELD(time_date_stamp); + TRANSLATE_IMAGE_FILE_HEADER_FIELD(pointer_to_symbol_table); + TRANSLATE_IMAGE_FILE_HEADER_FIELD(number_of_symbols); + TRANSLATE_IMAGE_FILE_HEADER_FIELD(size_of_optional_header); + TRANSLATE_IMAGE_FILE_HEADER_FIELD(characteristics); + + ret = PyDict_SetItemString(result, "file_header", sub); + Py_DECREF(sub); + if (ret != 0) goto failed; + + /* Partie optional_header */ + + sub = PyObject_CallFunction((PyObject *)base, NULL); + assert(sub != NULL); + + is_32b = g_pe_format_get_is_32b(format); + +#define TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(_f) \ + do \ + { \ + if (is_32b) \ + attrib = PyLong_FromUnsignedLongLong(header->optional_header.header_32._f); \ + else \ + attrib = PyLong_FromUnsignedLongLong(header->optional_header.header_64._f); \ + ret = PyDict_SetItemString(sub, #_f, attrib); \ + Py_DECREF(attrib); \ + if (ret != 0) \ + { \ + Py_DECREF(sub); \ + goto failed; \ + } \ + } \ + while (0); + +#define TRANSLATE_IMAGE_OPTIONAL_HEADER_32B_FIELD(_f) \ + do \ + { \ + attrib = PyLong_FromUnsignedLongLong(header->optional_header.header_32._f); \ + ret = PyDict_SetItemString(sub, #_f, attrib); \ + Py_DECREF(attrib); \ + if (ret != 0) \ + { \ + Py_DECREF(sub); \ + goto failed; \ + } \ + } \ + while (0); + + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(magic); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(major_linker_version); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(minor_linker_version); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_code); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_initialized_data); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_uninitialized_data); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(address_of_entry_point); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(base_of_code); + if (is_32b) TRANSLATE_IMAGE_OPTIONAL_HEADER_32B_FIELD(base_of_data); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(image_base); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(section_alignment); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(file_alignment); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(major_operating_system_version); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(minor_operating_system_version); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(major_image_version); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(minor_image_version); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(major_subsystem_version); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(minor_subsystem_version); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(win32_version_value); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_image); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_headers); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(checksum); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(subsystem); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(dll_characteristics); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_stack_reserve); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_stack_commit); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_heap_reserve); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_heap_commit); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(loader_flags); + TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(number_of_rva_and_sizes); + + ret = PyDict_SetItemString(result, "optional_header", sub); + Py_DECREF(sub); + if (ret != 0) goto failed; + + /* Répertoires de données */ + + if (is_32b) + { + directories = header->optional_header.header_32.data_directory; + number_of_rva_and_sizes = header->optional_header.header_32.number_of_rva_and_sizes; + } + else + { + directories = header->optional_header.header_64.data_directory; + number_of_rva_and_sizes = header->optional_header.header_64.number_of_rva_and_sizes; + } + + dirs = PyTuple_New(number_of_rva_and_sizes); + + for (i = 0; i < number_of_rva_and_sizes; i++) + { + subsub = translate_pe_image_data_directory_to_python(format, directories + i); + if (subsub == NULL) break; + + ret = PyTuple_SetItem(dirs, i, subsub); + if (ret != 0) break; + + } + + if (i < number_of_rva_and_sizes) + goto failed_with_dirs; + + /** + * La fonction PyTuple_SetItem() comporte le prologue suivant : + * + * if (!PyTuple_Check(op) || Py_REFCNT(op) != 1) { + * Py_XDECREF(newitem); + * PyErr_BadInternalCall(); + * return -1; + * } + * + * Comme l'appel à PyDict_SetItemString() incrémente le compte de référence + * de dirs, il convient de le réaliser après la consitution de la liste. + */ + + ret = PyDict_SetItemString(sub, "directories", dirs); + if (ret != 0) goto failed_with_dirs; + + Py_DECREF(dirs); + + return result; + + failed_with_dirs: + + Py_DECREF(dirs); + + failed: + + Py_DECREF(result); + + return NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : format = format PE chargé sur lequel s'appuyer. * * dir = répertoire PE à décrire en Python. * * * * Description : Traduit un répertoire PE en Python. * @@ -129,7 +344,7 @@ PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_h * * ******************************************************************************/ -PyObject *translate_pe_image_data_directory_to_python(GPeFormat *format, const image_data_directory *dir) +PyObject *translate_pe_image_data_directory_to_python(GPeFormat *format, const image_data_directory_t *dir) { PyObject *result; /* Construction à retourner */ PyTypeObject *base; /* Modèle d'objet à créer */ @@ -146,6 +361,7 @@ PyObject *translate_pe_image_data_directory_to_python(GPeFormat *format, const i { \ attrib = PyLong_FromUnsignedLongLong(dir->_f); \ ret = PyDict_SetItemString(result, #_f, attrib); \ + Py_DECREF(attrib); \ if (ret != 0) goto failed; \ } \ while (0); @@ -167,6 +383,73 @@ PyObject *translate_pe_image_data_directory_to_python(GPeFormat *format, const i /****************************************************************************** * * * Paramètres : format = format PE chargé sur lequel s'appuyer. * +* header = en-tête de section à décrire en Python. * +* * +* Description : Traduit une section PE en Python. * +* * +* Retour : Structure mise en place ou NULL en cas d'erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyObject *translate_pe_section_header_to_python(GPeFormat *format, const image_section_header_t *header) +{ + PyObject *result; /* Construction à retourner */ + PyTypeObject *base; /* Modèle d'objet à créer */ + PyObject *attrib; /* Attribut à constituer */ + int ret; /* Bilan d'une mise en place */ + + base = get_python_py_struct_type(); + + result = PyObject_CallFunction((PyObject *)base, NULL); + assert(result != NULL); + + /* Nom de la section */ + + attrib = PyBytes_FromStringAndSize(header->name, IMAGE_SIZEOF_SHORT_NAME); + + ret = PyDict_SetItemString(result, "name", attrib); + Py_DECREF(attrib); + + if (ret != 0) goto failed; + + /* Eléments classiques */ + +#define TRANSLATE_IMAGE_SECTION_HEADER_FIELD(_f) \ + do \ + { \ + attrib = PyLong_FromUnsignedLongLong(header->_f); \ + ret = PyDict_SetItemString(result, #_f, attrib); \ + Py_DECREF(attrib); \ + if (ret != 0) goto failed; \ + } \ + while (0); + + TRANSLATE_IMAGE_SECTION_HEADER_FIELD(virtual_size); + TRANSLATE_IMAGE_SECTION_HEADER_FIELD(virtual_address); + TRANSLATE_IMAGE_SECTION_HEADER_FIELD(size_of_raw_data); + TRANSLATE_IMAGE_SECTION_HEADER_FIELD(pointer_to_raw_data); + TRANSLATE_IMAGE_SECTION_HEADER_FIELD(pointer_to_relocations); + TRANSLATE_IMAGE_SECTION_HEADER_FIELD(pointer_to_line_numbers); + TRANSLATE_IMAGE_SECTION_HEADER_FIELD(number_of_relocations); + TRANSLATE_IMAGE_SECTION_HEADER_FIELD(number_of_line_numbers); + TRANSLATE_IMAGE_SECTION_HEADER_FIELD(characteristics); + + return result; + + failed: + + Py_DECREF(result); + + return NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : format = format PE chargé sur lequel s'appuyer. * * id = ensemble d'informations à décrire en Python. * * * * Description : Traduit une série d'informations enrichies en Python. * @@ -194,6 +477,7 @@ PyObject *translate_pe_comp_id_to_python(GPeFormat *format, const comp_id_t *id) { \ attrib = PyLong_FromUnsignedLongLong(id->_f); \ ret = PyDict_SetItemString(result, #_f, attrib); \ + Py_DECREF(attrib); \ if (ret != 0) goto failed; \ } \ while (0); diff --git a/plugins/pe/python/translate.h b/plugins/pe/python/translate.h index dbde6c8..e12b4ae 100644 --- a/plugins/pe/python/translate.h +++ b/plugins/pe/python/translate.h @@ -35,10 +35,16 @@ /* Traduit un en-tête MS-DOS en Python. */ -PyObject *translate_pe_dos_header_to_python(GPeFormat *, const image_dos_header *); +PyObject *translate_pe_dos_header_to_python(GPeFormat *, const image_dos_header_t *); + +/* Traduit un en-tête PE en Python. */ +PyObject *translate_pe_nt_headers_to_python(GPeFormat *, const image_nt_headers_t *); /* Traduit un répertoire PE en Python. */ -PyObject *translate_pe_image_data_directory_to_python(GPeFormat *, const image_data_directory *); +PyObject *translate_pe_image_data_directory_to_python(GPeFormat *, const image_data_directory_t *); + +/* Traduit une section PE en Python. */ +PyObject *translate_pe_section_header_to_python(GPeFormat *, const image_section_header_t *); /* Traduit une série d'informations enrichies en Python. */ PyObject *translate_pe_comp_id_to_python(GPeFormat *, const comp_id_t *); diff --git a/plugins/pe/section.c b/plugins/pe/section.c deleted file mode 100644 index 732b35c..0000000 --- a/plugins/pe/section.c +++ /dev/null @@ -1,73 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * section.h - prototypes pour la gestion des sections d'un PE - * - * Copyright (C) 2010-2021 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/>. - */ - - -#include "section.h" - - -#include <malloc.h> - - -#include "pe-int.h" - - - -/****************************************************************************** -* * -* Paramètres : format = description de l'exécutable à consulter. * -* pos = tête de lecture positionnée. [OUT] * -* * -* Description : Recherche une section donnée au sein de binaire par indice. * -* * -* Retour : Liste de sections reconstituées ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -image_section_header *read_all_pe_sections(const GPeFormat *format, vmpa2t *pos) -{ - image_section_header *result; /* Liste à retourner */ - uint16_t count; /* Quantité de sections */ - uint16_t i; /* Boucle de parcours */ - bool status; /* Bilan d'une lecture */ - - count = format->nt_headers.file_header.number_of_sections; - - result = malloc(count * sizeof(image_section_header)); - - for (i = 0; i < count; i++) - { - status = read_pe_image_section_header(format, pos, &result[i]); - - if (!status) - { - free(result); - result = NULL; - break; - } - - } - - return result; - -} diff --git a/plugins/pychrysalide/Makefile.am b/plugins/pychrysalide/Makefile.am index 183a4ef..d1bf457 100644 --- a/plugins/pychrysalide/Makefile.am +++ b/plugins/pychrysalide/Makefile.am @@ -1,7 +1,7 @@ DEFAULT_INCLUDES = -I$(top_builddir) -idirafter. -lib_LTLIBRARIES = pychrysalide.la +lib_LTLIBRARIES = pychrysalide.la pychrysalideui.la libdir = $(pluginslibdir) @@ -32,7 +32,10 @@ endif pychrysalide_la_SOURCES = \ access.h access.c \ + bindings.h bindings.c \ constants.h constants.c \ + convert.h convert.c \ + core-int.h \ core.h core.c \ helpers.h helpers.c \ star.h star.c \ @@ -71,6 +74,24 @@ pychrysalide_la_LDFLAGS = \ $(RUN_PATH) +EXTRA_pychrysalideui_la_DEPENDENCIES = pychrysalide.la + +pychrysalideui_la_SOURCES = \ + core-ui-int.h \ + core-ui.h core-ui.c + +pychrysalideui_la_LIBADD = + +# -ldl: dladdr(), dlerror() +pychrysalideui_la_LDFLAGS = \ + -module -avoid-version -ldl \ + $(LIBPYTHON_INTERPRETER_LIBS) \ + $(LIBPYGOBJECT_LIBS) \ + -L.libs -l:pychrysalide.so \ + -L$(top_srcdir)/src/.libs -lchrysacoreui\ + $(RUN_PATH) + + devdir = $(includedir)/chrysalide/$(subdir) dev_HEADERS = $(pychrysalide_la_SOURCES:%c=) diff --git a/plugins/pychrysalide/analysis/content.c b/plugins/pychrysalide/analysis/content.c index dd9c1c1..c271139 100644 --- a/plugins/pychrysalide/analysis/content.c +++ b/plugins/pychrysalide/analysis/content.c @@ -50,9 +50,9 @@ /* Initialise la classe générique des contenus de binaire. */ -static void py_binary_content_init_gclass(GBinContentClass *, gpointer); +static int py_binary_content_init_gclass(GBinContentClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(binary_content, G_TYPE_BIN_CONTENT, py_binary_content_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(binary_content, G_TYPE_BIN_CONTENT); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_binary_content_init(PyObject *, PyObject *, PyObject *); @@ -163,37 +163,39 @@ static PyObject *py_binary_content_get_data(PyObject *, void *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe générique des contenus de binaire. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_binary_content_init_gclass(GBinContentClass *class, gpointer unused) +static int py_binary_content_init_gclass(GBinContentClass *gclass, PyTypeObject *pyclass) { - class->describe = py_binary_content_describe_wrapper; + PY_CLASS_SET_WRAPPER(gclass->describe, py_binary_content_describe_wrapper); + + PY_CLASS_SET_WRAPPER(gclass->compute_checksum, py_binary_content_compute_checksum_wrapper); - class->compute_checksum = py_binary_content_compute_checksum_wrapper; + PY_CLASS_SET_WRAPPER(gclass->compute_size, py_binary_content_compute_size_wrapper); + PY_CLASS_SET_WRAPPER(gclass->compute_start_pos, py_binary_content_compute_start_pos_wrapper); + PY_CLASS_SET_WRAPPER(gclass->compute_end_pos, py_binary_content_compute_end_pos_wrapper); - class->compute_size = py_binary_content_compute_size_wrapper; - class->compute_start_pos = py_binary_content_compute_start_pos_wrapper; - class->compute_end_pos = py_binary_content_compute_end_pos_wrapper; + PY_CLASS_SET_WRAPPER(gclass->seek, py_binary_content_seek_wrapper); - class->seek = py_binary_content_seek_wrapper; + PY_CLASS_SET_WRAPPER(gclass->read_raw, py_binary_content_read_raw_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_u8, py_binary_content_read_u8_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_u16, py_binary_content_read_u16_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_u32, py_binary_content_read_u32_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_u64, py_binary_content_read_u64_wrapper); - class->read_raw = py_binary_content_read_raw_wrapper; - class->read_u8 = py_binary_content_read_u8_wrapper; - class->read_u16 = py_binary_content_read_u16_wrapper; - class->read_u32 = py_binary_content_read_u32_wrapper; - class->read_u64 = py_binary_content_read_u64_wrapper; + PY_CLASS_SET_WRAPPER(gclass->read_uleb128, py_binary_content_read_uleb128_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_leb128, py_binary_content_read_leb128_wrapper); - class->read_uleb128 = py_binary_content_read_uleb128_wrapper; - class->read_leb128 = py_binary_content_read_leb128_wrapper; + return 0; } @@ -2248,6 +2250,8 @@ bool ensure_python_binary_content_is_registered(void) dict = PyModule_GetDict(module); + pyg_register_class_init(G_TYPE_BIN_CONTENT, (PyGClassInitFunc)py_binary_content_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_BIN_CONTENT, type)) return false; diff --git a/plugins/pychrysalide/arch/module.c b/plugins/pychrysalide/arch/module.c index bbdaf76..0127348 100644 --- a/plugins/pychrysalide/arch/module.c +++ b/plugins/pychrysalide/arch/module.c @@ -28,9 +28,6 @@ #include <assert.h> -#include <arch/archbase.h> - - /* #include "context.h" #include "instriter.h" diff --git a/plugins/pychrysalide/bindings.c b/plugins/pychrysalide/bindings.c new file mode 100644 index 0000000..7e87e27 --- /dev/null +++ b/plugins/pychrysalide/bindings.c @@ -0,0 +1,1466 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * bindings.c - éléments d'un socle commun aux fonctionnalités graphiques et non graphiques + * + * Copyright (C) 2024 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "bindings.h" + + +#include <assert.h> +#include <dlfcn.h> +#include <pygobject.h> +#include <stddef.h> +#include <stdio.h> + + +#include <common/cpp.h> +#include <common/extstr.h> +#include <core/core.h> +#include <plugins/pglist.h> +#include <plugins/self.h> + + +#include "access.h" +#include "constants.h" +#include "helpers.h" +#include "star.h" +//#include "strenum.h" +#include "struct.h" +#include "analysis/module.h" +#include "arch/module.h" +#include "common/module.h" +#include "core/module.h" +#include "glibext/module.h" +/* #include "debug/module.h" */ +#include "format/module.h" +/* #ifdef INCLUDE_GTK_SUPPORT */ +/* # include "gtkext/module.h" */ +/* # include "gui/module.h" */ +/* #endif */ +/* #include "mangling/module.h" */ +#include "plugins/module.h" + + + + + +/* ------------------------ FONCTIONNALITES POUR CODE PYTHON ------------------------ */ + + +#define PYCHRYSALIDE_NAME "pychrysalide" + +#define PYCHRYSALIDE_DOC \ + "PyChrysalide is a module containing Chrysalide's features and designed for Python users.\n" \ + "\n" \ + "The whole API is defined in a single library named 'pychrysalide.so' and can be used in two ways:\n" \ + "* either from the Chrysalide's GUI, by registering hooks or GLib signals;\n" \ + "* or from a shell command line, by setting PYTHONPATH to point to the directory containing the library.\n" \ + "\n" \ + "In both cases, this is a good start point to have a look at already existing plugins to quickly learn " \ + "how the API works.\n" \ + "\n" \ + "These plugins are located in the 'plugins/python' directory.\n" \ + "\n" \ + "The *pychrysalide* module imports the GLib module (version 2.0) from the GI repository at startup." + + +/* Fournit la révision du programme global. */ +static PyObject *py_chrysalide_revision(PyObject *, PyObject *); + +/* Fournit la version du programme global. */ +static PyObject *py_chrysalide_version(PyObject *, PyObject *); + +/* Fournit la version du greffon pour Python. */ +static PyObject *py_chrysalide_mod_version(PyObject *, PyObject *); + + + +/* ------------------------ FONCTIONNALITES DE MISE EN PLACE ------------------------ */ + + +/* Détermine si l'interpréteur lancé est celui pris en compte. */ +static bool is_current_abi_suitable(void); + +/* Assure une pleine initialisation des objets de Python-GI. */ +static bool install_metaclass_for_python_gobjects(void); + +/* Met en place un environnement pour l'extension Python. */ +static bool setup_python_context(void); + +/* Intègre les éventuelles fonctions natives des interfaces. */ +static void inherit_interface_slots(PyObject *); + +/** + * Conservation d'anciens pointeurs remplacés + */ +static initproc __old_gobject_meta_base_init = NULL; +static initproc __old_gobject_meta_init = NULL; + +/** + * La fonction unhook_pygobject_behaviour(), inversant les opérations de la fonction + * unhook_pygobject_behaviour() manipulerait volontiers les fonctions PyImport_ImportModule() + * et PyObject_GetAttrString(). + * + * Cependant, les appels à ces dernières depuis la clôture organisée par la fonction + * PyExit_pychrysalide() provoque l'erreur suivante : + * + * Fatal Python error: _PyInterpreterState_GET: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL) + * + * Les accès nécessaires sont donc conservés ici. + */ +static PyTypeObject *__gobject_meta_base = NULL; +static PyTypeObject *__gobject_meta = NULL; + +/* Interceptionne une initialisation pour types gi._gi.GObject. */ +static int hook_gobject_meta_base_init(PyObject *, PyObject *, PyObject *); + +/* Interceptionne une initialisation pour types GObject.Object. */ +static int hook_gobject_meta_init(PyObject *, PyObject *, PyObject *); + +/* Modifie légèrement le comportement des GObjects en Python. */ +static bool hook_pygobject_behaviour(void); + +/* Restaure le comportement d'origine des GObjects en Python. */ +static void unhook_pygobject_behaviour(void); + +/* Assure la définition d'un type GObject pour Python adapté. */ +static void ensure_native_pygobject_type(PyTypeObject **); + +/* Fournit la référence à un éventuel module déjà en place. */ +static PyObject *get_existing_modules(void); + +/* Définit les différents modules du support Python. */ +static PyObject *create_basic_modules(void); + +/* Inscrit les défintions des objets Python de Chrysalide. */ +static bool populate_python_modules(const pyinit_details_t *); + +/* Restore une ancienne définition de type GObject au besoin. */ +static void restore_original_pygobject_type(PyTypeObject *); + + +/* ------------------------ FONCTIONS GLOBALES DE CHRYSALIDE ------------------------ */ + + +/* Assure le plein chargement dans un interpréteur Python. */ +static bool init_python_interpreter_for_standalone_mode(const pyinit_details_t *); + +/* Point de sortie pour l'initialisation de Python. */ +static void PyExit_pychrysalide(void); + + + + + +/* ---------------------------------------------------------------------------------- */ +/* FONCTIONNALITES POUR CODE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + +/****************************************************************************** +* * +* Paramètres : self = NULL car méthode statique. * +* args = non utilisé ici. * +* * +* Description : Fournit la révision du programme global. * +* * +* Retour : Numéro de révision. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_chrysalide_revision(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + +#define PY_CHRYSALIDE_REVISION_METHOD PYTHON_METHOD_DEF \ +( \ + revision, "/", \ + METH_NOARGS, py_chrysalide, \ + "Provide the revision number of Chrysalide.\n" \ + "\n" \ + "The returned value is provided as a string, for instance: 'r1665'." \ +) + + result = PyUnicode_FromString("r" XSTR(REVISION)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = NULL car méthode statique. * +* args = non utilisé ici. * +* * +* Description : Fournit la version du programme global. * +* * +* Retour : Numéro de version. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_chrysalide_version(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + int major; /* Numéro de version majeur */ + int minor; /* Numéro de version mineur */ + int revision; /* Numéro de révision */ + char version[16]; /* Conservation temporaire */ + +#define PY_CHRYSALIDE_VERSION_METHOD PYTHON_METHOD_DEF \ +( \ + version, "/", \ + METH_NOARGS, py_chrysalide, \ + "Provide the version number of Chrysalide.\n" \ + "\n" \ + "The returned value is provided as a string, for instance: '1.6.65'." \ +) + + major = REVISION / 1000; + minor = (REVISION - (major * 1000)) / 100; + revision = REVISION % 100; + + snprintf(version, sizeof(version), "%d.%d.%d", major, minor, revision); + + result = PyUnicode_FromString(version); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = NULL car méthode statique. * +* args = non utilisé ici. * +* * +* Description : Fournit la version du greffon pour Python. * +* * +* Retour : Numéro de version. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_chrysalide_mod_version(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + char version[16]; /* Conservation temporaire */ + +#define PY_CHRYSALIDE_MOD_VERSION_METHOD PYTHON_METHOD_DEF \ +( \ + mod_version, "/", \ + METH_NOARGS, py_chrysalide, \ + "Provide the version number of Chrysalide module for Python.\n" \ + "\n" \ + "The returned value is provided as a string, for instance: '0.1.0'." \ +) + + snprintf(version, sizeof(version), "%s", "x.x.x");// FIXME _chrysalide_plugin.version); + + result = PyUnicode_FromString(version); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* FONCTIONNALITES DE MISE EN PLACE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Détermine si l'interpréteur lancé est celui pris en compte. * +* * +* Retour : true si l'exécution peut se poursuivre, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool is_current_abi_suitable(void) +{ + bool result; + int fds[2]; + int ret; + char cmds[128]; + char content[64]; + ssize_t got; + +#define GRAB_ABI_FLAGS_IN_PYTHON \ + "import sys" "\n" \ + "import os" "\n" \ + "data = bytes(sys.abiflags, 'UTF-8') + b'\\0'" "\n" \ + "os.write(%d, data)" "\n" + + result = false; + + ret = pipe(fds); + if (ret == -1) + { + perror("pipe()"); + goto exit; + } + + snprintf(cmds, sizeof(cmds), GRAB_ABI_FLAGS_IN_PYTHON, fds[1]); + + ret = PyRun_SimpleString(cmds); + if (ret != 0) goto exit_with_pipe; + + got = read(fds[0], content, sizeof(content)); + if (got < 0) + { + perror("read()"); + goto exit_with_pipe; + } + + content[got] = '\0'; + + result = (strcmp(content, LIBPYTHON_ABI_FLAGS) == 0); + + exit_with_pipe: + + close(fds[0]); + close(fds[1]); + + exit: + + if (!result) + PyErr_SetString(PyExc_SystemError, "the ABI flags of the current interpreter do not match " \ + "the ones of the Python library used during the module compilation."); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Assure une pleine initialisation des objets de Python-GI. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool install_metaclass_for_python_gobjects(void) +{ + bool result; /* Bilan à retourner */ + PyObject *gi_types_mod; /* Module Python-GObject */ + + /** + * Les extensions Python sont chargées à partir de la fonction load_python_plugins(), + * qui fait appel à create_python_plugin(). Une instance y est construite via un + * appel à PyObject_CallFunction() avec la classe spécifiée par l'alias AutoLoad + * dans le fichier __init__.py présent dans chaque module d'extension. + * + * Le constructeur py_plugin_module_new() renvoie in fine à la fonction générique + * python_abstract_constructor_with_dynamic_gtype(), laquelle conduit à la fonction + * pygobject_register_class() définie dans <python3-gi>/gi/pygobject-object.c. + * Le code de cette dernière comprend notamment la portion suivante : + * + * [...] + * Py_SET_TYPE(type, PyGObject_MetaType); + * [...] + * if (PyType_Ready(type) < 0) { + * g_warning ("couldn't make the type `%s' ready", type->tp_name); + * return; + * } + * [...] + * + * La fonction PyType_Ready() est définie dans <python3>/Objects/typeobject.c + * et commence par : + * + * int PyType_Ready(PyTypeObject *type) + * { + * if (type->tp_flags & Py_TPFLAGS_READY) { + * assert(_PyType_CheckConsistency(type)); + * return 0; + * } + * [...] + * } + * + * La vérification de cohérencce commence par analyser le type et son propre + * type : + * + * - cf. _PyType_CheckConsistency() dans <python3>/Objects/typeobject.c : + * + * int _PyType_CheckConsistency(PyTypeObject *type) + * { + * [...] + * CHECK(!_PyObject_IsFreed((PyObject *)type)); + * [...] + * } + * + * - cf. _PyObject_IsFreed() dans <python3>/Objects/object.c : + * + * int _PyObject_IsFreed(PyObject *op) + * { + * if (_PyMem_IsPtrFreed(op) || _PyMem_IsPtrFreed(Py_TYPE(op))) { + * return 1; + * } + * + * La fonction _PyMem_IsPtrFreed() recherche entre autres la valeur NULL. + * + * Or le type du type est écrasé dans la fonction pygobject_register_class() + * avec la valeur de la variable PyGObject_MetaType. Cette variable n'est + * définie qu'à un seul endroit, dans <python3-gi>/gi/gimodule.c : + * + * static PyObject * + * pyg__install_metaclass(PyObject *dummy, PyTypeObject *metaclass) + * { + * Py_INCREF(metaclass); + * PyGObject_MetaType = metaclass; + * Py_INCREF(metaclass); + * + * Py_SET_TYPE(&PyGObject_Type, metaclass); + * + * Py_INCREF(Py_None); + * return Py_None; + * } + * + * Afin de valider la vérification de _PyType_CheckConsistency() pour les + * modules externes qui entraînent un enregistrement tout en portant le drapeau + * Py_TPFLAGS_READY (typiquement ceux du répertoire "plugins/python/", il faut + * initialiser au besoin la variable PyGObject_MetaType. + * + * Une ligne suffit donc à enregistrer le type intermédiaire : + * + * from _gi import types + * + * On simule ici une déclaration similaire si nécessaire, selon la valeur + * portée par PyGObject_Type.ob_base.ob_base.ob_type.tp_name : + * - "type" (PyType_Type) : état initial ; + * - "_GObjectMetaBase" : état revu. + */ + + /** + * PyGObject_Type.ob_base.ob_base.ob_type != &PyType_Type ? + */ + result = (PyType_CheckExact(&PyGObject_Type) == 0); + + if (!result) + { + gi_types_mod = PyImport_ImportModule("gi.types"); + + result = (PyErr_Occurred() == NULL); + + if (result) + result = (PyType_CheckExact(&PyGObject_Type) == 0); + + Py_XDECREF(gi_types_mod); + + } + +#ifndef NDEBUG + if (result) + assert(strcmp(PyGObject_Type.ob_base.ob_base.ob_type->tp_name, "_GObjectMetaBase") == 0); +#endif + + if (!result) + PyErr_SetString(PyExc_SystemError, "unable to install metaclass for Python GObjects."); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Met en place un environnement pour l'extension Python. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool setup_python_context(void) +{ + bool result; /* Bilan à retourner */ + + result = false; + + /** + * Un message d'erreur doit être défini en cas d'échec de l'initialisation, + * via un appel à PyErr_SetString(). + */ + + if (!is_current_abi_suitable()) + goto exit; + + if (pygobject_init(-1, -1, -1) == NULL) + { + PyErr_SetString(PyExc_SystemError, "unable to init GObject in Python."); + goto exit; + } + + if (!install_metaclass_for_python_gobjects()) + goto exit; + + result = true; + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : cls = classe instanciée pour la construction d'un objet. * +* * +* Description : Intègre les éventuelles fonctions natives des interfaces. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void inherit_interface_slots(PyObject *cls) +{ + GType gtype; /* Type GObject lié au Python */ + GType *ifaces; /* Interfaces implémentées */ + guint ifaces_count; /* Nombre de ces interfaces */ + guint i; /* Boucle de parcours */ + PyTypeObject *iface_type; /* Type Python pour interface */ + size_t k; /* Boucle de parcours */ + size_t offset; /* Position dans une structure */ + void *src_slot; /* Eventuelle fonction idéale */ + void *dst_slot; /* Eventuelle fonction en place*/ + + static size_t slot_offsets[] = { /* Emplacements à actualiser */ + //offsetof(PyTypeObject, tp_str), + offsetof(PyTypeObject, tp_hash), + offsetof(PyTypeObject, tp_richcompare), + }; + + /** + * Cette fonction reprend les principes de la fonction d'importation de + * PyGObject pygobject_inherit_slots(). + * + * Cependant, cette dernière n'est appelée que depuis les fonctions : + * - pygobject_register_class() (send C -> Python), qui peut écraser des + * slots existants ; + * - pygobject_new_with_interfaces() / pygobject_lookup_class(), qui ne + * remplace pas les fonctions par défaut déjà en place. + * + * Pour mémoire, les types créés dynamiquement depuis des scripts (sens + * Python -> C) passent par la fonction _wrap_pyg_type_register(). + */ + + gtype = pyg_type_from_object(cls); + assert(gtype != G_TYPE_INVALID); + + ifaces = g_type_interfaces(gtype, &ifaces_count); + + for (i = 0; i < ifaces_count; i++) + { + iface_type = pygobject_lookup_class(ifaces[i]); + +#define PYTYPE_SLOT(tp, off) \ + *(void **)(void *)(((char *)tp) + off) + + for (k = 0; k < ARRAY_SIZE(slot_offsets); k++) + { + offset = slot_offsets[k]; + + src_slot = PYTYPE_SLOT(iface_type, offset); + + if (src_slot == NULL) + continue; + + if (src_slot == PYTYPE_SLOT(&PyBaseObject_Type, offset) + || src_slot == PYTYPE_SLOT(&PyGObject_Type, offset)) + continue; + + dst_slot = PYTYPE_SLOT(cls, offset); + + if (src_slot == dst_slot) + continue; + + if (dst_slot != NULL) + { + if (dst_slot != PYTYPE_SLOT(&PyBaseObject_Type, offset) + && dst_slot != PYTYPE_SLOT(&PyGObject_Type, offset)) + continue; + } + + /** + * Usage du *(void **)(void *) + */ + PYTYPE_SLOT(cls, offset) = src_slot; + + } + + } + + g_free(ifaces); + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Interceptionne une initialisation pour types gi._gi.GObject. * +* * +* Retour : Bilan de l'initialisation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int hook_gobject_meta_base_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int result; /* Bilan à retourner */ + + /** + * Le type de self (self->ob_type->tp_name) est ici _GObjectMetaBase. + */ + + result = __old_gobject_meta_base_init(self, args, kwds); + + if (result == 0) + inherit_interface_slots(self); + + return result; + + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Interceptionne une initialisation pour types GObject.Object. * +* * +* Retour : Bilan de l'initialisation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int hook_gobject_meta_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int result; /* Bilan à retourner */ + + /** + * Le type de self (self->ob_type->tp_name) est ici GObjectMeta. + */ + + result = __old_gobject_meta_init(self, args, kwds); + + if (result == 0) + inherit_interface_slots(self); + + return result; + + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Modifie légèrement le comportement des GObjects en Python. * +* * +* Retour : Bilan de l'initialisation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool hook_pygobject_behaviour(void) +{ + bool result; /* Bilan à retourner */ + PyObject *gi_types_mod; /* Module Python-GObject */ + + result = false; + + /** + * Validation des accès. + * + * Les références prises sur les attributs sont restituées dans + * unhook_pygobject_behaviour(). + */ + + gi_types_mod = PyImport_ImportModule("gi.types"); + if (gi_types_mod == NULL) goto exit; + + __gobject_meta_base = (PyTypeObject *)PyObject_GetAttrString(gi_types_mod, "_GObjectMetaBase"); + assert(__gobject_meta_base != NULL); + if (__gobject_meta_base == NULL) goto exit_with_mod; + + __gobject_meta = (PyTypeObject *)PyObject_GetAttrString(gi_types_mod, "GObjectMeta"); + assert(__gobject_meta != NULL); + if (__gobject_meta == NULL) goto exit_with_mod; + + /** + * Modification des comportements. + */ + + __old_gobject_meta_base_init = __gobject_meta_base->tp_init; + + __gobject_meta_base->tp_init = hook_gobject_meta_base_init; + + __old_gobject_meta_init = __gobject_meta->tp_init; + + __gobject_meta->tp_init = hook_gobject_meta_init; + + result = true; + + exit_with_mod: + + Py_DECREF(gi_types_mod); + + exit: + + if (!result) + PyErr_SetString(PyExc_SystemError, "unable to hook the GObject behaviour in Python."); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Restaure le comportement d'origine des GObjects en Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void unhook_pygobject_behaviour(void) +{ + /** + * Le déclenchement de la fonction PyExit_pychrysalide() appelante est + * programmé depuis init_python_pychrysalide_module(), appelée si + * hook_pygobject_behaviour() a opéré avec réussite. + */ + assert(__gobject_meta_base != NULL); + assert(__gobject_meta != NULL); + + __gobject_meta_base->tp_init = __old_gobject_meta_base_init; + + Py_XDECREF(__gobject_meta_base); + + __gobject_meta->tp_init = __old_gobject_meta_init; + + Py_XDECREF(__gobject_meta); + +} + + +/****************************************************************************** +* * +* Paramètres : namespace = module particulier à charger à partir de gi. * +* version = idenfiant de la version à stipuler. * +* * +* Description : Charge un module GI dans Python avec une version attendue. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool import_namespace_from_gi_repository(const char *namespace, const char *version) +{ + bool result; /* Bilan à retourner */ + PyObject *module; /* Module Python-GObject */ + PyObject *args; /* Arguments à fournir */ + int ret; /* Bilan d'une mise en place */ + + result = false; + + /* Sélection d'une version */ + + module = PyImport_ImportModule("gi"); + + if (module != NULL) + { + args = Py_BuildValue("ss", namespace, version); + + run_python_method(module, "require_version", args); + + result = (PyErr_Occurred() == NULL); + + Py_DECREF(args); + Py_DECREF(module); + + } + + /* Importation du module visé */ + + if (result) + { + args = PyTuple_New(1); + + ret = PyTuple_SetItem(args, 0, PyUnicode_FromString(namespace)); + if (ret != 0) + { + result = false; + goto args_error; + } + + module = PyImport_ImportModuleEx("gi.repository", NULL, NULL, args); + + result = (module != NULL); + + Py_XDECREF(module); + + args_error: + + Py_DECREF(args); + + } + + // TODO : message d'erreur ? + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : py_gobj_def = définition de type actuelle. [OUT] * +* * +* Description : Assure la définition d'un type GObject pour Python adapté. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void ensure_native_pygobject_type(PyTypeObject **py_gobj_def) +{ + GQuark pygobject_class_key; /* Copie d'un accès GI interne */ + + /** + * Les appels suivants procèdent à l'enregistrement de différents éléments + * dans l'espace de noms Python pour Chrysalide. + * + * Une majeure partie de ces éléments est constituée d'objets dérivés de + * GObject. Ce type d'objet (G_TYPE_OBJECT) est représenté par deux types + * en Python : + * + * - gi._gi.GObject, mis en place lors de l'importation du module gi. + * + * Ce dernier lance automatiquement l'importation du module natif gi._gi, + * lequel, via son initialisation dans la fonction PyInit__gi() + * (cf. ./gi/gimodule.c) lance un appel à pyi_object_register_types() + * qui procède à l'enregistrement du type "GObject" porté par la structure + * PyGObject_Type en correspondance au type GLib G_TYPE_OBJECT (cf. appel + * à pygobject_register_class() dans gi/pygobject-object.c). + * + * - gi.repository.GObject.Object, qui vient surclasser le type précédent + * lors d'un appel d'initialisation : from gi.repository import GObject. + * + * Cette seconde définition est destinée à apporter une représentation + * de l'introspection GObject de plus haut niveau pour l'utilisateur par + * rapport à celle de bas niveau gi._gi. + * + * Il demeure que la seconde définition est entièrement implémentée en Python + * et porte ainsi le fanion Py_TPFLAGS_HEAPTYPE, imposant cette même dernière + * propriétée à tous les objets qui en dérivent. + * + * Les définitions de Chrysalide sont cependant toutes statiques et donc + * incompatibles avec une définition gi.repository.GObject.Object, comme le + * pointent les validations opérées par PyType_Ready(). + * + * Une solution consiste ici à restaurer au besoin la définition gi._gi.GObject + * brute, effectuer les enregistrements de Chrysalide sur cette base, et + * remettre en place la définition éventuellement remplacée ensuite. + */ + + *py_gobj_def = pygobject_lookup_class(G_TYPE_OBJECT); + + if (*py_gobj_def != &PyGObject_Type) + { + Py_INCREF((PyObject *)*py_gobj_def); + + /* Définition récupérée de pyi_object_register_types() */ + pygobject_class_key = g_quark_from_static_string("PyGObject::class"); + + g_type_set_qdata(G_TYPE_OBJECT, pygobject_class_key, &PyGObject_Type); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit la référence à un éventuel module déjà en place. * +* * +* Retour : Pointeur vers le module mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *get_existing_modules(void) +{ + PyObject *result; /* Module Python à retourner */ + + /** + * Vérification préalable : dans le cas où on est embarqué directement dans + * un interpréteur Python, le module se charge et termine par charger à leur + * tour les différentes extensions trouvées, via load_remaning_plugins() puis + * chrysalide_plugin_on_native_loaded(). + * + * Lesquelles vont très probablement charger le module pychrysalide. + * + * Comme le chargement de ce dernier n'est alors pas encore terminé, + * Python va relancer cette procédure, et register_access_to_python_module() + * va détecter un doublon. + */ + + result = get_access_to_python_module(PYCHRYSALIDE_NAME); + + Py_XINCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Définit les différents modules du support Python. * +* * +* Retour : Pointeur vers le module mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *create_basic_modules(void) +{ + PyObject *result; /* Module Python à retourner */ + bool status; /* Bilan des inclusions */ + + static PyMethodDef py_chrysalide_methods[] = { + PY_CHRYSALIDE_REVISION_METHOD, + PY_CHRYSALIDE_VERSION_METHOD, + PY_CHRYSALIDE_MOD_VERSION_METHOD, + { NULL } + }; + + static PyModuleDef py_chrysalide_module = { + + .m_base = PyModuleDef_HEAD_INIT, + + .m_name = PYCHRYSALIDE_NAME, + .m_doc = PYCHRYSALIDE_DOC, + + .m_size = -1, + + .m_methods = py_chrysalide_methods + + }; + + result = PyModule_Create(&py_chrysalide_module); + + register_access_to_python_module(py_chrysalide_module.m_name, result); + + status = true; + + /** + * Réceptacle pour objets et constantes : à laisser en premier ajout donc. + */ + if (status) status = add_features_module(result); + + if (status) status = define_data_types_constants(result); + + if (status) status = add_analysis_module(result); + if (status) status = add_arch_module(result); + if (status) status = add_common_module(result); + if (status) status = add_glibext_module(result); + if (status) status = add_core_module(result); + /* + if (status) status = add_debug_module(result); + */ + if (status) status = add_format_module(result); + /* +#ifdef INCLUDE_GTK_SUPPORT + if (status) status = add_gtkext_module(result); + if (status) status = add_gui_module(result); +#endif + if (status) status = add_mangling_module(result); + */ + if (status) status = add_plugins_module(result); + + if (!status) + { + Py_DECREF(result); + result = NULL; + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : details = précisions de chargement complémentaires. * +* * +* Description : Inscrit les défintions des objets Python de Chrysalide. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool populate_python_modules(const pyinit_details_t *details) +{ + bool result; /* Bilan à retourner */ + + /** + * Les chargements de types supplémentaires, apportés par la version UI, ne + * peuvent être forcés depuis les mises en places des versions non-UI via + * les classiques appels ensure_xxx(). + * + * L'assurance d'un chargement préalable est ainsi réalisée ici, via + * un chargement préliminaire, si besoin est. + */ + + if (details->populate_extra) + result = details->populate_extra(); + else + result = true; + + /* + if (result) result = ensure_python_string_enum_is_registered(); + */ + if (result) result = ensure_python_py_struct_is_registered(); + + if (result) result = populate_analysis_module(); + if (result) result = populate_arch_module(); + if (result) result = populate_glibext_module(); + if (result) result = populate_common_module(); + if (result) result = populate_core_module(); + /* + if (result) result = populate_debug_module(); + */ + if (result) result = populate_format_module(); + /* +#ifdef INCLUDE_GTK_SUPPORT + if (result) result = populate_gtkext_module(); + if (result) result = populate_gui_module(); +#endif + if (result) result = populate_mangling_module(); + */ + if (result) result = populate_plugins_module(); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : py_gobj_def = définition de type actuelle. [OUT] * +* * +* Description : Restore une ancienne définition de type GObject au besoin. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void restore_original_pygobject_type(PyTypeObject *py_gobj_def) +{ + GQuark pygobject_class_key; /* Copie d'un accès GI interne */ + + if (py_gobj_def != &PyGObject_Type) + { + /* Définition récupérée de pyi_object_register_types() */ + pygobject_class_key = g_quark_from_static_string("PyGObject::class"); + + g_type_set_qdata(G_TYPE_OBJECT, pygobject_class_key, py_gobj_def); + + Py_DECREF((PyObject *)py_gobj_def); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : details = précisions de chargement complémentaires. * +* * +* Description : Implémente le point d'entrée pour l'initialisation de Python.* +* * +* Retour : Module mis en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyObject *init_python_pychrysalide_module(const pyinit_details_t *details) +{ + PyObject *result; /* Module Python à retourner */ + PyTypeObject *py_gobj_def; /* Définition GObject courante */ + bool status; /* Bilan des inclusions */ + + result = get_existing_modules(); + + if (result != NULL) + return result; + + if (!setup_python_context()) + goto exit; + + if (!hook_pygobject_behaviour()) + goto exit; + + /** + * Le chargement forcé de l'espace GLib pour Python permet d'éviter un écueil, + * à savoir des types convertis de façon incomplète. Par exemple, pour une + * structure GChecksum, le type à l'exécution est : + * + * - sans module GLib : [<class 'gobject.GBoxed'>, <class 'object'>] + * + * - avec module GLib : [<class 'gi.repository.GLib.Checksum'>, <class 'gi.Boxed'>, <class 'gobject.GBoxed'>, <class 'object'>] + * + * Par ailleurs, il est à noter que le message suivant n'apparaît qu'avec + * la version debug de Python3 (version de python3-gi : 3.42.2-3) : + * + * <frozen importlib._bootstrap>:673: ImportWarning: DynamicImporter.exec_module() not found; falling back to load_module() + * + * Code de reproduction dans un interpréteur classique : + * + * import gi + * gi.require_version('GLib', '2.0') + * from gi.repository import GLib + * + */ + + if (!import_namespace_from_gi_repository("GLib", "2.0")) + goto exit; + + /* Mise en place des fonctionnalités offertes */ + + ensure_native_pygobject_type(&py_gobj_def); + + result = create_basic_modules(); + + if (result == NULL) + PyErr_SetString(PyExc_SystemError, "failed to create all PyChrysalide modules."); + + else + { + status = populate_python_modules(details); + + if (!status) + PyErr_SetString(PyExc_SystemError, "failed to load all PyChrysalide components."); + + else if (details->standalone) + status = init_python_interpreter_for_standalone_mode(details); + + if (!status) + { + Py_DECREF(result); + result = NULL; + } + + } + + restore_original_pygobject_type(py_gobj_def); + + exit: + + if (result == NULL && !details->standalone) + log_pychrysalide_exception("Python bindings loading failed"); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : prefix = message d'introduction à faire apparaître à l'écran.* +* * +* Description : Présente dans le journal une exception survenue. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void log_pychrysalide_exception(const char *prefix, ...) +{ + va_list ap; /* Compléments argumentaires */ + char *msg; /* Message complet à imprimer */ + PyObject *err_type; /* Type d'erreur Python */ + PyObject *err_value; /* Instance Python d'erreur */ + PyObject *err_traceback; /* Trace Python associée */ + PyObject *err_string; /* Description Python d'erreur */ + const char *err_msg; /* Représentation humaine */ + + assert(PyGILState_Check() == 1); + + if (PyErr_Occurred()) + { + /* Base de la communication */ + + va_start(ap, prefix); + + vasprintf(&msg, prefix, ap); + + va_end(ap); + + /* Détails complémentaires */ + + PyErr_Fetch(&err_type, &err_value, &err_traceback); + + PyErr_NormalizeException(&err_type, &err_value, &err_traceback); + + if (err_traceback == NULL) + { + err_traceback = Py_None; + Py_INCREF(err_traceback); + } + + PyException_SetTraceback(err_value, err_traceback); + + if (err_value == NULL) + msg = stradd(msg, _(": no extra information is provided...")); + + else + { + err_string = PyObject_Str(err_value); + err_msg = PyUnicode_AsUTF8(err_string); + + msg = stradd(msg, ": "); + msg = stradd(msg, err_msg); + + Py_DECREF(err_string); + + } + + /** + * Bien que la documentation précise que la fonction PyErr_Fetch() + * transfère la propritété des éléments retournés, la pratique + * montre que le programme plante à la terminaison en cas d'exception. + * + * C'est par exemple le cas quand un greffon Python ne peut se lancer + * correctement ; l'exception est alors levée à partir de la fonction + * create_python_plugin() et le plantage intervient en sortie d'exécution, + * au moment de la libération de l'extension Python : + * + * ==14939== Jump to the invalid address stated on the next line + * ==14939== at 0x1A8FCBC9: ??? + * ==14939== by 0x53DCDB2: g_object_unref (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5800.3) + * ==14939== by 0x610F834: on_plugin_ref_toggle (pglist.c:370) + * ==14939== by 0x610F31A: exit_all_plugins (pglist.c:153) + * ==14939== by 0x10AD19: main (main.c:440) + * ==14939== Address 0x1a8fcbc9 is not stack'd, malloc'd or (recently) free'd + * + * Curieusement, un appel à PyErr_PrintEx(1) corrige l'effet, alors qu'un + * appel à PyErr_PrintEx(0) ne change rien. + * + * La seule différence de l'instruction set_sys_last_vars réside en quelques + * lignes dans le code de l'interpréteur Python : + * + * if (set_sys_last_vars) { + * _PySys_SetObjectId(&PyId_last_type, exception); + * _PySys_SetObjectId(&PyId_last_value, v); + * _PySys_SetObjectId(&PyId_last_traceback, tb); + * } + * + * L'explication n'est pas encore déterminé : bogue dans Chrysalide ou dans Python ? + * L'ajout des éléments dans le dictionnaire du module sys ajoute une référence + * à ces éléments. + * + * On reproduit ici le comportement du code correcteur avec PySys_SetObject(). + */ + + PySys_SetObject("last_type", err_type); + PySys_SetObject("last_value", err_value); + PySys_SetObject("last_traceback", err_traceback); + + Py_XDECREF(err_traceback); + Py_XDECREF(err_value); + Py_XDECREF(err_type); + + log_plugin_simple_message(LMT_EXT_ERROR, msg); + + free(msg); + + } + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* FONCTIONS GLOBALES DE CHRYSALIDE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : details = précisions de chargement complémentaires. * +* * +* Description : Assure le plein chargement dans un interpréteur Python. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool init_python_interpreter_for_standalone_mode(const pyinit_details_t *details) +{ + bool result; /* Bilan à retourner */ + int ret; /* Bilan de préparatifs */ + Dl_info info; /* Informations dynamiques */ + GModule *module; /* Structure de chargement GLib*/ + GPluginModule *self; /* Représentation interne */ + + result = false; + + ret = Py_AtExit(PyExit_pychrysalide); + if (ret == -1) + { + PyErr_SetString(PyExc_SystemError, "failed to register a cleanup function."); + goto exit; + } + + if (!load_core_components(ACC_ALL_COMPONENTS)) + { + PyErr_SetString(PyExc_SystemError, "unable to load core components."); + goto exit; + } + + /** + * Le module chargé par Python n'apparaît pas dans la liste des greffons de + * Chrysalide et ne peut donc pas être référencé comme dépendance par d'autres + * extensions. + * + * Par ailleurs, lors de la recherche d'autres greffons via l'appel à la + * fonction init_all_plugins() ci-après, il faut que le nom du greffon soit + * déjà réservé pour faire échouer le second chargement du greffon courant + * lors du parcours des répertoires conservant les fichiers d'extensions. + */ + + ret = dladdr(__FUNCTION__, &info); + if (ret == 0) + { + LOG_ERROR_DL_N("dladdr"); + + PyErr_SetString(PyExc_SystemError, "failed to force bindings registration."); + goto exit; + + } + + module = g_module_open(info.dli_fname, G_MODULE_BIND_LAZY); + assert(module != NULL); + + self = details->create_self(module); + + /* A ce stade, le greffon a été chargé correctement */ + g_plugin_module_override_flags(self, PSF_LOADED); + + register_plugin(self); + + unref_object(self); + + /** + * Intégration des fonctionnalités portées par d'autres greffons. + */ + + result = true; + + init_all_plugins(true); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Point de sortie pour l'initialisation de Python. * +* * +* Retour : ? * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void PyExit_pychrysalide(void) +{ + unhook_pygobject_behaviour(); + + exit_all_plugins(); + + unload_core_components(ACC_ALL_COMPONENTS); + +} diff --git a/plugins/pychrysalide/bindings.h b/plugins/pychrysalide/bindings.h new file mode 100644 index 0000000..1758747 --- /dev/null +++ b/plugins/pychrysalide/bindings.h @@ -0,0 +1,73 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * bindings.h - prototypes pour les éléments d'un socle commun aux fonctionnalités graphiques et non graphiques + * + * Copyright (C) 2024 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_BINDINGS_H +#define _PLUGINS_PYCHRYSALIDE_BINDINGS_H + + +/** + * Note: + * Since Python may define some pre-processor definitions which affect the standard headers + * on some systems, you must include Python.h before any standard headers are included. + * + * cf. https://docs.python.org/3.4/c-api/intro.html + */ +#include <Python.h> + + +#include <gmodule.h> +#include <stdbool.h> + + +#include <plugins/plugin.h> + + + +/* Charge un module GI dans Python avec une version attendue. */ +bool import_namespace_from_gi_repository(const char *, const char *); + +/* Raffinements pour la mise en place du module Python */ +typedef struct _pyinit_details_t +{ + bool standalone; /* Chargement depuis Python ? */ + + bool (* populate_extra) (void); /* Ajout de types ? */ + + /** + * Prototype de la fonction de création, à garder synchronisé avec + * NATIVE_PLUGIN_ENTRYPOINT() (cf. native-int.h). + */ + GPluginModule * (* create_self) (GModule *); + +} pyinit_details_t; + +/* Implémente le point d'entrée pour l'initialisation de Python. */ +PyObject *init_python_pychrysalide_module(const pyinit_details_t *); + +/* Présente dans le journal une exception survenue. */ +void log_pychrysalide_exception(const char *, ...); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_BINDINGS_H */ diff --git a/plugins/pychrysalide/constants.c b/plugins/pychrysalide/constants.c index 8142284..34caa12 100644 --- a/plugins/pychrysalide/constants.c +++ b/plugins/pychrysalide/constants.c @@ -25,7 +25,6 @@ #include "constants.h" -#include <arch/archbase.h> #include <common/datatypes.h> diff --git a/plugins/pychrysalide/convert.c b/plugins/pychrysalide/convert.c new file mode 100644 index 0000000..c67c8ba --- /dev/null +++ b/plugins/pychrysalide/convert.c @@ -0,0 +1,89 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * convert.c - conversion d'arguments en éléments usuels externes + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "convert.h" + + +#include <assert.h> +#include <pygobject.h> +#ifndef NDEBUG +# include <stdbool.h> +#endif +#include <gio/gio.h> + + + +/****************************************************************************** +* * +* 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 instance GSettings. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_gsettings(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + GType type; /* Type obtenu ou 0 */ + + result = PyObject_IsInstance(arg, (PyObject *)&PyGObject_Type); + + if (result == 1) + { + type = pyg_type_from_object(arg); + + if (type != G_TYPE_SETTINGS) + result = 0; + + } + + 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 GSetting instance"); + break; + + case 1: + *((GSettings **)dst) = G_SETTINGS(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/plugins/translate.h b/plugins/pychrysalide/convert.h index facffd2..7bdf7da 100644 --- a/plugins/pychrysalide/plugins/translate.h +++ b/plugins/pychrysalide/convert.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * translate.h - prototypes pour la conversion de structures liées aux greffons en objets Python + * convert.h - prototypes pour la conversion d'arguments en éléments usuels externes * - * Copyright (C) 2021 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,20 +22,17 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_TRANSLATE_H -#define _PLUGINS_PYCHRYSALIDE_PLUGINS_TRANSLATE_H +#ifndef _PLUGINS_PYCHRYSALIDE_CONVERT_H +#define _PLUGINS_PYCHRYSALIDE_CONVERT_H #include <Python.h> -#include <plugins/plugin-def.h> +/* Tente de convertir en instance GSettings. */ +int convert_to_gsettings(PyObject *, void *); -/* Traduit une description d'interface de greffon. */ -PyObject *translate_plugin_interface_to_python(const plugin_interface *); - - -#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_TRANSLATE_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_CONVERT_H */ diff --git a/plugins/pychrysalide/core-int.h b/plugins/pychrysalide/core-int.h new file mode 100644 index 0000000..2b8fcc8 --- /dev/null +++ b/plugins/pychrysalide/core-int.h @@ -0,0 +1,58 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-int.h - prototypes internes pour le plugin permettant des extensions en Python + * + * Copyright (C) 2025 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 _PLUGINS_PYCHRYSALIDE_CORE_INT_H +#define _PLUGINS_PYCHRYSALIDE_CORE_INT_H + + +#include "core.h" + + +#include <plugins/native-int.h> + + + +/* Greffon natif pour la liaison Python de Chrysalide (instance) */ +struct _GPyChrysalidePlugin +{ + GNativePlugin parent; /* A laisser en premier */ + + PyObject *py_module; /* Réceptacle de chargement */ + +}; + + +/* Greffon natif pour la liaison Python de Chrysalide (classe) */ +struct _GPyChrysalidePluginClass +{ + GNativePluginClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un module pour un greffon de support Python. */ +bool g_pychrysalide_plugin_create(GPyChrysalidePlugin *, GModule *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_CORE_INT_H */ diff --git a/plugins/pychrysalide/core-ui-int.h b/plugins/pychrysalide/core-ui-int.h new file mode 100644 index 0000000..7cecc13 --- /dev/null +++ b/plugins/pychrysalide/core-ui-int.h @@ -0,0 +1,56 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-ui-int.h - prototypes internes pour le plugin permettant des extensions UI en Python + * + * Copyright (C) 2025 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 _PLUGINS_PYCHRYSALIDE_CORE_UI_INT_H +#define _PLUGINS_PYCHRYSALIDE_CORE_UI_INT_H + + +#include "core-ui.h" + + +#include "core-int.h" + + + +/* Greffon natif pour la liaison Python de Chrysalide sous forme graphique (instance) */ +struct _GPyChrysalidePluginUI +{ + GPyChrysalidePlugin parent; /* A laisser en premier */ + +}; + + +/* Greffon natif pour la liaison Python de Chrysalide sous forme graphique (classe) */ +struct _GPyChrysalidePluginUIClass +{ + GPyChrysalidePluginClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un module pour un greffon de support Python. */ +bool g_pychrysalide_plugin_ui_create(GPyChrysalidePluginUI *, GModule *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_CORE_UI_INT_H */ diff --git a/plugins/pychrysalide/core-ui.c b/plugins/pychrysalide/core-ui.c new file mode 100644 index 0000000..1b332b7 --- /dev/null +++ b/plugins/pychrysalide/core-ui.c @@ -0,0 +1,319 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-ui.c - plugin permettant des extensions UI en Python + * + * Copyright (C) 2025 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/>. + */ + + +#include "core-ui.h" + + +#include <stdbool.h> + + +#include <i18n.h> +#include <plugins/self.h> + + +#include "bindings.h" +#include "core-ui-int.h" + + + +/* Note la nature du chargement */ +static bool _standalone = true; + + + +/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */ + + +/* Initialise la classe des greffons de support Python. */ +static void g_pychrysalide_plugin_ui_class_init(GPyChrysalidePluginUIClass *); + +/* Initialise une instance de greffon de support Python. */ +static void g_pychrysalide_plugin_ui_init(GPyChrysalidePluginUI *); + +/* Supprime toutes les références externes. */ +static void g_pychrysalide_plugin_ui_dispose(GPyChrysalidePluginUI *); + +/* Procède à la libération totale de la mémoire. */ +static void g_pychrysalide_plugin_ui_finalize(GPyChrysalidePluginUI *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Prend acte de l'activation du greffon. */ +static bool g_pychrysalide_plugin_ui_enable(GPyChrysalidePluginUI *); + + + +/* ---------------------------------------------------------------------------------- */ +/* COMPOSITION DE NOUVEAU GREFFON NATIF */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un greffon de liaison Python */ +G_DEFINE_TYPE(GPyChrysalidePluginUI, g_pychrysalide_plugin_ui, G_TYPE_PYCHRYSALIDE_PLUGIN); + + +NATIVE_PLUGIN_ENTRYPOINT(g_pychrysalide_plugin_ui_new); + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* * +* Description : Initialise la classe des greffons de support Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pychrysalide_plugin_ui_class_init(GPyChrysalidePluginUIClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GPluginModuleClass *plugin; /* Version parente de la classe*/ + + object = G_OBJECT_CLASS(class); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_pychrysalide_plugin_ui_dispose; + object->finalize = (GObjectFinalizeFunc)g_pychrysalide_plugin_ui_finalize; + + plugin = G_PLUGIN_MODULE_CLASS(class); + + plugin->enable = (pg_management_fc)g_pychrysalide_plugin_ui_enable; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser. * +* * +* Description : Initialise une instance de greffon de support Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pychrysalide_plugin_ui_init(GPyChrysalidePluginUI *plugin) +{ + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pychrysalide_plugin_ui_dispose(GPyChrysalidePluginUI *plugin) +{ + G_OBJECT_CLASS(g_pychrysalide_plugin_ui_parent_class)->dispose(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_pychrysalide_plugin_ui_finalize(GPyChrysalidePluginUI *plugin) +{ + G_OBJECT_CLASS(g_pychrysalide_plugin_ui_parent_class)->finalize(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* Paramètres : filename = nom du fichier à charger. * +* * +* Description : Crée un module pour un greffon de support Python. * +* * +* Retour : Adresse de la structure mise en place. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +GPluginModule *g_pychrysalide_plugin_ui_new(GModule *module) +{ + GPyChrysalidePluginUI *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_PYCHRYSALIDE_PLUGIN_UI, NULL); + + if (!g_pychrysalide_plugin_ui_create(result, module)) + g_clear_object(&result); + + return G_PLUGIN_MODULE(result); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser pleinement. * +* module = module système correspondant. * +* * +* Description : Met en place un module pour un greffon de support Python. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +bool g_pychrysalide_plugin_ui_create(GPyChrysalidePluginUI *plugin, GModule *module) +{ + bool result; /* Bilan à retourner */ + + result = g_pychrysalide_plugin_create(G_PYCHRYSALIDE_PLUGIN(plugin), module); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* * +* Description : Prend acte de l'activation du greffon. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_pychrysalide_plugin_ui_enable(GPyChrysalidePluginUI *plugin) +{ + bool result; /* Bilan à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + int ret; /* Bilan de préparatifs */ + + _standalone = false; + + /* Chargement du module pour Python */ + + ret = PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalideui); + + if (ret == -1) + { + g_plugin_module_log_simple_message(G_PLUGIN_MODULE(plugin), + LMT_ERROR, + _("Can not extend the existing table of Python built-in modules.")); + + result = false; + goto done; + + } + + Py_Initialize(); + + gstate = PyGILState_Ensure(); + + G_PYCHRYSALIDE_PLUGIN(plugin)->py_module = PyImport_ImportModule("pychrysalide"); + + /** + * Pour mémoire, une situation concrête conduisant à un échec : + * le paquet python3-gi-dbg n'est pas installé alors que le + * programme est compilé en mode débogage. + * + * Dans ce cas, pygobject_init(-1, -1, -1) échoue, et Py_Initialize() + * le laisse rien filtrer... + * + * En mode autonome, le shell Python remonte bien l'erreur par contre. + */ + + // TODO : check (2025) + + result = (G_PYCHRYSALIDE_PLUGIN(plugin)->py_module != NULL); + + PyGILState_Release(gstate); + + done: + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* POINT D'ENTREE POUR PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Point d'entrée pour l'initialisation de Python. * +* * +* Retour : ? * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyMODINIT_FUNC PyInit_pychrysalideui(void) +{ + PyObject *result; /* Module Python à retourner */ + pyinit_details_t details; /* Détails de chargement */ + + details.standalone = _standalone; + + details.populate_extra = NULL; + details.create_self = g_pychrysalide_plugin_ui_new; + + result = init_python_pychrysalide_module(&details); + + return result; + +} diff --git a/plugins/pychrysalide/core-ui.h b/plugins/pychrysalide/core-ui.h new file mode 100644 index 0000000..658aa19 --- /dev/null +++ b/plugins/pychrysalide/core-ui.h @@ -0,0 +1,64 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-ui.h - prototypes pour le plugin permettant des extensions UI en Python + * + * Copyright (C) 2025 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 _PLUGINS_PYCHRYSALIDE_CORE_UI_H +#define _PLUGINS_PYCHRYSALIDE_CORE_UI_H + + +/** + * Note: + * Since Python may define some pre-processor definitions which affect the standard headers + * on some systems, you must include Python.h before any standard headers are included. + * + * cf. https://docs.python.org/3.4/c-api/intro.html + */ +#include <Python.h> + + +#include <glibext/helpers.h> +#include <plugins/plugin.h> + + + +/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */ + + +#define G_TYPE_PYCHRYSALIDE_PLUGIN_UI (g_pychrysalide_plugin_ui_get_type()) + +DECLARE_GTYPE(GPyChrysalidePluginUI, g_pychrysalide_plugin_ui, G, PYCHRYSALIDE_PLUGIN_UI); + + +/* Crée un module pour un greffon de support Python. */ +GPluginModule *g_pychrysalide_plugin_ui_new(GModule *); + + + +/* --------------------------- POINT D'ENTREE POUR PYTHON --------------------------- */ + + +/* Point d'entrée pour l'initialisation de Python. */ +PyMODINIT_FUNC PyInit_pychrysalideui(void); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_CORE_UI_H */ diff --git a/plugins/pychrysalide/core.c b/plugins/pychrysalide/core.c index 8d69933..0e72b46 100644 --- a/plugins/pychrysalide/core.c +++ b/plugins/pychrysalide/core.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * core.c - plugin permettant des extensions en Python * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,6 +21,15 @@ */ +#include "core.h" + + +#include "core-int.h" + + + + + #undef NO_IMPORT_PYGOBJECT #include <pygobject.h> #define NO_IMPORT_PYGOBJECT @@ -32,9 +41,8 @@ #include <assert.h> #include <errno.h> #include <malloc.h> -#include <pygobject.h> +//#include <pygobject.h> #include <stdarg.h> -#include <stdio.h> #include <stdbool.h> #include <string.h> #include <unistd.h> @@ -42,741 +50,402 @@ #include <i18n.h> #include <gleak.h> -#include <common/cpp.h> #include <common/environment.h> #include <common/extstr.h> #include <core/core.h> #include <core/logs.h> #include <core/paths.h> -#include <plugins/pglist.h> +#include <plugins/manager-int.h> +#include <plugins/plugin.h> #include <plugins/self.h> + + #include "access.h" -#include "constants.h" -#include "helpers.h" -#include "star.h" -#include "strenum.h" -#include "struct.h" -#include "analysis/module.h" -#include "arch/module.h" -#include "common/module.h" -#include "core/module.h" -#include "glibext/module.h" -/* #include "debug/module.h" */ -#include "format/module.h" -/* #ifdef INCLUDE_GTK_SUPPORT */ -/* # include "gtkext/module.h" */ -/* # include "gui/module.h" */ -/* #endif */ -/* #include "mangling/module.h" */ -#include "plugins/module.h" -#include "plugins/plugin.h" - - - -DEFINE_CHRYSALIDE_CONTAINER_PLUGIN("PyChrysalide", "Chrysalide bindings to Python", - PACKAGE_VERSION, CHRYSALIDE_WEBSITE("api/python/pychrysalide"), - NO_REQ, AL(PGA_PLUGIN_INIT, PGA_PLUGIN_EXIT, - PGA_NATIVE_PLUGINS_LOADED, PGA_TYPE_BUILDING)); +#include "bindings.h" + /* Note la nature du chargement */ static bool _standalone = true; -/* Réceptacle pour le chargement forcé */ -static PyObject *_chrysalide_module = NULL; -/* Fournit la révision du programme global. */ -static PyObject *py_chrysalide_revision(PyObject *, PyObject *); -/* Fournit la version du programme global. */ -static PyObject *py_chrysalide_version(PyObject *, PyObject *); -/* Fournit la version du greffon pour Python. */ -static PyObject *py_chrysalide_mod_version(PyObject *, PyObject *); -/* Détermine si l'interpréteur lancé est celui pris en compte. */ -static bool is_current_abi_suitable(void); -/* Assure une pleine initialisation des objets de Python-GI. */ -static bool install_metaclass_for_python_gobjects(void); -/* Définit la version attendue de GTK à charger dans Python. */ -#ifdef INCLUDE_GTK_SUPPORT -static bool set_version_for_gtk_namespace(const char *); -#endif -/* Point de sortie pour l'initialisation de Python. */ -static void PyExit_pychrysalide(void); + + +/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */ + + +/* Initialise la classe des greffons de support Python. */ +static void g_pychrysalide_plugin_class_init(GPyChrysalidePluginClass *); + +/* Procède à l'initialisation de l'interface de gestion. */ +static void g_pychrysalide_plugin_plugin_manager_interface_init(GPluginManagerInterface *); + +/* Initialise une instance de greffon de support Python. */ +static void g_pychrysalide_plugin_init(GPyChrysalidePlugin *); + +/* Supprime toutes les références externes. */ +static void g_pychrysalide_plugin_dispose(GPyChrysalidePlugin *); + +/* Procède à la libération totale de la mémoire. */ +static void g_pychrysalide_plugin_finalize(GPyChrysalidePlugin *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Prend acte de l'activation du greffon. */ +static bool g_pychrysalide_plugin_enable(GPyChrysalidePlugin *); + +/* Prend acte de la désactivation du greffon. */ +static bool g_pychrysalide_plugin_disable(GPyChrysalidePlugin *); + + + +/* -------------------- INTERVENTION DANS LA GESTION DE GREFFONS -------------------- */ + /* Complète les chemins de recherches de Python. */ static void extend_python_path(const char *); +/* Crée un greffon à partir de code Python. */ +static GPluginModule *create_python_plugin(const char *, const char *); + /* Charge autant de greffons composés en Python que possible. */ static void load_python_plugins(GPluginModule *); -/* Efface un type Python pour greffon de la mémoire. */ -static void free_native_plugin_type(PyTypeObject *); +/* Prend acte du chargement de l'ensemble des greffons natifs. */ +static void g_pychrysalide_plugin_handle_native_plugins_loaded_event(GPyChrysalidePlugin *); + + + +/* ---------------------------------------------------------------------------------- */ +/* COMPOSITION DE NOUVEAU GREFFON NATIF */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un greffon de liaison Python */ +G_DEFINE_TYPE_WITH_CODE(GPyChrysalidePlugin, g_pychrysalide_plugin, G_TYPE_NATIVE_PLUGIN, + G_IMPLEMENT_INTERFACE(G_TYPE_PLUGIN_MANAGER, g_pychrysalide_plugin_plugin_manager_interface_init)); + +NATIVE_PLUGIN_ENTRYPOINT(g_pychrysalide_plugin_new); /****************************************************************************** * * -* Paramètres : self = NULL car méthode statique. * -* args = non utilisé ici. * +* Paramètres : class = classe à initialiser. * * * -* Description : Fournit la révision du programme global. * +* Description : Initialise la classe des greffons de support Python. * * * -* Retour : Numéro de révision. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_chrysalide_revision(PyObject *self, PyObject *args) +static void g_pychrysalide_plugin_class_init(GPyChrysalidePluginClass *class) { - PyObject *result; /* Valeur à retourner */ + GObjectClass *object; /* Autre version de la classe */ + GPluginModuleClass *plugin; /* Version parente de la classe*/ -#define PY_CHRYSALIDE_REVISION_METHOD PYTHON_METHOD_DEF \ -( \ - revision, "/", \ - METH_NOARGS, py_chrysalide, \ - "Provide the revision number of Chrysalide.\n" \ - "\n" \ - "The returned value is provided as a string, for instance: 'r1665'." \ -) + object = G_OBJECT_CLASS(class); - result = PyUnicode_FromString("r" XSTR(REVISION)); + object->dispose = (GObjectFinalizeFunc/* ! */)g_pychrysalide_plugin_dispose; + object->finalize = (GObjectFinalizeFunc)g_pychrysalide_plugin_finalize; - return result; + plugin = G_PLUGIN_MODULE_CLASS(class); + + plugin->enable = (pg_management_fc)g_pychrysalide_plugin_enable; + plugin->disable = (pg_management_fc)g_pychrysalide_plugin_disable; } /****************************************************************************** * * -* Paramètres : self = NULL car méthode statique. * -* args = non utilisé ici. * +* Paramètres : iface = interface GLib à initialiser. * * * -* Description : Fournit la version du programme global. * +* Description : Procède à l'initialisation de l'interface de gestion. * * * -* Retour : Numéro de version. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_chrysalide_version(PyObject *self, PyObject *args) +static void g_pychrysalide_plugin_plugin_manager_interface_init(GPluginManagerInterface *iface) { - PyObject *result; /* Valeur à retourner */ - int major; /* Numéro de version majeur */ - int minor; /* Numéro de version mineur */ - int revision; /* Numéro de révision */ - char version[16]; /* Conservation temporaire */ - -#define PY_CHRYSALIDE_VERSION_METHOD PYTHON_METHOD_DEF \ -( \ - version, "/", \ - METH_NOARGS, py_chrysalide, \ - "Provide the version number of Chrysalide.\n" \ - "\n" \ - "The returned value is provided as a string, for instance: '1.6.65'." \ -) - - major = REVISION / 1000; - minor = (REVISION - (major * 1000)) / 100; - revision = REVISION % 100; - - snprintf(version, sizeof(version), "%d.%d.%d", major, minor, revision); - - result = PyUnicode_FromString(version); - - return result; + iface->handle_native = (handle_native_plugins_cb)g_pychrysalide_plugin_handle_native_plugins_loaded_event; } /****************************************************************************** * * -* Paramètres : self = NULL car méthode statique. * -* args = non utilisé ici. * +* Paramètres : plugin = instance à initialiser. * * * -* Description : Fournit la version du greffon pour Python. * +* Description : Initialise une instance de greffon de support Python. * * * -* Retour : Numéro de version. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_chrysalide_mod_version(PyObject *self, PyObject *args) +static void g_pychrysalide_plugin_init(GPyChrysalidePlugin *plugin) { - PyObject *result; /* Valeur à retourner */ - char version[16]; /* Conservation temporaire */ + STORE_PLUGIN_ABI(plugin); -#define PY_CHRYSALIDE_MOD_VERSION_METHOD PYTHON_METHOD_DEF \ -( \ - mod_version, "/", \ - METH_NOARGS, py_chrysalide, \ - "Provide the version number of Chrysalide module for Python.\n" \ - "\n" \ - "The returned value is provided as a string, for instance: '0.1.0'." \ -) - - snprintf(version, sizeof(version), "%s", _chrysalide_plugin.version); - - result = PyUnicode_FromString(version); - - return result; + plugin->py_module = NULL; } /****************************************************************************** * * -* Paramètres : - * +* Paramètres : plugin = instance d'objet GLib à traiter. * * * -* Description : Détermine si l'interpréteur lancé est celui pris en compte. * +* Description : Supprime toutes les références externes. * * * -* Retour : true si l'exécution peut se poursuivre, false sinon. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static bool is_current_abi_suitable(void) +static void g_pychrysalide_plugin_dispose(GPyChrysalidePlugin *plugin) { - bool result; - int fds[2]; - int ret; - char cmds[128]; - char content[64]; - ssize_t got; - -#define GRAB_ABI_FLAGS_IN_PYTHON \ - "import sys" "\n" \ - "import os" "\n" \ - "data = bytes(sys.abiflags, 'UTF-8') + b'\\0'" "\n" \ - "os.write(%d, data)" "\n" - - result = false; - - ret = pipe(fds); - if (ret == -1) - { - perror("pipe()"); - return false; - } - - snprintf(cmds, sizeof(cmds), GRAB_ABI_FLAGS_IN_PYTHON, fds[1]); - - ret = PyRun_SimpleString(cmds); - if (ret != 0) goto icas_exit; - - got = read(fds[0], content, sizeof(content)); - if (got < 0) - { - perror("read()"); - goto icas_exit; - } - - content[got] = '\0'; - - result = (strcmp(content, LIBPYTHON_ABI_FLAGS) == 0); - - icas_exit: - - if (!result) - PyErr_SetString(PyExc_SystemError, "the ABI flags of the current interpreter do not match " \ - "the ones of the Python library used during the module compilation."); - - return result; + G_OBJECT_CLASS(g_pychrysalide_plugin_parent_class)->dispose(G_OBJECT(plugin)); } /****************************************************************************** * * -* Paramètres : - * +* Paramètres : plugin = instance d'objet GLib à traiter. * * * -* Description : Assure une pleine initialisation des objets de Python-GI. * +* Description : Procède à la libération totale de la mémoire. * * * -* Retour : Bilan de l'opération. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static bool install_metaclass_for_python_gobjects(void) +static void g_pychrysalide_plugin_finalize(GPyChrysalidePlugin *plugin) { - bool result; /* Bilan à retourner */ - PyObject *gi_types_mod; /* Module Python-GObject */ - - /** - * Les extensions Python sont chargées à partir de la fonction load_python_plugins(), - * qui fait appel à create_python_plugin(). Une instance y est construite via un - * appel à PyObject_CallFunction() avec la classe spécifiée par l'alias AutoLoad - * dans le fichier __init__.py présent dans chaque module d'extension. - * - * Le constructeur py_plugin_module_new() renvoie in fine à la fonction générique - * python_abstract_constructor_with_dynamic_gtype(), laquelle conduit à la fonction - * pygobject_register_class() définie dans <python3-gi>/gi/pygobject-object.c. - * Le code de cette dernière comprend notamment la portion suivante : - * - * [...] - * Py_SET_TYPE(type, PyGObject_MetaType); - * [...] - * if (PyType_Ready(type) < 0) { - * g_warning ("couldn't make the type `%s' ready", type->tp_name); - * return; - * } - * [...] - * - * La fonction PyType_Ready() est définie dans <python3>/Objects/typeobject.c - * et commence par : - * - * int PyType_Ready(PyTypeObject *type) - * { - * if (type->tp_flags & Py_TPFLAGS_READY) { - * assert(_PyType_CheckConsistency(type)); - * return 0; - * } - * [...] - * } - * - * La vérification de cohérencce commence par analyser le type et son propre - * type : - * - * - cf. _PyType_CheckConsistency() dans <python3>/Objects/typeobject.c : - * - * int _PyType_CheckConsistency(PyTypeObject *type) - * { - * [...] - * CHECK(!_PyObject_IsFreed((PyObject *)type)); - * [...] - * } - * - * - cf. _PyObject_IsFreed() dans <python3>/Objects/object.c : - * - * int _PyObject_IsFreed(PyObject *op) - * { - * if (_PyMem_IsPtrFreed(op) || _PyMem_IsPtrFreed(Py_TYPE(op))) { - * return 1; - * } - * - * La fonction _PyMem_IsPtrFreed() recherche entre autres la valeur NULL. - * - * Or le type du type est écrasé dans la fonction pygobject_register_class() - * avec la valeur de la variable PyGObject_MetaType. Cette variable n'est - * définie qu'à un seul endroit, dans <python3-gi>/gi/gimodule.c : - * - * static PyObject * - * pyg__install_metaclass(PyObject *dummy, PyTypeObject *metaclass) - * { - * Py_INCREF(metaclass); - * PyGObject_MetaType = metaclass; - * Py_INCREF(metaclass); - * - * Py_SET_TYPE(&PyGObject_Type, metaclass); - * - * Py_INCREF(Py_None); - * return Py_None; - * } - * - * Afin de valider la vérification de _PyType_CheckConsistency() pour les - * modules externes qui entraînent un enregistrement tout en portant le drapeau - * Py_TPFLAGS_READY (typiquement ceux du répertoire "plugins/python/", il faut - * initialiser au besoin la variable PyGObject_MetaType. - * - * Une ligne suffit donc à enregistrer le type intermédiaire : - * - * from _gi import types - * - * On simule ici une déclaration similaire si nécessaire - */ - - result = false; - - if (PyType_CheckExact(&PyGObject_Type)) - { - gi_types_mod = PyImport_ImportModule("gi.types"); - - result = (PyErr_Occurred() == NULL); - - if (result) - result = (PyType_CheckExact(&PyGObject_Type) == 0); - - Py_XDECREF(gi_types_mod); - - } - - return result; + G_OBJECT_CLASS(g_pychrysalide_plugin_parent_class)->finalize(G_OBJECT(plugin)); } /****************************************************************************** * * -* Paramètres : version = idenfiant de la version de GTK à stipuler. * +* Paramètres : filename = nom du fichier à charger. * * * -* Description : Définit la version attendue de GTK à charger dans Python. * +* Description : Crée un module pour un greffon de support Python. * * * -* Retour : Bilan de l'opération. * +* Retour : Adresse de la structure mise en place. * * * -* Remarques : - * +* Remarques : Le transfert de propriétée du module est total. * * * ******************************************************************************/ -#ifdef INCLUDE_GTK_SUPPORT -static bool set_version_for_gtk_namespace(const char *version) -{ - bool result; /* Bilan à retourner */ - PyObject *gi_mod; /* Module Python-GObject */ - PyObject *args; /* Arguments à fournir */ - - result = false; - - /** - * On cherche ici à éviter le message suivant si on charge 'gi.repository.Gtk' directement : - * - * - * PyGIWarning: Gtk was imported without specifying a version first. \ - * Use gi.require_version('Gtk', '3.0') before import to ensure that the right version gets loaded. - * - */ - - gi_mod = PyImport_ImportModule("gi"); - - if (gi_mod != NULL) - { - args = Py_BuildValue("ss", "Gtk", version); - - run_python_method(gi_mod, "require_version", args); - result = (PyErr_Occurred() == NULL); +GPluginModule *g_pychrysalide_plugin_new(GModule *module) +{ + GPyChrysalidePlugin *result; /* Structure à retourner */ - Py_DECREF(args); - Py_DECREF(gi_mod); + result = g_object_new(G_TYPE_PYCHRYSALIDE_PLUGIN, NULL); - } + if (!g_pychrysalide_plugin_create(result, module)) + g_clear_object(&result); - return result; + return G_PLUGIN_MODULE(result); } -#endif /****************************************************************************** * * -* Paramètres : - * +* Paramètres : plugin = instance à initialiser pleinement. * +* module = module système correspondant. * * * -* Description : Point de sortie pour l'initialisation de Python. * +* Description : Met en place un module pour un greffon de support Python. * * * -* Retour : ? * +* Retour : Bilan de l'opération. * * * -* Remarques : - * +* Remarques : Le transfert de propriétée du module est total. * * * ******************************************************************************/ -static void PyExit_pychrysalide(void) +bool g_pychrysalide_plugin_create(GPyChrysalidePlugin *plugin, GModule *module) { - assert(_standalone); - - /* - extern void set_current_project(void *project); + bool result; /* Bilan à retourner */ - set_current_project(NULL); - */ + result = g_native_plugin_create(G_NATIVE_PLUGIN(plugin), + "PyChrysalide", + "Chrysalide bindings to Python", + PACKAGE_VERSION, + CHRYSALIDE_WEBSITE("api/python/pychrysalide"), + NO_REQ, + module); -#ifdef TRACK_GOBJECT_LEAKS - remember_gtypes_for_leaks(); -#endif + return result; - exit_all_plugins(); +} - //unload_all_core_components(true); -#ifdef TRACK_GOBJECT_LEAKS - dump_remaining_gtypes(); -#endif -} +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : - * +* Paramètres : plugin = greffon à manipuler. * * * -* Description : Point d'entrée pour l'initialisation de Python. * +* Description : Prend acte de l'activation du greffon. * * * -* Retour : ? * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -#define PYCHRYSALIDE_DOC \ - "PyChrysalide is a module containing Chrysalide's features and designed for Python users.\n" \ - "\n" \ - "The whole API is defined in a single library named 'pychrysalide.so' and can be used in two ways:\n" \ - "* either from the Chrysalide's GUI, by registering hooks or GLib signals;\n" \ - "* or from a shell command line, by setting PYTHONPATH to point to the directory containing the library.\n" \ - "\n" \ - "In both cases, this is a good start point to have a look at already existing plugins to quickly learn " \ - "how the API works.\n" \ - "\n" \ - "These plugins are located in the 'plugins/python' directory.\n" \ - "\n" \ - "The *pychrysalide* module imports the GLib module (version 2.0) from the GI repository at startup." - -PyMODINIT_FUNC PyInit_pychrysalide(void) +static bool g_pychrysalide_plugin_enable(GPyChrysalidePlugin *plugin) { - PyObject *result; /* Module Python à retourner */ - bool status; /* Bilan des inclusions */ + bool result; /* Bilan à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ int ret; /* Bilan de préparatifs */ -#ifdef PYTHON_PACKAGE - Dl_info info; /* Informations dynamiques */ -#endif - GPluginModule *self; /* Représentation interne */ - PluginStatusFlags self_flags; /* Fanions à mettre à jour */ - - static PyMethodDef py_chrysalide_methods[] = { - PY_CHRYSALIDE_REVISION_METHOD, - PY_CHRYSALIDE_VERSION_METHOD, - PY_CHRYSALIDE_MOD_VERSION_METHOD, - { NULL } - }; - - static PyModuleDef py_chrysalide_module = { - - .m_base = PyModuleDef_HEAD_INIT, - - .m_name = "pychrysalide", - .m_doc = PYCHRYSALIDE_DOC, - .m_size = -1, + _standalone = false; - .m_methods = py_chrysalide_methods + /* Chargement du module pour Python */ - }; + ret = PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalide); - /** - * Vérification préalable : dans le cas où on est embarqué directement dans - * un interpréteur Python, le module se charge et termine par charger à leur - * tour les différentes extensions trouvées, via load_remaning_plugins() puis - * chrysalide_plugin_on_native_loaded(). - * - * Lesquelles vont très probablement charger le module pychrysalide. - * - * Comme le chargement de ce dernier n'est alors pas encore terminé, - * Python va relancer cette procédure, et register_access_to_python_module() - * va détecter un doublon. - */ + if (ret == -1) + { + g_plugin_module_log_simple_message(G_PLUGIN_MODULE(plugin), + LMT_ERROR, + _("Can not extend the existing table of Python built-in modules.")); - result = get_access_to_python_module(py_chrysalide_module.m_name); + result = false; + goto done; - if (result != NULL) - { - Py_INCREF(result); - return result; } - if (!is_current_abi_suitable()) - goto exit; + Py_Initialize(); - if (pygobject_init(-1, -1, -1) == NULL) - { - PyErr_SetString(PyExc_SystemError, "unable to init GObject in Python."); - goto exit; - } + gstate = PyGILState_Ensure(); - if (!install_metaclass_for_python_gobjects()) - goto exit; + plugin->py_module = PyImport_ImportModule("pychrysalide"); /** - * Le chargement forcé de l'espace GLib pour Python permet d'éviter un écueil, - * à savoir des types convertis de façon incomplète. Par exemple, pour une - * structure GChecksum, le type à l'exécution est : - * - * - sans module GLib : [<class 'gobject.GBoxed'>, <class 'object'>] - * - * - avec module GLib : [<class 'gi.repository.GLib.Checksum'>, <class 'gi.Boxed'>, <class 'gobject.GBoxed'>, <class 'object'>] - * - * Par ailleurs, il est à noter que le message suivant n'apparaît qu'avec - * la version debug de Python3 (version de python3-gi : 3.42.2-3) : - * - * <frozen importlib._bootstrap>:673: ImportWarning: DynamicImporter.exec_module() not found; falling back to load_module() - * - * Code de reproduction dans un interpréteur classique : + * Pour mémoire, une situation concrête conduisant à un échec : + * le paquet python3-gi-dbg n'est pas installé alors que le + * programme est compilé en mode débogage. * - * import gi - * gi.require_version('GLib', '2.0') - * from gi.repository import GLib + * Dans ce cas, pygobject_init(-1, -1, -1) échoue, et Py_Initialize() + * le laisse rien filtrer... * + * En mode autonome, le shell Python remonte bien l'erreur par contre. */ - if (!import_namespace_from_gi_repository("GLib", "2.0")) - goto exit; - -#if 0 -#ifdef INCLUDE_GTK_SUPPORT - if (!set_version_for_gtk_namespace("3.0")) - goto exit; -#endif -#endif - if (!load_core_components(ACC_GLOBAL_VARS)) - { - PyErr_SetString(PyExc_SystemError, "unable to load all basic components."); - goto exit; - } - - /* Mise en place des fonctionnalités offertes */ - - result = PyModule_Create(&py_chrysalide_module); + // TODO : check (2025) - register_access_to_python_module(py_chrysalide_module.m_name, result); + result = (plugin->py_module != NULL); - status = true; - - if (status) status = add_features_module(result); - - if (status) status = add_analysis_module(result); - if (status) status = add_arch_module(result); - if (status) status = add_common_module(result); - if (status) status = add_glibext_module(result); - if (status) status = add_core_module(result); - /* - if (status) status = add_debug_module(result); - */ - if (status) status = add_format_module(result); - /* -#ifdef INCLUDE_GTK_SUPPORT - if (status) status = add_gtkext_module(result); - if (status) status = add_gui_module(result); -#endif - if (status) status = add_mangling_module(result); - */ - if (status) status = add_plugins_module(result); - - /* - if (status) status = ensure_python_string_enum_is_registered(); - */ - if (status) status = ensure_python_py_struct_is_registered(); - - if (status) status = define_data_types_constants(result); - - if (status) status = populate_analysis_module(); - if (status) status = populate_arch_module(); - if (status) status = populate_glibext_module(); - if (status) status = populate_common_module(); - if (status) status = populate_core_module(); - /* - if (status) status = populate_debug_module(); - */ - if (status) status = populate_format_module(); - /* -#ifdef INCLUDE_GTK_SUPPORT - if (status) status = populate_gtkext_module(); - if (status) status = populate_gui_module(); -#endif - if (status) status = populate_mangling_module(); - */ - if (status) status = populate_plugins_module(); + PyGILState_Release(gstate); - if (!status) - { - PyErr_SetString(PyExc_SystemError, "failed to load all PyChrysalide components."); - Py_DECREF(result); - result = NULL; - goto exit; - } + done: - if (_standalone) - { - ret = Py_AtExit(PyExit_pychrysalide); + return result; - if (ret == -1) - { - PyErr_SetString(PyExc_SystemError, "failed to register a cleanup function."); - Py_DECREF(result); - result = NULL; - goto exit; - } +} - /** - * Comme les sources locales sont prioritaires, le fichier "core/global.h" - * du greffon masque la fonction suivante, issue du corps principal du - * programme. - * - * On la déclare donc à la main. - */ - /* - extern void set_batch_mode(void); - - set_batch_mode(); - */ - - /** - * Si cette extension pour Python est chargée depuis un dépôt Python, - * elle ne se trouve pas dans le répertoire classique des extensions et - * n'est donc pas chargée et enregistrée comme attendu. - * - * Cet enregistrement est donc forcé ici. - */ - -#ifdef PYTHON_PACKAGE - - ret = dladdr(__FUNCTION__, &info); - if (ret == 0) - { - LOG_ERROR_DL_N("dladdr"); - Py_DECREF(result); - result = NULL; - goto exit; - } - self = g_plugin_module_new(info.dli_fname); - assert(self != NULL); +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* * +* Description : Prend acte de la désactivation du greffon. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ - register_plugin(self); +static bool g_pychrysalide_plugin_disable(GPyChrysalidePlugin *plugin) +{ + bool result; /* Bilan à retourner */ + bool standalone; /* Nature du chargement */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ -#endif + result = true; - init_all_plugins(false); + /** + * Le champ plugin->py_module n'est défini que via la fonction + * g_pychrysalide_plugin_enable(), qui n'est pas sollicitée lorsque + * le module PyChrysalide est mis en place directement par Python. + * + * L'analyse de ce champ pour retrouver la situation courante est + * plus fiable que celle du champ _standalone, potentiellement + * cohérent dans la version UI du greffon et resté à son état + * initial ici. + */ - lock_plugin_list_for_reading(); + standalone = (plugin->py_module == NULL); - self = get_plugin_by_name("PyChrysalide", NULL); - assert(self != NULL); + /** + * Si on se trouve embarqué dans un interpréteur Python, le déchargement + * des greffons est organisé à partir de la fonction PyExit_pychrysalide(), + * directement appelée depuis un contexte Python. + * + * Un verrou n'est alors pas souhaité ici : + * + * python3d: ../Python/pystate.c:1687: PyGILState_Ensure: Assertion `gilstate->autoInterpreterState' failed. + * + * Avec : + * + * $ python3d --version + * Python 3.11.2 + * + */ - self_flags = g_plugin_module_get_flags(self); - self_flags &= ~(PSF_FAILURE | PSF_LOADED); - self_flags |= (status ? PSF_LOADED : PSF_FAILURE); + if (!standalone) + gstate = PyGILState_Ensure(); - g_plugin_module_override_flags(self, self_flags); + clear_all_accesses_to_python_modules(); - unlock_plugin_list_for_reading(); + Py_XDECREF(plugin->py_module); + plugin->py_module = NULL; - load_remaning_plugins(); + if (!standalone) + PyGILState_Release(gstate); - /** - * On laisse fuir ici la référence sur self afin d'avoir - * l'assurance que le greffon se déchargera toujours en dernier. - * - * La fuite mémoire est au final évitée dans PyExit_pychrysalide(). - */ + return result; - } +} - exit: - if (result == NULL && !_standalone) - log_pychrysalide_exception("Loading failed"); - return result; +/* ---------------------------------------------------------------------------------- */ +/* INTERVENTION DANS LA GESTION DE GREFFONS */ +/* ---------------------------------------------------------------------------------- */ -} /****************************************************************************** * * @@ -812,6 +481,81 @@ static void extend_python_path(const char *path) /****************************************************************************** * * +* Paramètres : modname = nom du module à charger. * +* filename = chemin d'accès au code Python à charger. * +* * +* Description : Crée un greffon à partir de code Python. * +* * +* Retour : Adresse de la structure mise en place ou NULL si erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static GPluginModule *create_python_plugin(const char *modname, const char *filename) +{ + GPluginModule *result; /* Structure à retourner */ + PyObject *name; /* Chemin d'accès pour Python */ + PyObject *module; /* Script Python chargé */ + PyObject *dict; /* Dictionnaire associé */ + PyObject *class; /* Classe à instancier */ + PyObject *instance; /* Instance Python du greffon */ + + name = PyUnicode_FromString(modname); + if (name == NULL) goto bad_exit; + + module = PyImport_Import(name); + Py_DECREF(name); + + if (module == NULL) goto no_import; + + dict = PyModule_GetDict(module); + class = PyDict_GetItemString(dict, "AutoLoad"); + + if (class == NULL) goto no_class; + if (!PyType_Check(class->ob_type)) goto no_class; + + instance = PyObject_CallFunction(class, NULL); + if (instance == NULL) goto no_instance; + + result = G_PLUGIN_MODULE(pygobject_get(instance)); + + /** + * L'instance Python et l'objet GLib résultant sont un même PyGObject. + * + * Donc pas besoin de toucher au comptage des références ici, la libération + * se réalisera à la fin, quand l'objet GLib sera libéré. + */ + + Py_DECREF(module); + + return result; + + no_instance: + + log_pychrysalide_exception(_("An error occured when building the 'AutoLoad' instance")); + + no_class: + + if (class == NULL) + log_plugin_simple_message(LMT_ERROR, + _("An error occured when looking for the 'AutoLoad': item not found!")); + + no_import: + + Py_XDECREF(module); + + log_pychrysalide_exception(_("An error occured when importing '%s'"), modname); + + bad_exit: + + return NULL; + +} + + +/****************************************************************************** +* * * Paramètres : plugin = instance représentant le greffon Python d'origine. * * * * Description : Charge autant de greffons composés en Python que possible. * @@ -836,7 +580,6 @@ static void load_python_plugins(GPluginModule *plugin) char *filename; /* Chemin d'accès reconstruit */ GPluginModule *pyplugin; /* Lien vers un grffon Python */ bool status; /* Bilan d'une opération */ - //GGenConfig *config; /* Configuration à charger */ /* Définition des zones d'influence */ @@ -847,19 +590,19 @@ static void load_python_plugins(GPluginModule *plugin) #else edir = get_effective_directory(PLUGINS_DATA_DIR G_DIR_SEPARATOR_S "python"); + dir = opendir(edir); - free(edir); if (dir != NULL) { closedir(dir); - edir = get_effective_directory(PLUGINS_DATA_DIR G_DIR_SEPARATOR_S "python"); extend_python_path(edir); - free(edir); } + free(edir); + #endif g_plugin_module_log_variadic_message(plugin, LMT_INFO, @@ -923,8 +666,7 @@ static void load_python_plugins(GPluginModule *plugin) goto done_with_plugin; } - //g_plugin_module_create_config(pyplugin); - + /* status = g_plugin_module_manage(pyplugin, PGA_PLUGIN_LOADED); if (!status) @@ -934,18 +676,19 @@ static void load_python_plugins(GPluginModule *plugin) g_object_unref(G_OBJECT(pyplugin)); goto done_with_plugin; } - - /* - config = g_plugin_module_get_config(pyplugin); - g_generic_config_read(config); - g_object_unref(G_OBJECT(config)); */ g_plugin_module_log_variadic_message(plugin, LMT_PROCESS, _("Loaded the Python plugin found in the '<b>%s</b>' directory"), filename); - _register_plugin(pyplugin); + printf(" -> BUG // %p\n", pyplugin); + + printf(" -> BUG // %u\n", ((GObject *)pyplugin)->ref_count); + + //register_plugin(pyplugin); + + /////////unref_object(pyplugin); done_with_plugin: @@ -965,116 +708,7 @@ static void load_python_plugins(GPluginModule *plugin) /****************************************************************************** * * -* Paramètres : plugin = greffon à manipuler. * -* * -* Description : Prend acte du chargement du greffon. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin) -{ - bool result; /* Bilan à retourner */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - int ret; /* Bilan de préparatifs */ - - _standalone = false; - - /* Chargement du module pour Python */ - - ret = PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalide); - - if (ret == -1) - { - log_plugin_simple_message(LMT_ERROR, _("Can not extend the existing table of Python built-in modules.")); - result = false; - goto cpi_done; - } - - Py_Initialize(); - - gstate = PyGILState_Ensure(); - - _chrysalide_module = PyImport_ImportModule("pychrysalide"); - - /** - * Pour mémoire, une situation concrête conduisant à un échec : - * le paquet python3-gi-dbg n'est pas installé alors que le - * programme est compilé en mode débogage. - * - * Dans ce cas, pygobject_init(-1, -1, -1) échoue, et Py_Initialize() - * le laisse rien filtrer... - * - * En mode autonome, le shell Python remonte bien l'erreur par contre. - */ - - result = (_chrysalide_module != NULL); - - PyGILState_Release(gstate); - - cpi_done: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* * -* Description : Prend acte du déchargement du greffon. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -G_MODULE_EXPORT void chrysalide_plugin_exit(GPluginModule *plugin) -{ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - - gstate = PyGILState_Ensure(); - - clear_all_accesses_to_python_modules(); - - Py_XDECREF(_chrysalide_module); - - PyGILState_Release(gstate); - -} - - -/****************************************************************************** -* * -* Paramètres : type = informations à libérer de la mémoire. * -* * -* Description : Efface un type Python pour greffon de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void free_native_plugin_type(PyTypeObject *type) -{ - free((char *)type->tp_name); - free((char *)type->tp_doc); - - free(type); - -} - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * +* Paramètres : plugin = interface à manipuler. * * * * Description : Accompagne la fin du chargement des modules natifs. * * * @@ -1084,308 +718,49 @@ static void free_native_plugin_type(PyTypeObject *type) * * ******************************************************************************/ -G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *plugin, PluginAction action) +static void g_pychrysalide_plugin_handle_native_plugins_loaded_event(GPyChrysalidePlugin *plugin) { PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - size_t count; /* Quantité de greffons chargés*/ - PyObject *module; /* Module à recompléter */ - PyObject *dict; /* Dictionnaire du module */ - GPluginModule **list; /* Ensemble de ces greffons */ - size_t i; /* Boucle de parcours */ - char *name; /* Désignation complète */ - char *doc; /* Description adaptée */ - int ret; /* Bilan d'un appel */ - PyTypeObject *type; /* Nouveau type dynamique */ gstate = PyGILState_Ensure(); - if (action == PGA_NATIVE_PLUGINS_LOADED) - { - /* Intégration des greffons natifs en Python */ - - if (ensure_python_plugin_module_is_registered()) - { - module = get_access_to_python_module("pychrysalide.plugins"); - assert(module != NULL); - - dict = PyModule_GetDict(module); - - list = get_all_plugins(&count); - - for (i = 0; i < count; i++) - { - ret = asprintf(&name, "pychrysalide.plugins.%s", G_OBJECT_TYPE_NAME(list[i]) + 1); - if (ret == -1) - { - LOG_ERROR_N("asprintf"); - continue; - } - - ret = asprintf(&doc, "Place holder for the native plugin %s documentation", - G_OBJECT_TYPE_NAME(list[i]) + 1); - if (ret == -1) - { - LOG_ERROR_N("asprintf"); - free(name); - continue; - } - - type = calloc(1, sizeof(PyTypeObject)); - - type->tp_name = name; - type->tp_doc = doc; - type->tp_flags = Py_TPFLAGS_DEFAULT; - type->tp_new = no_python_constructor_allowed; - - if (register_class_for_pygobject(dict, G_OBJECT_TYPE(list[i]), type)) - g_object_set_data_full(G_OBJECT(list[i]), "python_type", type, - (GDestroyNotify)free_native_plugin_type); - - else - free_native_plugin_type(type); - - } - - if (list != NULL) - free(list); - - } - - /* Chargement des extensions purement Python */ - - load_python_plugins(plugin); - - } + load_python_plugins(G_PLUGIN_MODULE(plugin)); PyGILState_Release(gstate); } -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * -* type = type d'objet à mettre en place. * -* * -* Description : Crée une instance à partir d'un type dynamique externe. * -* * -* Retour : Instance d'objet gérée par l'extension ou NULL. * -* * -* Remarques : - * -* * -******************************************************************************/ -G_MODULE_EXPORT gpointer chrysalide_plugin_build_type_instance(GPluginModule *plugin, PluginAction action, GType type) -{ - gpointer result; /* Instance à retourner */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyTypeObject *pytype; /* Classe Python concernée */ - PyObject *instance; /* Initialisation forcée */ - - result = NULL; - - gstate = PyGILState_Ensure(); - - pytype = pygobject_lookup_class(type); - - if (pytype != NULL) - { - instance = PyObject_CallObject((PyObject *)pytype, NULL); - assert(instance != NULL); - - result = pygobject_get(instance); - - } - - PyGILState_Release(gstate); - - return result; - -} +/* ---------------------------------------------------------------------------------- */ +/* POINT D'ENTREE POUR PYTHON */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : namespace = module particulier à charger à partir de gi. * -* version = idenfiant de la version à stipuler. * +* Paramètres : - * * * -* Description : Charge un module GI dans Python avec une version attendue. * +* Description : Point d'entrée pour l'initialisation de Python. * * * -* Retour : Bilan de l'opération. * +* Retour : ? * * * * Remarques : - * * * ******************************************************************************/ -bool import_namespace_from_gi_repository(const char *namespace, const char *version) +PyMODINIT_FUNC PyInit_pychrysalide(void) { - bool result; /* Bilan à retourner */ - PyObject *module; /* Module Python-GObject */ - PyObject *args; /* Arguments à fournir */ - int ret; /* Bilan d'une mise en place */ - - result = false; - - /* Sélection d'une version */ - - module = PyImport_ImportModule("gi"); - - if (module != NULL) - { - args = Py_BuildValue("ss", namespace, version); - - run_python_method(module, "require_version", args); - - result = (PyErr_Occurred() == NULL); - - Py_DECREF(args); - Py_DECREF(module); - - } - - /* Importation du module visé */ - - if (result) - { - args = PyTuple_New(1); - - ret = PyTuple_SetItem(args, 0, PyUnicode_FromString(namespace)); - if (ret != 0) - { - result = false; - goto args_error; - } - - module = PyImport_ImportModuleEx("gi.repository", NULL, NULL, args); - - result = (module != NULL); - - Py_XDECREF(module); + PyObject *result; /* Module Python à retourner */ + pyinit_details_t details; /* Détails de chargement */ - args_error: + details.standalone = _standalone; - Py_DECREF(args); + details.populate_extra = NULL; + details.create_self = g_pychrysalide_plugin_new; - } + result = init_python_pychrysalide_module(&details); return result; } - - -/****************************************************************************** -* * -* Paramètres : prefix = message d'introduction à faire apparaître à l'écran.* -* * -* Description : Présente dans le journal une exception survenue. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void log_pychrysalide_exception(const char *prefix, ...) -{ - va_list ap; /* Compléments argumentaires */ - char *msg; /* Message complet à imprimer */ - PyObject *err_type; /* Type d'erreur Python */ - PyObject *err_value; /* Instance Python d'erreur */ - PyObject *err_traceback; /* Trace Python associée */ - PyObject *err_string; /* Description Python d'erreur */ - const char *err_msg; /* Représentation humaine */ - - assert(PyGILState_Check() == 1); - - if (PyErr_Occurred()) - { - /* Base de la communication */ - - va_start(ap, prefix); - - vasprintf(&msg, prefix, ap); - - va_end(ap); - - /* Détails complémentaires */ - - PyErr_Fetch(&err_type, &err_value, &err_traceback); - - PyErr_NormalizeException(&err_type, &err_value, &err_traceback); - - if (err_traceback == NULL) - { - err_traceback = Py_None; - Py_INCREF(err_traceback); - } - - PyException_SetTraceback(err_value, err_traceback); - - if (err_value == NULL) - msg = stradd(msg, _(": no extra information is provided...")); - - else - { - err_string = PyObject_Str(err_value); - err_msg = PyUnicode_AsUTF8(err_string); - - msg = stradd(msg, ": "); - msg = stradd(msg, err_msg); - - Py_DECREF(err_string); - - } - - /** - * Bien que la documentation précise que la fonction PyErr_Fetch() - * transfère la propritété des éléments retournés, la pratique - * montre que le programme plante à la terminaison en cas d'exception. - * - * C'est par exemple le cas quand un greffon Python ne peut se lancer - * correctement ; l'exception est alors levée à partir de la fonction - * create_python_plugin() et le plantage intervient en sortie d'exécution, - * au moment de la libération de l'extension Python : - * - * ==14939== Jump to the invalid address stated on the next line - * ==14939== at 0x1A8FCBC9: ??? - * ==14939== by 0x53DCDB2: g_object_unref (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5800.3) - * ==14939== by 0x610F834: on_plugin_ref_toggle (pglist.c:370) - * ==14939== by 0x610F31A: exit_all_plugins (pglist.c:153) - * ==14939== by 0x10AD19: main (main.c:440) - * ==14939== Address 0x1a8fcbc9 is not stack'd, malloc'd or (recently) free'd - * - * Curieusement, un appel à PyErr_PrintEx(1) corrige l'effet, alors qu'un - * appel à PyErr_PrintEx(0) ne change rien. - * - * La seule différence de l'instruction set_sys_last_vars réside en quelques - * lignes dans le code de l'interpréteur Python : - * - * if (set_sys_last_vars) { - * _PySys_SetObjectId(&PyId_last_type, exception); - * _PySys_SetObjectId(&PyId_last_value, v); - * _PySys_SetObjectId(&PyId_last_traceback, tb); - * } - * - * L'explication n'est pas encore déterminé : bogue dans Chrysalide ou dans Python ? - * L'ajout des éléments dans le dictionnaire du module sys ajoute une référence - * à ces éléments. - * - * On reproduit ici le comportement du code correcteur avec PySys_SetObject(). - */ - - PySys_SetObject("last_type", err_type); - PySys_SetObject("last_value", err_value); - PySys_SetObject("last_traceback", err_traceback); - - Py_XDECREF(err_traceback); - Py_XDECREF(err_value); - Py_XDECREF(err_type); - - log_plugin_simple_message(LMT_ERROR, msg); - - free(msg); - - } - -} diff --git a/plugins/pychrysalide/core.h b/plugins/pychrysalide/core.h index 5d25d3d..60c6c93 100644 --- a/plugins/pychrysalide/core.h +++ b/plugins/pychrysalide/core.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * core.h - prototypes pour le plugin permettant des extensions en Python * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -35,31 +35,29 @@ #include <Python.h> +#include <glibext/helpers.h> #include <plugins/plugin.h> -#include <plugins/plugin-int.h> -/* Point d'entrée pour l'initialisation de Python. */ -PyMODINIT_FUNC PyInit_pychrysalide(void); +/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */ + + +#define G_TYPE_PYCHRYSALIDE_PLUGIN (g_pychrysalide_plugin_get_type()) -/* Prend acte du chargement du greffon. */ -G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *); +DECLARE_GTYPE(GPyChrysalidePlugin, g_pychrysalide_plugin, G, PYCHRYSALIDE_PLUGIN); -/* Prend acte du déchargement du greffon. */ -G_MODULE_EXPORT void chrysalide_plugin_exit(GPluginModule *); -/* Accompagne la fin du chargement des modules natifs. */ -G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *, PluginAction); +/* Crée un module pour un greffon de support Python. */ +GPluginModule *g_pychrysalide_plugin_new(GModule *); -/* Crée une instance à partir d'un type dynamique externe. */ -G_MODULE_EXPORT gpointer chrysalide_plugin_build_type_instance(GPluginModule *, PluginAction, GType); -/* Charge un module GI dans Python avec une version attendue. */ -bool import_namespace_from_gi_repository(const char *, const char *); -/* Présente dans le journal une exception survenue. */ -void log_pychrysalide_exception(const char *, ...); +/* --------------------------- POINT D'ENTREE POUR PYTHON --------------------------- */ + + +/* Point d'entrée pour l'initialisation de Python. */ +PyMODINIT_FUNC PyInit_pychrysalide(void); diff --git a/plugins/pychrysalide/format/executable.c b/plugins/pychrysalide/format/executable.c index 7d05578..f0d3d6b 100644 --- a/plugins/pychrysalide/format/executable.c +++ b/plugins/pychrysalide/format/executable.c @@ -47,9 +47,9 @@ /* Initialise la classe des formats exécutables. */ -static void py_executable_format_init_gclass(GExecutableFormatClass *, gpointer); +static int py_executable_format_init_gclass(GExecutableFormatClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(executable_format, G_TYPE_EXECUTABLE_FORMAT, py_executable_format_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(executable_format, G_TYPE_EXECUTABLE_FORMAT); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_executable_format_init(PyObject *, PyObject *, PyObject *); @@ -95,23 +95,25 @@ static PyObject *py_executable_format_get_portions(PyObject *, void *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe des formats exécutables. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_executable_format_init_gclass(GExecutableFormatClass *class, gpointer unused) +static int py_executable_format_init_gclass(GExecutableFormatClass *gclass, PyTypeObject *pyclass) { - class->get_machine = py_executable_format_get_target_machine_wrapper; + PY_CLASS_SET_WRAPPER(gclass->get_machine, py_executable_format_get_target_machine_wrapper); - class->get_main_addr = py_executable_format_get_main_address_wrapper; - class->refine_portions = py_executable_format_refine_portions_wrapper; + PY_CLASS_SET_WRAPPER(gclass->get_main_addr, py_executable_format_get_main_address_wrapper); + PY_CLASS_SET_WRAPPER(gclass->refine_portions, py_executable_format_refine_portions_wrapper); + + return 0; } @@ -784,6 +786,8 @@ bool ensure_python_executable_format_is_registered(void) if (!ensure_python_program_format_is_registered()) return false; + pyg_register_class_init(G_TYPE_EXECUTABLE_FORMAT, (PyGClassInitFunc)py_executable_format_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_EXECUTABLE_FORMAT, type)) return false; diff --git a/plugins/pychrysalide/format/known.c b/plugins/pychrysalide/format/known.c index 5df2a8f..856c087 100644 --- a/plugins/pychrysalide/format/known.c +++ b/plugins/pychrysalide/format/known.c @@ -42,9 +42,9 @@ /* Initialise la classe des descriptions de fichier binaire. */ -static void py_known_format_init_gclass(GKnownFormatClass *, gpointer); +static int py_known_format_init_gclass(GKnownFormatClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(known_format, G_TYPE_KNOWN_FORMAT, py_known_format_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(known_format, G_TYPE_KNOWN_FORMAT); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_known_format_init(PyObject *, PyObject *, PyObject *); @@ -84,23 +84,25 @@ static PyObject *py_known_format_get_content(PyObject *, void *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe des formats connus. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_known_format_init_gclass(GKnownFormatClass *class, gpointer unused) +static int py_known_format_init_gclass(GKnownFormatClass *gclass, PyTypeObject *pyclass) { - class->get_key = py_known_format_get_key_wrapper; - class->get_desc = py_known_format_get_description_wrapper; + PY_CLASS_SET_WRAPPER(gclass->get_key, py_known_format_get_key_wrapper); + PY_CLASS_SET_WRAPPER(gclass->get_desc, py_known_format_get_description_wrapper); - class->analyze = py_known_format_analyze_wrapper; + PY_CLASS_SET_WRAPPER(gclass->analyze, py_known_format_analyze_wrapper); + + return 0; } @@ -635,6 +637,8 @@ bool ensure_python_known_format_is_registered(void) dict = PyModule_GetDict(module); + pyg_register_class_init(G_TYPE_KNOWN_FORMAT, (PyGClassInitFunc)py_known_format_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_KNOWN_FORMAT, type)) return false; diff --git a/plugins/pychrysalide/format/program.c b/plugins/pychrysalide/format/program.c index 28c1540..57a359a 100644 --- a/plugins/pychrysalide/format/program.c +++ b/plugins/pychrysalide/format/program.c @@ -55,9 +55,9 @@ /* Initialise la classe des formats de programmes. */ -static void py_program_format_init_gclass(GProgramFormatClass *, gpointer); +static int py_program_format_init_gclass(GProgramFormatClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(program_format, G_TYPE_PROGRAM_FORMAT, py_program_format_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(program_format, G_TYPE_PROGRAM_FORMAT); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_program_format_init(PyObject *, PyObject *, PyObject *); @@ -66,7 +66,7 @@ static int py_program_format_init(PyObject *, PyObject *, PyObject *); static SourceEndian py_program_format_get_endianness_wrapper(const GProgramFormat *); /* Fournit l'emplacement d'une section donnée. */ -static bool py_program_format_get_section_range_by_name_wrapper(const GProgramFormat *, const char *, mrange_t *); +static bool py_program_format_find_section_range_by_name_wrapper(const GProgramFormat *, const char *, mrange_t *); @@ -86,7 +86,7 @@ static PyObject *py_program_format_has_flag(PyObject *, PyObject *); /* Fournit l'emplacement d'une section donnée. */ -static PyObject *py_program_format_get_section_range_by_name(PyObject *, PyObject *); +static PyObject *py_program_format_find_section_range_by_name(PyObject *, PyObject *); #if 0 @@ -140,21 +140,23 @@ static PyObject *py_program_format_get_errors(PyObject *, void *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe des formats de programmes. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_program_format_init_gclass(GProgramFormatClass *class, gpointer unused) +static int py_program_format_init_gclass(GProgramFormatClass *gclass, PyTypeObject *pyclass) { - class->get_endian = py_program_format_get_endianness_wrapper; - class->get_range_by_name = py_program_format_get_section_range_by_name_wrapper; + PY_CLASS_SET_WRAPPER(gclass->get_endian, py_program_format_get_endianness_wrapper); + PY_CLASS_SET_WRAPPER(gclass->find_range_by_name, py_program_format_find_section_range_by_name_wrapper); + + return 0; } @@ -188,7 +190,7 @@ static int py_program_format_init(PyObject *self, PyObject *args, PyObject *kwds "* pychrysalide.format.ProgramFormat._get_endianness().\n" \ "\n" \ "Other optional method may be defined for new classes:\n" \ - "* pychrysalide.format.ProgramFormat._get_section_range_by_name().\n" \ + "* pychrysalide.format.ProgramFormat._find_section_range_by_name().\n" \ "\n" \ "Calls to the *__init__* constructor of this abstract object expect" \ " only one argument: a binary content, provided as a" \ @@ -295,7 +297,7 @@ static SourceEndian py_program_format_get_endianness_wrapper(const GProgramForma * * ******************************************************************************/ -static bool py_program_format_get_section_range_by_name_wrapper(const GProgramFormat *format, const char *name, mrange_t *range) +static bool py_program_format_find_section_range_by_name_wrapper(const GProgramFormat *format, const char *name, mrange_t *range) { bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ @@ -303,15 +305,15 @@ static bool py_program_format_get_section_range_by_name_wrapper(const GProgramFo PyObject *pyret; /* Valeur retournée */ int ret; /* Bilan d'une conversion */ -#define PROGRAM_FORMAT_GET_SECTION_RANGE_BY_NAME_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _get_section_range_by_name_wrapper, "$self, name", \ - METH_VARARGS, \ - "Abstract method used to compute the area of a section identified by" \ - " its name.\n" \ - "\n" \ - "The expected returned value is a pychrysalide.arch.mrange instance or" \ - " *None* in case of failure." \ +#define PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _find_section_range_by_name_wrapper, "$self, name", \ + METH_VARARGS, \ + "Abstract method used to compute the area of a section identified by" \ + " its name.\n" \ + "\n" \ + "The expected returned value is a pychrysalide.arch.mrange instance or" \ + " *None* in case of failure." \ ) result = false; @@ -320,9 +322,9 @@ static bool py_program_format_get_section_range_by_name_wrapper(const GProgramFo pyobj = pygobject_new(G_OBJECT(format)); - if (has_python_method(pyobj, "_get_section_range_by_name")) + if (has_python_method(pyobj, "_find_section_range_by_name")) { - pyret = run_python_method(pyobj, "_get_section_range_by_name", NULL); + pyret = run_python_method(pyobj, "_find_section_range_by_name", NULL); if (pyret != NULL) { @@ -520,7 +522,7 @@ static PyObject *py_program_format_has_flag(PyObject *self, PyObject *args) * * ******************************************************************************/ -static PyObject *py_program_format_get_section_range_by_name(PyObject *self, PyObject *args) +static PyObject *py_program_format_find_section_range_by_name(PyObject *self, PyObject *args) { PyObject *result; /* Emplacement à retourner */ const char *name; /* Nom de section ciblée */ @@ -529,9 +531,9 @@ static PyObject *py_program_format_get_section_range_by_name(PyObject *self, PyO mrange_t range; /* Emplacement obtenu ? */ bool status; /* Bilan de l'opération */ -#define PROGRAM_FORMAT_GET_SECTION_RANGE_BY_NAME_METHOD PYTHON_METHOD_DEF \ +#define PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_METHOD PYTHON_METHOD_DEF \ ( \ - get_section_range_by_name, "$self, name, /", \ + find_section_range_by_name, "$self, name, /", \ METH_VARARGS, py_program_format, \ "Compute the area of a section identified by its name.\n" \ "\n" \ @@ -546,7 +548,7 @@ static PyObject *py_program_format_get_section_range_by_name(PyObject *self, PyO format = G_PROGRAM_FORMAT(pygobject_get(self)); - status = g_program_format_get_section_range_by_name(format, name, &range); + status = g_program_format_find_section_range_by_name(format, name, &range); if (status) result = build_from_internal_mrange(&range); @@ -1194,8 +1196,8 @@ PyTypeObject *get_python_program_format_type(void) { static PyMethodDef py_program_format_methods[] = { PROGRAM_FORMAT_GET_ENDIANNESS_WRAPPER, - PROGRAM_FORMAT_GET_SECTION_RANGE_BY_NAME_WRAPPER, - PROGRAM_FORMAT_GET_SECTION_RANGE_BY_NAME_METHOD, + PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_WRAPPER, + PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_METHOD, /* PROGRAM_FORMAT_SET_FLAG_METHOD, PROGRAM_FORMAT_UNSET_FLAG_METHOD, @@ -1273,6 +1275,8 @@ bool ensure_python_program_format_is_registered(void) if (!ensure_python_known_format_is_registered()) return false; + pyg_register_class_init(G_TYPE_PROGRAM_FORMAT, (PyGClassInitFunc)py_program_format_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_PROGRAM_FORMAT, type)) return false; diff --git a/plugins/pychrysalide/glibext/Makefile.am b/plugins/pychrysalide/glibext/Makefile.am index 8b021bb..af1d9f2 100644 --- a/plugins/pychrysalide/glibext/Makefile.am +++ b/plugins/pychrysalide/glibext/Makefile.am @@ -5,12 +5,10 @@ noinst_LTLIBRARIES = libpychrysaglibext.la # binarycursor.h binarycursor.c \ # buffercache.h buffercache.c \ # bufferline.h bufferline.c \ -# comparison.h comparison.c \ # configuration.h configuration.c \ # linecursor.h linecursor.c \ # linegen.h linegen.c \ -# module.h module.c \ -# singleton.h singleton.c +# module.h module.c # if BUILD_GTK_SUPPORT @@ -22,9 +20,15 @@ noinst_LTLIBRARIES = libpychrysaglibext.la # endif libpychrysaglibext_la_SOURCES = \ + comparable.h comparable.c \ constants.h constants.c \ + hashable.h hashable.c \ module.h module.c \ + objhole.h objhole.c \ portion.h portion.c \ + secstorage.h secstorage.c \ + singleton.h singleton.c \ + strbuilder.h strbuilder.c \ work.h work.c \ workqueue.h workqueue.c diff --git a/plugins/pychrysalide/glibext/comparable.c b/plugins/pychrysalide/glibext/comparable.c new file mode 100644 index 0000000..e4982d7 --- /dev/null +++ b/plugins/pychrysalide/glibext/comparable.c @@ -0,0 +1,482 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable.c - équivalent Python du fichier "glibext/comparable.c" + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "comparable.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <glibext/comparable-int.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Procède à l'initialisation de l'interface de détermination. */ +static void py_comparable_object_interface_init(GComparableObjectInterface *, gpointer *); + +/* Réalise une comparaison étendue entre objets. */ +static int py_comparable_object_compare_wrapper(const GComparableObject *, const GComparableObject *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Transmet le statut d'une comparaison effectuée par le parent. */ +static PyObject *py_comparable_object_parent_compare(PyObject *, PyObject *); + +/* Effectue une comparaison avec un objet 'ComparableObject'. */ +static PyObject *py_comparable_object_richcompare(PyObject *, PyObject *, int); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* unused = adresse non utilisée ici. * +* * +* Description : Procède à l'initialisation de l'interface de détermination. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_comparable_object_interface_init(GComparableObjectInterface *iface, gpointer *unused) +{ +#define COMPARABLE_OBJECT_DOC \ + "The ComparableObject class provides an interface to compare" \ + " objects.\n" \ + "\n" \ + "A typical class declaration for a new implementation looks like:\n" \ + "\n" \ + " class NewImplem(GObject.Object, ComparableObject):\n" \ + " ...\n" \ + "\n" \ + "The following method has to be defined for new implementations:\n" \ + "* pychrysalide.glibext.ComparableObject._compare().\n" + + iface->compare = py_comparable_object_compare_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : object = premier objet à consulter pour une comparaison. * +* other = second objet à consulter pour une comparaison. * +* * +* Description : Réalise une comparaison étendue entre objets. * +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_comparable_object_compare_wrapper(const GComparableObject *object, const GComparableObject *other) +{ + int result; /* Bilan à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + +#define COMPARABLE_OBJECT_COMPARE_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _compare, "$self, other", \ + METH_VARARGS, \ + "Abstract method allowing to compare two objects implementing" \ + " the interface. This method is used to handle rich comparisons"\ + " automatically.\n" \ + "\n" \ + "The result has to be an integer lesser than, equal to, or" \ + " greater than zero if *self* is found, respectively, to be" \ + " lesser than, to match, or to be greater than *other*.\n" \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " an integer." \ +) + + result = 0; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(object)); + + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(other))); + + pyret = run_python_method(pyobj, "_compare", args); + + if (pyret != NULL) + { + if (PyLong_Check(pyret)) + result = PyLong_AsLong(pyret); + + else + PyErr_SetString(PyExc_TypeError, _("comparison status has to be a signed integer")); + + } + + Py_XDECREF(pyret); + + Py_DECREF(args); + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet dont l'instance se veut unique. * +* args = adresse non utilisée ici. * +* * +* Description : Transmet le statut d'une comparaison effectuée par le parent.* +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_comparable_object_parent_compare(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + GComparableObject *other; /* Second objet à comparer */ + int ret; /* Bilan de lecture des args. */ + GComparableObject *object; /* Mécanismes natifs */ + GComparableObjectInterface *iface; /* Interface utilisée */ + GComparableObjectInterface *parent_iface; /* Interface parente */ + int status; /* Bilan d'une comparaison */ + +#define COMPARABLE_OBJECT_PARENT_COMPARE_METHOD PYTHON_METHOD_DEF \ +( \ + parent_compare, "$sel, otherf", \ + METH_VARARGS, py_comparable_object, \ + "Provide the comparison status defined by the interface" \ + " implementation from the object native parent.\n" \ + "\n" \ + "The result is a signed integer.\n" \ + "\n" \ + "A *TypeError* exception is raised if the object parent does" \ + " not implement the pychrysalide.glibext.ComparableObject" \ + " interface.\n" \ + "\n" \ + "A *RuntimeError* exception is raised if the direct parent type"\ + " of the object has not a native implementation. For Python" \ + " implementations, the super()._compare() function has to be" \ + " used instead." \ +) + + if (!check_for_native_parent(self)) + return NULL; + + ret = PyArg_ParseTuple(args, "O&", convert_to_comparable_object, &other); + if (!ret) return NULL; + + object = G_COMPARABLE_OBJECT(pygobject_get(self)); + + iface = G_COMPARABLE_OBJECT_GET_IFACE(object); + + parent_iface = g_type_interface_peek_parent(iface); + + if (parent_iface == NULL) + { + PyErr_SetString(PyExc_TypeError, _("object parent does not implement the ComparableObject interface")); + + result = NULL; + + } + else + { + status = parent_iface->compare(object, other); + + result = PyLong_FromLong(status); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : a = premier object Python à consulter. * +* b = second object Python à consulter. * +* op = type de comparaison menée. * +* * +* Description : Effectue une comparaison avec un objet 'ComparableObject'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_comparable_object_richcompare(PyObject *a, PyObject *b, int op) +{ + PyObject *result; /* Bilan à retourner */ + int ret; /* Bilan de lecture des args. */ + GComparableObject *obj_a; /* Instance à manipuler #1 */ + GComparableObject *obj_b; /* Instance à manipuler #2 */ + int status; /* Bilan d'une comparaison */ + + ret = PyObject_IsInstance(b, (PyObject *)get_python_comparable_object_type()); + if (!ret) + { + result = Py_NotImplemented; + goto cmp_done; + } + + obj_a = G_COMPARABLE_OBJECT(pygobject_get(a)); + obj_b = G_COMPARABLE_OBJECT(pygobject_get(b)); + + status = g_comparable_object_compare(obj_a, obj_b); + + switch (op) + { + case Py_LT: + result = (status < 0 ? Py_True : Py_False); + break; + + case Py_LE: + result = (status <= 0 ? Py_True : Py_False); + break; + + case Py_EQ: + result = (status == 0 ? Py_True : Py_False); + break; + + case Py_NE: + result = (status != 0 ? Py_True : Py_False); + break; + + case Py_GT: + result = (status > 0 ? Py_True : Py_False); + break; + + case Py_GE: + result = (status >= 0 ? Py_True : Py_False); + break; + + default: + assert(false); + result = Py_NotImplemented; + break; + + } + + cmp_done: + + Py_INCREF(result); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + return result; + +} + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_comparable_object_type(void) +{ + static PyMethodDef py_comparable_object_methods[] = { + COMPARABLE_OBJECT_COMPARE_WRAPPER, + COMPARABLE_OBJECT_PARENT_COMPARE_METHOD, + { NULL } + }; + + static PyGetSetDef py_comparable_object_getseters[] = { + { NULL } + }; + + static PyTypeObject py_comparable_object_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.ComparableObject", + .tp_basicsize = sizeof(PyObject), + + /** + * Une valeur de .tp_richcompare non nulle écarte la définition du + * champ .tp_hash à la valeur par défaut du type PyBaseObject_Type + * dans les préparatifs de la fonction Python inherit_slots(). + * + * Ces préparatifs se poursuivent avec type_ready_set_hash(), + * qui initialise .tp_hash avec PyObject_HashNotImplemented(), + * qui n'est donc pas un comportement par défaut. + * + * Côté PyGObject, la fonction pygobject_inherit_slots() y voit + * une implémentation de .tp_hash personnalisée, ce qui bloque + * la défintion d'autres personnalisations, comme celle de + * l'interface HashableObject. + * + * La valeur nominale nulle est ainsi écartée au préalable ici. + */ + .tp_hash = (hashfunc)_Py_HashPointer, + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = COMPARABLE_OBJECT_DOC, + + .tp_richcompare = py_comparable_object_richcompare, + + .tp_methods = py_comparable_object_methods, + .tp_getset = py_comparable_object_getseters + + }; + + return &py_comparable_object_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.....ComparableObject'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_comparable_object_is_registered(void) +{ + PyTypeObject *type; /* Type 'ComparableObject' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + static GInterfaceInfo info = { /* Paramètres d'inscription */ + + .interface_init = (GInterfaceInitFunc)py_comparable_object_interface_init, + .interface_finalize = NULL, + .interface_data = NULL, + + }; + + type = get_python_comparable_object_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_interface_for_pygobject(dict, G_TYPE_COMPARABLE_OBJECT, type, &info)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* 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 interface d'objet comparable. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_comparable_object(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_comparable_object_type()); + + 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 comparable object"); + break; + + case 1: + *((GComparableObject **)dst) = G_COMPARABLE_OBJECT(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/comparable.h b/plugins/pychrysalide/glibext/comparable.h new file mode 100644 index 0000000..d4c6ecf --- /dev/null +++ b/plugins/pychrysalide/glibext/comparable.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable.h - prototypes pour l'équivalent Python du fichier "glibext/comparable.h" + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_comparable_object_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.ComparableObject'. */ +bool ensure_python_comparable_object_is_registered(void); + +/* Tente de convertir en interface d'objet comparable. */ +int convert_to_comparable_object(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H */ diff --git a/plugins/pychrysalide/glibext/comparison.c b/plugins/pychrysalide/glibext/hashable.c index 548f700..c870d55 100644 --- a/plugins/pychrysalide/glibext/comparison.c +++ b/plugins/pychrysalide/glibext/hashable.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * comparison.c - équivalent Python du fichier "glibext/comparison.h" + * hashable.c - équivalent Python du fichier "glibext/hashable.c" * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,38 +22,40 @@ */ -#include "comparison.h" +#include "hashable.h" +#include <assert.h> #include <pygobject.h> -#include <glibext/comparison-int.h> +#include <glibext/hashable-int.h> -#include "constants.h" #include "../access.h" #include "../helpers.h" -#include "../analysis/content.h" /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Procède à l'initialisation de l'interface de comparaison. */ -static void py_comparable_item_interface_init(GComparableItemIface *, gpointer *); +/* Procède à l'initialisation de l'interface de détermination. */ +static void py_hashable_object_interface_init(GHashableObjectInterface *, gpointer *); -/* Réalise une comparaison entre objets selon un critère précis. */ -static bool py_comparable_item_compare_rich(const GComparableItem *, const GComparableItem *, RichCmpOperation, bool *); +/* Calcule l'empreinte sur 32 bits d'un objet. */ +static guint py_hashable_object_hash_wrapper(const GHashableObject *); /* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ -/* Effectue une comparaison avec un objet 'ComparableItem'. */ -static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int); +/* Transmet l'empreinte d'un objet calculée par son parent. */ +static PyObject *py_hashable_object_parent_hash(PyObject *, PyObject *); + +/* Calcule l'empreinte sur 32 bits d'un objet. */ +static Py_hash_t py_hashable_object_hash(PyObject *); @@ -67,7 +69,7 @@ static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int); * Paramètres : iface = interface GLib à initialiser. * * unused = adresse non utilisée ici. * * * -* Description : Procède à l'initialisation de l'interface de comparaison. * +* Description : Procède à l'initialisation de l'interface de détermination. * * * * Retour : - * * * @@ -75,72 +77,78 @@ static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int); * * ******************************************************************************/ -static void py_comparable_item_interface_init(GComparableItemIface *iface, gpointer *unused) +static void py_hashable_object_interface_init(GHashableObjectInterface *iface, gpointer *unused) { - -#define COMPARABLE_ITEM_DOC \ - "ComparableItem provides an interface to compare objects.\n" \ +#define HASHABLE_OBJECT_DOC \ + "The HashableObject class defines a interface ensuring that a" \ + " customized hashing method is available for an object.\n" \ "\n" \ "A typical class declaration for a new implementation looks like:\n" \ "\n" \ - " class NewImplem(GObject.Object, ComparableItem):\n" \ + " class NewImplem(GObject.Object, HashableObject):\n" \ " ...\n" \ - "\n" + "\n" \ + "The following method has to be defined for new implementations:\n" \ + "* pychrysalide.glibext.HashableObject._hash().\n" - iface->cmp_rich = py_comparable_item_compare_rich; + iface->hash = py_hashable_object_hash_wrapper; } /****************************************************************************** * * -* Paramètres : item = premier objet à cnsulter pour une comparaison. * -* other = second objet à cnsulter pour une comparaison. * -* op = opération de comparaison à réaliser. * -* status = bilan des opérations de comparaison. [OUT] * +* Paramètres : object = objet dont l'instance est à consulter. * * * -* Description : Réalise une comparaison entre objets selon un critère précis.* +* Description : Calcule l'empreinte sur 32 bits d'un objet. * * * -* Retour : true si la comparaison a pu être effectuée, false sinon. * +* Retour : Valeur de représentation, unique pour l'objet ou non. * * * * Remarques : - * * * ******************************************************************************/ -static bool py_comparable_item_compare_rich(const GComparableItem *item, const GComparableItem *other, RichCmpOperation op, bool *status) +static guint py_hashable_object_hash_wrapper(const GHashableObject *object) { - bool result; /* Etat à retourner */ + guint result; /* Valeur à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyitem; /* Objet Python concerné #1 */ - PyObject *pyother; /* Objet Python concerné #2 */ + PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ - int ret; /* Bilan d'une conversion */ - result = false; +#define HASHABLE_OBJECT_HASH_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _hash, "$self", \ + METH_NOARGS, \ + "Abstract method computing a hash from an object which is used" \ + " as the default implementation of the __hash__() method.\n" \ + "\n" \ + "The result has to be an unsigned integer.\n" \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " an integer." \ +) + + result = 0; gstate = PyGILState_Ensure(); - pyitem = pygobject_new(G_OBJECT(item)); - pyother = pygobject_new(G_OBJECT(other)); + pyobj = pygobject_new(G_OBJECT(object)); - pyret = PyObject_RichCompare(pyitem, pyother, op); + pyret = run_python_method(pyobj, "_hash", NULL); if (pyret != NULL) { - ret = PyBool_Check(pyret); + if (PyLong_Check(pyret)) + result = PyLong_AsUnsignedLong(pyret); - if (ret) - { - *status = (pyret == Py_True); - result = true; - } - - Py_DECREF(pyret); + else + PyErr_SetString(PyExc_TypeError, _("computed hash value has to be an unsigned integer")); } - Py_DECREF(pyother); - Py_DECREF(pyitem); + Py_XDECREF(pyret); + + Py_DECREF(pyobj); PyGILState_Release(gstate); @@ -157,47 +165,97 @@ static bool py_comparable_item_compare_rich(const GComparableItem *item, const G /****************************************************************************** * * -* Paramètres : a = premier object Python à consulter. * -* b = second object Python à consulter. * -* op = type de comparaison menée. * +* Paramètres : self = objet dont l'instance se veut unique. * +* args = adresse non utilisée ici. * * * -* Description : Effectue une comparaison avec un objet 'ComparableItem'. * +* Description : Transmet l'empreinte d'un objet calculée par son parent. * * * -* Retour : Bilan de l'opération. * +* Retour : Valeur de représentation, unique pour l'objet ou non. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_comparable_item_richcompare(PyObject *a, PyObject *b, int op) +static PyObject *py_hashable_object_parent_hash(PyObject *self, PyObject *args) { - PyObject *result; /* Bilan à retourner */ - int ret; /* Bilan de lecture des args. */ - GComparableItem *item_a; /* Instance à manipuler #1 */ - GComparableItem *item_b; /* Instance à manipuler #2 */ - bool valid; /* Indication de validité */ - bool status; /* Résultat d'une comparaison */ - - ret = PyObject_IsInstance(b, (PyObject *)get_python_comparable_item_type()); - if (!ret) + PyObject *result; /* Valeur à retourner */ + GHashableObject *object; /* Mécanismes natifs */ + GHashableObjectInterface *iface; /* Interface utilisée */ + GHashableObjectInterface *parent_iface; /* Interface parente */ + guint hash; /* Valeur d'empreitne */ + +#define HASHABLE_OBJECT_PARENT_HASH_METHOD PYTHON_METHOD_DEF \ +( \ + parent_hash, "$self", \ + METH_NOARGS, py_hashable_object, \ + "Provide the hash value defined by the interface implementation"\ + " from the object native parent.\n" \ + "\n" \ + "The result is an unsigned integer.\n" \ + "\n" \ + "A *TypeError* exception is raised if the object parent does" \ + " not implement the pychrysalide.glibext.HashableObject" \ + " interface.\n" \ + "\n" \ + "A *RuntimeError* exception is raised if the direct parent type"\ + " of the object has not a native implementation. For Python" \ + " implementations, the super()._hash() function has to be used" \ + " instead." \ +) + + if (!check_for_native_parent(self)) + return NULL; + + object = G_HASHABLE_OBJECT(pygobject_get(self)); + + iface = G_HASHABLE_OBJECT_GET_IFACE(object); + + parent_iface = g_type_interface_peek_parent(iface); + + if (parent_iface == NULL) { - result = Py_NotImplemented; - goto cmp_done; + PyErr_SetString(PyExc_TypeError, _("object parent does not implement the HashableObject interface")); + + result = NULL; + } + else + { + hash = parent_iface->hash(object); - item_a = G_COMPARABLE_ITEM(pygobject_get(a)); - item_b = G_COMPARABLE_ITEM(pygobject_get(b)); + result = PyLong_FromUnsignedLong(hash); - valid = g_comparable_item_compare_rich(item_a, item_b, op, &status); + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + } + + return result; + +} - if (valid) - result = status ? Py_True : Py_False; - else - result = Py_NotImplemented; - cmp_done: +/****************************************************************************** +* * +* Paramètres : self = objet manipulé ici. * +* * +* Description : Calcule l'empreinte sur 32 bits d'un objet. * +* * +* Retour : Valeur de représentation, unique pour l'objet ou non. * +* * +* Remarques : - * +* * +******************************************************************************/ - Py_INCREF(result); +static Py_hash_t py_hashable_object_hash(PyObject *self) +{ + Py_hash_t result; /* Empreinte à retourner */ + GHashableObject *object; /* Mécanismes natifs */ + + object = G_HASHABLE_OBJECT(pygobject_get(self)); + + result = g_hashable_object_hash(object); + + UPDATE_RESULT_IF_RAISED_EXCEPTION(-1); return result; @@ -216,44 +274,46 @@ static PyObject *py_comparable_item_richcompare(PyObject *a, PyObject *b, int op * * ******************************************************************************/ -PyTypeObject *get_python_comparable_item_type(void) +PyTypeObject *get_python_hashable_object_type(void) { - static PyMethodDef py_comparable_item_methods[] = { + static PyMethodDef py_hashable_object_methods[] = { + HASHABLE_OBJECT_HASH_WRAPPER, + HASHABLE_OBJECT_PARENT_HASH_METHOD, { NULL } }; - static PyGetSetDef py_comparable_item_getseters[] = { + static PyGetSetDef py_hashable_object_getseters[] = { { NULL } }; - static PyTypeObject py_comparable_item_type = { + static PyTypeObject py_hashable_object_type = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.glibext.ComparableItem", + .tp_name = "pychrysalide.glibext.HashableObject", .tp_basicsize = sizeof(PyObject), - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_hash = py_hashable_object_hash, - .tp_doc = COMPARABLE_ITEM_DOC, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_richcompare = py_comparable_item_richcompare, + .tp_doc = HASHABLE_OBJECT_DOC, - .tp_methods = py_comparable_item_methods, - .tp_getset = py_comparable_item_getseters, + .tp_methods = py_hashable_object_methods, + .tp_getset = py_hashable_object_getseters, }; - return &py_comparable_item_type; + return &py_hashable_object_type; } /****************************************************************************** * * -* Paramètres : module = module dont la définition est à compléter. * +* Paramètres : - * * * -* Description : Prend en charge l'objet 'pychrysalide.....ComparableItem'. * +* Description : Prend en charge l'objet 'pychrysalide.....HashableObject'. * * * * Retour : Bilan de l'opération. * * * @@ -261,21 +321,21 @@ PyTypeObject *get_python_comparable_item_type(void) * * ******************************************************************************/ -bool ensure_python_comparable_item_is_registered(void) +bool ensure_python_hashable_object_is_registered(void) { - PyTypeObject *type; /* Type Python 'ComparableItem' */ + PyTypeObject *type; /* Type Python 'HashableObject'*/ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ static GInterfaceInfo info = { /* Paramètres d'inscription */ - .interface_init = (GInterfaceInitFunc)py_comparable_item_interface_init, + .interface_init = (GInterfaceInitFunc)py_hashable_object_interface_init, .interface_finalize = NULL, .interface_data = NULL, }; - type = get_python_comparable_item_type(); + type = get_python_hashable_object_type(); if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { @@ -283,10 +343,7 @@ bool ensure_python_comparable_item_is_registered(void) dict = PyModule_GetDict(module); - if (!register_interface_for_pygobject(dict, G_TYPE_COMPARABLE_ITEM, type, &info)) - return false; - - if (!define_comparable_item_constants(type)) + if (!register_interface_for_pygobject(dict, G_TYPE_HASHABLE_OBJECT, type, &info)) return false; } @@ -301,7 +358,7 @@ bool ensure_python_comparable_item_is_registered(void) * 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 comparable. * +* Description : Tente de convertir en interface d'objet réductible. * * * * Retour : Bilan de l'opération, voire indications supplémentaires. * * * @@ -309,11 +366,11 @@ bool ensure_python_comparable_item_is_registered(void) * * ******************************************************************************/ -int convert_to_comparable_item(PyObject *arg, void *dst) +int convert_to_hashable_object(PyObject *arg, void *dst) { int result; /* Bilan à retourner */ - result = PyObject_IsInstance(arg, (PyObject *)get_python_comparable_item_type()); + result = PyObject_IsInstance(arg, (PyObject *)get_python_hashable_object_type()); switch (result) { @@ -323,11 +380,11 @@ int convert_to_comparable_item(PyObject *arg, void *dst) break; case 0: - PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to comparable item"); + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to hashable object"); break; case 1: - *((GComparableItem **)dst) = G_COMPARABLE_ITEM(pygobject_get(arg)); + *((GHashableObject **)dst) = G_HASHABLE_OBJECT(pygobject_get(arg)); break; default: diff --git a/plugins/pychrysalide/glibext/comparison.h b/plugins/pychrysalide/glibext/hashable.h index 79f7092..8583118 100644 --- a/plugins/pychrysalide/glibext/comparison.h +++ b/plugins/pychrysalide/glibext/hashable.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * comparison.h - prototypes pour l'équivalent Python du fichier "glibext/comparison.h" + * hashable.h - prototypes pour l'équivalent Python du fichier "glibext/hashable.h" * - * Copyright (C) 2018 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,8 +22,8 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H -#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H #include <Python.h> @@ -32,14 +32,14 @@ /* Fournit un accès à une définition de type à diffuser. */ -PyTypeObject *get_python_comparable_item_type(void); +PyTypeObject *get_python_hashable_object_type(void); -/* Prend en charge l'objet 'pychrysalide.glibext.ComparableItem'. */ -bool ensure_python_comparable_item_is_registered(void); +/* Prend en charge l'objet 'pychrysalide.glibext.HashableObject'. */ +bool ensure_python_hashable_object_is_registered(void); -/* Tente de convertir en élément comparable. */ -int convert_to_comparable_item(PyObject *, void *); +/* Tente de convertir en interface d'objet réductible. */ +int convert_to_hashable_object(PyObject *, void *); -#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H */ diff --git a/plugins/pychrysalide/glibext/module.c b/plugins/pychrysalide/glibext/module.c index e62d587..6ce0709 100644 --- a/plugins/pychrysalide/glibext/module.c +++ b/plugins/pychrysalide/glibext/module.c @@ -33,15 +33,19 @@ #include "buffercache.h" #include "bufferline.h" #include "bufferview.h" -#include "comparison.h" #include "configuration.h" #include "linecursor.h" #include "linegen.h" #include "loadedpanel.h" #include "named.h" -#include "singleton.h" */ +#include "comparable.h" +#include "hashable.h" +#include "objhole.h" #include "portion.h" +#include "secstorage.h" +#include "singleton.h" +#include "strbuilder.h" #include "work.h" #include "workqueue.h" #include "../helpers.h" @@ -110,12 +114,19 @@ bool populate_glibext_module(void) result = true; + if (result) result = ensure_python_comparable_object_is_registered(); + if (result) result = ensure_python_hashable_object_is_registered(); + if (result) result = ensure_python_singleton_candidate_is_registered(); + if (result) result = ensure_python_string_builder_is_registered(); + + if (result) result = ensure_python_thick_object_is_registered(); if (result) result = ensure_python_binary_portion_is_registered(); if (result) result = ensure_python_generic_work_is_registered(); + if (result) result = ensure_python_secret_storage_is_registered(); + if (result) result = ensure_python_singleton_factory_is_registered(); if (result) result = ensure_python_work_queue_is_registered(); /* - if (result) result = ensure_python_singleton_candidate_is_registered(); if (result) result = ensure_python_binary_cursor_is_registered(); if (result) result = ensure_python_buffer_cache_is_registered(); @@ -123,7 +134,6 @@ bool populate_glibext_module(void) #ifdef INCLUDE_GTK_SUPPORT if (result) result = ensure_python_buffer_view_is_registered(); #endif - if (result) result = ensure_python_comparable_item_is_registered(); if (result) result = ensure_python_config_param_is_registered(); if (result) result = ensure_python_config_param_iterator_is_registered(); if (result) result = ensure_python_generic_config_is_registered(); @@ -133,7 +143,6 @@ bool populate_glibext_module(void) if (result) result = ensure_python_loaded_panel_is_registered(); if (result) result = ensure_python_named_widget_is_registered(); #endif - if (result) result = ensure_python_singleton_factory_is_registered(); */ assert(result); diff --git a/plugins/pychrysalide/glibext/objhole.c b/plugins/pychrysalide/glibext/objhole.c new file mode 100644 index 0000000..2a3ad6f --- /dev/null +++ b/plugins/pychrysalide/glibext/objhole.c @@ -0,0 +1,329 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * objhole.c - équivalent Python du fichier "glibext/objhole.c" + * + * Copyright (C) 2024 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "objhole.h" + + +#include <pygobject.h> + + +#include <i18n.h> +#include <glibext/objhole-int.h> + + +#include "../access.h" +#include "../helpers.h" + + + +CREATE_DYN_CONSTRUCTOR(thick_object, G_TYPE_THICK_OBJECT); + + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_thick_object_init(PyObject *, PyObject *, PyObject *); + +/* Indique le nombre de bits accaparés par la GLib. */ +static PyObject *py_thick_object_get__GOBJECT_RESERVED_EXTRA_BITS(PyObject *, void *); + +/* Fournit la valeur courante de la zone de stockage d'un objet. */ +static PyObject *py_thick_object_get_extra(PyObject *, void *); + +/* Définit la valeur courante de la zone de stockage d'un objet. */ +static int py_thick_object_set_extra(PyObject *, PyObject *, void *); + + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_thick_object_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int ret; /* Bilan de lecture des args. */ + +#define THICK_OBJECT_DOC \ + "The ThickObject class is an alternative version of the GObject class." \ + " It provides an access to a 32-bit wide memory hole inside the native" \ + " GObject structure in order to exploit the area as various values" \ + " storage.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " ThickObject()" + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Indique le nombre de bits accaparés par la GLib. * +* * +* Retour : Nombre de bits, à priori inférieur à 32. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_thick_object_get__GOBJECT_RESERVED_EXTRA_BITS(PyObject *self, void *closure) +{ + PyObject *result; /* Résultat à retourner */ + +#define THICK_OBJECT__GOBJECT_RESERVED_EXTRA_BITS_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + _GOBJECT_RESERVED_EXTRA_BITS, py_thick_object, \ + "Quantity of lower bits used by the GLib inside the memory hole.\n" \ + "\n" \ + "The returned value should be less then 32 and is provided for" \ + " pychrysalide.glibext.ThickObject subclass implementation." \ +) + + result = PyLong_FromUnsignedLong(GOBJECT_RESERVED_EXTRA_BITS); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit la valeur courante de la zone de stockage d'un objet.* +* * +* Retour : Valeur de 32 lues et expurgées des bits GLib. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_thick_object_get_extra(PyObject *self, void *closure) +{ + PyObject *result; /* Résultat à retourner */ + GThickObject *obj; /* Version GLib de l'instance */ + guint extra; /* Valeur à transmettre */ + +#define THICK_OBJECT_EXTRA_ATTRIB PYTHON_GETSET_DEF_FULL \ +( \ + extra, py_thick_object, \ + "Data stored inside the structure memory hole of all GObject" \ + " native instances.\n" \ + "\n" \ + "Less than 32 bits are available for storing arbitrary values." \ +) + + obj = G_THICK_OBJECT(pygobject_get(self)); + + extra = g_thick_object_get_extra(obj); + + result = PyLong_FromUnsignedLong(extra); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* value = valeur fournie à intégrer ou prendre en compte. * +* closure = non utilisé ici. * +* * +* Description : Définit la valeur courante de la zone de stockage d'un objet.* +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_thick_object_set_extra(PyObject *self, PyObject *value, void *closure) +{ + unsigned long extra; /* Valeur à inscrire */ + GThickObject *obj; /* Version GLib de l'instance */ + + if (!PyLong_Check(value)) + { + PyErr_SetString(PyExc_TypeError, _("Invalid integer value")); + return -1; + } + + extra = PyLong_AsUnsignedLong(value); + + obj = G_THICK_OBJECT(pygobject_get(self)); + + g_thick_object_set_extra(obj, extra); + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_thick_object_type(void) +{ + static PyMethodDef py_thick_object_methods[] = { + { NULL } + }; + + static PyGetSetDef py_thick_object_getseters[] = { + THICK_OBJECT__GOBJECT_RESERVED_EXTRA_BITS_ATTRIB, + THICK_OBJECT_EXTRA_ATTRIB, + { NULL } + }; + + static PyTypeObject py_thick_object_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.ThickObject", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = THICK_OBJECT_DOC, + + .tp_methods = py_thick_object_methods, + .tp_getset = py_thick_object_getseters, + + .tp_init = py_thick_object_init, + .tp_new = py_thick_object_new, + + }; + + return &py_thick_object_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide.glibext.ThickObject'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_thick_object_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'ThickObject' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_thick_object_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_THICK_OBJECT, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* 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 objet dense. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_thick_object(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_thick_object_type()); + + 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 thick object"); + break; + + case 1: + *((GThickObject **)dst) = G_THICK_OBJECT(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/objhole.h b/plugins/pychrysalide/glibext/objhole.h new file mode 100644 index 0000000..b541b70 --- /dev/null +++ b/plugins/pychrysalide/glibext/objhole.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * objhole.h - prototypes pour l'équivalent Python du fichier "glibext/objhole.h" + * + * Copyright (C) 2024 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_OBJHOLE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_OBJHOLE_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_thick_object_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.ThickObject'. */ +bool ensure_python_thick_object_is_registered(void); + +/* Tente de convertir en objet dense. */ +int convert_to_thick_object(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_OBJHOLE_H */ diff --git a/plugins/pychrysalide/glibext/secstorage.c b/plugins/pychrysalide/glibext/secstorage.c new file mode 100644 index 0000000..b5adb7c --- /dev/null +++ b/plugins/pychrysalide/glibext/secstorage.c @@ -0,0 +1,624 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage.c - équivalent Python du fichier "glibext/secstorage.c" + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "secstorage.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <glibext/secstorage-int.h> + + +#include "../access.h" +#include "../convert.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_CONSTRUCTOR(secret_storage, G_TYPE_SECRET_STORAGE); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_secret_storage_init(PyObject *, PyObject *, PyObject *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Définit un mot de passe pour protéger une clef maître. */ +static PyObject *py_secret_storage_set_password(PyObject *, PyObject *); + +/* Déverrouille la clef de chiffrement maître. */ +static PyObject *py_secret_storage_unlock(PyObject *, PyObject *); + +/* Verrouille la clef de chiffrement maître. */ +static PyObject *py_secret_storage_lock(PyObject *, PyObject *); + +/* Chiffre des données avec la clef de chiffrement maître. */ +static PyObject *py_secret_storage_encrypt_data(PyObject *, PyObject *); + +/* Déchiffre des données avec la clef de chiffrement maître. */ +static PyObject *py_secret_storage_decrypt_data(PyObject *, PyObject *); + +/* Détermine si une clef de chiffrement protégée est en place. */ +static PyObject *py_secret_storage_has_key(PyObject *, void *); + +/* Détermine si la clef de chiffrement maître est vérouillée. */ +static PyObject *py_secret_storage_is_locked(PyObject *, void *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_secret_storage_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + GSettings *settings; /* Configuration à considérer */ + int ret; /* Bilan de lecture des args. */ + GSecretStorage *storage; /* Stockage natif à manipuler */ + +#define SECRET_STORAGE_DOC \ + "SecretStorage acts as guardian for secrets using ecryption," \ + " mainly for sensitive information stored as configuration" \ + " parameters.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " SecretStorage(settings)" \ + "\n" \ + "The *settings* arguement must point to a GSettings intance;" \ + " the main configuration settings are used by default." \ + + settings = NULL; + + ret = PyArg_ParseTuple(args, "|O&", convert_to_gsettings, &settings); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + if (!g_secret_storage_create(storage, settings)) + return -1; + + return 0; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Définit un mot de passe pour protéger une clef maître. * +* * +* Retour : Bilan de la mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_set_password(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *passwd; /* Mot de passe associé */ + int ret; /* Bilan de lecture des args. */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + +#define SECRET_STORAGE_SET_PASSWORD_METHOD PYTHON_METHOD_DEF \ +( \ + set_password, "/, password=''", \ + METH_VARARGS, py_secret_storage, \ + "Create a master key used for protecting secrets. This key is" \ + " itself protected by the provided password.\n" \ + "\n" \ + "The supplied *password* has to be a string.\n" \ + "\n" \ + "The result is a boolean status: *True* if the operation successed,"\ + " *False* otherwise." \ +) + + passwd = ""; + + ret = PyArg_ParseTuple(args, "|s", &passwd); + if (!ret) return NULL; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_set_password(storage, passwd); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Déverrouille la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_unlock(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *passwd; /* Mot de passe associé */ + int ret; /* Bilan de lecture des args. */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + +#define SECRET_STORAGE_UNLOCK_METHOD PYTHON_METHOD_DEF \ +( \ + unlock, "/, password=''", \ + METH_VARARGS, py_secret_storage, \ + "Decrypt in memory the master key used for protecting secrets.\n" \ + "\n" \ + "The supplied *password* is the primary password used to protect" \ + " this key.\n" \ + "\n" \ + "The result is a boolean status: *True* if the operation successed" \ + " or if the master key is already unlocked, *False* otherwise." \ +) + + passwd = ""; + + ret = PyArg_ParseTuple(args, "|s", &passwd); + if (!ret) return NULL; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_unlock(storage, passwd); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Verrouille la clef de chiffrement maître. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_lock(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + +#define SECRET_STORAGE_LOCK_METHOD PYTHON_METHOD_DEF \ +( \ + lock, "", \ + METH_NOARGS, py_secret_storage, \ + "Clear from memory the master key used for protecting secrets." \ +) + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + g_secret_storage_lock(storage); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Chiffre des données avec la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_encrypt_data(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *data_in; /* Données d'entrée à chiffrer */ + Py_ssize_t size_in; /* Quantité de ces données */ + int ret; /* Bilan de lecture des args. */ + sized_binary_t in; /* Données à chiffer */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + sized_binary_t out; /* Données chiffrées */ + +#define SECRET_STORAGE_ENCRYPT_DATA_METHOD PYTHON_METHOD_DEF \ +( \ + encrypt_data, "data", \ + METH_VARARGS, py_secret_storage, \ + "Encrypt data using an unlocked the master key.\n" \ + "\n" \ + "The *data* arguement points to bytes to process." \ + "\n" \ + "The result is either encrypted *data* as bytes in case of success,"\ + " or *None* in case of failure." \ +) + + ret = PyArg_ParseTuple(args, "s#", &data_in, &size_in); + if (!ret) return NULL; + + in.static_data = data_in; + in.size = size_in; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_encrypt_data(storage, &in, &out); + + if (status) + { + result = PyBytes_FromStringAndSize(out.static_data, out.size); + exit_sized_binary(&out); + } + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Déchiffre des données avec la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_decrypt_data(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *data_in; /* Données d'entrée à chiffrer */ + Py_ssize_t size_in; /* Quantité de ces données */ + int ret; /* Bilan de lecture des args. */ + sized_binary_t in; /* Données à chiffer */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + sized_binary_t out; /* Données chiffrées */ + +#define SECRET_STORAGE_DECRYPT_DATA_METHOD PYTHON_METHOD_DEF \ +( \ + decrypt_data, "data", \ + METH_VARARGS, py_secret_storage, \ + "Decrypt data using an unlocked the master key.\n" \ + "\n" \ + "The *data* arguement points to bytes to process." \ + "\n" \ + "The result is either decrypted *data* as bytes in case of success,"\ + " or *None* in case of failure." \ +) + + ret = PyArg_ParseTuple(args, "s#", &data_in, &size_in); + if (!ret) return NULL; + + in.static_data = data_in; + in.size = size_in; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_decrypt_data(storage, &in, &out); + + if (status) + { + result = PyBytes_FromStringAndSize(out.static_data, out.size); + exit_sized_binary(&out); + } + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Détermine si une clef de chiffrement protégée est en place. * +* * +* Retour : Bilan de l'analyse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_has_key(PyObject *self, void *closure) +{ + PyObject *result; /* Instance Python à retourner */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de consultation */ + +#define PY_SECRET_STORAGE_HAS_KEY_ATTRIB PYTHON_HAS_DEF_FULL \ +( \ + key, py_secret_storage, \ + "Indicate if a master key used for protecting secrets seems to have"\ + " been defined. Without any unlocking attempt, the test only relies"\ + " on the length of saved master data.\n" \ + "\n" \ + "The returned status is a boolean status: *True* if the master key" \ + " seems to exist, *False* otherwise." \ +) + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_has_key(storage); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Détermine si la clef de chiffrement maître est vérouillée. * +* * +* Retour : Bilan de la détermination. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_is_locked(PyObject *self, void *closure) +{ + PyObject *result; /* Instance Python à retourner */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de consultation */ + +#define SECRET_STORAGE_IS_LOCKED_ATTRIB PYTHON_IS_DEF_FULL \ +( \ + locked, py_secret_storage, \ + "Indicate if the master key used for protecting secrets is" \ + " currently decrypted in memory.\n" \ + "\n" \ + "The *settings* arguement must point to a GSettings intance; the" \ + " main configuration settings are used by default.\n" \ + "\n" \ + "The returned status is a boolean status: *True* if the master key" \ + " is unlocked and ready for use, *False* otherwise." \ +) + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_is_locked(storage); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_secret_storage_type(void) +{ + static PyMethodDef py_secret_storage_methods[] = { + SECRET_STORAGE_SET_PASSWORD_METHOD, + SECRET_STORAGE_UNLOCK_METHOD, + SECRET_STORAGE_LOCK_METHOD, + SECRET_STORAGE_ENCRYPT_DATA_METHOD, + SECRET_STORAGE_DECRYPT_DATA_METHOD, + { NULL } + }; + + static PyGetSetDef py_secret_storage_getseters[] = { + PY_SECRET_STORAGE_HAS_KEY_ATTRIB, + SECRET_STORAGE_IS_LOCKED_ATTRIB, + { NULL } + }; + + static PyTypeObject py_secret_storage_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.SecretStorage", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = SECRET_STORAGE_DOC, + + .tp_methods = py_secret_storage_methods, + .tp_getset = py_secret_storage_getseters, + + .tp_init = py_secret_storage_init, + .tp_new = py_secret_storage_new, + + }; + + return &py_secret_storage_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide.glibext.SecretStorage'.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_secret_storage_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'SecretStorage' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_secret_storage_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_SECRET_STORAGE, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* 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 gardien des secrets avec stockage. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_secret_storage(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_secret_storage_type()); + + 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 secret storage"); + break; + + case 1: + *((GSecretStorage **)dst) = G_SECRET_STORAGE(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/secstorage.h b/plugins/pychrysalide/glibext/secstorage.h new file mode 100644 index 0000000..68726c3 --- /dev/null +++ b/plugins/pychrysalide/glibext/secstorage.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage.h - prototypes pour l'équivalent Python du fichier "glibext/secstorage.h" + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_secret_storage_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.SecretStorage'. */ +bool ensure_python_secret_storage_is_registered(void); + +/* Tente de convertir en gardien des secrets avec stockage. */ +int convert_to_secret_storage(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H */ diff --git a/plugins/pychrysalide/glibext/singleton.c b/plugins/pychrysalide/glibext/singleton.c index 8491473..8712506 100644 --- a/plugins/pychrysalide/glibext/singleton.c +++ b/plugins/pychrysalide/glibext/singleton.c @@ -30,7 +30,6 @@ #include <glibext/singleton-int.h> -#include <plugins/dt.h> #include "../access.h" @@ -42,7 +41,7 @@ /* Procède à l'initialisation de l'interface de candidature. */ -static void py_singleton_candidate_interface_init(GSingletonCandidateIface *, gpointer *); +static void py_singleton_candidate_interface_init(GSingletonCandidateInterface *, gpointer *); /* Fournit une liste de candidats embarqués par un candidat. */ static GSingletonCandidate **py_singleton_candidate_list_inner_instances_wrapper(const GSingletonCandidate *, size_t *); @@ -50,37 +49,27 @@ static GSingletonCandidate **py_singleton_candidate_list_inner_instances_wrapper /* Met à jour une liste de candidats embarqués par un candidat. */ static void py_singleton_candidate_update_inner_instances_wrapper(GSingletonCandidate *, GSingletonCandidate **, size_t); -/* Fournit l'empreinte d'un candidat à une centralisation. */ -static guint py_singleton_candidate___hash__wrapper(const GSingletonCandidate *); - -/* Détermine si deux candidats à l'unicité sont identiques. */ -static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate *, const GSingletonCandidate *); - /* Marque un candidat comme figé. */ -static void py_singleton_candidate_set_ro_wrapper(GSingletonCandidate *); +static void py_singleton_candidate_mark_as_read_only_wrapper(GSingletonCandidate *); /* Indique si le candidat est figé. */ -static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *); - -/* Fournit l'empreinte d'un candidat à une centralisation. */ -static PyObject *py_singleton_candidate_hash(PyObject *, PyObject *); - -/* Fournit une liste de candidats embarqués par un candidat. */ -static PyObject *py_singleton_candidate_get_inner_instances(PyObject *, void *); +static bool py_singleton_candidate_is_read_only_wrapper(const GSingletonCandidate *); /* Indique si le candidat est figé. */ -static PyObject *py_singleton_candidate_get_read_only(PyObject *, void *); +static PyObject *py_singleton_candidate_is_read_only(PyObject *, void *); + +/* Crée une copie modifiable d'un object unique. */ +static GSingletonCandidate *py_singleton_candidate_dup_wrapper(const GSingletonCandidate *); -/* Effectue une comparaison avec un objet 'SingletonCandidate'. */ -static PyObject *py_singleton_candidate_richcompare(PyObject *, PyObject *, int); +/* Crée une copie modifiable d'un object unique. */ +static PyObject *py_singleton_candidate_dup(PyObject *, PyObject *); /* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_singleton_factory_new(PyTypeObject *, PyObject *, PyObject *); +CREATE_DYN_CONSTRUCTOR(singleton_factory, G_TYPE_SINGLETON_FACTORY); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_singleton_factory_init(PyObject *, PyObject *, PyObject *); @@ -108,37 +97,44 @@ static PyObject *py_singleton_factory_get_instance(PyObject *, PyObject *); * * ******************************************************************************/ -static void py_singleton_candidate_interface_init(GSingletonCandidateIface *iface, gpointer *unused) +static void py_singleton_candidate_interface_init(GSingletonCandidateInterface *iface, gpointer *unused) { #define SINGLETON_CANDIDATE_DOC \ "The SingletonCandidate class is a required interface for objects" \ " aiming at becoming singleton instances. All shared singletons are" \ " registered within a pychrysalide.glibext.SingletonFactory object.\n" \ "\n" \ + "Implementations of the pychrysalide.glibext.HashableObject and" \ + " pychrysalide.glibext.ComparableObject interfaces are required for" \ + " types implementing the SingletonCandidate interface.\n" \ + "\n" \ "The main implemantations come with types derived from" \ - " pychrysalide.analysis.DataType.\n" \ + " pychrysalide.analysis.DataType (with possible recursivity) or from" \ + " pychrysalide.arch.ArchOperand.\n" \ "\n" \ "A typical class declaration for a new implementation looks like:\n" \ "\n" \ - " class NewImplem(GObject.Object, SingletonCandidate):\n" \ + " class NewImplem(GObject.Object, HashableObject, ComparableObject," \ + " SingletonCandidate):\n" \ " ...\n" \ "\n" \ "The following methods have to be defined for new implementations:\n" \ + "* pychrysalide.glibext.SingletonCandidate._mark_as_read_only();\n" \ + "* pychrysalide.glibext.SingletonCandidate._is_read_only();\n" \ + "* pychrysalide.glibext.SingletonCandidate._dup().\n" \ + "\n" \ + "The following methods may bbe defined for new implementations if" \ + " inner SingletonCandidate objets are carried:\n" \ "* pychrysalide.glibext.SingletonCandidate._list_inner_instances();\n" \ - "* pychrysalide.glibext.SingletonCandidate._update_inner_instances();\n"\ - "* pychrysalide.glibext.SingletonCandidate.__hash__();\n" \ - "* pychrysalide.glibext.SingletonCandidate.__eq__();\n" \ - "* pychrysalide.glibext.SingletonCandidate._set_read_only();\n" \ - "* pychrysalide.glibext.SingletonCandidate._is_read_only().\n" + "* pychrysalide.glibext.SingletonCandidate._update_inner_instances().\n" iface->update_inner = py_singleton_candidate_update_inner_instances_wrapper; iface->list_inner = py_singleton_candidate_list_inner_instances_wrapper; - iface->hash = py_singleton_candidate___hash__wrapper; - iface->is_equal = py_singleton_candidate___eq__wrapper; + iface->mark_as_ro = py_singleton_candidate_mark_as_read_only_wrapper; + iface->is_ro = py_singleton_candidate_is_read_only_wrapper; - iface->set_ro = py_singleton_candidate_set_ro_wrapper; - iface->is_ro = py_singleton_candidate_is_ro_wrapper; + iface->dup = py_singleton_candidate_dup_wrapper; } @@ -313,122 +309,97 @@ static void py_singleton_candidate_update_inner_instances_wrapper(GSingletonCand * * * Paramètres : candidate = objet dont l'instance se veut unique. * * * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * +* Description : Marque un candidat comme figé. * * * -* Retour : Empreinte de l'élément représenté. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static guint py_singleton_candidate___hash__wrapper(const GSingletonCandidate *candidate) +static void py_singleton_candidate_mark_as_read_only_wrapper(GSingletonCandidate *candidate) { - guint result; /* Empreinte à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ -#define SINGLETON_CANDIDATE_HASH_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - __hash__, "$self, /", \ - METH_NOARGS, \ - "Abstract method used to produce a hash of the object.\n" \ - "\n" \ - "The result must be an integer value up to 64 bits." \ - "\n" \ - "Inner instances which are listed through the" \ - " pychrysalide.glibext.SingletonCandidate._list_inner_instances()" \ - " method do not need to get processed here as they are handled" \ - " automatically by the interface core." \ +#define SINGLETON_CANDIDATE_MARK_AS_READ_ONLY_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _mark_as_read_only, "$self", \ + METH_NOARGS, \ + "Abstract method used to seal the object as unmodifiable.\n" \ + "\n" \ + "No result is expected." \ ) - result = 0; - gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(candidate)); - if (has_python_method(pyobj, "__hash__")) - { - pyret = run_python_method(pyobj, "__hash__", NULL); + pyret = run_python_method(pyobj, "_mark_as_read_only", NULL); - if (pyret != NULL) - { - if (PyLong_Check(pyret)) - result = PyLong_AsUnsignedLongMask(pyret); - - Py_DECREF(pyret); - - } - - } + Py_XDECREF(pyret); Py_DECREF(pyobj); PyGILState_Release(gstate); - return result; - } /****************************************************************************** * * * Paramètres : candidate = objet dont l'instance se veut unique. * -* other = second élément à analyser. * * * -* Description : Détermine si deux candidats à l'unicité sont identiques. * +* Description : Indique si le candidat est figé. * * * -* Retour : Bilan de la comparaison. * +* Retour : true si le contenu du candidat ne peut plus être modifié. * * * * Remarques : - * * * ******************************************************************************/ -static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate *candidate, const GSingletonCandidate *other) +static bool py_singleton_candidate_is_read_only_wrapper(const GSingletonCandidate *candidate) { - guint result; /* Empreinte à retourner */ + bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan de consultation */ -#define SINGLETON_CANDIDATE_EQ_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - __eq__, "$self, other, /", \ - METH_NOARGS, \ - "Abstract method used to provide the *__eq__* method for" \ - " rich comparison.\n" \ - "\n" \ - "The expected result is a boolean value." \ +#define SINGLETON_CANDIDATE_IS_READ_ONLY_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _is_read_only, "$self", \ + METH_NOARGS, \ + "Abstract method used to provide the state of the object: are" \ + " its properties frozen (*True*) or can it be modified" \ + " (*False*)?\n" \ + "\n" \ + "The result has to be a boolean status.\n" \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " a boolean." \ ) - result = 0; + result = false; gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(candidate)); - if (has_python_method(pyobj, "__eq__")) - { - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(other))); - - pyret = run_python_method(pyobj, "__eq__", args); - - if (pyret != NULL) - { - if (PyLong_Check(pyret)) - result = PyLong_AsUnsignedLong(pyret); + pyret = run_python_method(pyobj, "_is_read_only", NULL); - Py_DECREF(pyret); - - } + if (pyret != NULL) + { + if (PyBool_Check(pyret)) + result = (pyret == Py_True); - Py_DECREF(args); + else + PyErr_SetString(PyExc_TypeError, _("status has to be provided as a boolean value")); } + Py_XDECREF(pyret); + Py_DECREF(pyobj); PyGILState_Release(gstate); @@ -442,80 +413,38 @@ static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate * * * * Paramètres : candidate = objet dont l'instance se veut unique. * * * -* Description : Marque un candidat comme figé. * +* Description : Crée une copie modifiable d'un object unique. * * * -* Retour : - * +* Retour : Nouvelle instance mise en place. * * * * Remarques : - * * * ******************************************************************************/ -static void py_singleton_candidate_set_ro_wrapper(GSingletonCandidate *candidate) +static GSingletonCandidate *py_singleton_candidate_dup_wrapper(const GSingletonCandidate *candidate) { + GSingletonCandidate *result; /* Instance à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ + PyObject *state; /* Validation du mode */ -#define SINGLETON_CANDIDATE_SET_RO_WRAPPER PYTHON_WRAPPER_DEF \ +#define SINGLETON_CANDIDATE_DUP_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _set_read_only, "$self, /", \ + _dup, "$self", \ METH_NOARGS, \ - "Abstract method used to mark the content of a singleton" \ - " candidate as read-only.\n" \ + "Abstract method used to create a copy of the object. This" \ + " has to be able to get modified (ie. its" \ + " pychrysalide.glibext.SingletonCandidate.read_only status" \ + " has to be *False*).\n" \ "\n" \ - "The read-only state is mandatory once the candidate is" \ - " registered inside a pychrysalide.glibext.SingletonFactory"\ - " instance as official singleton." \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(candidate)); - - if (has_python_method(pyobj, "_set_read_only")) - { - pyret = run_python_method(pyobj, "_set_read_only", NULL); - - Py_XDECREF(pyret); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - -} - - -/****************************************************************************** -* * -* Paramètres : candidate = objet dont l'instance se veut unique. * -* * -* Description : Indique si le candidat est figé. * -* * -* Retour : true si le contenu du candidat ne peut plus être modifié. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *candidate) -{ - bool result; /* Etat à retourner */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *pyret; /* Bilan de consultation */ - -#define SINGLETON_CANDIDATE_IS_RO_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _is_read_only, "$self, /", \ - METH_NOARGS, \ - "Abstract method used to retrieve the status of the data" \ - " contained by a singleton candidate.\n" \ + "The result has to be a new intance of type(self).\n" \ + "\n" \ + "A *TypeError* exception is raised if the type of the" \ + " return value is different from the type of self.\n" \ "\n" \ - "The retured value is *True* if the candidate is" \ - " registered inside a pychrysalide.glibext.SingletonFactory"\ - " instance as official singleton, *False* otherwise." \ + "A *ValueError* exception is raised of the return object" \ + " is in read-only mode." \ ) result = false; @@ -524,16 +453,32 @@ static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *cand pyobj = pygobject_new(G_OBJECT(candidate)); - if (has_python_method(pyobj, "_is_read_only")) + pyret = run_python_method(pyobj, "_dup", NULL); + + if (pyret != NULL) { - pyret = run_python_method(pyobj, "_is_read_only", NULL); + if (Py_TYPE(pyret) != Py_TYPE(pyobj)) + PyErr_SetString(PyExc_TypeError, _("the result type is different from the source type")); - result = (pyret == Py_True); + else + { + state = py_singleton_candidate_is_read_only(pyret, NULL); - Py_XDECREF(pyret); + if (state != NULL) + { + if (state != Py_False) + PyErr_SetString(PyExc_ValueError, _("the result type can not be in read-only mode")); + + Py_DECREF(state); + + } + + } } + Py_XDECREF(pyret); + Py_DECREF(pyobj); PyGILState_Release(gstate); @@ -545,97 +490,51 @@ static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *cand /****************************************************************************** * * -* Paramètres : self = objet dont l'instance se veut unique. * +* Paramètres : self = objet manipulé ici. * * args = adresse non utilisée ici. * * * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * +* Description : Crée une copie modifiable d'un object unique. * * * -* Retour : Empreinte de l'élément représenté. * +* Retour : Nouvelle instance mise en place. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_singleton_candidate_hash(PyObject *self, PyObject *args) +static PyObject *py_singleton_candidate_dup(PyObject *self, PyObject *args) { PyObject *result; /* Emplacement à retourner */ GSingletonCandidate *candidate; /* Mécanismes natifs */ - guint hash; /* Valeur d'empreitne */ + GSingletonCandidate *copy; /* Copie des mécanismes natifs */ -#define SINGLETON_CANDIDATE_HASH_METHOD PYTHON_METHOD_DEF \ -( \ - hash, "$self", \ - METH_NOARGS, py_singleton_candidate, \ - "Compute the hash value of the singleton candidate.\n" \ - "\n" \ - "The method relies on the interface core to include in the" \ - " process the optional embedded instances which may become" \ - " singletons.\n" \ - "\n" \ - "The result is an integer value.\n" \ - "\n" \ - "Even if the Python *hash()* method, relying on the" \ - " pychrysalide.glibext.SingletonCandidate.__hash__()" \ - " implementation, provides values up to 64 bits, the final" \ - " hashes processed by the native GLib hash methods are" \ - " limited to 32 bits values." \ +#define SINGLETON_CANDIDATE_DUP_METHOD PYTHON_METHOD_DEF \ +( \ + dup, "$self", \ + METH_NOARGS, py_singleton_candidate, \ + "Create a copy of the object. This has to be able to get" \ + " modified (ie. its" \ + " pychrysalide.glibext.SingletonCandidate.read_only status" \ + " has to be *False*).\n" \ + "\n" \ + "The result has to be a new intance of type(self)." \ ) candidate = G_SINGLETON_CANDIDATE(pygobject_get(self)); - hash = g_singleton_candidate_hash(candidate); + copy = g_singleton_candidate_dup(candidate); - result = PyLong_FromUnsignedLong(hash); + if (copy == NULL) + result = NULL; - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* closure = non utilisé ici. * -* * -* Description : Fournit une liste de candidats embarqués par un candidat. * -* * -* Retour : Liste de candidats internes, vide si aucun. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_singleton_candidate_get_inner_instances(PyObject *self, void *closure) -{ - PyObject *result; /* Valeur à retourner */ - GSingletonCandidate *candidate; /* Mécanismes natifs */ - size_t count; /* Quantité d'objets internes */ - GSingletonCandidate **instances; /* Liste des embarqués */ - size_t i; /* Boucle de parcours */ - -#define SINGLETON_CANDIDATE_INNER_INSTANCES_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - inner_instances, py_singleton_candidate, \ - "List of optional internal singleton candidate instances.\n" \ - "\n" \ - "The result has to be a tuple containing zero or more" \ - " pychrysalide.glibext.SingletonCandidate instances." \ -) - - candidate = G_SINGLETON_CANDIDATE(pygobject_get(self)); - - instances = g_singleton_candidate_list_inner_instances(candidate, &count); + else + { + result = pygobject_new(G_OBJECT(candidate)); - result = PyTuple_New(count); + unref_object(copy); - for (i = 0; i < count; i++) - { - PyTuple_SetItem(result, i, pygobject_new(G_OBJECT(instances[i]))); - g_object_unref(G_OBJECT(instances[i])); } - if (instances != NULL) - free(instances); + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); return result; @@ -649,87 +548,33 @@ static PyObject *py_singleton_candidate_get_inner_instances(PyObject *self, void * * * Description : Indique si le candidat est figé. * * * -* Retour : true si le contenu du candidat ne peut plus être modifié. * +* Retour : True si le contenu du candidat ne peut plus être modifié. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_singleton_candidate_get_read_only(PyObject *self, void *closure) +static PyObject *py_singleton_candidate_is_read_only(PyObject *self, void *closure) { PyObject *result; /* Valeur à retourner */ GSingletonCandidate *candidate; /* Mécanismes natifs */ - bool status; /* Etat de l'élément consulté */ + bool state; /* Etat de l'objet courant */ -#define SINGLETON_CANDIDATE_READ_ONLY_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - read_only, py_singleton_candidate, \ - "State of the singleton candidate content.\n" \ - "\n" \ - "The result is a boolean: *True* if the object is registered" \ - " as singleton, *False* otherwise.\n" \ - "\n" \ - "Once a singleton, the object must not change its content as" \ - " it is a shared instance." \ +#define SINGLETON_CANDIDATE_READ_ONLY_ATTRIB PYTHON_IS_DEF_FULL \ +( \ + read_only, py_singleton_candidate, \ + "Boolean state of the object: *True* if all its properties are" \ + " frozen, *False* if the object can be modified." \ ) candidate = G_SINGLETON_CANDIDATE(pygobject_get(self)); - status = g_singleton_candidate_is_read_only(candidate); + state = g_singleton_candidate_is_read_only(candidate); - result = status ? Py_True : Py_False; + result = state ? Py_True : Py_False; Py_INCREF(result); - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : a = premier object Python à consulter. * -* b = second object Python à consulter. * -* op = type de comparaison menée. * -* * -* Description : Effectue une comparaison avec un objet 'SingletonCandidate'. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_singleton_candidate_richcompare(PyObject *a, PyObject *b, int op) -{ - PyObject *result; /* Bilan à retourner */ - int ret; /* Bilan de lecture des args. */ - GSingletonCandidate *cand_a; /* Premier élément à traiter */ - GSingletonCandidate *cand_b; /* Second élément à traiter */ - gboolean status; /* Résultat d'une comparaison */ - - if (op != Py_EQ) - { - result = Py_NotImplemented; - goto cmp_done; - } - - ret = PyObject_IsInstance(b, (PyObject *)get_python_singleton_candidate_type()); - if (!ret) - { - result = Py_NotImplemented; - goto cmp_done; - } - - cand_a = G_SINGLETON_CANDIDATE(pygobject_get(a)); - cand_b = G_SINGLETON_CANDIDATE(pygobject_get(b)); - - status = g_singleton_candidate_is_equal(cand_a, cand_b); - - result = (status ? Py_True : Py_False); - - cmp_done: - - Py_INCREF(result); + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); return result; @@ -753,16 +598,14 @@ PyTypeObject *get_python_singleton_candidate_type(void) static PyMethodDef py_singleton_candidate_methods[] = { SINGLETON_CANDIDATE_LIST_INNER_INSTANCES_WRAPPER, SINGLETON_CANDIDATE_UPDATE_INNER_INSTANCES_WRAPPER, - SINGLETON_CANDIDATE_HASH_WRAPPER, - SINGLETON_CANDIDATE_EQ_WRAPPER, - SINGLETON_CANDIDATE_SET_RO_WRAPPER, - SINGLETON_CANDIDATE_IS_RO_WRAPPER, - SINGLETON_CANDIDATE_HASH_METHOD, + SINGLETON_CANDIDATE_MARK_AS_READ_ONLY_WRAPPER, + SINGLETON_CANDIDATE_IS_READ_ONLY_WRAPPER, + SINGLETON_CANDIDATE_DUP_WRAPPER, + SINGLETON_CANDIDATE_DUP_METHOD, { NULL } }; static PyGetSetDef py_singleton_candidate_getseters[] = { - SINGLETON_CANDIDATE_INNER_INSTANCES_ATTRIB, SINGLETON_CANDIDATE_READ_ONLY_ATTRIB, { NULL } }; @@ -778,8 +621,6 @@ PyTypeObject *get_python_singleton_candidate_type(void) .tp_doc = SINGLETON_CANDIDATE_DOC, - .tp_richcompare = py_singleton_candidate_richcompare, - .tp_methods = py_singleton_candidate_methods, .tp_getset = py_singleton_candidate_getseters @@ -804,7 +645,7 @@ PyTypeObject *get_python_singleton_candidate_type(void) bool ensure_python_singleton_candidate_is_registered(void) { - PyTypeObject *type; /* Type Python 'BinContent' */ + PyTypeObject *type; /* Type 'SingletonCandidate' */ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ @@ -887,66 +728,6 @@ int convert_to_singleton_candidate(PyObject *arg, void *dst) /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* 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 : - * -* * -******************************************************************************/ - -static PyObject *py_singleton_factory_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type de base à dériver */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ - - /* Validations diverses */ - - base = get_python_singleton_factory_type(); - - 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(G_TYPE_SINGLETON_FACTORY, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - 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 : self = objet à initialiser (théoriquement). * * args = arguments fournis à l'appel. * * kwds = arguments de type key=val fournis. * @@ -973,11 +754,6 @@ static int py_singleton_factory_init(PyObject *self, PyObject *args, PyObject *k "\n" \ "The first processed instance defines the type handled by the factory." - /* Récupération des paramètres */ - - ret = PyArg_ParseTuple(args, ""); - if (!ret) return -1; - /* Initialisation d'un objet GLib */ ret = forward_pygobjet_init(self); diff --git a/plugins/pychrysalide/glibext/strbuilder.c b/plugins/pychrysalide/glibext/strbuilder.c new file mode 100644 index 0000000..a6de0f0 --- /dev/null +++ b/plugins/pychrysalide/glibext/strbuilder.c @@ -0,0 +1,542 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * strbuilder.c - équivalent Python du fichier "glibext/strbuilder.c" + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "strbuilder.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <glibext/strbuilder-int.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Procède à l'initialisation de l'interface d'exportation. */ +static void py_string_builder_interface_init(GStringBuilderInterface *, gpointer *); + +/* Exporte une chaîne de caractères à partir d'un objet. */ +static bool py_string_builder_to_string_wrapper(const GStringBuilder *, unsigned int, sized_binary_t *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Transmet la description d'un objet définie par son parent. */ +static PyObject *py_string_builder_parent_to_string(PyObject *, PyObject *); + +/* Exporte une chaîne de caractères à partir d'un objet. */ +static PyObject *py_string_builder_to_string(PyObject *, PyObject *); + +/* Fournit une représentation de l'objet exportable. */ +static PyObject *py_string_builder_str(PyObject *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* unused = adresse non utilisée ici. * +* * +* Description : Procède à l'initialisation de l'interface d'exportation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_string_builder_interface_init(GStringBuilderInterface *iface, gpointer *unused) +{ +#define STRING_BUILDER_DOC \ + "The StringBuilder class defines a interface for native objects aiming" \ + " at providing a string representation of themselves.\n" \ + "\n" \ + "A typical class declaration for a new implementation looks like:\n" \ + "\n" \ + " class NewImplem(GObject.Object, StringBuilder):\n" \ + " ...\n" \ + "\n" \ + "The following method has to be defined for new implementations:\n" \ + "* pychrysalide.glibext.StringBuilder._to_string().\n" + + iface->to_string = py_string_builder_to_string_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : builder = objet dont l'instance est exportable. * +* flags = éventuelles indications pour l'opération. * +* out = chaîne de caractères mise en place. [OUT] * +* * +* Description : Exporte une chaîne de caractères à partir d'un objet. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : La sortie out est à nettoyer avec exit_sized_binary() après * +* usage. * +* * +******************************************************************************/ + +static bool py_string_builder_to_string_wrapper(const GStringBuilder *builder, unsigned int flags, sized_binary_t *out) +{ + bool result; /* Bilan à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + const char *utf8; /* Chaîne UTF-8 portée */ + Py_ssize_t size; /* Taille de ces données */ + +#define STRING_BUILDER_TO_STRING_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _to_string, "$self, /, flags=0", \ + METH_VARARGS, \ + "Abstract method providing a string representation for the" \ + " object which is used as the default implementation of the" \ + " __repr__() method.\n" \ + "\n" \ + "The optional *flags* argument define hints for the operation" \ + " (for instance the Intel or AT&T flavor for x86 assembly).\n" \ + "\n" \ + "The result has to be a string." \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " a string." \ +) + + result = false; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(builder)); + + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(flags)); + + pyret = run_python_method(pyobj, "_to_string", args); + + if (pyret != NULL) + { + if (PyUnicode_Check(pyret)) + { + utf8 = PyUnicode_AsUTF8AndSize(pyret, &size); + + if (utf8 != NULL) + { + assert(size >= 0); + + add_to_sized_binary(out, utf8, size); + result = true; + + } + + } + + if (!result) + PyErr_SetString(PyExc_TypeError, _("object description has to get provided as an UTF-8 string value")); + + } + + Py_XDECREF(pyret); + + Py_DECREF(args); + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet dont l'instance se veut unique. * +* args = adresse non utilisée ici. * +* * +* Description : Transmet la description d'un objet définie par son parent. * +* * +* Retour : Présentation de l'élément construite. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_string_builder_parent_to_string(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + unsigned int flags; /* Eventuelles indications */ + int ret; /* Bilan de lecture des args. */ + GStringBuilder *builder; /* Mécanismes natifs */ + GStringBuilderInterface *iface; /* Interface utilisée */ + GStringBuilderInterface *parent_iface; /* Interface parente */ + sized_binary_t out; /* Description construite */ + bool status; /* Bilan de l'opération */ + +#define STRING_BUILDER_PARENT_TO_STRING_METHOD PYTHON_METHOD_DEF \ +( \ + parent_to_string, "$self, /, flags=0", \ + METH_VARARGS, py_string_builder, \ + "Provide a string representation defined by the interface" \ + " implementation from the object native parent.\n" \ + "\n" \ + "The result is a string.\n" \ + "\n" \ + "A *TypeError* exception is raised if the object parent does" \ + " not implement the pychrysalide.glibext.StringBuilder" \ + " interface." \ + "\n" \ + "A *RuntimeError* exception is raised if the direct parent type"\ + " of the object has not a native implementation. For Python" \ + " implementations, the super()._to_string() function has to be" \ + " used instead.\n" \ + "\n" \ + "A *BufferError* exception is raised if the description has" \ + " not been able to get created." \ +) + + if (!check_for_native_parent(self)) + return NULL; + + flags = 0; + + ret = PyArg_ParseTuple(args, "|I", &flags); + if (!ret) return NULL; + + builder = G_STRING_BUILDER(pygobject_get(self)); + + iface = G_STRING_BUILDER_GET_IFACE(builder); + + parent_iface = g_type_interface_peek_parent(iface); + + if (parent_iface == NULL) + { + PyErr_SetString(PyExc_TypeError, _("object parent does not implement the StringBuilder interface")); + + result = NULL; + + } + else + { + init_sized_binary(&out); + + status = parent_iface->to_string(builder, flags, &out); + + if (status) + result = PyUnicode_FromStringAndSize(out.data, out.size); + + else + { + result = NULL; + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_BufferError, _("unable to create a description")); + + } + + exit_sized_binary(&out); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet manipulé ici. * +* args = adresse non utilisée ici. * +* * +* Description : Exporte une chaîne de caractères à partir d'un objet. * +* * +* Retour : Présentation de l'élément construite. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_string_builder_to_string(PyObject *self, PyObject *args) +{ + PyObject *result; /* Emplacement à retourner */ + unsigned int flags; /* Eventuelles indications */ + int ret; /* Bilan de lecture des args. */ + GStringBuilder *builder; /* Mécanismes natifs */ + sized_binary_t out; /* Description construite */ + bool status; /* Bilan de l'opération */ + +#define STRING_BUILDER_TO_STRING_METHOD PYTHON_METHOD_DEF \ +( \ + to_string, "$self, /, flags=0", \ + METH_VARARGS, py_string_builder, \ + "Provide a string representation for the object which is used" \ + " as the default implementation of the __repr__() method.\n" \ + "\n" \ + "The optional *flags* argument define hints for the operation" \ + " (for instance the Intel or AT&T flavor for x86 assembly).\n" \ + "\n" \ + "The result is a string.\n" \ + "\n" \ + "A *BufferError* exception is raised if the description has" \ + " not been able to get created." \ +) + + flags = 0; + + ret = PyArg_ParseTuple(args, "|I", &flags); + if (!ret) return NULL; + + builder = G_STRING_BUILDER(pygobject_get(self)); + + init_sized_binary(&out); + + status = g_string_builder_to_string(builder, flags, &out); + + if (status) + result = PyUnicode_FromStringAndSize(out.data, out.size); + + else + { + result = NULL; + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_BufferError, _("unable to create a description")); + + } + + exit_sized_binary(&out); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet manipulé ici. * +* * +* Description : Fournit une représentation de l'objet exportable. * +* * +* Retour : Présentation de l'élément construite. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_string_builder_str(PyObject *self) +{ + PyObject *result; /* Emplacement à retourner */ + GStringBuilder *builder; /* Mécanismes natifs */ + sized_binary_t out; /* Description construite */ + bool status; /* Bilan de l'opération */ + + builder = G_STRING_BUILDER(pygobject_get(self)); + + init_sized_binary(&out); + + status = g_string_builder_to_string(builder, 0, &out); + + if (status) + result = PyUnicode_FromStringAndSize(out.data, out.size); + + else + { + result = NULL; + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_BufferError, _("unable to create a description")); + + } + + exit_sized_binary(&out); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_string_builder_type(void) +{ + static PyMethodDef py_string_builder_methods[] = { + STRING_BUILDER_TO_STRING_WRAPPER, + STRING_BUILDER_PARENT_TO_STRING_METHOD, + STRING_BUILDER_TO_STRING_METHOD, + { NULL } + }; + + static PyGetSetDef py_string_builder_getseters[] = { + { NULL } + }; + + static PyTypeObject py_string_builder_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.StringBuilder", + .tp_basicsize = sizeof(PyObject), + + .tp_str = py_string_builder_str, + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = STRING_BUILDER_DOC, + + .tp_methods = py_string_builder_methods, + .tp_getset = py_string_builder_getseters + + }; + + return &py_string_builder_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.glibext.StringBuilder'.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_string_builder_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'StringBuilder' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + static GInterfaceInfo info = { /* Paramètres d'inscription */ + + .interface_init = (GInterfaceInitFunc)py_string_builder_interface_init, + .interface_finalize = NULL, + .interface_data = NULL, + + }; + + type = get_python_string_builder_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_interface_for_pygobject(dict, G_TYPE_STRING_BUILDER, type, &info)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* 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 interface d'exportation en chaîne. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_string_builder(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_string_builder_type()); + + 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 string builder"); + break; + + case 1: + *((GStringBuilder **)dst) = G_STRING_BUILDER(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/strbuilder.h b/plugins/pychrysalide/glibext/strbuilder.h new file mode 100644 index 0000000..1881cae --- /dev/null +++ b/plugins/pychrysalide/glibext/strbuilder.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * strbuilder.h - prototypes pour l'équivalent Python du fichier "glibext/strbuilder.h" + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_STRBUILDER_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_STRBUILDER_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_string_builder_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.StringBuilder'. */ +bool ensure_python_string_builder_is_registered(void); + +/* Tente de convertir en interface d'exportation en chaîne. */ +int convert_to_string_builder(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_STRBUILDER_H */ diff --git a/plugins/pychrysalide/glibext/work.c b/plugins/pychrysalide/glibext/work.c index 6a15984..e6791e3 100644 --- a/plugins/pychrysalide/glibext/work.c +++ b/plugins/pychrysalide/glibext/work.c @@ -41,9 +41,9 @@ /* Initialise la classe des travaux programmés. */ -static void py_generic_work_init_gclass(GGenericWorkClass *, gpointer); +static int py_generic_work_init_gclass(GGenericWorkClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(generic_work, G_TYPE_GENERIC_WORK, py_generic_work_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(generic_work, G_TYPE_GENERIC_WORK); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_generic_work_init(PyObject *, PyObject *, PyObject *); @@ -68,20 +68,22 @@ static PyObject *py_generic_work_process(PyObject *, PyObject *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe des travaux programmés. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_generic_work_init_gclass(GGenericWorkClass *class, gpointer unused) +static int py_generic_work_init_gclass(GGenericWorkClass *gclass, PyTypeObject *pyclass) { - class->run = py_generic_work_run_wrapper; + PY_CLASS_SET_WRAPPER(gclass->run, py_generic_work_run_wrapper); + + return 0; } @@ -296,6 +298,8 @@ bool ensure_python_generic_work_is_registered(void) dict = PyModule_GetDict(module); + pyg_register_class_init(G_TYPE_GENERIC_WORK, (PyGClassInitFunc)py_generic_work_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_GENERIC_WORK, type)) return false; diff --git a/plugins/pychrysalide/glibext/workqueue.c b/plugins/pychrysalide/glibext/workqueue.c index d8126be..ca6c73c 100644 --- a/plugins/pychrysalide/glibext/workqueue.c +++ b/plugins/pychrysalide/glibext/workqueue.c @@ -94,9 +94,9 @@ static int py_work_queue_init(PyObject *self, PyObject *args, PyObject *kwds) { int ret; /* Bilan de lecture des args. */ -#define WORK_QUEUE_DOC \ - "WorkQueue defines a basic work aimed to get processed in a" \ - " thread setup by a pychrysalide.glibext.WorkQueue instance.\n" \ +#define WORK_QUEUE_DOC \ + "WorkQueue creates threads in order to process" \ + " pychrysalide.glibext.Work instances.\n" \ "\n" \ "Instances can be created using the following constructor:\n" \ "\n" \ diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c index d361c3e..0c84278 100644 --- a/plugins/pychrysalide/helpers.c +++ b/plugins/pychrysalide/helpers.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * helpers.c - simplification des interactions de base avec Python * - * Copyright (C) 2018-2020 Cyrille Bagard + * Copyright (C) 2018-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -39,7 +39,6 @@ #include <i18n.h> #include <common/extstr.h> -#include <plugins/dt.h> #include "access.h" @@ -217,6 +216,53 @@ bool has_python_method(PyObject *module, const char *method) * * * Paramètres : target = propriétaire de la routine visée. * * method = désignation de la fonction à appeler. * +* * +* Description : Indique si une routine Python possède une implémentation. * +* * +* Retour : Bilan de l'analyse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool has_python_implementation_method(PyObject *module, const char *method) +{ + bool result; /* Bilan à retourner */ + PyObject *func; /* Fonction visée */ + const PyMethodDef *def; /* Définition de la fonction */ + + result = (PyObject_HasAttrString(module, method) == 1); + + if (result) + { + func = PyObject_GetAttrString(module, method); + assert(func != NULL); + + result = PyCallable_Check(func); + + if (func->ob_type == &PyCFunction_Type) + { + def = ((PyCFunctionObject *)func)->m_ml; + + assert(strcmp(def->ml_name, method) == 0); + + result = (def->ml_meth != not_yet_implemented_method); + + } + + Py_DECREF(func); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : target = propriétaire de la routine visée. * +* method = désignation de la fonction à appeler. * * args = arguments à associer à l'opération. * * * * Description : Appelle une routine Python. * @@ -517,75 +563,10 @@ bool register_python_module_object(PyObject *module, PyTypeObject *type) * * ******************************************************************************/ -PyObject *python_constructor_with_dynamic_gtype(PyTypeObject *type, GType gbase, PyObject *args, PyObject *kwds) -{ - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type parent version Python */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ - - /* Validations diverses */ - - base = pygobject_lookup_class(gbase); - - 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(gbase, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - 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. * -* gbase = type de base natif. * -* cinit = procédure d'initialisation de la classe associée. * -* 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_abstract_constructor_with_dynamic_gtype(PyTypeObject *type, GType gbase, GClassInitFunc cinit, PyObject *args, PyObject *kwds) +PyObject *python_abstract_constructor(PyTypeObject *type, GType gbase, PyObject *args, PyObject *kwds) { PyObject *result; /* Objet à retourner */ PyTypeObject *base; /* Type parent version Python */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ /* Validations diverses */ @@ -598,24 +579,6 @@ PyObject *python_abstract_constructor_with_dynamic_gtype(PyTypeObject *type, GTy goto exit; } - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(gbase, type->tp_name, cinit, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } - - } - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ result = PyType_GenericNew(type, args, kwds); @@ -1159,6 +1122,60 @@ int forward_pygobjet_init(PyObject *self) /****************************************************************************** * * +* Paramètres : type = type Python à ausculter. * +* * +* Description : Détermine si un type Python est implémenté en C ou non. * +* * +* Retour : Bilan de l'analyse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool pytype_has_native_implementation(const PyTypeObject *type) +{ + bool result; /* Bilan à retourner */ + GType gtype; /* Type associé à la classe */ + + static GQuark pygobject_custom_key = 0; /* Clef d'accès direct */ + + /** + * Dans les sources de PyGObject, la fonction pyg_type_register() de + * gi/gimodule.c, appelée depuis gi/types.py, contient la bribe de code + * suivante : + * + * // Mark this GType as a custom python type + * g_type_set_qdata(instance_type, pygobject_custom_key, + * GINT_TO_POINTER (1)); + * + * La fonction pyi_object_register_types() de gi/pygobject-object.c indique + * la clef associée au Quark : + * + * pygobject_custom_key = g_quark_from_static_string("PyGObject::custom"); + * + * Enfin, une fonction inspirante est codée dans le fichier gi/pygi-type.c : + * + * gboolean pyg_gtype_is_custom(GType gtype) + * { + * return g_type_get_qdata (gtype, pygobject_custom_key) != NULL; + * } + * + */ + + if (pygobject_custom_key == 0) + pygobject_custom_key = g_quark_from_static_string("PyGObject::custom"); + + gtype = pyg_type_from_object((PyObject *)type); + + result = (g_type_get_qdata(gtype, pygobject_custom_key) == NULL); + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : arg = argument quelconque à tenter de convertir. * * dst = destination des valeurs récupérées en cas de succès. * * * @@ -1486,6 +1503,109 @@ int convert_to_gdk_rgba(PyObject *arg, void *dst) +/****************************************************************************** +* * +* 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 tableau de chaînes de caractères. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_sequence_to_charp_array(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + charp_array_t *array; /* Tableau à constituer */ + size_t i; /* Boucle de parcours */ + PyObject *value; /* Valeur brute d'un élément */ + + array = (charp_array_t *)dst; + + /* Nettoyage ? */ + if (arg == NULL) + { + result = 1; + goto clean; + } + + else + { + result = 0; + + if (PySequence_Check(arg) != 1) + goto done; + + array->length = PySequence_Length(arg); + + array->values = calloc(array->length, sizeof(char *)); + + for (i = 0; i < array->length; i++) + { + value = PySequence_ITEM(arg, i); + + if (!PyUnicode_Check(value)) + { + Py_DECREF(value); + goto clean; + } + + array->values[i] = strdup(PyUnicode_DATA(value)); + + Py_DECREF(value); + + } + + result = Py_CLEANUP_SUPPORTED; + + } + + done: + + return result; + + clean: + + clean_charp_array(array); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : array = tableau de chaînes de caractères à traiter. * +* * +* Description : Libère de la mémoire un tableau de chaînes de caractères. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void clean_charp_array(charp_array_t *array) +{ + size_t i; /* Boucle de parcours */ + + for (i = 0; i < array->length; i++) + if (array->values[i] != NULL) + free(array->values[i]); + + if (array->values != NULL) + free(array->values); + + array->values = NULL; + array->length = 0; + +} + + + /* ---------------------------------------------------------------------------------- */ /* TRANSFERT DES VALEURS CONSTANTES */ /* ---------------------------------------------------------------------------------- */ diff --git a/plugins/pychrysalide/helpers.h b/plugins/pychrysalide/helpers.h index cc82bba..745d013 100644 --- a/plugins/pychrysalide/helpers.h +++ b/plugins/pychrysalide/helpers.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * helpers.h - prototypes pour la simplification des interactions de base avec Python * - * Copyright (C) 2018-2020 Cyrille Bagard + * Copyright (C) 2018-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -34,6 +34,9 @@ #endif +#include <i18n.h> + + /* ---------------------- ACCELERATEURS POUR PYTHON UNIQUEMENT ---------------------- */ @@ -47,6 +50,9 @@ int convert_to_callable(PyObject *, void *); /* Indique si une routine Python existe ou non. */ bool has_python_method(PyObject *, const char *); +/* Indique si une routine Python possède une implémentation. */ +bool has_python_implementation_method(PyObject *, const char *); + /* Appelle une routine Python. */ PyObject *run_python_method(PyObject *, const char *, PyObject *); @@ -126,7 +132,7 @@ bool register_python_module_object(PyObject *, PyTypeObject *); PYTHON_GETSET_DEF("is_" #name, base ## _is_ ## name, NULL, ATTRIB_RO doc, NULL) #define PYTHON_HAS_DEF_FULL(name, base, doc) \ - PYTHON_GETSET_DEF(#name, base ## _has_ ## name, NULL, ATTRIB_RO doc, NULL) + PYTHON_GETSET_DEF("has_" #name, base ## _has_ ## name, NULL, ATTRIB_RO doc, NULL) #define PYTHON_RAWGET_DEF_FULL(name, base, doc) \ PYTHON_GETSET_DEF(#name, base ## _ ## name, NULL, ATTRIB_RO doc, NULL) @@ -152,14 +158,11 @@ bool register_python_module_object(PyObject *, PyTypeObject *); * * Cf. http://stackoverflow.com/questions/20432335/can-python-abstract-base-classes-inherit-from-c-extensions */ -#define APPLY_ABSTRACT_FLAG(tp) tp->tp_new = PyBaseObject_Type.tp_new +#define APPLY_ABSTRACT_FLAG(tp) tp->tp_new = PyBaseObject_Type.tp_new // REMME /* Accompagne la création d'une instance dérivée en Python. */ -PyObject *python_constructor_with_dynamic_gtype(PyTypeObject *, GType, PyObject *, PyObject *); - -/* Accompagne la création d'une instance dérivée en Python. */ -PyObject *python_abstract_constructor_with_dynamic_gtype(PyTypeObject *, GType, GClassInitFunc, PyObject *, PyObject *); +PyObject *python_abstract_constructor(PyTypeObject *, GType, PyObject *, PyObject *); #define CREATE_DYN_CONSTRUCTOR(pyname, gbase) \ @@ -167,22 +170,41 @@ static PyObject *py_ ## pyname ## _new(PyTypeObject *, PyObject *, PyObject *); static PyObject *py_ ## pyname ## _new(PyTypeObject *type, PyObject *args, PyObject *kwds) \ { \ PyObject *result; /* Objet à retourner */ \ - result = python_constructor_with_dynamic_gtype(type, gbase, args, kwds); \ + result = PyType_GenericNew(type, args, kwds); \ return result; \ } -#define CREATE_DYN_ABSTRACT_CONSTRUCTOR(pyname, gbase, cinit) \ +#define CREATE_DYN_ABSTRACT_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 */ \ - result = python_abstract_constructor_with_dynamic_gtype(type, gbase, (GClassInitFunc)cinit, \ - args, kwds); \ + result = python_abstract_constructor(type, gbase, args, kwds); \ return result; \ } +/** + * Les initialisations de classes engagées par les appels à pyg_register_class_init() + * ne se déclenchent qu'après les initialisations complètes des classes côté GObject. + * + * Typiquement, pour une déclinaison Python du type PythonModule, sont appelées + * successivement les fonctions suivantes : + * - g_plugin_module_class_init() ; + * - g_python_plugin_class_init() ; + * - py_plugin_module_init_gclass(). + * + * Il est alors impératif de considérer les pointeurs de fonction déjà en place + * afin de ne par remplacer les implémentations de GPythonPlugin par les + * wrappers par défaut de PythonModule. + */ + +#define PY_CLASS_SET_WRAPPER(field, ptr) \ + if (field == NULL) \ + field = ptr; + + /* Marque l'interdiction d'une instanciation depuis Python. */ PyObject *no_python_constructor_allowed(PyTypeObject *, PyObject *, PyObject *); @@ -206,6 +228,41 @@ PyTypeObject *define_python_dynamic_type(const PyTypeObject *); /** + * Prise en compte d'éventuelles exceptions levées dans les implémentations. + * + * Par exemple : + * - du code Python exécute une fonction implémentée en C ; + * - cette dernière fait appel à un Wrapper C qui sollicite du code + * d'implémentation Python. + * + * Cette seconde étape peut lever une exception (impletation manquante ou + * implémentation levant une exception. + * + * Les codes C des étapes 1 et 2 ne dispose pas de mécanismes pour transmettre + * le détail des éventuelles exceptions, mais Python le mémorise. + */ + +#define UPDATE_RESULT_IF_RAISED_EXCEPTION(val) \ + do \ + { \ + if (PyErr_Occurred() != NULL) \ + result = val; \ + } \ + while (0) + +#define CLEAN_RESULT_IF_RAISED_EXCEPTION(val) \ + do \ + { \ + if (PyErr_Occurred() != NULL) \ + { \ + Py_XDECREF(result); \ + result = NULL; \ + } \ + } \ + while (0) + + +/** * pygobject_new() prend en compte les références flottantes au moment de la * construction d'un objet Python. * @@ -231,11 +288,23 @@ bool register_class_for_pygobject(PyObject *, GType, PyTypeObject *); bool register_interface_for_pygobject(PyObject *, GType, PyTypeObject *, const GInterfaceInfo *); /* Enregistre un type Python dérivant d'un type GLib dynamique. */ -bool register_class_for_dynamic_pygobject(GType, PyTypeObject *); +bool register_class_for_dynamic_pygobject(GType, PyTypeObject *); // REMME /* Fait suivre à la partie GObject une initialisation nouvelle. */ int forward_pygobjet_init(PyObject *); +/* Détermine si un type Python est implémenté en C ou non. */ +bool pytype_has_native_implementation(const PyTypeObject *); + +#define check_for_native_parent(obj) \ + ({ \ + bool __result; \ + __result = pytype_has_native_implementation((obj)->ob_type->tp_base); \ + if (!__result) \ + PyErr_SetString(PyExc_RuntimeError, _("object parent is not a native type")); \ + __result; \ + }) + /* Tente de convertir en valeur GType. */ int convert_to_gtype(PyObject *, void *); @@ -283,6 +352,21 @@ int convert_to_gdk_rgba(PyObject *, void *); #endif +/* Tableau de chaînes de caractères converti */ +typedef struct _charp_array_t +{ + char **values; /* Liste de chaînes textuelles */ + size_t length; /* Taille de cette liste */ + +} charp_array_t; + +/* Tente de convertir en tableau de chaînes de caractères. */ +int convert_to_sequence_to_charp_array(PyObject *, void *); + +/* Libère de la mémoire un tableau de chaînes de caractères. */ +void clean_charp_array(charp_array_t *); + + /* ----------------------- TRANSFERT DES VALEURS CONSTANTES ------------------------- */ diff --git a/plugins/pychrysalide/plugins/Makefile.am b/plugins/pychrysalide/plugins/Makefile.am index bb9ed5d..abadb7f 100644 --- a/plugins/pychrysalide/plugins/Makefile.am +++ b/plugins/pychrysalide/plugins/Makefile.am @@ -2,10 +2,10 @@ noinst_LTLIBRARIES = libpychrysaplugins.la libpychrysaplugins_la_SOURCES = \ - constants.h constants.c \ - plugin.h plugin.c \ module.h module.c \ - translate.h translate.c + plugin.h plugin.c \ + python-int.h \ + python.h python.c libpychrysaplugins_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT diff --git a/plugins/pychrysalide/plugins/constants.c b/plugins/pychrysalide/plugins/constants.c deleted file mode 100644 index 7e20e4c..0000000 --- a/plugins/pychrysalide/plugins/constants.c +++ /dev/null @@ -1,147 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * constants.c - ajout des constantes principales - * - * Copyright (C) 2020 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include "constants.h" - - -#include <plugins/plugin-def.h> - - -#include "../helpers.h" - - - -/****************************************************************************** -* * -* Paramètres : type = type dont le dictionnaire est à compléter. * -* * -* Description : Définit les constantes relatives aux greffons Python. * -* * -* Retour : true en cas de succès de l'opération, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool define_plugin_module_constants(PyTypeObject *type) -{ - bool result; /* Bilan à retourner */ - PyObject *values; /* Groupe de valeurs à établir */ - - result = true; - - values = PyDict_New(); - - if (result) result = add_const_to_group(values, "BASIC_NONE", PGA_BASIC_NONE); - if (result) result = add_const_to_group(values, "PLUGIN_INIT", PGA_PLUGIN_INIT); - if (result) result = add_const_to_group(values, "PLUGIN_LOADED", PGA_PLUGIN_LOADED); - if (result) result = add_const_to_group(values, "PLUGIN_EXIT", PGA_PLUGIN_EXIT); - if (result) result = add_const_to_group(values, "NATIVE_PLUGINS_LOADED", PGA_NATIVE_PLUGINS_LOADED); - if (result) result = add_const_to_group(values, "ALL_PLUGINS_LOADED", PGA_ALL_PLUGINS_LOADED); - if (result) result = add_const_to_group(values, "TYPE_BUILDING", PGA_TYPE_BUILDING); - if (result) result = add_const_to_group(values, "GUI_THEME", PGA_GUI_THEME); - if (result) result = add_const_to_group(values, "PANEL_CREATION", PGA_PANEL_CREATION); - if (result) result = add_const_to_group(values, "PANEL_DOCKING", PGA_PANEL_DOCKING); - if (result) result = add_const_to_group(values, "CONTENT_EXPLORER", PGA_CONTENT_EXPLORER); - if (result) result = add_const_to_group(values, "CONTENT_RESOLVER", PGA_CONTENT_RESOLVER); - if (result) result = add_const_to_group(values, "CONTENT_ANALYZED", PGA_CONTENT_ANALYZED); - if (result) result = add_const_to_group(values, "FORMAT_ANALYSIS_STARTED", PGA_FORMAT_ANALYSIS_STARTED); - if (result) result = add_const_to_group(values, "FORMAT_PRELOAD", PGA_FORMAT_PRELOAD); - if (result) result = add_const_to_group(values, "FORMAT_ATTACH_DEBUG", PGA_FORMAT_ATTACH_DEBUG); - if (result) result = add_const_to_group(values, "FORMAT_ANALYSIS_ENDED", PGA_FORMAT_ANALYSIS_ENDED); - if (result) result = add_const_to_group(values, "FORMAT_POST_ANALYSIS_STARTED", PGA_FORMAT_POST_ANALYSIS_STARTED); - if (result) result = add_const_to_group(values, "FORMAT_POST_ANALYSIS_ENDED", PGA_FORMAT_POST_ANALYSIS_ENDED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_STARTED", PGA_DISASSEMBLY_STARTED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_RAW", PGA_DISASSEMBLY_RAW); - if (result) result = add_const_to_group(values, "DISASSEMBLY_HOOKED_LINK", PGA_DISASSEMBLY_HOOKED_LINK); - if (result) result = add_const_to_group(values, "DISASSEMBLY_HOOKED_POST", PGA_DISASSEMBLY_HOOKED_POST); - if (result) result = add_const_to_group(values, "DISASSEMBLY_LIMITED", PGA_DISASSEMBLY_LIMITED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_LOOPS", PGA_DISASSEMBLY_LOOPS); - if (result) result = add_const_to_group(values, "DISASSEMBLY_LINKED", PGA_DISASSEMBLY_LINKED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_GROUPED", PGA_DISASSEMBLY_GROUPED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_RANKED", PGA_DISASSEMBLY_RANKED); - if (result) result = add_const_to_group(values, "DISASSEMBLY_ENDED", PGA_DISASSEMBLY_ENDED); - if (result) result = add_const_to_group(values, "DETECTION_OBFUSCATORS", PGA_DETECTION_OBFUSCATORS); - - if (!result) - { - Py_DECREF(values); - goto exit; - } - - result = attach_constants_group_to_type(type, true, "PluginAction", values, - "Features with which plugins can extend the core of Chrysalide."); - - exit: - - return result; - -} - - -/****************************************************************************** -* * -* 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 constante PluginAction. * -* * -* Retour : Bilan de l'opération, voire indications supplémentaires. * -* * -* Remarques : - * -* * -******************************************************************************/ - -int convert_to_plugin_action(PyObject *arg, void *dst) -{ - int result; /* Bilan à retourner */ - unsigned long value; /* Valeur récupérée */ - - result = PyObject_IsInstance(arg, (PyObject *)&PyLong_Type); - - 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 PluginAction"); - break; - - case 1: - value = PyLong_AsUnsignedLong(arg); - *((PluginAction *)dst) = value; - break; - - default: - assert(false); - break; - - } - - return result; - -} diff --git a/plugins/pychrysalide/plugins/module.c b/plugins/pychrysalide/plugins/module.c index 1c7b326..ce94611 100644 --- a/plugins/pychrysalide/plugins/module.c +++ b/plugins/pychrysalide/plugins/module.c @@ -33,8 +33,8 @@ #include <plugins/pglist.h> -#include "constants.h" #include "plugin.h" +#include "python.h" #include "../helpers.h" @@ -46,7 +46,7 @@ static PyObject *py_plugins_get_plugin_by_name(PyObject *, PyObject *); static PyObject *py_plugins_get_all_plugins(PyObject *, PyObject *); /* Fournit les greffons offrant le service demandé. */ -static PyObject *py_plugins_get_all_plugins_for_action(PyObject *, PyObject *); +//static PyObject *py_plugins_get_all_plugins_for_action(PyObject *, PyObject *); @@ -162,7 +162,7 @@ static PyObject *py_plugins_get_all_plugins(PyObject *self, PyObject *args) } - +#if 0 /****************************************************************************** * * * Paramètres : self = NULL car méthode statique. * @@ -219,7 +219,7 @@ static PyObject *py_plugins_get_all_plugins_for_action(PyObject *self, PyObject return result; } - +#endif /****************************************************************************** * * @@ -248,7 +248,7 @@ bool add_plugins_module(PyObject *super) static PyMethodDef py_plugins_methods[] = { PY_PLUGINS_GET_PLUGIN_BY_NAME_METHOD, PY_PLUGINS_GET_ALL_PLUGINS_METHOD, - PY_PLUGINS_GET_ALL_PLUGINS_FOR_ACTION_METHOD, + //PY_PLUGINS_GET_ALL_PLUGINS_FOR_ACTION_METHOD, { NULL } }; @@ -293,6 +293,7 @@ bool populate_plugins_module(void) result = true; if (result) result = ensure_python_plugin_module_is_registered(); + if (result) result = ensure_python_python_plugin_is_registered(); assert(result); diff --git a/plugins/pychrysalide/plugins/plugin.c b/plugins/pychrysalide/plugins/plugin.c index de070cb..78f57ba 100644 --- a/plugins/pychrysalide/plugins/plugin.c +++ b/plugins/pychrysalide/plugins/plugin.c @@ -25,22 +25,17 @@ #include "plugin.h" -#include <assert.h> -#include <libgen.h> #include <malloc.h> #include <pygobject.h> #include <string.h> -#include <common/extstr.h> -#include <plugins/dt.h> +#include <common/compiler.h> #include <plugins/plugin-int.h> #include <plugins/pglist.h> #include <plugins/self.h> -#include "constants.h" -#include "translate.h" #include "../access.h" #include "../core.h" #include "../helpers.h" @@ -52,85 +47,53 @@ /* Initialise la classe des greffons d'extension. */ -static void py_plugin_module_init_gclass(GPluginModuleClass *, gpointer); +static int py_plugin_module_init_gclass(GPluginModuleClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(plugin_module, G_TYPE_PLUGIN_MODULE, py_plugin_module_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(plugin_module, G_TYPE_PLUGIN_MODULE); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds); -/* Encadre une étape de la vie d'un greffon. */ -static bool py_plugin_module_manage_wrapper(GPluginModule *); - -/* Assiste la désactivation d'un greffon. */ -static bool py_plugin_module_exit(GPluginModule *); - -/* Accompagne la fin du chargement des modules natifs. */ -static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *, PluginAction); - -/* Fournit le nom brut associé au greffon par défaut. */ -static PyObject *py_plugin_module_get_modname_by_default(PyObject *, PyObject *); +/* Pointe le fichier contenant le greffon manipulé. */ +static char *py_plugin_module_get_filename_wrapper(const GPluginModule *); /* Fournit le nom brut associé au greffon. */ static char *py_plugin_module_get_modname_wrapper(const GPluginModule *); -#if 0 - -#ifdef INCLUDE_GTK_SUPPORT - -/* Complète une liste de resources pour thème. */ -static void py_plugin_module_include_theme_wrapper(const GPluginModule *, PluginAction, gboolean, char ***, size_t *); - -/* Rend compte de la création d'un panneau. */ -static void py_plugin_module_notify_panel_creation_wrapper(const GPluginModule *, PluginAction, GPanelItem *); - -/* Rend compte d'un affichage ou d'un retrait de panneau. */ -static void py_plugin_module_notify_panel_docking_wrapper(const GPluginModule *, PluginAction, GPanelItem *, bool); - -#endif - -/* Procède à une opération liée à un contenu binaire. */ -static void py_plugin_module_handle_binary_content_wrapper(const GPluginModule *, PluginAction, GBinContent *, wgroup_id_t, GtkStatusStack *); - -/* Procède à une opération liée à un contenu chargé. */ -static void py_plugin_module_handle_loaded_content_wrapper(const GPluginModule *, PluginAction, GLoadedContent *, wgroup_id_t, GtkStatusStack *); +/* Prend acte de l'activation du greffon. */ +static bool py_plugin_module_enable_wrapper(GPluginModule *); -/* Procède à une opération liée à l'analyse d'un format. */ -static bool py_plugin_module_handle_known_format_analysis_wrapper(const GPluginModule *, PluginAction, GKnownFormat *, wgroup_id_t, GtkStatusStack *); +/* Prend acte de la désactivation du greffon. */ +static bool py_plugin_module_disable_wrapper(GPluginModule *); -/* Procède à un préchargement de format de fichier. */ -static bool py_plugin_module_preload_binary_format_wrapper(const GPluginModule *, PluginAction, GBinFormat *, GPreloadInfo *, GtkStatusStack *); -/* Procède au rattachement d'éventuelles infos de débogage. */ -static void py_plugin_module_attach_debug_format_wrapper(const GPluginModule *, PluginAction, GExeFormat *); -/* Exécute une action pendant un désassemblage de binaire. */ -static void py_plugin_module_process_disassembly_event_wrapper(const GPluginModule *, PluginAction, GLoadedBinary *, GtkStatusStack *, GProcContext *); - -/* Effectue la détection d'effets d'outils externes. */ -static void py_plugin_module_detect_external_tools_wrapper(const GPluginModule *, PluginAction, const GLoadedContent *, bool, char ***, size_t *); - -#endif +/* ------------------------- MODULE PYTHON POUR LES SCRIPTS ------------------------- */ +/* Affiche un message dans le journal des messages système. */ +static PyObject *py_plugin_module_log_message(PyObject *, PyObject *); -/* ------------------------- MODULE PYTHON POUR LES SCRIPTS ------------------------- */ +/* Indique le nom associé à un greffon. */ +static PyObject *py_plugin_module_get_name(PyObject *, void *); +/* Fournit une description fonctionnelle d'un greffon. */ +static PyObject *py_plugin_module_get_desc(PyObject *, void *); -/* Construit le nom d'un fichier de configuration du greffon. */ -static PyObject *py_plugin_module_build_config_filename(PyObject *, PyObject *); +/* Fournit la version d'un greffon et de ses fonctionnalités. */ +static PyObject *py_plugin_module_get_version(PyObject *, void *); -/* Affiche un message dans le journal des messages système. */ -static PyObject *py_plugin_module_log_message(PyObject *, PyObject *); +/* Fournit l'URL des ressources en ligne liées à un greffon. */ +static PyObject *py_plugin_module_get_url(PyObject *, void *); -/* Fournit le nom brut associé au greffon. */ -static PyObject *py_plugin_module_get_modname(PyObject *, void *); +/* Fournit la liste des dépendances d'un greffon donné. */ +static PyObject *py_plugin_module_get_requirements(PyObject *, void *); /* Indique le fichier contenant le greffon manipulé. */ static PyObject *py_plugin_module_get_filename(PyObject *, void *); -/* Fournit la description du greffon dans son intégralité. */ -static PyObject *py_plugin_module_get_interface(PyObject *, void *); +/* Fournit le nom brut associé au greffon. */ +static PyObject *py_plugin_module_get_modname(PyObject *, void *); @@ -141,47 +104,26 @@ static PyObject *py_plugin_module_get_interface(PyObject *, void *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe des greffons d'extension. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_plugin_module_init_gclass(GPluginModuleClass *class, gpointer unused) +static int py_plugin_module_init_gclass(GPluginModuleClass *gclass, PyTypeObject *pyclass) { - class->init = NULL; - class->manage = py_plugin_module_manage_wrapper; - class->exit = py_plugin_module_exit; + PY_CLASS_SET_WRAPPER(gclass->get_filename, py_plugin_module_get_filename_wrapper); + PY_CLASS_SET_WRAPPER(gclass->get_modname, py_plugin_module_get_modname_wrapper); - class->plugins_loaded = py_plugin_module_notify_plugins_loaded_wrapper; + PY_CLASS_SET_WRAPPER(gclass->enable, py_plugin_module_enable_wrapper); + PY_CLASS_SET_WRAPPER(gclass->disable, py_plugin_module_disable_wrapper); - class->get_modname = py_plugin_module_get_modname_wrapper; - -#if 0 - -#ifdef INCLUDE_GTK_SUPPORT - class->include_theme = py_plugin_module_include_theme_wrapper; - class->notify_panel = py_plugin_module_notify_panel_creation_wrapper; - class->notify_docking = py_plugin_module_notify_panel_docking_wrapper; -#endif - - class->handle_content = py_plugin_module_handle_binary_content_wrapper; - class->handle_loaded = py_plugin_module_handle_loaded_content_wrapper; - - class->handle_fmt_analysis = py_plugin_module_handle_known_format_analysis_wrapper; - class->preload_format = py_plugin_module_preload_binary_format_wrapper; - class->attach_debug = py_plugin_module_attach_debug_format_wrapper; - - class->process_disass = py_plugin_module_process_disassembly_event_wrapper; - - class->detect = py_plugin_module_detect_external_tools_wrapper; - -#endif + return 0; } @@ -202,45 +144,56 @@ static void py_plugin_module_init_gclass(GPluginModuleClass *class, gpointer unu static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds) { + const char *desc; /* Description plus loquace */ + const char *version; /* Version du greffon */ + const char *url; /* Site Web associé */ + const char *name; /* Désignation humaine courte */ + charp_array_t required; /* Liste des dépendances */ int ret; /* Bilan d'un appel */ GPluginModule *plugin; /* Greffon à manipuler */ - plugin_interface *iface; /* Interface à constituer */ - GPluginModule *dependency; /* Module nécessaire */ - PyObject *value; /* Valeur à présence imposée */ - size_t i; /* Boucle de parcours */ - PyObject *action; /* Identifiant d'une action */ + + static char *kwlist[] = { "name", "desc", "version", "url", "required", NULL }; #define PLUGIN_MODULE_DOC \ - "PythonModule is the class allowing the creation of Chrysalide plugins" \ - " for Python." \ + "PluginModule is the core class handling the Chrysalide plugins." \ + " Python plugins should inherit the pychrysalide.plugins.PythonPlugin" \ + " instead of this one." \ "\n" \ "Calls to the *__init__* constructor of this abstract object expect" \ - " no particular argument.\n" \ + " the following arguments as keyword parameters:\n" \ + "* *name*: a string providing a key name for the plugin;\n" \ + "* *desc* (optional): a string for a human readable description of the" \ + " features provided by the plugin;\n" \ + "* *version* (optional): a string providing the version of the plugin;" \ + " Version format is free;\n" \ + "* *url* (optional): a string for the homepage describing the plugin;\n"\ + "* *required* (optional): dependencies of the plugin." \ "\n" \ - "Several items have to be defined as class attributes in the final" \ - " class:\n" \ - "* *_name*: a string providing a small name for the plugin;\n" \ - "* *_desc*: a string for a human readable description of the plugin;\n" \ - "* *_version*: a string providing the version of the plugin;\n" \ - "* *_url*: a string for the homepage describing the plugin;\n" \ - "* *_actions*: a tuple of" \ - " pychrysalide.plugins.PluginModule.PluginAction defining the features" \ - " the plugin is bringing; this list can be empty.\n" \ + "Dependency to the *PyChrysalide* plugin is added automatically if not" \ + " specified.\n" \ "\n" \ - "Depending on the implemented actions, some of the following methods" \ - " have to be defined for new classes:\n" \ - "* pychrysalide.plugins.PluginModule._init_config();\n" \ - "* pychrysalide.plugins.PluginModule._notify_plugins_loaded();\n" \ - "* pychrysalide.plugins.PluginModule._include_theme();\n" \ - "* pychrysalide.plugins.PluginModule._on_panel_creation;\n" \ - "* pychrysalide.plugins.PluginModule._on_panel_docking();\n" \ - "* pychrysalide.plugins.PluginModule._handle_binary_content();\n" \ - "* pychrysalide.plugins.PluginModule._handle_loaded_content();\n" \ - "* pychrysalide.plugins.PluginModule._handle_format_analysis();\n" \ - "* pychrysalide.plugins.PluginModule._preload_format();\n" \ - "* pychrysalide.plugins.PluginModule._attach_debug_format();\n" \ - "* pychrysalide.plugins.PluginModule._process_disassembly_event();\n" \ - "* pychrysalide.plugins.PluginModule._detect_external_tools()." + "The following methods have to be defined for new classes:\n" \ + "* pychrysalide.plugins.PluginModule._get_filename();\n" \ + "* pychrysalide.plugins.PluginModule._get_modname();\n" \ + "* pychrysalide.plugins.PluginModule._enable();\n" \ + "* pychrysalide.plugins.PluginModule._disable()." + + /* Récupération des paramètres */ + + desc = NULL; + version = NULL; + url = NULL; + required.values = NULL; + required.length = 0; + + ret = PyArg_ParseTupleAndKeywords(args, kwds, "s|sssO&", kwlist, + &name, &desc, &version, &url, + convert_to_sequence_to_charp_array, &required); + if (!ret) return -1; + + required.values = realloc(required.values, ++required.length * sizeof(char *)); + + required.values[required.length - 1] = strdup("PyChrysalide"); /* Initialisation d'un objet GLib */ @@ -251,105 +204,17 @@ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds) plugin = G_PLUGIN_MODULE(pygobject_get(self)); - iface = calloc(1, sizeof(plugin_interface)); - plugin->interface = iface; - -#define LOAD_PYTHON_IFACE(attr) \ - do \ - { \ - value = PyObject_GetAttrString(self, "_" #attr); \ - if (value == NULL) \ - { \ - PyErr_SetString(PyExc_TypeError, _("A '_" #attr "' class attributes is missing.")); \ - return -1; \ - } \ - if (PyUnicode_Check(value)) \ - { \ - iface->attr = strdup(PyUnicode_AsUTF8(value)); \ - Py_DECREF(value); \ - } \ - else \ - { \ - Py_DECREF(value); \ - PyErr_SetString(PyExc_TypeError, _("The '_" #attr "' class attributes must be a string.")); \ - return -1; \ - } \ - assert(iface->attr != NULL); \ - } \ - while (0); - - LOAD_PYTHON_IFACE(name); - LOAD_PYTHON_IFACE(desc); - LOAD_PYTHON_IFACE(version); - LOAD_PYTHON_IFACE(url); - - iface->container = false; - - /** - * Comme le greffon n'est pas passé par la résolution des dépendances, - * orchestrée par la fonction g_plugin_module_resolve_dependencies(), - * on simule l'effet attendu en obtenant une référence par un appel à - * get_plugin_by_name(). - * - * L'incrémentation des références doit coller au plus près de - * l'inscription nominative du greffon : en cas de sortie impromptue - * (lorsqu'une erreur intervient pendant un chargement par exemple), - * l'état de l'ensemble est ainsi cohérent au moment du retrait du - * greffon fautif via la fonction g_plugin_module_dispose(). - */ - - lock_plugin_list_for_reading(); - dependency = get_plugin_by_name("PyChrysalide", NULL); - unlock_plugin_list_for_reading(); - - assert(dependency != NULL); - - if (dependency == NULL) - { - PyErr_SetString(PyExc_TypeError, _("The internal name of the Python plugin has changed!")); - return -1; - } - - iface->required = malloc(sizeof(char *)); - iface->required[0] = "PyChrysalide"; - iface->required_count = 1; + STORE_PLUGIN_ABI(plugin); - /* Validation du reste de l'interface */ - - value = PyObject_GetAttrString(self, "_actions"); - - if (value == NULL) + if (!g_plugin_module_create(plugin, name, desc, version, url, + CONST_ARRAY_CAST(required.values, char), required.length)) { - PyErr_SetString(PyExc_TypeError, _("An '_actions' class attributes is missing.")); + clean_charp_array(&required); + PyErr_SetString(PyExc_ValueError, _("Unable to create plugin module.")); return -1; } - if (!PyTuple_Check(value)) - { - Py_DECREF(value); - PyErr_SetString(PyExc_TypeError, _("The '_actions' class attributes must be a tuple.")); - return -1; - } - - iface->actions_count = PyTuple_Size(value); - iface->actions = malloc(iface->actions_count * sizeof(plugin_action_t)); - - for (i = 0; i < iface->actions_count; i++) - { - action = PyTuple_GetItem(value, i); - - if (!PyLong_Check(action)) - { - Py_DECREF(value); - PyErr_SetString(PyExc_TypeError, _("invalid type for plugin action.")); - return -1; - } - - iface->actions[i] = PyLong_AsUnsignedLong(action); - - } - - Py_DECREF(value); + clean_charp_array(&required); return 0; @@ -358,56 +223,55 @@ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds) /****************************************************************************** * * -* Paramètres : plugin = greffon à manipuler. * +* Paramètres : plugin = greffon à consulter. * * * -* Description : Encadre une étape de la vie d'un greffon. * +* Description : Pointe le fichier contenant le greffon manipulé. * * * -* Retour : Bilan de l'opération. * +* Retour : Chemin d'accès au greffon. * * * * Remarques : - * * * ******************************************************************************/ -static bool py_plugin_module_manage_wrapper(GPluginModule *plugin) +static char *py_plugin_module_get_filename_wrapper(const GPluginModule *plugin) { - bool result; /* Bilan à faire remonter */ + char *result; /* Désignation brute à renvoyer*/ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan d'exécution */ -#define PLUGIN_MODULE_MANAGE_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _manage, "$self, action, /", \ - METH_VARARGS, \ - "Abstract method called to react to several steps of the plugin" \ - " life.\n" \ - "\n" \ - "The expected action is a" \ - " pychrysalide.plugins.PluginModule.PluginAction value.\n" \ - "\n" \ - "This method has to be defined in order to handle actions such as" \ - " *PLUGIN_LOADED*." \ +#define PLUGIN_MODULE_GET_FILENAME_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _get_filename, "$self, /", \ + METH_NOARGS, \ + "Abstract method providing the filename of the script.\n" \ + "\n" \ + "The result should be the string value pointing to the plugin" \ + " file path.\n" \ ) - result = true; + result = NULL; gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(plugin)); - if (has_python_method(pyobj, "_manage")) + if (has_python_method(pyobj, "_get_filename")) { - args = PyTuple_New(1); + pyret = run_python_method(pyobj, "_get_filename", NULL); - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(PGA_PLUGIN_LOADED)); + if (pyret != NULL) + { + if (!PyUnicode_Check(pyret)) + g_plugin_module_log_variadic_message(plugin, LMT_ERROR, + _("The returned raw name must be a string")); - pyret = run_python_method(pyobj, "_manage", args); + else + result = strdup(PyUnicode_DATA(pyret)); - result = (pyret == Py_True); + } Py_XDECREF(pyret); - Py_DECREF(args); } @@ -422,44 +286,62 @@ static bool py_plugin_module_manage_wrapper(GPluginModule *plugin) /****************************************************************************** * * -* Paramètres : plugin = greffon à manipuler. * +* Paramètres : plugin = greffon à valider. * * * -* Description : Assiste la désactivation d'un greffon. * +* Description : Fournit le nom brut associé au greffon. * * * -* Retour : Bilan de l'opération. * +* Retour : Désignation brute du greffon. * * * * Remarques : - * * * ******************************************************************************/ -static bool py_plugin_module_exit(GPluginModule *plugin) +static char *py_plugin_module_get_modname_wrapper(const GPluginModule *plugin) { - bool result; /* Bilan à faire remonter */ - plugin_interface *final; /* Interface finale conservée */ + char *result; /* Désignation brute à renvoyer*/ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Bilan d'exécution */ - result = true; +#define PLUGIN_MODULE_GET_MODNAME_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _get_modname, "$self, /", \ + METH_NOARGS, \ + "Abstract method providing the raw module name of the loaded" \ + " plugin.\n" \ + "\n" \ + "The result should be a short string value.\n" \ +) - final = (plugin_interface *)G_PLUGIN_MODULE(plugin)->interface; + result = NULL; + + gstate = PyGILState_Ensure(); - if (final != NULL) + pyobj = pygobject_new(G_OBJECT(plugin)); + + if (has_python_method(pyobj, "_get_modname")) { - if (final->name != NULL) free(final->name); - if (final->desc != NULL) free(final->desc); - if (final->version != NULL) free(final->version); - if (final->url != NULL) free(final->url); + pyret = run_python_method(pyobj, "_get_modname", NULL); - assert(final->required_count == 1); + if (pyret != NULL) + { + if (!PyUnicode_Check(pyret)) + g_plugin_module_log_variadic_message(plugin, LMT_ERROR, + _("The returned raw name must be a string")); - if (final->required != NULL) - free(final->required); + else + result = strdup(PyUnicode_DATA(pyret)); - if (final->actions != NULL) - free(final->actions); + } - free(final); + Py_XDECREF(pyret); } + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + return result; } @@ -468,51 +350,45 @@ static bool py_plugin_module_exit(GPluginModule *plugin) /****************************************************************************** * * * Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * * * -* Description : Accompagne la fin du chargement des modules natifs. * +* Description : Prend acte de l'activation du greffon. * * * -* Retour : - * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *plugin, PluginAction action) +static bool py_plugin_module_enable_wrapper(GPluginModule *plugin) { + bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan d'exécution */ -#define PLUGIN_MODULE_NOTIFY_PLUGINS_LOADED_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _notify_plugins_loaded, "$self, action, /", \ - METH_VARARGS, \ - "Abstract method called once all the (native?) plugins are" \ - " loaded.\n" \ - "\n" \ - "The expected action is a" \ - " pychrysalide.plugins.PluginModule.PluginAction value.\n" \ - "\n" \ - "This method has to be defined in order to handle actions such as" \ - " *NATIVE_PLUGINS_LOADED* or *PLUGINS_LOADED*." \ +#define PLUGIN_MODULE_ENABLE_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _enable, "$self, /", \ + METH_VARARGS, \ + "Abstract method called when the plugin gets enabled.\n" \ + "\n" \ + "The result is a boolean status: *False* if an implementation" \ + " failed, *True* otherwise." \ ) + result = true; + gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(plugin)); - if (has_python_method(pyobj, "_notify_plugins_loaded")) + if (has_python_implementation_method(pyobj, "_enable")) { - args = PyTuple_New(1); - - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action)); + pyret = run_python_method(pyobj, "_enable", NULL); - pyret = run_python_method(pyobj, "_notify_plugins_loaded", args); + result = (pyret == Py_True); Py_XDECREF(pyret); - Py_DECREF(args); } @@ -520,36 +396,6 @@ static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *plugin PyGILState_Release(gstate); -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* args = arguments fournis à l'appel. * -* * -* Description : Fournit le nom brut associé au greffon par défaut. * -* * -* Retour : Désignation brute du greffon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_plugin_module_get_modname_by_default(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - GPluginModule *plugin; /* Version native du greffon */ - char *path; /* Chemin à traiter */ - - plugin = G_PLUGIN_MODULE(pygobject_get(self)); - - path = strdup(g_plugin_module_get_filename(plugin)); - - result = PyUnicode_FromString(basename(path)); - - free(path); - return result; } @@ -557,52 +403,44 @@ static PyObject *py_plugin_module_get_modname_by_default(PyObject *self, PyObjec /****************************************************************************** * * -* Paramètres : plugin = greffon à valider. * +* Paramètres : plugin = greffon à manipuler. * * * -* Description : Fournit le nom brut associé au greffon. * +* Description : Prend acte de la désactivation du greffon. * * * -* Retour : Désignation brute du greffon. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static char *py_plugin_module_get_modname_wrapper(const GPluginModule *plugin) +static bool py_plugin_module_disable_wrapper(GPluginModule *plugin) { - char *result; /* Désignation brute à renvoyer*/ + bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan d'exécution */ -#define PLUGIN_MODULE_GET_MODNAME_WRAPPER PYTHON_WRAPPER_DEF_WITH \ -( \ - _get_modname, "$self, /", \ - METH_VARARGS, py_plugin_module_get_modname_by_default, \ - "(Abstract) method providing the raw module name of the plugin.\n" \ - " loaded.\n" \ - "\n" \ - "The result should be a short string value.\n" \ - "\n" \ - "A default implementation builds the module name from the Python" \ - " script filename." \ +#define PLUGIN_MODULE_DISABLE_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _disable, "$self, /", \ + METH_VARARGS, \ + "Abstract method called when the plugin gets disabled.\n" \ + "\n" \ + "The result is a boolean status: *False* if an implementation" \ + " failed, *True* otherwise." \ ) - result = NULL; + result = true; gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(plugin)); - if (has_python_method(pyobj, "_get_modname")) + if (has_python_implementation_method(pyobj, "_disable")) { - pyret = run_python_method(pyobj, "_get_modname", NULL); + pyret = run_python_method(pyobj, "_disable", NULL); - if (!PyUnicode_Check(pyret)) - g_plugin_module_log_variadic_message(plugin, LMT_ERROR, - _("The returned raw name must be a string")); - - else - result = strdup(PyUnicode_DATA(pyret)); + result = (pyret == Py_True); Py_XDECREF(pyret); @@ -617,6 +455,15 @@ static char *py_plugin_module_get_modname_wrapper(const GPluginModule *plugin) } + + + + + + + + + #if 0 #ifdef INCLUDE_GTK_SUPPORT @@ -1382,51 +1229,42 @@ static void py_plugin_module_detect_external_tools_wrapper(const GPluginModule * * Paramètres : self = objet Python concerné par l'appel. * * args = arguments fournis à l'appel. * * * -* Description : Construit le nom d'un fichier de configuration du greffon. * +* Description : Affiche un message dans le journal des messages système. * * * -* Retour : Chemin d'accès déterminé, ou NULL en cas d'erreur. * +* Retour : Rien en équivalent Python. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_plugin_module_build_config_filename(PyObject *self, PyObject *args) +static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args) { PyObject *result; /* Bilan à retourner */ - const char *final; /* Suffixe de fichier imposé */ - int create; /* Volonté de création */ - char *filename; /* Nom de fichier déterminé */ + LogMessageType type; /* Espèce du message */ + const char *msg; /* Contenu du message */ -#define PLUGIN_MODULE_BUILD_CONFIG_FILENAME_METHOD PYTHON_METHOD_DEF \ +#define PLUGIN_MODULE_LOG_MESSAGE_METHOD PYTHON_METHOD_DEF \ ( \ - build_config_filename, "final, /, create=False", \ + log_message, "type, msg, /", \ METH_VARARGS, py_plugin_module, \ - "Build a filename suitable for the plugin configuration, ending with" \ - " the *final* suffix.\n" \ + "Display a message in the log window, in graphical mode, or in the" \ + " console output if none.\n" \ "\n" \ - "If the *create* parameter is set, the path to this filename is" \ - " created.\n" \ + "The type of the message has to be a pychrysalide.core.LogMessageType" \ + " value." \ "\n" \ - "The result is a string or None on failure." \ + "The only difference with the main pychrysalide.core.log_message()" \ + " function is that messages are automatically prefixed with the plugin" \ + " name here." \ ) - create = 0; - - if (!PyArg_ParseTuple(args, "s|p", &final, &create)) + if (!PyArg_ParseTuple(args, "O&s", convert_to_log_message_type, &type, &msg)) return NULL; - filename = g_plugin_module_build_config_filename(G_PLUGIN_MODULE(pygobject_get(self)), final, create); + g_plugin_module_log_simple_message(G_PLUGIN_MODULE(pygobject_get(self)), type, msg); - if (filename != NULL) - { - result = PyUnicode_FromString(filename); - free(filename); - } - else - { - result = Py_None; - Py_INCREF(result); - } + result = Py_None; + Py_INCREF(result); return result; @@ -1435,45 +1273,35 @@ static PyObject *py_plugin_module_build_config_filename(PyObject *self, PyObject /****************************************************************************** * * -* Paramètres : self = objet Python concerné par l'appel. * -* args = arguments fournis à l'appel. * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * * * -* Description : Affiche un message dans le journal des messages système. * +* Description : Indique le nom associé à un greffon. * * * -* Retour : Rien en équivalent Python. * +* Retour : Désignation interne de l'extension, pour référence(s). * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args) +static PyObject *py_plugin_module_get_name(PyObject *self, void *closure) { - PyObject *result; /* Bilan à retourner */ - LogMessageType type; /* Espèce du message */ - const char *msg; /* Contenu du message */ + PyObject *result; /* Valeur à retourner */ + GPluginModule *plugin; /* Version native du greffon */ + const char *name; /* Nom attribué au greffon */ -#define PLUGIN_MODULE_LOG_MESSAGE_METHOD PYTHON_METHOD_DEF \ -( \ - log_message, "type, msg, /", \ - METH_VARARGS, py_plugin_module, \ - "Display a message in the log window, in graphical mode, or in the" \ - " console output if none.\n" \ - "\n" \ - "The type of the message has to be a pychrysalide.core.LogMessageType" \ - " value." \ - "\n" \ - "The only difference with the main pychrysalide.core.log_message()" \ - " function is that messages are automatically prefixed with the plugin" \ - " name here." \ +#define PLUGIN_MODULE_NAME_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + name, py_plugin_module, \ + "Name of the plugin. This string value is used to" \ + " reference the plugin as dependency." \ ) - if (!PyArg_ParseTuple(args, "O&s", convert_to_log_message_type, &type, &msg)) - return NULL; + plugin = G_PLUGIN_MODULE(pygobject_get(self)); - g_plugin_module_log_simple_message(G_PLUGIN_MODULE(pygobject_get(self)), type, msg); + name = g_plugin_module_get_name(plugin); - result = Py_None; - Py_INCREF(result); + result = PyUnicode_FromString(name); return result; @@ -1485,32 +1313,31 @@ static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args) * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * -* Description : Fournit le nom brut associé au greffon. * +* Description : Fournit une description fonctionnelle d'un greffon. * * * -* Retour : Désignation brute du greffon. * +* Retour : Description textuelle associée à une extension ou NULL. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_plugin_module_get_modname(PyObject *self, void *closure) +static PyObject *py_plugin_module_get_desc(PyObject *self, void *closure) { PyObject *result; /* Valeur à retourner */ GPluginModule *plugin; /* Version native du greffon */ - char *modname; /* Désignation brute */ + const char *desc; /* Description du greffon */ -#define PLUGIN_MODULE_MODNAME_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - modname, py_plugin_module, \ - "Raw module name of the plugin." \ +#define PLUGIN_MODULE_DESC_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + desc, py_plugin_module, \ + "Optional plugin description as string or *None*." \ ) plugin = G_PLUGIN_MODULE(pygobject_get(self)); - modname = g_plugin_module_get_modname(plugin); - result = PyUnicode_FromString(modname); + desc = g_plugin_module_get_desc(plugin); - free(modname); + result = PyUnicode_FromString(desc); return result; @@ -1522,30 +1349,32 @@ static PyObject *py_plugin_module_get_modname(PyObject *self, void *closure) * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * -* Description : Indique le fichier contenant le greffon manipulé. * +* Description : Fournit la version d'un greffon et de ses fonctionnalités. * * * -* Retour : Chemin d'accès au greffon. * +* Retour : Version sous forme de chaîne de caractères ou None. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_plugin_module_get_filename(PyObject *self, void *closure) +static PyObject *py_plugin_module_get_version(PyObject *self, void *closure) { PyObject *result; /* Valeur à retourner */ GPluginModule *plugin; /* Version native du greffon */ - const char *filename; /* Chemin d'accès associé */ + const char *version; /* Version du greffon */ -#define PLUGIN_MODULE_FILENAME_ATTRIB PYTHON_GET_DEF_FULL \ +#define PLUGIN_MODULE_VERSION_ATTRIB PYTHON_GET_DEF_FULL \ ( \ - filename, py_plugin_module, \ - "Filename of the plugin." \ + version, py_plugin_module, \ + "Optional plugin version, in free format, as string," \ + " or *None*." \ ) plugin = G_PLUGIN_MODULE(pygobject_get(self)); - filename = g_plugin_module_get_filename(plugin); - result = PyUnicode_FromString(filename); + version = g_plugin_module_get_version(plugin); + + result = PyUnicode_FromString(version); return result; @@ -1557,113 +1386,204 @@ static PyObject *py_plugin_module_get_filename(PyObject *self, void *closure) * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * -* Description : Fournit la description du greffon dans son intégralité. * +* Description : Fournit l'URL des ressources en ligne liées à un greffon. * * * -* Retour : Interfaçage renseigné. * +* Retour : URL de renvoi associée à une extension ou None. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_plugin_module_get_interface(PyObject *self, void *closure) +static PyObject *py_plugin_module_get_url(PyObject *self, void *closure) { PyObject *result; /* Valeur à retourner */ GPluginModule *plugin; /* Version native du greffon */ - const plugin_interface *iface; /* Interface liée à traduire */ + const char *url; /* URL associée au greffon */ -#define PLUGIN_MODULE_INTERFACE_ATTRIB PYTHON_GET_DEF_FULL \ +#define PLUGIN_MODULE_URL_ATTRIB PYTHON_GET_DEF_FULL \ ( \ - interface, py_plugin_module, \ - "Interface exported by the plugin..\n" \ - "\n" \ - "This property is a pychrysalide.StructObject instance." \ - "\n" \ - "The provided information is composed of the following" \ - " properties :\n" \ - "\n" \ - "* gtp_name;\n" \ - "* name;\n" \ - "* desc;\n" \ - "* version;\n" \ - "* url;\n" \ - "* container;\n" \ - "* required;\n" \ - "* actions.\n" \ - "\n" \ - "The *gtp_name* value may be *None* for non-native plugin." \ - " All other fields carry a string value except:\n" \ - "* *container*: a boolean status indicating if the plugin" \ - " can embed other plugins;\n" \ - "* *required*: a tuple of depedencies names;\n" \ - "* *actions*: a tuple of available features from the plugin"\ - " coded as pychrysalide.plugins.PluginModule.PluginAction" \ - " values." \ + url, py_plugin_module, \ + "Optional URL pointing to the plugin homepage as string" \ + " or *None*." \ ) plugin = G_PLUGIN_MODULE(pygobject_get(self)); - iface = g_plugin_module_get_interface(plugin); - result = translate_plugin_interface_to_python(iface); + url = g_plugin_module_get_url(plugin); + + result = PyUnicode_FromString(url); return result; } -#if 0 + /****************************************************************************** * * * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * -* Description : Fournit la configuration mise en place pour le greffon. * +* Description : Fournit la liste des dépendances d'un greffon donné. * * * -* Retour : Configuration dédiée à l'extension. * +* Retour : Liste des noms d'extensions requises pour une extension. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_plugin_module_get_config(PyObject *self, void *closure) +static PyObject *py_plugin_module_get_requirements(PyObject *self, void *closure) { PyObject *result; /* Valeur à retourner */ GPluginModule *plugin; /* Version native du greffon */ - GGenConfig *config; /* Configuration associée */ + const char * const *required; /* Liste de dépendances */ + size_t count; /* Nombre de ces dépendances */ + size_t i; /* Boucle de parcours */ -#define PLUGIN_MODULE_CONFIG_ATTRIB PYTHON_GET_DEF_FULL \ +#define PLUGIN_MODULE_REQUIREMENTS_ATTRIB PYTHON_GET_DEF_FULL \ ( \ - config, py_plugin_module, \ - "Dedicated configuration for the plugin." \ - "\n" \ - "The value is a pychrysalide.glibext.GenConfig instance" \ - " or None if the configuration is not yet created.\n" \ - "\n" \ - "As configuration storage path depends on the plugin name," \ - " all plugin properties have to get fully loaded by the" \ - " core before the configuration can be setup." \ - "automatically" \ + requirements, py_plugin_module, \ + "Tuple of the plugin dependencies." \ +) + + plugin = G_PLUGIN_MODULE(pygobject_get(self)); + + required = g_plugin_module_get_requirements(plugin, &count); + + result = PyTuple_New(count); + + for (i = 0; i < count; i++) + PyTuple_SetItem(result, i, PyUnicode_FromString(required[i])); + + return result; + +} + + + + + + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Indique le fichier contenant le greffon manipulé. * +* * +* Retour : Chemin d'accès au greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_plugin_module_get_filename(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GPluginModule *plugin; /* Version native du greffon */ + char *filename; /* Chemin d'accès associé */ + +#define PLUGIN_MODULE_FILENAME_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + filename, py_plugin_module, \ + "Filename of the plugin." \ +) + + plugin = G_PLUGIN_MODULE(pygobject_get(self)); + + filename = g_plugin_module_get_filename(plugin); + + if (filename != NULL) + { + result = PyUnicode_FromString(filename); + + free(filename); + + } + + else + { + /** + * La méthode de classe sollicitée a renvoyé une valeur nulle. + * + * Si cette méthode correspond à une implémentation Python + * (avec un appel à not_yet_implemented_method()), une exception + * est déjà en place. + * + * Si aucune exception n'a été prévue, un rattrapage est effectué ici. + */ + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_NotImplementedError, _("unexpected NULL value as filename")); + + result = NULL; + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit le nom brut associé au greffon. * +* * +* Retour : Désignation brute du greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_plugin_module_get_modname(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GPluginModule *plugin; /* Version native du greffon */ + char *modname; /* Désignation brute */ + +#define PLUGIN_MODULE_MODNAME_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + modname, py_plugin_module, \ + "Raw module name of the plugin." \ ) plugin = G_PLUGIN_MODULE(pygobject_get(self)); - config = g_plugin_module_get_config(plugin); - if (config == NULL) + modname = g_plugin_module_get_modname(plugin); + + if (modname != NULL) { - result = Py_None; - Py_INCREF(result); + result = PyUnicode_FromString(modname); + + free(modname); + } else { - result = pygobject_new(G_OBJECT(config)); + /** + * La méthode de classe sollicitée a renvoyé une valeur nulle. + * + * Si cette méthode correspond à une implémentation Python + * (avec un appel à not_yet_implemented_method()), une exception + * est déjà en place. + * + * Si aucune exception n'a été prévue, un rattrapage est effectué ici. + */ + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_NotImplementedError, _("unexpected NULL value as modname")); - g_object_unref(G_OBJECT(config)); + result = NULL; } return result; } -#endif /****************************************************************************** @@ -1681,33 +1601,22 @@ static PyObject *py_plugin_module_get_config(PyObject *self, void *closure) PyTypeObject *get_python_plugin_module_type(void) { static PyMethodDef py_plugin_module_methods[] = { - PLUGIN_MODULE_MANAGE_WRAPPER, - PLUGIN_MODULE_NOTIFY_PLUGINS_LOADED_WRAPPER, + PLUGIN_MODULE_GET_FILENAME_WRAPPER, PLUGIN_MODULE_GET_MODNAME_WRAPPER, -#if 0 -#ifdef INCLUDE_GTK_SUPPORT - PLUGIN_MODULE_INCLUDE_THEME_WRAPPER, - PLUGIN_MODULE_ON_PANEL_CREATION_WRAPPER, - PLUGIN_MODULE_ON_PANEL_DOCKING_WRAPPER, -#endif - PLUGIN_MODULE_HANDLE_BINARY_CONTENT_WRAPPER, - PLUGIN_MODULE_HANDLE_LOADED_CONTENT_WRAPPER, - PLUGIN_MODULE_HANDLE_BINARY_FORMAT_ANALYSIS_WRAPPER, - PLUGIN_MODULE_PRELOAD_BINARY_FORMAT_WRAPPER, - PLUGIN_MODULE_ATTACH_DEBUG_FORMAT_WRAPPER, - PLUGIN_MODULE_PROCESS_DISASSEMBLY_EVENT_WRAPPER, - PLUGIN_MODULE_DETECT_EXTERNAL_TOOLS_WRAPPER, - PLUGIN_MODULE_BUILD_CONFIG_FILENAME_METHOD, -#endif + PLUGIN_MODULE_ENABLE_WRAPPER, + PLUGIN_MODULE_DISABLE_WRAPPER, PLUGIN_MODULE_LOG_MESSAGE_METHOD, { NULL } }; static PyGetSetDef py_plugin_module_getseters[] = { - PLUGIN_MODULE_MODNAME_ATTRIB, + PLUGIN_MODULE_NAME_ATTRIB, + PLUGIN_MODULE_DESC_ATTRIB, + PLUGIN_MODULE_VERSION_ATTRIB, + PLUGIN_MODULE_URL_ATTRIB, + PLUGIN_MODULE_REQUIREMENTS_ATTRIB, PLUGIN_MODULE_FILENAME_ATTRIB, - PLUGIN_MODULE_INTERFACE_ATTRIB, - //PLUGIN_MODULE_CONFIG_ATTRIB, + PLUGIN_MODULE_MODNAME_ATTRIB, { NULL } }; @@ -1761,10 +1670,9 @@ bool ensure_python_plugin_module_is_registered(void) dict = PyModule_GetDict(module); - if (!register_class_for_pygobject(dict, G_TYPE_PLUGIN_MODULE, type)) - return false; + pyg_register_class_init(G_TYPE_PLUGIN_MODULE, (PyGClassInitFunc)py_plugin_module_init_gclass); - if (!define_plugin_module_constants(type)) + if (!register_class_for_pygobject(dict, G_TYPE_PLUGIN_MODULE, type)) return false; } @@ -1772,80 +1680,3 @@ bool ensure_python_plugin_module_is_registered(void) return true; } - - -/****************************************************************************** -* * -* Paramètres : modname = nom du module à charger. * -* filename = chemin d'accès au code Python à charger. * -* * -* Description : Crée un greffon à partir de code Python. * -* * -* Retour : Adresse de la structure mise en place ou NULL si erreur. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GPluginModule *create_python_plugin(const char *modname, const char *filename) -{ - GPluginModule *result; /* Structure à retourner */ - PyObject *name; /* Chemin d'accès pour Python */ - PyObject *module; /* Script Python chargé */ - PyObject *dict; /* Dictionnaire associé */ - PyObject *class; /* Classe à instancier */ - PyObject *instance; /* Instance Python du greffon */ - - name = PyUnicode_FromString(modname); - if (name == NULL) goto bad_exit; - - module = PyImport_Import(name); - Py_DECREF(name); - - if (module == NULL) goto no_import; - - dict = PyModule_GetDict(module); - class = PyDict_GetItemString(dict, "AutoLoad"); - - if (class == NULL) goto no_class; - if (!PyType_Check(class->ob_type)) goto no_class; - - instance = PyObject_CallFunction(class, NULL); - if (instance == NULL) goto no_instance; - - result = G_PLUGIN_MODULE(pygobject_get(instance)); - - result->filename = strdup(filename); - - /** - * L'instance Python et l'objet GLib résultante sont un même PyGObject. - * - * Donc pas besoin de toucher au comptage des références ici, la libération - * se réalisera à la fin, quand l'objet GLib sera libéré. - */ - - Py_DECREF(module); - - return result; - - no_instance: - - log_pychrysalide_exception(_("An error occured when building the 'AutoLoad' instance")); - - no_class: - - if (class == NULL) - log_plugin_simple_message(LMT_ERROR, - _("An error occured when looking for the 'AutoLoad': item not found!")); - - no_import: - - Py_XDECREF(module); - - log_pychrysalide_exception(_("An error occured when importing '%s'"), modname); - - bad_exit: - - return NULL; - -} diff --git a/plugins/pychrysalide/plugins/plugin.h b/plugins/pychrysalide/plugins/plugin.h index ad54b8e..fd3d239 100644 --- a/plugins/pychrysalide/plugins/plugin.h +++ b/plugins/pychrysalide/plugins/plugin.h @@ -27,13 +27,9 @@ #include <Python.h> -#include <glib-object.h> #include <stdbool.h> -#include <plugins/plugin.h> - - /* Fournit un accès à une définition de type à diffuser. */ PyTypeObject *get_python_plugin_module_type(void); @@ -41,9 +37,6 @@ PyTypeObject *get_python_plugin_module_type(void); /* Prend en charge l'objet 'pychrysalide.plugins.PluginModule'. */ bool ensure_python_plugin_module_is_registered(void); -/* Crée un greffon à partir de code Python. */ -GPluginModule *create_python_plugin(const char *, const char *); - #endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_PLUGIN_H */ diff --git a/plugins/pychrysalide/plugins/python-int.h b/plugins/pychrysalide/plugins/python-int.h new file mode 100644 index 0000000..7408d18 --- /dev/null +++ b/plugins/pychrysalide/plugins/python-int.h @@ -0,0 +1,58 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * python-int.h - prototypes internes pour la déclinaison Python de greffons + * + * Copyright (C) 2025 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 _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_INT_H +#define _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_INT_H + + +#include "python.h" + + +#include <plugins/plugin-int.h> + + + +/* Greffon Python pour Chrysalide (instance) */ +struct _GPythonPlugin +{ + GPluginModule parent; /* A laisser en premier */ + + char *file; /* Valeur initiale de __file__ */ + +}; + + +/* Greffon Python pour Chrysalide (classe) */ +struct _GPythonPluginClass +{ + GPluginModuleClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un greffon Python. */ +bool g_python_plugin_create(GPythonPlugin *, const char *, const char *, const char *, const char *, char ***, size_t *, const char *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_INT_H */ diff --git a/plugins/pychrysalide/plugins/python.c b/plugins/pychrysalide/plugins/python.c new file mode 100644 index 0000000..d6b9281 --- /dev/null +++ b/plugins/pychrysalide/plugins/python.c @@ -0,0 +1,489 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * python.c - déclinaison Python de greffons + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "python.h" + + +#include <malloc.h> +#include <pygobject.h> + + +#include <common/compiler.h> +#include <common/cpp.h> + + +#include "plugin.h" +#include "python-int.h" +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------- COMPOSITION DE NOUVEAU GREFFON ------------------------- */ + + +/* Initialise la classe des greffons Python. */ +static void g_python_plugin_class_init(GPythonPluginClass *); + +/* Initialise une instance de greffon Python. */ +static void g_python_plugin_init(GPythonPlugin *); + +/* Supprime toutes les références externes. */ +static void g_python_plugin_dispose(GPythonPlugin *); + +/* Procède à la libération totale de la mémoire. */ +static void g_python_plugin_finalize(GPythonPlugin *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Pointe le fichier contenant le greffon manipulé. */ +static char *g_python_plugin_get_filename(const GPythonPlugin *); + +/* Fournit le nom brut associé au greffon. */ +static char *g_python_plugin_get_modname(const GPythonPlugin *); + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_ABSTRACT_CONSTRUCTOR(python_plugin, G_TYPE_PYTHON_PLUGIN); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_python_plugin_init(PyObject *self, PyObject *args, PyObject *kwds); + + + +/* ---------------------------------------------------------------------------------- */ +/* COMPOSITION DE NOUVEAU GREFFON */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un greffon Python. */ +G_DEFINE_TYPE(GPythonPlugin, g_python_plugin, G_TYPE_PLUGIN_MODULE); + + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* * +* Description : Initialise la classe des greffons Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_class_init(GPythonPluginClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GPluginModuleClass *plugin; /* Version parente de la classe*/ + + object = G_OBJECT_CLASS(class); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_python_plugin_dispose; + object->finalize = (GObjectFinalizeFunc)g_python_plugin_finalize; + + plugin = G_PLUGIN_MODULE_CLASS(class); + + plugin->get_filename = (get_plugin_filename_fc)g_python_plugin_get_filename; + plugin->get_modname = (get_plugin_modname_fc)g_python_plugin_get_modname; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser. * +* * +* Description : Initialise une instance de greffon Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_init(GPythonPlugin *plugin) +{ + STORE_PLUGIN_ABI(plugin); + + plugin->file = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_dispose(GPythonPlugin *plugin) +{ + G_OBJECT_CLASS(g_python_plugin_parent_class)->dispose(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_plugin_finalize(GPythonPlugin *plugin) +{ + if (plugin->file != NULL) + free(plugin->file); + + G_OBJECT_CLASS(g_python_plugin_parent_class)->finalize(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser pleinement. * +* name = nom du greffon pour référence, principalement. * +* desc = présentation éventuelle à destination humaine. * +* version = indication de version éventuelle. * +* url = référence vers une ressource en ligne. * +* required = liste de dépendances éventuelles ou NULL. * +* count = taille de cette liste. * +* file = emplacement du script considéré. * +* * +* Description : Met en place un greffon Python. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +bool g_python_plugin_create(GPythonPlugin *plugin, const char *name, const char *desc, const char *version, const char *url, char ***required, size_t *count, const char *file) +{ + bool result; /* Bilan à retourner */ + + /* Ajout imposé d'une dépendance à Python */ + + *required = realloc(*required, ++(*count) * sizeof(char *)); + + (*required)[*count - 1] = strdup("PyChrysalide"); + + /* Poursuite de la mise en place */ + + result = g_plugin_module_create(G_PLUGIN_MODULE(plugin), + name, desc, version, url, + CONST_ARRAY_CAST(*required, char), *count); + + if (result) + plugin->file = strdup(file); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à consulter. * +* * +* Description : Pointe le fichier contenant le greffon manipulé. * +* * +* Retour : Chemin d'accès au greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_python_plugin_get_filename(const GPythonPlugin *plugin) +{ + char *result; /* Chemin d'accès à renvoyer */ + + result = strdup(plugin->file); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à valider. * +* * +* Description : Fournit le nom brut associé au greffon. * +* * +* Retour : Désignation brute du greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_python_plugin_get_modname(const GPythonPlugin *plugin) +{ + char *result; /* Désignation brute à renvoyer*/ + char *filename; /* Chemin du script Python */ + char *name; /* Nom du fichier associé */ + size_t length; /* Taille du nom */ + int ret; /* Bilan d'une comparaison */ + + filename = g_python_plugin_get_filename(plugin); + + name = basename(filename); + + length = strlen(name); + +#define PYTHON_SUFFIX ".py" + + ret = strncmp(&name[length - STATIC_STR_SIZE(PYTHON_SUFFIX)], + PYTHON_SUFFIX, + STATIC_STR_SIZE(PYTHON_SUFFIX)); + + if (ret == 0) + name[length - STATIC_STR_SIZE(PYTHON_SUFFIX)] = '\0'; + + result = strdup(name); + + free(filename); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_python_plugin_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + const char *version; /* Version du greffon */ + const char *url; /* Site Web associé */ + charp_array_t required; /* Liste des dépendances */ + const char *file; /* Emplacement du script */ + int ret; /* Bilan de lecture des args. */ + PyObject *value; /* Valeur d'un arg. implicite */ + const char *name; /* Désignation humaine courte */ + const char *desc; /* Description plus loquace */ + GPythonPlugin *plugin; /* Greffon à manipuler */ + + static char *kwlist[] = { "file", "version", "url", "required", NULL }; + +#define PYTHON_PLUGIN_DOC \ + "The PythonPlugin class helps to build custom Python plugins:\n" \ + "* some required information (*name* and *desc* for the parent" \ + " constructor) is automatically extracted from the final class name" \ + " or its documentation;\n" \ + "* implementations for pychrysalide.plugins.PluginModule._get_filename()" \ + " and pychrysalide.plugins.PluginModule._get_modname() are provided," \ + " relying on the *file* argument.\n" \ + "\n" \ + "Calls to the *__init__* constructor of this abstract object expect the" \ + " following arguments as keyword parameters:\n" \ + "* *file*: path to the Python script; the value should be equal to the" \ + " *__file__* keyword;\n" \ + "* *version* (optional): a string providing the version of the plugin;" \ + " Version format is free;\n" \ + "* *url* (optional): a string for the homepage describing the plugin;\n" \ + "* *required* (optional): dependencies of the plugin." + + /* Récupération des paramètres */ + + version = NULL; + url = NULL; + required.values = NULL; + required.length = 0; + + ret = PyArg_ParseTupleAndKeywords(args, kwds, "s|ssO&", kwlist, + &file, &version, &url, + convert_to_sequence_to_charp_array, &required); + if (!ret) return -1; + + name = self->ob_type->tp_name; + + value = PyObject_GetAttrString(self, "__doc__"); + + if (value != NULL && PyUnicode_Check(value)) + desc = PyUnicode_AsUTF8(value); + else + desc = NULL; + + Py_XDECREF(value); + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + plugin = G_PYTHON_PLUGIN(pygobject_get(self)); + + STORE_PLUGIN_ABI(plugin); + + if (!g_python_plugin_create(plugin, name, desc, version, url, + &required.values, &required.length, + file)) + { + clean_charp_array(&required); + PyErr_SetString(PyExc_ValueError, _("Unable to create Python plugin.")); + return -1; + } + + clean_charp_array(&required); + + return 0; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION POUR SUPPORT PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_python_plugin_type(void) +{ + static PyMethodDef py_python_plugin_methods[] = { + { NULL } + }; + + static PyGetSetDef py_python_plugin_getseters[] = { + { NULL } + }; + + static PyTypeObject py_python_plugin_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.plugins.PythonPlugin", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = PYTHON_PLUGIN_DOC, + + .tp_methods = py_python_plugin_methods, + .tp_getset = py_python_plugin_getseters, + + .tp_init = py_python_plugin_init, + .tp_new = py_python_plugin_new, + + }; + + return &py_python_plugin_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.plugins.PythonPlugin'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_python_plugin_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'PythonPlugin' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_python_plugin_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.plugins"); + + dict = PyModule_GetDict(module); + + if (!ensure_python_plugin_module_is_registered()) + return false; + + if (!register_class_for_pygobject(dict, G_TYPE_PYTHON_PLUGIN, type)) + return false; + + } + + return true; + +} diff --git a/plugins/pychrysalide/plugins/python.h b/plugins/pychrysalide/plugins/python.h new file mode 100644 index 0000000..8613bd1 --- /dev/null +++ b/plugins/pychrysalide/plugins/python.h @@ -0,0 +1,57 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * python.h - prototypes pour la déclinaison Python de greffons + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_H +#define _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_H + + +#include <Python.h> +#include <stdbool.h> + + +#include <glibext/helpers.h> + + + +/* ------------------------- COMPOSITION DE NOUVEAU GREFFON ------------------------- */ + + +#define G_TYPE_PYTHON_PLUGIN (g_python_plugin_get_type()) + +DECLARE_GTYPE(GPythonPlugin, g_python_plugin, G, PYTHON_PLUGIN); + + + +/* ------------------------- DEFINITION POUR SUPPORT PYTHON ------------------------- */ + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_python_plugin_type(void); + +/* Prend en charge l'objet 'pychrysalide.plugins.PythonPlugin'. */ +bool ensure_python_python_plugin_is_registered(void); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_H */ diff --git a/plugins/pychrysalide/plugins/translate.c b/plugins/pychrysalide/plugins/translate.c deleted file mode 100644 index 055788c..0000000 --- a/plugins/pychrysalide/plugins/translate.c +++ /dev/null @@ -1,110 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * translate.c - conversion de structures liées aux greffons en objets Python - * - * Copyright (C) 2021 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include "translate.h" - - -#include <assert.h> - - -#include "plugin.h" -#include "../helpers.h" -#include "../struct.h" - - - -/****************************************************************************** -* * -* Paramètres : iface = ensemble d'informations à transcrire en Python. * -* * -* Description : Traduit une description d'interface de greffon. * -* * -* Retour : Structure mise en place ou NULL en cas d'erreur. * -* * -* Remarques : - * -* * -******************************************************************************/ - -PyObject *translate_plugin_interface_to_python(const plugin_interface *iface) -{ - PyObject *result; /* Construction à retourner */ - PyTypeObject *base; /* Modèle d'objet à créer */ - bool status; /* Bilan d'une traduction */ - PyObject *array; /* Tableau à insérer */ - size_t i; /* Boucle de parcours */ - PyTypeObject *itype; /* Type d'élément à créer */ - PyObject *item; /* Elément mis en place */ - - base = get_python_py_struct_type(); - - result = PyObject_CallFunction((PyObject *)base, NULL); - assert(result != NULL); - - status = true; - - if (status) status = TRANSLATE_STRING_FIELD(result, iface, gtp_name); - if (status) status = TRANSLATE_STRING_FIELD(result, iface, name); - if (status) status = TRANSLATE_STRING_FIELD(result, iface, desc); - if (status) status = TRANSLATE_STRING_FIELD(result, iface, version); - if (status) status = TRANSLATE_STRING_FIELD(result, iface, url); - - if (status) status = TRANSLATE_BOOLEAN_FIELD(result, iface, container); - - if (status) status = TRANSLATE_ARRAY_FIELD(result, iface, required, &array); - - if (status) - { - itype = get_python_plugin_module_type(); - - for (i = 0; i < iface->required_count; i++) - { - item = PyUnicode_FromString(iface->required[i]); - PyTuple_SetItem(array, i, item); - } - - } - - if (status) status = TRANSLATE_ARRAY_FIELD(result, iface, actions, &array); - - if (status) - { - itype = get_python_plugin_module_type(); - - for (i = 0; i < iface->actions_count; i++) - { - item = cast_with_constants_group_from_type(itype, "PluginAction", iface->actions[i]); - PyTuple_SetItem(array, i, item); - } - - } - - if (!status) - { - Py_DECREF(result); - result = NULL; - } - - return result; - -} diff --git a/plugins/pynb/Makefile.am b/plugins/pynb/Makefile.am new file mode 100644 index 0000000..50e549f --- /dev/null +++ b/plugins/pynb/Makefile.am @@ -0,0 +1,77 @@ + +BUILT_SOURCES = resources.h resources.c + + +lib_LTLIBRARIES = libpynbui.la + +libdir = $(pluginslibdir) + + +# if BUILD_PYTHON3_BINDINGS + +# PYTHON3_LIBADD = python/libpynbpython.la + +# if BUILD_DISCARD_LOCAL + +# PYTHON3_LDFLAGS = -Wl,-rpath,$(pluginslibdir) \ +# -L$(top_srcdir)/plugins/pychrysalide/.libs -l:pychrysalide.so + +# else + +# PYTHON3_LDFLAGS = -Wl,-rpath,$(abs_top_srcdir)/plugins/pychrysalide/.libs \ +# -L$(top_srcdir)/plugins/pychrysalide/.libs -l:pychrysalide.so + +# endif + +# PYTHON3_SUBDIRS = python + +# endif + + +libpynbui_la_SOURCES = \ + core-ui-int.h \ + core-ui.h core-ui.c \ + panel-int.h \ + panel.h panel.c \ + params-int.h \ + params.h params.c \ + prefs-int.h \ + prefs.h prefs.c \ + resources.h resources.c + +libpynbui_la_LIBADD = \ + $(PYTHON3_LIBADD) + +libpynbui_la_CFLAGS = $(LIBGTK4_CFLAGS) + +libpynbui_la_LDFLAGS = \ + $(LIBGTK4_LIBS) $(PYTHON3_LDFLAGS) + + +devdir = $(includedir)/chrysalide-$(subdir) + +dev_HEADERS = $(libpynbui_la_SOURCES:%c=) + + +RES_FILES = \ + panel.ui \ + params.ui \ + prefs.ui \ + data/images/pynb-symbolic.svg + + +resources.c: gresource.xml $(RES_FILES) + glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-source --c-name plugins_pynb gresource.xml + +resources.h: gresource.xml + glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-header --c-name plugins_pynb gresource.xml + + +CLEANFILES = resources.h resources.c + +EXTRA_DIST = gresource.xml $(RES_FILES) + + +AM_CPPFLAGS = -I$(top_srcdir)/src $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) + +SUBDIRS = $(PYTHON3_SUBDIRS) diff --git a/plugins/pynb/core-ui-int.h b/plugins/pynb/core-ui-int.h new file mode 100644 index 0000000..caf5713 --- /dev/null +++ b/plugins/pynb/core-ui-int.h @@ -0,0 +1,56 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-ui-int.h - prototypes internes pour le plugin présentant des notes avec code Python + * + * Copyright (C) 2025 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 _PLUGINS_PYNB_CORE_UI_INT_H +#define _PLUGINS_PYNB_CORE_UI_INT_H + + +#include <plugins/native-int.h> + + +#include "core-ui.h" + + + +/* Greffon natif pour la présentation de notes avec texte et code Python (instance) */ +struct _GPythonNotebookPluginUI +{ + GNativePlugin parent; /* A laisser en premier */ + +}; + + +/* Greffon natif pour la présentation de notes avec texte et code Python (classe) */ +struct _GPythonNotebookPluginUIClass +{ + GNativePluginClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un module pour un module pour présentation. */ +bool g_python_notebook_plugin_ui_create(GPythonNotebookPluginUI *, GModule *); + + + +#endif /* _PLUGINS_PYNB_CORE_UI_INT_H */ diff --git a/plugins/pynb/core-ui.c b/plugins/pynb/core-ui.c new file mode 100644 index 0000000..dd49a83 --- /dev/null +++ b/plugins/pynb/core-ui.c @@ -0,0 +1,363 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-ui.c - présentation de notes sous forme de texte et de code Python + * + * Copyright (C) 2025 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/>. + */ + + +#include "core-ui.h" + + +#include <i18n.h> +#include <gui/core/panels.h> +#include <plugins/self.h> +#include <plugins/tweakable-int.h> + + +#include "core-ui-int.h" +#include "panel.h" +#include "params.h" +#include "prefs.h" + + + +/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */ + + +/* Initialise la classe des recherches et identifications. */ +static void g_python_notebook_plugin_ui_class_init(GPythonNotebookPluginUIClass *); + +/* Procède à l'initialisation de l'interface d'intervention. */ +static void g_python_notebook_plugin_ui_tweakable_plugin_interface_init(GTweakablePluginInterface *); + +/* Initialise une instance de recherches et identifications. */ +static void g_python_notebook_plugin_ui_init(GPythonNotebookPluginUI *); + +/* Supprime toutes les références externes. */ +static void g_python_notebook_plugin_ui_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void g_python_notebook_plugin_ui_finalize(GObject *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Prend acte de l'activation du greffon. */ +static bool g_python_notebook_plugin_ui_enable(GPythonNotebookPluginUI *); + +/* Prend acte de la désactivation du greffon. */ +static bool g_python_notebook_plugin_ui_disable(GPythonNotebookPluginUI *); + + + +/* ------------------- INTEGRATION DANS L'EDITION DES PREFERENCES ------------------- */ + + +/* Fournit une liste de sections de configuration. */ +static tweak_info_t *g_python_notebook_plugin_ui_get_tweak_info(const GTweakablePlugin *, size_t *); + + + +/* ---------------------------------------------------------------------------------- */ +/* COMPOSITION DE NOUVEAU GREFFON NATIF */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour une présentation de notes texte et code Python. */ +G_DEFINE_TYPE_WITH_CODE(GPythonNotebookPluginUI, g_python_notebook_plugin_ui, G_TYPE_NATIVE_PLUGIN, + G_IMPLEMENT_INTERFACE(G_TYPE_TWEAKABLE_PLUGIN, g_python_notebook_plugin_ui_tweakable_plugin_interface_init)); + + +NATIVE_PLUGIN_ENTRYPOINT(g_python_notebook_plugin_ui_new); + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* * +* Description : Initialise la classe des recherches et identifications. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_notebook_plugin_ui_class_init(GPythonNotebookPluginUIClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GPluginModuleClass *plugin; /* Version parente de la classe*/ + + object = G_OBJECT_CLASS(class); + + object->dispose = g_python_notebook_plugin_ui_dispose; + object->finalize = g_python_notebook_plugin_ui_finalize; + + plugin = G_PLUGIN_MODULE_CLASS(class); + + plugin->enable = (pg_management_fc)g_python_notebook_plugin_ui_enable; + plugin->disable = (pg_management_fc)g_python_notebook_plugin_ui_disable; + +} + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface d'intervention. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_notebook_plugin_ui_tweakable_plugin_interface_init(GTweakablePluginInterface *iface) +{ + iface->get_info = g_python_notebook_plugin_ui_get_tweak_info; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser. * +* * +* Description : Initialise une instance de recherches et identifications. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_notebook_plugin_ui_init(GPythonNotebookPluginUI *plugin) +{ + STORE_PLUGIN_ABI(plugin); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_notebook_plugin_ui_dispose(GObject *object) +{ + G_OBJECT_CLASS(g_python_notebook_plugin_ui_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_python_notebook_plugin_ui_finalize(GObject *object) +{ + G_OBJECT_CLASS(g_python_notebook_plugin_ui_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : module = extension vue du système. * +* * +* Description : Crée un module pour présentation de notes. * +* * +* Retour : Adresse de la structure mise en place. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +GPluginModule *g_python_notebook_plugin_ui_new(GModule *module) +{ + GPythonNotebookPluginUI *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_PYTHON_NOTEBOOK_PLUGIN_UI, NULL); + + if (!g_python_notebook_plugin_ui_create(result, module)) + g_clear_object(&result); + + return G_PLUGIN_MODULE(result); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser pleinement. * +* module = extension vue du système. * +* * +* Description : Met en place un module pour un module pour présentation. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +bool g_python_notebook_plugin_ui_create(GPythonNotebookPluginUI *plugin, GModule *module) +{ + bool result; /* Bilan à retourner */ + +#ifdef INCLUDE_PYTHON3_BINDINGS +# define PG_REQ REQ_LIST("PyChrysalide") +#else +# define PG_REQ NO_REQ +#endif + + result = g_native_plugin_create(G_NATIVE_PLUGIN(plugin), + "PythonNotebook", + "Edit notebook with text and code to support binary analysis", + PACKAGE_VERSION, + CHRYSALIDE_WEBSITE("doc/plugins/pynb"), + PG_REQ, + module); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* * +* Description : Prend acte de l'activation du greffon. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_python_notebook_plugin_ui_enable(GPythonNotebookPluginUI *plugin) +{ + bool result; /* Bilan à retourner */ + panel_info_t info; /* Infos d'enregistrement */ + + info.category = "Main"; + + info.image = "pynb-symbolic"; + info.title = _("Python notebook"); + info.desc = _("Edit notebook with text and code to support binary analysis"); + + info.personality = FPP_MAIN_PANEL; + + info.panel_type = GTK_TYPE_PYTHON_NOTEBOOK_PANEL; + info.params_type = GTK_TYPE_PYTHON_NOTEBOOK_PARAMETERS; + + result = register_framework_panel_definition(&info); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à manipuler. * +* * +* Description : Prend acte de la désactivation du greffon. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_python_notebook_plugin_ui_disable(GPythonNotebookPluginUI *plugin) +{ + bool result; /* Bilan à retourner */ + + + // TODO : unregister + + result = true; + + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* INTEGRATION DANS L'EDITION DES PREFERENCES */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : plugin = interface à manipuler. * +* count = taille de la liste renvoyée. [OUT] * +* * +* Description : Fournit une liste de sections de configuration. * +* * +* Retour : Définition(s) de section de configuration ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static tweak_info_t *g_python_notebook_plugin_ui_get_tweak_info(const GTweakablePlugin *plugin, size_t *count) +{ + tweak_info_t *result; /* Liste à renvoyer */ + + tweak_info_t infos[] = { + TWEAK_SIMPLE_DEF("root", "Basics", + "pynb-symbolic", "pynotebook", "Notebook", GTK_TYPE_PYTHON_NOTEBOOK_TWEAK_PANEL), + }; + + *count = 1; + result = malloc(*count * sizeof(tweak_info_t)); + + memcpy(result, infos, *count * sizeof(tweak_info_t)); + + return result; + +} diff --git a/plugins/pynb/core-ui.h b/plugins/pynb/core-ui.h new file mode 100644 index 0000000..8ab9dd9 --- /dev/null +++ b/plugins/pynb/core-ui.h @@ -0,0 +1,43 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * core-ui.h - prototypes pour la présentation de notes sous forme de texte et de code Python + * + * Copyright (C) 2025 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 _PLUGINS_FIDO_CORE_UI_H +#define _PLUGINS_FIDO_CORE_UI_H + + +#include <plugins/plugin.h> +#include <plugins/plugin-int.h> + + + +#define G_TYPE_PYTHON_NOTEBOOK_PLUGIN_UI (g_python_notebook_plugin_ui_get_type()) + +DECLARE_GTYPE(GPythonNotebookPluginUI, g_python_notebook_plugin_ui, G, PYTHON_NOTEBOOK_PLUGIN_UI); + + +/* Crée un module pour présentation de notes. */ +GPluginModule *g_python_notebook_plugin_ui_new(GModule *); + + + +#endif /* _PLUGINS_FIDO_CORE_UI_H */ diff --git a/plugins/pynb/data/images/pynb-symbolic.svg b/plugins/pynb/data/images/pynb-symbolic.svg new file mode 100644 index 0000000..f8cae60 --- /dev/null +++ b/plugins/pynb/data/images/pynb-symbolic.svg @@ -0,0 +1,144 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="256" + height="256" + viewBox="0 0 67.73333 67.733333" + version="1.1" + id="svg2759" + sodipodi:docname="pynotebook.svg" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview2761" + pagecolor="#ffffff" + bordercolor="#000000" + borderopacity="0.25" + inkscape:showpageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="false" + inkscape:deskcolor="#d1d1d1" + inkscape:document-units="px" + showgrid="false" + inkscape:zoom="2.9329997" + inkscape:cx="126.83261" + inkscape:cy="128.1964" + inkscape:window-width="1920" + inkscape:window-height="1011" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="layer1" /> + <defs + id="defs2756"> + <linearGradient + id="b" + x1="28.809" + y1="28.882" + x2="45.803001" + y2="45.162998" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(12.026515,0,0,11.963752,-37.015053,-73.520624)"> + <stop + stop-color="#FFE052" + id="stop3586" /> + <stop + offset="1" + stop-color="#FFC331" + id="stop3588" /> + </linearGradient> + <linearGradient + id="a" + x1="19.075001" + y1="18.782" + x2="34.897999" + y2="34.658001" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(12.026515,0,0,11.963752,-37.015053,-73.520624)"> + <stop + stop-color="#387EB8" + id="stop3581" /> + <stop + offset="1" + stop-color="#366994" + id="stop3583" /> + </linearGradient> + </defs> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1"> + <g + id="g3524" + transform="translate(2.9562937,-0.62320885)" + sodipodi:insensitive="true" + style="display:none"> + <path + id="rect2966" + style="fill:#3a1616;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="m 49.272443,10.836031 c 0.102978,0.351405 0.158647,0.723392 0.158647,1.108976 V 57.03476 c 0,2.168007 -1.745406,3.913456 -3.913456,3.913456 H 16.30288 c -0.385538,0 -0.75709,-0.05622 -1.108459,-0.159163 0.237938,0.812336 0.730853,1.513585 1.387112,2.01175 0.65626,0.498165 1.475864,0.793246 2.367181,0.793246 h 29.214753 c 2.16805,0 3.913456,-1.745449 3.913456,-3.913456 V 14.59084 c 0,-1.782422 -1.180022,-3.278791 -2.80448,-3.754809 z" + sodipodi:nodetypes="csssscsssssc" /> + <path + id="rect2964" + style="display:inline;fill:#a44040;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="m 13.657273,5.3857002 h 29.21476 c 2.168053,0 3.913452,1.7453645 3.913452,3.9133733 V 54.388991 c 0,2.168009 -1.745399,3.913373 -3.913452,3.913373 h -29.21476 c -1.084026,0 -2.062389,-0.436341 -2.769745,-1.143683 -0.707357,-0.707342 -1.1437069,-1.685685 -1.1437069,-2.76969 V 9.2990735 c 0,-2.1680088 1.7453999,-3.9133733 3.9134519,-3.9133733 z" + sodipodi:nodetypes="ssssssssss" /> + <path + id="rect3518" + style="fill:#2d3a16;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="m 10.887528,57.158681 5.291742,4.9e-5 5.29299,5.29246 h -5.291667 z" + sodipodi:nodetypes="ccccc" /> + </g> + <g + id="g3807" + transform="matrix(0.275,0,0,0.27644269,10.179141,20.324238)" + style="display:none;stroke-width:3.62685"> + <path + d="m 76.135321,-13.223349 c -26.868437,0 -25.19164,11.6515879 -25.19164,11.6515879 l 0.03307,12.0716141 h 25.638125 v 3.621485 h -35.82789 c 0,0 -17.187994,-1.951302 -17.187994,25.161874 0,27.106562 15.005182,26.147447 15.005182,26.147447 h 8.956145 V 52.849722 c 0,0 -0.482864,-15.005182 14.767057,-15.005182 h 25.426458 c 0,0 14.287506,0.231511 14.287506,-13.807942 V 0.82271803 c 0,0 2.16958,-14.04606703 -25.906021,-14.04606703 z m -14.138672,8.1160932 c 2.549922,0 4.613672,2.0637499 4.613672,4.61367177 0,2.54992183 -2.06375,4.61367183 -4.613672,4.61367183 a 4.6070572,4.6070572 0 0 1 -4.613672,-4.61367183 c 0,-2.54992187 2.06375,-4.61367177 4.613672,-4.61367177 z" + fill="url(#a)" + id="path3577" + style="fill:url(#a);stroke-width:11.9951" /> + <path + d="m 76.895998,92.057664 c 26.868432,0 25.191642,-11.651588 25.191642,-11.651588 l -0.0331,-12.071615 h -25.6381 v -3.621484 h 35.82458 c 0,0 17.1913,1.951302 17.1913,-25.158567 0,-27.109869 -15.00518,-26.147447 -15.00518,-26.147447 H 105.471 v 12.57763 c 0,0 0.48286,15.005182 -14.76706,15.005182 H 65.277482 c 0,0 -14.287499,-0.231511 -14.287499,13.807942 v 23.21388 c 0,0 -2.169584,14.046067 25.906015,14.046067 z m 14.138671,-8.116093 a 4.6070572,4.6070572 0 0 1 -4.613671,-4.613672 c 0,-2.546615 2.06375,-4.610365 4.613671,-4.610365 2.549922,0 4.613672,2.060443 4.613672,4.610365 0,2.553229 -2.06375,4.613672 -4.613672,4.613672 z" + fill="url(#b)" + id="path3579" + style="fill:url(#b);stroke-width:11.9951" /> + </g> + <g + id="g3859" + style="display:none;fill:#000000;fill-opacity:1"> + <path + id="path3544" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="m 52.228737,10.212822 c 0.102978,0.351405 0.158647,0.723392 0.158647,1.108976 v 45.089753 c 0,2.168007 -1.745406,3.913456 -3.913456,3.913456 H 19.259174 c -0.385538,0 -0.75709,-0.05622 -1.108459,-0.159163 0.237938,0.812336 0.730853,1.513585 1.387112,2.01175 0.65626,0.498165 1.475864,0.793246 2.367181,0.793246 h 29.214753 c 2.16805,0 3.913456,-1.745449 3.913456,-3.913456 V 13.967631 c 0,-1.782422 -1.180022,-3.278791 -2.80448,-3.754809 z" + sodipodi:nodetypes="csssscsssssc" /> + <path + id="path3546" + style="display:inline;fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 16.613456,4.7624999 C 14.445408,4.7625 12.7,6.5079516 12.7,8.675956 v 45.089753 c 0,1.084003 0.436244,2.062515 1.143599,2.769857 0.707354,0.70734 1.685833,1.143599 2.769857,1.143599 h 29.214753 c 2.168049,0 3.913456,-1.745451 3.913456,-3.913456 V 8.675956 c 0,-2.1680044 -1.745407,-3.9134561 -3.913456,-3.9134561 z M 31.116446,16.66875 c 7.720775,0 7.124113,3.882967 7.124113,3.882967 v 6.417179 c 0,3.881096 -3.928959,3.817338 -3.928959,3.817338 h -6.992338 c -4.19372,0 -4.061251,4.148067 -4.061251,4.148067 v 3.477824 h -2.462898 c 0,0 -4.126363,0.264897 -4.126363,-7.2285 0,-7.495223 4.726843,-6.955647 4.726843,-6.955647 h 9.852628 v -1.000973 h -7.050215 l -0.0093,-3.337264 c 0,0 -0.461064,-3.220991 6.927742,-3.220991 z m -3.888135,2.24379 c -0.701226,0 -1.268656,0.570469 -1.268656,1.275374 a 1.2669408,1.2735872 0 0 0 1.268656,1.275375 c 0.701229,0 1.268657,-0.570467 1.268657,-1.275375 0,-0.704905 -0.567428,-1.275374 -1.268657,-1.275374 z m 14.46475,5.117 c 0.453342,-0.0018 4.079854,0.203094 4.079854,7.229016 0,7.494318 -4.727359,6.955131 -4.727359,6.955131 h -9.852112 v 1.000973 h 7.050732 l 0.0093,3.337263 c 0,0 0.461069,3.220992 -6.927742,3.220992 -7.720783,0 -7.124113,-3.882967 -7.124113,-3.882967 v -6.417179 c 0,-3.881101 3.928959,-3.817338 3.928959,-3.817338 h 6.992338 c 4.193725,0 4.060734,-4.148067 4.060734,-4.148067 v -3.47679 h 2.462898 c 0,0 0.01629,-9.15e-4 0.04651,-0.001 z M 35.21387,40.97941 c -0.701227,0 -1.269173,0.570348 -1.269173,1.27434 a 1.2669408,1.2735872 0 0 0 1.269173,1.275375 c 0.701228,0 1.268657,-0.569554 1.268657,-1.275375 0,-0.704906 -0.567429,-1.27434 -1.268657,-1.27434 z" /> + <path + id="path3548" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="m 13.843822,56.535472 5.291742,4.9e-5 5.29299,5.29246 h -5.291667 z" + sodipodi:nodetypes="ccccc" /> + </g> + <path + id="rect3934" + style="display:none;fill:#00ba2b;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 4.7624999,62.970832 20.637499,4.7624999 h 42.333333 l -15.875,58.2083321 z" + sodipodi:nodetypes="ccccc" /> + <path + id="path4924" + style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 24.551 4.7625 C 22.3829 4.7625 20.1615 6.50795 19.5702 8.67596 L 7.27299 53.7657 C 6.97735 54.8497 7.14673 55.8282 7.66117 56.5356 L 11.5111 61.8277 L 11.5147 61.8277 C 11.6056 61.9525 11.7049 62.0706 11.8167 62.1776 C 12.3371 62.6758 13.0763 62.9708 13.9676 62.9708 L 43.1824 62.9708 C 45.3504 62.9708 47.5719 61.2254 48.1631 59.0574 L 60.4603 13.9676 C 60.9465 12.1852 60.1745 10.6888 58.6799 10.2128 C 58.687 10.5642 58.6413 10.9362 58.5361 11.3218 L 46.2389 56.4115 C 45.6476 58.5795 43.4262 60.325 41.2581 60.325 L 15.7098 60.325 L 13.7851 57.6792 L 39.3339 57.6792 C 41.5019 57.6792 43.7234 55.9337 44.3147 53.7657 L 56.6119 8.67596 C 57.2031 6.50795 55.9338 4.7625 53.7657 4.7625 L 24.551 4.7625 Z M 35.8068 16.6688 C 43.5276 16.6688 41.8719 20.5517 41.8719 20.5517 L 40.1218 26.9689 C 39.0633 30.85 35.1517 30.7862 35.1517 30.7862 L 28.1594 30.7862 C 23.9657 30.7862 22.9668 34.9343 22.9668 34.9343 L 22.0183 38.4121 L 19.5554 38.4121 C 19.5554 38.4121 15.3568 38.677 17.4005 31.1836 C 19.4446 23.6884 24.0243 24.228 24.0243 24.228 L 33.877 24.228 L 34.1499 23.227 L 27.0997 23.227 L 28.0006 19.8897 C 28.0006 19.8897 28.418 16.6688 35.8068 16.6688 Z M 31.3067 18.9125 C 30.6055 18.9125 29.8825 19.483 29.6902 20.1879 C 29.5975 20.5263 29.6424 20.851 29.8152 21.0902 C 29.9879 21.3295 30.2743 21.4637 30.611 21.4633 C 31.3123 21.4633 32.0353 20.8928 32.2275 20.1879 C 32.4198 19.483 32.0079 18.9125 31.3067 18.9125 Z M 44.3759 24.0295 C 44.8297 24.0277 48.4004 24.2326 46.4842 31.2586 C 44.4403 38.7529 39.86 38.2137 39.86 38.2137 L 30.0079 38.2137 L 29.7349 39.2147 L 36.7856 39.2147 L 35.8848 42.5519 C 35.8848 42.5519 35.4674 45.7729 28.0786 45.7729 C 20.3578 45.7729 22.0135 41.8899 22.0135 41.8899 L 23.7636 35.4728 C 24.8221 31.5917 28.7336 31.6554 28.7336 31.6554 L 35.726 31.6554 C 39.9197 31.6554 40.918 27.5074 40.918 27.5074 L 41.8662 24.0306 L 44.3291 24.0306 C 44.3291 24.0306 44.3457 24.0296 44.3759 24.0295 Z M 33.274 40.9794 C 32.5728 40.9794 31.8493 41.5498 31.6573 42.2538 C 31.5645 42.5922 31.6095 42.917 31.7824 43.1563 C 31.9553 43.3956 32.2418 43.5297 32.5787 43.5291 C 33.2799 43.5291 34.0026 42.9596 34.1951 42.2538 C 34.3874 41.5488 33.9753 40.9794 33.274 40.9794 Z" /> + <path + style="display:none;fill:#af00ba;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" + d="M 4.7624999,62.970832 20.637499,4.7624999 h 42.333333 l -15.875,58.2083321 z" + id="path5158" + sodipodi:nodetypes="ccccc" /> + </g> +</svg> diff --git a/plugins/pynb/gresource.xml b/plugins/pynb/gresource.xml new file mode 100644 index 0000000..71eb8ed --- /dev/null +++ b/plugins/pynb/gresource.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<gresources> + <gresource prefix="/re/chrysalide/framework/gui/panels"> + <file compressed="true" alias="pynb-panel.ui">panel.ui</file> + <file compressed="true" alias="pynb-params.ui">params.ui</file> + <file compressed="true" alias="pynb-prefs.ui">prefs.ui</file> + </gresource> + <gresource prefix="/re/chrysalide/framework/gui/icons/scalable/actions"> + <file compressed="true" alias="pynb-symbolic.svg">data/images/pynb-symbolic.svg</file> + </gresource> +</gresources> diff --git a/plugins/pynb/panel-int.h b/plugins/pynb/panel-int.h new file mode 100644 index 0000000..23884c7 --- /dev/null +++ b/plugins/pynb/panel-int.h @@ -0,0 +1,56 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * panel-int.h - prototypes internes pour le panneau dédié à la présentation de notes + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYNB_PANEL_INT_H +#define _PLUGINS_PYNB_PANEL_INT_H + + +#include <gtkext/panel-int.h> + + +#include "panel.h" + + + +/* Panneau de cartopgraphie des dispositions d'échantillons (instance) */ +struct _GtkPythonNotebookPanel +{ + GtkTiledPanel parent; /* A laisser en premier */ + +}; + +/* Panneau de cartopgraphie des dispositions d'échantillons (classe) */ +struct _GtkPythonNotebookPanelClass +{ + GtkTiledPanelClass parent; /* A laisser en premier */ + +}; + + +/* Met en place nouvelle instance de panneau de présentation. */ +bool gtk_python_notebook_panel_create(GtkPythonNotebookPanel *); + + + +#endif /* _PLUGINS_PYNB_PANEL_INT_H */ diff --git a/plugins/pynb/panel.c b/plugins/pynb/panel.c new file mode 100644 index 0000000..8ce9cdb --- /dev/null +++ b/plugins/pynb/panel.c @@ -0,0 +1,210 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * panel.c - panneau dédié à la présentation de notes + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "panel.h" + + +#include <gtkext/helpers.h> + + +#include "panel-int.h" + + + +/* ------------------------- COEUR D'UN PANNEAU D'AFFICHAGE ------------------------- */ + + +/* Initialise la classe des panneaux pour binaires. */ +static void gtk_python_notebook_panel_class_init(GtkPythonNotebookPanelClass *); + +/* Initialise une instance de panneau pour binaire. */ +static void gtk_python_notebook_panel_init(GtkPythonNotebookPanel *); + +/* Supprime toutes les références externes. */ +static void gtk_python_notebook_panel_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_python_notebook_panel_finalize(GObject *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Fournit les composants adaptés pour la barre de titre. */ +//static GListStore *gtk_python_notebook_panel_get_title_widgets(GtkTiledPanel *, bool); + + + +/* ---------------------------------------------------------------------------------- */ +/* COEUR D'UN PANNEAU D'AFFICHAGE */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un panneau de présentation de notes. */ +G_DEFINE_TYPE(GtkPythonNotebookPanel, gtk_python_notebook_panel, GTK_TYPE_TILED_PANEL); + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* * +* Description : Initialise la classe des panneaux pour binaires. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_panel_class_init(GtkPythonNotebookPanelClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + GtkTiledPanelClass *panel; /* Version parente de classe */ + + object = G_OBJECT_CLASS(class); + + object->dispose = gtk_python_notebook_panel_dispose; + object->finalize = gtk_python_notebook_panel_finalize; + + widget = GTK_WIDGET_CLASS(class); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/panels/pynb-panel.ui"); + + //gtk_widget_class_bind_template_child(widget, GtkPythonNotebookPanel, summary); + + panel = GTK_TILED_PANEL_CLASS(class); + + //panel->get_widgets = (get_tiled_panel_widgets_cb)gtk_python_notebook_panel_get_title_widgets; + +} + + +/****************************************************************************** +* * +* Paramètres : panel = instance à initialiser. * +* * +* Description : Initialise une instance de panneau pour binaire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_panel_init(GtkPythonNotebookPanel *panel) +{ + gtk_widget_init_template(GTK_WIDGET(panel)); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_panel_dispose(GObject *object) +{ + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_PYTHON_NOTEBOOK_PANEL); + + G_OBJECT_CLASS(gtk_python_notebook_panel_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_panel_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_python_notebook_panel_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Crée une nouvelle instance de panneau de présentation. * +* * +* Retour : Composant GTK mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkTiledPanel *gtk_python_notebook_panel_new(void) +{ + GtkTiledPanel *result; /* Instance à retourner */ + + result = g_object_new(GTK_TYPE_PYTHON_NOTEBOOK_PANEL, NULL); + + if (!gtk_python_notebook_panel_create(GTK_PYTHON_NOTEBOOK_PANEL(result))) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : panel = panneau de recherche et récupération à remplir. * +* * +* Description : Met en place nouvelle instance de panneau de présentation. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool gtk_python_notebook_panel_create(GtkPythonNotebookPanel *panel) +{ + bool result; /* Bilan à retourner */ + + result = true; + + return result; + +} diff --git a/plugins/pynb/panel.h b/plugins/pynb/panel.h new file mode 100644 index 0000000..f9f8016 --- /dev/null +++ b/plugins/pynb/panel.h @@ -0,0 +1,47 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * panel.h - prototypes pour le panneau dédié à la présentation de notes + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYNB_PANEL_H +#define _PLUGINS_PYNB_PANEL_H + + +#include <gtk/gtk.h> + + +#include <glibext/helpers.h> +#include <gtkext/panel.h> + + + +#define GTK_TYPE_PYTHON_NOTEBOOK_PANEL (gtk_python_notebook_panel_get_type()) + +DECLARE_GTYPE(GtkPythonNotebookPanel, gtk_python_notebook_panel, GTK, PYTHON_NOTEBOOK_PANEL); + + +/* Crée une nouvelle instance de panneau de présentation. */ +GtkTiledPanel *gtk_python_notebook_panel_new(void); + + + +#endif /* _PLUGINS_PYNB_PANEL_H */ diff --git a/plugins/pynb/panel.ui b/plugins/pynb/panel.ui new file mode 100644 index 0000000..d16af80 --- /dev/null +++ b/plugins/pynb/panel.ui @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + + <template class="GtkPythonNotebookPanel" parent="GtkTiledPanel"> + <child> + <object class="GtkScrolledWindow"> + <property name="hscrollbar-policy">automatic</property> + <property name="vscrollbar-policy">automatic</property> + <property name="hexpand">true</property> + <property name="vexpand">true</property> + <property name="has-frame">true</property> + <property name="margin-start">8</property> + <property name="margin-top">8</property> + <property name="margin-end">8</property> + <property name="margin-bottom">8</property> + + </object> + </child> + </template> + +</interface> diff --git a/plugins/pynb/params-int.h b/plugins/pynb/params-int.h new file mode 100644 index 0000000..7f3bc8b --- /dev/null +++ b/plugins/pynb/params-int.h @@ -0,0 +1,50 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * params-int.h - définitions internes pour l'édition des paramètres initiaux d'une présentation de notes + * + * Copyright (C) 2025 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 _PLUGINS_PYNB_PARAMS_INT_H +#define _PLUGINS_PYNB_PARAMS_INT_H + + +#include "params.h" + + + +/* Composant pour les paramètres de chargement d'un binaire (instance) */ +struct _GtkPythonNotebookParameters +{ + GtkGrid parent; /* A laisser en premier */ + + //GtkEntry *filename; /* CHemin d'un binaire */ + +}; + +/* Composant pour les paramètres de chargement d'un binaire (classe) */ +struct _GtkPythonNotebookParametersClass +{ + GtkGridClass parent; /* A laisser en premier */ + +}; + + + +#endif /* _PLUGINS_PYNB_PARAMS_INT_H */ diff --git a/plugins/pynb/params.c b/plugins/pynb/params.c new file mode 100644 index 0000000..690ff95 --- /dev/null +++ b/plugins/pynb/params.c @@ -0,0 +1,171 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * params.c - édition des paramètres initiaux d'une présentation de notes + * + * Copyright (C) 2025 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/>. + */ + + +#include "params.h" + + +#include "panel.h" +#include "params-int.h" +#include <gtkext/helpers.h> +#include <gui/window.h> + + + +/* Initialise la classe des composants d'édition de paramètres. */ +static void gtk_python_notebook_parameters_class_init(GtkPythonNotebookParametersClass *); + +/* Initialise une instance de composant d'édition de paramètres. */ +static void gtk_python_notebook_parameters_init(GtkPythonNotebookParameters *); + +/* Supprime toutes les références externes. */ +static void gtk_python_notebook_parameters_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_python_notebook_parameters_finalize(GObject *); + +/* Réagit à une demande de création de notes. */ +static void gtk_python_notebook_parameters_on_create_clicked(GtkButton *, GtkPythonNotebookParameters *); + + + +/* Indique le type du composant d'édition des paramètres de chargement. */ +G_DEFINE_TYPE(GtkPythonNotebookParameters, gtk_python_notebook_parameters, GTK_TYPE_GRID); + + +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Initialise la classe des composants d'édition de paramètres. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_parameters_class_init(GtkPythonNotebookParametersClass *class) +{ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = gtk_python_notebook_parameters_dispose; + object->finalize = gtk_python_notebook_parameters_finalize; + + widget = GTK_WIDGET_CLASS(class); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/panels/pynb-params.ui"); + + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_python_notebook_parameters_on_create_clicked)); + + //gtk_widget_class_bind_template_child(widget, GtkPythonNotebookParameters, filename); + +} + + +/****************************************************************************** +* * +* Paramètres : params = composant GTK à initialiser. * +* * +* Description : Initialise une instance de composant d'édition de paramètres.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_parameters_init(GtkPythonNotebookParameters *params) +{ + gtk_widget_init_template(GTK_WIDGET(params)); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_parameters_dispose(GObject *object) +{ + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_PYTHON_NOTEBOOK_PARAMETERS); + + G_OBJECT_CLASS(gtk_python_notebook_parameters_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_parameters_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_python_notebook_parameters_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : button = bouton GTK concerné par l'appel. * +* params = paramètres du panneau à mettre en place. * +* * +* Description : Réagit à une demande de création de notes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_parameters_on_create_clicked(GtkButton *button, GtkPythonNotebookParameters *params) +{ + GtkRoot *root; /* Racine du composant */ + GtkTiledPanel *tiled; /* Panneau d'affichage complet */ + + root = gtk_widget_get_root(GTK_WIDGET(button)); + + tiled = gtk_python_notebook_panel_new(); + + gtk_framework_window_add(GTK_FRAMEWORK_WINDOW(root), tiled); + +} diff --git a/plugins/pynb/params.h b/plugins/pynb/params.h new file mode 100644 index 0000000..2d4b39b --- /dev/null +++ b/plugins/pynb/params.h @@ -0,0 +1,41 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * params.h - prototypes pour l'édition des paramètres initiaux d'une présentation de notes + * + * Copyright (C) 2025 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 _PLUGINS_PYNB_PARAMS_H +#define _PLUGINS_PYNB_PARAMS_H + + +#include <gtk/gtk.h> + + +#include <glibext/helpers.h> + + + +#define GTK_TYPE_PYTHON_NOTEBOOK_PARAMETERS (gtk_python_notebook_parameters_get_type()) + +DECLARE_GTYPE(GtkPythonNotebookParameters, gtk_python_notebook_parameters, GTK, PYTHON_NOTEBOOK_PARAMETERS); + + + +#endif /* _PLUGINS_PYNB_PARAMS_H */ diff --git a/plugins/pynb/params.ui b/plugins/pynb/params.ui new file mode 100644 index 0000000..21a4788 --- /dev/null +++ b/plugins/pynb/params.ui @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + + <template class="GtkPythonNotebookParameters" parent="GtkGrid"> + <property name="margin-bottom">12</property> + <property name="margin-end">12</property> + <property name="margin-start">12</property> + <property name="margin-top">12</property> + <property name="column-spacing">12</property> + <property name="row-spacing">8</property> + + <child> + <object class="GtkLabel"> + <property name="label">Load and analyze a new notebook file:</property> + <property name="xalign">0</property> + <layout> + <property name="column">0</property> + <property name="row">0</property> + </layout> + </object> + </child> + + <child> + <object class="GtkEntry" id="filename"> + <property name="secondary-icon-name">document-open-symbolic</property> + <property name="placeholder-text">File location</property> + <property name="hexpand">TRUE</property> + <property name="hexpand-set">TRUE</property> + <layout> + <property name="column">0</property> + <property name="row">1</property> + </layout> + <style> + <class name="background"/> + </style> + </object> + </child> + + <child> + <object class="GtkButton" id="create"> + <property name="label">Create</property> + <layout> + <property name="column">0</property> + <property name="row">2</property> + </layout> + <signal name="clicked" handler="gtk_python_notebook_parameters_on_create_clicked"/> + </object> + </child> + + </template> + +</interface> diff --git a/plugins/pynb/prefs-int.h b/plugins/pynb/prefs-int.h new file mode 100644 index 0000000..9c44c68 --- /dev/null +++ b/plugins/pynb/prefs-int.h @@ -0,0 +1,48 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * prefs-int.h - définitions internes pour la configuration des paramètres liés aux notes + * + * Copyright (C) 2025 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 _PLUGINS_PYNB_PREFS_INT_H +#define _PLUGINS_PYNB_PREFS_INT_H + + +#include "prefs.h" + + + +/* Composant d'édition des paramètres liés aux notes (instance) */ +struct _GtkPythonNotebookTweakPanel +{ + GtkBox parent; /* A laisser en premier */ + +}; + +/* Composant d'édition des paramètres liés aux notes (classe) */ +struct _GtkPythonNotebookTweakPanelClass +{ + GtkBoxClass parent; /* A laisser en premier */ + +}; + + + +#endif /* _PLUGINS_PYNB_PREFS_INT_H */ diff --git a/plugins/pynb/prefs.c b/plugins/pynb/prefs.c new file mode 100644 index 0000000..9363e9b --- /dev/null +++ b/plugins/pynb/prefs.c @@ -0,0 +1,143 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * prefs.c - configuration des paramètres liés aux notes + * + * Copyright (C) 2025 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/>. + */ + + +#include "prefs.h" + + +#include <gtkext/helpers.h> + + +#include "prefs-int.h" + + + +/* Procède à l'initialisation de classe des configurations. */ +static void gtk_python_notebook_tweak_panel_class_init(GtkPythonNotebookTweakPanelClass *); + +/* Procède à l'initialisation des configurations de sécurité. */ +static void gtk_python_notebook_tweak_panel_init(GtkPythonNotebookTweakPanel *); + +/* Supprime toutes les références externes. */ +static void gtk_python_notebook_tweak_panel_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_python_notebook_tweak_panel_finalize(GObject *); + + + +/* Indique le type du composant de configuration des notes. */ +G_DEFINE_TYPE(GtkPythonNotebookTweakPanel, gtk_python_notebook_tweak_panel, GTK_TYPE_BOX); + + +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Procède à l'initialisation de classe des configurations. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_tweak_panel_class_init(GtkPythonNotebookTweakPanelClass *class) +{ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = gtk_python_notebook_tweak_panel_dispose; + object->finalize = gtk_python_notebook_tweak_panel_finalize; + + widget = GTK_WIDGET_CLASS(class); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/panels/pynb-prefs.ui"); + + /* Stockage sécurisé */ + + //gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_python_notebook_tweak_panel_on_new_passwords_changed)); + + //gtk_widget_class_bind_template_child(widget, GtkPythonNotebookTweakPanel, current_primary_passwd); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = composant GTK à initialiser. * +* * +* Description : Procède à l'initialisation des configurations de sécurité. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_tweak_panel_init(GtkPythonNotebookTweakPanel *panel) +{ + gtk_widget_init_template(GTK_WIDGET(panel)); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_tweak_panel_dispose(GObject *object) +{ + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_PYTHON_NOTEBOOK_TWEAK_PANEL); + + G_OBJECT_CLASS(gtk_python_notebook_tweak_panel_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_python_notebook_tweak_panel_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_python_notebook_tweak_panel_parent_class)->finalize(object); + +} diff --git a/plugins/pynb/prefs.h b/plugins/pynb/prefs.h new file mode 100644 index 0000000..6551983 --- /dev/null +++ b/plugins/pynb/prefs.h @@ -0,0 +1,41 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * prefs.h - prototypes pour la configuration des paramètres liés aux notes + * + * Copyright (C) 2025 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 _PLUGINS_PYNB_PREFS_H +#define _PLUGINS_PYNB_PREFS_H + + +#include <gtk/gtk.h> + + +#include <glibext/helpers.h> + + + +#define GTK_TYPE_PYTHON_NOTEBOOK_TWEAK_PANEL (gtk_python_notebook_tweak_panel_get_type()) + +DECLARE_GTYPE(GtkPythonNotebookTweakPanel, gtk_python_notebook_tweak_panel, GTK, PYTHON_NOTEBOOK_TWEAK_PANEL); + + + +#endif /* _PLUGINS_PYNB_PREFS_H */ diff --git a/plugins/pynb/prefs.ui b/plugins/pynb/prefs.ui new file mode 100644 index 0000000..b8a6962 --- /dev/null +++ b/plugins/pynb/prefs.ui @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + + <template class="GtkPythonNotebookTweakPanel" parent="GtkBox"> + + <property name="orientation">vertical</property> + + <!-- Conservation de paramètres sécurisée --> + <child> + <object class="GtkGrid"> + <property name="margin-start">20</property> + <property name="margin-end">20</property> + <property name="margin-top">20</property> + <property name="margin-bottom">20</property> + <property name="row-spacing">10</property> + <property name="column-spacing">10</property> + + <child> + <object class="GtkLabel"> + <property name="label">Notes config</property> + <property name="use-markup">true</property> + <property name="xalign">0</property> + <layout> + <property name="column">0</property> + <property name="row">0</property> + <property name="column-span">2</property> + </layout> + <style> + <class name="heading"/> + </style> + </object> + </child> + + </object> + </child> + + </template> +</interface> diff --git a/src/arch/archbase.h b/src/arch/archbase.h index 59bf18e..b29d2a1 100644 --- a/src/arch/archbase.h +++ b/src/arch/archbase.h @@ -50,6 +50,7 @@ typedef uint64_t vmpa_t; #define VMPA_MAX_SIZE 19 +#if 0 /* Taille des données intégrées */ typedef enum _MemoryDataSize { @@ -68,6 +69,7 @@ typedef enum _MemoryDataSize MDS_64_BITS_SIGNED = 0x85 /* Opérande sur 64 bits signés */ } MemoryDataSize; +#endif #define MDS_RANGE(mds) ((mds & 0xf) - 1) @@ -99,13 +101,13 @@ typedef enum _MemoryDataSize __result; \ }) - +#if 0 #define MDS_4_BITS MDS_4_BITS_UNSIGNED #define MDS_8_BITS MDS_8_BITS_UNSIGNED #define MDS_16_BITS MDS_16_BITS_UNSIGNED #define MDS_32_BITS MDS_32_BITS_UNSIGNED #define MDS_64_BITS MDS_64_BITS_UNSIGNED - +#endif /* Etablit la comparaison entre deux adresses. */ diff --git a/src/arch/vmpa.h b/src/arch/vmpa.h index 42136e2..8a9b961 100644 --- a/src/arch/vmpa.h +++ b/src/arch/vmpa.h @@ -30,7 +30,6 @@ #include <stdint.h> -#include "archbase.h" #include "../common/cpp.h" #include "../common/datatypes.h" #include "../common/packed.h" diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 7925b66..d09b661 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -50,6 +50,7 @@ libcommon4_la_SOURCES = \ asm.h asm.c \ bits.h bits.c \ compiler.h \ + cpp.h \ datatypes.h \ dllist.h dllist.c \ entropy.h entropy.c \ @@ -57,7 +58,6 @@ libcommon4_la_SOURCES = \ extstr.h extstr.c \ fnv1a.h fnv1a.c \ io.h io.c \ - json.h json.c \ leb128.h leb128.c \ macros.h \ packed.h packed.c \ @@ -74,6 +74,13 @@ libcommon4_la_SOURCES += \ endif +if BUILD_JSONGLIB_SUPPORT + +libcommon4_la_SOURCES += \ + json.h json.c + +endif + libcommon4_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBSSL_CFLAGS) $(LIBJSONGLIB_CFLAGS) if BUILD_CURL_SUPPORT @@ -82,6 +89,12 @@ libcommon4_la_CFLAGS += $(LIBCURL_CFLAGS) endif +if BUILD_JSONGLIB_SUPPORT + +libcommon4_la_CFLAGS += $(LIBJSONGLIB_CFLAGS) + +endif + devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) diff --git a/src/common/compiler.h b/src/common/compiler.h index 2585e47..65df8a4 100644 --- a/src/common/compiler.h +++ b/src/common/compiler.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * compiler.h - prototypes pour le regroupement d'astuces à destination du compilateur * - * Copyright (C) 2024 Cyrille Bagard + * Copyright (C) 2024-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -34,5 +34,19 @@ #define __weak __attribute__((weak)) +/** + * Contournement des avertissements de la forme suivante : + * + * assignment to 'const char * const*' from incompatible pointer type 'char **' [-Wincompatible-pointer-types] + * + * Références : + * - https://www.reddit.com/r/C_Programming/comments/qa2231/const_char_const_and_char_are_incompatible/ + * - https://stackoverflow.com/questions/78125/why-cant-i-convert-char-to-a-const-char-const-in-c + * - https://c-faq.com/ansi/constmismatch.html + */ + +#define CONST_ARRAY_CAST(a, tp) (const tp **)a + + #endif /* _COMMON_COMPILER_H */ diff --git a/src/common/cpp.h b/src/common/cpp.h index 39e7676..2644281 100644 --- a/src/common/cpp.h +++ b/src/common/cpp.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * cpp.h - prototypes pour avoir à disposition un langage C plus plus mieux * - * Copyright (C) 2010-2020 Cyrille Bagard + * Copyright (C) 2010-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -31,6 +31,12 @@ /** + * Fournit la taille d'une chaîne statique. + */ +#define STATIC_STR_SIZE(s) (sizeof(s) - 1) + + +/** * Fournit la taille d'un tableau statique. */ #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) diff --git a/src/common/datatypes.h b/src/common/datatypes.h index 3983267..681e232 100644 --- a/src/common/datatypes.h +++ b/src/common/datatypes.h @@ -52,5 +52,31 @@ typedef enum _SourceEndian } SourceEndian; +/* Taille des données intégrées */ +typedef enum _MemoryDataSize +{ + MDS_UNDEFINED = 0x0, /* Taille non définie */ + + MDS_4_BITS_UNSIGNED = 0x1, /* Opérande sur 4 bits n.-s. */ + MDS_8_BITS_UNSIGNED = 0x2, /* Opérande sur 8 bits n.-s. */ + MDS_16_BITS_UNSIGNED = 0x3, /* Opérande sur 16 bits n.-s. */ + MDS_32_BITS_UNSIGNED = 0x4, /* Opérande sur 32 bits n.-s. */ + MDS_64_BITS_UNSIGNED = 0x5, /* Opérande sur 64 bits n.-s. */ + + MDS_4_BITS_SIGNED = 0x8 | 0x1, /* Opérande sur 4 bits signés */ + MDS_8_BITS_SIGNED = 0x8 | 0x2, /* Opérande sur 8 bits signés */ + MDS_16_BITS_SIGNED = 0x8 | 0x3, /* Opérande sur 16 bits signés */ + MDS_32_BITS_SIGNED = 0x8 | 0x4, /* Opérande sur 32 bits signés */ + MDS_64_BITS_SIGNED = 0x8 | 0x5 /* Opérande sur 64 bits signés */ + +} MemoryDataSize; + +#define MDS_4_BITS MDS_4_BITS_UNSIGNED +#define MDS_8_BITS MDS_8_BITS_UNSIGNED +#define MDS_16_BITS MDS_16_BITS_UNSIGNED +#define MDS_32_BITS MDS_32_BITS_UNSIGNED +#define MDS_64_BITS MDS_64_BITS_UNSIGNED + + #endif /* _COMMON_DATATYPES_H */ diff --git a/src/common/entropy.h b/src/common/entropy.h index b677a77..cfd51ef 100644 --- a/src/common/entropy.h +++ b/src/common/entropy.h @@ -26,9 +26,10 @@ #include <stdbool.h> +#include <stddef.h> -#include "../arch/archbase.h" +#include "../common/datatypes.h" diff --git a/src/common/szbin.h b/src/common/szbin.h index 5891449..23aac67 100644 --- a/src/common/szbin.h +++ b/src/common/szbin.h @@ -97,6 +97,16 @@ typedef struct _sized_binary_t while (0) +#define resize_sized_binary(sb, s) \ + do \ + { \ + (sb)->size = s; \ + (sb)->data = realloc((sb)->data, \ + (sb)->size); \ + } \ + while (0) + + #define add_to_sized_binary(sb, d, s) \ do \ { \ diff --git a/src/core/Makefile.am b/src/core/Makefile.am index e1e3c4e..17fd2bf 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -22,7 +22,7 @@ libcore4_la_SOURCES = \ nproc.h nproc.c \ paths.h paths.c -libcore4_la_CFLAGS = $(TOOLKIT_CFLAGS) +libcore4_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBSSL_CFLAGS) devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) diff --git a/src/core/core.c b/src/core/core.c index 9514ee1..8fe12f5 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -53,6 +53,8 @@ bool load_core_components(AvailableCoreComponent flags) if ((flags & ACC_GLOBAL_VARS) != 0 && (__loaded & ACC_GLOBAL_VARS) == 0) { + set_secret_storage(g_secret_storage_new(NULL)); + set_work_queue(g_work_queue_new()); __loaded |= ACC_GLOBAL_VARS; @@ -82,6 +84,8 @@ void unload_core_components(AvailableCoreComponent flags) { set_work_queue(NULL); + set_secret_storage(NULL); + __loaded &= ~ACC_GLOBAL_VARS; } diff --git a/src/core/core.h b/src/core/core.h index 7c50f6c..e5f0a60 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -32,9 +32,11 @@ /* Eléments à (dé)charger disponibles */ typedef enum _AvailableCoreComponent { - ACC_NONE = (0 << 0), /* Statut initial */ - ACC_GLOBAL_VARS = (1 << 0), /* Singletons globaux */ - ACC_SCAN_FEATURES = (1 << 0), /* Espace de noms pour scan */ + ACC_NONE = (0 << 0), /* Statut initial */ + ACC_GLOBAL_VARS = (1 << 0), /* Singletons globaux */ + ACC_SCAN_FEATURES = (1 << 1), /* Espace de noms pour scan */ + + ACC_ALL_COMPONENTS = (1 << 2) - 1 } AvailableCoreComponent; diff --git a/src/core/global.c b/src/core/global.c index d38656b..0275e09 100644 --- a/src/core/global.c +++ b/src/core/global.c @@ -36,6 +36,9 @@ static size_t _bytes_sent = 0; /* Gestionnaire de tâches parallèles */ static GWorkQueue *_queue = NULL; +/* Gardien des secrets avec support des stockages */ +static GSecretStorage *_storage = NULL; + /****************************************************************************** @@ -131,6 +134,49 @@ GWorkQueue *get_work_queue(void) } +/****************************************************************************** +* * +* Paramètres : queue = nouveau stockage sécurisé à mémoriser ou NULL. * +* * +* Description : Définit le stockage sécurisé principal. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void set_secret_storage(/* __steal */GSecretStorage *storage) +{ + if (_storage != NULL) + unref_object(_storage); + + _storage = storage; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit le stockage sécurisé principal. * +* * +* Retour : Gestionnaire de traitements parallèles courant. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GSecretStorage *get_secret_storage(void) +{ + ref_object(_storage); + + return _storage; + +} + + diff --git a/src/core/global.h b/src/core/global.h index b861ad8..f5d8a62 100644 --- a/src/core/global.h +++ b/src/core/global.h @@ -25,6 +25,7 @@ #define _CORE_GLOBAL_H +#include "../glibext/secstorage.h" #include "../glibext/workqueue.h" @@ -41,6 +42,12 @@ void set_work_queue(/* __steal */GWorkQueue *); /* Fournit le gestionnaire de traitements parallèles courant. */ GWorkQueue *get_work_queue(void); +/* Définit le stockage sécurisé principal. */ +void set_secret_storage(/* __steal */GSecretStorage *); + +/* Fournit le stockage sécurisé principal. */ +GSecretStorage *get_secret_storage(void); + diff --git a/src/format/program-int.h b/src/format/program-int.h index 1549a0a..8464fe4 100644 --- a/src/format/program-int.h +++ b/src/format/program-int.h @@ -36,7 +36,7 @@ typedef SourceEndian (* program_get_endian_fc) (const GProgramFormat *); /* Fournit l'emplacement d'une section donnée. */ -typedef bool (* get_range_by_name_fc) (const GProgramFormat *, const char *, mrange_t *); +typedef bool (* find_range_by_name_fc) (const GProgramFormat *, const char *, mrange_t *); /* Format de programme générique (instance) */ @@ -52,7 +52,7 @@ struct _GProgramFormatClass GKnownFormatClass parent; /* A laisser en premier */ program_get_endian_fc get_endian; /* Boutisme employé */ - get_range_by_name_fc get_range_by_name; /* Emplacement de sections */ + find_range_by_name_fc find_range_by_name; /* Emplacement de sections */ }; diff --git a/src/format/program.c b/src/format/program.c index d44f988..9b9df81 100644 --- a/src/format/program.c +++ b/src/format/program.c @@ -480,18 +480,18 @@ SourceEndian g_program_format_get_endianness(const GProgramFormat *format) * * ******************************************************************************/ -bool g_program_format_get_section_range_by_name(const GProgramFormat *format, const char *name, mrange_t *range) +bool g_program_format_find_section_range_by_name(const GProgramFormat *format, const char *name, mrange_t *range) { bool result; /* Bilan à retourner */ GProgramFormatClass *class; /* Classe de l'instance */ class = G_PROGRAM_FORMAT_GET_CLASS(format); - if (class->get_range_by_name == NULL) + if (class->find_range_by_name == NULL) result = false; else - result = class->get_range_by_name(format, name, range); + result = class->find_range_by_name(format, name, range); return result; diff --git a/src/format/program.h b/src/format/program.h index 0eb26ae..12b095c 100644 --- a/src/format/program.h +++ b/src/format/program.h @@ -43,7 +43,7 @@ DECLARE_GTYPE(GProgramFormat, g_program_format, G, PROGRAM_FORMAT); SourceEndian g_program_format_get_endianness(const GProgramFormat *); /* Fournit l'emplacement d'une section donnée. */ -bool g_program_format_get_section_range_by_name(const GProgramFormat *, const char *, mrange_t *); +bool g_program_format_find_section_range_by_name(const GProgramFormat *, const char *, mrange_t *); diff --git a/src/glibext/Makefile.am b/src/glibext/Makefile.am index adbec4c..81d13d2 100644 --- a/src/glibext/Makefile.am +++ b/src/glibext/Makefile.am @@ -4,8 +4,6 @@ BUILT_SOURCES = chrysamarshal.h chrysamarshal.c resources.h resources.c noinst_LTLIBRARIES = libglibext.la libglibextui.la # libglibext_la_SOURCES = \ -# comparison-int.h \ -# comparison.h comparison.c \ # configuration-int.h \ # configuration.h configuration.c \ # gbinarycursor.h gbinarycursor.c \ @@ -13,7 +11,7 @@ noinst_LTLIBRARIES = libglibext.la libglibextui.la # glinecursor.h glinecursor.c \ # gnhash.h gnhash.c \ # notifier.h \ -# objhole.h \ +# \ # proto.h \ # seq.h seq.c \ # _signal.h signal.c \ @@ -40,9 +38,21 @@ noinst_LTLIBRARIES = libglibext.la libglibextui.la libglibext_la_SOURCES = \ chrysamarshal.h chrysamarshal.c \ + comparable-int.h \ + comparable.h comparable.c \ + hashable-int.h \ + hashable.h hashable.c \ helpers.h \ + objhole-int.h \ + objhole.h objhole.c \ portion-int.h \ portion.h portion.c \ + secstorage-int.h \ + secstorage.h secstorage.c \ + singleton-int.h \ + singleton.h singleton.c \ + strbuilder-int.h \ + strbuilder.h strbuilder.c \ work-int.h \ work.h work.c \ workgroup-int.h \ diff --git a/src/glibext/comparable-int.h b/src/glibext/comparable-int.h new file mode 100644 index 0000000..18972c2 --- /dev/null +++ b/src/glibext/comparable-int.h @@ -0,0 +1,47 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable-int.h - définitions internes propres aux opérations de comparaison d'objets + * + * Copyright (C) 2022-2025 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 _GLIBEXT_COMPARABLE_INT_H +#define _GLIBEXT_COMPARABLE_INT_H + + +#include "comparable.h" + + + +/* Réalise une comparaison étendue entre objets. */ +typedef int (* compare_object_fc) (const GComparableObject *, const GComparableObject *); + + +/* Instance d'objet comparable (interface) */ +struct _GComparableObjectInterface +{ + GTypeInterface base_iface; /* A laisser en premier */ + + compare_object_fc compare; /* Comparaison étendue */ + +}; + + + +#endif /* _GLIBEXT_COMPARABLE_INT_H */ diff --git a/src/glibext/comparable.c b/src/glibext/comparable.c new file mode 100644 index 0000000..95e7de7 --- /dev/null +++ b/src/glibext/comparable.c @@ -0,0 +1,110 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable.c - opérations de comparaison d'objets + * + * Copyright (C) 2022-2025 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 Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "comparable.h" + + +#include "comparable-int.h" + + + +/* Procède à l'initialisation de l'interface de comparaison. */ +static void g_comparable_object_default_init(GComparableObjectInterface *); + + + +/* Détermine le type d'une interface pour un objet comparable. */ +G_DEFINE_INTERFACE(GComparableObject, g_comparable_object, G_TYPE_OBJECT) + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de comparaison. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_comparable_object_default_init(GComparableObjectInterface *iface) +{ + iface->compare = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : object = premier objet à consulter pour une comparaison. * +* other = second objet à consulter pour une comparaison. * +* * +* Description : Réalise une comparaison étendue entre objets. * +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int g_comparable_object_compare(const GComparableObject *object, const GComparableObject *other) +{ + int result; /* Bilan à retourner */ + GComparableObjectInterface *iface; /* Interface utilisée */ + + iface = G_COMPARABLE_OBJECT_GET_IFACE(object); + + result = iface->compare(object, other); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : object = premier objet à consulter pour une comparaison. * +* other = second objet à consulter pour une comparaison. * +* * +* Description : Détermine si deux objets sont fonctionnellement identiques. * +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +gboolean g_comparable_object_is_equal(const GComparableObject *object, const GComparableObject *other) +{ + gboolean result; /* Bilan à renvoyer */ + int ret; /* Bilan d'une comparaison */ + + ret = g_comparable_object_compare(object, other); + + result = (ret == 0); + + return result; + +} diff --git a/src/glibext/comparable.h b/src/glibext/comparable.h new file mode 100644 index 0000000..7a652ff --- /dev/null +++ b/src/glibext/comparable.h @@ -0,0 +1,46 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable.h - prototypes pour les opérations de comparaison d'objets + * + * Copyright (C) 2022-2025 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 Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_COMPARABLE_H +#define _GLIBEXT_COMPARABLE_H + + +#include "helpers.h" + + + +#define G_TYPE_COMPARABLE_OBJECT (g_comparable_object_get_type()) + +DECLARE_INTERFACE(GComparableObject, g_comparable_object, G, COMPARABLE_OBJECT); + + + +/* Réalise une comparaison étendue entre objets. */ +int g_comparable_object_compare(const GComparableObject *, const GComparableObject *); + +/* Détermine si deux objets sont fonctionnellement identiques. */ +gboolean g_comparable_object_is_equal(const GComparableObject *, const GComparableObject *); + + + +#endif /* _GLIBEXT_COMPARABLE_H */ diff --git a/src/glibext/comparison-int.h b/src/glibext/comparison-int.h deleted file mode 100644 index 446f25d..0000000 --- a/src/glibext/comparison-int.h +++ /dev/null @@ -1,58 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * comparison-int.h - définitions internes propres aux opérations de comparaison d'objets - * - * 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 _GLIBEXT_COMPARISON_INT_H -#define _GLIBEXT_COMPARISON_INT_H - - -#include "comparison.h" - - - -/* Réalise une comparaison entre objets selon un critère précis. */ -typedef bool (* compare_rich_fc) (const GComparableItem *, const GComparableItem *, RichCmpOperation, bool *); - - -/* Instance d'élément comparable (interface) */ -struct _GComparableItemIface -{ - GTypeInterface base_iface; /* A laisser en premier */ - - compare_rich_fc cmp_rich; /* Comparaison de façon précise*/ - -}; - - -/* Redéfinition */ -typedef GComparableItemIface GComparableItemInterface; - - -/* Réalise une comparaison riche entre valeurs entière. */ -bool compare_rich_integer_values_signed(long long, long long, RichCmpOperation); - -/* Réalise une comparaison riche entre valeurs entière. */ -bool compare_rich_integer_values_unsigned(unsigned long long, unsigned long long, RichCmpOperation); - - - -#endif /* _GLIBEXT_COMPARISON_INT_H */ diff --git a/src/glibext/comparison.c b/src/glibext/comparison.c deleted file mode 100644 index 8ce6941..0000000 --- a/src/glibext/comparison.c +++ /dev/null @@ -1,199 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * comparison.c - opérations de comparaison d'objets - * - * 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 Foobar. If not, see <http://www.gnu.org/licenses/>. - */ - - -#include "comparison.h" - - -#include <assert.h> - - -#include "comparison-int.h" - - - -/* Procède à l'initialisation de l'interface de comparaison. */ -static void g_comparable_item_default_init(GComparableItemInterface *); - - - -/* Détermine le type d'une interface pour un objet comparable. */ -G_DEFINE_INTERFACE(GComparableItem, g_comparable_item, G_TYPE_OBJECT) - - -/****************************************************************************** -* * -* Paramètres : iface = interface GLib à initialiser. * -* * -* Description : Procède à l'initialisation de l'interface de comparaison. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_comparable_item_default_init(GComparableItemInterface *iface) -{ - -} - - -/****************************************************************************** -* * -* Paramètres : item = premier objet à consulter pour une comparaison. * -* other = second objet à consulter pour une comparaison. * -* op = opération de comparaison à réaliser. * -* status = bilan des opérations de comparaison. [OUT] * -* * -* Description : Réalise une comparaison entre objets selon un critère précis.* -* * -* Retour : true si la comparaison a pu être effectuée, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_comparable_item_compare_rich(const GComparableItem *item, const GComparableItem *other, RichCmpOperation op, bool *status) -{ - bool result; /* Etat à retourner */ - GComparableItemIface *iface; /* Interface utilisée */ - - iface = G_COMPARABLE_ITEM_GET_IFACE(item); - - result = iface->cmp_rich(item, other, op, status); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : a = premier élément à consulter pour une comparaison. * -* b = second objet à consulter pour une comparaison. * -* op = opération de comparaison à réaliser. * -* * -* Description : Réalise une comparaison riche entre valeurs entière. * -* * -* Retour : Bilan des opérations de comparaison. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool compare_rich_integer_values_signed(long long a, long long b, RichCmpOperation op) -{ - bool result; /* Bilan à retourner */ - - switch (op) - { - case RCO_LT: - result = (a < b); - break; - - case RCO_LE: - result = (a <= b); - break; - - case RCO_EQ: - result = (a == b); - break; - - case RCO_NE: - result = (a != b); - break; - - case RCO_GT: - result = (a > b); - break; - - case RCO_GE: - result = (a >= b); - break; - - default: - assert(false); - result = false; - break; - - } - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : a = premier élément à consulter pour une comparaison. * -* b = second objet à consulter pour une comparaison. * -* op = opération de comparaison à réaliser. * -* * -* Description : Réalise une comparaison riche entre valeurs entière. * -* * -* Retour : Bilan des opérations de comparaison. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool compare_rich_integer_values_unsigned(unsigned long long a, unsigned long long b, RichCmpOperation op) -{ - bool result; /* Bilan à retourner */ - - switch (op) - { - case RCO_LT: - result = (a < b); - break; - - case RCO_LE: - result = (a <= b); - break; - - case RCO_EQ: - result = (a == b); - break; - - case RCO_NE: - result = (a != b); - break; - - case RCO_GT: - result = (a > b); - break; - - case RCO_GE: - result = (a >= b); - break; - - default: - assert(false); - result = false; - break; - - } - - return result; - -} diff --git a/src/glibext/comparison.h b/src/glibext/comparison.h deleted file mode 100644 index 8d43210..0000000 --- a/src/glibext/comparison.h +++ /dev/null @@ -1,80 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * comparison.h - prototypes pour les opérations de comparaison d'objets - * - * 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 Foobar. If not, see <http://www.gnu.org/licenses/>. - */ - - -#ifndef _GLIBEXT_COMPARISON_H -#define _GLIBEXT_COMPARISON_H - - -#include <glib-object.h> -#include <stdbool.h> - - - -#define G_TYPE_COMPARABLE_ITEM (g_comparable_item_get_type()) -#define G_COMPARABLE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_COMPARABLE_ITEM, GComparableItem)) -#define G_COMPARABLE_ITEM_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST((vtable), G_TYPE_COMPARABLE_ITEM, GComparableItemIface)) -#define G_IS_COMPARABLE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_COMPARABLE_ITEM)) -#define G_IS_COMPARABLE_ITEM_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE((vtable), G_TYPE_COMPARABLE_ITEM)) -#define G_COMPARABLE_ITEM_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE((inst), G_TYPE_COMPARABLE_ITEM, GComparableItemIface)) - - -/* Instance d'élément comparable (coquille vide) */ -typedef struct _GComparableItem GComparableItem; - -/* Instance d'élément comparable (interface) */ -typedef struct _GComparableItemIface GComparableItemIface; - - -/* Modes de comparaison */ -typedef enum _RichCmpOperation -{ - RCO_LT, /* Equivalent de '<' */ - RCO_LE, /* Equivalent de '<=' */ - RCO_EQ, /* Equivalent de '==' */ - RCO_NE, /* Equivalent de '!=' */ - RCO_GT, /* Equivalent de '>' */ - RCO_GE, /* Equivalent de '>°' */ - -} RichCmpOperation; - -/* Détermination d'un besoin de comparaison supplémentaire */ -#define STATUS_NOT_EQUAL(_s, _o) \ - ({ \ - bool __result; \ - if (_o == RCO_LE || _o == RCO_EQ || _o == RCO_GE) \ - __result = !_s; \ - else \ - __result = _s; \ - __result; \ - }) - - -/* Détermine le type d'une interface pour un objet comparable. */ -GType g_comparable_item_get_type(void) G_GNUC_CONST; - -/* Réalise une comparaison entre objets selon un critère précis. */ -bool g_comparable_item_compare_rich(const GComparableItem *, const GComparableItem *, RichCmpOperation, bool *); - - - -#endif /* _GLIBEXT_COMPARISON_H */ diff --git a/src/glibext/hashable-int.h b/src/glibext/hashable-int.h new file mode 100644 index 0000000..f8a85e1 --- /dev/null +++ b/src/glibext/hashable-int.h @@ -0,0 +1,47 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hashable-int.h - définitions internes propres aux calculs de l'empreinte d'un objet + * + * Copyright (C) 2025 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 _GLIBEXT_HASHABLE_INT_H +#define _GLIBEXT_HASHABLE_INT_H + + +#include "hashable.h" + + + +/* Calcule l'empreinte sur 32 bits d'un objet. */ +typedef guint (* hash_object_fc) (const GHashableObject *); + + +/* Instance d'objet visant à être unique (interface) */ +struct _GHashableObjectInterface +{ + GTypeInterface base_iface; /* A laisser en premier */ + + hash_object_fc hash; /* Réduction en valeur */ + +}; + + + +#endif /* _GLIBEXT_HASHABLE_INT_H */ diff --git a/src/glibext/hashable.c b/src/glibext/hashable.c new file mode 100644 index 0000000..f988a90 --- /dev/null +++ b/src/glibext/hashable.c @@ -0,0 +1,82 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hashable.c - calculs de l'empreinte d'un objet + * + * Copyright (C) 2025 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 Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "hashable.h" + + +#include "hashable-int.h" + + + +/* Procède à l'initialisation de l'interface de détermination. */ +static void g_hashable_object_default_init(GHashableObjectInterface *); + + + +/* Détermine le type d'une interface pour la réduction d'un objet à une valeur. */ +G_DEFINE_INTERFACE(GHashableObject, g_hashable_object, G_TYPE_OBJECT) + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de détermination. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_hashable_object_default_init(GHashableObjectInterface *iface) +{ + iface->hash = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : object = objet dont l'instance est à consulter. * +* * +* Description : Calcule l'empreinte sur 32 bits d'un objet. * +* * +* Retour : Valeur de représentation, unique pour l'objet ou non. * +* * +* Remarques : - * +* * +******************************************************************************/ + +guint g_hashable_object_hash(const GHashableObject *object) +{ + guint result; /* Valeur à retourner */ + GHashableObjectInterface *iface; /* Interface utilisée */ + + iface = G_HASHABLE_OBJECT_GET_IFACE(object); + + result = iface->hash(object); + + return result; + +} diff --git a/src/glibext/hashable.h b/src/glibext/hashable.h new file mode 100644 index 0000000..165c744 --- /dev/null +++ b/src/glibext/hashable.h @@ -0,0 +1,42 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hashable.h - prototypes pour les calculs de l'empreinte d'un objet + * + * Copyright (C) 2025 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 Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_HASHABLE_H +#define _GLIBEXT_HASHABLE_H + + +#include "helpers.h" + + + +#define G_TYPE_HASHABLE_OBJECT (g_hashable_object_get_type()) + +DECLARE_INTERFACE(GHashableObject, g_hashable_object, G, HASHABLE_OBJECT); + + +/* Calcule l'empreinte sur 32 bits d'un objet. */ +guint g_hashable_object_hash(const GHashableObject *); + + + +#endif /* _GLIBEXT_HASHABLE_H */ diff --git a/src/glibext/objhole-int.h b/src/glibext/objhole-int.h new file mode 100644 index 0000000..b4abf6f --- /dev/null +++ b/src/glibext/objhole-int.h @@ -0,0 +1,166 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * objhole.h - définitions internes pour ll'utilisation d'un espace inutilisé dans la structure GObject + * + * Copyright (C) 2024 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 _GLIBEXT_OBJHOLE_INT_H +#define _GLIBEXT_OBJHOLE_INT_H + + +#include "objhole.h" + + +#include "../common/cpp.h" + + + +/** + * Une structure GObject a la définition suivante : + * + * struct _GObject + * { + * GTypeInstance g_type_instance; + * volatile guint ref_count; + * GData *qdata; + * }; + * + * Chaque objet GLib alloué comporte ainsi 4 octets inutilisés : + * + * (gdb) pt /o GObject + * type = struct _GObject { + * / 0 | 8 / GTypeInstance g_type_instance; + * / 8 | 4 / guint ref_count; + * / XXX 4-byte hole / + * / 16 | 8 / GData *qdata; + * + * / total size (bytes): 24 / + * } + * + * La situation n'a pas échappé aux développeurs GLib, avec la définition réelle + * de la structure (cf. https://github.com/GNOME/glib/blob/main/gobject/gobject.c) : + * + * #if SIZEOF_INT == 4 && GLIB_SIZEOF_VOID_P == 8 + * #define HAVE_OPTIONAL_FLAGS + * #endif + * + * typedef struct + * { + * GTypeInstance g_type_instance; + * guint ref_count; + * #ifdef HAVE_OPTIONAL_FLAGS + * guint optional_flags; + * #endif + * GData *qdata; + * } GObjectReal; + * + * G_STATIC_ASSERT(sizeof(GObject) == sizeof(GObjectReal)); + * G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, ref_count) == G_STRUCT_OFFSET(GObjectReal, ref_count)); + * G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, qdata) == G_STRUCT_OFFSET(GObjectReal, qdata)); + * + * L'espace entre les deux derniers champs ne peut donc être pleinement exploité deux fois. + */ + +/** + * Les bits effectivements utilisés par GLib s'identifie ainsi (02/12/24) : + * + * $ wget -qO /dev/stdout https://raw.githubusercontent.com/GNOME/glib/refs/heads/main/gobject/gobject.c \ + * | grep ' * #define OPTIONAL_FLAG_' + * #define OPTIONAL_FLAG_IN_CONSTRUCTION (1 << 0) + * #define OPTIONAL_FLAG_HAS_SIGNAL_HANDLER (1 << 1) + * #define OPTIONAL_FLAG_HAS_NOTIFY_HANDLER (1 << 2) + * #define OPTIONAL_FLAG_LOCK (1 << 3) + * #define OPTIONAL_FLAG_EVER_HAD_WEAK_REF (1 << 4) + * + * Les n premiers bits doivent ainsi être préservés, même s'il est possible de + * partager le bit de verrouilage OPTIONAL_FLAG_LOCK. + */ + + +#ifndef HAVE_OPTIONAL_FLAGS_IN_GOBJECT + +# if SIZEOF_INT == 4 && GLIB_SIZEOF_VOID_P >= 8 +# define HAVE_OPTIONAL_FLAGS_IN_GOBJECT 1 +# else +# define HAVE_OPTIONAL_FLAGS_IN_GOBJECT 0 +# endif + +#endif + + +/* Nouvelle version dense des objets (instance) */ +typedef struct _GThickObject +{ + /** + * (cf. structure GObjectReal officielle). + */ + + GTypeInstance g_type_instance; /* Type d'objet */ + guint ref_count; /* Décompte des références */ + +#ifdef HAVE_OPTIONAL_FLAGS_IN_GOBJECT + guint extra; /* Zone partagée avec GLib */ +#endif + + GData *qdata; /* Données complémentaires ? */ + +#ifndef HAVE_OPTIONAL_FLAGS_IN_GOBJECT + guint extra; /* Zone supplémentaire propre */ +#endif + +} GThickObject; + +/* Nouvelle version dense des objets (classe) */ +struct _GThickObjectClass +{ + GObjectClass parent; /* A laisser en premier */ + +}; + + +/** + * Définition du périmètre et des moyens d'accès. + */ + +/* GLib 2.83.0 - cfa36f5e9 */ +#define GOBJECT_RESERVED_EXTRA_BITS 5 + +#define GET_GOBJECT_EXTRA(obj, tp) \ + ({ \ + BUILD_BUG_ON(sizeof(tp) > sizeof(guint)); \ + tp *___result; \ + guint __val; \ + __val = g_thick_object_get_extra(obj); \ + ___result = (tp *)(guint []){ __val }; \ + ___result; \ + }) + +#define SET_GOBJECT_EXTRA(obj, tp, data) \ + ({ \ + BUILD_BUG_ON(sizeof(tp) > sizeof(guint)); \ + BUILD_BUG_ON(sizeof(data) > sizeof(guint *)); \ + guint __val; \ + __val = *(guint *)data; \ + g_thick_object_set_extra(obj, __val); \ + }) + + + +#endif /* _GLIBEXT_OBJHOLE_INT_H */ diff --git a/src/glibext/objhole.c b/src/glibext/objhole.c new file mode 100644 index 0000000..20bb2a8 --- /dev/null +++ b/src/glibext/objhole.c @@ -0,0 +1,191 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * objhole.c - utilisation d'un espace inutilisé dans la structure GObject + * + * Copyright (C) 2024 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/>. + */ + + +#include "objhole.h" + + +#include <assert.h> + + +#include "objhole-int.h" + + + +/* Initialise la classe des objets à la structure dense. */ +static void g_thick_object_class_init(GThickObjectClass *); + +/* Initialise une instance d'objet à la structure dense. */ +static void g_thick_object_init(GThickObject *); + +/* Supprime toutes les références externes. */ +static void g_thick_object_dispose(GThickObject *); + +/* Procède à la libération totale de la mémoire. */ +static void g_thick_object_finalize(GThickObject *); + + + +G_DEFINE_TYPE(GThickObject, g_thick_object, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des objets à la structure dense. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_thick_object_class_init(GThickObjectClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_thick_object_dispose; + object->finalize = (GObjectFinalizeFunc)g_thick_object_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : obj = instance à initialiser. * +* * +* Description : Initialise une instance d'objet à la structure dense. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_thick_object_init(GThickObject *obj) +{ + obj->extra = 0; + +} + + +/****************************************************************************** +* * +* Paramètres : obj = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_thick_object_dispose(GThickObject *obj) +{ + G_OBJECT_CLASS(g_thick_object_parent_class)->dispose(G_OBJECT(obj)); + +} + + +/****************************************************************************** +* * +* Paramètres : obj = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_thick_object_finalize(GThickObject *obj) +{ + G_OBJECT_CLASS(g_thick_object_parent_class)->finalize(G_OBJECT(obj)); + +} + + +/****************************************************************************** +* * +* Paramètres : obj = instance d'objet GLib à consulter. * +* * +* Description : Fournit la valeur courante de la zone de stockage d'un objet.* +* * +* Retour : Valeur de 32 lues et expurgées des bits GLib. * +* * +* Remarques : - * +* * +******************************************************************************/ + +guint g_thick_object_get_extra(const GThickObject *obj) +{ + guint result; /* Valeur à retourner */ + guint mask; /* Masque à appliquer */ + + result = g_atomic_int_get(&obj->extra); + + assert(GOBJECT_RESERVED_EXTRA_BITS < 30); + + mask = (1 << GOBJECT_RESERVED_EXTRA_BITS) - 1; + + result &= ~mask; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : obj = instance d'objet GLib à consulter. * +* val = valeur de 32 à conserver. * +* * +* Description : Définit la valeur courante de la zone de stockage d'un objet.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_thick_object_set_extra(GThickObject *obj, guint val) +{ + guint mask; /* Masque à appliquer */ + + assert(GOBJECT_RESERVED_EXTRA_BITS < 30); + + mask = (1 << GOBJECT_RESERVED_EXTRA_BITS) - 1; + + assert((val & mask) == 0); + + val &= ~mask; + + g_atomic_int_and(&obj->extra, val | mask); + + g_atomic_int_or(&obj->extra, val); + +} diff --git a/src/glibext/objhole.h b/src/glibext/objhole.h index 38f4bd5..c1e8cf1 100644 --- a/src/glibext/objhole.h +++ b/src/glibext/objhole.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * objhole.h - prototypes pour l'utilisation d'un espace inutilisé dans la structure GObject * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,118 +25,20 @@ #define _GLIBEXT_OBJHOLE_H -#include <glib.h> -#include <glib-object.h> +#include "../glibext/helpers.h" -#include "../common/cpp.h" +#define G_TYPE_THICK_OBJECT (g_thick_object_get_type()) +DECLARE_GTYPE(GThickObject, g_thick_object, G, THICK_OBJECT); -/** - * Une structure GObject a la définition suivante : - * - * struct _GObject - * { - * GTypeInstance g_type_instance; - * volatile guint ref_count; - * GData *qdata; - * }; - * - * En revanche, le fichier "glib/gobject/gobject.c" précise la définition - * réelle de la structure selon l'environnement : - * - * #if SIZEOF_INT == 4 && GLIB_SIZEOF_VOID_P == 8 - * #define HAVE_OPTIONAL_FLAGS - * #endif - * - * typedef struct - * { - * GTypeInstance g_type_instance; - * guint ref_count; - * #ifdef HAVE_OPTIONAL_FLAGS - * guint optional_flags; - * #endif - * GData *qdata; - * } GObjectReal; - * - * G_STATIC_ASSERT(sizeof(GObject) == sizeof(GObjectReal)); - * G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, ref_count) == G_STRUCT_OFFSET(GObjectReal, ref_count)); - * G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, qdata) == G_STRUCT_OFFSET(GObjectReal, qdata)); - * - * L'espace entre les deux derniers champs ne peut donc être pleinement exploité deux fois. - */ - -#if 0 - -# define GET_GOBJECT_EXTRA(obj, tp) \ - ({ \ - BUILD_BUG_ON(sizeof(tp) > sizeof(guint)); \ - tp *___result; \ - ___result = (tp *)(((guint *)&obj->ref_count) + 1); \ - BUILD_BUG_ON((___result + 1) == (tp *)&obj->qdata); \ - ___result; \ - }) - -#endif - - -/** - * Choix du bit de verrou pour le champ "lock". - * - * Dans la structure exploitant le mot utilisé ici, ce verrou est généralement - * placé dans le bit de poids fort pour les objets qui l'utilisent. - */ - -#if __BYTE_ORDER == __LITTLE_ENDIAN - -# define HOLE_LOCK_BIT 31 - -#elif __BYTE_ORDER == __BIG_ENDIAN - -# define HOLE_LOCK_BIT 0 - -#else - -# error "Unknown byte order" - -#endif - - -/* Verrou d'accès pour une encapsulation */ -typedef struct _lockable_obj_extra_t -{ - gint lock; /* Gestion d'accès aux fanions */ - -} lockable_obj_extra_t; - - -#define INIT_GOBJECT_EXTRA_LOCK(xtr) \ - do \ - { \ - lockable_obj_extra_t *__lockable; \ - __lockable = (lockable_obj_extra_t *)xtr; \ - __lockable->lock = 0; \ - } \ - while (0) -#define LOCK_GOBJECT_EXTRA(xtr) \ - do \ - { \ - lockable_obj_extra_t *__lockable; \ - __lockable = (lockable_obj_extra_t *)xtr; \ - g_bit_lock(&__lockable->lock, HOLE_LOCK_BIT); \ - } \ - while (0) +/* Fournit la valeur courante de la zone de stockage d'un objet. */ +guint g_thick_object_get_extra(const GThickObject *); -#define UNLOCK_GOBJECT_EXTRA(xtr) \ - do \ - { \ - lockable_obj_extra_t *__lockable; \ - __lockable = (lockable_obj_extra_t *)xtr; \ - g_bit_unlock(&__lockable->lock, HOLE_LOCK_BIT); \ - } \ - while (0) +/* Définit la valeur courante de la zone de stockage d'un objet. */ +void g_thick_object_set_extra(GThickObject *, guint); diff --git a/src/glibext/secstorage-int.h b/src/glibext/secstorage-int.h new file mode 100644 index 0000000..bbac133 --- /dev/null +++ b/src/glibext/secstorage-int.h @@ -0,0 +1,60 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage-int.h - définitions internes pour la conservation sécurisée d'éléments de configuration + * + * Copyright (C) 2025 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 _GLIBEXT_SECSTORAGE_INT_H +#define _GLIBEXT_SECSTORAGE_INT_H + + +#include "secstorage.h" + + + +/* Gardien des secrets avec support des stockages (instance) */ +struct _GSecretStorage +{ + GObject parent; /* A laisser en premier */ + + GSettings *settings; /* Configuration sollicitée */ + + void *master_key; /* Clef déverrouillée */ + +}; + +/* Gardien des secrets avec support des stockages (classe) */ +struct _GSecretStorageClass +{ + GObjectClass parent; /* A laisser en premier */ + + /* Signaux */ + + void (* lock_update) (GSecretStorage *); + +}; + + +/* Met en place un gardien des secrets avec support de stockage. */ +bool g_secret_storage_create(GSecretStorage *, GSettings *); + + + +#endif /* _GLIBEXT_SECSTORAGE_INT_H */ diff --git a/src/glibext/secstorage.c b/src/glibext/secstorage.c new file mode 100644 index 0000000..b118aa6 --- /dev/null +++ b/src/glibext/secstorage.c @@ -0,0 +1,950 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage.c - conservation sécurisée d'éléments de configuration + * + * Copyright (C) 2025 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/>. + */ + + +#include "secstorage.h" + + +#include <assert.h> +#include <string.h> +#include <openssl/evp.h> +#include <openssl/rand.h> + + +#include "secstorage-int.h" +#include "../core/logs.h" + + + +/** + * Les mécanismes de hachage de mot de passe doivent être utilisés avec un sel, + * et la longueur du sel doit être d’au moins 128 bits. + * + * Cette note concerne le hachage de mots de passe et non la dérivation de secrets + * cependant. + * + * Source : https://cyber.gouv.fr/sites/default/files/2021/03/anssi-guide-selection_crypto-1.0.pdf + */ + +#define SECRET_STORAGE_SALT_SIZE (256 / 8) + + +/** + * Nombre d'itérations pour PBKDF2 : en 2023, OWASP recommande 600000 itérations + * pour PBKDF2-HMAC-SHA256 (et 210000 pour PBKDF2-HMAC-SHA512). + * + * Source : https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2 + */ + +#define PBKDF2_HMAC_SHA256_ITERATIONS (2 << 20) + + +/** + * AES 256 : clef de 256 bits, IV de 128 bits + */ + +#define SECRET_STORAGE_KEK_SIZE (256 / 8) + +#define SECRET_STORAGE_KEY_SIZE (256 / 8) + +#define SECRET_STORAGE_BLOCK_SIZE (128 / 8) + +#define SECRET_STORAGE_IV_SIZE SECRET_STORAGE_BLOCK_SIZE + + + +/* Initialise la classe des stockages de secrets. */ +static void g_secret_storage_class_init(GSecretStorageClass *); + +/* Initialise une instance de stockage de secrets. */ +static void g_secret_storage_init(GSecretStorage *); + +/* Supprime toutes les références externes. */ +static void g_secret_storage_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void g_secret_storage_finalize(GObject *); + +/* Teste un mot de passe par Déverrouillage de clef maître. */ +static bool g_secret_storage_check_primary_password(GSecretStorage *, const char *, void **); + + + +/* Indique le type défini pour un gardien des secrets avec support des stockages. */ +G_DEFINE_TYPE(GSecretStorage, g_secret_storage, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des stockages de secrets. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_secret_storage_class_init(GSecretStorageClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = g_secret_storage_dispose; + object->finalize = g_secret_storage_finalize; + + g_signal_new("lock-update", + G_TYPE_SECRET_STORAGE, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GSecretStorageClass, lock_update), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + +} + + +/****************************************************************************** +* * +* Paramètres : storage = instance à initialiser. * +* * +* Description : Initialise une instance de stockage de secrets. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_secret_storage_init(GSecretStorage *storage) +{ + storage->settings = NULL; + + storage->master_key = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_secret_storage_dispose(GObject *object) +{ + GSecretStorage *storage; /* Gestion de stockage sécurisé*/ + + storage = G_SECRET_STORAGE(object); + + g_clear_object(&storage->settings); + + G_OBJECT_CLASS(g_secret_storage_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_secret_storage_finalize(GObject *object) +{ + GSecretStorage *storage; /* Gestion de stockage sécurisé*/ + + storage = G_SECRET_STORAGE(object); + + if (storage->master_key != NULL) + free(storage->master_key); + + G_OBJECT_CLASS(g_secret_storage_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : settings = éventuel espace de configuration à utiliser. * +* * +* Description : Créé un nouveau gardien des secrets avec support de stockage.* +* * +* Retour : Gestionnaire de stockage sécurisé mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GSecretStorage *g_secret_storage_new(GSettings *settings) +{ + GSecretStorage *result; /* Instance à retourner */ + + result = g_object_new(G_TYPE_SECRET_STORAGE, NULL); + + if (!g_secret_storage_create(result, settings)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = stockage sécurisé à initialiser. * +* settings = éventuel espace de configuration à utiliser. * +* * +* Description : Met en place un gardien des secrets avec support de stockage.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_create(GSecretStorage *storage, GSettings *settings) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if (settings != NULL) + { + ref_object(settings); + storage->settings = settings; + } + else + storage->settings = g_settings_new("re.chrysalide.framework.secstorage"); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* * +* Description : Détermine si une clef de chiffrement protégée est en place. * +* * +* Retour : Bilan de l'analyse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_has_key(const GSecretStorage *storage) +{ + bool result; /* Bilan à retourner */ + GVariant *value; /* Valeur de configuration */ + gsize length; /* Taille d'une valeur donnée */ + + result = false; + + value = g_settings_get_value(storage->settings, "master"); + + g_variant_get_fixed_array(value, &length, 1); + + result = (length > SECRET_STORAGE_IV_SIZE); + + g_variant_unref(value); + + return result;; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* password = mot de passe principal à appliquer. * +* * +* Description : Définit un mot de passe pour protéger une clef maître. * +* * +* Retour : Bilan de la mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_set_password(GSecretStorage *storage, const char *passwd) +{ + bool result; /* Bilan à retourner */ + unsigned char salt[SECRET_STORAGE_SALT_SIZE]; /* Sel pour la dérivation*/ + int ret; /* Bilan à d'un appel */ + GVariant *value; /* Valeur de configuration */ + unsigned char kek[SECRET_STORAGE_KEK_SIZE]; /* Clef de protection */ + unsigned char key[SECRET_STORAGE_KEY_SIZE]; /* Clef maître */ + unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ + EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/ + unsigned char encrypted[64]; /* Partie chiffrée à conserver */ + unsigned char *iter; /* Tête d'écriture */ + int outlen; /* Taille des données utiles */ + + result = false; + + if (g_secret_storage_has_key(storage)) + goto exit; + + /* Création d'un sel pour la dérivation du mot de passe */ + + ret = RAND_bytes(salt, sizeof(salt)); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + /** + * La fonction g_variant_new_fixed_array() retourne un variant + * avec un décompte de référence flottant. + */ + + value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, + salt, SECRET_STORAGE_SALT_SIZE, sizeof(unsigned char)); + + /** + * Comme le variant à une référence flottante, la fonction + * g_settings_set_value() consomme cette référence. + * + * Il n'y a donc pas lieu d'appeler g_variant_unref(). + */ + + g_settings_set_value(storage->settings, "salt", value); + + /* Dérivation du mot de passe */ + + ret = PKCS5_PBKDF2_HMAC(passwd, strlen(passwd), + salt, SECRET_STORAGE_SALT_SIZE, + PBKDF2_HMAC_SHA256_ITERATIONS, EVP_sha256(), + SECRET_STORAGE_KEK_SIZE, kek); + + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + /* Définition de la clef maître et de son IV de chiffrement */ + + ret = RAND_bytes(key, sizeof(key)); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + ret = RAND_bytes(iv, sizeof(iv)); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + /* Chiffrement de la clef maître */ + + ctx = EVP_CIPHER_CTX_new(); + + if (ctx == NULL) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + + ret = EVP_EncryptInit_ex2(ctx, EVP_aes_256_wrap_pad(), kek, iv, NULL); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + memcpy(encrypted, iv, SECRET_STORAGE_IV_SIZE); + + iter = encrypted + SECRET_STORAGE_IV_SIZE; + + ret = EVP_EncryptUpdate(ctx, iter, &outlen, key, SECRET_STORAGE_KEY_SIZE); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + ret = EVP_EncryptFinal_ex(ctx, iter, &outlen); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + assert((iter - encrypted) < 64); + + /* Conservation de la clef protégée */ + + value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, + encrypted, iter - encrypted, sizeof(unsigned char)); + + g_settings_set_value(storage->settings, "master", value); + + g_signal_emit_by_name(storage, "lock-update"); + + result = true; + + exit_with_ctx: + + EVP_CIPHER_CTX_free(ctx); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* old = ancien mot de passe principal à vérifier. * +* new = nouveau mot de passe principal à appliquer. * +* * +* Description : Modifie le mot de passe protégeant une clef maître. * +* * +* Retour : Bilan de la mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_change_password(GSecretStorage *storage, const char *old, const char *new) +{ + bool result; /* Bilan à retourner */ + + result = false; + + if (!g_secret_storage_has_key(storage)) + goto exit; + + if (!g_secret_storage_check_primary_password(storage, old, NULL)) + goto exit; + + if (!g_secret_storage_is_locked(storage)) + g_secret_storage_lock(storage); + + g_settings_reset(storage->settings, "salt"); + g_settings_reset(storage->settings, "master"); + + result = g_secret_storage_set_password(storage, new); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* password = mot de passe principal à appliquer. * +* * +* Description : Supprime le mot de passe protégeant une clef maître. * +* * +* Retour : Bilan de la mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_remove_password(GSecretStorage *storage, const char *passwd) +{ + bool result; /* Bilan à retourner */ + + result = false; + + if (!g_secret_storage_has_key(storage)) + goto exit; + + if (!g_secret_storage_check_primary_password(storage, passwd, NULL)) + goto exit; + + if (!g_secret_storage_is_locked(storage)) + g_secret_storage_lock(storage); + + g_settings_reset(storage->settings, "salt"); + g_settings_reset(storage->settings, "master"); + + g_signal_emit_by_name(storage, "lock-update"); + + result = true; + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* * +* Description : Détermine si la clef de chiffrement maître est vérouillée. * +* * +* Retour : Bilan de la détermination. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_is_locked(const GSecretStorage *storage) +{ + bool result; /* Bilan à retourner */ + + result = (storage->master_key == NULL); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à manipuler. * +* password = mot de passe principal à utiliser. * +* master = éventuelle adresse pour un stockage de clef. [OUT]* +* * +* Description : Teste un mot de passe par Déverrouillage de clef maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_secret_storage_check_primary_password(GSecretStorage *storage, const char *passwd, void **master) +{ + bool result; /* Bilan à retourner */ + GVariant *salt_value; /* Valeur du sel configuré */ + gsize salt_length; /* Taille du sel conservé */ + gconstpointer salt; /* Données associées #1 */ + unsigned char kek[SECRET_STORAGE_KEK_SIZE]; /* Clef de protection */ + int ret; /* Bilan à d'un appel */ + GVariant *enc_value; /* Paramètres de chiffrement */ + gsize enc_length; /* Taille de ces paramètrs */ + gconstpointer encrypted; /* Données associées #2 */ + EVP_CIPHER_CTX *ctx; /* Contexte de déchiffrement */ + unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ + unsigned char key[SECRET_STORAGE_KEY_SIZE]; /* Clef maître */ + unsigned char *iter; /* Tête d'écriture */ + int outlen; /* Taille des données utiles */ + + result = false; + + if (!g_secret_storage_is_locked(storage)) + { + result = true; + goto quick_exit; + } + + /* Récupération du sel mis en place */ + + salt_value = g_settings_get_value(storage->settings, "salt"); + + salt = g_variant_get_fixed_array(salt_value, &salt_length, sizeof(bin_t)); + + if (salt_length != SECRET_STORAGE_SALT_SIZE) + goto exit_with_salt; + + /* Dérivation du mot de passe */ + + ret = PKCS5_PBKDF2_HMAC(passwd, strlen(passwd), + salt, SECRET_STORAGE_SALT_SIZE, + PBKDF2_HMAC_SHA256_ITERATIONS, EVP_sha256(), + SECRET_STORAGE_KEK_SIZE, kek); + + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_salt; + } + + /* Récupération des paramètres chiffrés */ + + enc_value = g_settings_get_value(storage->settings, "master"); + + encrypted = g_variant_get_fixed_array(enc_value, &enc_length, sizeof(bin_t)); + + if (enc_length <= SECRET_STORAGE_IV_SIZE) + goto exit_with_enc; + + /* Déhiffrement de la clef maître */ + + ctx = EVP_CIPHER_CTX_new(); + + if (ctx == NULL) + { + LOG_ERROR_OPENSSL; + goto exit_with_enc; + } + + EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + + memcpy(iv, encrypted, SECRET_STORAGE_IV_SIZE); + + ret = EVP_DecryptInit_ex2(ctx, EVP_aes_256_wrap_pad(), kek, iv, NULL); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter = key; + + ret = EVP_DecryptUpdate(ctx, iter, &outlen, + ((unsigned char *)encrypted) + SECRET_STORAGE_IV_SIZE, + enc_length - SECRET_STORAGE_IV_SIZE); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + ret = EVP_DecryptFinal_ex(ctx, iter, &outlen); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + assert((iter - key) == SECRET_STORAGE_KEY_SIZE); + + /* Stockage de la clef maître en mémoire */ + + if (master != NULL) + { + *master = malloc(SECRET_STORAGE_KEY_SIZE); + memcpy(*master, key, SECRET_STORAGE_KEY_SIZE); + } + + result = true; + + /* Sortie */ + + exit_with_ctx: + + EVP_CIPHER_CTX_free(ctx); + + exit_with_enc: + + g_variant_unref(enc_value); + + exit_with_salt: + + g_variant_unref(salt_value); + + quick_exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à manipuler. * +* password = mot de passe principal à utiliser. * +* * +* Description : Déverrouille la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_unlock(GSecretStorage *storage, const char *passwd) +{ + bool result; /* Bilan à retourner */ + + result = g_secret_storage_check_primary_password(storage, passwd, &storage->master_key); + + if (result) + g_signal_emit_by_name(storage, "lock-update"); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à manipuler. * +* * +* Description : Verrouille la clef de chiffrement maître. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_secret_storage_lock(GSecretStorage *storage) +{ + if (storage->master_key != NULL) + { + free(storage->master_key); + storage->master_key = NULL; + + g_signal_emit_by_name(storage, "lock-update"); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* in = séquence d'octets à traiter. * +* out = séquence d'octets résultantes. [OUT] * +* * +* Description : Chiffre des données avec la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_encrypt_data(const GSecretStorage *storage, const sized_binary_t *in, sized_binary_t *out) +{ + bool result; /* Bilan à retourner */ + unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ + int ret; /* Bilan à d'un appel */ + EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/ + size_t needed; /* Taille de la sortie */ + unsigned char *iter; /* Tête d'écriture */ + int outlen; /* Taille des données utiles */ + + result = false; + + if (g_secret_storage_is_locked(storage)) + goto quick_exit; + + /* Récupération de la clef maître et d'un IV de chiffrement */ + + ret = RAND_bytes(iv, sizeof(iv)); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + /* Préparation de la zone de réception */ + + needed = SECRET_STORAGE_IV_SIZE + ((in->size / SECRET_STORAGE_BLOCK_SIZE) + 1) * SECRET_STORAGE_BLOCK_SIZE; + + setup_sized_binary(out, needed); + + /* Chiffrement des données */ + + ctx = EVP_CIPHER_CTX_new(); + + if (ctx == NULL) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + ret = EVP_EncryptInit_ex2(ctx, EVP_aes_256_cbc(), storage->master_key, iv, NULL); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + memcpy(out->data, iv, SECRET_STORAGE_IV_SIZE); + + iter = out->bin_data + SECRET_STORAGE_IV_SIZE; + + ret = EVP_EncryptUpdate(ctx, iter, &outlen, in->bin_data, in->size); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + ret = EVP_EncryptFinal_ex(ctx, iter, &outlen); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + assert((iter - out->bin_data) == out->size); + + result = true; + + /* Sortie */ + + exit_with_ctx: + + EVP_CIPHER_CTX_free(ctx); + + if (!result) + exit_sized_binary(out); + + exit: + quick_exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* in = séquence d'octets à traiter. * +* out = séquence d'octets résultantes. [OUT] * +* * +* Description : Déchiffre des données avec la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_decrypt_data(const GSecretStorage *storage, const sized_binary_t *in, sized_binary_t *out) +{ + bool result; /* Bilan à retourner */ + unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ + int ret; /* Bilan à d'un appel */ + EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/ + size_t needed; /* Taille de la sortie */ + unsigned char *iter; /* Tête d'écriture */ + int outlen; /* Taille des données utiles */ + + result = false; + + if (g_secret_storage_is_locked(storage)) + goto quick_exit; + + /* Récupération d'un IV de déchiffrement */ + + if (in->size < SECRET_STORAGE_IV_SIZE) + goto exit; + + memcpy(iv, in->data, SECRET_STORAGE_IV_SIZE); + + /* Préparation de la zone de réception */ + + needed = in->size - SECRET_STORAGE_IV_SIZE; + + setup_sized_binary(out, needed); + + /* Chiffrement des données */ + + ctx = EVP_CIPHER_CTX_new(); + + if (ctx == NULL) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + ret = EVP_DecryptInit_ex2(ctx, EVP_aes_256_cbc(), storage->master_key, iv, NULL); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter = out->bin_data; + + ret = EVP_DecryptUpdate(ctx, iter, &outlen, + in->bin_data + SECRET_STORAGE_IV_SIZE, in->size - SECRET_STORAGE_IV_SIZE); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + ret = EVP_DecryptFinal_ex(ctx, iter, &outlen); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + assert((iter - out->bin_data) <= out->size); + + resize_sized_binary(out, iter - out->bin_data); + + result = true; + + /* Sortie */ + + exit_with_ctx: + + EVP_CIPHER_CTX_free(ctx); + + if (!result) + exit_sized_binary(out); + + exit: + quick_exit: + + return result; + +} diff --git a/src/glibext/secstorage.h b/src/glibext/secstorage.h new file mode 100644 index 0000000..ed3f79c --- /dev/null +++ b/src/glibext/secstorage.h @@ -0,0 +1,74 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage.h - prototypes pour la conservation sécurisée d'éléments de configuration + * + * Copyright (C) 2025 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 _GLIBEXT_SECSTORAGE_H +#define _GLIBEXT_SECSTORAGE_H + + +#include <stdbool.h> +#include <gio/gio.h> + + +#include "helpers.h" +#include "../common/szbin.h" + + + +#define G_TYPE_SECRET_STORAGE (g_secret_storage_get_type()) + +DECLARE_GTYPE(GSecretStorage, g_secret_storage, G, SECRET_STORAGE); + + +/* Créé un nouveau gardien des secrets avec support de stockage. */ +GSecretStorage *g_secret_storage_new(GSettings *); + +/* Détermine si une clef de chiffrement protégée est en place. */ +bool g_secret_storage_has_key(const GSecretStorage *); + +/* Définit un mot de passe pour protéger une clef maître. */ +bool g_secret_storage_set_password(GSecretStorage *, const char *); + +/* Modifie le mot de passe protégeant une clef maître. */ +bool g_secret_storage_change_password(GSecretStorage *, const char *, const char *); + +/* Supprime le mot de passe protégeant une clef maître. */ +bool g_secret_storage_remove_password(GSecretStorage *, const char *); + +/* Détermine si la clef de chiffrement maître est vérouillée. */ +bool g_secret_storage_is_locked(const GSecretStorage *); + +/* Déverrouille la clef de chiffrement maître. */ +bool g_secret_storage_unlock(GSecretStorage *, const char *); + +/* Verrouille la clef de chiffrement maître. */ +void g_secret_storage_lock(GSecretStorage *); + +/* Chiffre des données avec la clef de chiffrement maître. */ +bool g_secret_storage_encrypt_data(const GSecretStorage *, const sized_binary_t *, sized_binary_t *); + +/* Déchiffre des données avec la clef de chiffrement maître. */ +bool g_secret_storage_decrypt_data(const GSecretStorage *, const sized_binary_t *, sized_binary_t *); + + + +#endif /* _GLIBEXT_SECSTORAGE_H */ diff --git a/src/glibext/singleton-int.h b/src/glibext/singleton-int.h index 3db17f9..747e64a 100644 --- a/src/glibext/singleton-int.h +++ b/src/glibext/singleton-int.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * singleton-int.h - définitions internes propres aux réductions du nombre d'instances d'un même type * - * Copyright (C) 2021 Cyrille Bagard + * Copyright (C) 2021-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -29,44 +29,61 @@ +/* ------------------ INTERFACE POUR CANDIDAT A UNE CENTRALISATION ------------------ */ + + /* Fournit une liste de candidats embarqués par un candidat. */ typedef GSingletonCandidate ** (* list_inner_instances_fc) (const GSingletonCandidate *, size_t *); /* Met à jour une liste de candidats embarqués par un candidat. */ typedef void (* update_inner_instances_fc) (GSingletonCandidate *, GSingletonCandidate **, size_t); -/* Fournit l'empreinte d'un candidat à une centralisation. */ -typedef guint (* hash_candidate_fc) (const GSingletonCandidate *); - -/* Détermine si deux candidats à l'unicité sont identiques. */ -typedef gboolean (* is_candidate_equal_fc) (const GSingletonCandidate *, const GSingletonCandidate *); - /* Marque un candidat comme figé. */ -typedef void (* set_candidate_ro_fc) (GSingletonCandidate *); +typedef void (* mark_candidate_as_ro_fc) (GSingletonCandidate *); /* Indique si le candidat est figé. */ typedef bool (* is_candidate_ro_fc) (const GSingletonCandidate *); +/* Crée une copie modifiable d'un object unique. */ +typedef GSingletonCandidate * (* dup_candidate_fc) (const GSingletonCandidate *); + /* Instance d'objet visant à être unique (interface) */ -struct _GSingletonCandidateIface +struct _GSingletonCandidateInterface { GTypeInterface base_iface; /* A laisser en premier */ list_inner_instances_fc list_inner; /* Récupération d'internes */ update_inner_instances_fc update_inner; /* Mise à jour des éléments */ - hash_candidate_fc hash; /* Prise d'empreinte */ - is_candidate_equal_fc is_equal; /* Comparaison */ - - set_candidate_ro_fc set_ro; /* Bascule en mode figé */ + mark_candidate_as_ro_fc mark_as_ro; /* Bascule en mode figé */ is_candidate_ro_fc is_ro; /* Consultation de l'état */ + dup_candidate_fc dup; /* Création de copie modifiable*/ + +}; + + + +/* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */ + + +/* Définition d'un compacteur d'instances de types (instance) */ +struct _GSingletonFactory +{ + GObject parent; /* A laisser en premier */ + + GHashTable *table; /* Suivi des conservations */ + GMutex access; /* Verrou pour la concurrence */ + }; +/* Définition d'un compacteur d'instances de types (classe) */ +struct _GSingletonFactoryClass +{ + GObjectClass parent; /* A laisser en premier */ -/* Redéfinition */ -typedef GSingletonCandidateIface GSingletonCandidateInterface; +}; diff --git a/src/glibext/singleton.c b/src/glibext/singleton.c index 78a3ad4..0a50d19 100644 --- a/src/glibext/singleton.c +++ b/src/glibext/singleton.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * singleton.c - réduction du nombre d'instances d'un même type * - * Copyright (C) 2021 Cyrille Bagard + * Copyright (C) 2021-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,52 +25,35 @@ #include <assert.h> +#include <malloc.h> +#include "comparable.h" +#include "hashable.h" #include "singleton-int.h" /* ------------------ INTERFACE POUR CANDIDAT A UNE CENTRALISATION ------------------ */ + /* Procède à l'initialisation de l'interface de rassemblement. */ static void g_singleton_candidate_default_init(GSingletonCandidateInterface *); +/* Fournit une liste de candidats embarqués par un candidat. */ +static GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingletonCandidate *, size_t *); + /* Met à jour une liste de candidats embarqués par un candidat. */ static void g_singleton_candidate_update_inner_instances(GSingletonCandidate *, GSingletonCandidate **, size_t); -/* Fournit l'empreinte d'un candidat à une centralisation. */ -static guint _g_singleton_candidate_hash(GSingletonCandidate *, GList **); - -/* Détermine si deux candidats à l'unicité sont identiques. */ -static gboolean _g_singleton_candidate_is_equal(GSingletonCandidate *, GSingletonCandidate *, GList **); - /* Marque un candidat comme figé. */ -static void _g_singleton_candidate_set_read_only(GSingletonCandidate *, GList **); +static void g_singleton_candidate_mark_as_read_only(GSingletonCandidate *); /* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */ -/* Définition d'un compacteur d'instances de types (instance) */ -struct _GSingletonFactory -{ - GObject parent; /* A laisser en premier */ - - GHashTable *table; /* Suivi des conservations */ - GMutex access; /* Verrou pour la concurrence */ - -}; - -/* Définition d'un compacteur d'instances de types (classe) */ -struct _GSingletonFactoryClass -{ - GObjectClass parent; /* A laisser en premier */ - -}; - - /* Initialise la classe des compacteurs d'instances de types. */ static void g_singleton_factory_class_init(GSingletonFactoryClass *); @@ -90,8 +73,10 @@ static void g_singleton_factory_finalize(GSingletonFactory *); /* ---------------------------------------------------------------------------------- */ -/* Détermine le type d'une interface pour la lecture de binaire. */ -G_DEFINE_INTERFACE(GSingletonCandidate, g_singleton_candidate, G_TYPE_OBJECT) +/* Détermine le type d'une interface pour la constitution d'objets uniques. */ +G_DEFINE_INTERFACE_WITH_CODE(GSingletonCandidate, g_singleton_candidate, G_TYPE_OBJECT, + g_type_interface_add_prerequisite(g_define_type_id, G_TYPE_HASHABLE_OBJECT); + g_type_interface_add_prerequisite(g_define_type_id, G_TYPE_COMPARABLE_OBJECT)) /****************************************************************************** @@ -108,6 +93,13 @@ G_DEFINE_INTERFACE(GSingletonCandidate, g_singleton_candidate, G_TYPE_OBJECT) static void g_singleton_candidate_default_init(GSingletonCandidateInterface *iface) { + iface->list_inner = NULL; + iface->update_inner = NULL; + + iface->mark_as_ro = NULL; + iface->is_ro = NULL; + + iface->dup = NULL; } @@ -128,14 +120,17 @@ static void g_singleton_candidate_default_init(GSingletonCandidateInterface *ifa GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingletonCandidate *candidate, size_t *count) { GSingletonCandidate **result; /* Instances à retourner */ - GSingletonCandidateIface *iface; /* Interface utilisée */ + GSingletonCandidateInterface *iface; /* Interface utilisée */ iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); if (iface->list_inner == NULL) { + assert(iface->update_inner == NULL); + *count = 0; result = NULL; + } else @@ -162,7 +157,7 @@ GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingleto static void g_singleton_candidate_update_inner_instances(GSingletonCandidate *candidate, GSingletonCandidate **instances, size_t count) { - GSingletonCandidateIface *iface; /* Interface utilisée */ + GSingletonCandidateInterface *iface; /* Interface utilisée */ iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); @@ -181,82 +176,36 @@ static void g_singleton_candidate_update_inner_instances(GSingletonCandidate *ca /****************************************************************************** * * * Paramètres : candidate = objet dont l'instance se veut unique. * -* processed = liste de candidats déjà traités. * * * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * +* Description : Marque un candidat comme figé. * * * -* Retour : Empreinte de l'élément représenté. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static guint _g_singleton_candidate_hash(GSingletonCandidate *candidate, GList **processed) +static void g_singleton_candidate_mark_as_read_only(GSingletonCandidate *candidate) { - guint result; /* Valeur à retourner */ - GList *skip; /* Détection de boucle */ - GSingletonCandidateIface *iface; /* Interface utilisée */ + GSingletonCandidateInterface *iface; /* Interface utilisée */ GSingletonCandidate **children; /* Instances internes */ size_t count; /* Quantité de ces instances */ size_t i; /* Boucle de parcours */ - skip = g_list_find(*processed, candidate); - - if (skip != NULL) - result = 0; - - else - { - iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); - - result = iface->hash(candidate); - - *processed = g_list_append(*processed, candidate); - - children = g_singleton_candidate_list_inner_instances(candidate, &count); + iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); - for (i = 0; i < count; i++) - { - result ^= _g_singleton_candidate_hash(children[i], processed); - g_object_unref(G_OBJECT(children[i])); - } + iface->mark_as_ro(candidate); - if (children != NULL) - free(children); + children = g_singleton_candidate_list_inner_instances(candidate, &count); + for (i = 0; i < count; i++) + { + g_singleton_candidate_mark_as_read_only(children[i]); + unref_object(G_OBJECT(children[i])); } - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : candidate = objet dont l'instance se veut unique. * -* * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * -* * -* Retour : Empreinte de l'élément représenté. * -* * -* Remarques : - * -* * -******************************************************************************/ - -guint g_singleton_candidate_hash(GSingletonCandidate *candidate) -{ - guint result; /* Valeur à retourner */ - GList *processed; /* Suivi des traitements */ - - processed = NULL; - - result = _g_singleton_candidate_hash(candidate, &processed); - - assert(processed != NULL); - - g_list_free(processed); - - return result; + if (children != NULL) + free(children); } @@ -264,116 +213,43 @@ guint g_singleton_candidate_hash(GSingletonCandidate *candidate) /****************************************************************************** * * * Paramètres : candidate = objet dont l'instance se veut unique. * -* other = second élément à analyser. * -* processed = liste de candidats déjà traités. * * * -* Description : Détermine si deux candidats à l'unicité sont identiques. * +* Description : Indique si le candidat est figé. * * * -* Retour : Bilan de la comparaison. * +* Retour : true si le contenu du candidat ne peut plus être modifié. * * * * Remarques : - * * * ******************************************************************************/ -static gboolean _g_singleton_candidate_is_equal(GSingletonCandidate *candidate, GSingletonCandidate *other, GList **processed) +bool g_singleton_candidate_is_read_only(const GSingletonCandidate *candidate) { - gboolean result; /* Bilan à renvoyer */ - GList *skip; /* Détection de boucle */ - GSingletonCandidateIface *iface; /* Interface utilisée */ - GSingletonCandidate **children[2]; /* Instances internes */ - size_t count[2]; /* Quantité de ces instances */ + bool result; /* Etat à retourner */ + GSingletonCandidateInterface *iface; /* Interface utilisée */ +#ifndef NDEBUG + GSingletonCandidate **children; /* Instances internes */ + size_t count; /* Quantité de ces instances */ size_t i; /* Boucle de parcours */ +#endif - skip = g_list_find(processed[0], candidate); - - if (skip != NULL) - result = (g_list_find(processed[1], other) != NULL); - - else - { - iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); - - result = iface->is_equal(candidate, other); - - processed[0] = g_list_append(processed[0], candidate); - processed[1] = g_list_append(processed[1], other); - - if (!result) - goto done; - - children[0] = g_singleton_candidate_list_inner_instances(candidate, &count[0]); - children[1] = g_singleton_candidate_list_inner_instances(other, &count[1]); - - if (count[0] != count[1]) - { - for (i = 0; i < count[0]; i++) - g_object_unref(G_OBJECT(children[0][i])); - - for (i = 0; i < count[1]; i++) - g_object_unref(G_OBJECT(children[1][i])); - - } - - else - { - for (i = 0; i < count[0] && result; i++) - { - result = _g_singleton_candidate_is_equal(children[0][i], children[1][i], processed); - g_object_unref(G_OBJECT(children[0][i])); - g_object_unref(G_OBJECT(children[1][i])); - } - - for (; i < count[0]; i++) - { - g_object_unref(G_OBJECT(children[0][i])); - g_object_unref(G_OBJECT(children[1][i])); - } + iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); - if (children[0] != NULL) - free(children[0]); + result = iface->is_ro(candidate); - if (children[1] != NULL) - free(children[1]); +#ifndef NDEBUG - } + children = g_singleton_candidate_list_inner_instances(candidate, &count); + for (i = 0; i < count; i++) + { + assert(result == g_singleton_candidate_is_read_only(children[i])); + unref_object(G_OBJECT(children[i])); } - done: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : candidate = objet dont l'instance se veut unique. * -* other = second élément à analyser. * -* * -* Description : Détermine si deux candidats à l'unicité sont identiques. * -* * -* Retour : Bilan de la comparaison. * -* * -* Remarques : - * -* * -******************************************************************************/ - -gboolean g_singleton_candidate_is_equal(GSingletonCandidate *candidate, GSingletonCandidate *other) -{ - gboolean result; /* Bilan à renvoyer */ - GList *processed[2]; /* Suivi des traitements */ + if (children != NULL) + free(children); - processed[0] = NULL; - processed[1] = NULL; - - result = _g_singleton_candidate_is_equal(candidate, other, processed); - - assert(processed[0] != NULL); - assert(processed[1] != NULL); - - g_list_free(processed[0]); - g_list_free(processed[1]); +#endif return result; @@ -383,97 +259,60 @@ gboolean g_singleton_candidate_is_equal(GSingletonCandidate *candidate, GSinglet /****************************************************************************** * * * Paramètres : candidate = objet dont l'instance se veut unique. * -* processed = liste de candidats déjà traités. * * * -* Description : Marque un candidat comme figé. * +* Description : Crée une copie modifiable d'un object unique. * * * -* Retour : - * +* Retour : Nouvelle instance mise en place. * * * * Remarques : - * * * ******************************************************************************/ -static void _g_singleton_candidate_set_read_only(GSingletonCandidate *candidate, GList **processed) +GSingletonCandidate *g_singleton_candidate_dup(const GSingletonCandidate *candidate) { - GList *skip; /* Détection de boucle */ - GSingletonCandidateIface *iface; /* Interface utilisée */ - GSingletonCandidate **children; /* Instances internes */ - size_t count; /* Quantité de ces instances */ + GSingletonCandidate *result; /* Instance à retourner */ + GSingletonCandidateInterface *iface; /* Interface utilisée */ + size_t count; /* Quantité d'objets internes */ + GSingletonCandidate **children; /* Liste d'instances internes */ size_t i; /* Boucle de parcours */ + GSingletonCandidate **new_children; /* Nouvelle liste d'instances */ - skip = g_list_find(*processed, candidate); + iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); - if (skip == NULL) - { - iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); + result = iface->dup(candidate); - iface->set_ro(candidate); + assert(!g_singleton_candidate_is_read_only(result)); - *processed = g_list_append(*processed, candidate); + children = g_singleton_candidate_list_inner_instances(candidate, &count); - children = g_singleton_candidate_list_inner_instances(candidate, &count); + if (count > 0) + { + new_children = malloc(count * sizeof(GSingletonCandidate *)); for (i = 0; i < count; i++) { - _g_singleton_candidate_set_read_only(candidate, processed); - g_object_unref(G_OBJECT(children[i])); - } - - if (children != NULL) - free(children); - - } - -} + new_children[i] = g_singleton_candidate_dup(children[i]); + assert(!g_singleton_candidate_is_read_only(new_children[i])); -/****************************************************************************** -* * -* Paramètres : candidate = objet dont l'instance se veut unique. * -* * -* Description : Marque un candidat comme figé. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_singleton_candidate_set_read_only(GSingletonCandidate *candidate) -{ - GList *processed; /* Suivi des traitements */ - - processed = NULL; - - _g_singleton_candidate_set_read_only(candidate, &processed); - - assert(processed != NULL); + } - g_list_free(processed); - -} + g_singleton_candidate_update_inner_instances(result, new_children, count); + for (i = 0; i < count; i++) + { + unref_object(G_OBJECT(new_children[i])); + unref_object(G_OBJECT(children[i])); + } -/****************************************************************************** -* * -* Paramètres : candidate = objet dont l'instance se veut unique. * -* * -* Description : Indique si le candidat est figé. * -* * -* Retour : true si le contenu du candidat ne peut plus être modifié. * -* * -* Remarques : - * -* * -******************************************************************************/ + free(new_children); -bool g_singleton_candidate_is_read_only(const GSingletonCandidate *candidate) -{ - bool result; /* Etat à retourner */ - GSingletonCandidateIface *iface; /* Interface utilisée */ + } - iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); + if (children != NULL) + free(children); - result = iface->is_ro(candidate); + assert(G_OBJECT_TYPE(result) == G_OBJECT_TYPE(candidate)); return result; @@ -528,8 +367,8 @@ static void g_singleton_factory_class_init(GSingletonFactoryClass *klass) static void g_singleton_factory_init(GSingletonFactory *factory) { - factory->table = g_hash_table_new_full((GHashFunc)g_singleton_candidate_hash, - (GEqualFunc)g_singleton_candidate_is_equal, + factory->table = g_hash_table_new_full((GHashFunc)g_hashable_object_hash, + (GEqualFunc)g_comparable_object_is_equal, g_object_unref, NULL); g_mutex_init(&factory->access); @@ -688,7 +527,7 @@ GSingletonCandidate *g_singleton_factory_get_instance(GSingletonFactory *factory g_hash_table_add(factory->table, candidate); #endif - g_singleton_candidate_set_read_only(candidate); + g_singleton_candidate_mark_as_read_only(candidate); result = candidate; diff --git a/src/glibext/singleton.h b/src/glibext/singleton.h index 629687a..11afffd 100644 --- a/src/glibext/singleton.h +++ b/src/glibext/singleton.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * singleton.h - prototypes pour la réduction du nombre d'instances d'un même type * - * Copyright (C) 2021 Cyrille Bagard + * Copyright (C) 2021-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,71 +25,36 @@ #define _GLIBEXT_SINGLETON_H -#include <glib-object.h> #include <stdbool.h> - -/* Définition d'un compacteur d'instances de types (instance) */ -typedef struct _GSingletonFactory GSingletonFactory; +#include "helpers.h" /* ------------------ INTERFACE POUR CANDIDAT A UNE CENTRALISATION ------------------ */ -#define G_TYPE_SINGLETON_CANDIDATE (g_singleton_candidate_get_type()) -#define G_SINGLETON_CANDIDATE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_SINGLETON_CANDIDATE, GSingletonCandidate)) -#define G_SINGLETON_CANDIDATE_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST((vtable), G_TYPE_SINGLETON_CANDIDATE, GSingletonCandidateIface)) -#define G_IS_SINGLETON_CANDIDATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_SINGLETON_CANDIDATE)) -#define G_IS_SINGLETON_CANDIDATE_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE((vtable), G_TYPE_SINGLETON_CANDIDATE)) -#define G_SINGLETON_CANDIDATE_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE((inst), G_TYPE_SINGLETON_CANDIDATE, GSingletonCandidateIface)) - - -/* Instance d'objet visant à être unique (coquille vide) */ -typedef struct _GSingletonCandidate GSingletonCandidate; - -/* Instance d'objet visant à être unique (interface) */ -typedef struct _GSingletonCandidateIface GSingletonCandidateIface; - - -/* Détermine le type d'une interface pour la lecture de binaire. */ -GType g_singleton_candidate_get_type(void) G_GNUC_CONST; +#define G_TYPE_SINGLETON_CANDIDATE (g_singleton_candidate_get_type()) -/* Fournit une liste de candidats embarqués par un candidat. */ -GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingletonCandidate *, size_t *); +DECLARE_INTERFACE(GSingletonCandidate, g_singleton_candidate, G, SINGLETON_CANDIDATE); -/* Fournit l'empreinte d'un candidat à une centralisation. */ -guint g_singleton_candidate_hash(GSingletonCandidate *); - -/* Détermine si deux candidats à l'unicité sont identiques. */ -gboolean g_singleton_candidate_is_equal(GSingletonCandidate *, GSingletonCandidate *); - -/* Marque un candidat comme figé. */ -void g_singleton_candidate_set_read_only(GSingletonCandidate *); /* Indique si le candidat est figé. */ bool g_singleton_candidate_is_read_only(const GSingletonCandidate *); +/* Crée une copie modifiable d'un object unique. */ +GSingletonCandidate *g_singleton_candidate_dup(const GSingletonCandidate *); -/* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */ - -#define G_TYPE_SINGLETON_FACTORY g_singleton_factory_get_type() -#define G_SINGLETON_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_SINGLETON_FACTORY, GSingletonFactory)) -#define G_IS_SINGLETON_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_SINGLETON_FACTORY)) -#define G_SINGLETON_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_SINGLETON_FACTORY, GSingletonFactoryClass)) -#define G_IS_SINGLETON_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_SINGLETON_FACTORY)) -#define G_SINGLETON_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_SINGLETON_FACTORY, GSingletonFactoryClass)) +/* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */ -/* Définition d'un compacteur d'instances de types (classe) */ -typedef struct _GSingletonFactoryClass GSingletonFactoryClass; +#define G_TYPE_SINGLETON_FACTORY (g_singleton_factory_get_type()) +DECLARE_GTYPE(GSingletonFactory, g_singleton_factory, G, SINGLETON_FACTORY); -/* Indique le type défini pour une mémoire de types d'objets. */ -GType g_singleton_factory_get_type(void); /* Crée un compacteur d'instances de types. */ GSingletonFactory *g_singleton_factory_new(void); diff --git a/src/glibext/strbuilder-int.h b/src/glibext/strbuilder-int.h new file mode 100644 index 0000000..d74e65c --- /dev/null +++ b/src/glibext/strbuilder-int.h @@ -0,0 +1,47 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * strbuilder-int.h - définitions internes propres aux éléments exportant une chaîne de caractères + * + * Copyright (C) 2025 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 _GLIBEXT_STRBUILDER_INT_H +#define _GLIBEXT_STRBUILDER_INT_H + + +#include "strbuilder.h" + + + +/* Exporte une chaîne de caractères à partir d'un objet. */ +typedef bool (* strbuilder_to_string_fc) (const GStringBuilder *, unsigned int, sized_binary_t *); + + +/* Instance d'objet visant à être unique (interface) */ +struct _GStringBuilderInterface +{ + GTypeInterface base_iface; /* A laisser en premier */ + + strbuilder_to_string_fc to_string; /* Conversion en chaîne */ + +}; + + + +#endif /* _GLIBEXT_STRBUILDER_INT_H */ diff --git a/src/glibext/strbuilder.c b/src/glibext/strbuilder.c new file mode 100644 index 0000000..e48674c --- /dev/null +++ b/src/glibext/strbuilder.c @@ -0,0 +1,89 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * strbuilder.c - exportation d'une chaîne de caractères depuis un élément + * + * Copyright (C) 2025 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 Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "strbuilder.h" + + +#include "strbuilder-int.h" + + + +/* Procède à l'initialisation de l'interface d'exportation. */ +static void g_string_builder_default_init(GStringBuilderInterface *); + + + +/* Détermine le type d'une interface pour la constitution d'objets uniques. */ +G_DEFINE_INTERFACE(GStringBuilder, g_string_builder, G_TYPE_OBJECT) + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface d'exportation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_string_builder_default_init(GStringBuilderInterface *iface) +{ + iface->to_string = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : builder = objet dont l'instance est exportable. * +* flags = éventuelles indications pour l'opération. * +* out = chaîne de caractères mise en place. [OUT] * +* * +* Description : Exporte une chaîne de caractères à partir d'un objet. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : La sortie out est à nettoyer avec exit_sized_binary() après * +* usage. * +* * +******************************************************************************/ + +bool g_string_builder_to_string(const GStringBuilder *builder, unsigned int flags, sized_binary_t *out) +{ + bool result; /* Bilan à retourner */ + GStringBuilderInterface *iface; /* Interface utilisée */ + + iface = G_STRING_BUILDER_GET_IFACE(builder); + + if (iface->to_string == NULL) + result = false; + + else + result = iface->to_string(builder, flags, out); + + return result; + +} diff --git a/src/glibext/strbuilder.h b/src/glibext/strbuilder.h new file mode 100644 index 0000000..b6422f7 --- /dev/null +++ b/src/glibext/strbuilder.h @@ -0,0 +1,46 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * strbuilder.h - prototypes pour l'exportation d'une chaîne de caractères depuis un élément + * + * Copyright (C) 2025 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 Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_STRBUILDER_H +#define _GLIBEXT_STRBUILDER_H + + +#include <stdbool.h> + + +#include "helpers.h" +#include "../common/szbin.h" + + + +#define G_TYPE_STRING_BUILDER (g_string_builder_get_type()) + +DECLARE_INTERFACE(GStringBuilder, g_string_builder, G, STRING_BUILDER); + + +/* Exporte une chaîne de caractères à partir d'un objet. */ +bool g_string_builder_to_string(const GStringBuilder *, unsigned int, sized_binary_t *); + + + +#endif /* _GLIBEXT_STRBUILDER_H */ diff --git a/src/gtkext/Makefile.am b/src/gtkext/Makefile.am index 2a2738a..839ee8f 100644 --- a/src/gtkext/Makefile.am +++ b/src/gtkext/Makefile.am @@ -26,10 +26,17 @@ libgtkext_la_LIBADD = \ libgtkext_la_CFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) +IMG_PATH = ../../data/images + RES_FILES = \ hexview.css \ hexview.ui \ - statusstack.ui + launcher.ui \ + statusstack.ui \ + $(IMG_PATH)/nolock-symbolic.svg \ + $(IMG_PATH)/locked-symbolic.svg \ + $(IMG_PATH)/unlocked-symbolic.svg \ + tweak.ui libgtkext4_la_SOURCES = \ area-int.h \ @@ -45,17 +52,22 @@ libgtkext4_la_SOURCES = \ helpers.h \ hexview-int.h \ hexview.h hexview.c \ + launcher-int.h \ + launcher.h launcher.c \ panel-int.h \ panel.h panel.c \ resources.h resources.c \ - statusstack.h statusstack.c + statusstack-int.h \ + statusstack.h statusstack.c \ + tweak-int.h \ + tweak.h tweak.c libgtkext4_la_CFLAGS = $(LIBGTK4_CFLAGS) devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) -dev_HEADERS = $(libgtkext_la_SOURCES:%c=) +dev_HEADERS = $(libgtkext4_la_SOURCES:%c=) #SUBDIRS = graph diff --git a/src/gtkext/bufferview.c b/src/gtkext/bufferview.c index 6b8f17c..13c2632 100644 --- a/src/gtkext/bufferview.c +++ b/src/gtkext/bufferview.c @@ -279,9 +279,9 @@ GBufferView *gtk_buffer_view_get_view(const GtkBufferView *view) /****************************************************************************** * * -* Paramètres : widget = composant GTK à examiner. * -* width = largeur affectée au composant graphique. * -* height = hauteur affectée au composant graphique. * +* Paramètres : widget = composant GTK à examiner. * +* width = largeur affectée au composant graphique. * +* height = hauteur affectée au composant graphique. * * baseline = ligne de base affectée au composant graphique. * * * * Description : Prend acte de la taille allouée au composant d'affichage. * diff --git a/src/gtkext/gresource.xml b/src/gtkext/gresource.xml index 3bfd8c5..640985f 100644 --- a/src/gtkext/gresource.xml +++ b/src/gtkext/gresource.xml @@ -3,6 +3,13 @@ <gresource prefix="/re/chrysalide/framework/gtkext"> <file compressed="true">hexview.css</file> <file compressed="true">hexview.ui</file> + <file compressed="true">launcher.ui</file> <file compressed="true">statusstack.ui</file> + <file compressed="true">tweak.ui</file> + </gresource> + <gresource prefix="/re/chrysalide/framework/gui/icons/scalable/actions"> + <file compressed="true" alias="nolock-symbolic.svg">../../data/images/nolock-symbolic.svg</file> + <file compressed="true" alias="locked-symbolic.svg">../../data/images/locked-symbolic.svg</file> + <file compressed="true" alias="unlocked-symbolic.svg">../../data/images/unlocked-symbolic.svg</file> </gresource> </gresources> diff --git a/src/gtkext/hexview.c b/src/gtkext/hexview.c index 6df1140..5a8dd04 100644 --- a/src/gtkext/hexview.c +++ b/src/gtkext/hexview.c @@ -438,9 +438,9 @@ void demo_snapshot (GtkWidget *widget, GtkSnapshot *snapshot, GtkWidget *parent) /****************************************************************************** * * -* Paramètres : widget = composant GTK à examiner. * -* width = largeur affectée au composant graphique. * -* height = hauteur affectée au composant graphique. * +* Paramètres : widget = composant GTK à examiner. * +* width = largeur affectée au composant graphique. * +* height = hauteur affectée au composant graphique. * * baseline = ligne de base affectée au composant graphique. * * * * Description : Prend acte de la taille allouée au composant d'affichage. * diff --git a/src/gtkext/launcher-int.h b/src/gtkext/launcher-int.h new file mode 100644 index 0000000..07152f0 --- /dev/null +++ b/src/gtkext/launcher-int.h @@ -0,0 +1,56 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tweak-int.h - définitions internes pour un lanceur de panneau majeur + * + * Copyright (C) 2025 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 _GTKEXT_LAUNCHER_INT_H +#define _GTKEXT_LAUNCHER_INT_H + + +#include "launcher.h" + + + +/* Elément de lancement d'un panneau majeur pour l'interface (instance) */ +struct _GtkPanelLauncher +{ + GtkListBoxRow parent; /* A laisser en premier */ + + GtkImage *icon; /* Eventuelle image */ + GtkLabel *title; /* Etiquette associée */ + GtkLabel *desc; /* Description du panneau */ + +}; + +/* Elément de lancement d'un panneau majeur pour l'interface (classe) */ +struct _GtkPanelLauncherClass +{ + GtkListBoxRowClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un nouveau lanceur de panneau majeur. */ +bool gtk_panel_launcher_create(GtkPanelLauncher *, const char *, const char *, const char *); + + + +#endif /* _GTKEXT_LAUNCHER_INT_H */ diff --git a/src/gtkext/launcher.c b/src/gtkext/launcher.c new file mode 100644 index 0000000..5bb850b --- /dev/null +++ b/src/gtkext/launcher.c @@ -0,0 +1,211 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * launcher.c - lanceur de panneau majeur + * + * Copyright (C) 2025 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/>. + */ + + +#include "launcher.h" + + +#include <malloc.h> +#include <string.h> + + +#include "helpers.h" +#include "launcher-int.h" +#include "../common/extstr.h" + + + +/* Initialise la classe des sections d'éléments paramétrables. */ +static void gtk_panel_launcher_class_init(GtkPanelLauncherClass *); + +/* Initialise une instance de lanceur de panneau majeur. */ +static void gtk_panel_launcher_init(GtkPanelLauncher *); + +/* Supprime toutes les références externes. */ +static void gtk_panel_launcher_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_panel_launcher_finalize(GObject *); + + + +/* Détermine le type du composant d'affichage générique. */ +G_DEFINE_TYPE(GtkPanelLauncher, gtk_panel_launcher, GTK_TYPE_LIST_BOX_ROW); + + +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Initialise la classe des sections d'éléments paramétrables. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_panel_launcher_class_init(GtkPanelLauncherClass *class) +{ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = gtk_panel_launcher_dispose; + object->finalize = gtk_panel_launcher_finalize; + + widget = GTK_WIDGET_CLASS(class); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gtkext/launcher.ui"); + + gtk_widget_class_bind_template_child(widget, GtkPanelLauncher, icon); + gtk_widget_class_bind_template_child(widget, GtkPanelLauncher, title); + gtk_widget_class_bind_template_child(widget, GtkPanelLauncher, desc); + +} + + +/****************************************************************************** +* * +* Paramètres : launcher = composant GTK à initialiser. * +* * +* Description : Initialise une instance de lanceur de panneau majeur. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_panel_launcher_init(GtkPanelLauncher *launcher) +{ + gtk_widget_init_template(GTK_WIDGET(launcher)); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_panel_launcher_dispose(GObject *object) +{ + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_PANEL_LAUNCHER); + + G_OBJECT_CLASS(gtk_panel_launcher_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_panel_launcher_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_panel_launcher_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : icon = désignation de l'image de représentation. * +* title = titre principal à afficher. * +* desc = description du panneau ciblé. * +* * +* Description : Crée un nouveau lanceur de panneau majeur. * +* * +* Retour : Composant GTK mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkPanelLauncher *gtk_panel_launcher_new(const char *icon, const char *title, const char *desc) +{ + GtkPanelLauncher *result; /* Instance à retourner */ + + result = g_object_new(GTK_TYPE_PANEL_LAUNCHER, NULL); + + if (!gtk_panel_launcher_create(result, icon, title, desc)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : launcher = lanceur à initialiser pleinement. * +* icon = désignation de l'image de représentation. * +* title = titre principal à afficher. * +* desc = description du panneau ciblé. * +* * +* Description : Met en place un nouveau lanceur de panneau majeur. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool gtk_panel_launcher_create(GtkPanelLauncher *launcher, const char *icon, const char *title, const char *desc) +{ + bool result; /* Bilan à retourner */ + char *bold; /* Titre sublimé */ + + result = true; + + gtk_image_set_from_icon_name(launcher->icon, icon); + + bold = strdup(title); + bold = strprep(bold, "<b>"); + bold = stradd(bold, "</b>"); + + gtk_label_set_label(launcher->title, bold); + + free(bold); + + gtk_label_set_label(launcher->desc, desc); + + return result; + +} diff --git a/src/gtkext/launcher.h b/src/gtkext/launcher.h new file mode 100644 index 0000000..3857216 --- /dev/null +++ b/src/gtkext/launcher.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * launcher.h - prototypes pour pour un lanceur de panneau majeur + * + * Copyright (C) 2025 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 _GTKEXT_LAUNCHER_H +#define _GTKEXT_LAUNCHER_H + + +#include <gtk/gtk.h> + + +#include "../glibext/helpers.h" + + + +#define GTK_TYPE_PANEL_LAUNCHER (gtk_panel_launcher_get_type()) + +DECLARE_GTYPE(GtkPanelLauncher, gtk_panel_launcher, GTK, PANEL_LAUNCHER); + + +/* Crée un nouveau lanceur de panneau majeur. */ +GtkPanelLauncher *gtk_panel_launcher_new(const char *, const char *, const char *); + + + +#endif /* _GTKEXT_LAUNCHER_H */ diff --git a/src/gtkext/launcher.ui b/src/gtkext/launcher.ui new file mode 100644 index 0000000..f6f4fec --- /dev/null +++ b/src/gtkext/launcher.ui @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <template class="GtkPanelLauncher" parent="GtkListBoxRow"> + + <property name="child"> + + <object class="GtkGrid"> + <property name="margin-bottom">12</property> + <property name="margin-end">12</property> + <property name="margin-start">12</property> + <property name="margin-top">12</property> + <property name="column-spacing">12</property> + + <child> + <object class="GtkImage" id="icon"> + <property name="icon-name"></property> + <property name="pixel-size">48</property> + <layout> + <property name="column">0</property> + <property name="row">0</property> + <property name="row-span">2</property> + </layout> + <style> + <class name="icon-dropshadow"/> + </style> + </object> + </child> + + <child> + <object class="GtkLabel" id="title"> + <property name="label"></property> + <property name="use-markup">TRUE</property> + <property name="xalign">0</property> + <layout> + <property name="column">1</property> + <property name="row">0</property> + </layout> + </object> + </child> + + <child> + <object class="GtkLabel" id="desc"> + <property name="label"></property> + <property name="hexpand">true</property> + <property name="xalign">0</property> + <layout> + <property name="column">1</property> + <property name="row">1</property> + </layout> + <style> + <class name="dim-label"/> + </style> + </object> + </child> + + <child> + <object class="GtkImage"> + <property name="icon-name">go-next-symbolic</property> + <property name="margin-start">12</property> + <layout> + <property name="column">2</property> + <property name="row">0</property> + <property name="row-span">2</property> + </layout> + <style> + <class name="icon-dropshadow"/> + </style> + </object> + </child> + + </object> + + </property> + + </template> +</interface> diff --git a/src/gtkext/statusstack-int.h b/src/gtkext/statusstack-int.h index facc5af..2a9e99e 100644 --- a/src/gtkext/statusstack-int.h +++ b/src/gtkext/statusstack-int.h @@ -26,6 +26,7 @@ #include "statusstack.h" +#include "../glibext/secstorage.h" @@ -70,6 +71,9 @@ struct _GtkStatusStack /* Tronc commun */ + GSecretStorage *storage; /* Stockage des secrets */ + GtkToggleButton *lock_update; /* Activation des accès */ + GtkLabel *net_recv_speed; /* Débit en réception */ GtkLabel *net_send_speed; /* Débit en émission */ diff --git a/src/gtkext/statusstack.c b/src/gtkext/statusstack.c index 0d8ef62..76dbee8 100644 --- a/src/gtkext/statusstack.c +++ b/src/gtkext/statusstack.c @@ -53,6 +53,9 @@ static void gtk_status_stack_dispose(GtkStatusStack *); /* Procède à la libération totale de la mémoire. */ static void gtk_status_stack_finalize(GtkStatusStack *); +/* Note le changement de verrouillage du stockage sécurisé. */ +static void gtk_status_stack_on_secret_storage_lock_update(GSecretStorage *, GtkStatusStack *); + /* Met à jour dans la barre les débits réseau observés. */ static gboolean gtk_status_stack_update_network_stats(GtkStatusStack *); @@ -190,6 +193,8 @@ static void gtk_status_stack_class_init(GtkStatusStackClass *class) gtk_widget_class_bind_template_child(widget, GtkStatusStack, activity_message); gtk_widget_class_bind_template_child(widget, GtkStatusStack, activity_progress); + gtk_widget_class_bind_template_child(widget, GtkStatusStack, lock_update); + gtk_widget_class_bind_template_child(widget, GtkStatusStack, net_recv_speed); gtk_widget_class_bind_template_child(widget, GtkStatusStack, net_send_speed); @@ -220,6 +225,17 @@ static void gtk_status_stack_init(GtkStatusStack *stack) stack->activity_info = calloc(1, sizeof(activity_info_t)); init_activity_info(stack->activity_info); + /* Suivi des évolutions relatives au stockage sécurisé */ + + stack->storage = get_secret_storage(); + + g_signal_connect(stack->storage, "lock-update", + G_CALLBACK(gtk_status_stack_on_secret_storage_lock_update), stack); + + gtk_status_stack_on_secret_storage_lock_update(stack->storage, stack); + + /* Suivi des débits de connexion */ + stack->next_index = 0; stack->network_update_tag = g_timeout_add(NETWORK_UPDATE_INTERVAL, @@ -242,6 +258,12 @@ static void gtk_status_stack_init(GtkStatusStack *stack) static void gtk_status_stack_dispose(GtkStatusStack *stack) { + if (stack->storage != NULL) + g_signal_handlers_disconnect_by_func(stack->storage, + gtk_status_stack_on_secret_storage_lock_update, stack); + + g_clear_object(&stack->storage); + g_source_remove(stack->network_update_tag); gtk_widget_dispose_template(GTK_WIDGET(stack), GTK_TYPE_STATUS_STACK); @@ -322,6 +344,40 @@ void gtk_status_stack_reset(GtkStatusStack *stack) /****************************************************************************** * * +* Paramètres : storage = gardien des secrets impliqué. * +* stack = barre de statut à actualiser. * +* * +* Description : Note le changement de verrouillage du stockage sécurisé. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_status_stack_on_secret_storage_lock_update(GSecretStorage *storage, GtkStatusStack *stack) +{ + if (!g_secret_storage_has_key(stack->storage)) + { + gtk_widget_set_sensitive(GTK_WIDGET(stack->lock_update), false); + gtk_button_set_icon_name(GTK_BUTTON(stack->lock_update), "nolock-symbolic"); + } + else + { + gtk_widget_set_sensitive(GTK_WIDGET(stack->lock_update), true); + + if (g_secret_storage_is_locked(stack->storage)) + gtk_button_set_icon_name(GTK_BUTTON(stack->lock_update), "locked-symbolic"); + else + gtk_button_set_icon_name(GTK_BUTTON(stack->lock_update), "unlocked-symbolic"); + + } + +} + + +/****************************************************************************** +* * * Paramètres : stack = barre de statut à actualiser. * * * * Description : Met à jour dans la barre les débits réseau observés. * diff --git a/src/gtkext/statusstack.ui b/src/gtkext/statusstack.ui index 422f95d..8469e6e 100644 --- a/src/gtkext/statusstack.ui +++ b/src/gtkext/statusstack.ui @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> <interface> <template class="GtkStatusStack" parent="GtkBox"> @@ -148,6 +149,20 @@ </child> <child> + <object class="GtkToggleButton" id="lock_update"> + <property name="sensitive">false</property> + <property name="has-frame">false</property> + <property name="icon-name">nolock-symbolic</property> + </object> + </child> + + <child> + <object class="GtkSeparator"> + <property name="orientation">vertical</property> + </object> + </child> + + <child> <object class="GtkImage"> <property name="margin-start">8</property> <property name="icon-name">pan-down-symbolic</property> diff --git a/src/gtkext/tweak-int.h b/src/gtkext/tweak-int.h new file mode 100644 index 0000000..0d2c213 --- /dev/null +++ b/src/gtkext/tweak-int.h @@ -0,0 +1,65 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tweak-int.h - définitions internes pour une section d'éléments à paramétrer + * + * Copyright (C) 2025 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 _GTKEXT_TWEAK_INT_H +#define _GTKEXT_TWEAK_INT_H + + +#include "tweak.h" + + + +/* Section de paramétrage pour liste d'éléments à configurer (instance) */ +struct _GtkTweakSection +{ + GtkListBoxRow parent; /* A laisser en premier */ + + GtkImage *icon; /* Eventuelle image */ + GtkLabel *label; /* Etiquette associée */ + GtkWidget *next; /* Eventuelle progression */ + + char *category; /* Groupe de rassemblement */ + + union + { + GType panel; /* Accès à la page de config. */ + char *sub; /* Sous-ensemble à presenter */ + }; + bool has_sub_section; /* Choix du champ valide */ + +}; + +/* Section de paramétrage pour liste d'éléments à configurer (classe) */ +struct _GtkTweakSectionClass +{ + GtkListBoxRowClass parent; /* A laisser en premier */ + +}; + + +/* Met en place une nouvelle section de configuration. */ +bool gtk_tweak_section_create(GtkTweakSection *, const tweak_info_t *); + + + +#endif /* _GTKEXT_TWEAK_INT_H */ diff --git a/src/gtkext/tweak.c b/src/gtkext/tweak.c new file mode 100644 index 0000000..b03cf17 --- /dev/null +++ b/src/gtkext/tweak.c @@ -0,0 +1,319 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tweak.c - section d'éléments à paramétrer + * + * Copyright (C) 2025 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/>. + */ + + +#include "tweak.h" + + +#include <assert.h> +#include <malloc.h> +#include <string.h> + + +#include "helpers.h" +#include "tweak-int.h" + + + +/* Initialise la classe des sections d'éléments paramétrables. */ +static void gtk_tweak_section_class_init(GtkTweakSectionClass *); + +/* Initialise une instance de section d'éléments paramétrables. */ +static void gtk_tweak_section_init(GtkTweakSection *); + +/* Supprime toutes les références externes. */ +static void gtk_tweak_section_dispose(GtkTweakSection *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_tweak_section_finalize(GtkTweakSection *); + + + +/* Détermine le type du composant d'affichage générique. */ +G_DEFINE_TYPE(GtkTweakSection, gtk_tweak_section, GTK_TYPE_LIST_BOX_ROW); + + +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Initialise la classe des sections d'éléments paramétrables. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tweak_section_class_init(GtkTweakSectionClass *class) +{ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = (GObjectFinalizeFunc/* ! */)gtk_tweak_section_dispose; + object->finalize = (GObjectFinalizeFunc)gtk_tweak_section_finalize; + + widget = GTK_WIDGET_CLASS(class); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gtkext/tweak.ui"); + + gtk_widget_class_bind_template_child(widget, GtkTweakSection, icon); + gtk_widget_class_bind_template_child(widget, GtkTweakSection, label); + gtk_widget_class_bind_template_child(widget, GtkTweakSection, next); + +} + + +/****************************************************************************** +* * +* Paramètres : section = composant GTK à initialiser. * +* * +* Description : Initialise une instance de section d'éléments paramétrables. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tweak_section_init(GtkTweakSection *section) +{ + gtk_widget_init_template(GTK_WIDGET(section)); + + section->category = NULL; + + section->sub = NULL; + section->has_sub_section = true; + +} + + +/****************************************************************************** +* * +* Paramètres : section = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tweak_section_dispose(GtkTweakSection *section) +{ + gtk_widget_dispose_template(GTK_WIDGET(section), GTK_TYPE_TWEAK_SECTION); + + G_OBJECT_CLASS(gtk_tweak_section_parent_class)->dispose(G_OBJECT(section)); + +} + + +/****************************************************************************** +* * +* Paramètres : section = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_tweak_section_finalize(GtkTweakSection *section) +{ + if (section->category != NULL) + free(section->category); + + if (section->has_sub_section && section->sub != NULL) + free(section->sub); + + G_OBJECT_CLASS(gtk_tweak_section_parent_class)->finalize(G_OBJECT(section)); + +} + + +/****************************************************************************** +* * +* Paramètres : info = informations associées à la section. * +* * +* Description : Crée une nouvelle section de configuration. * +* * +* Retour : Composant GTK mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkTweakSection *gtk_tweak_section_new(const tweak_info_t *info) +{ + GtkTweakSection *result; /* Instance à retourner */ + + result = g_object_new(GTK_TYPE_TWEAK_SECTION, NULL); + + if (!gtk_tweak_section_create(result, info)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : section = section à initialiser pleinement. * +* info = informations associées à la section. * +* * +* Description : Met en place une nouvelle section de configuration. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool gtk_tweak_section_create(GtkTweakSection *section, const tweak_info_t *info) +{ + bool result; /* Bilan à retourner */ + + result = true; + + gtk_image_set_from_icon_name(section->icon, info->image); + gtk_label_set_label(section->label, info->label); + + gtk_widget_set_visible(section->next, info->has_sub_section); + + section->category = strdup(info->category); + + if (info->has_sub_section) + { + section->sub = strdup(info->sub); + section->has_sub_section = true; + } + else + { + section->panel = info->panel; + section->has_sub_section = false; + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : section = section à consulter. * +* * +* Description : Fournit l'étiquette associée à une section de configuration. * +* * +* Retour : Désignation humaine de la section. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const char *gtk_tweak_section_get_label(const GtkTweakSection *section) +{ + const char *result; /* Texte à retourner */ + + result = gtk_label_get_text(section->label); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : section = section à consulter. * +* * +* Description : Indique si la section renvoie vers une sous-section. * +* * +* Retour : Bilan de la consultation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool gtk_tweak_section_has_sub_section(const GtkTweakSection *section) +{ + bool result; /* Statut à retourner */ + + result = section->has_sub_section; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : section = section à consulter. * +* * +* Description : Fournit le type d'un éventuel panneau de configuration lié. * +* * +* Retour : Bilan de la consultation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GType gtk_tweak_section_get_panel(const GtkTweakSection *section) +{ + GType result; /* Type d'objet à retourner */ + + assert(!section->has_sub_section); + + result = section->panel; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : section = section à consulter. * +* * +* Description : Fournit la désignation d'une éventuelle sous-section liée. * +* * +* Retour : Désignation associée à la sous-section. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const char *gtk_tweak_section_get_sub_section(const GtkTweakSection *section) +{ + const char *result; /* Désignation à renvoyer */ + + assert(section->has_sub_section); + + result = section->sub; + + return result; + +} diff --git a/src/gtkext/tweak.h b/src/gtkext/tweak.h new file mode 100644 index 0000000..8c44844 --- /dev/null +++ b/src/gtkext/tweak.h @@ -0,0 +1,90 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tweak.h - prototypes pour pour une section d'éléments à paramétrer + * + * Copyright (C) 2025 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 _GTKEXT_TWEAK_H +#define _GTKEXT_TWEAK_H + + +#include <stdbool.h> +#include <gtk/gtk.h> + + +#include "../glibext/helpers.h" + + + +/* Définition d'une section de configuration */ +typedef struct _tweak_info_t +{ + const char *parent; /* Ensemble d'appartenance */ + + const char *category; /* Groupe de rassemblement */ + + const char *image; /* Eventuelle image associée */ + const char *key; /* Désignation de la section */ + const char *label; /* Désignation humaine */ + + union + { + GType panel; /* Accès à la page de config. */ + const char *sub; /* Sous-ensemble à presenter */ + }; + bool has_sub_section; /* Choix du champ valide */ + +} tweak_info_t; + + +#define TWEAK_SIMPLE_DEF(p, c, i, k, l, t) \ + { \ + .parent = p, \ + .category = c, \ + .image = i, \ + .key = k, \ + .label = l, \ + .panel = t, \ + .has_sub_section = false, \ + } + +#define GTK_TYPE_TWEAK_SECTION (gtk_tweak_section_get_type()) + +DECLARE_GTYPE(GtkTweakSection, gtk_tweak_section, GTK, TWEAK_SECTION); + + +/* Crée une nouvelle section de configuration. */ +GtkTweakSection *gtk_tweak_section_new(const tweak_info_t *); + +/* Fournit l'étiquette associée à une section de configuration. */ +const char *gtk_tweak_section_get_label(const GtkTweakSection *); + +/* Indique si la section renvoie vers une sous-section. */ +bool gtk_tweak_section_has_sub_section(const GtkTweakSection *); + +/* Fournit le type d'un éventuel panneau de configuration lié. */ +GType gtk_tweak_section_get_panel(const GtkTweakSection *); + +/* Fournit la désignation d'une éventuelle sous-section liée. */ +const char *gtk_tweak_section_get_sub_section(const GtkTweakSection *); + + + +#endif /* _GTKEXT_TWEAK_H */ diff --git a/src/gtkext/tweak.ui b/src/gtkext/tweak.ui new file mode 100644 index 0000000..576e25e --- /dev/null +++ b/src/gtkext/tweak.ui @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <template class="GtkTweakSection" parent="GtkListBoxRow"> + + <property name="child"> + + <object class="GtkBox"> + <property name="orientation">horizontal</property> + <property name="margin-top">14</property> + <property name="margin-bottom">14</property> + + <child> + <object class="GtkImage" id="icon"> + <property name="icon-name">security-high-symbolic</property> + <property name="margin-start">12</property> + <property name="margin-end">12</property> + <style> + <class name="icon-dropshadow"/> + </style> + </object> + </child> + + <child> + <object class="GtkLabel" id="label"> + <property name="xalign">0</property> + <property name="label" translatable="yes">Security</property> + <property name="hexpand">true</property> + </object> + </child> + + <child> + <object class="GtkImage" id="next"> + <property name="icon-name">go-next-symbolic</property> + <property name="margin-start">12</property> + <property name="margin-end">12</property> + <style> + <class name="icon-dropshadow"/> + </style> + </object> + </child> + + </object> + </property> + + </template> +</interface> diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am index 42761c4..be70445 100644 --- a/src/gui/Makefile.am +++ b/src/gui/Makefile.am @@ -26,8 +26,6 @@ libgui_la_CFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) libgui4_la_SOURCES = \ - panel-int.h \ - panel.h panel.c \ resources.h resources.c \ window-int.h \ window.h window.c diff --git a/src/gui/core/core.c b/src/gui/core/core.c index 4a6809c..57a398a 100644 --- a/src/gui/core/core.c +++ b/src/gui/core/core.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * core.c - chargement et le déchargement du tronc commun pour l'éditeur graphique * - * Copyright (C) 2016-2019 Cyrille Bagard + * Copyright (C) 2016-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -63,7 +63,7 @@ bool load_gui_components(AvailableGuiComponent flags) if ((flags & AGC_PANELS) != 0 && (__loaded & AGC_PANELS) == 0) { - result = load_main_panels(); + result = load_main_framework_panel_definitions(); if (!result) goto done; __loaded |= AGC_PANELS; @@ -93,7 +93,7 @@ void unload_gui_components(AvailableGuiComponent flags) { if ((flags & AGC_PANELS) != 0 && (__loaded & AGC_PANELS) == 0) { - unload_all_panels(); + unload_all_framework_panel_definitions(); __loaded &= ~AGC_PANELS; diff --git a/src/gui/core/panels.c b/src/gui/core/panels.c index 69ab2aa..4c113b8 100644 --- a/src/gui/core/panels.c +++ b/src/gui/core/panels.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * panels.c - gestion d'ensemble de tous les panneaux graphiques du framework * - * Copyright (C) 2016-2024 Cyrille Bagard + * Copyright (C) 2016-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,25 +25,137 @@ #include "panels.h" +#include <assert.h> #include <malloc.h> +#include <string.h> + + +#include <i18n.h> #include "../panels/binary.h" +#include "../panels/binary-params.h" #include "../panels/welcome.h" +#include "../../gtkext/launcher.h" + + + +/* Définition générique complète d'un panneau */ +typedef struct _ext_panel_info_t +{ + /* Début des champs copiés de panel_info_t */ + + char *category; /* Groupe de rassemblement */ + + char *image; /* Eventuelle image associée */ + char *title; /* Désignation humaine */ + char *desc; /* Description humaine */ + FrameworkPanelPersonality personality; /* Comportement attendu */ + + GType panel_type; /* Type du panneau représenté */ + GType params_type; /* Composant de paramètre */ + + /* Fin des champs copiés de panel_info_t */ + + GtkTiledPanel *singleton; /* Conservation des allocations*/ + +} ext_panel_info_t; /* Liste des panneaux disponibles */ -static GPanelItem **_panels_list = NULL; +static ext_panel_info_t **_panels_list = NULL; static size_t _panels_count = 0; +/* Copie une définition basique de panneau graphqiue. */ +static ext_panel_info_t *copy_panel_info(const panel_info_t *); + +/* Efface une définition étendue de panneau graphique. */ +static void delete_panel_info(ext_panel_info_t *); + + + +/****************************************************************************** +* * +* Paramètres : info = information de base à copier. * +* * +* Description : Copie une définition basique de panneau graphqiue. * +* * +* Retour : Structure mémorisant l'ensemble des informations. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static ext_panel_info_t *copy_panel_info(const panel_info_t *info) +{ + ext_panel_info_t *result; /* Structure à retourner */ + + result = calloc(1, sizeof(ext_panel_info_t)); + + if (info->category != NULL) + result->category = strdup(info->category); + + if (info->image != NULL) + result->image = strdup(info->image); + + result->title = strdup(info->title); + + if (info->desc != NULL) + result->desc = strdup(info->desc); + + result->personality = info->personality; + + result->panel_type = info->panel_type; + result->params_type = info->params_type; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : info = informations à supprimer de la mémoire. * +* * +* Description : Efface une définition étendue de panneau graphique. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void delete_panel_info(ext_panel_info_t *info) +{ + if (info->category != NULL) + free(info->category); + + if (info->image != NULL) + free(info->image); + + free(info->title); + + if (info->desc != NULL) + free(info->desc); + + if (info->singleton != NULL) + { + assert(info->personality & FPP_SINGLETON); + unref_object(info->singleton); + } + + free(info); + +} + /****************************************************************************** * * * Paramètres : - * * * -* Description : Charge les principaux panneaux graphiques du framework. * +* Description : Charge les définitions des principaux panneaux du framework. * * * * Retour : Bilan de l'opération. * * * @@ -51,19 +163,45 @@ static size_t _panels_count = 0; * * ******************************************************************************/ -bool load_main_panels(void) +bool load_main_framework_panel_definitions(void) { bool result; /* Bilan à retourner */ - - result = true; + panel_info_t info; /* Infos d'enregistrement */ // TODO register_panel_item(G_TYPE_LOG_PANEL, config); /* Chargement du panneau de rapport au plus tôt */ // TODO panel = g_panel_item_new(G_TYPE_LOG_PANEL, NULL); - register_panel_item(g_binary_panel_new()); - register_panel_item(g_welcome_panel_new()); + info.category = "Main"; + + info.image = "binfile-symbolic"; + info.title = _("Binary analysis"); + info.desc = _("Load a binary content and parse its format if recognized"); + + info.personality = FPP_MAIN_PANEL; + + info.panel_type = GTK_TYPE_BINARY_PANEL; + info.params_type = GTK_TYPE_BINARY_PARAMETERS; + + result = register_framework_panel_definition(&info); + if (!result) goto done; + + info.category = NULL; + + info.image = NULL; + info.title = _("Welcome"); + info.desc = NULL; + + info.personality = FPP_MAIN_PANEL | FPP_SINGLETON; + + info.panel_type = GTK_TYPE_WELCOME_PANEL; + info.params_type = G_TYPE_INVALID; + + result = register_framework_panel_definition(&info); + if (!result) goto done; + + done: return result; @@ -82,12 +220,12 @@ bool load_main_panels(void) * * ******************************************************************************/ -void unload_all_panels(void) +void unload_all_framework_panel_definitions(void) { size_t i; /* Boucle de parcours */ for (i = 0; i < _panels_count; i++) - unref_object(_panels_list[i]); + delete_panel_info(_panels_list[i]); _panels_list = NULL; _panels_count = 0; @@ -97,10 +235,9 @@ void unload_all_panels(void) /****************************************************************************** * * -* Paramètres : type = type du composant à présenter à l'affichage. * -* config = configuration à compléter. * +* Paramètres : info = information de base à copier. * * * -* Description : Enregistre un panneau comme partie intégrante de l'éditeur. * +* Description : Enregistre la définition d'un panneau graphique. * * * * Retour : - * * * @@ -108,11 +245,36 @@ void unload_all_panels(void) * * ******************************************************************************/ -void register_panel_item(/* __steal */ GPanelItem *item) +bool register_framework_panel_definition(const panel_info_t *info) { - _panels_list = realloc(_panels_list, ++_panels_count * sizeof(GPanelItem *)); + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours */ + ext_panel_info_t *ext_info; /* Informations conservées */ + + result = false; - _panels_list[_panels_count - 1] = item; + /* Validation */ + + for (i = 0; i < _panels_count; i++) + if (_panels_list[i]->panel_type == info->panel_type) + break; + + if (i < _panels_count) + goto done; + + /* Enregistrement */ + + ext_info = copy_panel_info(info); + + _panels_list = realloc(_panels_list, ++_panels_count * sizeof(ext_panel_info_t *)); + + _panels_list[_panels_count - 1] = ext_info; + + result = true; + + done: + + return result; } @@ -121,7 +283,7 @@ void register_panel_item(/* __steal */ GPanelItem *item) * * * Paramètres : target = type de définition de panneau recherchée. * * * -* Description : Retrouve la définition d'un type de panneau. * +* Description : Met en place (au besoin) un panneau graphique unique. * * * * Retour : Instance de définition identifiée ou NULL en cas d'échec. * * * @@ -129,23 +291,27 @@ void register_panel_item(/* __steal */ GPanelItem *item) * * ******************************************************************************/ -GPanelItem *find_item_panel_by_type(GType target) +GtkTiledPanel *get_framework_panel_singleton(GType target) { - GPanelItem *result; /* Instance à renvoyer */ + GtkTiledPanel *result; /* Instance à renvoyer */ size_t i; /* Boucle de parcours */ - GPanelItem *item; /* Définition de panneau */ + ext_panel_info_t *info; /* Informations conservées */ result = NULL; for (i = 0; i < _panels_count; i++) { - item = _panels_list[i]; + info = _panels_list[i]; - if (G_OBJECT_TYPE(item) == target) + if (info->panel_type == target) { - result = item; + if (info->singleton == NULL) + info->singleton = g_object_new(target, NULL); + + result = info->singleton; ref_object(result); break; + } } @@ -157,39 +323,84 @@ GPanelItem *find_item_panel_by_type(GType target) /****************************************************************************** * * -* Paramètres : skip = saute le panneau d'accueil lors du parcours ? * -* handle = routine à appeler pour chaque panneau. * -* data = données fournies pour accompagner cet appel. * +* Paramètres : list = liste à compléter. * * * -* Description : Effectue le parcours de tous les panneaux chargés. * +* Description : Intègre une définition de panneau enregistrée. * * * -* Retour : true si le parcours a été total, false sinon. * +* Retour : true pour un parcours complet de la liste des définitions. * * * * Remarques : - * * * ******************************************************************************/ -bool browse_all_item_panels(bool skip, handle_panel_item_fc handle, void *data) +void populate_framework_panel_launcher_list(GtkListBox *list) { - bool result; /* Résultat à renvoyer */ size_t i; /* Boucle de parcours */ - GPanelItem *item; /* Définition de panneau */ - - result = true; + ext_panel_info_t *info; /* Informations conservées */ + GtkPanelLauncher *launcher; /* Lanceur à intégrer */ for (i = 0; i < _panels_count; i++) { - item = _panels_list[i]; + info = _panels_list[i]; - if (skip && G_OBJECT_TYPE(item) == G_TYPE_WELCOME_PANEL) + if (info->category == NULL) continue; - result = handle(item, data); + launcher = gtk_panel_launcher_new(info->image, info->title, info->desc); + + g_object_set_data(G_OBJECT(launcher), "panel_type", GSIZE_TO_POINTER(info->panel_type)); + + gtk_list_box_append(list, GTK_WIDGET(launcher)); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : row = lanceur sélectionné. * +* * +* Description : Fournit un composant d'édition de paramètres de panneau. * +* * +* Retour : Composant d'édition de paramètres ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkWidget *get_framework_panel_parameters(GtkListBoxRow *row) +{ + GtkWidget *result; /* Composant à retourner */ + gpointer *data; /* Valeur incrustée */ + GType target; /* Type de panneau recherché */ + ext_panel_info_t *info; /* Informations conservées */ + size_t i; /* Boucle de parcours */ + + data = g_object_get_data(G_OBJECT(row), "panel_type"); + assert(data != NULL); + + target = GPOINTER_TO_SIZE(data); - if (!result) break; + info = NULL; + + for (i = 0; i < _panels_count; i++) + { + info = _panels_list[i]; + + if (info->panel_type == target) + break; } + assert(info != NULL); + assert(i < _panels_count); + + if (info->params_type == G_TYPE_INVALID) + result = NULL; + else + result = g_object_new(info->params_type, NULL); + return result; } diff --git a/src/gui/core/panels.h b/src/gui/core/panels.h index aaea9e6..4d0ce41 100644 --- a/src/gui/core/panels.h +++ b/src/gui/core/panels.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * panels.h - prototypes pour la gestion d'ensemble de tous les panneaux graphiques du framework * - * Copyright (C) 2016-2024 Cyrille Bagard + * Copyright (C) 2016-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -26,30 +26,59 @@ #define _GUI_CORE_PANELS_H +#include <glib-object.h> #include <stdbool.h> +#include <gtk/gtk.h> -#include "../panel.h" +#include "../../gtkext/panel.h" -/* Charge les principaux panneaux graphiques du framework. */ -bool load_main_panels(void); +/* Types de panneaux pour éditeur */ +typedef enum _FrameworkPanelPersonality +{ + FPP_NONE = (0 << 0), /* Pas de particularité */ + + FPP_MAIN_PANEL = (1 << 0), /* Panneau principal */ + FPP_SINGLETON = (1 << 1), /* Instance unique */ + +} FrameworkPanelPersonality; + +/* Définition générique d'un panneau */ +typedef struct _panel_info_t +{ + const char *category; /* Groupe de rassemblement */ + + const char *image; /* Eventuelle image associée */ + const char *title; /* Désignation humaine */ + const char *desc; /* Description humaine */ + + FrameworkPanelPersonality personality; /* Comportement attendu */ + + GType panel_type; /* Type du panneau représenté */ + GType params_type; /* Composant de paramètre */ + +} panel_info_t; + + +/* Charge les définitions des principaux panneaux du framework. */ +bool load_main_framework_panel_definitions(void); /* Décharge tous les panneaux graphiques du framework. */ -void unload_all_panels(void); +void unload_all_framework_panel_definitions(void); -/* Enregistre un panneau comme partie intégrante de l'éditeur. */ -void register_panel_item(/* __steal */ GPanelItem *); +/* Enregistre la définition d'un panneau graphique. */ +bool register_framework_panel_definition(const panel_info_t *); -/* Retrouve la définition d'un type de panneau. */ -GPanelItem *find_item_panel_by_type(GType); +/* Met en place (au besoin) un panneau graphique unique. */ +GtkTiledPanel *get_framework_panel_singleton(GType); -/* Réalise un traitement sur un panneau de l'éditeur. */ -typedef bool (* handle_panel_item_fc) (GPanelItem *, void *); +/* Intègre une définition de panneau enregistrée. */ +void populate_framework_panel_launcher_list(GtkListBox *); -/* Effectue le parcours de tous les panneaux chargés. */ -bool browse_all_item_panels(bool, handle_panel_item_fc, void *); +/* Fournit un composant d'édition de paramètres de panneau. */ +GtkWidget *get_framework_panel_parameters(GtkListBoxRow *); diff --git a/src/gui/dialogs/Makefile.am b/src/gui/dialogs/Makefile.am index 5a77b99..e910c96 100644 --- a/src/gui/dialogs/Makefile.am +++ b/src/gui/dialogs/Makefile.am @@ -3,21 +3,23 @@ BUILT_SOURCES = resources.h resources.c noinst_LTLIBRARIES = libguidialogs.la -UI_FILES = \ - about.ui +UI_FILES = \ + about.ui \ + preferences.ui # bookmark.ui \ # export_graph.ui \ # identity.ui \ # loading.ui \ -# preferences.ui \ # prefs_fgraph.ui \ # prefs_labels.ui \ # snapshots.ui \ # storage.ui -libguidialogs_la_SOURCES = \ - about-int.h \ - about.h about.c \ +libguidialogs_la_SOURCES = \ + about-int.h \ + about.h about.c \ + preferences-int.h \ + preferences.h preferences.c \ resources.h resources.c # bookmark.h bookmark.c \ # export_disass.h export_disass.c \ @@ -26,12 +28,15 @@ libguidialogs_la_SOURCES = \ # gotox.h gotox.c \ # identity.h identity.c \ # loading.h loading.c \ -# preferences.h preferences.c \ +# \ # prefs_fgraph.h prefs_fgraph.c \ # prefs_labels.h prefs_labels.c \ # snapshots.h snapshots.c \ # storage.h storage.c +libguidialogs_la_LIBADD = \ + prefs/libguidialogsprefs.la + libguidialogs_la_CFLAGS = $(LIBGTK4_CFLAGS) @@ -40,6 +45,9 @@ devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) dev_HEADERS = $(libguidialogs_la_SOURCES:%c=) +SUBDIRS = prefs + + resources.c: gresource.xml $(UI_FILES) glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-source --c-name gui_dialogs gresource.xml diff --git a/src/gui/dialogs/about-int.h b/src/gui/dialogs/about-int.h index 382859b..616c73f 100644 --- a/src/gui/dialogs/about-int.h +++ b/src/gui/dialogs/about-int.h @@ -1,6 +1,6 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * about.h - définitions internes pour la boîte de dialogue d'information sur le programme + * about-int.h - définitions internes pour la boîte de dialogue d'information sur le programme * * Copyright (C) 2024 Cyrille Bagard * @@ -28,7 +28,7 @@ #include <stdbool.h> -#include "about-int.h" +#include "about.h" diff --git a/src/gui/dialogs/about.c b/src/gui/dialogs/about.c index 5c938ad..956918b 100644 --- a/src/gui/dialogs/about.c +++ b/src/gui/dialogs/about.c @@ -207,8 +207,8 @@ GtkWindow *gtk_app_about_dialog_new(GtkWindow *parent) /****************************************************************************** * * -* Paramètres : dialog = boîte de dialogue à initialiser pleinement. * -* content = contenu binaire à exposer de façon brute. * +* Paramètres : dialog = boîte de dialogue à initialiser pleinement. * +* parent = fenêtre parente à surpasser. * * * * Description : Met en place la fenêtre d'informations sur le logiciel. * * * diff --git a/src/gui/dialogs/about.ui b/src/gui/dialogs/about.ui index 892b468..989e53b 100644 --- a/src/gui/dialogs/about.ui +++ b/src/gui/dialogs/about.ui @@ -94,7 +94,7 @@ <object class="GtkLabel"> <property name="margin-top">22</property> <property name="margin-bottom">10</property> - <property name="label" translatable="yes"><span fgcolor='white'>Copyright (C) 2008-2024 Cyrille Bagard</span></property> + <property name="label" translatable="yes"><span fgcolor='white'>Copyright (C) 2008-2025 Cyrille Bagard</span></property> <property name="use-markup">True</property> </object> </child> diff --git a/src/gui/dialogs/gresource.xml b/src/gui/dialogs/gresource.xml index b9bc7e2..169f440 100644 --- a/src/gui/dialogs/gresource.xml +++ b/src/gui/dialogs/gresource.xml @@ -2,15 +2,7 @@ <gresources> <gresource prefix="/re/chrysalide/framework/gui/dialogs"> <file compressed="true">about.ui</file> - <file compressed="true">bookmark.ui</file> - <file compressed="true">export_graph.ui</file> - <file compressed="true">identity.ui</file> - <file compressed="true">loading.ui</file> <file compressed="true">preferences.ui</file> - <file compressed="true">prefs_fgraph.ui</file> - <file compressed="true">prefs_labels.ui</file> - <file compressed="true">snapshots.ui</file> - <file compressed="true">storage.ui</file> </gresource> <gresource prefix="/org/chrysalide/gui/dialogs/about"> <file compressed="true" alias="chrysalide-full.png">../../../pixmaps/chrysalide-full.png</file> diff --git a/src/gui/dialogs/preferences-int.h b/src/gui/dialogs/preferences-int.h new file mode 100644 index 0000000..806f5f6 --- /dev/null +++ b/src/gui/dialogs/preferences-int.h @@ -0,0 +1,62 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * preferences-int.h - définitions internes pour la boîte de dialogue d'édition des préférences de l'utilisateur + * + * Copyright (C) 2025 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 _GUI_DIALOGS_PREFERENCES_INT_H +#define _GUI_DIALOGS_PREFERENCES_INT_H + + +#include <stdbool.h> + + +#include "preferences.h" + + + +/* Fenêtre d'édition générale de la configuration (instance) */ +struct _GtkPreferencesDialog +{ + GtkWindow parent; /* A laisser en premier */ + + GtkLabel *side_title; /* Titre principal */ + GtkScrolledWindow *side_content; /* Liste des sections */ + GtkLabel *main_title; /* Titre de section */ + GtkScrolledWindow *main_content; /* Page de configuration */ + + GHashTable *navigations; /* Liste de sections en place */ + +}; + +/* Fenêtre d'édition générale de la configuration (classe) */ +struct _GtkPreferencesDialogClass +{ + GtkWindowClass parent; /* A laisser en premier */ + +}; + + +/* Met en place la boîte de dialogue pour les préférences. */ +bool gtk_preferences_dialog_create(GtkPreferencesDialog *, GtkWindow *); + + + +#endif /* _GUI_DIALOGS_PREFERENCES_INT_H */ diff --git a/src/gui/dialogs/preferences.c b/src/gui/dialogs/preferences.c index 4a3fb7c..68e7fd9 100644 --- a/src/gui/dialogs/preferences.c +++ b/src/gui/dialogs/preferences.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * preferences.c - (re)définition de l'identité de l'utilisateur + * preferences.c - boîte de dialogue d'édition des préférences de l'utilisateur * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -24,129 +24,104 @@ #include "preferences.h" -#include <i18n.h> +#include <assert.h> +#include <math.h> +#include <stdio.h> +#include <gdk/gdkkeysyms.h> -#include "prefs_fgraph.h" -#include "prefs_labels.h" -#include "../../core/params.h" -#include "../../gtkext/easygtk.h" +#include <config.h> +#include "preferences-int.h" +#include "prefs/security.h" +#include "../../common/cpp.h" +#include "../../gtkext/tweak.h" +#include "../../plugins/pglist.h" +#include "../../plugins/tweakable.h" -/* Constructeur de panneau de paramétrage */ -typedef GtkWidget * (* prefs_panel_creation_cb) (GtkBuilder **); -/* Chargement de la configuration */ -typedef void (* prefs_config_update_cb) (GtkBuilder *, GGenConfig *); -/* Description d'un noeud de préférences */ -typedef struct _pref_node_desc_t -{ - prefs_panel_creation_cb create; /* Procédure de création */ - prefs_config_update_cb load; /* Procédure de chargement */ - prefs_config_update_cb store; /* Procédure d'enregistrement */ - - const char *name; /* Désignation interne */ - const char *title; /* Désignation humaine */ - - GtkBuilder *builder; /* Constructeur GTK */ - GtkWidget *panel; /* Panneau GTK */ - - struct _pref_node_desc_t *children; /* Sous-arborescence */ - -} pref_node_desc_t; - - -#define PREF_NODE_NULL_ENTRY { .title = NULL } +/* --------------------------- BASES DE BOITE DE DIALOGUE --------------------------- */ -/* Liste des paramétrages à afficher */ -static pref_node_desc_t _prefs_nodes[] = { +/* Procède à l'initialisation de la fenêtre des paramètres. */ +static void gtk_preferences_dialog_class_init(GtkPreferencesDialogClass *); - { - .create = NULL, - - .title = "Analysis", +/* Procède à l'initialisation de la fenêtre des paramètres. */ +static void gtk_preferences_dialog_init(GtkPreferencesDialog *); - .children = (pref_node_desc_t []){ +/* Supprime toutes les références externes. */ +static void gtk_preferences_dialog_dispose(GObject *); - { - .create = create_labels_preferences, - .load = load_labels_configuration, - .store = store_labels_configuration, +/* Procède à la libération totale de la mémoire. */ +static void gtk_preferences_dialog_finalize(GObject *); - .name = "labels", - .title = "Colored labels", +/* Fournit la liste de section désignée par un nom. */ +static GtkListBox *gtk_preferences_dialog_get_navigation(GtkPreferencesDialog *, const char *, bool); - }, +/* Réagit à un changement de sélection dans les sections. */ +static void gtk_preferences_dialog_on_row_selected(GtkListBox *, GtkListBoxRow *, GtkPreferencesDialog *); - PREF_NODE_NULL_ENTRY - } - }, +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ - { - .create = NULL, - .title = "Editor", - .children = (pref_node_desc_t []){ - { - .create = create_fgraph_preferences, - .load = load_fgraph_configuration, - .store = store_fgraph_configuration, - .name = "fgraph", - .title = "Function graph", +/* ---------------------------------------------------------------------------------- */ +/* BASES DE BOITE DE DIALOGUE */ +/* ---------------------------------------------------------------------------------- */ - }, - - PREF_NODE_NULL_ENTRY - - } - }, +/* Détermine le type du composant d'affichage générique. */ +G_DEFINE_TYPE(GtkPreferencesDialog, gtk_preferences_dialog, GTK_TYPE_WINDOW); - PREF_NODE_NULL_ENTRY - -}; +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Procède à l'initialisation de la fenêtre des paramètres. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ -/* Eléments de la liste de sections */ -typedef enum _PrefListItem +static void gtk_preferences_dialog_class_init(GtkPreferencesDialogClass *class) { - PLI_TITLE, /* Etiquette de la section */ - PLI_PANEL, /* Panneau graphique associé */ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ -} PrefListItem; + object = G_OBJECT_CLASS(class); + object->dispose = gtk_preferences_dialog_dispose; + object->finalize = gtk_preferences_dialog_finalize; -/* Ajoute un panneau de paramétrage à la boîte de dialogue. */ -static void add_preferences_node(GtkTreeStore *, GtkTreeIter *, GGenConfig *, GtkStack *, pref_node_desc_t *); + widget = GTK_WIDGET_CLASS(class); -/* Affiche le panneau correspondant au noeud sélectionné. */ -static void on_prefs_selection_changed(GtkTreeSelection *, GtkBuilder *); + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/dialogs/preferences.ui"); -/* Lance la sauvegarde d'éléments de paramétrage. */ -static void store_preferences_node(GGenConfig *, pref_node_desc_t *); + gtk_widget_class_bind_template_child(widget, GtkPreferencesDialog, side_title); + gtk_widget_class_bind_template_child(widget, GtkPreferencesDialog, side_content); + gtk_widget_class_bind_template_child(widget, GtkPreferencesDialog, main_title); + gtk_widget_class_bind_template_child(widget, GtkPreferencesDialog, main_content); -/* Sauvegarde l'ensemble des paramètres de configuration. */ -static void on_prefs_apply_button_clicked(GtkButton *, GtkBuilder *); + /* Active une action native (cf. https://docs.gtk.org/gtk4/class.Window.html#actions) */ + gtk_widget_class_add_binding_action(widget, GDK_KEY_Escape, 0 /* GDK 4.14 : GDK_NO_MODIFIER_MASK */, "window.close", NULL); +} /****************************************************************************** * * -* Paramètres : store = arborescence des sections à compléter. * -* parent = point d'insertion du parent. * -* config = configuration globale à charger. * -* stack = pile de composants GTK à constituer. * -* node = noeud de description courant à traiter. * +* Paramètres : dialog = composant GTK à initialiser. * * * -* Description : Ajoute un panneau de paramétrage à la boîte de dialogue. * +* Description : Procède à l'initialisation de la fenêtre des paramètres. * * * * Retour : - * * * @@ -154,95 +129,150 @@ static void on_prefs_apply_button_clicked(GtkButton *, GtkBuilder *); * * ******************************************************************************/ -static void add_preferences_node(GtkTreeStore *store, GtkTreeIter *parent, GGenConfig *config, GtkStack *stack, pref_node_desc_t *node) +static void gtk_preferences_dialog_init(GtkPreferencesDialog *dialog) { - GtkTreeIter iter; /* Point d'insertion */ - pref_node_desc_t *child; /* Sous-élément à traiter */ + size_t i; /* Boucle de parcours */ + const tweak_info_t *info; /* Informations à considérer */ + GtkListBox *navigation; /* Liste de sections à afficher*/ + GtkTweakSection *section; /* Nouvelle section à présenter*/ + tweak_info_t *dyn_infos; /* Informations supplémentaires*/ + size_t dyn_count; /* Quantité de ces informations*/ + + tweak_info_t infos[] = { + TWEAK_SIMPLE_DEF("root", "Basics", + "security-high-symbolic", "security", "Security", GTK_TYPE_SECURITY_TWEAK_PANEL), + }; + + gtk_widget_init_template(GTK_WIDGET(dialog)); - if (node->create == NULL) + dialog->navigations = g_hash_table_new_full(g_str_hash, g_str_equal, free, g_object_unref); + + /* Chargement des sections fixes */ + + for (i = 0; i < ARRAY_SIZE(infos); i++) { - node->builder = NULL; - node->panel = NULL; + info = &infos[i]; + + navigation = gtk_preferences_dialog_get_navigation(dialog, info->parent, true); + assert(navigation != NULL); + + section = gtk_tweak_section_new(info); + + gtk_list_box_append(navigation, GTK_WIDGET(section)); + } - else + + /* Chargement des sections dynamiques */ + + dyn_infos = get_tweakable_plugins_info(&dyn_count); + + for (i = 0; i < dyn_count; i++) { - node->panel = node->create(&node->builder); + info = &dyn_infos[i]; - node->load(node->builder, config); + navigation = gtk_preferences_dialog_get_navigation(dialog, info->parent, true); + assert(navigation != NULL); - gtk_widget_show(node->panel); + section = gtk_tweak_section_new(info); - gtk_stack_add_named(stack, node->panel, node->name); + gtk_list_box_append(navigation, GTK_WIDGET(section)); } - gtk_tree_store_append(store, &iter, parent); + if (dyn_infos != NULL) + free(dyn_infos); + + /* Affichage de la liste racine */ - gtk_tree_store_set(store, &iter, - PLI_TITLE, _(node->title), - PLI_PANEL, node->panel, - -1); + navigation = gtk_preferences_dialog_get_navigation(dialog, "root", false); + assert(navigation != NULL); - if (node->children != NULL) - for (child = node->children; child->title != NULL; child++) - add_preferences_node(store, &iter, config, stack, child); + g_signal_connect(navigation, "row-selected", + G_CALLBACK(gtk_preferences_dialog_on_row_selected), dialog); + + gtk_scrolled_window_set_child(dialog->side_content, GTK_WIDGET(navigation)); + + unref_object(navigation); } /****************************************************************************** * * -* Paramètres : parent = fenêtre principale de l'éditeur. * -* outb = constructeur à détruire après usage. [OUT] * +* Paramètres : object = instance d'objet GLib à traiter. * * * -* Description : Propose une boîte de dialogue pour la configuration générale.* +* Description : Supprime toutes les références externes. * * * -* Retour : Adresse de la fenêtre mise en place. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -GtkWidget *create_preferences_dialog(GtkWindow *parent, GtkBuilder **outb) +static void gtk_preferences_dialog_dispose(GObject *object) { - GtkWidget *result; /* Fenêtre à renvoyer */ - GtkBuilder *builder; /* Constructeur utilisé */ - GGenConfig *config; /* Configuration globale */ - GtkStack *stack; /* Pile à mettre à jour */ - GtkTreeStore *store; /* Arborescence des sections */ - pref_node_desc_t *iter; /* Boucle de parcours */ - GtkTreeView *treeview; /* Arborescence principale */ + GtkPreferencesDialog *dialog; /* Version spécialisée */ + + dialog = GTK_PREFERENCES_DIALOG(object); - builder = gtk_builder_new_from_resource("/org/chrysalide/gui/dialogs/preferences.ui"); - *outb = builder; + if (dialog->navigations != NULL) + { + /** + * Cf. documentation de g_hash_table_new_full(). + */ + g_hash_table_remove_all(dialog->navigations); - result = GTK_WIDGET(gtk_builder_get_object(builder, "window")); + g_hash_table_unref(dialog->navigations); + dialog->navigations = NULL; - gtk_window_set_transient_for(GTK_WINDOW(result), parent); + } - /* Intégration des différentes sections */ + gtk_widget_dispose_template(GTK_WIDGET(dialog), GTK_TYPE_PREFERENCES_DIALOG); + + G_OBJECT_CLASS(gtk_preferences_dialog_parent_class)->dispose(object); + +} - config = get_main_configuration(); - stack = GTK_STACK(gtk_builder_get_object(builder, "stack")); +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ - store = GTK_TREE_STORE(gtk_builder_get_object(builder, "pref_list")); +static void gtk_preferences_dialog_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_preferences_dialog_parent_class)->finalize(object); - for (iter = _prefs_nodes; iter->title != NULL; iter++) - add_preferences_node(store, NULL, config, stack, iter); +} - treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview")); - gtk_tree_view_expand_all(treeview); +/****************************************************************************** +* * +* Paramètres : parent = fenêtre parente à surpasser. * +* * +* Description : Construit une boîte de dialogue pour les préférences. * +* * +* Retour : Adresse de la fenêtre mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ - /* Connexion des signaux */ +GtkWindow *gtk_preferences_dialog_new(GtkWindow *parent) +{ + GtkWindow *result; /* Boite de dialogue à renvoyer*/ - gtk_builder_add_callback_symbols(builder, - BUILDER_CALLBACK(on_prefs_selection_changed), - BUILDER_CALLBACK(on_prefs_apply_button_clicked), - NULL); + result = g_object_new(GTK_TYPE_PREFERENCES_DIALOG, NULL); - gtk_builder_connect_signals(builder, builder); + if (!gtk_preferences_dialog_create(GTK_PREFERENCES_DIALOG(result), parent)) + g_clear_object(&result); return result; @@ -251,79 +281,85 @@ GtkWidget *create_preferences_dialog(GtkWindow *parent, GtkBuilder **outb) /****************************************************************************** * * -* Paramètres : selection = sélection courante de l'arborescence des options.* -* builder = constructeur GTK avec toutes les références. * +* Paramètres : dialog = boîte de dialogue à initialiser pleinement. * +* parent = fenêtre parente à surpasser. * * * -* Description : Affiche le panneau correspondant au noeud sélectionné. * +* Description : Met en place la boîte de dialogue pour les préférences. * * * -* Retour : - * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void on_prefs_selection_changed(GtkTreeSelection *selection, GtkBuilder *builder) +bool gtk_preferences_dialog_create(GtkPreferencesDialog *dialog, GtkWindow *parent) { - GtkTreeModel *model; /* Gestionnaire de données */ - GtkTreeIter iter; /* Position courante */ - GtkWidget *panel; /* Panneau à mettre en avant */ - GtkStack *stack; /* Pile à mettre à jour */ - - if (gtk_tree_selection_get_selected(selection, &model, &iter)) - { - gtk_tree_model_get(model, &iter, PLI_PANEL, &panel, -1); + bool result; /* Bilan à retourner */ - stack = GTK_STACK(gtk_builder_get_object(builder, "stack")); + result = true; - if (panel == NULL) - gtk_stack_set_visible_child_name(stack, "empty"); - - else - { - gtk_stack_set_visible_child(stack, panel); + gtk_window_set_transient_for(GTK_WINDOW(dialog), parent); - g_object_unref(G_OBJECT(panel)); - - } - - } + return result; } /****************************************************************************** * * -* Paramètres : config = configuration globale à actualiser. * -* node = noeud de description courant à traiter. * +* Paramètres : dialog = boîte de dialogue à initialiser pleinement. * +* key = désignation de la liste à fournir. * +* create = autorisation d'une création si besoin est. * * * -* Description : Lance la sauvegarde d'éléments de paramétrage. * +* Description : Fournit la liste de section désignée par un nom. * * * -* Retour : - * +* Retour : Composant graphique à utiliser. * * * * Remarques : - * * * ******************************************************************************/ -static void store_preferences_node(GGenConfig *config, pref_node_desc_t *node) +static GtkListBox *gtk_preferences_dialog_get_navigation(GtkPreferencesDialog *dialog, const char *key, bool create) { - pref_node_desc_t *child; /* Sous-élément à traiter */ + GtkListBox *result; /* Instance à retourner */ +#ifndef NDEBUG + gboolean status; /* Bilan d'une insertion */ +#endif - if (node->create != NULL) - node->store(node->builder, config); + result = g_hash_table_lookup(dialog->navigations, key); - if (node->children != NULL) - for (child = node->children; child->title != NULL; child++) - store_preferences_node(config, child); + if (result == NULL && create) + { + result = GTK_LIST_BOX(gtk_list_box_new()); + g_object_ref_sink(G_OBJECT(result)); + + gtk_list_box_set_selection_mode(result, GTK_SELECTION_BROWSE); + gtk_widget_add_css_class(GTK_WIDGET(result), "navigation-sidebar"); + +#ifndef NDEBUG + status = g_hash_table_insert(dialog->navigations, strdup(key), result); + assert(status); +#else + g_hash_table_insert(dialog->navigations, key, result); +#endif + + } + + if (result != NULL) + ref_object(result); + + return result; } /****************************************************************************** * * -* Paramètres : button = bouton GTK à l'origine de l'opération. * -* builder = constructeur GTK avec toutes les références. * +* Paramètres : navigation = liste concernée par l'événement. * +* selected = élément sélectionné (voire NULL). * +* dialog = boîte de dialogue à initialiser pleinement. * * * -* Description : Sauvegarde l'ensemble des paramètres de configuration. * +* Description : Réagit à un changement de sélection dans les sections. * * * * Retour : - * * * @@ -331,14 +367,41 @@ static void store_preferences_node(GGenConfig *config, pref_node_desc_t *node) * * ******************************************************************************/ -static void on_prefs_apply_button_clicked(GtkButton *button, GtkBuilder *builder) +static void gtk_preferences_dialog_on_row_selected(GtkListBox *navigation, GtkListBoxRow *selected, GtkPreferencesDialog *dialog) { - GGenConfig *config; /* Configuration globale */ - pref_node_desc_t *iter; /* Boucle de parcours */ + GtkTweakSection *section; /* Nature réelle sélectionnée */ + GType type; /* Type de panneau de config. */ + GtkWidget *panel; /* Nouveau panneau à présenter */ + + if (selected != NULL) + { + if (!gtk_tweak_section_has_sub_section(GTK_TWEAK_SECTION(selected))) + { + section = GTK_TWEAK_SECTION(selected); + + gtk_label_set_text(dialog->main_title, gtk_tweak_section_get_label(section)); + + type = gtk_tweak_section_get_panel(section); + panel = g_object_new(type, NULL); - config = get_main_configuration(); + gtk_scrolled_window_set_child(dialog->main_content, panel); - for (iter = _prefs_nodes; iter->title != NULL; iter++) - store_preferences_node(config, iter); + } + + else + { + + // TODO + + } + + } } + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + diff --git a/src/gui/dialogs/preferences.h b/src/gui/dialogs/preferences.h index d04af72..e291ea8 100644 --- a/src/gui/dialogs/preferences.h +++ b/src/gui/dialogs/preferences.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * preferences.h - prototypes pour la (re)définition de l'identité de l'utilisateur + * preferences.h - prototypes pour la boîte de dialogue d'édition des préférences de l'utilisateur * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -28,9 +28,17 @@ #include <gtk/gtk.h> +#include "../../glibext/helpers.h" -/* Propose une boîte de dialogue pour la configuration générale. */ -GtkWidget *create_preferences_dialog(GtkWindow *, GtkBuilder **); + + +#define GTK_TYPE_PREFERENCES_DIALOG (gtk_preferences_dialog_get_type()) + +DECLARE_GTYPE(GtkPreferencesDialog, gtk_preferences_dialog, GTK, PREFERENCES_DIALOG); + + +/* Construit une boîte de dialogue pour les préférences. */ +GtkWindow *gtk_preferences_dialog_new(GtkWindow *); diff --git a/src/gui/dialogs/preferences.ui b/src/gui/dialogs/preferences.ui index 251ef46..950242a 100644 --- a/src/gui/dialogs/preferences.ui +++ b/src/gui/dialogs/preferences.ui @@ -1,156 +1,111 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.21.0 --> <interface> - <requires lib="gtk+" version="3.20"/> - <object class="GtkTreeStore" id="pref_list"> - <columns> - <!-- column-name title --> - <column type="gchararray"/> - <!-- column-name panel --> - <column type="GObject"/> - </columns> - </object> - <object class="GtkDialog" id="window"> - <property name="can_focus">False</property> - <property name="title" translatable="yes">General preferences</property> - <property name="modal">True</property> - <property name="default_width">800</property> - <property name="default_height">500</property> - <property name="type_hint">dialog</property> - <child internal-child="vbox"> - <object class="GtkBox"> - <property name="can_focus">False</property> - <property name="orientation">vertical</property> - <property name="spacing">2</property> - <child internal-child="action_area"> - <object class="GtkButtonBox"> - <property name="can_focus">False</property> - <property name="margin_left">8</property> - <property name="margin_right">8</property> - <property name="margin_top">8</property> - <property name="margin_bottom">8</property> - <property name="layout_style">end</property> - <child> - <object class="GtkButton" id="button1"> - <property name="label">gtk-cancel</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="button2"> - <property name="label">gtk-apply</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - <signal name="clicked" handler="on_prefs_apply_button_clicked" swapped="no"/> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> + + <template class="GtkPreferencesDialog" parent="GtkWindow"> + + <property name="default-width">980</property> + <property name="default-height">640</property> + <property name="modal">true</property> + <property name="resizable">true</property> + + <child type="titlebar"> + <object class="GtkBox"> + <child> + <object class="GtkHeaderBar" id="sidebar-header"> + <property name="decoration-layout">:</property> + + <property name="title-widget"> + <object class="GtkLabel" id="side_title"> + <property name="label" translatable="yes">Parameters</property> + <property name="single-line-mode">True</property> + <property name="ellipsize">end</property> + <property name="width-chars">5</property> + <style> + <class name="title"/> + </style> + </object> + </property> + + <child type="start"> + <object class="GtkToggleButton"> + <property name="icon-name">edit-find-symbolic</property> + <property name="valign">center</property> + </object> + </child> + </object> + </child> + + <!-- Séparation centrale --> + <child> + <object class="GtkSeparator"> + <property name="orientation">vertical</property> + <property name="css-name">sidebar</property> + </object> + </child> + + <child> + <object class="GtkHeaderBar" id="main-header"> + <property name="hexpand">1</property> + + <property name="title-widget"> + <object class="GtkLabel" id="main_title"> + <property name="label" translatable="yes"></property> + <property name="single-line-mode">True</property> + <property name="ellipsize">end</property> + <property name="width-chars">5</property> + <style> + <class name="title"/> + </style> + </object> + </property> + + </object> + </child> + </object> </child> + <child> - <object class="GtkPaned"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="position">200</property> - <property name="wide_handle">True</property> - <child> - <object class="GtkScrolledWindow"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="margin_left">8</property> - <property name="margin_right">8</property> - <property name="margin_top">8</property> - <property name="shadow_type">in</property> + <object class="GtkBox"> <child> - <object class="GtkTreeView" id="treeview"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="model">pref_list</property> - <property name="headers_visible">False</property> - <child internal-child="selection"> - <object class="GtkTreeSelection"> - <signal name="changed" handler="on_prefs_selection_changed" swapped="no"/> - </object> - </child> - <child> - <object class="GtkTreeViewColumn"> - <property name="title" translatable="yes">column</property> - <child> - <object class="GtkCellRendererText"/> - <attributes> - <attribute name="text">0</attribute> - </attributes> - </child> - </object> - </child> - </object> + <object class="GtkScrolledWindow" id="side_content"> + <property name="hexpand">false</property> + <property name="vexpand">true</property> + <property name="hscrollbar-policy">never</property> + <property name="vscrollbar-policy">automatic</property> + </object> + </child> + + <!-- Séparation centrale --> + <child> + <object class="GtkSeparator"> + <property name="orientation">vertical</property> + <property name="css-name">sidebar</property> + </object> </child> - </object> - <packing> - <property name="resize">False</property> - <property name="shrink">True</property> - </packing> - </child> - <child> - <object class="GtkStack" id="stack"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="margin_left">8</property> - <property name="margin_right">8</property> - <property name="margin_top">8</property> + <child> - <object class="GtkBox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="orientation">vertical</property> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="name">empty</property> - </packing> + <object class="GtkScrolledWindow" id="main_content"> + <property name="hexpand">1</property> + <property name="vexpand">1</property> + <property name="hscrollbar-policy">never</property> + <property name="vscrollbar-policy">automatic</property> + </object> </child> - </object> - <packing> - <property name="resize">True</property> - <property name="shrink">True</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> + </object> </child> - </object> - </child> - <action-widgets> - <action-widget response="-6">button1</action-widget> - <action-widget response="-10">button2</action-widget> - </action-widgets> - <child> - <placeholder/> - </child> - </object> + + <object class="GtkSizeGroup"> + <widgets> + <widget name="sidebar-header"/> + <widget name="side_content"/> + </widgets> + </object> + <object class="GtkSizeGroup"> + <widgets> + <widget name="main-header"/> + <widget name="main_content"/> + </widgets> + </object> + + </template> </interface> diff --git a/src/gui/dialogs/prefs/Makefile.am b/src/gui/dialogs/prefs/Makefile.am new file mode 100644 index 0000000..fa1af96 --- /dev/null +++ b/src/gui/dialogs/prefs/Makefile.am @@ -0,0 +1,31 @@ + +BUILT_SOURCES = resources.h resources.c + +noinst_LTLIBRARIES = libguidialogsprefs.la + +UI_FILES = \ + security.ui + +libguidialogsprefs_la_SOURCES = \ + resources.h resources.c \ + security-int.h \ + security.h security.c + +libguidialogsprefs_la_CFLAGS = $(LIBGTK4_CFLAGS) + + +devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) + +dev_HEADERS = $(libguidialogsprefs_la_SOURCES:%c=) + + +resources.c: gresource.xml $(UI_FILES) + glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-source --c-name gui_dialogs_prefs gresource.xml + +resources.h: gresource.xml + glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-header --c-name gui_dialogs_prefs gresource.xml + + +CLEANFILES = resources.h resources.c + +EXTRA_DIST = gresource.xml $(UI_FILES) diff --git a/src/gui/dialogs/prefs/gresource.xml b/src/gui/dialogs/prefs/gresource.xml new file mode 100644 index 0000000..7b18143 --- /dev/null +++ b/src/gui/dialogs/prefs/gresource.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<gresources> + <gresource prefix="/re/chrysalide/framework/gui/dialogs/prefs"> + <file compressed="true">security.ui</file> + </gresource> +</gresources> diff --git a/src/gui/dialogs/prefs/security-int.h b/src/gui/dialogs/prefs/security-int.h new file mode 100644 index 0000000..be7867c --- /dev/null +++ b/src/gui/dialogs/prefs/security-int.h @@ -0,0 +1,64 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * security-int.h - définitions internes pour la configuration des paramètres liés à la sécurité + * + * Copyright (C) 2025 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 _GUI_DIALOGS_PREFS_SECURITY_INT_H +#define _GUI_DIALOGS_PREFS_SECURITY_INT_H + + +#include "security.h" +#include "../../../glibext/secstorage.h" + + + +/* Composant d'édition des paramètres de sécurité (instance) */ +struct _GtkSecurityTweakPanel +{ + GtkBox parent; /* A laisser en premier */ + + /* Stockage sécurisé */ + + GSecretStorage *storage; /* Stockage des secrets */ + + GtkWidget *current_primary_passwd; /* Mot de passe courant */ + + GtkWidget *new_primary_passwd; /* Nouveau mot de passe */ + GtkWidget *new_primary_passwd_lbl; /* Etiquette associée */ + GtkWidget *new_primary_passwd_2; /* Nouveau mot de passe (#2) */ + GtkWidget *new_primary_passwd_2_lbl; /* Etiquette associée (#2) */ + + GtkWidget *set_password; /* Bouton de définition de mdp */ + GtkWidget *change_password; /* Bouton de changement de mdp */ + GtkWidget *remove_password; /* Bouton de suppression de mdp*/ + +}; + +/* Composant d'édition des paramètres de sécurité (classe) */ +struct _GtkSecurityTweakPanelClass +{ + GtkBoxClass parent; /* A laisser en premier */ + +}; + + + +#endif /* _GUI_DIALOGS_PREFS_SECURITY_INT_H */ diff --git a/src/gui/dialogs/prefs/security.c b/src/gui/dialogs/prefs/security.c new file mode 100644 index 0000000..2b82339 --- /dev/null +++ b/src/gui/dialogs/prefs/security.c @@ -0,0 +1,400 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * security.c - configuration des paramètres liés à la sécurité + * + * Copyright (C) 2025 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/>. + */ + + +#include "security.h" + + +#include <i18n.h> + + +#include "security-int.h" +#include "../../../core/global.h" +#include "../../../core/logs.h" +#include "../../../gtkext/helpers.h" + + + +/* ---------------------- GESTION DE L'ENSEMBLE DES PARAMETRES ---------------------- */ + + +/* Procède à l'initialisation de classe des configurations. */ +static void gtk_security_tweak_panel_class_init(GtkSecurityTweakPanelClass *); + +/* Procède à l'initialisation des configurations de sécurité. */ +static void gtk_security_tweak_panel_init(GtkSecurityTweakPanel *); + +/* Supprime toutes les références externes. */ +static void gtk_security_tweak_panel_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_security_tweak_panel_finalize(GObject *); + + + +/* ---------------------- CONSERVATION SECURISEE DE PARAMETRES ---------------------- */ + + +/* Initalise la partie relative au stockage sécurisé. */ +static void gtk_security_tweak_panel_init_secret_storage(GtkSecurityTweakPanel *); + +/* Supprime toutes les références externes. */ +static void gtk_security_tweak_panel_dispose_secret_storage(GtkSecurityTweakPanel *); + +/* Note le changement de verrouillage du stockage sécurisé. */ +static void gtk_security_tweak_panel_on_secret_storage_lock_update(GSecretStorage *, GtkSecurityTweakPanel *); + +/* Réagit à un changement dans l'édition d'un mot de passe. */ +static void gtk_security_tweak_panel_on_new_passwords_changed(GtkEditable *, GtkSecurityTweakPanel *); + +/* Réagit à une demande de création de mot de passe. */ +static void gtk_security_tweak_panel_on_set_password_clicked(GtkButton *, GtkSecurityTweakPanel *); + +/* Réagit à une demande de changement de mot de passe. */ +static void gtk_security_tweak_panel_on_change_password_clicked(GtkButton *, GtkSecurityTweakPanel *); + +/* Réagit à une demande de suppression de mot de passe. */ +static void gtk_security_tweak_panel_on_remove_password_clicked(GtkButton *, GtkSecurityTweakPanel *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GESTION DE L'ENSEMBLE DES PARAMETRES */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type du composant d'édition des paramètres de sécurité. */ +G_DEFINE_TYPE(GtkSecurityTweakPanel, gtk_security_tweak_panel, GTK_TYPE_BOX); + + +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Procède à l'initialisation de classe des configurations. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_class_init(GtkSecurityTweakPanelClass *class) +{ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = gtk_security_tweak_panel_dispose; + object->finalize = gtk_security_tweak_panel_finalize; + + widget = GTK_WIDGET_CLASS(class); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/dialogs/prefs/security.ui"); + + /* Stockage sécurisé */ + + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_security_tweak_panel_on_new_passwords_changed)); + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_security_tweak_panel_on_set_password_clicked)); + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_security_tweak_panel_on_change_password_clicked)); + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_security_tweak_panel_on_remove_password_clicked)); + + gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, current_primary_passwd); + gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, new_primary_passwd); + gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, new_primary_passwd_lbl); + gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, new_primary_passwd_2); + gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, new_primary_passwd_2_lbl); + gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, set_password); + gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, change_password); + gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, remove_password); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = composant GTK à initialiser. * +* * +* Description : Procède à l'initialisation des configurations de sécurité. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_init(GtkSecurityTweakPanel *panel) +{ + gtk_widget_init_template(GTK_WIDGET(panel)); + + gtk_security_tweak_panel_init_secret_storage(panel); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_dispose(GObject *object) +{ + GtkSecurityTweakPanel *panel; /* Version spécialisée */ + + panel = GTK_SECURITY_TWEAK_PANEL(object); + + gtk_security_tweak_panel_dispose_secret_storage(panel); + + gtk_widget_dispose_template(GTK_WIDGET(panel), GTK_TYPE_SECURITY_TWEAK_PANEL); + + G_OBJECT_CLASS(gtk_security_tweak_panel_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_security_tweak_panel_parent_class)->finalize(object); + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONSERVATION SECURISEE DE PARAMETRES */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : panel = composant GTK à initialiser. * +* * +* Description : Initalise la partie relative au stockage sécurisé. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_init_secret_storage(GtkSecurityTweakPanel *panel) +{ + panel->storage = get_secret_storage(); + + g_signal_connect(panel->storage, "lock-update", + G_CALLBACK(gtk_security_tweak_panel_on_secret_storage_lock_update), panel); + + gtk_security_tweak_panel_on_secret_storage_lock_update(panel->storage, panel); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_dispose_secret_storage(GtkSecurityTweakPanel *panel) +{ + if (panel->storage != NULL) + g_signal_handlers_disconnect_by_func(panel->storage, + gtk_security_tweak_panel_on_secret_storage_lock_update, panel); + + g_clear_object(&panel->storage); + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gardien des secrets impliqué. * +* panel = support de la partie relative au stockage sécurisé.* +* * +* Description : Note le changement de verrouillage du stockage sécurisé. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_on_secret_storage_lock_update(GSecretStorage *storage, GtkSecurityTweakPanel *panel) +{ + bool has_key; /* Existence d'une clef ? */ + + has_key = g_secret_storage_has_key(panel->storage); + + gtk_widget_set_visible(panel->new_primary_passwd, has_key); + gtk_widget_set_visible(panel->new_primary_passwd_lbl, has_key); + + gtk_widget_set_visible(panel->new_primary_passwd_2, has_key); + gtk_widget_set_visible(panel->new_primary_passwd_2_lbl, has_key); + + gtk_widget_set_visible(panel->set_password, !has_key); + gtk_widget_set_visible(panel->change_password, has_key); + gtk_widget_set_visible(panel->remove_password, has_key); + +} + + +/****************************************************************************** +* * +* Paramètres : editable = zone de saisie bouton GTK concerné par l'appel. * +* panel = support de la partie liée au stockage sécurisé. * +* * +* Description : Réagit à un changement dans l'édition d'un mot de passe. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_on_new_passwords_changed(GtkEditable *editable, GtkSecurityTweakPanel *panel) +{ + const char *new_passwd; /* Nouveau mot de passe #1 */ + const char *new_passwd_2; /* Nouveau mot de passe #2 */ + bool status; /* Bilan de l'opération */ + + new_passwd = gtk_editable_get_text(GTK_EDITABLE(panel->new_primary_passwd)); + + new_passwd_2 = gtk_editable_get_text(GTK_EDITABLE(panel->new_primary_passwd_2)); + + status = (strcmp(new_passwd, new_passwd_2) == 0); + + gtk_widget_set_sensitive(panel->change_password, status); + +} + + +/****************************************************************************** +* * +* Paramètres : button = bouton GTK concerné par l'appel. * +* panel = support de la partie relative au stockage sécurisé. * +* * +* Description : Réagit à une demande de création de mot de passe. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_on_set_password_clicked(GtkButton *button, GtkSecurityTweakPanel *panel) +{ + const char *passwd; /* Mot de passe à considérer */ + bool status; /* Bilan de l'opération */ + + passwd = gtk_editable_get_text(GTK_EDITABLE(panel->current_primary_passwd)); + + status = g_secret_storage_set_password(panel->storage, passwd); + + log_variadic_message(LMT_INFO, _("Setting password for secret storage: %s"), + status ? _("success") : _("failed")); + +} + + +/****************************************************************************** +* * +* Paramètres : button = bouton GTK concerné par l'appel. * +* panel = support de la partie relative au stockage sécurisé. * +* * +* Description : Réagit à une demande de changement de mot de passe. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_on_change_password_clicked(GtkButton *button, GtkSecurityTweakPanel *panel) +{ + const char *old_passwd; /* Ancien mot de passe */ + const char *new_passwd; /* Nouveau mot de passe */ + bool status; /* Bilan de l'opération */ + + old_passwd = gtk_editable_get_text(GTK_EDITABLE(panel->current_primary_passwd)); + + new_passwd = gtk_editable_get_text(GTK_EDITABLE(panel->new_primary_passwd)); + + status = g_secret_storage_change_password(panel->storage, old_passwd, new_passwd); + + log_variadic_message(LMT_INFO, _("Changed password for secret storage: %s"), + status ? _("success") : _("failed")); + +} + + +/****************************************************************************** +* * +* Paramètres : button = bouton GTK concerné par l'appel. * +* panel = support de la partie relative au stockage sécurisé. * +* * +* Description : Réagit à une demande de suppression de mot de passe. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_security_tweak_panel_on_remove_password_clicked(GtkButton *button, GtkSecurityTweakPanel *panel) +{ + const char *passwd; /* Mot de passe à considérer */ + bool status; /* Bilan de l'opération */ + + passwd = gtk_editable_get_text(GTK_EDITABLE(panel->current_primary_passwd)); + + status = g_secret_storage_remove_password(panel->storage, passwd); + + log_variadic_message(LMT_INFO, _("Removed password for secret storage: %s"), + status ? _("success") : _("failed")); + +} diff --git a/src/gui/dialogs/prefs/security.h b/src/gui/dialogs/prefs/security.h new file mode 100644 index 0000000..206a123 --- /dev/null +++ b/src/gui/dialogs/prefs/security.h @@ -0,0 +1,41 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * security.h - prototypes pour la configuration des paramètres liés à la sécurité + * + * Copyright (C) 2025 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 _GUI_DIALOGS_PREFS_SECURITY_H +#define _GUI_DIALOGS_PREFS_SECURITY_H + + +#include <gtk/gtk.h> + + +#include "../../../glibext/helpers.h" + + + +#define GTK_TYPE_SECURITY_TWEAK_PANEL (gtk_security_tweak_panel_get_type()) + +DECLARE_GTYPE(GtkSecurityTweakPanel, gtk_security_tweak_panel, GTK, SECURITY_TWEAK_PANEL); + + + +#endif /* _GUI_DIALOGS_PREFS_SECURITY_H */ diff --git a/src/gui/dialogs/prefs/security.ui b/src/gui/dialogs/prefs/security.ui new file mode 100644 index 0000000..ccf2d39 --- /dev/null +++ b/src/gui/dialogs/prefs/security.ui @@ -0,0 +1,170 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + + <template class="GtkSecurityTweakPanel" parent="GtkBox"> + + <property name="orientation">vertical</property> + + <!-- Conservation de paramètres sécurisée --> + <child> + <object class="GtkGrid"> + <property name="margin-start">20</property> + <property name="margin-end">20</property> + <property name="margin-top">20</property> + <property name="margin-bottom">20</property> + <property name="row-spacing">10</property> + <property name="column-spacing">10</property> + + <child> + <object class="GtkLabel"> + <property name="label">Secret storage</property> + <property name="use-markup">true</property> + <property name="xalign">0</property> + <layout> + <property name="column">0</property> + <property name="row">0</property> + <property name="column-span">2</property> + </layout> + <style> + <class name="heading"/> + </style> + </object> + </child> + + <child> + <object class="GtkLabel"> + <property name="label">Configuration may handle sensitive data (such as passwords or API keys) which can be saved in plain text or stored encrypted using a Primary Password. + +If encryption is activated, entering the Primary Password will be asked on need once per session.</property> + <property name="wrap">true</property> + <property name="xalign">0</property> + <layout> + <property name="column">0</property> + <property name="row">1</property> + <property name="column-span">2</property> + </layout> + <style> + <class name="dim-label"/> + </style> + </object> + </child> + + <!-- Mot de passe courant --> + + <child> + <object class="GtkLabel"> + <property name="label">Enter current password:</property> + <property name="xalign">0</property> + <layout> + <property name="column">0</property> + <property name="row">2</property> + </layout> + </object> + </child> + + <child> + <object class="GtkPasswordEntry" id="current_primary_passwd"> + <property name="hexpand">true</property> + <property name="show-peek-icon">true</property> + <layout> + <property name="column">1</property> + <property name="row">2</property> + </layout> + </object> + </child> + + <!-- Nouveau mot de passe --> + + <child> + <object class="GtkLabel" id="new_primary_passwd_lbl"> + <property name="label">Enter new password:</property> + <property name="xalign">0</property> + <layout> + <property name="column">0</property> + <property name="row">3</property> + </layout> + </object> + </child> + + <child> + <object class="GtkPasswordEntry" id="new_primary_passwd"> + <property name="hexpand">true</property> + <property name="show-peek-icon">true</property> + <signal name="changed" handler="gtk_security_tweak_panel_on_new_passwords_changed"/> + <layout> + <property name="column">1</property> + <property name="row">3</property> + </layout> + </object> + </child> + + <!-- Confirmation de mot de passe --> + + <child> + <object class="GtkLabel" id="new_primary_passwd_2_lbl"> + <property name="label">Re-enter new password:</property> + <property name="xalign">0</property> + <layout> + <property name="column">0</property> + <property name="row">4</property> + </layout> + </object> + </child> + + <child> + <object class="GtkPasswordEntry" id="new_primary_passwd_2"> + <property name="hexpand">true</property> + <property name="show-peek-icon">true</property> + <signal name="changed" handler="gtk_security_tweak_panel_on_new_passwords_changed"/> + <layout> + <property name="column">1</property> + <property name="row">4</property> + </layout> + </object> + </child> + + <!-- Boutons de contrôle --> + + <child> + <object class="GtkBox"> + <property name="halign">center</property> + <property name="homogeneous">true</property> + <property name="spacing">8</property> + <layout> + <property name="column">0</property> + <property name="row">5</property> + <property name="column-span">2</property> + </layout> + + <child> + <object class="GtkButton" id="set_password"> + <property name="label">Set</property> + <signal name="clicked" handler="gtk_security_tweak_panel_on_set_password_clicked"/> + </object> + </child> + + <child> + <object class="GtkButton" id="change_password"> + <property name="label">Change</property> + <signal name="clicked" handler="gtk_security_tweak_panel_on_change_password_clicked"/> + </object> + </child> + + <child> + <object class="GtkButton" id="remove_password"> + <property name="label">Remove</property> + <signal name="clicked" handler="gtk_security_tweak_panel_on_remove_password_clicked"/> + <style> + <class name="destructive-action"/> + </style> + </object> + </child> + + </object> + </child> + + </object> + </child> + + </template> +</interface> diff --git a/src/gui/panel-int.h b/src/gui/panel-int.h deleted file mode 100644 index 7e6f7e3..0000000 --- a/src/gui/panel-int.h +++ /dev/null @@ -1,71 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * panel-int.h - prototypes pour les définitions internes liées aux panneaux d'affichage - * - * Copyright (C) 2019-2024 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#ifndef _GUI_PANEL_INT_H -#define _GUI_PANEL_INT_H - - -#include "panel.h" - - - -/* Fournit une indication sur la personnalité du panneau. */ -typedef PanelItemPersonality (* get_panel_item_personality_cb) (const GPanelItem *); - -/* Fournit un composant pour lancer l'activité d'un panneau. */ -typedef GtkWidget * (* get_panel_item_widget_cb) (GPanelItem *); - -/* Fournit un composant représentant un panneau graphique. */ -typedef GtkTiledPanel * (* get_panel_item_panel_cb) (GPanelItem *, GtkWidget *); - - -/* Elément réactif pour panneaux de l'éditeur (instance) */ -struct _GPanelItem -{ - GObject parent; /* A laisser en premier */ - - GtkWidget *launcher; /* Eventuel lanceur associé */ - GtkWidget *properties; /* Propriétés de lancement */ - - GtkTiledPanel **panels; /* Instances complètes ouvertes*/ - size_t pcount; /* Quantité de ces instances */ - -}; - -/* Elément réactif pour panneaux de l'éditeur (classe) */ -struct _GPanelItemClass -{ - GObjectClass parent; /* A laisser en premier */ - - get_panel_item_personality_cb get_personality; /* Fourniture de nature */ - - get_panel_item_widget_cb get_launcher; /* Lancement d'une activité */ - get_panel_item_widget_cb get_properties;/* Préparation au lancement */ - get_panel_item_panel_cb get_panel; /* Panneau principal */ - -}; - - - -#endif /* _GUI_PANEL_INT_H */ diff --git a/src/gui/panel.c b/src/gui/panel.c deleted file mode 100644 index b361153..0000000 --- a/src/gui/panel.c +++ /dev/null @@ -1,1207 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * panel.c - gestion des éléments réactifs spécifiques aux panneaux - * - * Copyright (C) 2019-2024 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include "panel.h" - - -#include "panel-int.h" - - - -/* ---------------------- MANIPULATIONS D'UN PANNEAU GRAPHIQUE ---------------------- */ - - - - - -/* Initialise la classe des panneaux graphiques de l'éditeur. */ -static void g_panel_item_class_init(GPanelItemClass *); - -/* Initialise une instance de panneau graphique pour l'éditeur. */ -static void g_panel_item_init(GPanelItem *); - -/* Supprime toutes les références externes. */ -static void g_panel_item_dispose(GPanelItem *); - -/* Procède à la libération totale de la mémoire. */ -static void g_panel_item_finalize(GPanelItem *); - - - - - -/* Indique le type défini pour un élément destiné à un panneau. */ -G_DEFINE_TYPE(GPanelItem, g_panel_item, G_TYPE_OBJECT); - - -/****************************************************************************** -* * -* Paramètres : class = classe à initialiser. * -* * -* Description : Initialise la classe des panneaux graphiques de l'éditeur. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_panel_item_class_init(GPanelItemClass *class) -{ - GObjectClass *object; /* Autre version de la classe */ - - object = G_OBJECT_CLASS(class); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_panel_item_dispose; - object->finalize = (GObjectFinalizeFunc)g_panel_item_finalize; - -} - - -/****************************************************************************** -* * -* Paramètres : item = instance à initialiser. * -* * -* Description : Initialise une instance de panneau graphique pour l'éditeur. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_panel_item_init(GPanelItem *item) -{ - //item->docked = false; - - //item->widget = NULL; - //item->cached_widget = NULL; - - //item->filter = NULL; - - //g_atomic_int_set(&item->switched, 0); - -} - - -/****************************************************************************** -* * -* Paramètres : item = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_panel_item_dispose(GPanelItem *item) -{ - //g_clear_object(&item->widget); - //g_clear_object(&item->cached_widget); - - G_OBJECT_CLASS(g_panel_item_parent_class)->dispose(G_OBJECT(item)); - -} - - -/****************************************************************************** -* * -* Paramètres : item = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_panel_item_finalize(GPanelItem *item) -{ - G_OBJECT_CLASS(g_panel_item_parent_class)->finalize(G_OBJECT(item)); - -} - - -/****************************************************************************** -* * -* Paramètres : item = définition de panneau à consulter. * -* * -* Description : Fournit une indication sur la personnalité du panneau. * -* * -* Retour : Identifiant lié à la nature du panneau. * -* * -* Remarques : - * -* * -******************************************************************************/ - -PanelItemPersonality g_panel_item_get_personality(const GPanelItem *item) -{ - PanelItemPersonality result; /* Personnalité à retourner */ - GPanelItemClass *class; /* Classe à actionner */ - - class = G_PANEL_ITEM_GET_CLASS(item); - - result = class->get_personality(item); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : item = définition de panneau à manipuler. * -* * -* Description : Fournit un composant pour lancer l'activité d'un panneau. * -* * -* Retour : Composant GTK (déjà ?) mis en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GtkWidget *g_panel_item_get_launcher(GPanelItem *item) -{ - GtkWidget *result; /* Composant à retourner */ - GPanelItemClass *class; /* Classe à actionner */ - - if (item->launcher == NULL) - { - class = G_PANEL_ITEM_GET_CLASS(item); - item->launcher = class->get_launcher(item); - } - - result = item->launcher; - - if (result != NULL) - ref_object(result); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : item = définition de panneau à manipuler. * -* * -* Description : Fournit un composant pour paramétrer l'activité d'un panneau.* -* * -* Retour : Composant GTK (déjà ?) mis en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GtkWidget *g_panel_item_get_properties(GPanelItem *item) -{ - GtkWidget *result; /* Composant à retourner */ - GPanelItemClass *class; /* Classe à actionner */ - - if (item->properties == NULL) - { - class = G_PANEL_ITEM_GET_CLASS(item); - item->properties = class->get_properties(item); - } - - result = item->properties; - - if (result != NULL) - ref_object(result); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : item = définition de panneau à manipuler. * -* * -* Description : Fournit un composant représentant un panneau graphique. * -* * -* Retour : Composant GTK (déjà ?) mis en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GtkTiledPanel *g_panel_item_get_panel(GPanelItem *item) -{ - GtkTiledPanel *result; /* Composant à retourner */ - GPanelItemClass *class; /* Classe à actionner */ - - if (item->pcount == 0 /* || !singleton */) - { - class = G_PANEL_ITEM_GET_CLASS(item); - result = class->get_panel(item, item->properties); - - if (result != NULL) - { - item->panels = realloc(item->panels, ++item->pcount * sizeof(GtkTiledPanel *)); - item->panels[item->pcount - 1] = result; - } - - } - - else - result = item->panels[item->pcount - 1]; - - if (result != NULL) - ref_object(result); - - return result; - -} - - - - - - - - - - - - - -#if 0 - - -#include <assert.h> -#include <stdio.h> -#include <string.h> - - -#include "panel-int.h" -#include "core/global.h" -#include "core/items.h" -#include "../common/extstr.h" -#include "../core/params.h" -#include "../gtkext/gtkdockable-int.h" -#include "../gtkext/named.h" -#include "../plugins/dt.h" -#include "../plugins/pglist.h" - - - -/* ------------------------- COEUR DES PANNEAUX D'AFFICHAGE ------------------------- */ - - - - -/* ------------------------- COEUR D'UN PANNEAU D'AFFICHAGE ------------------------- */ - - -/* Construit la chaîne d'accès à un élément de configuration. */ -static char *gtk_panel_item_class_build_configuration_key(const GPanelItemClass *, const char *); - -/* Fournit le nom court du composant encapsulable. */ -static char *gtk_panel_item_get_name(const GPanelItem *); - -/* Fournit le nom long du composant encapsulable. */ -static char *gtk_panel_item_get_desc(const GPanelItem *); - -/* Détermine si un panneau peut être filtré. */ -static bool gtk_panel_item_can_search(const GPanelItem *); - -/* Fournit le composant graphique intégrable dans un ensemble. */ -static GtkWidget *gtk_panel_item_get_widget(GPanelItem *); - -/* Démarre l'actualisation du filtrage du contenu. */ -static void gtk_panel_item_update_filtered(GPanelItem *, const char *); - - - -/* ---------------------- MECANISMES DE MISE A JOUR DE PANNEAU ---------------------- */ - - -/* Présente une copie de l'affichage du composant rafraîchi. */ -static gboolean g_panel_item_draw_mask(GtkWidget *, cairo_t *, GPanelItem *); - - - -/* ---------------------------------------------------------------------------------- */ -/* COEUR DES PANNEAUX D'AFFICHAGE */ -/* ---------------------------------------------------------------------------------- */ - - - -/****************************************************************************** -* * -* Paramètres : class = classe à consulter. * -* * -* Description : Fournit une indication sur la personnalité du panneau. * -* * -* Retour : Identifiant lié à la nature unique du panneau. * -* * -* Remarques : - * -* * -******************************************************************************/ - -PanelItemPersonality gtk_panel_item_class_get_personality_singleton(const GPanelItemClass *class) -{ - PanelItemPersonality result; /* Personnalité à retourner */ - - result = PIP_SINGLETON; - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe à consulter. * -* * -* Description : Fournit une indication d'accroche du panneau au démarrage. * -* * -* Retour : true si le panneau doit être affiché de prime abord. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool gtk_panel_item_class_dock_at_startup(const GPanelItemClass *class) -{ - bool result; /* Statut à retourner */ - GGenConfig *config; /* Configuration courante */ - char *key; /* Clef d'accès à un paramètre */ -#ifndef NDEBUG - bool status; /* Bilan de consultation */ -#endif - - config = get_main_configuration(); - - key = gtk_panel_item_class_build_configuration_key(class, "dock_at_startup"); - -#ifndef NDEBUG - status = g_generic_config_get_value(config, key, &result); - assert(status); -#else - g_generic_config_get_value(config, key, &result); -#endif - - free(key); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe associée à la consultation. * -* * -* Description : Renvoie false lors d'une consultation de la classe. * -* * -* Retour : false. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool gtk_panel_item_class_return_false(const GPanelItemClass *class) -{ - bool result; /* Statut à retourner */ - - result = false; - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe associée à la consultation. * -* * -* Description : Renvoie true lors d'une consultation de la classe. * -* * -* Retour : true. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool gtk_panel_item_class_return_true(const GPanelItemClass *class) -{ - bool result; /* Statut à retourner */ - - result = true; - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe à consulter. * -* * -* Description : Détermine si un panneau peut être filtré. * -* * -* Retour : Bilan de la consultation. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool gtk_panel_item_class_can_search(const GPanelItemClass *class) -{ - bool result; /* Statut à retourner */ - - result = class->can_search(class); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe à consulter. * -* * -* Description : Indique le chemin initial de la localisation d'un panneau. * -* * -* Retour : Chemin fixé associé à la position initiale. * -* * -* Remarques : - * -* * -******************************************************************************/ - -char *gtk_panel_item_class_get_path(const GPanelItemClass *class) -{ - char *result; /* Emplacement à retourner */ - GGenConfig *config; /* Configuration courante */ - char *key; /* Clef d'accès à un paramètre */ - const char *path; /* Nouveau chemin de placement */ -#ifndef NDEBUG - bool status; /* Statut de l'encapsulation */ -#endif - - config = get_main_configuration(); - - key = gtk_panel_item_class_build_configuration_key(class, "path"); - -#ifndef NDEBUG - status = g_generic_config_get_value(config, key, &path); - assert(status); -#else - g_generic_config_get_value(config, key, &path); -#endif - - free(key); - - result = strdup(path); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe à consulter. * -* * -* Description : Indique la définition d'un éventuel raccourci clavier. * -* * -* Retour : Description d'un raccourci ou NULL si aucun de défini. * -* * -* Remarques : - * -* * -******************************************************************************/ - -char *gtk_panel_item_class_get_key_bindings(const GPanelItemClass *class) -{ - char *result; /* Emplacement à retourner */ - - if (class->get_bindings != NULL) - result = class->get_bindings(class); - - else - result = NULL; - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe du type de panneau à traiter. * -* attrib = élément de configuration à inclure dans le résultat.* -* * -* Description : Construit la chaîne d'accès à un élément de configuration. * -* * -* Retour : Chaîne de caractères à libérer après usage. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static char *gtk_panel_item_class_build_configuration_key(const GPanelItemClass *class, const char *attrib) -{ - char *result; /* Construction à renvoyer */ - const char *name; /* Nom court du panneau */ - - name = g_editor_item_class_get_key(G_EDITOR_ITEM_CLASS(class)); - - asprintf(&result, "gui.panels.%s.%s", attrib, name); - - result = strrpl(result, " ", "_"); - - result = strlower(result); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : class = classe de panneau à consulter. * -* config = configuration à compléter. * -* * -* Description : Met en place les bases de la configuration d'un panneau. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool gtk_panel_item_class_setup_configuration(const GPanelItemClass *class, GGenConfig *config) -{ - bool result; /* Bilan à retourner */ - char *key; /* Clef d'accès à un paramètre */ - bool dock_at_startup; /* Affichage dès le départ ? */ - char *path; /* Localisation du panneau */ - - key = gtk_panel_item_class_build_configuration_key(class, "dock_at_startup"); - - dock_at_startup = class->dock_at_startup(class); - - result = g_generic_config_create_param_if_not_exist(config, key, CPT_BOOLEAN, dock_at_startup); - - free(key); - - if (!result) - goto exit; - - key = gtk_panel_item_class_build_configuration_key(class, "path"); - - path = class->get_path(class); - - result = g_generic_config_create_param_if_not_exist(config, key, CPT_STRING, path); - - free(path); - - free(key); - - exit: - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : type = type de panneau à mettre en place. * -* path = emplacement d'affichage ou NULL. * -* * -* Description : Crée un élément de panneau réactif. * -* * -* Retour : Adresse de la structure mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GPanelItem *g_panel_item_new(GType type, const char *path) -{ - GPanelItem *result; /* Structure à retourner */ - GPanelItemClass *class; /* Classe associée au type */ - PanelItemPersonality personality; /* Caractéristique de panneau */ - GtkTiledGrid *grid; /* Composant d'affichage */ - - class = g_type_class_ref(type); - - personality = gtk_panel_item_class_get_personality(class); - assert(path != NULL || personality == PIP_PERSISTENT_SINGLETON); - - g_type_class_unref(class); - - if (personality == PIP_PERSISTENT_SINGLETON || personality == PIP_SINGLETON) - { - result = G_PANEL_ITEM(find_editor_item_by_type(type)); - - if (result != NULL) - goto singleton; - - } - - result = create_object_from_type(type); - - grid = get_tiled_grid(); - - g_signal_connect_swapped(result, "dock-request", G_CALLBACK(gtk_tiled_grid_add), grid); - g_signal_connect_swapped(result, "undock-request", G_CALLBACK(gtk_tiled_grid_remove), grid); - - gtk_dockable_setup_dnd(GTK_DOCKABLE(result)); - - register_editor_item(G_EDITOR_ITEM(result)); - - notify_panel_creation(result); - - singleton: - - if (path != NULL) - { - if (path[0] != '\0') - gtk_panel_item_set_path(result, path); - - g_panel_item_dock(result); - - } - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : item = instance de panneau à consulter. * -* * -* Description : Indique le composant graphique principal du panneau. * -* * -* Retour : Composant graphique avec nom constituant le panneau. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GNamedWidget *gtk_panel_item_get_named_widget(const GPanelItem *item) -{ - GNamedWidget *result; /* Composant nommé à retourner */ - - result = item->widget; - - g_object_ref(G_OBJECT(result)); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : item = instance GTK dont l'interface est à consulter. * -* * -* Description : Fournit le nom court du composant encapsulable. * -* * -* Retour : Désignation humaine pour titre d'onglet ou de fenêtre. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static char *gtk_panel_item_get_name(const GPanelItem *item) -{ - char *result; /* Désignation à retourner */ - - result = g_named_widget_get_name(G_NAMED_WIDGET(item->widget), false); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : item = instance GTK dont l'interface est à consulter. * -* * -* Description : Fournit le nom long du composant encapsulable. * -* * -* Retour : Désignation humaine pour titre d'onglet ou de fenêtre. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static char *gtk_panel_item_get_desc(const GPanelItem *item) -{ - char *result; /* Description à retourner */ - - result = g_named_widget_get_name(G_NAMED_WIDGET(item->widget), true); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : item = instance GTK dont l'interface est à consulter. * -* * -* Description : Détermine si un panneau peut être filtré. * -* * -* Retour : Bilan de la consultation. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool gtk_panel_item_can_search(const GPanelItem *item) -{ - bool result; /* Indication à retourner */ - GPanelItemClass *class; /* Classe de l'élément visé */ - - class = G_PANEL_ITEM_GET_CLASS(item); - - result = gtk_panel_item_class_can_search(class); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : item = instance GTK dont l'interface est à consulter. * -* * -* Description : Fournit le composant graphique intégrable dans un ensemble. * -* * -* Retour : Composant graphique prêt à emploi. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GtkWidget *gtk_panel_item_get_widget(GPanelItem *item) -{ - GtkWidget *result; /* Composant à retourner */ - - if (item->cached_widget == NULL) - item->cached_widget = g_named_widget_get_widget(G_NAMED_WIDGET(item->widget)); - - result = item->cached_widget; - - g_object_ref(G_OBJECT(result)); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : item = instance GTK dont l'interface est à sollicitée. * -* * -* Description : Démarre l'actualisation du filtrage du contenu. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void gtk_panel_item_update_filtered(GPanelItem *item, const char *filter) -{ - assert(gtk_panel_item_can_search(item)); - - if (item->filter != NULL) - free(item->filter); - - item->filter = (filter ? strdup(filter) : NULL); - - G_PANEL_ITEM_GET_CLASS(item)->update_filtered(item); - -} - - -/****************************************************************************** -* * -* Paramètres : item = instance GTK à consulter. * -* path = nouvelle emplacement d'inclusion. * -* * -* Description : Définit le chemin d'accès à utiliser pour les encapsulations.* -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void gtk_panel_item_set_path(GPanelItem *item, const char *path) -{ - GGenConfig *config; /* Configuration courante */ - char *key; /* Clef d'accès à un paramètre */ - - config = get_main_configuration(); - - key = gtk_panel_item_class_build_configuration_key(G_PANEL_ITEM_GET_CLASS(item), "path"); - - g_generic_config_set_value(config, key, path); - - free(key); - -} - - -/****************************************************************************** -* * -* Paramètres : item = composant à présenter à l'affichage. * -* * -* Description : Place un panneau dans l'ensemble affiché. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_panel_item_dock(GPanelItem *item) -{ - assert(!item->docked); - - g_signal_emit_by_name(item, "dock-request"); - - if (G_PANEL_ITEM_GET_CLASS(item)->ack_dock != NULL) - G_PANEL_ITEM_GET_CLASS(item)->ack_dock(item); - - notify_panel_docking(item, true); - -} - - -/****************************************************************************** -* * -* Paramètres : item = composant d'affichage à mettre à jour. * -* status = nouvel état d'encapsulation. * -* * -* Description : Définit si le composant repose sur un support de l'éditeur. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_panel_item_set_dock_at_startup(GPanelItem *item, bool status) -{ - char *key; /* Clef d'accès à un paramètre */ - - item->docked = status; - - key = gtk_panel_item_class_build_configuration_key(G_PANEL_ITEM_GET_CLASS(item), "dock_at_startup"); - - g_generic_config_set_value(get_main_configuration(), key, status); - - free(key); - -} - - -/****************************************************************************** -* * -* Paramètres : item = composant d'affichage à consulter. * -* * -* Description : Indique si le composant repose sur un support de l'éditeur. * -* * -* Retour : true si le composant est bien incrusté quelque part. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_panel_item_is_docked(const GPanelItem *item) -{ - bool result; /* Statut à retourner */ - - result = item->docked; - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : item = composant à retirer de l'affichage. * -* * -* Description : Supprime un panneau de l'ensemble affiché. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_panel_item_undock(GPanelItem *item) -{ - PanelItemPersonality personality; /* Caractéristique de panneau */ - - assert(item->docked); - - g_signal_emit_by_name(item, "undock-request"); - - if (G_PANEL_ITEM_GET_CLASS(item)->ack_undock != NULL) - G_PANEL_ITEM_GET_CLASS(item)->ack_undock(item); - - notify_panel_docking(item, false); - - personality = gtk_panel_item_class_get_personality(G_PANEL_ITEM_GET_CLASS(item)); - - if (personality != PIP_PERSISTENT_SINGLETON) - unregister_editor_item(G_EDITOR_ITEM(item)); - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* MECANISMES DE MISE A JOUR DE PANNEAU */ -/* ---------------------------------------------------------------------------------- */ - - -/****************************************************************************** -* * -* Paramètres : item = panneau ciblé par une mise à jour. * -* * -* Description : Obtient le groupe de travail dédié à une mise à jour. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -wgroup_id_t g_panel_item_get_group(const GPanelItem *item) -{ - wgroup_id_t result; /* Identifiant à retourner */ - - result = G_PANEL_ITEM_GET_CLASS(item)->gid; - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : widget = composant graphique sur lequel dessiner. * -* cr = contexte graphique pour le dessin. * -* panel = panneau ciblé par une mise à jour. * -* * -* Description : Présente une copie de l'affichage du composant rafraîchi. * -* * -* Retour : FALSE afin de poursuivre les traitements. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static gboolean g_panel_item_draw_mask(GtkWidget *widget, cairo_t *cr, GPanelItem *item) -{ - int width; /* Largeur du composant actuel */ - int height; /* Hauteur du composant actuel */ - - width = gtk_widget_get_allocated_width(widget); - height = gtk_widget_get_allocated_height(widget); - - cairo_save(cr); - - cairo_set_source_surface(cr, item->surface, 0, 0); - cairo_rectangle(cr, 0, 0, width, height); - - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - cairo_fill(cr); - - cairo_restore(cr); - - return FALSE; - -} - - -/****************************************************************************** -* * -* Paramètres : panel = panneau ciblé par une mise à jour. * -* * -* Description : Bascule l'affichage d'un panneau avant sa mise à jour. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_panel_item_switch_to_updating_mask(GPanelItem *item) -{ - GtkBuilder *builder; /* Constructeur sous-jacent */ - GtkWidget *content; /* Composant à faire évoluer */ - GdkWindow *window; /* Fenêtre au contenu à copier */ - int width; /* Largeur du composant actuel */ - int height; /* Hauteur du composant actuel */ - cairo_t *cr; /* Pinceau pour les dessins */ - GtkAdjustment *adj; /* Défilement éventuel */ - GtkStack *stack; /* Pile de composants GTK */ - GtkWidget *mask; /* Masque des travaux */ - - if (g_atomic_int_add(&item->switched, 1) > 0) - return; - - /* Copie de l'affichage courant */ - - assert(item->surface == NULL); - - builder = gtk_built_named_widget_get_builder(GTK_BUILT_NAMED_WIDGET(item->widget)); - - content = GTK_WIDGET(gtk_builder_get_object(builder, "content")); - - window = gtk_widget_get_window(content); - - if (window != NULL) - { - width = gtk_widget_get_allocated_width(content); - height = gtk_widget_get_allocated_height(content); - - item->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); - - cr = cairo_create(item->surface); - - gdk_cairo_set_source_window(cr, window, 0, 0); - - cairo_paint(cr); - - cairo_destroy(cr); - - } - - /* Sauvegarde de l'éventuelle position */ - - if (GTK_IS_SCROLLED_WINDOW(content)) - { - adj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(content)); - item->hadj_value = gtk_adjustment_get_value(adj); - - adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(content)); - item->vadj_value = gtk_adjustment_get_value(adj); - - } - - /* Opération de basculement effectif */ - - stack = GTK_STACK(gtk_builder_get_object(builder, "stack")); - - mask = GTK_WIDGET(gtk_builder_get_object(builder, "mask")); - - gtk_spinner_start(GTK_SPINNER(mask)); - - if (item->surface != NULL) - g_signal_connect(mask, "draw", G_CALLBACK(g_panel_item_draw_mask), item); - - gtk_stack_set_visible_child(stack, mask); - - g_object_unref(G_OBJECT(builder)); - -} - - -/****************************************************************************** -* * -* Paramètres : panel = panneau ciblé par une mise à jour. * -* * -* Description : Bascule l'affichage d'un panneau après sa mise à jour. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_panel_item_switch_to_updated_content(GPanelItem *item) -{ - GtkBuilder *builder; /* Constructeur sous-jacent */ - GtkWidget *content; /* Composant à faire évoluer */ - GtkAdjustment *adj; /* Défilement éventuel */ - GtkStack *stack; /* Pile de composants GTK */ - GtkWidget *mask; /* Masque des travaux */ - - if (g_atomic_int_get(&item->switched) > 1) - goto skip; - - /* Restauration d'une éventuelle position */ - - builder = gtk_built_named_widget_get_builder(GTK_BUILT_NAMED_WIDGET(item->widget)); - - content = GTK_WIDGET(gtk_builder_get_object(builder, "content")); - - if (GTK_IS_SCROLLED_WINDOW(content)) - { - adj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(content)); - gtk_adjustment_set_value(adj, item->hadj_value); - - adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(content)); - gtk_adjustment_set_value(adj, item->vadj_value); - - } - - /* Opération de basculement effectif */ - - stack = GTK_STACK(gtk_builder_get_object(builder, "stack")); - - gtk_stack_set_visible_child(stack, content); - - mask = GTK_WIDGET(gtk_builder_get_object(builder, "mask")); - - g_signal_handlers_disconnect_by_func(mask, G_CALLBACK(g_panel_item_draw_mask), item); - - gtk_spinner_stop(GTK_SPINNER(mask)); - - /* Supression de la copie d'affichage */ - - if (item->surface != NULL) - { - cairo_surface_destroy(item->surface); - item->surface = NULL; - } - - g_object_unref(G_OBJECT(builder)); - - skip: - - g_atomic_int_dec_and_test(&item->switched); - -} - - -#endif diff --git a/src/gui/panel.h b/src/gui/panel.h deleted file mode 100644 index 03b67d4..0000000 --- a/src/gui/panel.h +++ /dev/null @@ -1,148 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * panel.h - prototypes pour la gestion des éléments réactifs spécifiques aux panneaux - * - * Copyright (C) 2019-2024 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#ifndef _GUI_PANEL_H -#define _GUI_PANEL_H - - -#include "../glibext/helpers.h" -#include "../gtkext/panel.h" - - -#include <gtk/gtk.h> - - - -#define G_TYPE_PANEL_ITEM (g_panel_item_get_type()) - -DECLARE_GTYPE(GPanelItem, g_panel_item, G, PANEL_ITEM); - - -/* Types de panneaux pour éditeur */ -typedef enum _PanelItemPersonality -{ - PIP_NONE, /* Pas de particularité */ - - PIP_MAIN_PANEL, /* Panneau principal */ - PIP_SINGLETON, /* Instance unique */ - - PIP_COUNT - -} PanelItemPersonality; - - -/* Fournit une indication sur la personnalité du panneau. */ -PanelItemPersonality g_panel_item_get_personality(const GPanelItem *); - -/* Fournit un composant pour lancer l'activité d'un panneau. */ -GtkWidget *g_panel_item_get_launcher(GPanelItem *); - -/* Fournit un composant pour paramétrer l'activité d'un panneau. */ -GtkWidget *g_panel_item_get_properties(GPanelItem *); - -/* Fournit un composant représentant un panneau graphique. */ -GtkTiledPanel *g_panel_item_get_panel(GPanelItem *); - - - - - - - - - -#if 0 - - -#include <stdbool.h> -#include <gtk/gtk.h> - - -#include "../glibext/configuration.h" -#include "../glibext/named.h" - - - -#define G_TYPE_PANEL_ITEM g_panel_item_get_type() -#define G_PANEL_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_PANEL_ITEM, GPanelItem)) -#define G_IS_PANEL_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_PANEL_ITEM)) -#define G_PANEL_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_PANEL_ITEM, GPanelItemClass)) -#define G_IS_PANEL_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_PANEL_ITEM)) -#define G_PANEL_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_PANEL_ITEM, GPanelItemClass)) - - -/* Elément réactif pour panneaux de l'éditeur (instance) */ -typedef struct _GPanelItem GPanelItem; - -/* Elément réactif pour panneaux de l'éditeur (classe) */ -typedef struct _GPanelItemClass GPanelItemClass; - - - - -/* Indique le type défini pour un élément destiné à un panneau. */ -GType g_panel_item_get_type(void); - -/* Fournit une indication d'accroche du panneau au démarrage. */ -bool gtk_panel_item_class_dock_at_startup(const GPanelItemClass *); - -/* Détermine si un panneau peut être filtré. */ -bool gtk_panel_item_class_can_search(const GPanelItemClass *); - -/* Indique le chemin initial de la localisation d'un panneau. */ -char *gtk_panel_item_class_get_path(const GPanelItemClass *); - -/* Indique la définition d'un éventuel raccourci clavier. */ -char *gtk_panel_item_class_get_key_bindings(const GPanelItemClass *); - -/* Met en place les bases de la configuration du panneau. */ -bool gtk_panel_item_class_setup_configuration(const GPanelItemClass *, GGenConfig *); - -/* Crée un élément de panneau réactif. */ -GPanelItem *g_panel_item_new(GType, const char *); - -/* Indique le composant graphique principal du panneau. */ -GNamedWidget *gtk_panel_item_get_named_widget(const GPanelItem *); - -/* Définit le chemin d'accès à utiliser pour les encapsulations. */ -void gtk_panel_item_set_path(GPanelItem *, const char *); - -/* Place un panneau dans l'ensemble affiché. */ -void g_panel_item_dock(GPanelItem *); - -/* Définit si le composant repose sur un support de l'éditeur. */ -void g_panel_item_set_dock_at_startup(GPanelItem *, bool); - -/* Indique si le composant repose sur un support de l'éditeur. */ -bool g_panel_item_is_docked(const GPanelItem *); - -/* Supprime un panneau de l'ensemble affiché. */ -void g_panel_item_undock(GPanelItem *); - - -#endif - - - -#endif /* _GUI_PANEL_H */ diff --git a/src/gui/panels/Makefile.am b/src/gui/panels/Makefile.am index 476a436..2765405 100644 --- a/src/gui/panels/Makefile.am +++ b/src/gui/panels/Makefile.am @@ -38,8 +38,7 @@ IMG_PATH = ../../../data/images RES_FILES = \ binary.ui \ - binary-launch.ui \ - binary-props.ui \ + binary-params.ui \ $(IMG_PATH)/binfile-symbolic.svg \ welcome.ui \ welcome-hints.txt \ @@ -48,6 +47,8 @@ RES_FILES = \ libguipanels4_la_SOURCES = \ binary-int.h \ binary.h binary.c \ + binary-params-int.h \ + binary-params.h binary-params.c \ resources.h resources.c \ welcome-int.h \ welcome.h welcome.c diff --git a/src/gui/panels/binary-int.h b/src/gui/panels/binary-int.h index f68da9b..5116f5c 100644 --- a/src/gui/panels/binary-int.h +++ b/src/gui/panels/binary-int.h @@ -27,14 +27,10 @@ #include "binary.h" -#include "../panel-int.h" #include "../../gtkext/panel-int.h" -/* ------------------------- COEUR D'UN PANNEAU D'AFFICHAGE ------------------------- */ - - /* Panneau d'accueil par défaut (instance) */ struct _GtkBinaryPanel { @@ -53,23 +49,4 @@ struct _GtkBinaryPanelClass -/* ---------------------- MANIPULATIONS D'UN PANNEAU GRAPHIQUE ---------------------- */ - - -/* Définition pour gestion par le framework d'un panneau graphique (instance) */ -struct _GBinaryPanel -{ - GPanelItem parent; /* A laisser en premier */ - -}; - -/* Définition pour gestion par le framework d'un panneau graphique (classe) */ -struct _GBinaryPanelClass -{ - GPanelItemClass parent; /* A laisser en premier */ - -}; - - - #endif /* _GUI_PANELS_BINARY_INT_H */ diff --git a/src/gui/panels/binary-launch.ui b/src/gui/panels/binary-launch.ui deleted file mode 100644 index 553f758..0000000 --- a/src/gui/panels/binary-launch.ui +++ /dev/null @@ -1,65 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<interface> - - <object class="GtkGrid" id="launcher"> - <property name="margin-bottom">12</property> - <property name="margin-end">12</property> - <property name="margin-start">12</property> - <property name="margin-top">12</property> - <property name="column-spacing">12</property> - <child> - <object class="GtkImage"> - <property name="icon-name">binfile-symbolic</property> - <property name="pixel-size">48</property> - <layout> - <property name="column">0</property> - <property name="row">0</property> - <property name="row-span">2</property> - </layout> - <style> - <class name="icon-dropshadow"/> - </style> - </object> - </child> - <child> - <object class="GtkLabel"> - <property name="label"><b>Binary analysis</b></property> - <property name="use-markup">TRUE</property> - <property name="xalign">0</property> - <layout> - <property name="column">1</property> - <property name="row">0</property> - </layout> - </object> - </child> - <child> - <object class="GtkLabel"> - <property name="label">Load a binary content and parse its format if recognized</property> - <property name="hexpand">true</property> - <property name="xalign">0</property> - <layout> - <property name="column">1</property> - <property name="row">1</property> - </layout> - <style> - <class name="dim-label"/> - </style> - </object> - </child> - <child> - <object class="GtkImage"> - <property name="icon-name">go-next-symbolic</property> - <property name="margin-start">12</property> - <layout> - <property name="column">2</property> - <property name="row">0</property> - <property name="row-span">2</property> - </layout> - <style> - <class name="icon-dropshadow"/> - </style> - </object> - </child> - </object> - -</interface> diff --git a/src/gui/panels/binary-params-int.h b/src/gui/panels/binary-params-int.h new file mode 100644 index 0000000..0fbef24 --- /dev/null +++ b/src/gui/panels/binary-params-int.h @@ -0,0 +1,50 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * binary-params-int.h - définitions internes pour l'édition des paramètres initiaux d'un chargement de binaire + * + * Copyright (C) 2025 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 _GUI_PANELS_BINARY_PARAMS_INT_H +#define _GUI_PANELS_BINARY_PARAMS_INT_H + + +#include "binary-params.h" + + + +/* Composant pour les paramètres de chargement d'un binaire (instance) */ +struct _GtkBinaryParameters +{ + GtkGrid parent; /* A laisser en premier */ + + GtkEntry *filename; /* CHemin d'un binaire */ + +}; + +/* Composant pour les paramètres de chargement d'un binaire (classe) */ +struct _GtkBinaryParametersClass +{ + GtkGridClass parent; /* A laisser en premier */ + +}; + + + +#endif /* _GUI_PANELS_BINARY_PARAMS_INT_H */ diff --git a/src/gui/panels/binary-params.c b/src/gui/panels/binary-params.c new file mode 100644 index 0000000..1059761 --- /dev/null +++ b/src/gui/panels/binary-params.c @@ -0,0 +1,178 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * binary-params.c - édition des paramètres initiaux d'un chargement de binaire + * + * Copyright (C) 2025 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/>. + */ + + +#include "binary-params.h" + + +#include "binary.h" +#include "binary-params-int.h" +#include "../window.h" +#include "../../analysis/contents/file.h" +#include "../../gtkext/helpers.h" + + + +/* Initialise la classe des composants d'édition de paramètres. */ +static void gtk_binary_parameters_class_init(GtkBinaryParametersClass *); + +/* Initialise une instance de composant d'édition de paramètres. */ +static void gtk_binary_parameters_init(GtkBinaryParameters *); + +/* Supprime toutes les références externes. */ +static void gtk_binary_parameters_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_binary_parameters_finalize(GObject *); + +/* Ouvre une boîte de dialogue pour récupérer un fichier. */ +static void gtk_binary_parameters_on_new_file_entry_icon_release(GtkEntry *, GtkEntryIconPosition, GtkBinaryParameters *); + + + +/* Détermine le type du composant d'édition des paramètres de chargement. */ +G_DEFINE_TYPE(GtkBinaryParameters, gtk_binary_parameters, GTK_TYPE_GRID); + + +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Initialise la classe des composants d'édition de paramètres. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_binary_parameters_class_init(GtkBinaryParametersClass *class) +{ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = gtk_binary_parameters_dispose; + object->finalize = gtk_binary_parameters_finalize; + + widget = GTK_WIDGET_CLASS(class); + + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/panels/binary-params.ui"); + + gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_binary_parameters_on_new_file_entry_icon_release)); + + gtk_widget_class_bind_template_child(widget, GtkBinaryParameters, filename); + +} + + +/****************************************************************************** +* * +* Paramètres : params = composant GTK à initialiser. * +* * +* Description : Initialise une instance de composant d'édition de paramètres.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_binary_parameters_init(GtkBinaryParameters *params) +{ + gtk_widget_init_template(GTK_WIDGET(params)); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_binary_parameters_dispose(GObject *object) +{ + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_BINARY_PARAMETERS); + + G_OBJECT_CLASS(gtk_binary_parameters_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_binary_parameters_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_binary_parameters_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : entry = zone de saisie concernée par l'appel. * +* icon_pos = position de l'icone incrustée dans la zone. * +* params = composant d'édition des paramètres. * +* * +* Description : Ouvre une boîte de dialogue pour récupérer un fichier. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_binary_parameters_on_new_file_entry_icon_release(GtkEntry *entry, GtkEntryIconPosition icon_pos, GtkBinaryParameters *params) +{ + GtkRoot *root; /* Racine du composant */ + GBinContent *content; /* Contenu binaire à afficher */ + GtkTiledPanel *tiled; /* Panneau d'affichage complet */ + + root = gtk_widget_get_root(GTK_WIDGET(entry)); + + content = g_file_content_new("/bin/id"); + + tiled = gtk_binary_panel_new_for_content(content); + + unref_object(content); + + gtk_framework_window_add(GTK_FRAMEWORK_WINDOW(root), tiled); + +} diff --git a/plugins/pe/section.h b/src/gui/panels/binary-params.h index 2ac21d3..450da25 100644 --- a/plugins/pe/section.h +++ b/src/gui/panels/binary-params.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * section.h - prototypes pour la gestion des sections d'un PE + * binary-params.h - prototypes pour l'édition des paramètres initiaux d'un chargement de binaire * - * Copyright (C) 2010-2017 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,18 +21,21 @@ */ -#ifndef _PLUGINS_PE_SECTION_H -#define _PLUGINS_PE_SECTION_H +#ifndef _GUI_PANELS_BINARY_PARAMS_H +#define _GUI_PANELS_BINARY_PARAMS_H -#include "format.h" -#include "pe_def.h" +#include <gtk/gtk.h> +#include "../../glibext/helpers.h" -/* Recherche une section donnée au sein de binaire par indice. */ -image_section_header *read_all_pe_sections(const GPeFormat *, vmpa2t *); +#define GTK_TYPE_BINARY_PARAMETERS (gtk_binary_parameters_get_type()) -#endif /* _PLUGINS_PE_SECTION_H */ +DECLARE_GTYPE(GtkBinaryParameters, gtk_binary_parameters, GTK, BINARY_PARAMETERS); + + + +#endif /* _GUI_PANELS_BINARY_PARAMS_H */ diff --git a/src/gui/panels/binary-props.ui b/src/gui/panels/binary-params.ui index bd48ea8..dcbaf7c 100644 --- a/src/gui/panels/binary-props.ui +++ b/src/gui/panels/binary-params.ui @@ -1,13 +1,14 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> - <object class="GtkGrid" id="properties"> + <template class="GtkBinaryParameters" parent="GtkGrid"> <property name="margin-bottom">12</property> <property name="margin-end">12</property> <property name="margin-start">12</property> <property name="margin-top">12</property> <property name="column-spacing">12</property> <property name="row-spacing">8</property> + <child> <object class="GtkLabel"> <property name="label">Load and analyze a new file:</property> @@ -18,8 +19,9 @@ </layout> </object> </child> + <child> - <object class="GtkEntry" id="new_file"> + <object class="GtkEntry" id="filename"> <property name="secondary-icon-name">document-open-symbolic</property> <property name="placeholder-text">File location</property> <property name="hexpand">TRUE</property> @@ -31,9 +33,10 @@ <style> <class name="background"/> </style> - <signal name="icon-release" handler="g_binary_panel_on_new_file_entry_icon_release"/> + <signal name="icon-release" handler="gtk_binary_parameters_on_new_file_entry_icon_release"/> </object> </child> - </object> + + </template> </interface> diff --git a/src/gui/panels/binary.c b/src/gui/panels/binary.c index de0a4d0..f58c06b 100644 --- a/src/gui/panels/binary.c +++ b/src/gui/panels/binary.c @@ -26,8 +26,6 @@ #include "binary-int.h" -#include "../window.h" -#include "../../analysis/contents/file.h" #include "../../gtkext/helpers.h" #include "../../gtkext/hexview.h" @@ -43,10 +41,10 @@ static void gtk_binary_panel_class_init(GtkBinaryPanelClass *); static void gtk_binary_panel_init(GtkBinaryPanel *); /* Supprime toutes les références externes. */ -static void gtk_binary_panel_dispose(GtkBinaryPanel *); +static void gtk_binary_panel_dispose(GObject *); /* Procède à la libération totale de la mémoire. */ -static void gtk_binary_panel_finalize(GtkBinaryPanel *); +static void gtk_binary_panel_finalize(GObject *); @@ -57,49 +55,12 @@ static void gtk_binary_panel_finalize(GtkBinaryPanel *); -/* ---------------------- MANIPULATIONS D'UN PANNEAU GRAPHIQUE ---------------------- */ - - -/* Initialise la classe des panneaux graphiques pour binaires. */ -static void g_binary_panel_class_init(GBinaryPanelClass *); - -/* Initialise une instance de panneau graphique pour binaire. */ -static void g_binary_panel_init(GBinaryPanel *); - -/* Supprime toutes les références externes. */ -static void g_binary_panel_dispose(GBinaryPanel *); - -/* Procède à la libération totale de la mémoire. */ -static void g_binary_panel_finalize(GBinaryPanel *); - - - -/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ - - -/* Fournit une indication sur la personnalité du panneau. */ -static PanelItemPersonality g_binary_panel_get_personality(const GBinaryPanel *); - -/* Fournit un composant pour lancer l'activité d'un panneau. */ -static GtkWidget *g_binary_panel_get_launcher(GBinaryPanel *); - -/* Fournit un composant pour paramétrer l'activité d'un panneau. */ -static GtkWidget *g_binary_panel_get_properties(GBinaryPanel *); - -/* Ouvre une boîte de dialogue pour récupérer un fichier. */ -static void g_binary_panel_on_new_file_entry_icon_release(GtkEntry *, GtkEntryIconPosition, GBinaryPanel *); - -/* Fournit un composant représentant un panneau graphique. */ -static GtkTiledPanel *g_binary_panel_get_panel(GBinaryPanel *, GtkWidget *); - - - /* ---------------------------------------------------------------------------------- */ /* COEUR D'UN PANNEAU D'AFFICHAGE */ /* ---------------------------------------------------------------------------------- */ -/* Indique le type défini pour un panneau d'accueil. */ +/* Indique le type défini pour un panneau d'affichage de contenus d'un binaire. */ G_DEFINE_TYPE(GtkBinaryPanel, gtk_binary_panel, GTK_TYPE_TILED_PANEL); @@ -122,8 +83,8 @@ static void gtk_binary_panel_class_init(GtkBinaryPanelClass *class) object = G_OBJECT_CLASS(class); - object->dispose = (GObjectFinalizeFunc/* ! */)gtk_binary_panel_dispose; - object->finalize = (GObjectFinalizeFunc)gtk_binary_panel_finalize; + object->dispose = gtk_binary_panel_dispose; + object->finalize = gtk_binary_panel_finalize; widget = GTK_WIDGET_CLASS(class); @@ -155,7 +116,7 @@ static void gtk_binary_panel_init(GtkBinaryPanel *panel) /****************************************************************************** * * -* Paramètres : panel = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * @@ -165,18 +126,18 @@ static void gtk_binary_panel_init(GtkBinaryPanel *panel) * * ******************************************************************************/ -static void gtk_binary_panel_dispose(GtkBinaryPanel *panel) +static void gtk_binary_panel_dispose(GObject *object) { - gtk_widget_dispose_template(GTK_WIDGET(panel), GTK_TYPE_BINARY_PANEL); + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_BINARY_PANEL); - G_OBJECT_CLASS(gtk_binary_panel_parent_class)->dispose(G_OBJECT(panel)); + G_OBJECT_CLASS(gtk_binary_panel_parent_class)->dispose(object); } /****************************************************************************** * * -* Paramètres : panel = instance d'objet GLib à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * @@ -186,9 +147,9 @@ static void gtk_binary_panel_dispose(GtkBinaryPanel *panel) * * ******************************************************************************/ -static void gtk_binary_panel_finalize(GtkBinaryPanel *panel) +static void gtk_binary_panel_finalize(GObject *object) { - G_OBJECT_CLASS(gtk_binary_panel_parent_class)->finalize(G_OBJECT(panel)); + G_OBJECT_CLASS(gtk_binary_panel_parent_class)->finalize(object); } @@ -232,286 +193,3 @@ GtkTiledPanel *gtk_binary_panel_new_for_content(GBinContent *content) - -/* ---------------------------------------------------------------------------------- */ -/* MANIPULATIONS D'UN PANNEAU GRAPHIQUE */ -/* ---------------------------------------------------------------------------------- */ - - -/* Indique le type défini pour une manipulation de panneau pour binaires. */ -G_DEFINE_TYPE(GBinaryPanel, g_binary_panel, G_TYPE_PANEL_ITEM); - - -/****************************************************************************** -* * -* Paramètres : class = classe à initialiser. * -* * -* Description : Initialise la classe des panneaux graphiques pour binaires. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_binary_panel_class_init(GBinaryPanelClass *class) -{ - GObjectClass *object; /* Autre version de la classe */ - GPanelItemClass *panel; /* Encore une autre vision... */ - - object = G_OBJECT_CLASS(class); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_binary_panel_dispose; - object->finalize = (GObjectFinalizeFunc)g_binary_panel_finalize; - - panel = G_PANEL_ITEM_CLASS(class); - - panel->get_personality = (get_panel_item_personality_cb)g_binary_panel_get_personality; - panel->get_launcher = (get_panel_item_widget_cb)g_binary_panel_get_launcher; - panel->get_properties = (get_panel_item_widget_cb)g_binary_panel_get_properties; - panel->get_panel = (get_panel_item_panel_cb)g_binary_panel_get_panel; - -} - - -/****************************************************************************** -* * -* Paramètres : panel = instance à initialiser. * -* * -* Description : Initialise une instance de panneau graphique pour binaire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_binary_panel_init(GBinaryPanel *panel) -{ - -} - - -/****************************************************************************** -* * -* Paramètres : panel = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_binary_panel_dispose(GBinaryPanel *panel) -{ - G_OBJECT_CLASS(g_binary_panel_parent_class)->dispose(G_OBJECT(panel)); - -} - - -/****************************************************************************** -* * -* Paramètres : panel = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_binary_panel_finalize(GBinaryPanel *panel) -{ - G_OBJECT_CLASS(g_binary_panel_parent_class)->finalize(G_OBJECT(panel)); - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Constitue une définition de manipulation de panneau. * -* * -* Retour : Définition de propriétés mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GPanelItem *g_binary_panel_new(void) -{ - GPanelItem *result; /* Structure à retourner */ - - result = g_object_new(G_TYPE_BINARY_PANEL, NULL); - - return result; - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ -/* ---------------------------------------------------------------------------------- */ - - -/****************************************************************************** -* * -* Paramètres : panel = définition de panneau à consulter. * -* * -* Description : Fournit une indication sur la personnalité du panneau. * -* * -* Retour : Identifiant lié à la nature du panneau. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PanelItemPersonality g_binary_panel_get_personality(const GBinaryPanel *panel) -{ - PanelItemPersonality result; /* Personnalité à retourner */ - - result = PIP_MAIN_PANEL; - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : panel = définition de panneau à manipuler. * -* * -* Description : Fournit un composant pour lancer l'activité d'un panneau. * -* * -* Retour : Composant GTK (déjà ?) mis en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GtkWidget *g_binary_panel_get_launcher(GBinaryPanel *panel) -{ - GtkWidget *result; /* Composant à retourner */ - GtkBuilder *builder; /* Constructeur d'UI */ - - builder = gtk_builder_new_from_resource("/re/chrysalide/framework/gui/panels/binary-launch.ui"); - - result = GTK_WIDGET(gtk_builder_get_object(builder, "launcher")); - ref_object(result); - - unref_object(builder); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : panel = définition de panneau à manipuler. * -* * -* Description : Fournit un composant pour paramétrer l'activité d'un panneau.* -* * -* Retour : Composant GTK (déjà ?) mis en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GtkWidget *g_binary_panel_get_properties(GBinaryPanel *panel) -{ - GtkWidget *result; /* Composant à retourner */ - GtkBuilderScope *scope; /* Fonctions pour signaux */ - GtkBuilderCScope *cscope; /* Fonctions pour signaux */ - GtkBuilder *builder; /* Constructeur d'UI */ -#ifndef NDEBUG - gboolean status; /* Bilan d'un chargement */ -#endif - - scope = gtk_builder_cscope_new(); - cscope = GTK_BUILDER_CSCOPE(scope); - - gtk_builder_cscope_add_callback_symbol(cscope, BUILDER_CB(g_binary_panel_on_new_file_entry_icon_release)); - - builder = gtk_builder_new(); - gtk_builder_set_scope(builder, scope); - gtk_builder_set_current_object(builder, G_OBJECT(panel)); - -#ifndef NDEBUG - status = gtk_builder_add_from_resource(builder, "/re/chrysalide/framework/gui/panels/binary-props.ui", NULL); - assert(status); -#else - gtk_builder_add_from_resource(builder, "/re/chrysalide/framework/gui/panels/binary-props.ui", NULL); -#endif - - result = GTK_WIDGET(gtk_builder_get_object(builder, "properties")); - ref_object(result); - - unref_object(builder); - unref_object(scope); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : entry = zone de saisie concernée par l'appel. * -* icon_pos = position de l'icone incrustée dans la zone. * -* panel = définition de panneau à manipuler. * -* * -* Description : Ouvre une boîte de dialogue pour récupérer un fichier. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_binary_panel_on_new_file_entry_icon_release(GtkEntry *entry, GtkEntryIconPosition icon_pos, GBinaryPanel *panel) -{ - GtkRoot *root; /* Racine du composant */ - GtkTiledPanel *tiled; /* Panneau d'affichage complet */ - - root = gtk_widget_get_root(GTK_WIDGET(entry)); - - tiled = g_binary_panel_get_panel(panel, NULL); - - gtk_framework_window_add(GTK_FRAMEWORK_WINDOW(root), tiled); - -} - - -/****************************************************************************** -* * -* Paramètres : panel = définition de panneau à manipuler. * -* props = éventuels éléments graphiques de paramétrages. * -* * -* Description : Fournit un composant représentant un panneau graphique. * -* * -* Retour : Composant GTK (déjà ?) mis en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GtkTiledPanel *g_binary_panel_get_panel(GBinaryPanel *panel, GtkWidget *props) -{ - GtkTiledPanel *result; /* Composant à retourner */ - GBinContent *content; /* Contenu binaire à afficher */ - - content = g_file_content_new("/bin/id"); - - result = gtk_binary_panel_new_for_content(content); - - unref_object(content); - - return result; - -} diff --git a/src/gui/panels/binary.h b/src/gui/panels/binary.h index e92895b..26f8a7d 100644 --- a/src/gui/panels/binary.h +++ b/src/gui/panels/binary.h @@ -29,16 +29,12 @@ #include <gtk/gtk.h> -#include "../panel.h" #include "../../analysis/content.h" #include "../../glibext/helpers.h" #include "../../gtkext/panel.h" -/* ------------------------- COEUR D'UN PANNEAU D'AFFICHAGE ------------------------- */ - - #define GTK_TYPE_BINARY_PANEL (gtk_binary_panel_get_type()) DECLARE_GTYPE(GtkBinaryPanel, gtk_binary_panel, GTK, BINARY_PANEL); @@ -49,17 +45,4 @@ GtkTiledPanel *gtk_binary_panel_new_for_content(GBinContent *); -/* ---------------------- MANIPULATIONS D'UN PANNEAU GRAPHIQUE ---------------------- */ - - -#define G_TYPE_BINARY_PANEL (g_binary_panel_get_type()) - -DECLARE_GTYPE(GBinaryPanel, g_binary_panel, G, BINARY_PANEL); - - -/* Constitue une définition de manipulation de panneau. */ -GPanelItem *g_binary_panel_new(void); - - - #endif /* _GUI_PANELS_BINARY_H */ diff --git a/src/gui/panels/gresource.xml b/src/gui/panels/gresource.xml index d00953d..f49a16f 100644 --- a/src/gui/panels/gresource.xml +++ b/src/gui/panels/gresource.xml @@ -2,8 +2,7 @@ <gresources> <gresource prefix="/re/chrysalide/framework/gui/panels"> <file compressed="true">binary.ui</file> - <file compressed="true">binary-launch.ui</file> - <file compressed="true">binary-props.ui</file> + <file compressed="true">binary-params.ui</file> <file compressed="true">welcome.ui</file> <file compressed="true">welcome-hints.txt</file> </gresource> diff --git a/src/gui/panels/welcome-int.h b/src/gui/panels/welcome-int.h index d99d316..206bc2c 100644 --- a/src/gui/panels/welcome-int.h +++ b/src/gui/panels/welcome-int.h @@ -27,20 +27,15 @@ #include "welcome.h" -#include "../panel-int.h" #include "../../gtkext/panel-int.h" -/* ------------------------- COEUR D'UN PANNEAU D'AFFICHAGE ------------------------- */ - - /* Panneau d'accueil par défaut (instance) */ struct _GtkWelcomePanel { GtkTiledPanel parent; /* A laisser en premier */ - GListStore *store; /* Données brutes */ GtkListBox *list; /* Liste de lanceurs */ GtkStack *properties; /* Premières propriétés */ @@ -62,28 +57,5 @@ struct _GtkWelcomePanelClass }; -/* Met en place un nouveau panneau d'accueil. */ -bool gtk_welcome_panel_create(GtkWelcomePanel *); - - - -/* ---------------------- MANIPULATIONS D'UN PANNEAU GRAPHIQUE ---------------------- */ - - -/* Définition pour gestion par le framework d'un panneau graphique (instance) */ -struct _GWelcomePanel -{ - GPanelItem parent; /* A laisser en premier */ - -}; - -/* Définition pour gestion par le framework d'un panneau graphique (classe) */ -struct _GWelcomePanelClass -{ - GPanelItemClass parent; /* A laisser en premier */ - -}; - - #endif /* _GUI_PANELS_WELCOME_INT_H */ diff --git a/src/gui/panels/welcome.c b/src/gui/panels/welcome.c index 3cd349f..6e8763b 100644 --- a/src/gui/panels/welcome.c +++ b/src/gui/panels/welcome.c @@ -28,9 +28,6 @@ #include <assert.h> -#include <i18n.h> - - #include "welcome-int.h" #include "../core/panels.h" #include "../../common/shuffle.h" @@ -48,16 +45,10 @@ static void gtk_welcome_panel_class_init(GtkWelcomePanelClass *); static void gtk_welcome_panel_init(GtkWelcomePanel *); /* Supprime toutes les références externes. */ -static void gtk_welcome_panel_dispose(GtkWelcomePanel *); +static void gtk_welcome_panel_dispose(GObject *); /* Procède à la libération totale de la mémoire. */ -static void gtk_welcome_panel_finalize(GtkWelcomePanel *); - -/* Intègre une définition de panneau enregistrée. */ -static bool gtk_welcome_panel_add_launcher(GPanelItem *, GListStore *); - -/* Prépare un composant pour représenter une définition. */ -static GtkWidget *gtk_welcome_panel_create_launcher_widget(GPanelItem *, gpointer); +static void gtk_welcome_panel_finalize(GObject *); /* Réagit à un changement de sélection de la liste de panneaux. */ static void gtk_welcome_panel_on_selected_rows_changed(GtkListBox *, GtkWelcomePanel *); @@ -78,32 +69,6 @@ static void gtk_welcome_panel_on_next_hint_clicked(GtkButton *, GtkWelcomePanel -/* ---------------------- MANIPULATIONS D'UN PANNEAU GRAPHIQUE ---------------------- */ - - -/* Initialise la classe des panneaux graphiques pour binaires. */ -static void g_welcome_panel_class_init(GWelcomePanelClass *); - -/* Initialise une instance de panneau graphique pour binaire. */ -static void g_welcome_panel_init(GWelcomePanel *); - -/* Supprime toutes les références externes. */ -static void g_welcome_panel_dispose(GWelcomePanel *); - -/* Procède à la libération totale de la mémoire. */ -static void g_welcome_panel_finalize(GWelcomePanel *); - - - -/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ - - -/* Fournit une indication sur la personnalité du panneau. */ -static PanelItemPersonality g_welcome_panel_get_personality(const GWelcomePanel *); - -/* Fournit un composant représentant un panneau graphique. */ -static GtkTiledPanel *g_welcome_panel_get_panel(GWelcomePanel *, GtkWidget *); - /* ---------------------------------------------------------------------------------- */ @@ -134,8 +99,8 @@ static void gtk_welcome_panel_class_init(GtkWelcomePanelClass *class) object = G_OBJECT_CLASS(class); - object->dispose = (GObjectFinalizeFunc/* ! */)gtk_welcome_panel_dispose; - object->finalize = (GObjectFinalizeFunc)gtk_welcome_panel_finalize; + object->dispose = gtk_welcome_panel_dispose; + object->finalize = gtk_welcome_panel_finalize; widget = GTK_WIDGET_CLASS(class); @@ -169,13 +134,16 @@ static void gtk_welcome_panel_init(GtkWelcomePanel *panel) { GBytes *bytes; /* Données brutes de ressource */ const gchar *data; /* Données brutes natives */ + int min; /* Taille à gauche minimale */ + GtkConstraintLayout *layout; /* Disposition fixant la taille*/ + GtkConstraint *constraint; /* Contrainte à considérer */ gtk_widget_init_template(GTK_WIDGET(panel)); - panel->store = g_list_store_new(G_TYPE_PANEL_ITEM); - panel->other_child = NULL; + /* Chargement des astuces */ + bytes = g_resources_lookup_data("/re/chrysalide/framework/gui/panels/welcome-hints.txt", G_RESOURCE_LOOKUP_FLAGS_NONE, NULL); assert(bytes != NULL); @@ -191,105 +159,9 @@ static void gtk_welcome_panel_init(GtkWelcomePanel *panel) panel->cur_hint = 0; -} - - -/****************************************************************************** -* * -* Paramètres : panel = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void gtk_welcome_panel_dispose(GtkWelcomePanel *panel) -{ - gtk_widget_dispose_template(GTK_WIDGET(panel), GTK_TYPE_WELCOME_PANEL); - - g_clear_object(&panel->other_child); - - G_OBJECT_CLASS(gtk_welcome_panel_parent_class)->dispose(G_OBJECT(panel)); - -} - - -/****************************************************************************** -* * -* Paramètres : panel = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void gtk_welcome_panel_finalize(GtkWelcomePanel *panel) -{ - G_OBJECT_CLASS(gtk_welcome_panel_parent_class)->finalize(G_OBJECT(panel)); - - g_strfreev(panel->raw_hints); - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Crée une nouvelle instance de panneau d'accueil. * -* * -* Retour : Composant GTK mis en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GtkTiledPanel *gtk_welcome_panel_new(void) -{ - GtkTiledPanel *result; /* Instance à retourner */ - - result = g_object_new(GTK_TYPE_WELCOME_PANEL, NULL); - - if (!gtk_welcome_panel_create(GTK_WELCOME_PANEL(result))) - g_clear_object(&result); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : panel = panneau d'accueil à initialiser. * -* * -* Description : Met en place un nouveau panneau d'accueil. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool gtk_welcome_panel_create(GtkWelcomePanel *panel) -{ - bool result; /* Bilan à retourner */ - int min; /* Taille à gauche minimale */ - GtkConstraintLayout *layout; /* Disposition fixant la taille*/ - GtkConstraint *constraint; /* Contrainte à considérer */ - /* Constitution de la liste des démarreurs */ - result = browse_all_item_panels(true, (handle_panel_item_fc)gtk_welcome_panel_add_launcher, panel->store); - - gtk_list_box_bind_model(panel->list, G_LIST_MODEL(panel->store), - (GtkListBoxCreateWidgetFunc)gtk_welcome_panel_create_launcher_widget, - NULL, NULL); + populate_framework_panel_launcher_list(panel->list); /* Dimensionnement de la zone d'astuces */ @@ -327,57 +199,57 @@ bool gtk_welcome_panel_create(GtkWelcomePanel *panel) gtk_label_set_markup(panel->hints, panel->raw_hints[panel->cur_hint]); - return result; - } /****************************************************************************** * * -* Paramètres : item = définition de panneau à intégrer. * -* store = liste à compléter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * -* Description : Intègre une définition de panneau enregistrée. * +* Description : Supprime toutes les références externes. * * * -* Retour : true pour un parcours complet de la liste des définitions. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static bool gtk_welcome_panel_add_launcher(GPanelItem *item, GListStore *store) +static void gtk_welcome_panel_dispose(GObject *object) { - bool result; /* Poursuite du parcours */ + GtkWelcomePanel *panel; /* Version spécialisée */ + + gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_WELCOME_PANEL); - result = true; + panel = GTK_WELCOME_PANEL(object); - g_list_store_append(store, G_OBJECT(item)); + g_clear_object(&panel->other_child); - return result; + G_OBJECT_CLASS(gtk_welcome_panel_parent_class)->dispose(object); } /****************************************************************************** * * -* Paramètres : item = définition de panneau à consulter. * -* unused = adresse non utilisée ici. * +* Paramètres : object = instance d'objet GLib à traiter. * * * -* Description : Prépare un composant pour représenter une définition. * +* Description : Procède à la libération totale de la mémoire. * * * -* Retour : Composant de représentation de définition de panneau. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static GtkWidget *gtk_welcome_panel_create_launcher_widget(GPanelItem *item, gpointer unused) +static void gtk_welcome_panel_finalize(GObject *object) { - GtkWidget *result; /* Composant GTK à retourner */ + GtkWelcomePanel *panel; /* Version spécialisée */ + + panel = GTK_WELCOME_PANEL(object); - result = g_panel_item_get_launcher(item); + g_strfreev(panel->raw_hints); - return result; + G_OBJECT_CLASS(gtk_welcome_panel_parent_class)->finalize(object); } @@ -398,8 +270,6 @@ static GtkWidget *gtk_welcome_panel_create_launcher_widget(GPanelItem *item, gpo static void gtk_welcome_panel_on_selected_rows_changed(GtkListBox *box, GtkWelcomePanel *panel) { GtkListBoxRow *row; /* Ligne sélectionnée */ - int selected; /* Indice de sélection */ - GPanelItem *item; /* Elément correspondant */ GtkWidget *new; /* Nouvelles propriétés */ row = gtk_list_box_get_selected_row(box); @@ -414,7 +284,7 @@ static void gtk_welcome_panel_on_selected_rows_changed(GtkListBox *box, GtkWelco gtk_stack_set_visible_child(panel->properties, panel->def_child); gtk_stack_remove(panel->properties, panel->other_child); - g_clear_object(&panel->other_child); + panel->other_child = NULL; } @@ -423,24 +293,16 @@ static void gtk_welcome_panel_on_selected_rows_changed(GtkListBox *box, GtkWelco */ else { - selected = gtk_list_box_row_get_index(row); - item = g_list_model_get_item(G_LIST_MODEL(panel->store), selected); + new = get_framework_panel_parameters(row); + assert(new != NULL); - new = g_panel_item_get_properties(item); - - if (new == panel->other_child) - unref_object(new); - - else + if (new != panel->other_child) { gtk_stack_add_child(panel->properties, new); gtk_stack_set_visible_child(panel->properties, new); if (panel->other_child != NULL) - { gtk_stack_remove(panel->properties, panel->other_child); - g_clear_object(&panel->other_child); - } panel->other_child = new; @@ -513,178 +375,3 @@ static void gtk_welcome_panel_on_next_hint_clicked(GtkButton *button, GtkWelcome - -/* ---------------------------------------------------------------------------------- */ -/* MANIPULATIONS D'UN PANNEAU GRAPHIQUE */ -/* ---------------------------------------------------------------------------------- */ - - -/* Indique le type défini pour une manipulation de panneau de bienvenue. */ -G_DEFINE_TYPE(GWelcomePanel, g_welcome_panel, G_TYPE_PANEL_ITEM); - - -/****************************************************************************** -* * -* Paramètres : class = classe à initialiser. * -* * -* Description : Initialise la classe des panneaux graphiques pour binaires. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_welcome_panel_class_init(GWelcomePanelClass *class) -{ - GObjectClass *object; /* Autre version de la classe */ - GPanelItemClass *panel; /* Encore une autre vision... */ - - object = G_OBJECT_CLASS(class); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_welcome_panel_dispose; - object->finalize = (GObjectFinalizeFunc)g_welcome_panel_finalize; - - panel = G_PANEL_ITEM_CLASS(class); - - panel->get_personality = (get_panel_item_personality_cb)g_welcome_panel_get_personality; - panel->get_panel = (get_panel_item_panel_cb)g_welcome_panel_get_panel; - -} - - -/****************************************************************************** -* * -* Paramètres : panel = instance à initialiser. * -* * -* Description : Initialise une instance de panneau graphique pour binaire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_welcome_panel_init(GWelcomePanel *panel) -{ - -} - - -/****************************************************************************** -* * -* Paramètres : panel = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_welcome_panel_dispose(GWelcomePanel *panel) -{ - G_OBJECT_CLASS(g_welcome_panel_parent_class)->dispose(G_OBJECT(panel)); - -} - - -/****************************************************************************** -* * -* Paramètres : panel = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_welcome_panel_finalize(GWelcomePanel *panel) -{ - G_OBJECT_CLASS(g_welcome_panel_parent_class)->finalize(G_OBJECT(panel)); - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Constitue une définition de manipulation de panneau. * -* * -* Retour : Définition de propriétés mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GPanelItem *g_welcome_panel_new(void) -{ - GPanelItem *result; /* Structure à retourner */ - - result = g_object_new(G_TYPE_WELCOME_PANEL, NULL); - - return result; - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ -/* ---------------------------------------------------------------------------------- */ - - -/****************************************************************************** -* * -* Paramètres : panel = définition de panneau à consulter. * -* * -* Description : Fournit une indication sur la personnalité du panneau. * -* * -* Retour : Identifiant lié à la nature du panneau. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PanelItemPersonality g_welcome_panel_get_personality(const GWelcomePanel *panel) -{ - PanelItemPersonality result; /* Personnalité à retourner */ - - result = PIP_MAIN_PANEL | PIP_SINGLETON; - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : panel = définition de panneau à manipuler. * -* props = éventuels éléments graphiques de paramétrages. * -* * -* Description : Fournit un composant représentant un panneau graphique. * -* * -* Retour : Composant GTK (déjà ?) mis en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GtkTiledPanel *g_welcome_panel_get_panel(GWelcomePanel *panel, GtkWidget *props) -{ - GtkTiledPanel *result; /* Composant à retourner */ - - /** - * Il n'existe pas de composants de paramètrage pour ce panneau. - */ - assert(props == NULL); - - result = gtk_welcome_panel_new(); - - return result; - -} diff --git a/src/gui/panels/welcome.h b/src/gui/panels/welcome.h index d267f80..d9ea18d 100644 --- a/src/gui/panels/welcome.h +++ b/src/gui/panels/welcome.h @@ -29,36 +29,15 @@ #include <gtk/gtk.h> -#include "../panel.h" #include "../../glibext/helpers.h" #include "../../gtkext/panel.h" -/* ------------------------- COEUR D'UN PANNEAU D'AFFICHAGE ------------------------- */ - - #define GTK_TYPE_WELCOME_PANEL (gtk_welcome_panel_get_type()) DECLARE_GTYPE(GtkWelcomePanel, gtk_welcome_panel, GTK, WELCOME_PANEL); -/* Crée une nouvelle instance de panneau d'accueil. */ -GtkTiledPanel *gtk_welcome_panel_new(void); - - - -/* ---------------------- MANIPULATIONS D'UN PANNEAU GRAPHIQUE ---------------------- */ - - -#define G_TYPE_WELCOME_PANEL (g_welcome_panel_get_type()) - -DECLARE_GTYPE(GWelcomePanel, g_welcome_panel, G, WELCOME_PANEL); - - -/* Constitue une définition de manipulation de panneau. */ -GPanelItem *g_welcome_panel_new(void); - - #endif /* _GUI_PANELS_WELCOME_H */ diff --git a/src/gui/style.css b/src/gui/style.css index 44161f7..9078310 100644 --- a/src/gui/style.css +++ b/src/gui/style.css @@ -1,4 +1,13 @@ +/* Extension de style */ + +.dim-label { + + margin-bottom: 4px; + +} + + .boxed-widget { border-top-left-radius: 12px; diff --git a/src/gui/window.c b/src/gui/window.c index 2680c89..f45cec9 100644 --- a/src/gui/window.c +++ b/src/gui/window.c @@ -28,6 +28,7 @@ #include "window-int.h" #include "core/panels.h" #include "dialogs/about.h" +#include "dialogs/preferences.h" #include "panels/welcome.h" #include "../gtkext/helpers.h" #include "../gtkext/statusstack.h" @@ -46,6 +47,9 @@ static void gtk_framework_window_dispose(GtkFrameworkWindow *); /* Procède à la libération totale de la mémoire. */ static void gtk_framework_window_finalize(GtkFrameworkWindow *); +/* Réagit à une activation du menu "Préférences" de la fenetre. */ +static void gtk_framework_window_activate_preferences(GSimpleAction *, GVariant *, gpointer); + /* Réagit à une activation du menu "A propos de" de la fenetre. */ static void gtk_framework_window_activate_about(GSimpleAction *, GVariant *, gpointer); @@ -88,6 +92,8 @@ static void gtk_framework_window_class_init(GtkFrameworkWindowClass *class) /* Active une action native (cf. https://docs.gtk.org/gtk4/class.Window.html#actions) */ gtk_widget_class_add_binding_action(widget, GDK_KEY_Q, GDK_CONTROL_MASK, "window.close", NULL); + gtk_widget_class_add_binding_action(widget, GDK_KEY_comma, GDK_CONTROL_MASK, "win.preferences", NULL); + } @@ -106,6 +112,7 @@ static void gtk_framework_window_class_init(GtkFrameworkWindowClass *class) static void gtk_framework_window_init(GtkFrameworkWindow *window) { static GActionEntry app_entries[] = { + { "preferences", gtk_framework_window_activate_preferences, NULL, NULL, NULL }, { "about", gtk_framework_window_activate_about, NULL, NULL, NULL }, }; @@ -208,7 +215,6 @@ GtkApplicationWindow *gtk_framework_window_new(GtkApplication *app) bool gtk_framework_window_create(GtkFrameworkWindow *window, GtkApplication *app) { bool result; /* Bilan à retourner */ - GPanelItem *item; /* Définition de panneau */ GtkTiledPanel *panel; /* Panneau d'affichage */ GtkCssProvider *css; /* Feuille de style maison */ @@ -218,13 +224,10 @@ bool gtk_framework_window_create(GtkFrameworkWindow *window, GtkApplication *app /* Inclusion d'un écran d'accueil */ - item = find_item_panel_by_type(G_TYPE_WELCOME_PANEL); + panel = get_framework_panel_singleton(GTK_TYPE_WELCOME_PANEL); - panel = g_panel_item_get_panel(item); gtk_framework_window_add(window, panel); - unref_object(item); - /* Chargement des extensions de thème */ css = gtk_css_provider_new(); @@ -258,6 +261,34 @@ bool gtk_framework_window_create(GtkFrameworkWindow *window, GtkApplication *app * unused = adresse non utilisée ici. * * _window = instance de fenêtre principale à manipuler. * * * +* Description : Réagit à une activation du menu "Préférences" de la fenetre. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_framework_window_activate_preferences(GSimpleAction *action, GVariant *unused, gpointer _window) +{ + GtkFrameworkWindow *window; /* Fenêtre principale associée */ + GtkWindow *dialog; /* Boîte de dialogue à afficher*/ + + window = _window; + + dialog = gtk_preferences_dialog_new(GTK_WINDOW(window)); + + gtk_window_present(dialog); + +} + + +/****************************************************************************** +* * +* Paramètres : action = désignation de l'action concernée par l'appel. * +* unused = adresse non utilisée ici. * +* _window = instance de fenêtre principale à manipuler. * +* * * Description : Réagit à une activation du menu "A propos de" de la fenetre. * * * * Retour : - * diff --git a/src/gui/window.ui b/src/gui/window.ui index 59b8b2c..363ea54 100644 --- a/src/gui/window.ui +++ b/src/gui/window.ui @@ -5,6 +5,7 @@ <section> <item> <attribute name="label" translatable="yes">Preferences</attribute> + <attribute name="action">win.preferences</attribute> </item> <item> <attribute name="label" translatable="yes">About</attribute> diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index dd191fa..fa65484 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -1,13 +1,18 @@ noinst_LTLIBRARIES = libplugins.la -libplugins_la_SOURCES = \ - dt.h dt.c \ - pglist.h pglist.c \ - plugin-def.h \ - plugin-int.h \ - plugin.h plugin.c \ - self.h +libplugins_la_SOURCES = \ + manager-int.h \ + manager.h manager.c \ + native-int.h \ + native.h native.c \ + pglist.h pglist.c \ + plugin-def.h \ + plugin-int.h \ + plugin.h plugin.c \ + self.h \ + tweakable-int.h \ + tweakable.h tweakable.c libplugins_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) diff --git a/src/plugins/dt.c b/src/plugins/dt.c deleted file mode 100644 index 2899845..0000000 --- a/src/plugins/dt.c +++ /dev/null @@ -1,562 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * dt.c - possibilité de créer de nouveaux types de façon dynamique - * - * Copyright (C) 2019 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/>. - */ - - -#include "dt.h" - - -#include <assert.h> -#include <malloc.h> -#include <string.h> - - -#include "pglist.h" - - - -/* ------------------------- MODULE DE GESTION DES NOUVEAUX ------------------------- */ - - -#define G_TYPE_DYNAMIC_TYPES g_dynamic_types_get_type() -#define G_DYNAMIC_TYPES(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_DYNAMIC_TYPES, GDynamicTypes)) -#define G_IS_DYNAMIC_TYPES(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_DYNAMIC_TYPES)) -#define G_DYNAMIC_TYPES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DYNAMIC_TYPES, GDynamicTypesClass)) -#define G_IS_DYNAMIC_TYPES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DYNAMIC_TYPES)) -#define G_DYNAMIC_TYPES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DYNAMIC_TYPES, GDynamicTypesClass)) - - -/* Mémorisation des caractéristiques de type */ -typedef struct _type_dyn_info_t -{ - GType type; /* Identifiant unique obtenu */ - - GClassInitFunc cinit; /* Phase d'initialisation #1 */ - gconstpointer data; /* Eventuelles données utiles */ - - GInstanceInitFunc init; /* Phase d'initialisation #2 */ - -} type_dyn_info_t; - -/* Description de fichier binaire (instance) */ -typedef struct _GDynamicTypes -{ - GObject parent; /* A laisser en premier */ - - type_dyn_info_t **info; /* Liste d'informations utiles */ - size_t count; /* Taille de cette liste */ - -} GDynamicTypes; - -/* Description de fichier binaire (classe) */ -typedef struct _GDynamicTypesClass -{ - GObjectClass parent; /* A laisser en premier */ - -} GDynamicTypesClass; - - -/* Indique le type défini pour une gestion de types dynamique. */ -static GType g_dynamic_types_get_type(void); - -/* Initialise la classe de gestion de types dynamique. */ -static void g_dynamic_types_class_init(GDynamicTypesClass *); - -/* Initialise une gestion de types dynamique. */ -static void g_dynamic_types_init(GDynamicTypes *); - -/* Procède à l'initialisation de l'interface de typage nouveau. */ -static void g_dynamic_types_interface_init(GTypePluginClass *); - -/* Supprime toutes les références externes. */ -static void g_dynamic_types_dispose(GDynamicTypes *); - -/* Procède à la libération totale de la mémoire. */ -static void g_dynamic_types_finalize(GDynamicTypes *); - -/* Crée un nouveau gestionnaire de nouveaux types. */ -static GDynamicTypes *g_dynamic_types_new(void); - -/* Marque une augmentation des utilisations. */ -static void g_dynamic_types_use(GDynamicTypes *); - -/* Marque une diminution des utilisations. */ -static void g_dynamic_types_unuse(GDynamicTypes *); - -/* Complète la définition d'un type dynamiquement. */ -static void g_dynamic_types_complete_type(GDynamicTypes *, GType, GTypeInfo *, GTypeValueTable *); - -/* Retrouve les informations concernant un type dynamique. */ -static type_dyn_info_t *g_dynamic_types_find(const GDynamicTypes *, GType); - -/* Fournit un identifiant GLib pour un nouveau type. */ -static GType g_dynamic_types_register_type(GDynamicTypes *, GType, const char *, GClassInitFunc, gconstpointer, GInstanceInitFunc); - - - -/* ----------------------- ACCOMPAGNEMENTS DES NOUVEAUX TYPES ----------------------- */ - - -/* Encadrement des nouveaux types dérivés */ -static GDynamicTypes *_chrysalide_dtypes = NULL; - - - -/* ---------------------------------------------------------------------------------- */ -/* MODULE DE GESTION DES NOUVEAUX */ -/* ---------------------------------------------------------------------------------- */ - -/* Indique le type défini pour une gestion de types dynamique. */ -G_DEFINE_TYPE_WITH_CODE(GDynamicTypes, g_dynamic_types, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE(G_TYPE_TYPE_PLUGIN, g_dynamic_types_interface_init)); - - -/****************************************************************************** -* * -* Paramètres : klass = classe à initialiser. * -* * -* Description : Initialise la classe de gestion de types dynamique. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_class_init(GDynamicTypesClass *klass) -{ - GObjectClass *object; /* Autre version de la classe */ - - object = G_OBJECT_CLASS(klass); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_dynamic_types_dispose; - object->finalize = (GObjectFinalizeFunc)g_dynamic_types_finalize; - -} - - -/****************************************************************************** -* * -* Paramètres : types = instance à initialiser. * -* * -* Description : Initialise une gestion de types dynamique. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_init(GDynamicTypes *types) -{ - -} - -/****************************************************************************** -* * -* Paramètres : iface = interface GLib à initialiser. * -* * -* Description : Procède à l'initialisation de l'interface de typage nouveau. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_interface_init(GTypePluginClass *iface) -{ - iface->use_plugin = (GTypePluginUse)g_dynamic_types_use; - iface->unuse_plugin = (GTypePluginUnuse)g_dynamic_types_unuse; - iface->complete_type_info = (GTypePluginCompleteTypeInfo)g_dynamic_types_complete_type; - -} - -/****************************************************************************** -* * -* Paramètres : types = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_dispose(GDynamicTypes *types) -{ - size_t i; /* Boucle de parcours */ - type_dyn_info_t *info; /* Information à exploiter */ - gpointer g_class; /* Classe à oublier */ - - for (i = 0; i < types->count; i++) - { - info = types->info[i]; - - if (info->type != G_TYPE_INVALID) - { - g_class = g_type_class_peek(info->type); - - /** - * Constat est fait qu'un type implémenté en Python mais dont les - * instanciations conduisent toutes à des exceptions n'est pas - * associé à une classe à ce moment d'exécution. - * - * Cf. fonction testKnownFormatConstructor() de la suite de tests - * - * Cf. documentation de g_type_class_unref() également : - * - * """ - * Once the last reference count of a class has been released, - * classes may be finalized by the type system - * """ - */ - - if (g_class != NULL) - g_type_class_unref(g_class); - - info->type = G_TYPE_INVALID; - - } - - } - - G_OBJECT_CLASS(g_dynamic_types_parent_class)->dispose(G_OBJECT(types)); - -} - - -/****************************************************************************** -* * -* Paramètres : types = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_finalize(GDynamicTypes *types) -{ - size_t i; /* Boucle de parcours */ - - for (i = 0; i < types->count; i++) - free(types->info[i]); - - if (types->info != NULL) - free(types->info); - - G_OBJECT_CLASS(g_dynamic_types_parent_class)->finalize(G_OBJECT(types)); - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Crée un nouveau gestionnaire de nouveaux types. * -* * -* Retour : Instance mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GDynamicTypes *g_dynamic_types_new(void) -{ - GDynamicTypes *result; /* Adresse à retourner */ - - result = g_object_new(G_TYPE_DYNAMIC_TYPES, NULL); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : types = gestionnaire de types courant. * -* * -* Description : Marque une augmentation des utilisations. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_use(GDynamicTypes *types) -{ - -} - - -/****************************************************************************** -* * -* Paramètres : types = gestionnaire de types courant. * -* * -* Description : Marque une diminution des utilisations. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_unuse(GDynamicTypes *types) -{ - -} - - -/****************************************************************************** -* * -* Paramètres : types = gestionnaire de types courant. * -* type = nouveau type GLib à traiter. * -* info = information concernant ce type à constituer. [OUT] * -* table = table de valeur à éventuellement initialiser. [OUT] * -* * -* Description : Complète la définition d'un type dynamiquement. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_dynamic_types_complete_type(GDynamicTypes *types, GType type, GTypeInfo *info, GTypeValueTable *table) -{ - type_dyn_info_t *nfo; /* Source d'inspiration */ - GType parent; /* Type parent du type */ - GTypeQuery query; /* Informations complémentaires*/ - - /* Consultation */ - - nfo = g_dynamic_types_find(types, type); - assert(nfo != NULL); - - parent = g_type_parent(type); - g_type_query(parent, &query); - - /* Définition */ - - info->class_size = query.class_size; - info->class_init = nfo->cinit; - info->class_data = nfo->data; - - info->instance_size = query.instance_size; - info->instance_init = nfo->init; - -} - - -/****************************************************************************** -* * -* Paramètres : parent = type GLib parent. * -* type = identifiant du type GLib à considérer. * -* * -* Description : Retrouve les informations concernant un type dynamique. * -* * -* Retour : Structure contenant les informations associées au type. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static type_dyn_info_t *g_dynamic_types_find(const GDynamicTypes *types, GType target) -{ - type_dyn_info_t *result; /* Informations à retourner */ - size_t i; /* Boucle de parcours */ - - result = NULL; - - for (i = 0; i < types->count && result == NULL; i++) - if (types->info[i]->type == target) - result = types->info[i]; - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : parent = type GLib parent. * -* name = désignation du nouveau type. * -* cinit = procédure d'initialisation de la classe associée. * -* data = éventuelles données à associer à la future classe. * -* init = procédure d'initialisation pour chaque instance. * -* * -* Description : Fournit un identifiant GLib pour un nouveau type. * -* * -* Retour : identifiant d'un nouveau type valide, ou G_TYPE_INVALID. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GType g_dynamic_types_register_type(GDynamicTypes *types, GType parent, const char *name, GClassInitFunc cinit, gconstpointer data, GInstanceInitFunc init) -{ - GType result; /* Identifiant à retourner */ - type_dyn_info_t *new; /* Mémorisation de paramètres */ - - /* Création d'un nouveau type adapté */ - - result = g_type_register_dynamic(parent, name, G_TYPE_PLUGIN(types), 0); - - if (result == G_TYPE_INVALID) - goto exit; - - new = malloc(sizeof(type_dyn_info_t)); - - new->type = result; - - new->cinit = cinit; - new->data = data; - - new->init = init; - - /* Inscription définitive */ - - types->info = realloc(types->info, ++types->count * sizeof(type_dyn_info_t *)); - - types->info[types->count - 1] = new; - - exit: - - return result; - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* ACCOMPAGNEMENTS DES NOUVEAUX TYPES */ -/* ---------------------------------------------------------------------------------- */ - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Lance le support de dérivations de types dans Chrysalide. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool init_chrysalide_dynamic_types(void) -{ - bool result; /* Bilan à retourner */ - - _chrysalide_dtypes = g_dynamic_types_new(); - - result = (_chrysalide_dtypes != NULL); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Arrête le support de dérivations de types dans Chrysalide. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void exit_chrysalide_dynamic_types(void) -{ - g_object_unref(G_OBJECT(_chrysalide_dtypes)); - -} - - -/****************************************************************************** -* * -* Paramètres : parent = type GLib parent. * -* name = désignation du nouveau type. * -* cinit = procédure d'initialisation de la classe associée. * -* data = éventuelles données à associer à la future classe. * -* init = procédure d'initialisation pour chaque instance. * -* * -* Description : Fournit un identifiant GLib pour un nouveau type. * -* * -* Retour : Identifiant d'un nouveau type valide, ou G_TYPE_INVALID. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GType build_dynamic_type(GType parent, const char *name, GClassInitFunc cinit, gconstpointer data, GInstanceInitFunc init) -{ - GType result; /* Identifiant à retourner */ - - result = g_type_from_name(name); - - if (result == 0) - result = g_dynamic_types_register_type(_chrysalide_dtypes, parent, name, cinit, data, init); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : type = type d'instance à créer. * -* * -* Description : Crée un objet à partir d'un type, dynamique ou classique. * -* * -* Retour : Instance d'objet mise en place ou NULL en cas d'erreur. * -* * -* Remarques : - * -* * -******************************************************************************/ - -gpointer create_object_from_type(GType type) -{ - GObject *result; /* Instance à retourner */ - - result = NULL; - - if (g_dynamic_types_find(_chrysalide_dtypes, type) != NULL) - result = build_type_instance(type); - - else - result = g_object_new(type, NULL); - - assert(result != NULL); - - return result; - -} diff --git a/src/plugins/dt.h b/src/plugins/dt.h deleted file mode 100644 index 5c92593..0000000 --- a/src/plugins/dt.h +++ /dev/null @@ -1,50 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * dt.h - prototypes pour la possibilité de créer de nouveaux types de façon dynamique - * - * Copyright (C) 2019 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 _PLUGINS_DT_H -#define _PLUGINS_DT_H - - -#include <glib-object.h> -#include <stdbool.h> - - - -/* ----------------------- ACCOMPAGNEMENTS DES NOUVEAUX TYPES ----------------------- */ - - -/* Lance le support de dérivations de types dans Chrysalide. */ -bool init_chrysalide_dynamic_types(void); - -/* Arrête le support de dérivations de types dans Chrysalide. */ -void exit_chrysalide_dynamic_types(void); - -/* Fournit un identifiant GLib pour un nouveau type. */ -GType build_dynamic_type(GType, const char *, GClassInitFunc, gconstpointer, GInstanceInitFunc); - -/* Crée un objet à partir d'un type, dynamique ou classique. */ -gpointer create_object_from_type(GType); - - - -#endif /* _PLUGINS_PYCHRYSALIDE_DT_H */ diff --git a/src/plugins/manager-int.h b/src/plugins/manager-int.h new file mode 100644 index 0000000..dbd1d69 --- /dev/null +++ b/src/plugins/manager-int.h @@ -0,0 +1,54 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * manager-int.h - définitions internes propres aux interventions dans la gestion des extensions + * + * Copyright (C) 2025 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 _PLUGINS_MANAGER_INT_H +#define _PLUGINS_MANAGER_INT_H + + +#include "manager.h" + + + +/* -------------------- INTERVENTION DANS LA GESTION DE GREFFONS -------------------- */ + + +/* Accompagne la fin du chargement des modules natifs. */ +typedef void (* handle_native_plugins_cb) (GPluginManager *); + +/* Prend acte du chargement de l'ensemble des greffons. */ +typedef void (* handle_all_plugins_cb) (GPluginManager *); + + +/* Accompagnant dans la gestion des extensions (interface) */ +struct _GPluginManagerInterface +{ + GTypeInterface base_iface; /* A laisser en premier */ + + handle_native_plugins_cb handle_native; /* Greffons natifs chargés */ + handle_all_plugins_cb handle_all; /* Ensemble des greffons chargé*/ + +}; + + + +#endif /* _PLUGINS_MANAGER_INT_H */ diff --git a/src/plugins/manager.c b/src/plugins/manager.c new file mode 100644 index 0000000..381fbc1 --- /dev/null +++ b/src/plugins/manager.c @@ -0,0 +1,113 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * manager.c - intervention dans la gestion des extensions + * + * Copyright (C) 2025 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 Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "manager.h" + + +#include "manager-int.h" + + + +/* -------------------- INTERVENTION DANS LA GESTION DE GREFFONS -------------------- */ + + +/* Procède à l'initialisation de l'interface de gestion. */ +static void g_plugin_manager_default_init(GPluginManagerInterface *); + + + +/* ---------------------------------------------------------------------------------- */ +/* INTERVENTION DANS LA GESTION DE GREFFONS */ +/* ---------------------------------------------------------------------------------- */ + + +/* Détermine le type d'une interface pour l'intervention dans la gestion des greffons. */ +G_DEFINE_INTERFACE(GPluginManager, g_plugin_manager, G_TYPE_OBJECT) + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de gestion. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_plugin_manager_default_init(GPluginManagerInterface *iface) +{ + iface->handle_native = NULL; + iface->handle_all = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : manager = interface à manipuler. * +* * +* Description : Accompagne la fin du chargement des modules natifs. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_plugin_manager_handle_native_plugins_loaded_event(GPluginManager *manager) +{ + GPluginManagerInterface *iface; /* Interface utilisée */ + + iface = G_PLUGIN_MANAGER_GET_IFACE(manager); + + if (iface->handle_native != NULL) + iface->handle_native(manager); + +} + + +/****************************************************************************** +* * +* Paramètres : manager = interface à manipuler. * +* * +* Description : Prend acte du chargement de l'ensemble des greffons. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_plugin_manager_handle_all_plugins_loaded_event(GPluginManager *manager) +{ + GPluginManagerInterface *iface; /* Interface utilisée */ + + iface = G_PLUGIN_MANAGER_GET_IFACE(manager); + + if (iface->handle_all != NULL) + iface->handle_all(manager); + +} diff --git a/src/plugins/manager.h b/src/plugins/manager.h new file mode 100644 index 0000000..2eb90a8 --- /dev/null +++ b/src/plugins/manager.h @@ -0,0 +1,61 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * manager.h - prototypes pour l'intervention dans la gestion des extensions + * + * Copyright (C) 2025 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 Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_MANAGER_H +#define _PLUGINS_MANAGER_H + + +#include "../glibext/helpers.h" + + + +/* -------------------- INTERVENTION DANS LA GESTION DE GREFFONS -------------------- */ + + +#define G_TYPE_PLUGIN_MANAGER (g_plugin_manager_get_type()) + +DECLARE_INTERFACE(GPluginManager, g_plugin_manager, G, PLUGIN_MANAGER); + + +/* Accompagne la fin du chargement des modules natifs. */ +void g_plugin_manager_handle_native_plugins_loaded_event(GPluginManager *); + +/* Prend acte du chargement de l'ensemble des greffons. */ +void g_plugin_manager_handle_all_plugins_loaded_event(GPluginManager *); + + + +/* -------------------- SOLLICITATION DES FONCTIONNALITES CREEES -------------------- */ + + +#define notify_native_plugins_loaded() \ + process_all_plugins_for(G_TYPE_PLUGIN_MANAGER, G_PLUGIN_MANAGER, \ + g_plugin_manager_handle_native_plugins_loaded_event) + +#define notify_all_plugins_loaded() \ + process_all_plugins_for(G_TYPE_PLUGIN_MANAGER, G_PLUGIN_MANAGER, \ + g_plugin_manager_handle_all_plugins_loaded_event) + + + +#endif /* _PLUGINS_MANAGER_H */ diff --git a/src/plugins/native-int.h b/src/plugins/native-int.h new file mode 100644 index 0000000..575994f --- /dev/null +++ b/src/plugins/native-int.h @@ -0,0 +1,73 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * native-int.h - prototypes pour les structures internes des greffons natifs + * + * Copyright (C) 2025 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 _PLUGINS_NATIVE_INT_H +#define _PLUGINS_NATIVE_INT_H + + +#include "native.h" + + +#include "plugin-int.h" + + + +/* Marqueur identifiable */ +#define CHRYSALIDE_PLUGIN_MAGIC 0xdeadc0de + + +/* Greffon natif pour Chrysalide (instance) */ +struct _GNativePlugin +{ + GPluginModule parent; /* A laisser en premier */ + + /** + * Le module porte le code et les données en mémoire. + * + * Les fonctions *_dispose() et *_finalize() accompagnant la libération des + * greffons de la mémoire ne peuvent donc pas libérer ce module car elles + * scieraient la branche sur laquelle elles se trouvent. + * + * Par ailleurs, même s'ils sont conservés dans chaque greffon, les modules + * sont mis en place dans le code principal. C'est donc ce dernier qui les + * libère, dans la fonction on_plugin_ref_toggle(). + */ + GModule *module; /* Structure de chargement GLib*/ + +}; + + +/* Greffon natif pour Chrysalide (classe) */ +struct _GNativePluginClass +{ + GPluginModuleClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un greffon natif. */ +bool g_native_plugin_create(GNativePlugin *, const char *, const char *, const char *, const char *, const char * const *, size_t, GModule *); + + + +#endif /* _PLUGINS_NATIVE_INT_H */ diff --git a/src/plugins/native.c b/src/plugins/native.c new file mode 100644 index 0000000..de20abe --- /dev/null +++ b/src/plugins/native.c @@ -0,0 +1,295 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * native.c - interactions avec un greffon natif donné + * + * Copyright (C) 2025 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "native.h" + + +#include "native-int.h" +#include "../common/cpp.h" + + + +/* ------------------------- COMPOSITION DE NOUVEAU GREFFON ------------------------- */ + + +/* Initialise la classe des greffons natifs. */ +static void g_native_plugin_class_init(GNativePluginClass *); + +/* Initialise une instance de greffon natif. */ +static void g_native_plugin_init(GNativePlugin *); + +/* Supprime toutes les références externes. */ +static void g_native_plugin_dispose(GNativePlugin *); + +/* Procède à la libération totale de la mémoire. */ +static void g_native_plugin_finalize(GNativePlugin *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Pointe le fichier contenant le greffon manipulé. */ +static char *g_native_plugin_get_filename(const GNativePlugin *); + +/* Fournit le nom brut associé au greffon. */ +static char *g_native_plugin_get_modname(const GNativePlugin *); + + + +/* ---------------------------------------------------------------------------------- */ +/* COMPOSITION DE NOUVEAU GREFFON */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un greffon Python. */ +G_DEFINE_TYPE(GNativePlugin, g_native_plugin, G_TYPE_PLUGIN_MODULE); + + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* * +* Description : Initialise la classe des greffons natifs. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_native_plugin_class_init(GNativePluginClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + GPluginModuleClass *plugin; /* Version parente de la classe*/ + + object = G_OBJECT_CLASS(class); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_native_plugin_dispose; + object->finalize = (GObjectFinalizeFunc)g_native_plugin_finalize; + + plugin = G_PLUGIN_MODULE_CLASS(class); + + plugin->get_filename = (get_plugin_filename_fc)g_native_plugin_get_filename; + plugin->get_modname = (get_plugin_modname_fc)g_native_plugin_get_modname; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser. * +* * +* Description : Initialise une instance de greffon natif. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_native_plugin_init(GNativePlugin *plugin) +{ + plugin->module = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_native_plugin_dispose(GNativePlugin *plugin) +{ + G_OBJECT_CLASS(g_native_plugin_parent_class)->dispose(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_native_plugin_finalize(GNativePlugin *plugin) +{ + G_OBJECT_CLASS(g_native_plugin_parent_class)->finalize(G_OBJECT(plugin)); + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = instance à initialiser pleinement. * +* name = nom du greffon pour référence, principalement. * +* desc = présentation éventuelle à destination humaine. * +* version = indication de version éventuelle. * +* url = référence vers une ressource en ligne. * +* required = liste de dépendances éventuelles ou NULL. * +* count = taille de cette liste. * +* module = extension vue du système. * +* * +* Description : Met en place un greffon natif. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : Le transfert de propriétée du module est total. * +* * +******************************************************************************/ + +bool g_native_plugin_create(GNativePlugin *plugin, const char *name, const char *desc, const char *version, const char *url, const char * const *required, size_t count, GModule *module) +{ + bool result; /* Bilan à retourner */ + + result = g_plugin_module_create(G_PLUGIN_MODULE(plugin), name, desc, version, url, required, count); + + if (result) + plugin->module = module; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à consulter. * +* * +* Description : Renvoie la structure opaque associée au module en mémoire. * +* * +* Retour : Structure de chargement côté GLib. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GModule *g_native_plugin_get_module(const GNativePlugin *plugin) +{ + GModule *result; /* Accès au module à renvoyer */ + + result = plugin->module; + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à consulter. * +* * +* Description : Pointe le fichier contenant le greffon manipulé. * +* * +* Retour : Chemin d'accès au greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_native_plugin_get_filename(const GNativePlugin *plugin) +{ + char *result; /* Chemin d'accès à renvoyer */ + + result = strdup(g_module_name(plugin->module)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = greffon à valider. * +* * +* Description : Fournit le nom brut associé au greffon. * +* * +* Retour : Désignation brute du greffon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_native_plugin_get_modname(const GNativePlugin *plugin) +{ + char *result; /* Désignation brute à renvoyer*/ + char *path; /* Chemin à traiter */ + char *filename; /* Nom de bibliothèque partagée*/ + size_t length; /* Taille du nom */ + int ret; /* Bilan d'une comparaison */ + + path = g_native_plugin_get_filename(plugin); + + filename = basename(path); + + if (strncmp(filename, "lib", 3) == 0) + filename += 3; + + length = strlen(filename); + +#ifdef _WIN32 +# define SHARED_SUFFIX ".dll" +#else +# define SHARED_SUFFIX ".so" +#endif + + if (length >= STATIC_STR_SIZE(SHARED_SUFFIX)) + { + ret = strncmp(&filename[length - STATIC_STR_SIZE(SHARED_SUFFIX)], + SHARED_SUFFIX, + STATIC_STR_SIZE(SHARED_SUFFIX)); + + if (ret == 0) + filename[length - STATIC_STR_SIZE(SHARED_SUFFIX)] = '\0'; + + } + + result = strdup(filename); + + free(path); + + return result; + +} diff --git a/plugins/pychrysalide/plugins/constants.h b/src/plugins/native.h index 56612c9..18039c8 100644 --- a/plugins/pychrysalide/plugins/constants.h +++ b/src/plugins/native.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * constants.h - prototypes pour l'ajout des constantes principales + * native.h - prototypes pour les interactions avec un greffon natif donné * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,20 +22,25 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_CONSTANTS_H -#define _PLUGINS_PYCHRYSALIDE_PLUGINS_CONSTANTS_H +#ifndef _PLUGINS_NATIVE_H +#define _PLUGINS_NATIVE_H -#include <Python.h> -#include <stdbool.h> +#include <gmodule.h> -/* Définit les constantes relatives aux greffons Python. */ -bool define_plugin_module_constants(PyTypeObject *); +#include "../glibext/helpers.h" -/* Tente de convertir en constante PluginAction. */ -int convert_to_plugin_action(PyObject *, void *); +#define G_TYPE_NATIVE_PLUGIN (g_native_plugin_get_type()) -#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_CONSTANTS_H */ +DECLARE_GTYPE(GNativePlugin, g_native_plugin, G, NATIVE_PLUGIN); + + +/* Renvoie la structure opaque associée au module en mémoire. */ +GModule *g_native_plugin_get_module(const GNativePlugin *); + + + +#endif /* _PLUGINS_NATIVE_H */ diff --git a/src/plugins/pglist.c b/src/plugins/pglist.c index e4cb825..3e107b8 100644 --- a/src/plugins/pglist.c +++ b/src/plugins/pglist.c @@ -30,13 +30,16 @@ #include <malloc.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> #include <i18n.h> -#include "dt.h" +#include "manager.h" +#include "native.h" #include "plugin-int.h" +#include "../common/cpp.h" #include "../common/extstr.h" #include "../core/logs.h" #include "../core/nox.h" @@ -44,6 +47,12 @@ +/** + * Prototype de la fonction de création, à garder synchronisé avec + * NATIVE_PLUGIN_ENTRYPOINT() (cf. native-int.h). + */ +typedef GPluginModule * (* get_plugin_instance_cb) (GModule *); + /* Liste de l'ensemble des greffons */ static GPluginModule **_pg_list = NULL; static size_t _pg_count = 0; @@ -61,6 +70,9 @@ static void browse_directory_for_plugins(const char *); /* Suit les variations du compteur de références d'un greffon. */ static void on_plugin_ref_toggle(gpointer, GPluginModule *, gboolean); +/* Fournit le greffon répondant à un nom donné. */ +static GPluginModule *_find_plugin_by_name(const char *, size_t *); + /****************************************************************************** @@ -83,8 +95,7 @@ bool init_all_plugins(bool load) char *saveptr; /* Sauvegarde pour parcours */ char *udir; /* Répertoire supplémentaire ? */ - result = init_chrysalide_dynamic_types(); - if (!result) goto exit; + result = true; g_rw_lock_init(&_pg_lock); @@ -108,8 +119,6 @@ bool init_all_plugins(bool load) if (load) load_remaning_plugins(); - exit: - return result; } @@ -130,64 +139,22 @@ bool init_all_plugins(bool load) void exit_all_plugins(void) { size_t i; /* Boucle de parcours */ - const plugin_interface *pg_iface; /* Définition du greffon */ lock_plugin_list_for_reading(); - if (_pg_list != NULL) + for (i = 0; i < _pg_count; i++) { - for (i = 0; i < _pg_count; i++) - { - assert(_pg_list[i] != NULL); - - /** - * Si le greffon a conduit à la mise en place d'autres greffons, le - * système de dépendances ne suffit pas pour le décompte des références : - * le greffon voit à un instant T son compteur décroître ici ; à un - * instant T+1, un greffon fils décrémente à son tour le compteur vers - * le greffon principal. - * - * Le compteur du conteneur tombe alors à 0, et le code correspondant - * est retiré. Lorsque que le flot d'exécution revient à la procédure - * de sortie du second greffon, son code n'est plus en mémoire. - * - * On s'assure donc que les greffons qui génèrent d'autres greffons - * sont bien traités en dernier. - */ - - pg_iface = g_plugin_module_get_interface(_pg_list[i]); - - if (pg_iface != NULL && pg_iface->container) - g_object_ref(_pg_list[i]); - - g_object_unref(_pg_list[i]); - - } - - for (i = 0; i < _pg_count; i++) - { - if (_pg_list[i] == NULL) - continue; - - pg_iface = g_plugin_module_get_interface(_pg_list[i]); - - if (pg_iface == NULL || !pg_iface->container) - continue; - - g_object_unref(_pg_list[i]); - - } + assert(_pg_list[i] != NULL); + unref_object(_pg_list[i]); + } + if (_pg_list != NULL) free(_pg_list); - } - unlock_plugin_list_for_reading(); g_rw_lock_clear(&_pg_lock); - exit_chrysalide_dynamic_types(); - } @@ -244,83 +211,95 @@ static int filter_dirs_or_mods(const struct dirent *entry) * * * Paramètres : dir = répertoire à parcourir en quête de greffons (sans /). * * * -* Description : Part à la recherche de greffons sous forme de modules. * +* Description : Indique la version (NOX/UI) associée à un nom de fichier. * * * -* Retour : - * +* Retour : true si la version complémentaire existe ou false. * * * * Remarques : - * * * ******************************************************************************/ -static void browse_directory_for_plugins(const char *dir) +static bool check_for_plugin_versions(const char *dir, const char *filename, bool *is_nox, bool *is_ui) { - struct dirent **namelist; /* Eléments trouvés */ - int ret; /* Bilan du parcours */ - bool nox; /* Absence de support graphique*/ - char *filename; /* Elément à ausculter */ - GPluginModule *plugin; /* Greffon à intégrer ou pas */ - - ret = scandir(dir, &namelist, filter_dirs_or_mods, alphasort); - if (ret < 0) - { - LOG_ERROR_N("scandir"); - return; - } - - nox = run_in_nox_mode(); - - while (ret--) - { + bool result; /* Bilan à renvoyer */ + size_t length; /* Taille du nom de fichier */ + char *alt_path; /* Autre chemin complet testé */ + int ret; /* Bilan d'une impression */ - if (nox) - { #ifdef _WIN32 -# define UI_SHARED_SUFFIX "-ui.dll" +# define SHARED_SUFFIX ".dll" #else -# define UI_SHARED_SUFFIX "-ui.so" +# define SHARED_SUFFIX ".so" #endif +#define UI_SHARED_SUFFIX "ui" SHARED_SUFFIX - if (strstr(namelist[ret]->d_name, UI_SHARED_SUFFIX) != NULL) - { - log_variadic_message(LMT_ERROR, _("Skipping unsuitable file: %s"), namelist[ret]->d_name); - continue; - } + result = false; - } + /* Propriétés du fichier courant */ - filename = (char *)calloc(strlen(dir) + 1 + strlen(namelist[ret]->d_name) + 1, sizeof(char)); + length = strlen(filename); - strcpy(filename, dir); - strcat(filename, G_DIR_SEPARATOR_S); - strcat(filename, namelist[ret]->d_name); + if (length < STATIC_STR_SIZE(UI_SHARED_SUFFIX)) + *is_ui = false; - if (namelist[ret]->d_type == DT_DIR) - browse_directory_for_plugins(filename); + else + *is_ui = (strcmp(filename + length - STATIC_STR_SIZE(UI_SHARED_SUFFIX), UI_SHARED_SUFFIX) == 0); + + if (*is_ui) + *is_nox = false; + + else + { + if (length < STATIC_STR_SIZE(SHARED_SUFFIX)) + *is_nox = false; else - { - plugin = g_plugin_module_new(filename); + *is_nox = (strcmp(filename + length - STATIC_STR_SIZE(SHARED_SUFFIX), SHARED_SUFFIX) == 0); + + } + + /* Recherche d'une version alternative */ - if (plugin != NULL) - register_plugin(plugin); + if (*is_nox || *is_ui) + { + + if (*is_nox) + ret = asprintf(&alt_path, "%s%s%.*s%s", + dir, G_DIR_SEPARATOR_S, + (int)(length - STATIC_STR_SIZE(SHARED_SUFFIX)), filename, + UI_SHARED_SUFFIX); + else + ret = asprintf(&alt_path, "%s%s%.*s%s", + dir, G_DIR_SEPARATOR_S, + (int)(length - STATIC_STR_SIZE(SHARED_SUFFIX)), filename, + SHARED_SUFFIX); + if (ret <= 0) + { + LOG_ERROR_N("asprintf"); + goto exit; } - free(filename); - free(namelist[ret]); + ret = access(alt_path, R_OK | X_OK); + + result = (ret == 0); + + free(alt_path); } - free(namelist); + exit: + + return result; } /****************************************************************************** * * -* Paramètres : plugin = greffon à ajouter aux autres disponibles. * +* Paramètres : dir = répertoire à parcourir en quête de greffons (sans /). * * * -* Description : Ajoute un greffon à la liste principale de greffons. * +* Description : Part à la recherche de greffons sous forme de modules. * * * * Retour : - * * * @@ -328,52 +307,91 @@ static void browse_directory_for_plugins(const char *dir) * * ******************************************************************************/ -void _register_plugin(GPluginModule *plugin) +static void browse_directory_for_plugins(const char *dir) { - size_t i; /* Boucle de parcours */ - const plugin_interface *pg_iface; /* Informations à consulter */ - const char *name; /* Désignation du greffon */ - - /** - * L'appel sans verrou n'est fourni que pour les greffons - * mettant en place des greffons en interne ! - */ - - /* Recherche d'un éventuel doublon */ + struct dirent **namelist; /* Eléments trouvés */ + int ret; /* Bilan d'un appel */ + int k; /* Boucle de parcours */ + bool nox_mode; /* Absence de support graphique*/ + char *filename; /* Elément à ausculter */ + bool is_nox; /* Chemin de version basique ? */ + bool is_ui; /* Chemin de version graphique */ + bool has_alt; /* Existence d'une alternative */ + GModule *module; /* Abstration de manipulation */ + get_plugin_instance_cb get_instance; /* Point d'entrée exporté */ + GPluginModule *plugin; /* Greffon à intégrer ou pas */ - pg_iface = g_plugin_module_get_interface(plugin); + ret = scandir(dir, &namelist, filter_dirs_or_mods, alphasort); + if (ret < 0) + { + LOG_ERROR_N("scandir"); + return; + } - name = pg_iface->name; + nox_mode = run_in_nox_mode(); - for (i = 0; i < _pg_count; i++) + for (k = ret; k--; ) { - pg_iface = g_plugin_module_get_interface(_pg_list[i]); - - if (strcmp(name, pg_iface->name) == 0) + ret = asprintf(&filename, "%s%s%s", dir, G_DIR_SEPARATOR_S, namelist[k]->d_name); + if (ret <= 0) { - log_variadic_message(LMT_ERROR, - _("Plugin '%s' already registered!"), name); + LOG_ERROR_N("asprintf"); + continue; + } - break; + if (namelist[k]->d_type == DT_DIR) + browse_directory_for_plugins(filename); - } + else + { + has_alt = check_for_plugin_versions(dir, namelist[k]->d_name, &is_nox, &is_ui); - } + if ((nox_mode && is_nox) || (!nox_mode && ((is_nox && !has_alt) || is_ui))) + { + module = g_module_open(filename, G_MODULE_BIND_LAZY); + if (module == NULL) + { + log_variadic_message(LMT_ERROR, + _("Error while loading the plugin candidate '%s' : %s"), + filename, g_module_error()); + goto next_file; + } + + get_instance = NULL; + + if (!g_module_symbol(module, "get_chrysalide_plugin_instance", (gpointer *)&get_instance)) + log_variadic_message(LMT_ERROR, + _("No '%s' entry in plugin candidate '%s'"), + "<sym>", filename); + + if (get_instance == NULL) + plugin = NULL; + else + plugin = get_instance(module); + + if (plugin != NULL) + { + register_plugin(plugin); + unref_object(plugin); + } + + else + g_module_close(module); - /* Ajout du greffon à la liste */ + } + else + log_variadic_message(LMT_INFO, _("Skipping unsuitable file for plugin: %s"), filename); - if (i == _pg_count) - { - _pg_list = (GPluginModule **)realloc(_pg_list, ++_pg_count * sizeof(GPluginModule)); + } - _pg_list[_pg_count - 1] = plugin; + next_file: - g_object_add_toggle_ref(G_OBJECT(plugin), (GToggleNotify)on_plugin_ref_toggle, NULL); + free(filename); + free(namelist[k]); } - else - /* FIXME : leak(plugin); */; + free(namelist); } @@ -394,24 +412,55 @@ void _register_plugin(GPluginModule *plugin) static void on_plugin_ref_toggle(gpointer unused, GPluginModule *plugin, gboolean last) { - const plugin_interface *pg_iface; /* Vitrine d'un greffon */ + const char *name; /* Désignation du greffon */ size_t index; /* Indice du greffon */ GPluginModule *same; /* Juste pour la récupération */ + GModule *module; /* Structure de chargement GLib*/ if (last) { assert(g_rw_lock_writer_trylock(&_pg_lock) == FALSE); - pg_iface = g_plugin_module_get_interface(plugin); + name = g_plugin_module_get_name(plugin); + + /** + * Les mécanismes de g_object_unref() prennent en compte la bascule d'un + * compteur de références initialement à 2 avant appel pour déclencher + * cet appel à on_plugin_ref_toggle() mis en place par g_object_add_toggle_ref(). + * + * Incrémenter ce compteur à nouveau, via get_plugin_by_name(), puis le + * décrémenter ensuite via unref_object() va conduire à une nouvelle + * bascule des statuts de suivi dans g_object_unref(). + * + * Il est ainsi impératif de rechercher une instance du greffon dans + * la liste des extensions sans toucher au compteur de références. + */ + + same = _find_plugin_by_name(name, &index); - same = get_plugin_by_name(pg_iface->name, &index); assert(same != NULL); + assert(same == plugin); _pg_list[index] = NULL; - g_object_remove_toggle_ref(G_OBJECT(plugin), (GToggleNotify)on_plugin_ref_toggle, NULL); + /** + * Suppression de la dernière référence. + */ + + if (G_IS_NATIVE_PLUGIN(plugin)) + module = g_native_plugin_get_module(G_NATIVE_PLUGIN(plugin)); + else + module = NULL; + + g_object_remove_toggle_ref(G_OBJECT(same), (GToggleNotify)on_plugin_ref_toggle, NULL); + + /** + * Plus aucun code issu du greffon n'est désormais utile. Le module associé peut + * être libéré de la mémoire. + */ - g_object_unref(G_OBJECT(same)); + if (module != NULL) + g_module_close(module); } @@ -432,9 +481,40 @@ static void on_plugin_ref_toggle(gpointer unused, GPluginModule *plugin, gboolea void register_plugin(GPluginModule *plugin) { + size_t i; /* Boucle de parcours */ + const char *name; /* Désignation du greffon */ + const char *existing; /* Nom d'un greffon en place */ + g_rw_lock_writer_lock(&_pg_lock); - _register_plugin(plugin); + /* Recherche d'un éventuel doublon */ + + name = g_plugin_module_get_name(plugin); + + for (i = 0; i < _pg_count; i++) + { + existing = g_plugin_module_get_name(_pg_list[i]); + + if (strcmp(name, existing) == 0) + { + log_variadic_message(LMT_ERROR, _("Plugin '%s' already registered!"), name); + break; + } + + } + + /* Ajout du greffon à la liste */ + + if (i == _pg_count) + { + _pg_list = realloc(_pg_list, ++_pg_count * sizeof(GPluginModule)); + + _pg_list[_pg_count - 1] = plugin; + ref_object(plugin); + + g_object_add_toggle_ref(G_OBJECT(plugin), (GToggleNotify)on_plugin_ref_toggle, NULL); + + } g_rw_lock_writer_unlock(&_pg_lock); @@ -502,13 +582,18 @@ void load_remaning_plugins(void) /* Supprime les greffons non chargés */ - for (i = 0; i < _pg_count; i++) + for (i = 0; i < _pg_count;) { flags = g_plugin_module_get_flags(_pg_list[i]); - if ((flags & PSF_LOADED) == 0) + if (flags & PSF_LOADED) + i++; + + else { - g_object_unref(G_OBJECT(_pg_list[i])); + unref_object(_pg_list[i]); + + assert(_pg_list[i] == NULL); memmove(&_pg_list[i], &_pg_list[i + 1], (_pg_count - i - 1) * sizeof(GPluginModule *)); _pg_count--; @@ -535,15 +620,16 @@ void load_remaning_plugins(void) * * * Retour : Instance du greffon trouvé ou NULL si aucun. * * * -* Remarques : - * +* Remarques : Le compteur de référence d'un greffon trouvé n'est pas * +* modifié. * * * ******************************************************************************/ -GPluginModule *get_plugin_by_name(const char *name, size_t *index) +static GPluginModule *_find_plugin_by_name(const char *name, size_t *index) { GPluginModule *result; /* Greffon trouvé à renvoyer */ size_t i; /* Boucle de parcours */ - const plugin_interface *pg_iface; /* Vitrine d'un greffon */ + const char *current; /* Nom du greffon courant */ result = NULL; @@ -557,9 +643,9 @@ GPluginModule *get_plugin_by_name(const char *name, size_t *index) /* Si on est en train de procéder à un nettoyage... */ if (_pg_list[i] == NULL) continue; - pg_iface = g_plugin_module_get_interface(_pg_list[i]); + current = g_plugin_module_get_name(_pg_list[i]); - if (strcmp(pg_iface->name, name) == 0) + if (strcmp(current, name) == 0) { result = _pg_list[i]; @@ -570,9 +656,6 @@ GPluginModule *get_plugin_by_name(const char *name, size_t *index) } - if (result != NULL) - g_object_ref(G_OBJECT(result)); - return result; } @@ -580,33 +663,25 @@ GPluginModule *get_plugin_by_name(const char *name, size_t *index) /****************************************************************************** * * -* Paramètres : count = nombre de greffons trouvés. [OUT] * +* Paramètres : name = désignation du greffon recherché. * +* index = indice du greffon trouvé. [OUT] * * * -* Description : Fournit la liste de l'ensemble des greffons. * +* Description : Fournit le greffon répondant à un nom donné. * * * -* Retour : Liste de tous les greffons chargés. * +* Retour : Instance du greffon trouvé ou NULL si aucun. * * * * Remarques : - * * * ******************************************************************************/ -GPluginModule **get_all_plugins(size_t *count) +GPluginModule *get_plugin_by_name(const char *name, size_t *index) { - GPluginModule **result; /* Liste à retourner */ - size_t i; /* Boucle de parcours */ - - g_rw_lock_reader_lock(&_pg_lock); - - result = malloc(_pg_count * sizeof(GPluginModule *)); - *count = _pg_count; + GPluginModule *result; /* Greffon trouvé à renvoyer */ - for (i = 0; i < _pg_count; i++) - { - result[i] = _pg_list[i]; - g_object_ref(G_OBJECT(_pg_list[i])); - } + result = _find_plugin_by_name(name, index); - g_rw_lock_reader_unlock(&_pg_lock); + if (result != NULL) + ref_object(result); return result; @@ -615,48 +690,30 @@ GPluginModule **get_all_plugins(size_t *count) /****************************************************************************** * * -* Paramètres : action = fonctionnalité recherchée. * -* count = nombre de greffons trouvés. [OUT] * +* Paramètres : count = nombre de greffons trouvés. [OUT] * * * -* Description : Fournit les greffons offrant le service demandé. * +* Description : Fournit la liste de l'ensemble des greffons. * * * -* Retour : Liste de greffons correspondants issue d'un tri interne. * +* Retour : Liste de tous les greffons chargés. * * * * Remarques : - * * * ******************************************************************************/ -GPluginModule **get_all_plugins_for_action(PluginAction action, size_t *count) +GPluginModule **get_all_plugins(size_t *count) { GPluginModule **result; /* Liste à retourner */ - size_t i; /* Boucle de parcours #1 */ - const plugin_interface *pg_iface; /* Informations à consulter */ - size_t j; /* Boucle de parcours #2 */ - - result = NULL; - *count = 0; + size_t i; /* Boucle de parcours */ g_rw_lock_reader_lock(&_pg_lock); + result = malloc(_pg_count * sizeof(GPluginModule *)); + *count = _pg_count; + for (i = 0; i < _pg_count; i++) { - pg_iface = g_plugin_module_get_interface(_pg_list[i]); - - for (j = 0; j < pg_iface->actions_count; j++) - { - if (pg_iface->actions[j] == action) - { - result = realloc(result, ++(*count) * sizeof(GPluginModule *)); - - result[*count - 1] = _pg_list[i]; - g_object_ref(G_OBJECT(_pg_list[i])); - - break; - - } - - } - + result[i] = _pg_list[i]; + ref_object(result[i]); } g_rw_lock_reader_unlock(&_pg_lock); diff --git a/src/plugins/pglist.h b/src/plugins/pglist.h index 83e9091..777b19c 100644 --- a/src/plugins/pglist.h +++ b/src/plugins/pglist.h @@ -52,9 +52,6 @@ void _lock_unlock_plugin_list_for_reading(bool lock); #define unlock_plugin_list_for_reading() _lock_unlock_plugin_list_for_reading(false) /* Ajoute un greffon à la liste principale de greffons. */ -void _register_plugin(GPluginModule *); - -/* Ajoute un greffon à la liste principale de greffons. */ void register_plugin(GPluginModule *); /* Charge tous les greffons restant à charger. */ @@ -66,16 +63,74 @@ GPluginModule *get_plugin_by_name(const char *, size_t *); /* Fournit la liste de l'ensemble des greffons. */ GPluginModule **get_all_plugins(size_t *); -/* Fournit les greffons offrant le service demandé. */ -GPluginModule **get_all_plugins_for_action(PluginAction, size_t *); - /** * Définitions des opérations appliquables à une catégories de greffons. */ -#define process_all_plugins_for(a, f, ...) \ +#define process_all_plugins_for(tp, cst, fc) \ + do \ + { \ + size_t __count; \ + GPluginModule **__list; \ + size_t __i; \ + GPluginModule *__pg; \ + __list = get_all_plugins(&__count); \ + for (__i = 0; __i < __count; __i++) \ + { \ + __pg = __list[__i]; \ + if (G_TYPE_CHECK_INSTANCE_TYPE(__pg, tp)) \ + fc(cst(__pg)); \ + unref_object(__pg); \ + } \ + if (__list != NULL) \ + free(__list); \ + } \ + while (0) + +#define accumulate_from_all_plugins(tp, cst, fc, atp, cnt) \ + ({ \ + atp *__acc_list; \ + size_t __count; \ + GPluginModule **__list; \ + size_t __i; \ + GPluginModule *__pg; \ + size_t __tmp_count; \ + atp *__tmp_list; \ + *cnt = 0; \ + __acc_list = NULL; \ + __list = get_all_plugins(&__count); \ + for (__i = 0; __i < __count; __i++) \ + { \ + __pg = __list[__i]; \ + if (G_TYPE_CHECK_INSTANCE_TYPE(__pg, tp)) \ + { \ + __tmp_list = fc(cst(__pg), &__tmp_count); \ + if (__tmp_list != NULL) \ + { \ + __acc_list = realloc(__acc_list, \ + (*cnt + __tmp_count) * sizeof(atp)); \ + memcpy(&__acc_list[*cnt], __tmp_list, \ + __tmp_count * sizeof(atp)); \ + *cnt += __tmp_count; \ + free(__tmp_list); \ + } \ + } \ + unref_object(__pg); \ + } \ + if (__list != NULL) \ + free(__list); \ + __acc_list; \ + }) + + + +#if 0 + +// TODO : REMME + +#define process_all_plugins_for_old__(a, f, ...) \ do \ { \ size_t __count; \ @@ -114,11 +169,13 @@ GPluginModule **get_all_plugins_for_action(PluginAction, size_t *); /* DPS_PG_MANAGEMENT */ +/* #define notify_native_plugins_loaded() \ - process_all_plugins_for(PGA_NATIVE_PLUGINS_LOADED, g_plugin_module_notify_plugins_loaded, NULL) + process_all_plugins_for_old__(PGA_NATIVE_PLUGINS_LOADED, g_plugin_module_notify_plugins_loaded, NULL) #define notify_all_plugins_loaded() \ - process_all_plugins_for(PGA_ALL_PLUGINS_LOADED, g_plugin_module_notify_plugins_loaded, NULL) + process_all_plugins_for_old__(PGA_ALL_PLUGINS_LOADED, g_plugin_module_notify_plugins_loaded, NULL) +*/ #define build_type_instance(t) \ process_plugins_while_null(PGA_TYPE_BUILDING, g_plugin_module_build_type_instance, t) @@ -126,44 +183,47 @@ GPluginModule **get_all_plugins_for_action(PluginAction, size_t *); /* DPS_SETUP */ #define include_plugin_theme(d, r, c) \ - process_all_plugins_for(PGA_GUI_THEME, g_plugin_module_include_theme, d, r, c) + process_all_plugins_for_old__(PGA_GUI_THEME, g_plugin_module_include_theme, d, r, c) /* DPS_RUNNING */ #define notify_panel_creation(i) \ - process_all_plugins_for(PGA_PANEL_CREATION, g_plugin_module_notify_panel_creation, i) + process_all_plugins_for_old__(PGA_PANEL_CREATION, g_plugin_module_notify_panel_creation, i) #define notify_panel_docking(i, d) \ - process_all_plugins_for(PGA_PANEL_DOCKING, g_plugin_module_notify_panel_docking, i, d) + process_all_plugins_for_old__(PGA_PANEL_DOCKING, g_plugin_module_notify_panel_docking, i, d) /* DPS_CONTENT */ #define handle_binary_content(a, c, i, s) \ - process_all_plugins_for(a, g_plugin_module_handle_binary_content, c, i, s) + process_all_plugins_for_old__(a, g_plugin_module_handle_binary_content, c, i, s) #define handle_loaded_content(a, c, i, s) \ - process_all_plugins_for(a, g_plugin_module_handle_loaded_content, c, i, s) + process_all_plugins_for_old__(a, g_plugin_module_handle_loaded_content, c, i, s) /* DPS_FORMAT */ #define handle_known_format_analysis(a, f, g, s) \ - process_all_plugins_for(a, g_plugin_module_handle_known_format_analysis, f, g, s) + process_all_plugins_for_old__(a, g_plugin_module_handle_known_format_analysis, f, g, s) #define preload_binary_format(a, f, i, s) \ - process_all_plugins_for(a, g_plugin_module_preload_binary_format, f, i, s) + process_all_plugins_for_old__(a, g_plugin_module_preload_binary_format, f, i, s) #define attach_debug_format(f) \ - process_all_plugins_for(PGA_FORMAT_ATTACH_DEBUG, g_plugin_module_attach_debug_format, f) + process_all_plugins_for_old__(PGA_FORMAT_ATTACH_DEBUG, g_plugin_module_attach_debug_format, f) /* DPS_DISASSEMBLY */ #define process_disassembly_event(a, b, s, c) \ - process_all_plugins_for(a, g_plugin_module_process_disassembly_event, b, s, c) + process_all_plugins_for_old__(a, g_plugin_module_process_disassembly_event, b, s, c) /* DPS_DETECTION */ #define detect_external_tools(a, cnt, v, n, c) \ - process_all_plugins_for(a, g_plugin_module_detect_external_tools, cnt, v, n, c) + process_all_plugins_for_old__(a, g_plugin_module_detect_external_tools, cnt, v, n, c) + + +#endif diff --git a/src/plugins/plugin-def.h b/src/plugins/plugin-def.h index 1118140..b5e0b51 100644 --- a/src/plugins/plugin-def.h +++ b/src/plugins/plugin-def.h @@ -35,18 +35,6 @@ /* ------------------------ IDENTIFICATION DE COMPATIBILITES ------------------------ */ -/* Version identifiant les définitions courantes */ -typedef uint32_t plugin_abi_version_t; - -#define DEFINE_PLUGIN_ABI_VERSION(maj, min, rev) \ - (((maj & 0xff) << 24) | ((min & 0xff) << 16) | (rev & 0xffff)) - -#define GET_ABI_MAJ_VERSION(vs) ((vs >> 24) & 0xff) -#define GET_ABI_MIN_VERSION(vs) ((vs >> 16) & 0xff) -#define GET_ABI_REV_VERSION(vs) (vs & 0xffff) - -#define CURRENT_ABI_VERSION DEFINE_PLUGIN_ABI_VERSION(0, 3, 0) - /* ------------------------- DEFINITION D'UN PROJET INTERNE ------------------------- */ @@ -232,14 +220,14 @@ typedef enum _PluginAction /* ------------------------ PREMIER INTERFACAGE PROTOCOLAIRE ------------------------ */ -#define CHRYSALIDE_PLUGIN_MAGIC 0xdeadc0de1234abcdull +//#define CHRYSALIDE_PLUGIN_MAGIC 0xdeadc0de1234abcdull /* Définition d'un greffon */ typedef struct _plugin_interface { uint64_t magic; /* Vérification a minima */ - plugin_abi_version_t abi_version; /* Version du protocole utilisé*/ + uint32_t /*plugin_abi_version_t*/ abi_version; /* Version du protocole utilisé*/ /** * Les champs suivants ne sont généralement pas alloués dynamiquement, diff --git a/src/plugins/plugin-int.h b/src/plugins/plugin-int.h index 3ba19dc..07b455a 100644 --- a/src/plugins/plugin-int.h +++ b/src/plugins/plugin-int.h @@ -37,21 +37,28 @@ #include "../common/bits.h" -/* Transfert de la conscience de soi. */ -typedef void (* pg_set_self_fc) (GPluginModule *); -/* Prend acte du [dé]chargement du greffon. */ +/* Pointe le fichier contenant le greffon manipulé. */ +typedef char * (* get_plugin_filename_fc) (const GPluginModule *); + +/* Fournit le nom brut associé au greffon. */ +typedef char * (* get_plugin_modname_fc) (const GPluginModule *); + +/* Prend acte de (l'|la dés)activation du greffon. */ typedef bool (* pg_management_fc) (GPluginModule *); + + + +/* Transfert de la conscience de soi. */ +typedef void (* pg_set_self_fc) (GPluginModule *); + /* Accompagne la fin du chargement des modules natifs. */ typedef void (* pg_plugins_loaded_fc) (GPluginModule *, PluginAction); /* Crée une instance à partir d'un type dynamique externe. */ typedef gpointer (* pg_build_instance_fc) (GPluginModule *, PluginAction, GType); -/* Fournit le nom brut associé au greffon. */ -typedef char * (* pg_get_modname_fc) (const GPluginModule *); - #if 0 /* Procède à une opération liée à un contenu binaire. */ @@ -90,22 +97,47 @@ typedef void (* pg_detect_tools_fc) (const GPluginModule *, PluginAction, const #endif +/* Marqueur identifiable */ +#define CHRYSALIDE_PLUGIN_MAGIC 0xdeadc0de + + +/* Version identifiant les définitions courantes */ +typedef uint32_t plugin_abi_version_t; + +#define DEFINE_PLUGIN_ABI_VERSION(maj, min, rev) \ + (((maj & 0xff) << 24) | ((min & 0xff) << 16) | (rev & 0xffff)) + +#define GET_ABI_MAJ_VERSION(vs) ((vs >> 24) & 0xff) +#define GET_ABI_MIN_VERSION(vs) ((vs >> 16) & 0xff) +#define GET_ABI_REV_VERSION(vs) (vs & 0xffff) + +/** + * 0.3.0 : dernière version avec actions et fonctions associées + * 1.0.0 (04/01/25) : bascule en chargement d'objet et interfaces + */ +#define CURRENT_ABI_VERSION DEFINE_PLUGIN_ABI_VERSION(1, 0, 0) + + /* Greffon pour Chrysalide (instance) */ struct _GPluginModule { GObject parent; /* A laisser en premier */ - char *filename; /* Fichier associé au greffon */ - GModule *module; /* Abstration de manipulation */ + uint32_t magic; /* Vérification a minima */ + plugin_abi_version_t abi_version; /* Version du protocole utilisé*/ + + char *name; /* Désignation humaine courte */ + char *desc; /* Description plus loquace */ + char *version; /* Version du greffon */ + char *url; /* Site Web associé */ - const plugin_interface *interface; /* Déclaration d'interfaçage */ + char **required; /* Pré-chargements requis */ + size_t required_count; /* Quantité de ces dépendances */ PluginStatusFlags flags; /* Fanion pour l'état courant */ bitfield_t *dependencies; /* Cartographie des dépendances*/ - //GGenConfig *config; /* Configuration dédiée */ - }; @@ -114,6 +146,18 @@ struct _GPluginModuleClass { GObjectClass parent; /* A laisser en premier */ + get_plugin_filename_fc get_filename; /* Obtention du chemin */ + get_plugin_modname_fc get_modname; /* Fourniture du nom brut */ + + pg_management_fc enable; /* Procédure d'activation */ + pg_management_fc disable; /* Procédure d'extinction */ + + + + ///////////////////////////////////////////// + +#if 0 + pg_management_fc init; /* Procédure d'initialisation */ pg_management_fc manage; /* Etape dans la vie du greffon*/ pg_management_fc exit; /* Procédure d'extinction */ @@ -121,8 +165,6 @@ struct _GPluginModuleClass pg_plugins_loaded_fc plugins_loaded; /* Fin des chargements */ pg_build_instance_fc build_instance; /* Création d'objets */ - pg_get_modname_fc get_modname; /* Fourniture du nom brut */ - #if 0 #ifdef INCLUDE_GTK_SUPPORT pg_include_theme_fc include_theme; /* Extension d'un thème */ @@ -141,12 +183,28 @@ struct _GPluginModuleClass pg_detect_tools_fc detect; /* Lancement de détections */ #endif +#endif + + ///////////////////////////////////////////// + }; -/* Met en place la configuration dédiée au greffon. */ -void g_plugin_module_create_config(GPluginModule *); + +#define STORE_PLUGIN_ABI(p) \ + do \ + { \ + GPluginModule *_p; \ + _p = G_PLUGIN_MODULE(p); \ + _p->magic = CHRYSALIDE_PLUGIN_MAGIC; \ + _p->abi_version = CURRENT_ABI_VERSION; \ + } \ + while (0); + + +/* Met en place un greffon. */ +bool g_plugin_module_create(GPluginModule *, const char *, const char *, const char *, const char *, const char * const *, size_t); diff --git a/src/plugins/plugin.c b/src/plugins/plugin.c index 75cf4e0..dfdf3ed 100644 --- a/src/plugins/plugin.c +++ b/src/plugins/plugin.c @@ -30,14 +30,13 @@ #include <libgen.h> #include <malloc.h> #include <stdarg.h> -#include <stdbool.h> #include <stdio.h> #include <string.h> -#include "dt.h" #include "pglist.h" #include "plugin-int.h" +#include "../common/compiler.h" #include "../common/extstr.h" #include "../common/pathname.h" #include "../common/xdg.h" @@ -56,19 +55,12 @@ static void g_plugin_module_dispose(GPluginModule *); /* Procède à la libération totale de la mémoire. */ static void g_plugin_module_finalize(GPluginModule *); -/* Initialise la classe des greffons d'extension. */ -static void g_plugin_module_init_gclass(GPluginModuleClass *, GModule *); - -/* Fournit le nom brut associé au greffon. */ -static char *_g_plugin_module_get_modname(const GPluginModule *); - /* Indique le type défini pour un greffon. */ G_DEFINE_TYPE(GPluginModule, g_plugin_module, G_TYPE_OBJECT); - /****************************************************************************** * * * Paramètres : class = classe à initialiser. * @@ -84,16 +76,17 @@ G_DEFINE_TYPE(GPluginModule, g_plugin_module, G_TYPE_OBJECT); static void g_plugin_module_class_init(GPluginModuleClass *class) { GObjectClass *object; /* Autre version de la classe */ - GPluginModuleClass *plugin; /* Version parente de la classe*/ object = G_OBJECT_CLASS(class); object->dispose = (GObjectFinalizeFunc/* ! */)g_plugin_module_dispose; object->finalize = (GObjectFinalizeFunc)g_plugin_module_finalize; - plugin = G_PLUGIN_MODULE_CLASS(class); + class->get_filename = NULL; + class->get_modname = NULL; - plugin->get_modname = (pg_get_modname_fc)_g_plugin_module_get_modname; + class->enable = NULL; + class->disable = NULL; } @@ -112,7 +105,17 @@ static void g_plugin_module_class_init(GPluginModuleClass *class) static void g_plugin_module_init(GPluginModule *plugin) { - //plugin->config = NULL; + plugin->name = NULL; + plugin->desc = NULL; + plugin->version = NULL; + plugin->url = NULL; + + plugin->required = NULL; + plugin->required_count = 0; + + plugin->flags = PSF_NONE; + + plugin->dependencies = NULL; } @@ -131,29 +134,29 @@ static void g_plugin_module_init(GPluginModule *plugin) static void g_plugin_module_dispose(GPluginModule *plugin) { - const plugin_interface *pg_iface; /* Définition du greffon */ size_t i; /* Boucle de parcours */ + size_t index; /* Indice de greffon visé */ GPluginModule *dependency; /* Module nécessaire */ GPluginModuleClass *class; /* Classe de l'instance active */ - pg_iface = g_plugin_module_get_interface(plugin); - - if (pg_iface != NULL) + if (plugin->dependencies != NULL) { lock_plugin_list_for_reading(); - for (i = 0; i < pg_iface->required_count; i++) + for (i = 0; i < plugin->required_count; i++) { - dependency = get_plugin_by_name(pg_iface->required[i], NULL); + dependency = get_plugin_by_name(plugin->required[i], &index); - /* Si le chargement a bien été complet avant la sortie... */ - if (dependency != NULL) + /* Si la dépendance a bien été pris en compte... */ + if (test_in_bit_field(plugin->dependencies, index)) { + assert(dependency != NULL); + /* Un coup pour l'appel à get_plugin_by_name(). */ - g_object_unref(G_OBJECT(dependency)); + unref_object(dependency); /* Un coup pour la dépendance */ - g_object_unref(G_OBJECT(dependency)); + unref_object(dependency); } @@ -163,25 +166,13 @@ static void g_plugin_module_dispose(GPluginModule *plugin) } - class = G_PLUGIN_MODULE_GET_CLASS(plugin); - - if (class->exit != NULL) - class->exit(plugin); - - /* - if (plugin->config != NULL) + if (plugin->flags & PSF_LOADED) { - g_generic_config_write(plugin->config); - - g_clear_object(&plugin->config); + class = G_PLUGIN_MODULE_GET_CLASS(plugin); - } - */ + if (class->disable != NULL) + class->disable(plugin); - if (plugin->module != NULL) - { - g_module_close(plugin->module); - plugin->module = NULL; } G_OBJECT_CLASS(g_plugin_module_parent_class)->dispose(G_OBJECT(plugin)); @@ -203,7 +194,25 @@ static void g_plugin_module_dispose(GPluginModule *plugin) static void g_plugin_module_finalize(GPluginModule *plugin) { - free(plugin->filename); + size_t i; /* Boucle de parcours */ + + if (plugin->name != NULL) + free(plugin->name); + + if (plugin->desc != NULL) + free(plugin->desc); + + if (plugin->version != NULL) + free(plugin->version); + + if (plugin->url != NULL) + free(plugin->url); + + for (i = 0; i < plugin->required_count; i++) + free(plugin->required[i]); + + if (plugin->required != NULL) + free(plugin->required); if (plugin->dependencies != NULL) delete_bit_field(plugin->dependencies); @@ -215,627 +224,173 @@ static void g_plugin_module_finalize(GPluginModule *plugin) /****************************************************************************** * * -* Paramètres : filename = nom du fichier à charger. * +* Paramètres : plugin = instance à initialiser pleinement. * +* name = nom du greffon pour référence, principalement. * +* desc = présentation éventuelle à destination humaine. * +* version = indication de version éventuelle. * +* url = référence vers une ressource en ligne. * +* required = liste de dépendances éventuelles ou NULL. * +* count = taille de cette liste. * * * -* Description : Crée un module pour un greffon donné. * +* Description : Met en place un greffon. * * * -* Retour : Adresse de la structure mise en place. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -GPluginModule *g_plugin_module_new(const gchar *filename) +bool g_plugin_module_create(GPluginModule *plugin, const char *name, const char *desc, const char *version, const char *url, const char * const *required, size_t count) { - GPluginModule *result; /* Structure à retourner */ - GModule *module; /* Abstration de manipulation */ - pg_set_self_fc set_self; /* Copie du greffon */ - const plugin_interface *interface; /* Déclaration d'interfaçage */ - plugin_abi_version_t current; /* Version de l'ABI actuelle */ - bool valid; /* Statut de validité */ - size_t i; /* Boucle de parcours */ - uint32_t action; /* Identifiant d'une action */ - uint32_t category; /* Catégorie principale */ - uint32_t sub; /* Sous-catégorie visée */ - GType gtype; /* Nouveau type de greffon */ - - module = g_module_open(filename, G_MODULE_BIND_LAZY); - if (module == NULL) - { - log_variadic_message(LMT_ERROR, - _("Error while loading the plugin candidate '%s' : %s"), - filename, g_module_error()); - goto bad_module; - } - - -#define load_plugin_symbol(mod, sym, dest) \ - ({ \ - bool __result; \ - if (!g_module_symbol(mod, sym, (gpointer *)dest)) \ - { \ - log_variadic_message(LMT_ERROR, \ - _("No '%s' entry in plugin candidate '%s'"), \ - sym, filename); \ - __result = false; \ - } \ - else __result = true; \ - __result; \ - }) - - - /* Récupération de la version d'ABI */ - - if (!load_plugin_symbol(module, "chrysalide_plugin_set_self", &set_self)) - goto no_self_setter; - - if (!load_plugin_symbol(module, "_chrysalide_plugin", &interface)) - goto no_interface; - - current = CURRENT_ABI_VERSION; - - if (current != interface->abi_version) - goto wrong_abi; - - /* Localisation des différents points d'entrée déclarés */ - + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours #1 */ + size_t k; /* Boucle de parcours #2 */ -#define check_plugin_symbol(mod, sym) \ - ({ \ - bool __result; \ - __result = g_module_symbol(mod, sym, (gpointer []) { NULL }); \ - if (!__result) \ - log_variadic_message(LMT_ERROR, \ - _("No '%s' entry in plugin candidate '%s'"), \ - sym, filename); \ - __result; \ - }) + /* Validations préalables */ + assert(name != NULL); - valid = true; + result = (name != NULL); - for (i = 0; i < interface->actions_count && valid; i++) + if (result && plugin->abi_version != CURRENT_ABI_VERSION) { - action = interface->actions[i]; - category = MASK_PLUGIN_CATEGORY(action); - sub = MASK_PLUGIN_SUB_CATEGORY(action); - - switch (category) - { - case DPC_BASIC: - - switch (sub) - { - case DPS_NONE: - break; - - case DPS_PG_MANAGEMENT: - - switch (action) - { - case PGA_PLUGIN_INIT: - valid = check_plugin_symbol(module, "chrysalide_plugin_init"); - break; - - case PGA_PLUGIN_LOADED: - valid = check_plugin_symbol(module, "chrysalide_plugin_manage"); - break; - - case PGA_PLUGIN_EXIT: - valid = check_plugin_symbol(module, "chrysalide_plugin_exit"); - break; - - default: - log_variadic_message(LMT_WARNING, - _("Unknown action '0x%02x' in plugin '%s'..."), - interface->actions[i], filename); - break; - - } - - break; - - case DPS_CORE_MANAGEMENT: - - switch (action) - { - case PGA_NATIVE_PLUGINS_LOADED: - case PGA_ALL_PLUGINS_LOADED: - valid = check_plugin_symbol(module, "chrysalide_plugin_on_plugins_loaded"); - break; - - case PGA_TYPE_BUILDING: - valid = check_plugin_symbol(module, "chrysalide_plugin_build_type_instance"); - break; - - default: - log_variadic_message(LMT_WARNING, - _("Unknown action '0x%02x' in plugin '%s'..."), - interface->actions[i], filename); - break; - - } - - break; - - default: - log_variadic_message(LMT_WARNING, - _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename); - break; - - } - - break; - -#if 0 - - case DPC_GUI: - - switch (sub) - { - case DPS_SETUP: - - switch (action) - { - case PGA_GUI_THEME: - valid = check_plugin_symbol(module, "chrysalide_plugin_include_theme"); - break; - - default: - log_variadic_message(LMT_WARNING, - _("Unknown action '0x%02x' in plugin '%s'..."), - interface->actions[i], filename); - break; - - } - - break; - - case DPS_RUNNING: - - switch (action) - { - case PGA_PANEL_CREATION: - valid = check_plugin_symbol(module, "chrysalide_plugin_on_panel_creation"); - break; - - case PGA_PANEL_DOCKING: - valid = check_plugin_symbol(module, "chrysalide_plugin_on_panel_docking"); - break; - - default: - log_variadic_message(LMT_WARNING, - _("Unknown action '0x%02x' in plugin '%s'..."), - interface->actions[i], filename); - break; - - } - - break; - - default: - log_variadic_message(LMT_WARNING, - _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename); - break; - - } - - break; - - case DPC_BINARY_PROCESSING: - - switch (sub) - { - case DPS_CONTENT: - - switch (action) - { - case PGA_CONTENT_EXPLORER: - case PGA_CONTENT_RESOLVER: - valid = check_plugin_symbol(module, "chrysalide_plugin_handle_binary_content"); - break; - - case PGA_CONTENT_ANALYZED: - valid = check_plugin_symbol(module, "chrysalide_plugin_handle_loaded_content"); - break; - - default: - log_variadic_message(LMT_WARNING, - _("Unknown action '0x%02x' in plugin '%s'..."), - interface->actions[i], filename); - break; - - } + result = false; - break; - - case DPS_FORMAT: + log_variadic_message(LMT_ERROR, _("ABI mismatch detected: %08x (plugin) vs %08x (core)"), + plugin->abi_version, CURRENT_ABI_VERSION); - switch (action) - { - case PGA_FORMAT_ANALYSIS_STARTED: - case PGA_FORMAT_ANALYSIS_ENDED: - case PGA_FORMAT_POST_ANALYSIS_STARTED: - case PGA_FORMAT_POST_ANALYSIS_ENDED: - valid = check_plugin_symbol(module, "chrysalide_plugin_handle_binary_format_analysis"); - break; + } - case PGA_FORMAT_PRELOAD: - valid = check_plugin_symbol(module, "chrysalide_plugin_preload_binary_format"); - break; + /* Mémorisation des informations */ - case PGA_FORMAT_ATTACH_DEBUG: - valid = check_plugin_symbol(module, "chrysalide_plugin_attach_debug"); - break; + if (result) + { + plugin->name = strdup(name); - default: - log_variadic_message(LMT_WARNING, - _("Unknown action '0x%02x' in plugin '%s'..."), - interface->actions[i], filename); - break; + if (desc != NULL) + plugin->desc = strdup(desc); - } + if (version != NULL) + plugin->version = strdup(version); - break; + if (url != NULL) + plugin->url = strdup(url); - case DPS_DISASSEMBLY: - valid = check_plugin_symbol(module, "chrysalide_plugin_process_disassembly_event"); - break; - - case DPS_DETECTION: - valid = check_plugin_symbol(module, "chrysalide_plugin_detect_external_tools"); - break; + if (count > 0) + { + plugin->required = malloc(count * sizeof(char *)); + plugin->required_count = 0; - default: - log_variadic_message(LMT_WARNING, - _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename); + for (i = 0; i < count; i++) + { + for (k = 0; k < plugin->required_count; k++) + if (strcmp(required[i], plugin->required[k]) == 0) break; - } + if (k < plugin->required_count) + continue; -#endif + plugin->required[plugin->required_count++] = strdup(required[i]); - break; + } - default: - log_variadic_message(LMT_WARNING, - _("Unknown category '0x%02x' in plugin '%s'..."), category, filename); - break; + plugin->required = realloc(plugin->required, plugin->required_count * sizeof(char *)); } } - if (!valid) - goto missing_feature; - - gtype = build_dynamic_type(G_TYPE_PLUGIN_MODULE, interface->gtp_name, - (GClassInitFunc)g_plugin_module_init_gclass, module, NULL); - - if (gtype == G_TYPE_INVALID) - goto no_instance; - - result = g_object_new(gtype, NULL); - - result->filename = strdup(filename); - result->module = module; - - result->interface = interface; - - set_self(result); - return result; - no_self_setter: - - log_variadic_message(LMT_ERROR, _("Self pointer setter is missing for plugin '%s'"), filename); - goto bad_plugin; - - no_interface: - - log_variadic_message(LMT_ERROR, _("Main interface is missing for plugin '%s'"), filename); - goto bad_plugin; - - wrong_abi: - - log_variadic_message(LMT_ERROR, _("ABI mismatch detected! Plugin '%s' rejected"), filename); - goto bad_plugin; - - missing_feature: - - log_variadic_message(LMT_ERROR, _("An expected feature is missing for plugin '%s'"), filename); - goto bad_plugin; - - no_instance: - - log_variadic_message(LMT_ERROR, _("Unabled to create an instance of plugin '%s'"), filename); - - bad_plugin: - - g_module_close(module); - - bad_module: - - return NULL; - } /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* module = module représentant le greffon chargé en mémoire. * +* Paramètres : plugin = greffon à consulter. * * * -* Description : Initialise la classe des greffons d'extension. * +* Description : Indique le nom associé à un greffon. * * * -* Retour : - * +* Retour : Désignation interne de l'extension, pour référence(s). * * * * Remarques : - * * * ******************************************************************************/ -static void g_plugin_module_init_gclass(GPluginModuleClass *class, GModule *module) +const char *g_plugin_module_get_name(const GPluginModule *plugin) { - const plugin_interface *interface; /* Déclaration d'interfaçage */ - size_t i; /* Boucle de parcours */ - uint32_t action; /* Identifiant d'une action */ - uint32_t category; /* Catégorie principale */ - uint32_t sub; /* Sous-catégorie visée */ - - -#undef load_plugin_symbol - -#define load_plugin_symbol(mod, sym, dest) \ - ({ \ - bool __result; \ - __result = g_module_symbol(mod, sym, (gpointer *)dest); \ - assert(__result); \ - __result; \ - }) - - - load_plugin_symbol(module, "_chrysalide_plugin", &interface); - - for (i = 0; i < interface->actions_count; i++) - { - action = interface->actions[i]; - category = MASK_PLUGIN_CATEGORY(action); - sub = MASK_PLUGIN_SUB_CATEGORY(action); - - switch (category) - { - case DPC_BASIC: - - switch (sub) - { - case DPS_NONE: - break; - - case DPS_PG_MANAGEMENT: - - switch (action) - { - case PGA_PLUGIN_INIT: - load_plugin_symbol(module, "chrysalide_plugin_init", &class->init); - break; - - case PGA_PLUGIN_LOADED: - load_plugin_symbol(module, "chrysalide_plugin_manage", &class->manage); - break; - - case PGA_PLUGIN_EXIT: - load_plugin_symbol(module, "chrysalide_plugin_exit", &class->exit); - break; - - default: - assert(false); - break; - - } - - break; - - case DPS_CORE_MANAGEMENT: - - switch (action) - { - case PGA_NATIVE_PLUGINS_LOADED: - case PGA_ALL_PLUGINS_LOADED: - load_plugin_symbol(module, "chrysalide_plugin_on_plugins_loaded", - &class->plugins_loaded); - break; - - case PGA_TYPE_BUILDING: - load_plugin_symbol(module, "chrysalide_plugin_build_type_instance", - &class->build_instance); - break; - - default: - assert(false); - break; - - } - - break; - - default: - assert(false); - break; - - } - - break; -#if 0 - case DPC_GUI: - - switch (sub) - { - case DPS_SETUP: - - switch (action) - { - case PGA_GUI_THEME: -#ifdef INCLUDE_GTK_SUPPORT - load_plugin_symbol(module, "chrysalide_plugin_include_theme", - &class->include_theme); -#endif - break; - - default: - assert(false); - break; - - } - - break; - - case DPS_RUNNING: - - switch (action) - { - case PGA_PANEL_CREATION: -#ifdef INCLUDE_GTK_SUPPORT - load_plugin_symbol(module, "chrysalide_plugin_on_panel_creation", - &class->notify_panel); -#endif - break; - - case PGA_PANEL_DOCKING: -#ifdef INCLUDE_GTK_SUPPORT - load_plugin_symbol(module, "chrysalide_plugin_on_panel_docking", - &class->notify_docking); -#endif - break; - - default: - assert(false); - break; - - } - - break; - - default: - assert(false); - break; + const char *result; /* Valeur finale à renvoyer */ - } + result = plugin->name; - break; - - case DPC_BINARY_PROCESSING: - - switch (sub) - { - case DPS_CONTENT: - - switch (action) - { - case PGA_CONTENT_EXPLORER: - case PGA_CONTENT_RESOLVER: - load_plugin_symbol(module, "chrysalide_plugin_handle_binary_content", - &class->handle_content); - break; - - case PGA_CONTENT_ANALYZED: - load_plugin_symbol(module, "chrysalide_plugin_handle_loaded_content", - &class->handle_loaded); - break; - - default: - assert(false); - break; - - } - - break; - - case DPS_FORMAT: - - switch (action) - { - case PGA_FORMAT_ANALYSIS_STARTED: - case PGA_FORMAT_ANALYSIS_ENDED: - case PGA_FORMAT_POST_ANALYSIS_STARTED: - case PGA_FORMAT_POST_ANALYSIS_ENDED: - load_plugin_symbol(module, "chrysalide_plugin_handle_binary_format_analysis", - &class->handle_fmt_analysis); - break; - - case PGA_FORMAT_PRELOAD: - load_plugin_symbol(module, "chrysalide_plugin_preload_binary_format", &class->preload_format); - break; - - case PGA_FORMAT_ATTACH_DEBUG: - load_plugin_symbol(module, "chrysalide_plugin_attach_debug", &class->attach_debug); - break; + return result; - default: - assert(false); - break; +} - } - break; +/****************************************************************************** +* * +* Paramètres : plugin = greffon à consulter. * +* * +* Description : Fournit une description fonctionnelle d'un greffon. * +* * +* Retour : Description textuelle associée à une extension ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ - case DPS_DISASSEMBLY: - load_plugin_symbol(module, "chrysalide_plugin_process_disassembly_event", &class->process_disass); - break; +const char *g_plugin_module_get_desc(const GPluginModule *plugin) +{ + const char *result; /* Valeur finale à renvoyer */ - case DPS_DETECTION: - load_plugin_symbol(module, "chrysalide_plugin_detect_external_tools", &class->detect); - break; + result = plugin->desc; - default: - assert(false); - break; + return result; - } +} -#endif - break; +/****************************************************************************** +* * +* Paramètres : plugin = greffon à consulter. * +* * +* Description : Fournit la version d'un greffon et de ses fonctionnalités. * +* * +* Retour : Version sous forme de chaîne de caractères ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ - default: - assert(false); - break; +const char *g_plugin_module_get_version(const GPluginModule *plugin) +{ + const char *result; /* Valeur finale à renvoyer */ - } + result = plugin->version; - } + return result; } /****************************************************************************** * * -* Paramètres : plugin = greffon à valider. * +* Paramètres : plugin = greffon à consulter. * * * -* Description : Fournit le nom brut associé au greffon. * +* Description : Fournit l'URL des ressources en ligne liées à un greffon. * * * -* Retour : Désignation brute du greffon. * +* Retour : URL de renvoi associée à une extension ou NULL. * * * * Remarques : - * * * ******************************************************************************/ -char *g_plugin_module_get_modname(const GPluginModule *plugin) +const char *g_plugin_module_get_url(const GPluginModule *plugin) { - char *result; /* Désignation brute à renvoyer*/ - GPluginModuleClass *class; /* Classe de l'instance active */ - - class = G_PLUGIN_MODULE_GET_CLASS(plugin); - - result = class->get_modname(plugin); + const char *result; /* Valeur finale à renvoyer */ - /** - * Tente une opération de la dernière chance. - * - * Dans le cas d'un module Python, la fonction de classe peut ne pas - * trouver de support si l'extension Python n'est pas au point. - */ - if (result == NULL && class->get_modname != _g_plugin_module_get_modname) - result = _g_plugin_module_get_modname(plugin); + result = plugin->url; return result; @@ -844,41 +399,24 @@ char *g_plugin_module_get_modname(const GPluginModule *plugin) /****************************************************************************** * * -* Paramètres : plugin = greffon à valider. * +* Paramètres : plugin = greffon à consulter. * +* count = taille de la liste fournie. [OUT] * * * -* Description : Fournit le nom brut associé au greffon. * +* Description : Fournit la liste des dépendances d'un greffon donné. * * * -* Retour : Désignation brute du greffon. * +* Retour : Liste des noms d'extensions requises pour une extension. * * * * Remarques : - * * * ******************************************************************************/ -static char *_g_plugin_module_get_modname(const GPluginModule *plugin) +const char * const *g_plugin_module_get_requirements(const GPluginModule *plugin, size_t *count) { - char *result; /* Désignation brute à renvoyer*/ - char *path; /* Chemin à traiter */ - char *filename; /* Nom de bibliothèque partagée*/ - size_t length; /* Taille du nom */ - - path = strdup(g_plugin_module_get_filename(G_PLUGIN_MODULE(plugin))); - - filename = basename(path); - - if (strncmp(filename, "lib", 3) == 0) - filename += 3; - - length = strlen(filename); - - if (length >= 3) - { - if (strncmp(&filename[length - 3], ".so", 3) == 0) - filename[length - 3] = '\0'; - } + const char * const *result; /* Valeur finale à renvoyer */ - result = strdup(filename); + result = CONST_ARRAY_CAST(plugin->required, char); - free(path); + *count = plugin->required_count; return result; @@ -889,7 +427,7 @@ static char *_g_plugin_module_get_modname(const GPluginModule *plugin) * * * Paramètres : plugin = greffon à consulter. * * * -* Description : Indique le fichier contenant le greffon manipulé. * +* Description : Pointe le fichier contenant le greffon manipulé. * * * * Retour : Chemin d'accès au greffon. * * * @@ -897,28 +435,42 @@ static char *_g_plugin_module_get_modname(const GPluginModule *plugin) * * ******************************************************************************/ -const char *g_plugin_module_get_filename(const GPluginModule *plugin) +char *g_plugin_module_get_filename(const GPluginModule *plugin) { - return plugin->filename; + char *result; /* Chemin d'accès à renvoyer */ + GPluginModuleClass *class; /* Classe de l'instance active */ + + class = G_PLUGIN_MODULE_GET_CLASS(plugin); + + result = class->get_filename(plugin); + + return result; } /****************************************************************************** * * -* Paramètres : plugin = greffon à consulter. * +* Paramètres : plugin = greffon à valider. * * * -* Description : Fournit la description du greffon dans son intégralité. * +* Description : Fournit le nom brut associé au greffon. * * * -* Retour : Interfaçage renseigné. * +* Retour : Désignation brute du greffon. * * * * Remarques : - * * * ******************************************************************************/ -const plugin_interface *g_plugin_module_get_interface(const GPluginModule *plugin) +char *g_plugin_module_get_modname(const GPluginModule *plugin) { - return plugin->interface; + char *result; /* Désignation brute à renvoyer*/ + GPluginModuleClass *class; /* Classe de l'instance active */ + + class = G_PLUGIN_MODULE_GET_CLASS(plugin); + + result = class->get_modname(plugin); + + return result; } @@ -979,7 +531,6 @@ void g_plugin_module_override_flags(GPluginModule *plugin, PluginStatusFlags fla bool g_plugin_module_resolve_dependencies(GPluginModule *plugin, GPluginModule **list, size_t count) { bool result; /* Bilan à faire remonter */ - const plugin_interface *pg_iface; /* Définition du greffon */ bitfield_t *new; /* Nouvelle définition */ size_t i; /* Boucle de parcours */ GPluginModule *dependency; /* Module nécessaire */ @@ -997,15 +548,13 @@ bool g_plugin_module_resolve_dependencies(GPluginModule *plugin, GPluginModule * if ((plugin->flags & (PSF_UNKNOW_DEP | PSF_DEP_LOOP)) == 0) { - pg_iface = g_plugin_module_get_interface(plugin); - /* Collecte des dépendances */ new = dup_bit_field(plugin->dependencies); - for (i = 0; i < pg_iface->required_count; i++) + for (i = 0; i < plugin->required_count; i++) { - dependency = get_plugin_by_name(pg_iface->required[i], &index); + dependency = get_plugin_by_name(plugin->required[i], &index); if (dependency == NULL) plugin->flags |= PSF_UNKNOW_DEP; @@ -1023,7 +572,7 @@ bool g_plugin_module_resolve_dependencies(GPluginModule *plugin, GPluginModule * */ if (test_in_bit_field(plugin->dependencies, index)) - g_object_unref(G_OBJECT(dependency)); + unref_object(dependency); } @@ -1041,13 +590,14 @@ bool g_plugin_module_resolve_dependencies(GPluginModule *plugin, GPluginModule * /* Vérification sanitaire */ - dependency = get_plugin_by_name(pg_iface->name, &index); + dependency = get_plugin_by_name(plugin->name, &index); assert(dependency != NULL); + assert(dependency == plugin); if (test_in_bit_field(plugin->dependencies, index)) plugin->flags |= PSF_DEP_LOOP; - g_object_unref(G_OBJECT(dependency)); + unref_object(dependency); } @@ -1075,12 +625,12 @@ bool g_plugin_module_load(GPluginModule *plugin, GPluginModule **list, size_t co { bool result; /* Bilan à retourner */ PluginStatusFlags flags; /* Fanions de greffon */ - const plugin_interface *pg_iface; /* Définition du greffon */ size_t i; /* Boucle de parcours */ GPluginModule *dependency; /* Module nécessaire */ + char *filename; /* Chemin d'accès au greffon */ GPluginModuleClass *class; /* Classe de l'instance active */ - //GGenConfig *config; /* Configuration à charger */ - char *dir; /* Répertoire modifiable */ + char *tmp; /* Chaîne modifiable */ + char *dir; /* Pointeur vers répertoire */ /* Si un essai précédent a déjà échoué ou réussi... */ @@ -1092,40 +642,41 @@ bool g_plugin_module_load(GPluginModule *plugin, GPluginModule **list, size_t co /* Chargement des dépendances */ - pg_iface = g_plugin_module_get_interface(plugin); - result = true; - for (i = 0; i < pg_iface->required_count && result; i++) + filename = g_plugin_module_get_filename(plugin); + + for (i = 0; i < plugin->required_count && result; i++) { - dependency = get_plugin_by_name(pg_iface->required[i], NULL); + dependency = get_plugin_by_name(plugin->required[i], NULL); assert(dependency != NULL); result = g_plugin_module_load(dependency, list, count); - g_object_unref(G_OBJECT(dependency)); + unref_object(dependency); } if (!result) { - log_variadic_message(LMT_ERROR, - _("Some dependencies failed to load for plugin '%s'"), plugin->filename); + log_variadic_message(LMT_ERROR, _("Some dependencies failed to load for plugin '%s'"), filename); + + plugin->flags |= PSF_FAILURE; goto failure; + } /* Chargement du greffon courant */ class = G_PLUGIN_MODULE_GET_CLASS(plugin); - if (class->init != NULL) + if (class->enable != NULL) { - result = class->init(plugin); + result = class->enable(plugin); if (!result) { - log_variadic_message(LMT_ERROR, - _("Plugin '%s' failed to load itself..."), plugin->filename); + log_variadic_message(LMT_ERROR, _("Plugin '%s' failed to load itself..."), filename); plugin->flags |= PSF_FAILURE; goto failure; @@ -1134,39 +685,23 @@ bool g_plugin_module_load(GPluginModule *plugin, GPluginModule **list, size_t co } - g_plugin_module_create_config(plugin); - - result = g_plugin_module_manage(plugin, PGA_PLUGIN_LOADED); - - if (!result) - { - log_variadic_message(LMT_ERROR, - _("Plugin '%s' failed to complete loading..."), plugin->filename); - - plugin->flags |= PSF_FAILURE; - goto failure; - - } - - /* - config = g_plugin_module_get_config(plugin); - g_generic_config_read(config); - g_object_unref(G_OBJECT(config)); - */ + /* Message de bilan */ - dir = strdup(plugin->filename); - dir = dirname(dir); + tmp = strdup(filename); + dir = dirname(tmp); log_variadic_message(LMT_PROCESS, _("Loaded the '<b>%s</b>' file as plugin from the '<b>%s</b>' directory"), - strrchr(plugin->filename, G_DIR_SEPARATOR) + 1, dir); + strrchr(filename, G_DIR_SEPARATOR) + 1, dir); - free(dir); + free(tmp); plugin->flags |= PSF_LOADED; failure: + free(filename); + return result; } @@ -1227,31 +762,6 @@ char *g_plugin_module_build_config_filename(const GPluginModule *plugin, const c /****************************************************************************** * * -* Paramètres : plugin = greffon à compléter. * -* * -* Description : Met en place la configuration dédiée au greffon. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_plugin_module_create_config(GPluginModule *plugin) -{ - char *filename; /* Chemin d'accès particulier */ - - filename = g_plugin_module_build_config_filename(plugin, "config.xml", false); - - //plugin->config = g_generic_config_new_from_file(filename); - - free(filename); - -} - - -/****************************************************************************** -* * * Paramètres : plugin = greffon à consulter. * * * * Description : Fournit la configuration mise en place pour le greffon. * @@ -1295,11 +805,11 @@ void g_plugin_module_log_simple_message(const GPluginModule *plugin, LogMessageT size_t len; /* Taille tampon disponible */ char *buffer; /* Tampon du msg reconstitué */ - len = 4 + strlen(plugin->interface->name) + 6 + strlen(msg) + 1; + len = 4 + strlen(plugin->name) + 6 + strlen(msg) + 1; buffer = calloc(len, sizeof(char)); strcpy(buffer, "<i>["); - strcat(buffer, plugin->interface->name); + strcat(buffer, plugin->name); strcat(buffer, "]</i> "); strcat(buffer, msg); @@ -1345,80 +855,12 @@ void g_plugin_module_log_variadic_message(const GPluginModule *plugin, LogMessag } -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * -* * -* Description : Encadre une étape de la vie d'un greffon. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_plugin_module_manage(GPluginModule *plugin, PluginAction action) -{ - bool result; /* Bilan à faire remonter */ - GPluginModuleClass *class; /* Classe de l'instance active */ - const plugin_interface *pg_iface; /* Informations à consulter */ - size_t i; /* Boucle de parcours */ - bool handle_action; /* Action supportée ? */ - - class = G_PLUGIN_MODULE_GET_CLASS(plugin); - - if (class->manage == NULL) - result = true; - - else - { - handle_action = false; - - pg_iface = g_plugin_module_get_interface(plugin); - - for (i = 0; i < pg_iface->actions_count; i++) - if (pg_iface->actions[i] == PGA_PLUGIN_LOADED) - { - handle_action = true; - break; - } - - if (handle_action) - result = class->manage(plugin/*, action*/); - else - result = true; - - } - - return result; - -} - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * -* unused = variable non utilisé pour l'usage de __VA_ARGS__. * -* * -* Description : Accompagne la fin du chargement des modules. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ -void g_plugin_module_notify_plugins_loaded(GPluginModule *plugin, PluginAction action, void *unused) -{ - GPluginModuleClass *class; /* Classe de l'instance active */ - class = G_PLUGIN_MODULE_GET_CLASS(plugin); - class->plugins_loaded(plugin, action); -} +#if 0 /****************************************************************************** @@ -1450,8 +892,6 @@ gpointer g_plugin_module_build_type_instance(GPluginModule *plugin, PluginAction -#if 0 - #ifdef INCLUDE_GTK_SUPPORT diff --git a/src/plugins/plugin.h b/src/plugins/plugin.h index 5c473b2..a4cc388 100644 --- a/src/plugins/plugin.h +++ b/src/plugins/plugin.h @@ -52,6 +52,27 @@ DECLARE_GTYPE(GPluginModule, g_plugin_module, G, PLUGIN_MODULE); +/* Indique le nom associé à un greffon. */ +const char *g_plugin_module_get_name(const GPluginModule *); + +/* Fournit une description fonctionnelle d'un greffon. */ +const char *g_plugin_module_get_desc(const GPluginModule *); + +/* Fournit la version d'un greffon et de ses fonctionnalités. */ +const char *g_plugin_module_get_version(const GPluginModule *); + +/* Fournit l'URL des ressources en ligne liées à un greffon. */ +const char *g_plugin_module_get_url(const GPluginModule *); + +/* Fournit la liste des dépendances d'un greffon donné. */ +const char * const *g_plugin_module_get_requirements(const GPluginModule *, size_t *); + +/* Pointe le fichier contenant le greffon manipulé. */ +char *g_plugin_module_get_filename(const GPluginModule *); + +/* Fournit le nom brut associé au greffon. */ +char *g_plugin_module_get_modname(const GPluginModule *); + /* Fanions indiquant le statut du greffon */ typedef enum _PluginStatusFlags { @@ -66,19 +87,6 @@ typedef enum _PluginStatusFlags #define BROKEN_PLUGIN_STATUS (PSF_UNKNOW_DEP | PSF_DEP_LOOP | PSF_FAILURE) - -/* Crée un module pour un greffon donné. */ -GPluginModule *g_plugin_module_new(const gchar *); - -/* Fournit le nom brut associé au greffon. */ -char *g_plugin_module_get_modname(const GPluginModule *); - -/* Indique le fichier contenant le greffon manipulé. */ -const char *g_plugin_module_get_filename(const GPluginModule *); - -/* Fournit la description du greffon dans son intégralité. */ -const plugin_interface *g_plugin_module_get_interface(const GPluginModule *); - /* Fournit des indications sur l'état du greffon. */ PluginStatusFlags g_plugin_module_get_flags(const GPluginModule *); @@ -103,17 +111,12 @@ void g_plugin_module_log_simple_message(const GPluginModule *, LogMessageType, c /* Présente dans le journal un message complexe. */ void g_plugin_module_log_variadic_message(const GPluginModule *, LogMessageType, const char *, ...); -/* Encadre une étape de la vie d'un greffon. */ -bool g_plugin_module_manage(GPluginModule *, PluginAction); -/* Accompagne la fin du chargement des modules natifs. */ -void g_plugin_module_notify_plugins_loaded(GPluginModule *, PluginAction, void *); +#if 0 /* Crée une instance à partir d'un type dynamique externe. */ gpointer g_plugin_module_build_type_instance(GPluginModule *, PluginAction, GType); -#if 0 - #ifdef INCLUDE_GTK_SUPPORT /* Complète une liste de resources pour thème. */ diff --git a/src/plugins/self.h b/src/plugins/self.h index 4d5ddb0..78a3fe6 100644 --- a/src/plugins/self.h +++ b/src/plugins/self.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * self.h - définitions pour inclusion dans les différents greffons + * self.h - définitions de fonctionnalités facilitant la mise en place d'extensions natives * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -26,72 +26,55 @@ #define _PLUGINS_SELF_H -#ifndef _PLUGINS_PLUGIN_H -# include "plugin.h" -#endif +#include <assert.h> +#include <malloc.h> + + +#include "plugin.h" #include "../common/compiler.h" -/* Facilitations de déclarations */ +/* Symboles principaux */ + +#define PLUGIN_CORE_SELF \ +static GPluginModule *_this_plugin = NULL; \ +static void chrysalide_plugin_set_self(GPluginModule *); \ +static void chrysalide_plugin_set_self(GPluginModule *plugin) \ +{ \ + assert(_this_plugin == NULL); \ + _this_plugin = plugin; \ +}; \ +__private GPluginModule *_chrysalide_plugin_get_self(void); \ +__private GPluginModule *_chrysalide_plugin_get_self(void) \ +{ \ + return _this_plugin; \ +}; + +#define NATIVE_PLUGIN_ENTRYPOINT(fc) \ +PLUGIN_CORE_SELF; \ +G_MODULE_EXPORT GPluginModule *get_chrysalide_plugin_instance(GModule *); \ +G_MODULE_EXPORT GPluginModule *get_chrysalide_plugin_instance(GModule *module) \ +{ \ + GPluginModule *result; /* Instance à retourner */ \ + result = fc(module); \ + chrysalide_plugin_set_self(result); \ + return result; \ +} + + +/* Spécifications */ #define CHRYSALIDE_WEBSITE(p) "https://www.chrysalide.re/" p -#define EMPTY_PG_LIST(name) \ - name = NULL, \ - name ## _count = 0 \ - -#define BUILD_PG_LIST(name, lst) \ - name = lst, \ - name ## _count = sizeof(lst) / sizeof(lst[0]) \ - -#define AL(...) BUILD_PG_LIST(.actions, ((plugin_action_t []){ __VA_ARGS__ })) - -#define RL(...) BUILD_PG_LIST(.required, ((char *[]){ __VA_ARGS__ })) - -#define NO_REQ EMPTY_PG_LIST(.required) - - -/* Composants d'interface */ - -#define PLUGIN_CORE_SELF \ -static GPluginModule *_this_plugin = NULL; \ -G_MODULE_EXPORT void chrysalide_plugin_set_self(GPluginModule *p); \ -G_MODULE_EXPORT void chrysalide_plugin_set_self(GPluginModule *p) { _this_plugin = p; }; \ -__private GPluginModule *_chrysalide_plugin_get_self(void); \ -__private GPluginModule *_chrysalide_plugin_get_self(void) { return _this_plugin; }; - -#define PLUGIN_CORE_PROPS(n, d, v, u, c) \ - \ - .magic = CHRYSALIDE_PLUGIN_MAGIC, \ - .abi_version = CURRENT_ABI_VERSION, \ - \ - .gtp_name = "G" n "Plugin", \ - .name = n, \ - .desc = d, \ - .version = v, \ - .url = u, \ - \ - .container = c - -#define DEFINE_CHRYSALIDE_PLUGIN(n, d, v, u, r, a) \ -PLUGIN_CORE_SELF \ -G_MODULE_EXPORT const plugin_interface _chrysalide_plugin = { \ - PLUGIN_CORE_PROPS(n, d, v, u, false), \ - r, \ - a, \ -} +#define NO_REQ NULL, 0 -#define DEFINE_CHRYSALIDE_CONTAINER_PLUGIN(n, d, v, u, r, a) \ -PLUGIN_CORE_SELF \ -G_MODULE_EXPORT const plugin_interface _chrysalide_plugin = { \ - PLUGIN_CORE_PROPS(n, d, v, u, true), \ - r, \ - a, \ -} +#define BUILD_PG_LIST(lst) lst, sizeof(lst) / sizeof(lst[0]) + +#define REQ_LIST(...) BUILD_PG_LIST((const char *[]){ __VA_ARGS__ }) -/* Manipulations accélérées */ +/* Journalisation */ __private GPluginModule *_chrysalide_plugin_get_self(void); diff --git a/src/plugins/tweakable-int.h b/src/plugins/tweakable-int.h new file mode 100644 index 0000000..776626f --- /dev/null +++ b/src/plugins/tweakable-int.h @@ -0,0 +1,50 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tweakable-int.h - définitions internes propres aux participations aux mécanismes de configuration + * + * Copyright (C) 2025 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 _PLUGINS_TWEAKABLE_INT_H +#define _PLUGINS_TWEAKABLE_INT_H + + +#include "tweakable.h" + + + +/* ------------------- INTEGRATION DANS L'EDITION DES PREFERENCES ------------------- */ + + +/* Fournit une liste de sections de configuration. */ +typedef tweak_info_t * (* get_tweakable_plugin_info) (const GTweakablePlugin *, size_t *); + + +/* Greffon avec des compléments pour l'interface de configuration (interface) */ +struct _GTweakablePluginInterface +{ + GTypeInterface base_iface; /* A laisser en premier */ + + get_tweakable_plugin_info get_info; /* Récupération de section(s) */ + +}; + + + +#endif /* _PLUGINS_TWEAKABLE_INT_H */ diff --git a/src/plugins/tweakable.c b/src/plugins/tweakable.c new file mode 100644 index 0000000..517c4a3 --- /dev/null +++ b/src/plugins/tweakable.c @@ -0,0 +1,98 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tweakable.c - participation aux mécanismes de configuration + * + * Copyright (C) 2025 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 Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "tweakable.h" + + +#include "tweakable-int.h" + + + +/* ------------------- INTEGRATION DANS L'EDITION DES PREFERENCES ------------------- */ + + +/* Procède à l'initialisation de l'interface d'intervention. */ +static void g_tweakable_plugin_default_init(GTweakablePluginInterface *); + + + +/* ---------------------------------------------------------------------------------- */ +/* INTEGRATION DANS L'EDITION DES PREFERENCES */ +/* ---------------------------------------------------------------------------------- */ + + +/* Détermine le type d'une interface pour l'intervention dans la gestion des greffons. */ +G_DEFINE_INTERFACE(GTweakablePlugin, g_tweakable_plugin, G_TYPE_OBJECT) + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface d'intervention. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_tweakable_plugin_default_init(GTweakablePluginInterface *iface) +{ + iface->get_info = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : plugin = interface à manipuler. * +* count = taille de la liste renvoyée. [OUT] * +* * +* Description : Fournit une liste de sections de configuration. * +* * +* Retour : Définition(s) de section de configuration ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +tweak_info_t *g_tweakable_plugin_get_tweak_info(const GTweakablePlugin *plugin, size_t *count) +{ + tweak_info_t *result; /* Liste à renvoyer */ + GTweakablePluginInterface *iface; /* Interface utilisée */ + + iface = G_TWEAKABLE_PLUGIN_GET_IFACE(plugin); + + if (iface->get_info != NULL) + result = iface->get_info(plugin, count); + + else + { + *count = 0; + result = NULL; + } + + return result; + +} diff --git a/src/plugins/tweakable.h b/src/plugins/tweakable.h new file mode 100644 index 0000000..aea70b4 --- /dev/null +++ b/src/plugins/tweakable.h @@ -0,0 +1,62 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tweakable.h - prototypes pour la participation aux mécanismes de configuration + * + * Copyright (C) 2025 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 Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _PLUGINS_TWEAKABLE_H +#define _PLUGINS_TWEAKABLE_H + + +#include "../glibext/helpers.h" +#include "../gtkext/tweak.h" + + + +/* ------------------- INTEGRATION DANS L'EDITION DES PREFERENCES ------------------- */ + + +#define G_TYPE_TWEAKABLE_PLUGIN (g_tweakable_plugin_get_type()) + +DECLARE_INTERFACE(GTweakablePlugin, g_tweakable_plugin, G, TWEAKABLE_PLUGIN); + + +/* Fournit une liste de sections de configuration. */ +tweak_info_t *g_tweakable_plugin_get_tweak_info(const GTweakablePlugin *, size_t *); + + + +/* -------------------- SOLLICITATION DES FONCTIONNALITES CREEES -------------------- */ + + +#define get_tweakable_plugins_info(c) \ + ({ \ + tweak_info_t *__all_info; \ + __all_info = accumulate_from_all_plugins(G_TYPE_TWEAKABLE_PLUGIN, \ + G_TWEAKABLE_PLUGIN, \ + g_tweakable_plugin_get_tweak_info, \ + tweak_info_t, \ + c); \ + __all_info; \ + }) + + + +#endif /* _PLUGINS_TWEAKABLE_H */ diff --git a/src/schemas/re.chrysalide.framework.gschema.xml b/src/schemas/re.chrysalide.framework.gschema.xml index e8331ff..87088f7 100644 --- a/src/schemas/re.chrysalide.framework.gschema.xml +++ b/src/schemas/re.chrysalide.framework.gschema.xml @@ -1,19 +1,29 @@ <schemalist gettext-domain="chrysalide"> - <schema id="re.chrysalide.framework" path="/re/chrysalide/framework/"> - <child schema="re.chrysalide.framework.gui" name="gui"/> - </schema> + <schema id="re.chrysalide.framework" path="/re/chrysalide/framework/"> + <child schema="re.chrysalide.framework.gui" name="gui"/> + <child schema="re.chrysalide.framework.secstorage" name="secstorage"/> + </schema> - <schema id="re.chrysalide.framework.gui" path="/re/chrysalide/framework/gui/"> - <key name="window-width" type="i"> - <default>600</default> - </key> - <key name="window-height" type="i"> - <default>400</default> - </key> - <key name="window-maximized" type="b"> - <default>false</default> - </key> - </schema> + <schema id="re.chrysalide.framework.gui" path="/re/chrysalide/framework/gui/"> + <key name="window-width" type="i"> + <default>600</default> + </key> + <key name="window-height" type="i"> + <default>400</default> + </key> + <key name="window-maximized" type="b"> + <default>false</default> + </key> + </schema> + + <schema id="re.chrysalide.framework.secstorage" path="/re/chrysalide/framework/secstorage/"> + <key name="salt" type="ay"> + <default>[]</default> + </key> + <key name="master" type="ay"> + <default>[]</default> + </key> + </schema> </schemalist> diff --git a/tests/arch/immediate.py b/tests/arch/operands/immediate.py index 74b8069..74b8069 100644 --- a/tests/arch/immediate.py +++ b/tests/arch/operands/immediate.py diff --git a/tests/constval.py b/tests/constval.py deleted file mode 100644 index eafb8d3..0000000 --- a/tests/constval.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/python3-dbg -# -*- coding: utf-8 -*- - - -from chrysacase import ChrysalideTestCase -from pychrysalide import PyConstvalObject -from pychrysalide.arch import ArchInstruction -import pickle - - -class TestConstVal(ChrysalideTestCase): - """TestCase for PyConstvalObject.""" - - - def testCreation(self): - """Validate PyConstvalObject creation from Python.""" - - cst = PyConstvalObject(123, 'XXX') - - self.assertEqual(cst, 123) - - self.assertEqual(str(cst), 'XXX') - - - def testString(self): - """Validate the PyConstvalObject implementation.""" - - self.assertEqual(ArchInstruction.ILT_JUMP, 1) - - self.assertEqual(str(ArchInstruction.ILT_JUMP), 'ILT_JUMP') - - - def testStorage(self): - """Ensure PyConstvalObject instances are storable.""" - - cst = ArchInstruction.ILT_JUMP - - data = pickle.dumps(cst) - - cst = pickle.loads(data) - - self.assertEqual(cst, ArchInstruction.ILT_JUMP) - - self.assertEqual(str(cst), 'ILT_JUMP') diff --git a/tests/format/program.py b/tests/format/program.py index 7a649b8..7027cdf 100644 --- a/tests/format/program.py +++ b/tests/format/program.py @@ -1,6 +1,7 @@ from chrysacase import ChrysalideTestCase from pychrysalide import SourceEndian +from pychrysalide.analysis.contents import MemoryContent #from pychrysalide.arch import vmpa, mrange from pychrysalide.format import ProgramFormat #from pychrysalide.format import BinSymbol @@ -17,12 +18,16 @@ class TestProgramFormat(ChrysalideTestCase): def testCustomInstance(self): """Validate a full custom ProgramFormat implementation.""" + data = b'\x00\x00\x00\xef' + cnt = MemoryContent(data) + + class CustomFormat(ProgramFormat): def _get_endianness(self): return SourceEndian.BIG - cf = CustomFormat() + cf = CustomFormat(cnt) self.assertEqual(cf.endianness, SourceEndian.BIG) @@ -30,7 +35,7 @@ class TestProgramFormat(ChrysalideTestCase): class EmptyCustomFormat(ProgramFormat): pass - cf = EmptyCustomFormat() + cf = EmptyCustomFormat(cnt) self.assertEqual(cf.endianness, SourceEndian.LITTLE) diff --git a/tests/glibext/comparable.py b/tests/glibext/comparable.py new file mode 100644 index 0000000..48291ca --- /dev/null +++ b/tests/glibext/comparable.py @@ -0,0 +1,132 @@ + +import gi + +from chrysacase import ChrysalideTestCase +from gi.repository import GObject +from pychrysalide.glibext import ComparableObject + + +class TestStringBuilder(ChrysalideTestCase): + """Test cases for pychrysalide.glibext.ComparableObject.""" + + + def testComparableObjectCreation(self): + """Create objects with ComparableObject interface.""" + + with self.assertRaisesRegex(NotImplementedError, 'ComparableObject can not be constructed'): + + co = ComparableObject() + + + class NewComparableObjectImplem(gi._gi.GObject, ComparableObject): + pass + + nco = NewComparableObjectImplem() + + self.assertIsNotNone(nco) + + + class NewComparableObjectImplem2(GObject.Object, ComparableObject): + pass + + nco2 = NewComparableObjectImplem() + + self.assertIsNotNone(nco2) + + + def testComparableObjectMethods(self): + """Test the ComparableObject methods.""" + + class BasicComparableObjectImplem(GObject.Object, ComparableObject): + + def __init__(self, val): + super().__init__() + self._val = val + + def _compare(self, other): + if self._val < other._val: + status = -1 + elif self._val > other._val: + status = 1 + else: + status = 0 + return status + + + a = BasicComparableObjectImplem(123) + b = BasicComparableObjectImplem(456) + + self.assertTrue(a <= b) + + # Sans l'action de inherit_interface_slots(), c'est pygobject_richcompare() qui est appelée, + # laquelle compare simplement les adresses des pointeurs + + c = BasicComparableObjectImplem(789) + d = BasicComparableObjectImplem(234) + + self.assertTrue(c > d) + + + def testComparableObjectExceptions(self): + """Raise exceptions from the ComparableObject interface as expected.""" + + + class OtherComparableObject(GObject.Object, ComparableObject): + pass + + other = OtherComparableObject() + + + class BadComparableObjectImplem(GObject.Object, ComparableObject): + pass + + obj = BadComparableObjectImplem() + + + with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_compare'"): + + s = obj < other + + + class BadComparableObjectImplem2(GObject.Object, ComparableObject): + + def _compare(self, other): + return 'AAA' + + obj2 = BadComparableObjectImplem2() + + + with self.assertRaisesRegex(TypeError, 'comparison status has to be a signed integer'): + + s = obj2 < other + + + class BadComparableObjectImplem3a(GObject.Object, ComparableObject): + + def _compare(self, other): + return 123 + + class BadComparableObjectImplem3b(BadComparableObjectImplem3a, ComparableObject): + + def _compare(self, other): + return self.parent_compare() + + obj3 = BadComparableObjectImplem3b() + + + with self.assertRaisesRegex(RuntimeError, 'object parent is not a native type'): + + s = obj3 < other + + + class BadComparableObjectImplem4(GObject.Object, ComparableObject): + + def _compare(self, other): + raise Exception('error') + + obj4 = BadComparableObjectImplem4() + + + with self.assertRaisesRegex(Exception, 'error'): + + s = obj4 < other diff --git a/tests/glibext/configuration.py b/tests/glibext/configuration.py deleted file mode 100644 index 880b445..0000000 --- a/tests/glibext/configuration.py +++ /dev/null @@ -1,106 +0,0 @@ - -import gi -gi.require_version('Gdk', '3.0') -from gi.repository import Gdk - -from chrysacase import ChrysalideTestCase -from pychrysalide.glibext import ConfigParam, GenConfig - - -class TestConfiguration(ChrysalideTestCase): - """TestCase for configuration related items.*""" - - - def testCfgParamValues(self): - """Set and unset configuration parameter values.""" - - color = Gdk.RGBA() - color.parse('#3465A4') - - param = ConfigParam('config.color', ConfigParam.ConfigParamType.COLOR, color) - - self.assertEqual(param.value, color) - - param.make_empty() - - void = Gdk.RGBA(red=0, green=0, blue=0, alpha=0) - self.assertEqual(param.value, void) - - param.value = color - - self.assertEqual(param.value, color) - - - def testCfgParamStates(self): - """Validate all states of an evolving parameter.""" - - param = ConfigParam('config.int', ConfigParam.ConfigParamType.INTEGER) - - self.assertEqual(param.state, ConfigParam.ConfigParamState.EMPTY | ConfigParam.ConfigParamState.DEFAULT) - - param.make_empty() - - self.assertEqual(param.state, ConfigParam.ConfigParamState.EMPTY | ConfigParam.ConfigParamState.DEFAULT) - - param = ConfigParam('config.int', ConfigParam.ConfigParamType.INTEGER, 0x123) - - self.assertEqual(param.value, 0x123) - - self.assertEqual(param.state, ConfigParam.ConfigParamState.DEFAULT) - - param.make_empty() - - self.assertEqual(param.state, ConfigParam.ConfigParamState.EMPTY | ConfigParam.ConfigParamState.CHANGED) - - param.value = 0x1 - - self.assertEqual(param.state, ConfigParam.ConfigParamState.CHANGED) - - param.reset() - - self.assertEqual(param.state, ConfigParam.ConfigParamState.DEFAULT) - - - def testCfgParamDesc(self): - """Export types and states as strings when needed.""" - - param = ConfigParam('config.int', ConfigParam.ConfigParamType.INTEGER) - - self.assertTrue('|' in str(param.state)) - - self.assertTrue('.' in str(param.type)) - - - def testConfiguration(self): - """Feed and browse a basic configuration.""" - - config = GenConfig() - self.assertIsNotNone(config) - self.assertIsNone(config.filename) - - for i in range(5): - param = ConfigParam('config.int.%u' % i, ConfigParam.ConfigParamType.INTEGER, i) - config.add(param) - - chain = '' - - for p in config.params: - chain += '%u' % p.value - - self.assertTrue(chain == ''.join([ '%u' % i for i in range(5) ])) - - found = config.search('config.int.3') - self.assertTrue(found.value == 3) - - found = config.search('config.int.33') - self.assertIsNone(found) - - for p in config.params: - p.value *= 10 - - chain = '' - - for p in config.params: - chain += '%u' % p.value - - self.assertTrue(chain == ''.join([ '%u' % (i * 10) for i in range(5) ])) diff --git a/tests/glibext/hashable.py b/tests/glibext/hashable.py new file mode 100644 index 0000000..07f22e3 --- /dev/null +++ b/tests/glibext/hashable.py @@ -0,0 +1,190 @@ + +import gi + +from chrysacase import ChrysalideTestCase +from gi.repository import GObject +from pychrysalide.glibext import HashableObject + + +class TestStringBuilder(ChrysalideTestCase): + """Test cases for pychrysalide.glibext.HashableObject.""" + + + def testHashableObjectCreation(self): + """Create objects with HashableObject interface.""" + + with self.assertRaisesRegex(NotImplementedError, 'HashableObject can not be constructed'): + + ho = HashableObject() + + + class NewHashableObjectImplem(gi._gi.GObject, HashableObject): + pass + + nho = NewHashableObjectImplem() + + self.assertIsNotNone(nho) + + + class NewHashableObjectImplem2(GObject.Object, HashableObject): + pass + + nho2 = NewHashableObjectImplem() + + self.assertIsNotNone(nho2) + + + def testHashableObjectMethods(self): + """Test the HashableObject methods.""" + + class BasicHashableObjectImplem(gi._gi.GObject, HashableObject): + + def __init__(self, val): + super().__init__() + self._val = val + + def _hash(self): + return self._val + + value = 1234 + + ho = BasicHashableObjectImplem(value) + + self.assertEqual(hash(ho), value) + + + class BasicHashableObjectImplem2(GObject.Object, HashableObject): + + def __init__(self, val): + super().__init__() + self._val = val + + def _hash(self): + return self._val + + value = 5678 + + ho2 = BasicHashableObjectImplem2(value) + + self.assertEqual(hash(ho2), value) + + + def testCascadingHashableObjectImplementations(self): + """Request the hash from the object parent for a full computing.""" + + + class CascadingHashableObjectFullImplemA(GObject.Object, HashableObject): + + def _hash(self): + return 1 + + class CascadingHashableObjectFullImplemB(CascadingHashableObjectFullImplemA, HashableObject): + + def _hash(self): + return super()._hash() * 2 + + class CascadingHashableObjectFullImplemC(CascadingHashableObjectFullImplemB, HashableObject): + + def _hash(self): + return super()._hash() * 3 + + + obj_a = CascadingHashableObjectFullImplemA() + + obj_b = CascadingHashableObjectFullImplemB() + + obj_c = CascadingHashableObjectFullImplemC() + + + self.assertEqual(hash(obj_a), 1) + self.assertEqual(hash(obj_b), 2) + self.assertEqual(hash(obj_c), 6) + + + class CascadingHashableObjectPartialImplemA(GObject.Object, HashableObject): + + def _hash(self): + return 1 + + class CascadingHashableObjectPartialImplemB(CascadingHashableObjectPartialImplemA): + pass + + class CascadingHashableObjectPartialImplemC(CascadingHashableObjectPartialImplemB, HashableObject): + + def _hash(self): + return super()._hash() * 3 + + + obj_a = CascadingHashableObjectPartialImplemA() + + obj_b = CascadingHashableObjectPartialImplemB() + + obj_c = CascadingHashableObjectPartialImplemC() + + + self.assertEqual(hash(obj_a), 1) + self.assertEqual(hash(obj_b), 1) + self.assertEqual(hash(obj_c), 3) + + + def testHashableObjectExceptions(self): + """Raise exceptions from the HashableObject interface as expected.""" + + class BadHashableObjectImplem(GObject.Object, HashableObject): + pass + + obj = BadHashableObjectImplem() + + + with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_hash'"): + + h = hash(obj) + + + with self.assertRaisesRegex(TypeError, 'object parent does not implement the HashableObject interface'): + + h = obj.parent_hash() + + + class BadHashableObjectImplem2(GObject.Object, HashableObject): + + def _hash(self): + return 'AAA' + + obj2 = BadHashableObjectImplem2() + + + with self.assertRaisesRegex(TypeError, 'computed hash value has to be an unsigned integer'): + + h = hash(obj2) + + + class BadHashableObjectImplem3a(GObject.Object, HashableObject): + + def _hash(self): + return 123 + + class BadHashableObjectImplem3b(BadHashableObjectImplem3a, HashableObject): + + def _hash(self): + return self.parent_hash() + + obj3 = BadHashableObjectImplem3b() + + + with self.assertRaisesRegex(RuntimeError, 'object parent is not a native type'): + + h = hash(obj3) + + + class BadHashableObjectImplem4(GObject.Object, HashableObject): + + def _hash(self): + raise Exception('error') + + obj4 = BadHashableObjectImplem4() + + + with self.assertRaisesRegex(Exception, 'error'): + + h = hash(obj4) diff --git a/tests/glibext/objhole.py b/tests/glibext/objhole.py new file mode 100644 index 0000000..6a7c2e8 --- /dev/null +++ b/tests/glibext/objhole.py @@ -0,0 +1,31 @@ + +from chrysacase import ChrysalideTestCase +from pychrysalide.glibext import ThickObject + + +class TestWorks(ChrysalideTestCase): + """TestCase for pychrysalide.glibext.BinaryPortion""" + + def testExtraAccess(self): + """Access to various definitions of the extra data for ThickObject.""" + + obj = ThickObject() + + self.assertEqual(obj.extra, 0) + + obj.extra = 0xffffffe0 + + self.assertEqual(obj.extra, 0xffffffe0) + + obj.extra = 0x00123000 + + self.assertEqual(obj.extra, 0x00123000) + + + def testRservedBits(self): + """Check space leaved as available by the GLib.""" + + obj = ThickObject() + + self.assertTrue(obj._GOBJECT_RESERVED_EXTRA_BITS > 0) + self.assertTrue(obj._GOBJECT_RESERVED_EXTRA_BITS < 32) diff --git a/tests/glibext/re.chrysalide.tests.secstorage.gschema.xml b/tests/glibext/re.chrysalide.tests.secstorage.gschema.xml new file mode 100644 index 0000000..6afa96b --- /dev/null +++ b/tests/glibext/re.chrysalide.tests.secstorage.gschema.xml @@ -0,0 +1,15 @@ +<schemalist> + + <schema id="re.chrysalide.tests.secstorage" path="/re/chrysalide/tests/secstorage/"> + + <key name="salt" type="ay"> + <default>[]</default> + </key> + + <key name="master" type="ay"> + <default>[]</default> + </key> + + </schema> + +</schemalist> diff --git a/tests/glibext/secstorage.py b/tests/glibext/secstorage.py new file mode 100644 index 0000000..5b8f680 --- /dev/null +++ b/tests/glibext/secstorage.py @@ -0,0 +1,153 @@ + +import gi +import os +import subprocess + +from chrysacase import ChrysalideTestCase +from gi.repository import Gio, GLib +from pychrysalide.glibext import SecretStorage + + +class TestSecretStorage(ChrysalideTestCase): + """TestCase for secret storage features.""" + + @classmethod + def setUpClass(cls): + + super(TestSecretStorage, cls).setUpClass() + + cls.log('Creating GSettings schema...') + + path = os.path.dirname(os.path.realpath(__file__)) + + subprocess.run([ 'glib-compile-schemas', path ]) + + + @classmethod + def tearDownClass(cls): + + super(TestSecretStorage, cls).tearDownClass() + + cls.log('Removing compiled GSettings schema...') + + path = os.path.dirname(os.path.realpath(__file__)) + + filename = os.path.join(path, 'gschemas.compiled') + + if os.path.exists(filename): + os.remove(filename) + + + def _get_settings(self, sid): + """Provide local GSettings instance.""" + + path = os.path.dirname(os.path.realpath(__file__)) + + source = Gio.SettingsSchemaSource.new_from_directory(path, None, True) + + schema = Gio.SettingsSchemaSource.lookup(source, sid, False) + + settings = Gio.Settings.new_full(schema, None, None) + + return settings + + + def testMasterKeyDefinition(self): + """Check for cryptographic parameters for secret storage.""" + + settings = self._get_settings('re.chrysalide.tests.secstorage') + + storage = SecretStorage(settings) + + settings.reset('master') + + self.assertEqual(len(settings.get_value('master').unpack()), 0) + + self.assertFalse(storage.has_key) + + settings.set_value('master', GLib.Variant('ay', b'ABC')) + + self.assertFalse(storage.has_key) + + settings.set_value('master', GLib.Variant('ay', b'A' * 23)) + + self.assertTrue(storage.has_key) + + + def testMasterKeyCreation(self): + """Create and update cryptographic parameters for secret storage.""" + + settings = self._get_settings('re.chrysalide.tests.secstorage') + + storage = SecretStorage(settings) + + settings.reset('salt') + settings.reset('master') + + self.assertFalse(storage.has_key) + + status = storage.set_password('') + + self.assertTrue(status); + + self.assertTrue(storage.has_key) + self.assertTrue(storage.is_locked) + + status = storage.unlock('') + + self.assertTrue(status) + + self.assertFalse(storage.is_locked) + + storage.lock() + + self.assertTrue(storage.is_locked) + + status = storage.unlock('XXX') + + self.assertFalse(status) + + self.assertTrue(storage.is_locked) + + + def testDataEncryption(self): + """Encrypt and decrypt data with the secret storage.""" + + settings = self._get_settings('re.chrysalide.tests.secstorage') + + storage = SecretStorage(settings) + + settings.reset('salt') + settings.reset('master') + + status = storage.set_password('<s3cUre>') + + self.assertTrue(status); + + status = storage.unlock('<s3cUre>') + + self.assertTrue(status) + + + original = b'ABC' + + encrypted = storage.encrypt_data(original) + + self.assertIsNotNone(encrypted) + + plain = storage.decrypt_data(encrypted) + + self.assertIsNotNone(plain) + self.assertEqual(original, plain) + + + original = b'A' * 136 + + encrypted = storage.encrypt_data(original) + + self.assertIsNotNone(encrypted) + + plain = storage.decrypt_data(encrypted) + + self.assertIsNotNone(plain) + self.assertEqual(original, plain) diff --git a/tests/glibext/singleton.py b/tests/glibext/singleton.py index 4588ae5..712aece 100644 --- a/tests/glibext/singleton.py +++ b/tests/glibext/singleton.py @@ -1,21 +1,24 @@ +import gi + from chrysacase import ChrysalideTestCase from gi.repository import GObject -from pychrysalide.glibext import SingletonCandidate, SingletonFactory +from pychrysalide.glibext import ComparableObject, HashableObject, SingletonCandidate, SingletonFactory class TestSingleton(ChrysalideTestCase): """Test cases for pychrysalide.glibext.SingletonFactory.""" - def testSingletonCreation(self): - """Create singleton objects.""" + def testSingletonCandidateCreation(self): + """Create objects with SingletonCandidate interface.""" with self.assertRaisesRegex(NotImplementedError, 'SingletonCandidate can not be constructed'): sc = SingletonCandidate() - class NewSingletonImplem(GObject.Object, SingletonCandidate): + + class NewSingletonImplem(gi._gi.GObject, HashableObject, ComparableObject, SingletonCandidate): pass nsi = NewSingletonImplem() @@ -23,121 +26,132 @@ class TestSingleton(ChrysalideTestCase): self.assertIsNotNone(nsi) - def testFactoryCreation(self): - """Create singleton factories.""" + class NewSingletonImplem2(GObject.Object, HashableObject, ComparableObject, SingletonCandidate): + pass + + nsi2 = NewSingletonImplem2() - sf = SingletonFactory() + self.assertIsNotNone(nsi2) - self.assertIsNotNone(sf) - class MyFactory(SingletonFactory): - pass + # def testFactoryCreation(self): + # """Create singleton factories.""" + + # sf = SingletonFactory() + + # self.assertIsNotNone(sf) + + # class MyFactory(SingletonFactory): + # pass + + # msf = MyFactory() + + # self.assertIsNotNone(msf) + - msf = MyFactory() - self.assertIsNotNone(msf) - def testSingletonMethods(self): - """Test the singleton methods.""" + # def testSingletonMethods(self): + # """Test the singleton methods.""" - class IntegerCacheImplem(GObject.Object, SingletonCandidate): + # class IntegerCacheImplem(GObject.Object, SingletonCandidate): - def __init__(self, val): - super().__init__() - self._val = val + # def __init__(self, val): + # super().__init__() + # self._val = val - def _list_inner_instances(self): - return () + # def _list_inner_instances(self): + # return () - def __hash__(self): - return hash('common-key') + # def __hash__(self): + # return hash('common-key') - def __eq__(self, other): - return self._val == other._val + # def __eq__(self, other): + # return self._val == other._val - val_0 = IntegerCacheImplem(0) - val_0_bis = IntegerCacheImplem(0) - val_1 = IntegerCacheImplem(1) + # val_0 = IntegerCacheImplem(0) + # val_0_bis = IntegerCacheImplem(0) + # val_1 = IntegerCacheImplem(1) - self.assertEqual(hash(val_0), hash(val_0_bis)) - self.assertEqual(hash(val_0), hash(val_1)) + # self.assertEqual(hash(val_0), hash(val_0_bis)) + # self.assertEqual(hash(val_0), hash(val_1)) - self.assertEqual(val_0.hash(), val_0_bis.hash()) - self.assertEqual(val_0.hash(), val_1.hash()) + # self.assertEqual(val_0.hash(), val_0_bis.hash()) + # self.assertEqual(val_0.hash(), val_1.hash()) - self.assertTrue(val_0 == val_0_bis) - self.assertFalse(val_0 == val_1) + # self.assertTrue(val_0 == val_0_bis) + # self.assertFalse(val_0 == val_1) - def testSingletonFootprint(self): - """Check for singleton memory footprint.""" + # def testSingletonFootprint(self): + # """Check for singleton memory footprint.""" - sf = SingletonFactory() + # sf = SingletonFactory() - class IntegerCacheImplem(GObject.Object, SingletonCandidate): + # class IntegerCacheImplem(GObject.Object, SingletonCandidate): - def __init__(self, val): - super().__init__() - self._val = val + # def __init__(self, val): + # super().__init__() + # self._val = val - def _list_inner_instances(self): - return () + # def _list_inner_instances(self): + # return () - def __hash__(self): - return hash('common-key') + # def __hash__(self): + # return hash('common-key') - def __eq__(self, other): - return self._val == other._val + # def __eq__(self, other): + # return self._val == other._val - def _set_read_only(self): - pass + # def _set_read_only(self): + # pass - val_0 = IntegerCacheImplem(0) - val_0_bis = IntegerCacheImplem(0) - val_1 = IntegerCacheImplem(1) + # val_0 = IntegerCacheImplem(0) + # val_0_bis = IntegerCacheImplem(0) + # val_1 = IntegerCacheImplem(1) - obj = sf.get_instance(val_0) + # obj = sf.get_instance(val_0) - self.assertTrue(obj is val_0) + # self.assertTrue(obj is val_0) - obj = sf.get_instance(val_0_bis) + # obj = sf.get_instance(val_0_bis) - self.assertTrue(obj is val_0) + # self.assertTrue(obj is val_0) - obj = sf.get_instance(val_1) + # obj = sf.get_instance(val_1) - self.assertTrue(obj is val_1) + # self.assertTrue(obj is val_1) - self.assertEqual(len(obj.inner_instances), 0) + # self.assertEqual(len(obj.inner_instances), 0) - class MasterCacheImplem(GObject.Object, SingletonCandidate): + # class MasterCacheImplem(GObject.Object, SingletonCandidate): - def __init__(self, children): - super().__init__() - self._children = children + # def __init__(self, children): + # super().__init__() + # self._children = children - def _list_inner_instances(self): - return self._children + # def _list_inner_instances(self): + # return self._children - def _update_inner_instances(self, instances): - self._children = instances + # def _update_inner_instances(self, instances): + # self._children = instances - def __hash__(self): - return hash('master-key') + # def __hash__(self): + # return hash('master-key') - def __eq__(self, other): - return False + # def __eq__(self, other): + # return False - def _set_read_only(self): - pass + # def _set_read_only(self): + # pass - master = MasterCacheImplem(( val_0_bis, val_1 )) + # master = MasterCacheImplem(( val_0_bis, val_1 )) - obj = sf.get_instance(master) + # obj = sf.get_instance(master) - self.assertTrue(obj.inner_instances[0] is val_0) + # self.assertTrue(obj.inner_instances[0] is val_0) - self.assertTrue(obj.inner_instances[1] is val_1) + # self.assertTrue(obj.inner_instances[1] is val_1) diff --git a/tests/glibext/strbuilder.py b/tests/glibext/strbuilder.py new file mode 100644 index 0000000..72fd5c2 --- /dev/null +++ b/tests/glibext/strbuilder.py @@ -0,0 +1,114 @@ + +import gi + +from chrysacase import ChrysalideTestCase +from gi.repository import GObject +from pychrysalide.glibext import StringBuilder + + +class TestStringBuilder(ChrysalideTestCase): + """Test cases for pychrysalide.glibext.StringBuilder.""" + + + def ZZZtestStringBuilderCreation(self): + """Create objects with StringBuilder interface.""" + + with self.assertRaisesRegex(NotImplementedError, 'StringBuilder can not be constructed'): + + sb = StringBuilder() + + + class NewStringBuilderImplem(gi._gi.GObject, StringBuilder): + pass + + nsb = NewStringBuilderImplem() + + self.assertIsNotNone(nsb) + + + class NewStringBuilderImplem2(GObject.Object, StringBuilder): + pass + + nsb2 = NewStringBuilderImplem() + + self.assertIsNotNone(nsb2) + + + def ZZZtestStringBuilderMethods(self): + """Test the StringBuilder methods.""" + + class BasicStringBuilderImplem(GObject.Object, StringBuilder): + + def __init__(self, desc): + super().__init__() + self._desc = desc + + def _to_string(self, flags=0): + return self._desc + + desc = 'simple_desc' + + sb = BasicStringBuilderImplem(desc) + + self.assertEqual(sb.to_string(), desc) + self.assertEqual(str(sb), desc) + self.assertEqual(f'{sb}', desc) + + + def testStringBuilderExceptions(self): + """Raise exceptions from the StringBuilder interface as expected.""" + + + class BadStringBuilderImplem(GObject.Object, StringBuilder): + pass + + obj = BadStringBuilderImplem() + + + with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_to_string'"): + + s = str(obj) + + + class BadStringBuilderImplem2(GObject.Object, StringBuilder): + + def _to_string(self, flags=0): + return 0xcafe + + obj2 = BadStringBuilderImplem2() + + + with self.assertRaisesRegex(TypeError, 'object description has to get provided as an UTF-8 string value'): + + s = str(obj2) + + + class BadStringBuilderImplem3a(GObject.Object, StringBuilder): + + def _to_string(self, flags=0): + return 'desc' + + class BadStringBuilderImplem3b(BadStringBuilderImplem3a, StringBuilder): + + def _to_string(self, flags=0): + return self.parent_to_string() + + obj3 = BadStringBuilderImplem3b() + + + with self.assertRaisesRegex(RuntimeError, 'object parent is not a native type'): + + s = str(obj3) + + + class BadStringBuilderImplem4(GObject.Object, StringBuilder): + + def _to_string(self, flags=0): + raise Exception('error') + + obj4 = BadStringBuilderImplem4() + + + with self.assertRaisesRegex(Exception, 'error'): + + s = str(obj4) diff --git a/tests/plugins/plugin.py b/tests/plugins/plugin.py index 6409975..9015409 100644 --- a/tests/plugins/plugin.py +++ b/tests/plugins/plugin.py @@ -1,223 +1,96 @@ -#!/usr/bin/python3-dbg -# -*- coding: utf-8 -*- - from chrysacase import ChrysalideTestCase -from pychrysalide import PluginModule -import gc +from pychrysalide.plugins import PluginModule class TestPlugin(ChrysalideTestCase): """TestCase for GPluginModule.""" - def testGarbageCollecting(self): - """Ensure the garbarge collector is working for plugin modules.""" - + def testPluginInfoImplementations(self): + """Retrieve plugin basic information provided by __init__().""" - class MyPG_1(PluginModule): + class MyPlugin(PluginModule): def __init__(self): + super().__init__('custom-name', desc='custom-desc', url='custom-url', version='0.0.1') - interface = { - 'name' : 'some_name', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( ) - } + my = MyPlugin() - super(MyPG_1, self).__init__(**interface) + self.assertEqual(my.name, 'custom-name') + self.assertEqual(my.desc, 'custom-desc') + self.assertEqual(my.version, '0.0.1') + self.assertEqual(my.url, 'custom-url') - pg = MyPG_1() - self.assertIsNotNone(pg) + def testPluginMethodImplementations(self): + """Ensure required plugins abstract methods can be implemented.""" - - class MyPG_2(PluginModule): + class MyWrongPlugin(PluginModule): def __init__(self): + super().__init__('pg-name') - interface = { - 'name' : 'some_name', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( ) - } - - super(MyPG_2, self).__init__(**interface) + my = MyWrongPlugin() + with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_get_filename'"): + print(my.filename) - pg = MyPG_2() - self.assertIsNotNone(pg) + with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_get_modname'"): + print(my.modname) - class MyPG_3(PluginModule): + class MyPlugin(PluginModule): def __init__(self): + super().__init__('pg-name') - interface = { - 'name' : 'some_name', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( ) - } + def _get_filename(self): + return 'file-name' - super(MyPG_3, self).__init__(**interface) + def _get_modname(self): + return 'mod-name' + my = MyPlugin() - pg = MyPG_3() - self.assertIsNotNone(pg) + self.assertEqual(my.filename, 'file-name') + self.assertEqual(my.modname, 'mod-name') - class MyPG_4(PluginModule): - - def __init__(self): - - interface = { - 'name' : 'some_name', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( ) - } - super(MyPG_4, self).__init__(**interface) + def testPluginRequirementIncludePython(self): + """Ensure that plugin requirements include the Python bindings.""" - - pg = MyPG_4() - self.assertIsNotNone(pg) - - - class MyPG_5(PluginModule): + class MyPlugin(PluginModule): def __init__(self): + super().__init__('pg-name') - interface = { - 'name' : 'some_name', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( ) - } - - super(MyPG_5, self).__init__(**interface) - - - pg = MyPG_5() - self.assertIsNotNone(pg) + my = MyPlugin() + self.assertEqual(len(my.requirements), 1) + self.assertEqual(my.requirements[0], 'PyChrysalide') - gc.collect() + def testPluginRequirementUniqueness(self): + """Ensure that plugin requirements are unique.""" - def testCreation(self): - """Validate PluginModule creation from Python.""" - - - class MyPG_0(PluginModule): - pass - - - # TypeError: Required argument 'name' (pos 1) not found - with self.assertRaises(TypeError): - pg = MyPG_0() - - - class MyPG_1(PluginModule): + class MyPlugin(PluginModule): def __init__(self): + super().__init__('pg-name', required=[ 'PyChrysalide' ]) - interface = { - 'name' : 'some_name', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( ) - } - - super(MyPG_1, self).__init__(**interface) + my = MyPlugin() + self.assertEqual(my.requirements.count('PyChrysalide'), 1) - pg = MyPG_1() - self.assertIsNotNone(pg) - - class MyPG_2(PluginModule): + class MyPlugin2(PluginModule): def __init__(self): + super().__init__('pg-name2', required=[ 'AA', 'BB', 'AA' ]) - interface = { - 'name' : 'some_name', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( 'ABC', ) - } - - super(MyPG_2, self).__init__(**interface) - - - # TypeError: Invalid type for plugin action. - with self.assertRaises(TypeError): - pg = MyPG_2() - - - class MyPG_3(PluginModule): - - def __init__(self): - - interface = { - 'name' : 'some_name', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( PluginModule.PGA_CONTENT_EXPLORER, ) - } - - super(MyPG_3, self).__init__(**interface) - - - # TypeError: missing features for the declared actions. - with self.assertRaises(TypeError): - pg = MyPG_3() - - - class MyPG_4(PluginModule): - - def __init__(self): - - interface = { - 'name' : 'some_name4', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( PluginModule.PGA_CONTENT_EXPLORER, ) - } - - super(MyPG_4, self).__init__(**interface) - - def handle_binary_content(self, action, content, wid, status): - pass - - - pg = MyPG_4() - self.assertIsNotNone(pg) - - - def testDoubleUsage(self): - """Validate PluginModule double usage in Python.""" - - - class MyPG(PluginModule): - - def __init__(self): - - interface = { - 'name' : 'some_name', - 'desc' : 'Provide some information about the useless plugin', - 'version' : '0.1', - 'actions' : ( ) - } - - super(MyPG, self).__init__(**interface) - - - pg1 = MyPG() - self.assertIsNotNone(pg1) + my = MyPlugin2() - pg2 = MyPG() - self.assertIsNotNone(pg2) + self.assertEqual(my.requirements.count('AA'), 1) + self.assertEqual(my.requirements.count('PyChrysalide'), 1) diff --git a/tests/plugins/python.py b/tests/plugins/python.py new file mode 100644 index 0000000..1a3dd97 --- /dev/null +++ b/tests/plugins/python.py @@ -0,0 +1,27 @@ + +from chrysacase import ChrysalideTestCase +from pychrysalide.plugins import PythonPlugin + + +class TestPlugin(ChrysalideTestCase): + """TestCase for GPythonPlugin.""" + + + def testPluginInfoImplementations(self): + """Retrieve plugin basic information provided by __init__().""" + + class MyPlugin(PythonPlugin): + """Custom description.""" + + def __init__(self): + super().__init__(__file__, '0.0.1', 'custom-url') + + my = MyPlugin() + + self.assertEqual(my.name, 'MyPlugin') + self.assertEqual(my.desc, 'Custom description.') + self.assertEqual(my.version, '0.0.1') + self.assertEqual(my.url, 'custom-url') + + self.assertEqual(my.filename, __file__) + self.assertEqual(my.modname, 'python') diff --git a/tools/maint/exportall.inc b/tools/maint/exportall.inc new file mode 100644 index 0000000..f03be3c --- /dev/null +++ b/tools/maint/exportall.inc @@ -0,0 +1,8 @@ + +# source tools/maint/exportall.inc + +# unset PYTHONPATH GSETTINGS_SCHEMA_DIR LD_LIBRARY_PATH + +source tools/maint/pypath.inc +source tools/maint/gsettings.inc +source tools/maint/libpath.inc diff --git a/tools/maint/libpath.inc b/tools/maint/libpath.inc new file mode 100644 index 0000000..19e68d7 --- /dev/null +++ b/tools/maint/libpath.inc @@ -0,0 +1,10 @@ + +# Look for all path containing binaries for local execution + +# source tools/maint/libpath.inc + +LD_LIBRARY_PATH="$PWD/src/.libs:$LD_LIBRARY_PATH" + +find . -type d -name schemas -exec glib-compile-schemas {} \; + +export LD_LIBRARY_PATH=$( find plugins/ -maxdepth 2 -type d -name '.libs' -printf "$PWD/plugins/%P:" | sed 's/:$//' ):"$LD_LIBRARY_PATH" |