/* Chrysalide - Outil d'analyse de fichiers binaires
 * pychrysa.c - plugin permettant des extensions en Python
 *
 * Copyright (C) 2009-2014 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  OpenIDA 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.
 *
 *  OpenIDA 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 .
 */
#include "pychrysa.h"
#if 0
#include 
#include 
#include 
//#include 
#include 
#include "quirks.h"
#include "analysis/module.h"
#include "arch/module.h"
#include "debug/module.h"
#include "format/module.h"
#include "glibext/module.h"
#include "gtkext/module.h"
#include "gui/module.h"
/*
#include "analysis/py_binary.h"
#include "analysis/py_line.h"
#include "analysis/py_line_code.h"
#include "analysis/roptions.h"
*/
#include "../../src/common/environment.h"
#include "../../src/common/extstr.h"
#include "../../src/plugins/plugin-int.h"
/******************************************************************************
*                                                                             *
*  Paramètres  : plugin = instance représentant le greffon en chargement.     *
*                ref    = espace de référencement global.                     *
*                                                                             *
*  Description : Initialise le greffon permettant l'usage de Python.          *
*                                                                             *
*  Retour      : true.                                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
bool init_plugin(GPluginModule *plugin, GObject *ref)
{
    char *paths;                            /* Emplacements de greffons    */
    char *path;                             /* Chemin à fouiller           */
    char *save;                             /* Sauvegarde pour ré-entrance */
    DIR *dir;                               /* Répertoire à parcourir      */
    struct dirent entry;                    /* Elément trouvé              */
    struct dirent *next;                    /* Prochain élément fourni     */
    int ret;                                /* Bilan d'un appel système    */
    char *filename;                         /* Chemin d'accès reconstruit  */
    GPluginModule *pyplugin;
    /* Définition des zones d'influence */
    add_to_env_var("PYTHONPATH", PLUGINS_DIR G_DIR_SEPARATOR_S "python", ";");
    paths = get_env_var("PYTHONPATH");
    /* Intialisations Python */
    //return false;
    define_internal_ref(ref);
    Py_Initialize();
    //pychrysalide_set_gc_threshold(INT_MAX, INT_MAX, INT_MAX);
    //pychrysalide_set_gc_threshold(1, 1, 1);
    initpychrysa();
    /* Chargement des greffons */
    save = NULL;   /* gcc... */
    for (path = strtok_r(paths, ";", &save);
         path != NULL; 
         path = strtok_r(NULL, ";", &save))
    {
        dir = opendir(path);
        if (dir == NULL)
        {
            perror("opendir");
            continue;
        }
        for (ret = readdir_r(dir, &entry, &next);
             ret == 0 && next != NULL;
             ret = readdir_r(dir, &entry, &next))
        {
            if (entry.d_name[0] == '.') continue;
            //if (strcmp(entry.d_name, "test") != 0) continue;
            printf("NAME :: '%s'\n", entry.d_name);
            if (strcmp(entry.d_name, "apkfiles") != 0) continue;
            filename = strdup(entry.d_name);
            filename = stradd(filename, ".");
            filename = stradd(filename, "__init__");
            pyplugin = g_python_plugin_new(entry.d_name, filename);
            if (pyplugin == NULL)
                g_plugin_module_log_variadic_message(plugin, LMT_ERROR, 
                                                     _("No suitable Python plugin found in '%s'"),
                                                     filename);
            else
            {
                g_plugin_module_log_variadic_message(plugin, LMT_PROCESS, 
                                                     _("Loaded Python plugin '%s' from the '%s' directory"),
                                                     g_plugin_module_get_name(G_PLUGIN_MODULE(pyplugin)), path);
                add_plugin_to_main_list(pyplugin);
            }
            free(filename);
        }
         closedir(dir);
    }
    return true;
}
#endif
#include 
#include 
#include 
#include "analysis/module.h"
#include "arch/module.h"
#include "common/module.h"
#include "core/module.h"
#include "format/module.h"
#include "glibext/module.h"
#include "gtkext/module.h"
#include "gui/module.h"
// TODO : à bouger ?
#include "../../src/arch/processor.h"
#include "../../src/format/format.h"
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////:///////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////:///////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////:///////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////:///////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////:///////////////////////////
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "plugin.h"
#include "quirks.h"
DEFINE_CHRYSALIDE_ACTIVE_PLUGIN("PyChrysalide", "Provides bindings to Python", "0.1.0", PGA_PLUGIN_INIT);
/* 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 *);
/* Recherche et fournit si elle existe une valeur globale. */
static PyObject *py_chrysalide_get_global_gobject(PyObject *, PyObject *);
/* Détermine si l'interpréteur lancé est celui pris en compte. */
static bool is_current_abi_suitable(void);
/* Charge autant de greffons composés en Python que possible. */
static bool load_python_plugins(GPluginModule *plugin, GObject *);
/******************************************************************************
*                                                                             *
*  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)
{
    return PyUnicode_FromString("r" XSTR(REVISION));
}
/******************************************************************************
*                                                                             *
*  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)
{
    char version[16];
    int major;
    int minor;
    int revision;
    major = REVISION / 1000;
    minor = (REVISION - (major * 1000)) / 100;
    revision = REVISION % 100;
    snprintf(version, sizeof(version), "%d.%d.%d", major, minor, revision);
    return PyUnicode_FromString(version);
}
/******************************************************************************
*                                                                             *
*  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)
{
    char version[16];
    snprintf(version, sizeof(version), "%s", _chrysalide_plugin.version);
    return PyUnicode_FromString(version);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : self = NULL car méthode statique.                            *
*                args = contient la clef d'accès à un champ particulier.      *
*                                                                             *
*  Description : Recherche et fournit si elle existe une valeur globale.      *
*                                                                             *
*  Retour      : Object attaché à l'espace de référencement global ou NULL.   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static PyObject *py_chrysalide_get_global_gobject(PyObject *self, PyObject *args)
{
    PyObject *result;                       /* Instance à retourner        */
    const char *key;                        /* Désignation du champ visé   */
    int ret;                                /* Bilan de lecture des args.  */
    void *data;                             /* Donnée quelconque           */
    ret = PyArg_ParseTuple(args, "s", &key);
    if (!ret) Py_RETURN_NONE;
    data = g_object_get_data(get_internal_ref(), key);
    if (data == NULL) Py_RETURN_NONE;
    if (!G_IS_OBJECT(data)) Py_RETURN_NONE;
    result = pygobject_new(G_OBJECT(data));
    return result;
}
/******************************************************************************
*                                                                             *
*  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"                                    \
    "os.write(%d, bytes(sys.abiflags, 'UTF-8'))" "\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;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Point d'entrée pour l'initialisation de Python.              *
*                                                                             *
*  Retour      : ?                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
#include  // REMME
#include 
#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, it 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."
PyMODINIT_FUNC PyInit_pychrysalide(void)
{
    PyObject *result;                       /* Module Python à retourner   */
    PyObject *pygobj_mod;                   /* Module Python-GObject       */
    bool status;                            /* Bilan des inclusions        */
    static PyMethodDef py_chrysalide_methods[] = {
        { "revision", py_chrysalide_revision,
          METH_NOARGS,
          "revision(/)\n--\n\nProvide the revision number of Chrysalide."
        },
        { "version", py_chrysalide_version,
          METH_NOARGS,
          "version(/)\n--\n\nProvide the version number of Chrysalide."
        },
        { "mod_version", py_chrysalide_mod_version,
          METH_NOARGS,
          "mod_version(/)\n--\n\nProvide the version number of Chrysalide module for Python."
        },
        { "get_global_gobject", py_chrysalide_get_global_gobject,
          METH_VARARGS,
          "get_global_gobject(key, /)\n--\n\nRetrieve if it exists a global GObject registered with the given key."
        },
        { NULL }
    };
    static PyModuleDef py_chrysalide_module = {
        .m_base = PyModuleDef_HEAD_INIT,
        .m_name = "pychrysalide",
        .m_doc = PYCHRYSALIDE_DOC,
        .m_size = -1,
        .m_methods = py_chrysalide_methods
    };
#if 0
    do
    {
        int argc = 0;
        //char *argv[] = { NULL };
    /* Initialisation de GTK */
    g_set_prgname("Chrysalide");
    setlocale (LC_ALL, "");
    gtk_init(&argc, NULL/*&argv-*/);
    } while (0);
    add_pixmap_directory(PACKAGE_DATA_DIR);
    add_pixmap_directory(PACKAGE_SOURCE_DIR G_DIR_SEPARATOR_S "pixmaps");
    /**
     * On évite d'initialiser deux fois...
     */
    if (get_work_queue() != NULL)
        init_work_queue(NULL/* !! */);
#endif
    /**
     * On évite d'initialiser deux fois...
     */
    if (get_work_queue() == NULL)
        init_work_queue(NULL/* !! */);
    ////////////////////////
    if (!is_current_abi_suitable())
        return NULL;
    if (pygobject_init(-1, -1, -1) == NULL)
    {
        PyErr_SetString(PyExc_SystemError, "unable to init GObject in Python.");
        return NULL;
    }
    if (!load_all_basic_components())
    {
        PyErr_SetString(PyExc_SystemError, "unable to load all basic components.");
        return NULL;
    }
    /* Préparatifs préalables aux chargements */
    /**
     * Pour une raison non identifiée, si le module n'est pas préchargé,
     * le flot d'exécution plante dans la fonction insertdict() de Objects/dictobject.c:818.
     */
    pygobj_mod = PyImport_ImportModule("gi.repository.GObject");
    if (pygobj_mod == NULL)
    {
        PyErr_SetString(PyExc_ImportError, "could not import gi.gobject");
        return NULL;
    }
    /* Mise en place des fonctionnalités offertes */
    result = PyModule_Create(&py_chrysalide_module);
    status = register_python_plugin_module(result);
    status &= add_analysis_module_to_python_module(result);
    status &= add_arch_module_to_python_module(result);
    status &= add_common_module_to_python_module(result);
    status &= add_core_module_to_python_module(result);
    status &= add_format_module_to_python_module(result);
    status &= add_glibext_module_to_python_module(result);
    status &= add_gtkext_module_to_python_module(result);
    status &= add_gui_module_to_python_module(result);
    if (!status)
    {
        PyErr_SetString(PyExc_SystemError, "fail to load all PyChrysalide components.");
        return NULL;
    }
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : plugin = instance représentant le greffon Python d'origine.  *
*                ref    = espace de référencement global.                     *
*                                                                             *
*  Description : Charge autant de greffons composés en Python que possible.   *
*                                                                             *
*  Retour      : true.                                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static bool load_python_plugins(GPluginModule *plugin, GObject *ref)
{
    char *paths;                            /* Emplacements de greffons    */
    char *save;                             /* Sauvegarde pour ré-entrance */
    char *path;                             /* Chemin à fouiller           */
    DIR *dir;                               /* Répertoire à parcourir      */
    struct dirent entry;                    /* Elément trouvé              */
    struct dirent *next;                    /* Prochain élément fourni     */
    int ret;                                /* Bilan d'un appel système    */
    char *modname;                          /* Nom du module pour Python   */
    char *filename;                         /* Chemin d'accès reconstruit  */
    GPluginModule *pyplugin;                /* Lien vers un grffon Python  */
    paths = get_env_var("PYTHONPATH");
    save = NULL;   /* gcc... */
    for (path = strtok_r(paths, ";", &save);
         path != NULL; 
         path = strtok_r(NULL, ";", &save))
    {
        dir = opendir(path);
        if (dir == NULL)
        {
            perror("opendir");
            continue;
        }
        g_plugin_module_log_variadic_message(plugin, LMT_INFO, 
                                             _("Looking for Python plugins in '%s'..."),
                                             path);
        for (ret = readdir_r(dir, &entry, &next);
             ret == 0 && next != NULL;
             ret = readdir_r(dir, &entry, &next))
        {
            if (entry.d_type != DT_DIR) continue;
            if (entry.d_name[0] == '.') continue;
            modname = strdup(entry.d_name);
            modname = stradd(modname, ".");
            modname = stradd(modname, "__init__");
            filename = strdup(path);
            filename = stradd(filename, G_DIR_SEPARATOR_S);
            filename = stradd(filename, entry.d_name);
            pyplugin = g_python_plugin_new(modname, filename, ref);
            if (pyplugin == NULL)
                g_plugin_module_log_variadic_message(plugin, LMT_ERROR, 
                                                     _("No suitable Python plugin found in '%s'"),
                                                     filename);
            else
            {
                g_plugin_module_log_variadic_message(plugin, LMT_PROCESS, 
                                                     _("Loaded the Python plugin found in the '%s' directory"),
                                                     filename);
                add_plugin_to_main_list(pyplugin);
            }
            free(filename);
            free(modname);
        }
         closedir(dir);
    }
    return true;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : plugin = greffon à manipuler.                                *
*                ref    = espace de référencement global.                     *
*                                                                             *
*  Description : Prend acte du chargement du greffon.                         *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin, GObject *ref)
{
    bool result;                            /* Bilan à retourner           */
    DIR *dir;                               /* Répertoire à parcourir      */
    define_internal_ref(ref);
    /* Définition des zones d'influence */
    dir = opendir(PLUGINS_DIR G_DIR_SEPARATOR_S "python");
    if (dir != NULL)
    {
         closedir(dir);
         add_to_env_var("PYTHONPATH", PLUGINS_DIR G_DIR_SEPARATOR_S "python", ";");
    }
    else
        add_to_env_var("PYTHONPATH", PACKAGE_SOURCE_DIR G_DIR_SEPARATOR_S "plugins" \
                       G_DIR_SEPARATOR_S "python", ";");
    /* Chargement du module pour Python */
    PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalide);
    Py_Initialize();
    PySys_SetArgv(0, (wchar_t *[]) { NULL });
    result = load_python_plugins(plugin, ref);
    return result;
}