diff options
Diffstat (limited to 'src/gui/panels/welcome.c')
-rw-r--r-- | src/gui/panels/welcome.c | 865 |
1 files changed, 865 insertions, 0 deletions
diff --git a/src/gui/panels/welcome.c b/src/gui/panels/welcome.c new file mode 100644 index 0000000..209419f --- /dev/null +++ b/src/gui/panels/welcome.c @@ -0,0 +1,865 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * welcome.c - panneau d'accueil par défaut + * + * Copyright (C) 2016 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 "welcome.h" + + +#include <assert.h> +#include <malloc.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + + +#include <config.h> +#include <i18n.h> + + +#include "panel-int.h" +#include "../../common/cpp.h" +#include "../../common/io.h" +#include "../../common/net.h" +#include "../../common/shuffle.h" +#include "../../core/params.h" +#include "../../gtkext/support.h" + + + +/* Panneau d'accueil par défaut (instance) */ +struct _GWelcomePanel +{ + GPanelItem parent; /* A laisser en premier */ + + GtkBuilder *builder; /* Constructeur utilisé */ + cairo_surface_t *background; /* Fond pour astuces */ + + char **tips; /* Liste de toutes les astuces */ + size_t count; /* Quantité d'astuces */ + size_t current; /* Indice de l'astuce courante */ + + bool uorigin; /* Origine de l'affichage */ + +}; + +/* Panneau d'accueil par défaut (classe) */ +struct _GWelcomePanelClass +{ + GPanelItemClass parent; /* A laisser en premier */ + +}; + + +/* Colonnes de la liste des messages */ +typedef enum _RecentProjectColumn +{ + RPC_VALID, /* Validité de l'entrée */ + RPC_FULLPATH, /* Chemin d'accès à un projet */ + + RPC_COUNT /* Nombre de colonnes */ + +} RecentProjectColumn; + + +/* Initialise la classe des panneaux d'accueil par défaut. */ +static void g_welcome_panel_class_init(GWelcomePanelClass *); + +/* Initialise une instance de panneau d'accueil par défaut. */ +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 *); + +/* Place un panneau dans l'ensemble affiché. */ +static void g_welcome_panel_dock(GWelcomePanel *); + +/* Charge l'ensemble des astuces. */ +static void g_welcome_panel_load_tips(GWelcomePanel *); + +/* Assure le dessin du fond de la bulle d'astuce. */ +static gboolean on_tip_background_draw(GtkWidget *, cairo_t *, GWelcomePanel *); + +/* Réagit à la demande d'étude d'un nouveau binaire. */ +static void on_new_binary_clicked(GtkButton *, GWelcomePanel *); + +/* Actualise au besoin la liste des projets récents. */ +static void on_recent_list_changed(GtkRecentManager *, GWelcomePanel *); + +/* Recharge une liste à jour des projets récents. */ +static void g_welcome_panel_reload_project_list(GWelcomePanel *, GtkRecentManager *); + +/* Réagit à une sélection décidée d'un projet particulier. */ +static void on_row_activated_for_projects(GtkTreeView *, GtkTreePath *, GtkTreeViewColumn *, GWelcomePanel *); + +/* Enregistre les conditions d'affichage du panneau d'accueil. */ +static void on_startup_toggled(GtkToggleButton *, GWelcomePanel *); + +/* Consulte les versions existantes et affiche une conclusion. */ +static void g_welcome_panel_check_version(GWelcomePanel *); + +/* Affiche l'astuce précédente dans la liste globale. */ +static void on_tip_previous_clicked(GtkButton *, GWelcomePanel *); + +/* Affiche l'astuce suivante dans la liste globale. */ +static void on_tip_next_clicked(GtkButton *, GWelcomePanel *); + +/* Actualise l'affichage des astuces. */ +static void g_welcome_panel_refresh_tip(GWelcomePanel *); + + +/* Indique le type défini pour un panneau d'accueil. */ +G_DEFINE_TYPE(GWelcomePanel, g_welcome_panel, G_TYPE_PANEL_ITEM); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des panneaux d'accueil par défaut. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_welcome_panel_class_init(GWelcomePanelClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + GPanelItemClass *parent; /* Version parente de classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_welcome_panel_dispose; + object->finalize = (GObjectFinalizeFunc)g_welcome_panel_finalize; + + parent = G_PANEL_ITEM_CLASS(klass); + + parent->ack_dock = (ack_undock_process_fc)g_welcome_panel_dock; + +} + + +/****************************************************************************** +* * +* Paramètres : panel = instance à initialiser. * +* * +* Description : Initialise une instance de panneau d'accueil par défaut. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_welcome_panel_init(GWelcomePanel *panel) +{ + GEditorItem *base; /* Version basique d'instance */ + GPanelItem *pitem; /* Version parente du panneau */ + GtkTreeView *treeview; /* Affichage de la liste */ + GtkCellRenderer *renderer; /* Moteur de rendu de colonne */ + GtkTreeViewColumn *column; /* Colonne de la liste */ + GtkToggleButton *button; /* Bouton à bascule à traiter */ + bool state; /* Etat de la coche à définir */ + gchar *filename; /* Chemin d'accès à une image */ + + /* Eléments de base */ + + base = G_EDITOR_ITEM(panel); + + base->name = PANEL_WELCOME_ID; + + pitem = G_PANEL_ITEM(panel); + + pitem->personality = PIP_SINGLETON; + pitem->lname = _("Welcome"); + pitem->dock_at_startup = true; + pitem->path = strdup("N"); + + panel->uorigin = !pitem->dock_at_startup; + + /* Représentation graphique */ + + panel->builder = gtk_builder_new_from_resource("/org/chrysalide/gui/panels/welcome.ui"); + + base->widget = GTK_WIDGET(gtk_builder_get_object(panel->builder, "box")); + g_object_ref(G_OBJECT(base->widget)); + gtk_widget_unparent(base->widget); + + /* Liste des projets récents */ + + treeview = GTK_TREE_VIEW(gtk_builder_get_object(panel->builder, "treeview")); + + column = gtk_tree_view_column_new(); + gtk_tree_view_append_column(treeview, column); + gtk_tree_view_set_expander_column(treeview, column); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_add_attribute(column, renderer, "markup", RPC_FULLPATH); + + /* Affichage au démarrage ? */ + + button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(panel->builder, "startup")); + + g_generic_config_get_value(get_main_configuration(), MPK_WELCOME_STARTUP, &state); + + gtk_toggle_button_set_active(button, state); + + /* Chargement de l'image de fond */ + + filename = find_pixmap_file("tipoftheday.png"); + + panel->background = cairo_image_surface_create_from_png(filename); + + g_free(filename); + + /* Connexion des signaux */ + + gtk_builder_add_callback_symbols(panel->builder, + "on_tip_background_draw", G_CALLBACK(on_tip_background_draw), + "on_new_binary_clicked", G_CALLBACK(on_new_binary_clicked), + "on_row_activated_for_projects", G_CALLBACK(on_row_activated_for_projects), + "on_startup_toggled", G_CALLBACK(on_startup_toggled), + "on_tip_previous_clicked", G_CALLBACK(on_tip_previous_clicked), + "on_tip_next_clicked", G_CALLBACK(on_tip_next_clicked), + NULL); + + gtk_builder_connect_signals(panel->builder, 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_unref(G_OBJECT(panel->builder)); + + free(panel->tips); + + 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) +{ + cairo_surface_destroy(panel->background); + + G_OBJECT_CLASS(g_welcome_panel_parent_class)->finalize(G_OBJECT(panel)); + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Crée un panneau d'accueil par défaut. * +* * +* Retour : Adresse de la structure mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GPanelItem *g_welcome_panel_new(void) +{ + GWelcomePanel *result; /* Structure à retourner */ + GtkRecentManager *manager; /* Gestionnaire global */ + + result = g_object_new(G_TYPE_WELCOME_PANEL, NULL); + + manager = get_projects_manager(); + + g_signal_connect(manager, "changed", G_CALLBACK(on_recent_list_changed), result); + + g_welcome_panel_reload_project_list(result, manager); + + g_welcome_panel_load_tips(result); + + g_welcome_panel_check_version(result); + + return G_PANEL_ITEM(result); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = composant à présenter à l'affichage. * +* * +* Description : Place un panneau dans l'ensemble affiché. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_welcome_panel_dock(GWelcomePanel *panel) +{ + g_welcome_panel_set_user_origin(panel, true); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = panneau d'accueil à mettre à jour. * +* * +* Description : Charge l'ensemble des astuces. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_welcome_panel_load_tips(GWelcomePanel *panel) +{ + size_t i; /* Boucle de parcours */ + + char *tips[] = { + + _("There is no need to install Chrysalide on your system if you only want to give it a try.\n\n" + "Just compile the source code and run the program from there."), + + _("Chrysalide can be used in external Python scripts by setting PYTHONPATH to the directory " + "containing the 'pychrysalide.so' file. For instance:\n\n" + " cd plugins/pychrysa/.libs/\n" + " export PYTHONPATH=$PWD\n\n" + "Then run the interpreter suitable to your configuration (debug or release):\n\n" + " python3-dbg -c 'import pychrysalide ; print(pychrysalide.mod_version())'"), + + _("All the configuration files for chrysalide are located in $HOME/.config/chrysalide/.") + + }; + + panel->count = ARRAY_SIZE(tips); + + panel->tips = (char **)calloc(panel->count, sizeof(char *)); + + for (i = 0; i < panel->count; i++) + panel->tips[i] = tips[i]; + + shuffle(panel->tips, panel->count, sizeof(char *)); + + panel->current = 0; + + g_welcome_panel_refresh_tip(panel); + +} + + +/****************************************************************************** +* * +* Paramètres : widget = composant graphique à redessiner. * +* cr = contexte graphique à utiliser. * +* panel = panneau associé comportant des informations utiles. * +* * +* Description : Assure le dessin du fond de la bulle d'astuce. * +* * +* Retour : FALSE pour poursuivre la propagation de l'événement. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static gboolean on_tip_background_draw(GtkWidget *widget, cairo_t *cr, GWelcomePanel *panel) +{ + int wgt_width; /* Largeur disponible totale */ + int wgt_height; /* Hauteur disponible totale */ + int img_width; /* Largeur de l'image de fond */ + int img_height; /* Hauteur de l'image de fond */ + double scale; /* Echelle à appliquer */ + + if (cairo_surface_status(panel->background) == CAIRO_STATUS_SUCCESS) + { + wgt_width = gtk_widget_get_allocated_width(widget); + wgt_height = gtk_widget_get_allocated_height(widget); + + img_width = cairo_image_surface_get_width(panel->background); + img_height = cairo_image_surface_get_height(panel->background); + + scale = wgt_height / (2.0 * img_height); + + cairo_scale(cr, scale, scale); + + cairo_set_source_surface(cr, panel->background, + (wgt_width / scale) - img_width, + ((wgt_height / scale) - img_height) / 2); + + cairo_paint(cr); + + } + + return FALSE; + +} + + +/****************************************************************************** +* * +* Paramètres : button = bouton impliqué dans la procédure. * +* panel = panneau associé comportant des informations utiles. * +* * +* Description : Réagit à la demande d'étude d'un nouveau binaire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void on_new_binary_clicked(GtkButton *button, GWelcomePanel *panel) +{ + GObject *ref; /* Espace de référencements */ + GtkMenuItem *item; /* Elément de menu simulé */ + + ref = g_editor_item_get_global_ref(G_EDITOR_ITEM(panel)); + + item = GTK_MENU_ITEM(g_object_get_data(ref, "mnu_project_add_binary")); + + gtk_menu_item_activate(item); + +} + + +/****************************************************************************** +* * +* Paramètres : manager = gestion de fichiers récemment utilisés. * +* panel = panneau associé comportant des informations utiles.* +* * +* Description : Actualise au besoin la liste des projets récents. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void on_recent_list_changed(GtkRecentManager *manager, GWelcomePanel *panel) +{ + g_welcome_panel_reload_project_list(panel, manager); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = panneau comportant des informations utiles. * +* manager = gestion de fichiers récemment utilisés. * +* * +* Description : Recharge une liste à jour des projets récents. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_welcome_panel_reload_project_list(GWelcomePanel *panel, GtkRecentManager *manager) +{ + GtkListStore *store; /* Modèle de gestion */ + bool empty; /* Liste vide ? */ + GList *recents; /* Liste des fichiers récents */ + GList *recent; /* Elément à traiter */ + GtkRecentInfo *info; /* Informations sur l'élément */ + GtkTreeIter iter; /* Point d'insertion */ + + /* Réinitialisation */ + + store = GTK_LIST_STORE(gtk_builder_get_object(panel->builder, "store")); + + gtk_list_store_clear(store); + + empty = true; + + /* Chargement */ + + recents = gtk_recent_manager_get_items(manager); + + if (recents != NULL) + { + for (recent = g_list_first(recents); recent != NULL; recent = g_list_next(recent)) + { + info = recent->data; + + if (strcmp(gtk_recent_info_get_mime_type(info), "application/chrysalide.project") == 0) + { + gtk_list_store_append(store, &iter); + + gtk_list_store_set(store, &iter, + RPC_VALID, true, + RPC_FULLPATH, gtk_recent_info_get_uri_display(info), + -1); + + empty = false; + + } + + gtk_recent_info_unref(info); + + } + + g_list_free(recents); + + } + + /* Indication par défaut */ + if (empty) + { + gtk_list_store_append(store, &iter); + + gtk_list_store_set(store, &iter, + RPC_VALID, false, + RPC_FULLPATH, _("<i>(No recent project)</i>"), + -1); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : treeview = liste graphique concernée par la procédure. * +* path = chemin d'accès à la ligne sélectionnée. * +* column = colonne concernée par la sélection. * +* panel = panneau associé avec des informations utiles. * +* * +* Description : Réagit à une sélection décidée d'un projet particulier. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void on_row_activated_for_projects(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, GWelcomePanel *panel) +{ + GtkTreeModel *model; /* Modèle de gestion */ + GtkTreeIter iter; /* Point de la consultation */ + gboolean valid; /* Validité de l'entrée */ + gchar *filename; /* Chemin d'accès au projet */ + GObject *ref; /* Espace de référencements */ + GStudyProject *project; /* Nouveau projet à ouvrir */ + + model = gtk_tree_view_get_model(treeview); + + if (gtk_tree_model_get_iter(model, &iter, path)) + { + gtk_tree_model_get(model, &iter, RPC_VALID, &valid, RPC_FULLPATH, &filename, -1); + + if (valid) + { + ref = g_editor_item_get_global_ref(G_EDITOR_ITEM(panel)); + + project = g_study_project_open(ref, filename); + + if (project != NULL) + { + set_current_project(project); + + push_project_into_recent_list(project); + + } + + g_free(filename); + + } + + } + +} + + +/****************************************************************************** +* * +* Paramètres : button = bouton de défilement des astuces activé; * +* panel = panneau associé comportant des informations utiles. * +* * +* Description : Enregistre les conditions d'affichage du panneau d'accueil. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void on_startup_toggled(GtkToggleButton *button, GWelcomePanel *panel) +{ + g_generic_config_set_value(get_main_configuration(), + MPK_WELCOME_STARTUP, gtk_toggle_button_get_active(button)); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = panneau d'accueil à mettre à jour. * +* * +* Description : Consulte les versions existantes et affiche une conclusion. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_welcome_panel_check_version(GWelcomePanel *panel) +{ + bool skip; /* Saut de la vérification */ + bool unknown; /* Impossibilité de comparaison*/ + int current; /* Version courante */ + int sock; /* Canal de communication */ + bool status; /* Bilan d'une communication */ + char buffer[1024]; /* Tampon de réception */ + size_t got; /* Quantité de données reçues */ + char *version; /* Version récupérée */ + int available; /* Version disponible */ + GtkLabel *label; /* Etiquette à éditer */ + char *msg; /* Message à faire paraître */ + + g_generic_config_get_value(get_main_configuration(), MPK_WELCOME_CHECK, &skip); + skip = !skip; + + unknown = true; + + current = atoi(VERSION); + + if (skip) goto check_process; + + /* Recherche en ligne */ + + sock = connect_via_tcp("www.chrysalide.re", "80", NULL); + if (sock == -1) goto check_process; + +#define REQUEST "GET /version.last HTTP/1.1\r\nHost: www.chrysalide.re\r\n\r\n" + + status = safe_send(sock, REQUEST, strlen(REQUEST), 0); + if (!status) goto check_done; + + status = recv_all(sock, buffer, sizeof(buffer), &got); + if (!status) goto check_done; + + version = strstr(buffer, "\r\n\r\n"); + + if (version != NULL) + { + available = atoi(version + 4); + + unknown = false; + + } + + check_done: + + close(sock); + + check_process: + + /* Affichage */ + + label = GTK_LABEL(gtk_builder_get_object(panel->builder, "version")); + + if (skip) + asprintf(&msg, + "Your version is: <b>%d</b>\n\n" \ + "Automatic version check is disabled.", + current); + + else + { + if (unknown) + asprintf(&msg, + "Your version is: <b>%d</b>\n\n" \ + "Lastest available version is unknown.", + current); + + else + { + if (current >= available) + asprintf(&msg, + "Your version is: <b>%d</b>\n\n" \ + "Lastest version is: <b>%d</b>\n\n" \ + "Your software is <span color='green'><b>up-to-date</b></span>.", + current, available); + + else + asprintf(&msg, + "Your version is: <b>%d</b>\n\n" \ + "Lastest version is: <b>%d</b>\n\n" \ + "Your software is <span color='red'><b>outdated</b></span>.", + current, available); + + } + + } + + gtk_label_set_markup(label, msg); + + free(msg); + +} + + +/****************************************************************************** +* * +* Paramètres : button = bouton de défilement des astuces activé; * +* panel = panneau associé comportant des informations utiles. * +* * +* Description : Affiche l'astuce précédente dans la liste globale. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void on_tip_previous_clicked(GtkButton *button, GWelcomePanel *panel) +{ + if (panel->current > 0) + panel->current--; + else + panel->current = panel->count - 1; + + g_welcome_panel_refresh_tip(panel); + +} + + +/****************************************************************************** +* * +* Paramètres : button = bouton de défilement des astuces activé; * +* panel = panneau associé comportant des informations utiles. * +* * +* Description : Affiche l'astuce suivante dans la liste globale. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void on_tip_next_clicked(GtkButton *button, GWelcomePanel *panel) +{ + if ((panel->current + 1) < panel->count) + panel->current++; + else + panel->current = 0; + + g_welcome_panel_refresh_tip(panel); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = panneau associé comportant des informations utiles. * +* * +* Description : Actualise l'affichage des astuces. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_welcome_panel_refresh_tip(GWelcomePanel *panel) +{ + GtkLabel *label; /* Etiquette de présentation */ + + assert(panel->current < panel->count); + + label = GTK_LABEL(gtk_builder_get_object(panel->builder, "tip")); + + gtk_label_set_markup(label, panel->tips[panel->current]); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = panneau associé comportant des informations utiles. * +* * +* Description : Indique l'origine de l'affichage du panneau d'accueil. * +* * +* Retour : true si l'affichage est le fait de l'utilisateur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_welcome_panel_get_user_origin(const GWelcomePanel *panel) +{ + return panel->uorigin; + +} + + +/****************************************************************************** +* * +* Paramètres : panel = panneau associé comportant des informations utiles.* +* uorigin = true si l'affichage est le fait de l'utilisateur. * +* * +* Description : Détermine l'origine de l'affichage du panneau d'accueil. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_welcome_panel_set_user_origin(GWelcomePanel *panel, bool uorigin) +{ + panel->uorigin = uorigin; + +} |