/* Chrysalide - Outil d'analyse de fichiers binaires
 * pglist.c - gestion de l'ensemble des greffons
 *
 * Copyright (C) 2009-2012 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


#include "pglist.h"


#include <assert.h>
#include <dirent.h>
#include <malloc.h>
#include <string.h>


#include <config.h>


#include "plugin-int.h"
#include "../common/extstr.h"


/* Représentation dédiée aux listes de greffons */
typedef struct _pg_array
{
    PluginAction action;                    /* Action commune ou PGA_ALL   */

    GPluginModule **plugins;                /* Liste de greffons           */
    size_t plugins_count;                   /* Taille de cette liste       */

} pg_array;

/* Propriétés de l'ensemble des greffons */
typedef struct _plugins_list
{
    GObject *ref;                           /* Référencement global        */

    pg_array *all;                          /* Liste de tous les greffons  */
    pg_array sorted[PGA_COUNT];             /* Tri par catégories          */

} plugins_list;


/* Liste de l'ensemble des greffons */
static plugins_list _list;


/* Filtre les répertoire et les modules de greffons pootentels. */
int filter_dirs_or_mods(const struct dirent *);

/* Part à la recherche de greffons sous forme de modules. */
void browse_directory_for_plugins(plugins_list *, const char *);



/******************************************************************************
*                                                                             *
*  Paramètres  : ref = espace de référencement global.                        *
*                                                                             *
*  Description : Procède au chargement des différents greffons trouvés.       *
*                                                                             *
*  Retour      : Toujours true (même s'il y a des erreurs de chargement).     *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool init_all_plugins(GObject *ref)
{
    size_t i;                               /* Boucle de parcours          */

    _list.ref = ref;
    g_object_ref(ref);

    for (i = 0; i < PGA_COUNT; i++)
        _list.sorted[i].action = PGA_EMPTY;

    _list.sorted[0].action = PGA_ALL;
    _list.all = &_list.sorted[0];

    browse_directory_for_plugins(&_list, PACKAGE_SOURCE_DIR "/plugins");

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Procède au déchargement des différents greffons présents.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void exit_all_plugins(void)
{
    size_t i;                               /* Boucle de parcours          */

    for (i = 0; i < _list.all->plugins_count; i++)
        g_object_unref(_list.all->plugins[i]);

    for (i = 0; i < PGA_COUNT; i++)
        free(_list.sorted[i].plugins);

    g_object_unref(_list.ref);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : entry = entrée de répertoire à analyser.                     *
*                                                                             *
*  Description : Filtre les répertoire et les modules de greffons pootentels. *
*                                                                             *
*  Retour      : Valeur non nulle pour garder l'élément.                      *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

int filter_dirs_or_mods(const struct dirent *entry)
{
    int result;                             /* Conclusion à remonter       */

    if (entry->d_type == DT_DIR)
        result = strcmp(entry->d_name, ".") * strcmp(entry->d_name, "..");

    else
        result = (strrcmp(entry->d_name, "." G_MODULE_SUFFIX) == 0 ? 1 : 0);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : list = liste de greffons à compléter si possible.            *
*                dir  = répertoire à parcourir en quête de greffons (sans /). *
*                                                                             *
*  Description : Part à la recherche de greffons sous forme de modules.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void browse_directory_for_plugins(plugins_list *list, const char *dir)
{
    struct dirent **namelist;               /* Eléments trouvés            */
    int ret;                                /* Bilan du parcours           */
    char *filename;                         /* Elément à ausculter         */
    GPluginModule *plugin;                  /* Greffon à intégrer ou pas   */

    ret = scandir(dir, &namelist, filter_dirs_or_mods, alphasort);
    if (ret < 0)
    {
        perror("scandir");
        return;
    }

    while (ret--)
    {
        filename = (char *)calloc(strlen(dir) + 1 + strlen(namelist[ret]->d_name) + 1, sizeof(char));

        strcpy(filename, dir);
        strcat(filename, "/");    /* FIXME : Win */
        strcat(filename, namelist[ret]->d_name);

        if (namelist[ret]->d_type == DT_DIR)
            browse_directory_for_plugins(list, filename);

        else
        {
            plugin = g_plugin_module_new(filename, _list.ref);

            if (plugin != NULL)
                add_plugin_to_main_list(plugin);

        }

        free(filename);
        free(namelist[ret]);

    }

    free(namelist);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : action = fonctionnalité recherchée.                          *
*                count  = nombre de greffons trouvés. [OUT]                   *
*                                                                             *
*  Description : Founit les greffons offrant le service demandé.              *
*                                                                             *
*  Retour      : Liste de greffons correspondants issue d'un tri interne.     *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

const GPluginModule **get_all_plugins_for_action(PluginAction action, size_t *count)
{
    const GPluginModule **result;           /* Greffon à retourner         */
    size_t i;                               /* Boucle de parcours          */

    result = NULL;
    *count = 0;

    for (i = 0; i < PGA_COUNT; i++)
        if (_list.sorted[i].action == action)
        {
            result = (const GPluginModule **)_list.sorted[i].plugins;
            *count = _list.sorted[i].plugins_count;
            break;
        }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : plugin = greffon à ajouter aux autres disponibles.           *
*                                                                             *
*  Description : Ajoute un greffon à la liste principale de greffons.         *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void add_plugin_to_main_list(GPluginModule *plugin)
{
    const plugin_interface *interface;      /* Informations à consulter    */
    size_t i;                               /* Boucle de parcours #1       */
    size_t j;                               /* Boucle de parcours #2       */

    interface = g_plugin_module_get_interface(plugin);

    void add_plugin_into_array(pg_array *array, GPluginModule *pg)
    {
        array->plugins = (GPluginModule **)realloc(array->plugins,
                                                   ++array->plugins_count * sizeof(GPluginModule));

        array->plugins[array->plugins_count - 1] = pg;

    }

    /* FIXME : lock */

    add_plugin_into_array(_list.all, plugin);

    for (i = 0; i < interface->actions_count; i++)
    {
        if (interface->actions[i] == PGA_ALL) continue;

        for (j = 1; j < PGA_COUNT; j++)
        {
            if (_list.sorted[j].action == interface->actions[i])
            {
                add_plugin_into_array(&_list.sorted[j], plugin);
                break;
            }

            else if (_list.sorted[j].action == PGA_EMPTY)
            {
                add_plugin_into_array(&_list.sorted[j], plugin);
                _list.sorted[j].action = interface->actions[i];
                break;
            }

        }

        assert(j < PGA_COUNT);

    }

    /* FIXME : lock */

}