/* Chrysalide - Outil d'analyse de fichiers binaires * project.c - gestion d'un groupe de fichiers binaires * * Copyright (C) 2015-2017 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 . */ #include "project.h" #include #include #include #include #include #include "loaded.h" #include "loading.h" #include "../common/xml.h" #include "../core/global.h" #include "../core/logs.h" #include "../core/params.h" #include "../glibext/delayed-int.h" #include "../gui/core/panels.h" #include "../gui/panels/panel.h" #include "../format/format.h" /* ------------------------- DEFINITION D'UN PROJET INTERNE ------------------------- */ /* Conservation d'un contenu chargé */ typedef struct _loaded_content { GBinContent *content; /* Contenu binaire en place */ ProjectContentState state; /* Renseigne le type de contenu*/ } loaded_content; /* Conservation d'un binaire chargé */ typedef struct _loaded_binary { GLoadedBinary *binary; /* Binaire en question */ GPanelItem **items; /* Supports d'affichage final */ size_t count; /* Nombre de ces supports */ } loaded_binary; /* Projet d'étude regroupant les binaires analysés (instance) */ struct _GStudyProject { GObject parent; /* A laisser en premier */ char *filename; /* Lieu d'enregistrement */ GLoadedContent **contents; /* Contenus chargés et intégrés*/ size_t count; /* Quantité de ces contenus */ GMutex mutex; /* Encadrement des accès */ }; /* Projet d'étude regroupant les binaires analysés (classe) */ struct _GStudyProjectClass { GObjectClass parent; /* A laisser en premier */ /* Signaux */ void (* content_added) (GStudyProject *, GLoadedContent *); void (* content_removed) (GStudyProject *, GLoadedContent *); }; /* Initialise la classe des projets d'étude. */ static void g_study_project_class_init(GStudyProjectClass *); /*Initialise une instance de projet d'étude. */ static void g_study_project_init(GStudyProject *); /* ---------------------------------------------------------------------------------- */ /* DEFINITION D'UN PROJET INTERNE */ /* ---------------------------------------------------------------------------------- */ /* Indique le type défini pour un projet d'étude. */ G_DEFINE_TYPE(GStudyProject, g_study_project, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des projets d'étude. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_study_project_class_init(GStudyProjectClass *klass) { g_signal_new("content-added", G_TYPE_STUDY_PROJECT, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GStudyProjectClass, content_added), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); g_signal_new("content-removed", G_TYPE_STUDY_PROJECT, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GStudyProjectClass, content_removed), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); } /****************************************************************************** * * * Paramètres : project = instance à initialiser. * * * * Description : Initialise une instance de projet d'étude. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_study_project_init(GStudyProject *project) { g_mutex_init(&project->mutex); } /****************************************************************************** * * * Paramètres : - * * * * Description : Crée un nouveau projet vierge. * * * * Retour : Instance mise en place. * * * * Remarques : - * * * ******************************************************************************/ GStudyProject *g_study_project_new(void) { GStudyProject *result; /* Composant à retourner */ result = g_object_new(G_TYPE_STUDY_PROJECT, NULL); return result; } /****************************************************************************** * * * Paramètres : filename = chemin d'accès au fichier à charger. * * * * Description : Crée un projet à partir du contenu XML d'un fichier. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ GStudyProject *g_study_project_open(const char *filename) { return NULL; #if 0 GStudyProject *result; /* Adresse à retourner */ xmlDocPtr xdoc; /* Structure XML chargée */ xmlXPathContextPtr context; /* Contexte pour les XPath */ unsigned int root_contents; /* Quantité de contenus majeurs*/ GAsyncQueue *sema; /* Sémaphore taisant son nom */ xmlXPathObjectPtr xobject; /* Cible d'une recherche */ unsigned int i; /* Boucle de parcours */ size_t access_len; /* Taille d'un chemin interne */ char *access; /* Chemin pour une sous-config.*/ GBinContent *content; /* Contenu binaire retrouvé */ long state; /* Etat de ce contenu binaire */ bool status; /* Bilan d'une lecture */ GDelayedStudy *dstudy; /* Etude complémentaire à mener*/ GBinaryLoader *loader; /* Dispositif de chargement */ GWorkQueue *queue; /* Gestionnaire de différés */ if (!open_xml_file(filename, &xdoc, &context)) return NULL; result = g_study_project_new(); result->filename = strdup(filename); /* Préparations aux traitements parallèles */ root_contents = 0; sema = g_async_queue_new(); void ack_content_processing(GDelayedStudy *dstudy, GAsyncQueue *aqueue) { g_async_queue_push(aqueue, GINT_TO_POINTER(1)); } /* Chargement des contenus binaires attachés */ xobject = get_node_xpath_object(context, "/ChrysalideProject/Contents/Content"); for (i = 0; i < XPATH_OBJ_NODES_COUNT(xobject); i++) { access_len = strlen("/ChrysalideProject/Contents/Content[position()=") + SIZE_T_MAXLEN + strlen("]") + 1; access = calloc(access_len, sizeof(char)); snprintf(access, access_len, "/ChrysalideProject/Contents/Content[position()=%u]", i + 1); content = g_binary_content_new_from_xml(context, access, filename); status = get_node_prop_long_value(context, access, "state", &state); free(access); if (content == NULL) { log_variadic_message(LMT_ERROR, _("Unable to load the binary content #%u ; skipping..."), i); continue; } if (!status) { log_variadic_message(LMT_ERROR, _("Bad state for content '%s' ; skipping..."), g_binary_content_describe(content, true)); continue; } /* Le contenu peut être un conteneur ? */ if (state == PCS_ROOT) { dstudy = g_delayed_study_new(result, content, state); g_signal_connect(dstudy, "work-completed", G_CALLBACK(ack_content_processing), sema); g_delayed_study_preload_only(dstudy); root_contents++; study_new_content(dstudy); } } if(xobject != NULL) xmlXPathFreeObject(xobject); /* Attente pour la réception de contenus supplémentaires éventuels */ for (i = 0; i < root_contents; i++) g_async_queue_pop(sema); g_async_queue_unref(sema); /* Chargement des binaires analysés */ xobject = get_node_xpath_object(context, "/ChrysalideProject/Binaries/Binary"); for (i = 0; i < XPATH_OBJ_NODES_COUNT(xobject); i++) { access_len = strlen("/ChrysalideProject/Binaries/Binary[position()=") + SIZE_T_MAXLEN + strlen("]") + 1; access = calloc(access_len, sizeof(char)); snprintf(access, access_len, "/ChrysalideProject/Binaries/Binary[position()=%u]", i + 1); loader = g_binary_loader_new_from_xml(filename, access, result); free(access); queue = get_work_queue(); g_work_queue_schedule_work(queue, G_DELAYED_WORK(loader), DEFAULT_WORK_GROUP); } if(xobject != NULL) xmlXPathFreeObject(xobject); /* Fin du chargement */ close_xml_file(xdoc, context); return result; #endif } /****************************************************************************** * * * Paramètres : project = project à sauvegarder. * * filename = nom de fichier à utiliser ou NULL pour l'existant.* * * * Description : Procède à l'enregistrement d'un projet donné. * * * * Retour : true si l'enregistrement s'est déroule sans encombre. * * * * Remarques : - * * * ******************************************************************************/ bool g_study_project_save(GStudyProject *project, const char *filename) { return false; #if 0 bool result; /* Bilan à retourner */ xmlDocPtr xdoc; /* Document XML à créer */ xmlXPathContextPtr context; /* Contexte pour les recherches*/ const char *final; /* Lieu d'enregistrement final */ size_t i; /* Boucle de parcours */ char *access; /* Chemin pour une sous-config.*/ result = create_new_xml_file(&xdoc, &context); if (result) result = (ensure_node_exist(xdoc, context, "/ChrysalideProject") != NULL); final = filename != NULL ? filename : project->filename; /* Enregistrement des binaires analysés */ for (i = 0; i < project->binaries_count && result; i++) { asprintf(&access, "/ChrysalideProject/Binaries/Binary[position()=%zu]", i + 1); result = g_loaded_binary_save(project->binaries[i]->binary, xdoc, context, access, final); free(access); } /* Sauvegarde finale */ result &= save_xml_file(xdoc, final); if (result && filename != NULL) { if (project->filename != NULL) free(project->filename); project->filename = strdup(filename); } close_xml_file(xdoc, context); return result; #endif } /****************************************************************************** * * * Paramètres : project = project à consulter. * * * * Description : Indique le chemin du fichier destiné à la sauvegarde. * * * * Retour : Chemin de fichier pour l'enregistrement ou NULL si indéfini. * * * * Remarques : - * * * ******************************************************************************/ const char *g_study_project_get_filename(const GStudyProject *project) { return project->filename; } /****************************************************************************** * * * Paramètres : project = projet dont le contenu est à compléter. * * content = contenu binaire à mémoriser pour le projet. * * state = état du contenu à conserver. * * * * Description : Assure l'intégration d'un contenu binaire dans un projet. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_study_project_add_binary_content(GStudyProject *project, GBinContent *content, ProjectContentState state) { #if 0 loaded_content *new; /* Nouveau contenu à définir */ g_mutex_lock(&project->cnt_mutex); project->contents = (loaded_content *)realloc(project->contents, ++project->contents_count * sizeof(loaded_content)); new = &project->contents[project->contents_count - 1]; g_object_ref(G_OBJECT(content)); new->content = content; new->state = state; g_mutex_unlock(&project->cnt_mutex); #endif } /****************************************************************************** * * * Paramètres : project = projet dont le contenu est à compléter. * * hash = empreinte du contenu à retrouver. * * * * Description : Recherche un contenu binaire du projet selon son empreinte. * * * * Retour : Contenu avec propriété transférée ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ GBinContent *g_study_project_find_binary_content_by_hash(GStudyProject *project, const char *hash) { return NULL; #if 0 GBinContent *result; /* Trouvaille à retourner */ size_t i; /* Boucle de parcours */ GBinContent *iter; /* Contenu binaire analysé */ const gchar *other; /* Autre empreinte à comparer */ result = NULL; g_mutex_lock(&project->cnt_mutex); for (i = 0; i < project->contents_count && result == NULL; i++) { iter = project->contents[i].content; other = g_binary_content_get_checksum(iter); if (strcmp(hash, other) == 0) { g_object_ref(G_OBJECT(iter)); result = iter; } } g_mutex_unlock(&project->cnt_mutex); return result; #endif } /****************************************************************************** * * * Paramètres : project = project à manipuler. * * content = contenu chargé à associer au projet actuel. * * * * Description : Attache un contenu donné à un projet donné. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_study_project_attach_content(GStudyProject *project, GLoadedContent *content) { g_mutex_lock(&project->mutex); project->contents = (GLoadedContent **)realloc(project->contents, ++project->count * sizeof(GLoadedContent *)); project->contents[project->count - 1] = content; g_mutex_unlock(&project->mutex); g_signal_emit_by_name(project, "content-added", content); } /****************************************************************************** * * * Paramètres : project = project à manipuler. * * content = contenu chargé à dissocier du projet actuel. * * * * Description : Détache un contenu donné d'un projet donné. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_study_project_detach_content(GStudyProject *project, GLoadedContent *content) { size_t i; /* Boucle de parcours */ g_mutex_lock(&project->mutex); for (i = 0; i < project->count; i++) if (project->contents[i] == content) break; if ((i + 1) < project->count) memmove(&project->contents[i], &project->contents[i + 1], (project->count - i - 1) * sizeof(GLoadedContent *)); project->contents = (GLoadedContent **)realloc(project->contents, --project->count * sizeof(GLoadedContent *)); g_mutex_unlock(&project->mutex); g_signal_emit_by_name(project, "content-removed", content); g_object_unref(G_OBJECT(content)); } /****************************************************************************** * * * Paramètres : project = projet dont le contenu est à afficher. * * * * Description : Met en place un projet à l'écran. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_study_project_display(const GStudyProject *project) { #if 0 size_t i; /* Boucle de parcours #1 */ loaded_binary *handled; /* Binaire prise en compte */ size_t j; /* Boucle de parcours #2 */ for (i = 0; i < project->binaries_count; i++) { handled = project->binaries[i]; for (j = 0; j < handled->count; j++) g_panel_item_dock(handled->items[j]); } #endif } /****************************************************************************** * * * Paramètres : project = projet dont le contenu est à cacher. * * * * Description : Supprime de l'écran un projet en place. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_study_project_hide(const GStudyProject *project) { #if 0 size_t i; /* Boucle de parcours #1 */ loaded_binary *handled; /* Binaire prise en compte */ size_t j; /* Boucle de parcours #2 */ for (i = 0; i < project->binaries_count; i++) { handled = project->binaries[i]; for (j = 0; j < handled->count; j++) g_panel_item_undock(handled->items[j]); } #endif } /****************************************************************************** * * * Paramètres : project = projet dont le contenu est à afficher. * * count = nombre de contenus pris en compte. [OUT] * * * * Description : Fournit l'ensemble des contenus associés à un projet. * * * * Retour : Liste à libérer de la mémoire. * * * * Remarques : - * * * ******************************************************************************/ GLoadedContent **g_study_project_get_contents(GStudyProject *project, size_t *count) { GLoadedContent **result; /* Tableau à retourner */ size_t i; /* Boucle de parcours */ g_mutex_lock(&project->mutex); *count = project->count; result = (GLoadedContent **)calloc(*count, sizeof(GLoadedContent *)); for (i = 0; i < *count; i++) { result[i] = project->contents[i]; g_object_ref(G_OBJECT(result[i])); } g_mutex_unlock(&project->mutex); return result; } /* ---------------------------------------------------------------------------------- */ /* GESTION GLOBALISEE DES PROJETS */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : - * * * * Description : Fournit le gestionnaire des projets connus. * * * * Retour : Instance de gestion unique. * * * * Remarques : - * * * ******************************************************************************/ GtkRecentManager *get_project_manager(void) { static GtkRecentManager *result = NULL; /* Singleton à retourner */ if (result == NULL) { result = gtk_recent_manager_get_default(); //gtk_recent_manager_purge_items(result, NULL); } return result; } /****************************************************************************** * * * Paramètres : project = projet à traiter. * * * * Description : Place un projet au sommet de la pile des projets récents. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void push_project_into_recent_list(const GStudyProject *project) { GtkRecentManager *manager; /* Gestionnaire global */ char *qualified; /* Chemin avec 'file://' */ GtkRecentData recent; /* Données complètes */ if (project->filename == NULL) return; /* Constitution de la liste des projets récents */ manager = get_project_manager(); qualified = (char *)calloc(strlen("file://") + strlen(project->filename) + 1, sizeof(char)); strcpy(qualified, "file://"); strcat(qualified, project->filename); memset(&recent, 0, sizeof(GtkRecentData)); recent.mime_type = "application/chrysalide.project"; recent.app_name = "Chrysalide"; recent.app_exec = "chrysalide %f"; gtk_recent_manager_add_full(manager, qualified, &recent); free(qualified); /* Pour la prochaine ouverture du programme... */ g_generic_config_set_value(get_main_configuration(), MPK_LAST_PROJECT, project->filename); }